@expokit/cli 0.4.3 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +30 -3
- package/bin/expo-kit.js +749 -288
- package/bin/lib.mjs +313 -4
- package/package.json +1 -1
- package/registry/_docs/action-sheet.json +1 -1
- package/registry/_docs/alert.json +6 -0
- package/registry/_docs/bottom-sheet.json +6 -0
- package/registry/_docs/bullet.json +6 -0
- package/registry/_docs/dialog.json +1 -1
- package/registry/_docs/drawer.json +3 -3
- package/registry/_docs/flip-calendar.json +106 -0
- package/registry/_docs/glass-search-bar.json +1 -1
- package/registry/_docs/grain.json +6 -1
- package/registry/_docs/isometric-bar.json +6 -0
- package/registry/_docs/liquidglass-tab.json +1 -1
- package/registry/_docs/password-input.json +1 -1
- package/registry/_docs/paywall.json +144 -0
- package/registry/_docs/pixel-bar.json +6 -0
- package/registry/_docs/polar-area.json +6 -0
- package/registry/_docs/qr-code-button.json +9 -3
- package/registry/_docs/ruler-time-picker.json +147 -0
- package/registry/_docs/scratch-card.json +7 -1
- package/registry/_docs/search-bar.json +1 -1
- package/registry/_docs/select.json +2 -2
- package/registry/_docs/slide-to-confirm.json +2 -2
- package/registry/_docs/spinner.json +1 -1
- package/registry/_docs/stateful-button.json +2 -2
- package/registry/_docs/swipe-deck.json +1 -1
- package/registry/_docs/time-picker.json +2 -2
- package/registry/_docs/timeline-chart.json +6 -0
- package/registry/_docs/toast.json +15 -3
- package/registry/_docs/treemap.json +6 -0
- package/registry/_docs/waterfall.json +6 -0
- package/registry/accordion/files/src/components/ui/accordion.tsx +6 -4
- package/registry/accordion/files/src/lib/kit.tsx +462 -12
- package/registry/accordion.json +1 -0
- package/registry/action-sheet/files/src/components/ui/action-sheet.tsx +50 -58
- package/registry/action-sheet/files/src/lib/kit.tsx +462 -12
- package/registry/action-sheet.json +1 -0
- package/registry/alert/files/src/components/ui/alert.tsx +20 -13
- package/registry/alert/files/src/lib/kit.tsx +462 -12
- package/registry/alert.json +3 -4
- package/registry/assistant-app.json +1 -0
- package/registry/avatar/files/src/components/ui/avatar.tsx +89 -74
- package/registry/avatar/files/src/lib/kit.tsx +462 -12
- package/registry/avatar.json +1 -0
- package/registry/badge/files/src/components/ui/badge.tsx +36 -36
- package/registry/badge/files/src/lib/kit.tsx +462 -12
- package/registry/badge.json +1 -0
- package/registry/bottom-nav/files/src/components/ui/bottom-nav.tsx +39 -44
- package/registry/bottom-nav/files/src/lib/kit.tsx +462 -12
- package/registry/bottom-nav.json +5 -0
- package/registry/bottom-sheet/files/src/components/ui/bottom-sheet.tsx +357 -300
- package/registry/bottom-sheet/files/src/lib/kit.tsx +462 -12
- package/registry/bottom-sheet.json +5 -1
- package/registry/bullet.json +1 -0
- package/registry/button/files/src/components/ui/button.tsx +23 -34
- package/registry/button/files/src/lib/kit.tsx +462 -12
- package/registry/button-3d/files/src/components/ui/button-3d.tsx +6 -9
- package/registry/button-3d/files/src/lib/kit.tsx +462 -12
- package/registry/button-3d.json +1 -0
- package/registry/button.json +1 -0
- package/registry/calendar/files/src/components/ui/calendar.tsx +77 -79
- package/registry/calendar/files/src/lib/kit.tsx +462 -12
- package/registry/calendar.json +1 -0
- package/registry/card/files/src/components/ui/card.tsx +7 -6
- package/registry/card/files/src/lib/kit.tsx +462 -12
- package/registry/card-stack.json +1 -0
- package/registry/card.json +1 -0
- package/registry/carousel.json +1 -0
- package/registry/chat.json +7 -0
- package/registry/checkbox/files/src/components/ui/checkbox.tsx +4 -4
- package/registry/checkbox/files/src/lib/kit.tsx +462 -12
- package/registry/checkbox.json +1 -0
- package/registry/checkout.json +1 -0
- package/registry/chip-group/files/src/components/ui/chip-group.tsx +24 -24
- package/registry/chip-group/files/src/lib/kit.tsx +462 -12
- package/registry/chip-group.json +1 -0
- package/registry/confetti/files/src/components/ui/confetti.tsx +15 -14
- package/registry/confetti.json +3 -6
- package/registry/coupon/files/src/components/ui/coupon.tsx +47 -52
- package/registry/coupon/files/src/components/ui/flip-card.tsx +9 -14
- package/registry/coupon/files/src/components/ui/tilt-card.tsx +80 -101
- package/registry/coupon/files/src/lib/kit.tsx +462 -12
- package/registry/coupon/files/src/lib/ticket-shape.tsx +54 -44
- package/registry/coupon.json +1 -0
- package/registry/cube-pager.json +1 -0
- package/registry/dashboard.json +1 -0
- package/registry/date-picker/files/src/components/ui/calendar.tsx +77 -79
- package/registry/date-picker/files/src/components/ui/date-picker.tsx +64 -71
- package/registry/date-picker/files/src/lib/kit.tsx +462 -12
- package/registry/date-picker.json +1 -0
- package/registry/dialog/files/src/components/ui/dialog.tsx +66 -68
- package/registry/dialog/files/src/lib/kit.tsx +462 -12
- package/registry/dialog.json +1 -0
- package/registry/donut-shop.json +1 -0
- package/registry/drawer/files/src/components/ui/drawer.tsx +76 -88
- package/registry/drawer/files/src/lib/kit.tsx +462 -12
- package/registry/drawer.json +1 -0
- package/registry/fab.json +1 -0
- package/registry/fintech-app.json +1 -0
- package/registry/flip-calendar/files/src/components/ui/flip-calendar.tsx +632 -0
- package/registry/flip-calendar/files/src/lib/kit.tsx +672 -0
- package/registry/flip-calendar.json +24 -0
- package/registry/flip-card/files/src/components/ui/flip-card.tsx +9 -14
- package/registry/flip-card/files/src/lib/kit.tsx +462 -12
- package/registry/flip-card.json +1 -0
- package/registry/floating-input/files/src/components/ui/floating-input.tsx +33 -33
- package/registry/floating-input/files/src/lib/kit.tsx +462 -12
- package/registry/floating-input.json +1 -0
- package/registry/food-app.json +1 -0
- package/registry/gift-card.json +1 -0
- package/registry/glass-button.json +5 -0
- package/registry/glass-nav-bar.json +1 -0
- package/registry/glass-search-bar.json +1 -0
- package/registry/glass-segmented.json +1 -0
- package/registry/glass-sheet.json +1 -0
- package/registry/glass-tab-bar.json +5 -0
- package/registry/glass.json +1 -0
- package/registry/grain/files/src/components/ui/grain.tsx +16 -14
- package/registry/grain.json +8 -6
- package/registry/inbox.json +1 -0
- package/registry/isometric-bar.json +1 -0
- package/registry/kit/files/src/lib/kit.tsx +462 -12
- package/registry/kit.json +1 -0
- package/registry/liquidglass-tab/files/src/app/_layout.tsx +0 -1
- package/registry/liquidglass-tab/files/src/components/GlassTabBar.tsx +29 -16
- package/registry/liquidglass-tab/files/src/components/TabScreen.tsx +33 -19
- package/registry/liquidglass-tab/files/src/lib/tab-icons.ts +1 -1
- package/registry/liquidglass-tab.json +1 -0
- package/registry/list-item/files/src/components/ui/list-item.tsx +43 -43
- package/registry/list-item/files/src/lib/kit.tsx +462 -12
- package/registry/list-item.json +1 -0
- package/registry/loyalty-card.json +1 -0
- package/registry/morphing-button-group.json +1 -0
- package/registry/number-ticker.json +1 -0
- package/registry/offers.json +1 -0
- package/registry/onboarding.json +1 -0
- package/registry/orb-reveal.json +1 -0
- package/registry/otp-input/files/src/components/ui/otp-input.tsx +49 -30
- package/registry/otp-input/files/src/lib/kit.tsx +462 -12
- package/registry/otp-input.json +1 -0
- package/registry/parallax-layers.json +1 -0
- package/registry/parallax-scroll.json +1 -0
- package/registry/password-input/files/src/components/ui/floating-input.tsx +33 -33
- package/registry/password-input/files/src/components/ui/password-input.tsx +29 -25
- package/registry/password-input/files/src/lib/kit.tsx +462 -12
- package/registry/password-input.json +1 -0
- package/registry/payment-card/files/src/components/ui/flip-card.tsx +9 -14
- package/registry/payment-card/files/src/components/ui/payment-card.tsx +26 -20
- package/registry/payment-card/files/src/components/ui/tilt-card.tsx +80 -101
- package/registry/payment-card/files/src/lib/kit.tsx +462 -12
- package/registry/payment-card.json +1 -0
- package/registry/paywall.json +23 -0
- package/registry/pixel-bar.json +1 -0
- package/registry/polar-area.json +1 -0
- package/registry/profile.json +1 -0
- package/registry/progress/files/src/components/ui/progress.tsx +31 -29
- package/registry/progress/files/src/lib/kit.tsx +462 -12
- package/registry/progress-steps/files/src/components/ui/progress-steps.tsx +21 -21
- package/registry/progress-steps/files/src/lib/kit.tsx +462 -12
- package/registry/progress-steps.json +1 -0
- package/registry/progress.json +1 -0
- package/registry/qr-code-button.json +1 -0
- package/registry/radio-group/files/src/components/ui/radio-group.tsx +19 -19
- package/registry/radio-group/files/src/lib/kit.tsx +462 -12
- package/registry/radio-group.json +1 -0
- package/registry/rating.json +1 -0
- package/registry/ring-carousel.json +1 -0
- package/registry/ruler-time-picker/files/src/components/ui/ruler-time-picker.tsx +832 -0
- package/registry/ruler-time-picker/files/src/lib/kit.tsx +672 -0
- package/registry/ruler-time-picker.json +26 -0
- package/registry/scratch-card.json +1 -0
- package/registry/search-bar/files/src/components/ui/search-bar.tsx +28 -28
- package/registry/search-bar/files/src/lib/kit.tsx +462 -12
- package/registry/search-bar.json +1 -0
- package/registry/search-results.json +4 -0
- package/registry/segmented-control/files/src/components/ui/segmented-control.tsx +11 -17
- package/registry/segmented-control/files/src/lib/kit.tsx +462 -12
- package/registry/segmented-control.json +1 -0
- package/registry/select/files/src/components/ui/select.tsx +107 -104
- package/registry/select/files/src/lib/kit.tsx +462 -12
- package/registry/select.json +6 -0
- package/registry/settings.json +1 -0
- package/registry/sign-in.json +1 -0
- package/registry/skeleton/files/src/components/ui/skeleton.tsx +149 -80
- package/registry/skeleton/files/src/lib/kit.tsx +462 -12
- package/registry/skeleton.json +1 -0
- package/registry/slide-to-confirm.json +1 -0
- package/registry/slider/files/src/components/ui/slider.tsx +106 -108
- package/registry/slider/files/src/lib/kit.tsx +462 -12
- package/registry/slider.json +5 -0
- package/registry/spin-wheel.json +1 -0
- package/registry/spinner/files/src/components/ui/spinner.tsx +39 -29
- package/registry/spinner/files/src/lib/kit.tsx +462 -12
- package/registry/spinner.json +6 -0
- package/registry/stateful-button/files/src/components/ui/stateful-button.tsx +39 -32
- package/registry/stateful-button/files/src/lib/kit.tsx +462 -12
- package/registry/stateful-button.json +5 -0
- package/registry/stepper/files/src/components/ui/stepper.tsx +40 -38
- package/registry/stepper/files/src/lib/kit.tsx +462 -12
- package/registry/stepper.json +1 -0
- package/registry/swipe-deck.json +1 -0
- package/registry/swipeable-row.json +4 -0
- package/registry/switch/files/src/components/ui/switch.tsx +41 -47
- package/registry/switch/files/src/lib/kit.tsx +462 -12
- package/registry/switch.json +1 -0
- package/registry/tabs/files/src/components/ui/tabs.tsx +61 -64
- package/registry/tabs/files/src/lib/kit.tsx +462 -12
- package/registry/tabs.json +1 -0
- package/registry/text-animation/files/src/components/ui/text-animation.tsx +55 -42
- package/registry/text-animation/files/src/lib/kit.tsx +462 -12
- package/registry/text-animation.json +7 -0
- package/registry/textarea/files/src/components/ui/textarea.tsx +10 -10
- package/registry/textarea/files/src/lib/kit.tsx +462 -12
- package/registry/textarea.json +1 -0
- package/registry/ticket-card.json +1 -0
- package/registry/ticket-shape/files/src/lib/kit.tsx +462 -12
- package/registry/ticket-shape/files/src/lib/ticket-shape.tsx +54 -44
- package/registry/ticket-shape.json +1 -0
- package/registry/tilt-card/files/src/components/ui/tilt-card.tsx +80 -101
- package/registry/tilt-card/files/src/lib/kit.tsx +462 -12
- package/registry/tilt-card.json +1 -0
- package/registry/time-picker/files/src/components/ui/time-picker.tsx +136 -131
- package/registry/time-picker/files/src/lib/kit.tsx +462 -12
- package/registry/time-picker.json +1 -0
- package/registry/timeline/files/src/components/ui/avatar.tsx +89 -74
- package/registry/timeline/files/src/components/ui/timeline.tsx +49 -57
- package/registry/timeline/files/src/lib/kit.tsx +462 -12
- package/registry/timeline-chart.json +1 -0
- package/registry/timeline.json +1 -0
- package/registry/toast/files/src/components/ui/toast.tsx +543 -465
- package/registry/toast/files/src/lib/kit.tsx +462 -12
- package/registry/toast.json +7 -5
- package/registry/treemap.json +1 -0
- package/registry/waterfall.json +1 -0
- package/registry/wheel-list.json +1 -0
- package/schema/components.schema.json +1 -0
- package/registry/alert/files/src/components/ui/button.tsx +0 -303
- package/registry/confetti/files/src/lib/kit.tsx +0 -222
- package/registry/grain/files/src/lib/kit.tsx +0 -222
- package/registry/toast/files/src/components/ui/button.tsx +0 -303
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React, { useCallback, useEffect, useMemo, useRef, useState, type ComponentRef } from 'react';
|
|
1
|
+
import React, { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState, type ComponentRef } from 'react';
|
|
2
2
|
import {
|
|
3
3
|
AccessibilityInfo,
|
|
4
4
|
Animated,
|
|
@@ -7,7 +7,6 @@ import {
|
|
|
7
7
|
Keyboard,
|
|
8
8
|
KeyboardAvoidingView,
|
|
9
9
|
Modal,
|
|
10
|
-
PanResponder,
|
|
11
10
|
Platform,
|
|
12
11
|
Pressable,
|
|
13
12
|
ScrollView,
|
|
@@ -15,14 +14,13 @@ import {
|
|
|
15
14
|
Text,
|
|
16
15
|
TextInput,
|
|
17
16
|
View,
|
|
18
|
-
useWindowDimensions,
|
|
19
17
|
type StyleProp,
|
|
20
18
|
type TextStyle,
|
|
21
19
|
type ViewStyle,
|
|
22
20
|
} from 'react-native';
|
|
23
21
|
import Svg, { Circle, Path } from 'react-native-svg';
|
|
24
22
|
|
|
25
|
-
import { alpha, motion, readableOn, useKit } from '@/lib/kit';
|
|
23
|
+
import { alpha, motion, readableOn, useKit, useLayout, usePanResponder } from '@/lib/kit';
|
|
26
24
|
|
|
27
25
|
export type SelectPresentation = 'dropdown' | 'sheet';
|
|
28
26
|
/** @deprecated Use `SelectPresentation`. */
|
|
@@ -166,12 +164,12 @@ export function Select<T extends string | number = string>({
|
|
|
166
164
|
maxChips = 2,
|
|
167
165
|
label,
|
|
168
166
|
floatingLabel = true,
|
|
169
|
-
placeholder
|
|
167
|
+
placeholder: placeholderProp,
|
|
170
168
|
presentation,
|
|
171
169
|
variant,
|
|
172
170
|
size = 'md',
|
|
173
171
|
searchable,
|
|
174
|
-
searchPlaceholder
|
|
172
|
+
searchPlaceholder: searchPlaceholderProp,
|
|
175
173
|
emptyText,
|
|
176
174
|
title,
|
|
177
175
|
error,
|
|
@@ -193,7 +191,9 @@ export function Select<T extends string | number = string>({
|
|
|
193
191
|
optionStyle,
|
|
194
192
|
optionTextStyle,
|
|
195
193
|
}: SelectProps<T>) {
|
|
196
|
-
const { colors, radius, dark, font } = useKit();
|
|
194
|
+
const { colors, radius, dark, font, shadow, labels } = useKit();
|
|
195
|
+
const placeholder = placeholderProp ?? labels.selectPlaceholder;
|
|
196
|
+
const searchPlaceholder = searchPlaceholderProp ?? labels.search;
|
|
197
197
|
const reduce = useReduceMotion();
|
|
198
198
|
const ff = font.family ? { fontFamily: font.family } : null;
|
|
199
199
|
const tint = accent ?? colors.accent;
|
|
@@ -205,10 +205,12 @@ export function Select<T extends string | number = string>({
|
|
|
205
205
|
const HEIGHT = typeof userField.height === 'number' ? userField.height : sz.h;
|
|
206
206
|
const fieldRadius = typeof userField.borderRadius === 'number' ? userField.borderRadius : radius.scale(12 + sz.corner);
|
|
207
207
|
const panelRadius = Math.max(fieldRadius, radius.scale(14));
|
|
208
|
-
const
|
|
208
|
+
const layout = useLayout();
|
|
209
|
+
const { width: winW, height: winH } = layout;
|
|
209
210
|
const items = useMemo(() => options.map(normalize), [options]);
|
|
210
211
|
const asSheet = (presentation ?? variant ?? 'dropdown') === 'sheet';
|
|
211
|
-
|
|
212
|
+
// A centred card whenever the kit caps the sheet (foldables, tablets, landscape phones).
|
|
213
|
+
const wide = layout.sheetWidth < winW;
|
|
212
214
|
|
|
213
215
|
const [inner, setInner] = useState<T | null>(defaultValue);
|
|
214
216
|
const [innerMany, setInnerMany] = useState<T[]>(defaultValues ?? []);
|
|
@@ -231,17 +233,14 @@ export function Select<T extends string | number = string>({
|
|
|
231
233
|
const viewH = useRef(0);
|
|
232
234
|
const closeTimer = useRef<ReturnType<typeof setTimeout> | null>(null);
|
|
233
235
|
const alive = useRef(true);
|
|
234
|
-
// Read by the sheet's pan responder, which is created once.
|
|
235
|
-
const reduceRef = useRef(reduce);
|
|
236
|
-
reduceRef.current = reduce;
|
|
237
236
|
|
|
238
237
|
const floating = open || picked.length > 0;
|
|
239
|
-
const float =
|
|
240
|
-
const focus =
|
|
241
|
-
const halo =
|
|
242
|
-
const show =
|
|
243
|
-
const drag =
|
|
244
|
-
const shake =
|
|
238
|
+
const [float] = useState(() => new Animated.Value(floating ? 1 : 0)); // native
|
|
239
|
+
const [focus] = useState(() => new Animated.Value(0)); // JS: colours
|
|
240
|
+
const [halo] = useState(() => new Animated.Value(0)); // native: focus halo
|
|
241
|
+
const [show] = useState(() => new Animated.Value(0)); // native: backdrop + sheet/dropdown
|
|
242
|
+
const [drag] = useState(() => new Animated.Value(0)); // native: sheet drag offset
|
|
243
|
+
const [shake] = useState(() => new Animated.Value(0)); // native
|
|
245
244
|
|
|
246
245
|
useEffect(() => {
|
|
247
246
|
alive.current = true;
|
|
@@ -418,7 +417,8 @@ export function Select<T extends string | number = string>({
|
|
|
418
417
|
let i = from < 0 ? (dir > 0 ? -1 : n) : from;
|
|
419
418
|
for (let k = 0; k < n; k++) {
|
|
420
419
|
i = (i + dir + n) % n;
|
|
421
|
-
|
|
420
|
+
const o = flat[i];
|
|
421
|
+
if (o && !o.disabled) {
|
|
422
422
|
setHi(i);
|
|
423
423
|
reveal(i);
|
|
424
424
|
return;
|
|
@@ -428,35 +428,37 @@ export function Select<T extends string | number = string>({
|
|
|
428
428
|
|
|
429
429
|
// Web keyboard: ↑/↓ move, Home/End jump, Enter picks, Escape/Tab close.
|
|
430
430
|
const onKey = useRef<(e: KeyboardEvent) => void>(() => {});
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
step(e.key === 'ArrowDown' ? 1 : -1);
|
|
437
|
-
break;
|
|
438
|
-
case 'Home':
|
|
439
|
-
case 'End':
|
|
440
|
-
if (searchable && query) return; // let the caret move in the search field
|
|
441
|
-
e.preventDefault();
|
|
442
|
-
step(e.key === 'Home' ? 1 : -1, e.key === 'Home' ? -1 : flat.length);
|
|
443
|
-
break;
|
|
444
|
-
case 'Enter':
|
|
445
|
-
if (hi >= 0 && flat[hi]) {
|
|
431
|
+
useLayoutEffect(() => {
|
|
432
|
+
onKey.current = (e) => {
|
|
433
|
+
switch (e.key) {
|
|
434
|
+
case 'ArrowDown':
|
|
435
|
+
case 'ArrowUp':
|
|
446
436
|
e.preventDefault();
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
437
|
+
step(e.key === 'ArrowDown' ? 1 : -1);
|
|
438
|
+
break;
|
|
439
|
+
case 'Home':
|
|
440
|
+
case 'End':
|
|
441
|
+
if (searchable && query) return; // let the caret move in the search field
|
|
442
|
+
e.preventDefault();
|
|
443
|
+
step(e.key === 'Home' ? 1 : -1, e.key === 'Home' ? -1 : flat.length);
|
|
444
|
+
break;
|
|
445
|
+
case 'Enter':
|
|
446
|
+
if (hi >= 0 && flat[hi]) {
|
|
447
|
+
e.preventDefault();
|
|
448
|
+
choose(flat[hi]);
|
|
449
|
+
}
|
|
450
|
+
break;
|
|
451
|
+
case 'Escape':
|
|
452
|
+
e.preventDefault();
|
|
453
|
+
e.stopPropagation();
|
|
454
|
+
close();
|
|
455
|
+
break;
|
|
456
|
+
case 'Tab':
|
|
457
|
+
close();
|
|
458
|
+
break;
|
|
459
|
+
}
|
|
460
|
+
};
|
|
461
|
+
});
|
|
460
462
|
useEffect(() => {
|
|
461
463
|
if (!open || !isWeb || typeof document === 'undefined') return;
|
|
462
464
|
const handler = (e: KeyboardEvent) => onKey.current(e);
|
|
@@ -465,21 +467,19 @@ export function Select<T extends string | number = string>({
|
|
|
465
467
|
}, [open]);
|
|
466
468
|
|
|
467
469
|
// Drag the sheet's grabber down to dismiss.
|
|
468
|
-
const
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
});
|
|
482
|
-
}, [drag]);
|
|
470
|
+
const back = () =>
|
|
471
|
+
reduce
|
|
472
|
+
? Animated.timing(drag, { toValue: 0, duration: 150, easing: Easing.bezier(0.32, 0.72, 0, 1), useNativeDriver: true }).start()
|
|
473
|
+
: Animated.spring(drag, { toValue: 0, useNativeDriver: true, ...motion.snappy }).start();
|
|
474
|
+
const pan = usePanResponder({
|
|
475
|
+
onMoveShouldSetPanResponder: (_, g) => g.dy > 4 && Math.abs(g.dy) > Math.abs(g.dx),
|
|
476
|
+
onPanResponderMove: (_, g) => drag.setValue(Math.max(0, g.dy)),
|
|
477
|
+
onPanResponderRelease: (_, g) => {
|
|
478
|
+
if (g.dy > 110 || g.vy > 1.1) setOpen(false);
|
|
479
|
+
else back();
|
|
480
|
+
},
|
|
481
|
+
onPanResponderTerminate: back,
|
|
482
|
+
});
|
|
483
483
|
|
|
484
484
|
const active = error ? colors.danger : tint;
|
|
485
485
|
const borderColor = focus.interpolate({
|
|
@@ -523,7 +523,7 @@ export function Select<T extends string | number = string>({
|
|
|
523
523
|
onPress={() => onSearch('')}
|
|
524
524
|
hitSlop={12}
|
|
525
525
|
accessibilityRole="button"
|
|
526
|
-
accessibilityLabel=
|
|
526
|
+
accessibilityLabel={labels.clearSearch}
|
|
527
527
|
style={[styles.clear, { backgroundColor: alpha(colors.mutedForeground, 0.22) }]}
|
|
528
528
|
>
|
|
529
529
|
<Icon name="x" size={11} color={colors.foreground} strokeWidth={2.4} />
|
|
@@ -587,11 +587,11 @@ export function Select<T extends string | number = string>({
|
|
|
587
587
|
<Icon name="search" size={dense ? 16 : 20} color={colors.mutedForeground} />
|
|
588
588
|
</View>
|
|
589
589
|
<Text style={[styles.emptyTitle, ff, { color: colors.foreground, fontSize: dense ? 14 : 15 }]}>
|
|
590
|
-
{q ?
|
|
590
|
+
{q ? labels.noResults : labels.noOptions}
|
|
591
591
|
</Text>
|
|
592
592
|
{q ? (
|
|
593
593
|
<Text style={[styles.emptyText, ff, { color: colors.mutedForeground }]}>
|
|
594
|
-
{emptyText ??
|
|
594
|
+
{emptyText ?? labels.nothingMatches(query.trim())}
|
|
595
595
|
</Text>
|
|
596
596
|
) : null}
|
|
597
597
|
</View>
|
|
@@ -610,7 +610,7 @@ export function Select<T extends string | number = string>({
|
|
|
610
610
|
]}
|
|
611
611
|
>
|
|
612
612
|
<Text maxFontSizeMultiplier={FONT_CAP} style={[styles.footDenseText, ff, { color: colors.mutedForeground }]}>
|
|
613
|
-
|
|
613
|
+
{labels.clear}
|
|
614
614
|
</Text>
|
|
615
615
|
</Pressable>
|
|
616
616
|
<View style={styles.footMid}>
|
|
@@ -622,7 +622,7 @@ export function Select<T extends string | number = string>({
|
|
|
622
622
|
style={({ pressed }) => [styles.footSolid, { backgroundColor: tint, borderRadius: radius.capsule(32), opacity: pressed ? 0.85 : 1 }]}
|
|
623
623
|
>
|
|
624
624
|
<Text maxFontSizeMultiplier={FONT_CAP} style={[styles.footDenseText, styles.footStrong, ff, { color: readableOn(tint) }]}>
|
|
625
|
-
|
|
625
|
+
{labels.done}
|
|
626
626
|
</Text>
|
|
627
627
|
</Pressable>
|
|
628
628
|
</View>
|
|
@@ -635,7 +635,7 @@ export function Select<T extends string | number = string>({
|
|
|
635
635
|
style={({ pressed }) => [styles.footBtn, { borderRadius: radius.md, opacity: many.length ? (pressed ? 0.6 : 1) : 0.4 }]}
|
|
636
636
|
>
|
|
637
637
|
<Text maxFontSizeMultiplier={FONT_CAP} style={[styles.footText, ff, { color: colors.foreground }]}>
|
|
638
|
-
|
|
638
|
+
{labels.clear}
|
|
639
639
|
</Text>
|
|
640
640
|
</Pressable>
|
|
641
641
|
<Pressable
|
|
@@ -644,7 +644,7 @@ export function Select<T extends string | number = string>({
|
|
|
644
644
|
style={({ pressed }) => [styles.footBtn, styles.footMain, { backgroundColor: tint, borderRadius: radius.md, opacity: pressed ? 0.85 : 1 }]}
|
|
645
645
|
>
|
|
646
646
|
<Text maxFontSizeMultiplier={FONT_CAP} style={[styles.footText, styles.footStrong, styles.tabular, ff, { color: readableOn(tint) }]}>
|
|
647
|
-
{many.length ?
|
|
647
|
+
{many.length ? labels.doneCount(many.length) : labels.done}
|
|
648
648
|
</Text>
|
|
649
649
|
</Pressable>
|
|
650
650
|
</View>
|
|
@@ -701,7 +701,7 @@ export function Select<T extends string | number = string>({
|
|
|
701
701
|
disabled={disabled}
|
|
702
702
|
accessibilityRole="combobox"
|
|
703
703
|
accessibilityLabel={label}
|
|
704
|
-
accessibilityValue={{ text: picked.length ? picked.map((o) => o.label).join(', ') :
|
|
704
|
+
accessibilityValue={{ text: picked.length ? picked.map((o) => o.label).join(', ') : labels.noneSelected }}
|
|
705
705
|
accessibilityState={{ expanded: open, disabled: !!disabled }}
|
|
706
706
|
style={{ opacity: disabled ? 0.5 : 1 }}
|
|
707
707
|
>
|
|
@@ -758,7 +758,7 @@ export function Select<T extends string | number = string>({
|
|
|
758
758
|
onPress={() => item.option && remove(item.option.value)}
|
|
759
759
|
hitSlop={12}
|
|
760
760
|
accessibilityRole="button"
|
|
761
|
-
accessibilityLabel={
|
|
761
|
+
accessibilityLabel={labels.remove(item.option.label)}
|
|
762
762
|
style={({ pressed }) => [
|
|
763
763
|
styles.chipX,
|
|
764
764
|
{
|
|
@@ -776,7 +776,7 @@ export function Select<T extends string | number = string>({
|
|
|
776
776
|
) : (
|
|
777
777
|
<View
|
|
778
778
|
accessible
|
|
779
|
-
accessibilityLabel={maxChips > 0 ?
|
|
779
|
+
accessibilityLabel={maxChips > 0 ? labels.moreCount(item.more) : labels.selectedCount(item.more)}
|
|
780
780
|
style={[styles.chip, styles.more, { minHeight: sz.chip, backgroundColor: chipBg, borderRadius: radius.capsule(sz.chip) }]}
|
|
781
781
|
>
|
|
782
782
|
{maxChips > 0 ? (
|
|
@@ -787,7 +787,7 @@ export function Select<T extends string | number = string>({
|
|
|
787
787
|
<RollingNumber value={item.more} distance={sz.chipFont} reduce={reduce} style={[styles.chipText, ff, { color: tint, fontSize: sz.chipFont }]} />
|
|
788
788
|
{maxChips > 0 ? null : (
|
|
789
789
|
<Text maxFontSizeMultiplier={FONT_CAP} style={[styles.chipText, ff, { color: tint, fontSize: sz.chipFont, marginLeft: 3 }]}>
|
|
790
|
-
|
|
790
|
+
{labels.selectedSuffix}
|
|
791
791
|
</Text>
|
|
792
792
|
)}
|
|
793
793
|
</View>
|
|
@@ -851,7 +851,7 @@ export function Select<T extends string | number = string>({
|
|
|
851
851
|
|
|
852
852
|
<Modal visible={mounted} transparent animationType="none" statusBarTranslucent onRequestClose={close}>
|
|
853
853
|
<Animated.View style={[StyleSheet.absoluteFill, { backgroundColor: asSheet ? colors.overlay : 'transparent', opacity: show }]}>
|
|
854
|
-
<Pressable style={StyleSheet.absoluteFill} onPress={close} accessibilityLabel=
|
|
854
|
+
<Pressable style={StyleSheet.absoluteFill} onPress={close} accessibilityLabel={labels.close} accessibilityRole="button" />
|
|
855
855
|
</Animated.View>
|
|
856
856
|
|
|
857
857
|
{asSheet ? (
|
|
@@ -866,8 +866,14 @@ export function Select<T extends string | number = string>({
|
|
|
866
866
|
styles.sheet,
|
|
867
867
|
{ backgroundColor: colors.popover, maxHeight: winH * (wide ? 0.7 : 0.78), borderColor: colors.border },
|
|
868
868
|
wide
|
|
869
|
-
? { width: Math.min(460,
|
|
870
|
-
: {
|
|
869
|
+
? { width: Math.min(460, layout.sheetWidth), borderRadius: radius.xl, paddingBottom: 8, ...shadow('xl') }
|
|
870
|
+
: {
|
|
871
|
+
alignSelf: 'stretch',
|
|
872
|
+
borderTopLeftRadius: radius.xl + 4,
|
|
873
|
+
borderTopRightRadius: radius.xl + 4,
|
|
874
|
+
paddingBottom: 28,
|
|
875
|
+
boxShadow: `0px -4px 32px rgba(0,0,0,${dark ? 0.45 : 0.18})`,
|
|
876
|
+
},
|
|
871
877
|
menuStyle,
|
|
872
878
|
wide
|
|
873
879
|
? {
|
|
@@ -884,7 +890,7 @@ export function Select<T extends string | number = string>({
|
|
|
884
890
|
},
|
|
885
891
|
]}
|
|
886
892
|
>
|
|
887
|
-
<View {...pan
|
|
893
|
+
<View {...pan} style={styles.sheetHeader}>
|
|
888
894
|
{!wide ? <View style={[styles.grabber, { backgroundColor: colors.border }]} /> : null}
|
|
889
895
|
<Text style={[styles.title, ff, { color: colors.foreground }]}>{title ?? label}</Text>
|
|
890
896
|
{multiple ? (
|
|
@@ -892,7 +898,7 @@ export function Select<T extends string | number = string>({
|
|
|
892
898
|
{many.length ? (
|
|
893
899
|
<CountBadge count={many.length} tint={tint} reduce={reduce} />
|
|
894
900
|
) : (
|
|
895
|
-
<Text style={[styles.subtitle, ff, { color: colors.mutedForeground }]}>
|
|
901
|
+
<Text style={[styles.subtitle, ff, { color: colors.mutedForeground }]}>{labels.chooseOneOrMore}</Text>
|
|
896
902
|
)}
|
|
897
903
|
</View>
|
|
898
904
|
) : null}
|
|
@@ -912,7 +918,7 @@ export function Select<T extends string | number = string>({
|
|
|
912
918
|
backgroundColor: colors.popover,
|
|
913
919
|
borderColor: colors.border,
|
|
914
920
|
borderRadius: panelRadius,
|
|
915
|
-
|
|
921
|
+
...shadow('lg'),
|
|
916
922
|
transformOrigin: below ? 'top' : 'bottom',
|
|
917
923
|
},
|
|
918
924
|
menuStyle,
|
|
@@ -935,7 +941,7 @@ export function Select<T extends string | number = string>({
|
|
|
935
941
|
}
|
|
936
942
|
|
|
937
943
|
/** Groups options by `group` (first-appearance order); a single untitled section when none are grouped. */
|
|
938
|
-
function sectionsOf<T extends string | number>(list: SelectOption<T>[]): { title?: string; rows: SelectOption<T>[] }[] {
|
|
944
|
+
function sectionsOf<T extends string | number>(list: SelectOption<T>[]): { title?: string | undefined; rows: SelectOption<T>[] }[] {
|
|
939
945
|
if (!list.some((o) => o.group)) return [{ rows: list }];
|
|
940
946
|
const map = new Map<string, SelectOption<T>[]>();
|
|
941
947
|
list.forEach((o) => map.set(o.group ?? '', [...(map.get(o.group ?? '') ?? []), o]));
|
|
@@ -975,7 +981,7 @@ function OptionRow<T extends string | number>({
|
|
|
975
981
|
}) {
|
|
976
982
|
const { colors, radius, font, dark } = useKit();
|
|
977
983
|
const ff = font.family ? { fontFamily: font.family } : null;
|
|
978
|
-
const check =
|
|
984
|
+
const [check] = useState(() => new Animated.Value(selected ? 1 : 0)); // native: scale + opacity
|
|
979
985
|
useEffect(() => {
|
|
980
986
|
// Reduce Motion: the check only fades (no pop / twist).
|
|
981
987
|
if (reduce) Animated.timing(check, { toValue: selected ? 1 : 0, duration: 150, useNativeDriver: true }).start();
|
|
@@ -1075,7 +1081,10 @@ function usePresence<V>(items: { key: string; item: V }[]) {
|
|
|
1075
1081
|
const out: Present<V>[] = [];
|
|
1076
1082
|
let next = 0;
|
|
1077
1083
|
const emitUpTo = (end: number) => {
|
|
1078
|
-
for (; next <= end; next++)
|
|
1084
|
+
for (; next <= end; next++) {
|
|
1085
|
+
const e = items[next];
|
|
1086
|
+
if (e) out.push({ ...e, leaving: false, appear: ready.current });
|
|
1087
|
+
}
|
|
1079
1088
|
};
|
|
1080
1089
|
for (const e of prev.current) {
|
|
1081
1090
|
if (live.has(e.key)) emitUpTo(items.findIndex((x) => x.key === e.key));
|
|
@@ -1114,11 +1123,13 @@ function AnimatedChip({
|
|
|
1114
1123
|
onGone: () => void;
|
|
1115
1124
|
children: React.ReactNode;
|
|
1116
1125
|
}) {
|
|
1117
|
-
const pop =
|
|
1118
|
-
const size =
|
|
1126
|
+
const [pop] = useState(() => new Animated.Value(appear ? 0 : 1)); // native: scale + opacity
|
|
1127
|
+
const [size] = useState(() => new Animated.Value(1)); // JS: width + margin of the outer slot
|
|
1119
1128
|
const [w, setW] = useState(0);
|
|
1120
1129
|
const gone = useRef(onGone);
|
|
1121
|
-
|
|
1130
|
+
useLayoutEffect(() => {
|
|
1131
|
+
gone.current = onGone;
|
|
1132
|
+
});
|
|
1122
1133
|
|
|
1123
1134
|
useEffect(() => {
|
|
1124
1135
|
let alive = true;
|
|
@@ -1231,12 +1242,13 @@ function RollingNumber({ value, style, distance = 10, reduce }: { value: number;
|
|
|
1231
1242
|
|
|
1232
1243
|
/** "N selected" pill whose number rolls; it pops out when the count drops to 0. */
|
|
1233
1244
|
function CountBadge({ count, tint, reduce }: { count: number; tint: string; reduce: boolean }) {
|
|
1234
|
-
const { radius, font } = useKit();
|
|
1245
|
+
const { radius, font, labels } = useKit();
|
|
1235
1246
|
const ff = font.family ? { fontFamily: font.family } : null;
|
|
1236
1247
|
const on = count > 0;
|
|
1237
|
-
const vis =
|
|
1238
|
-
|
|
1239
|
-
|
|
1248
|
+
const [vis] = useState(() => new Animated.Value(on ? 1 : 0)); // native
|
|
1249
|
+
// The last non-zero count, kept on screen while the badge pops out at 0.
|
|
1250
|
+
const [last, setLast] = useState(count);
|
|
1251
|
+
if (on && count !== last) setLast(count);
|
|
1240
1252
|
useEffect(() => {
|
|
1241
1253
|
const a = reduce
|
|
1242
1254
|
? Animated.timing(vis, { toValue: on ? 1 : 0, duration: 150, useNativeDriver: true })
|
|
@@ -1248,7 +1260,7 @@ function CountBadge({ count, tint, reduce }: { count: number; tint: string; redu
|
|
|
1248
1260
|
return (
|
|
1249
1261
|
<Animated.View
|
|
1250
1262
|
accessible
|
|
1251
|
-
accessibilityLabel={
|
|
1263
|
+
accessibilityLabel={labels.selectedCount(last)}
|
|
1252
1264
|
accessibilityLiveRegion="polite"
|
|
1253
1265
|
style={[
|
|
1254
1266
|
styles.badge,
|
|
@@ -1260,9 +1272,9 @@ function CountBadge({ count, tint, reduce }: { count: number; tint: string; redu
|
|
|
1260
1272
|
},
|
|
1261
1273
|
]}
|
|
1262
1274
|
>
|
|
1263
|
-
<RollingNumber value={last
|
|
1275
|
+
<RollingNumber value={last} distance={9} reduce={reduce} style={text} />
|
|
1264
1276
|
<Text maxFontSizeMultiplier={FONT_CAP} style={[text, styles.badgeSuffix]}>
|
|
1265
|
-
|
|
1277
|
+
{labels.selectedSuffix}
|
|
1266
1278
|
</Text>
|
|
1267
1279
|
</Animated.View>
|
|
1268
1280
|
);
|
|
@@ -1290,7 +1302,7 @@ function Highlight({ text, query, tint, style }: { text: string; query: string;
|
|
|
1290
1302
|
/** Helper / error line that fades and slides in; errors get an icon and are announced. */
|
|
1291
1303
|
function FieldMessage({ text, error, reduce, style }: { text: string; error: boolean; reduce: boolean; style?: StyleProp<TextStyle> }) {
|
|
1292
1304
|
const { colors, font } = useKit();
|
|
1293
|
-
const enter =
|
|
1305
|
+
const [enter] = useState(() => new Animated.Value(0)); // native
|
|
1294
1306
|
useEffect(() => {
|
|
1295
1307
|
enter.setValue(0);
|
|
1296
1308
|
if (reduce) Animated.timing(enter, { toValue: 1, duration: 150, useNativeDriver: true }).start();
|
|
@@ -1312,7 +1324,7 @@ function FieldMessage({ text, error, reduce, style }: { text: string; error: boo
|
|
|
1312
1324
|
}
|
|
1313
1325
|
|
|
1314
1326
|
function Chevron({ open, reduce, color, size, right }: { open: boolean; reduce: boolean; color: string; size: number; right: number }) {
|
|
1315
|
-
const turn =
|
|
1327
|
+
const [turn] = useState(() => new Animated.Value(open ? 1 : 0));
|
|
1316
1328
|
useEffect(() => {
|
|
1317
1329
|
if (reduce) turn.setValue(open ? 1 : 0);
|
|
1318
1330
|
else Animated.spring(turn, { toValue: open ? 1 : 0, useNativeDriver: true, ...motion.snappy }).start();
|
|
@@ -1379,7 +1391,7 @@ const styles = StyleSheet.create({
|
|
|
1379
1391
|
chip: { flexDirection: 'row', alignItems: 'center', gap: 4, maxWidth: 150, flexShrink: 1 },
|
|
1380
1392
|
more: { gap: 0, paddingHorizontal: 9, flexShrink: 0 },
|
|
1381
1393
|
chipIcon: { overflow: 'hidden', alignItems: 'center', justifyContent: 'center' },
|
|
1382
|
-
chipText: { fontWeight: '400', flexShrink: 1 },
|
|
1394
|
+
chipText: { fontWeight: '400', flexShrink: 1, includeFontPadding: false, textAlignVertical: 'center' },
|
|
1383
1395
|
tabular: { fontVariant: ['tabular-nums'], fontWeight: '400' },
|
|
1384
1396
|
roll: { overflow: 'hidden' },
|
|
1385
1397
|
rollPrev: { position: 'absolute', left: 0, top: 0 },
|
|
@@ -1398,11 +1410,6 @@ const styles = StyleSheet.create({
|
|
|
1398
1410
|
sheet: {
|
|
1399
1411
|
paddingHorizontal: 12,
|
|
1400
1412
|
borderWidth: StyleSheet.hairlineWidth,
|
|
1401
|
-
shadowColor: '#000',
|
|
1402
|
-
shadowOpacity: 0.18,
|
|
1403
|
-
shadowRadius: 24,
|
|
1404
|
-
shadowOffset: { width: 0, height: -4 },
|
|
1405
|
-
elevation: 16,
|
|
1406
1413
|
},
|
|
1407
1414
|
sheetHeader: { alignItems: 'center', paddingTop: 10, paddingBottom: 12 },
|
|
1408
1415
|
grabber: { width: 40, height: 5, borderRadius: 3, marginBottom: 12 },
|
|
@@ -1453,10 +1460,6 @@ const styles = StyleSheet.create({
|
|
|
1453
1460
|
dropdown: {
|
|
1454
1461
|
position: 'absolute',
|
|
1455
1462
|
borderWidth: StyleSheet.hairlineWidth,
|
|
1456
|
-
shadowColor: '#000',
|
|
1457
|
-
shadowRadius: 28,
|
|
1458
|
-
shadowOffset: { width: 0, height: 10 },
|
|
1459
|
-
elevation: 10,
|
|
1460
1463
|
overflow: 'hidden',
|
|
1461
1464
|
},
|
|
1462
1465
|
row: { minHeight: 52, flexDirection: 'row', alignItems: 'center', paddingHorizontal: 14, gap: 12 },
|