@expokit/cli 0.4.4 → 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 +310 -3
- 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/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/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/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 +363 -11
- package/registry/accordion.json +1 -0
- package/registry/action-sheet/files/src/components/ui/action-sheet.tsx +43 -48
- package/registry/action-sheet/files/src/lib/kit.tsx +363 -11
- package/registry/action-sheet.json +1 -0
- package/registry/alert/files/src/components/ui/alert.tsx +19 -12
- package/registry/alert/files/src/lib/kit.tsx +363 -11
- package/registry/alert.json +3 -4
- package/registry/assistant-app.json +1 -0
- package/registry/avatar/files/src/components/ui/avatar.tsx +84 -66
- package/registry/avatar/files/src/lib/kit.tsx +363 -11
- package/registry/avatar.json +1 -0
- package/registry/badge/files/src/components/ui/badge.tsx +34 -34
- package/registry/badge/files/src/lib/kit.tsx +363 -11
- package/registry/badge.json +1 -0
- package/registry/bottom-nav/files/src/components/ui/bottom-nav.tsx +28 -27
- package/registry/bottom-nav/files/src/lib/kit.tsx +363 -11
- package/registry/bottom-nav.json +5 -0
- package/registry/bottom-sheet/files/src/components/ui/bottom-sheet.tsx +353 -289
- package/registry/bottom-sheet/files/src/lib/kit.tsx +363 -11
- package/registry/bottom-sheet.json +5 -1
- package/registry/bullet.json +1 -0
- package/registry/button/files/src/components/ui/button.tsx +18 -16
- package/registry/button/files/src/lib/kit.tsx +363 -11
- package/registry/button-3d/files/src/components/ui/button-3d.tsx +2 -2
- package/registry/button-3d/files/src/lib/kit.tsx +363 -11
- package/registry/button-3d.json +1 -0
- package/registry/button.json +1 -0
- package/registry/calendar/files/src/components/ui/calendar.tsx +74 -76
- package/registry/calendar/files/src/lib/kit.tsx +363 -11
- package/registry/calendar.json +1 -0
- package/registry/card/files/src/components/ui/card.tsx +3 -3
- package/registry/card/files/src/lib/kit.tsx +363 -11
- 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 +363 -11
- package/registry/checkbox.json +1 -0
- package/registry/checkout.json +1 -0
- package/registry/chip-group/files/src/components/ui/chip-group.tsx +22 -22
- package/registry/chip-group/files/src/lib/kit.tsx +363 -11
- 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 +42 -42
- package/registry/coupon/files/src/components/ui/flip-card.tsx +6 -5
- package/registry/coupon/files/src/components/ui/tilt-card.tsx +76 -91
- package/registry/coupon/files/src/lib/kit.tsx +363 -11
- 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 +74 -76
- package/registry/date-picker/files/src/components/ui/date-picker.tsx +55 -56
- package/registry/date-picker/files/src/lib/kit.tsx +363 -11
- package/registry/date-picker.json +1 -0
- package/registry/dialog/files/src/components/ui/dialog.tsx +60 -56
- package/registry/dialog/files/src/lib/kit.tsx +363 -11
- package/registry/dialog.json +1 -0
- package/registry/donut-shop.json +1 -0
- package/registry/drawer/files/src/components/ui/drawer.tsx +73 -69
- package/registry/drawer/files/src/lib/kit.tsx +363 -11
- 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 +92 -86
- package/registry/flip-calendar/files/src/lib/kit.tsx +363 -11
- package/registry/flip-calendar.json +7 -0
- package/registry/flip-card/files/src/components/ui/flip-card.tsx +6 -5
- package/registry/flip-card/files/src/lib/kit.tsx +363 -11
- 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 +363 -11
- 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 +363 -11
- 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 +23 -9
- package/registry/liquidglass-tab/files/src/components/TabScreen.tsx +2 -2
- 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 +42 -42
- package/registry/list-item/files/src/lib/kit.tsx +363 -11
- 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 +48 -29
- package/registry/otp-input/files/src/lib/kit.tsx +363 -11
- 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 +363 -11
- package/registry/password-input.json +1 -0
- package/registry/payment-card/files/src/components/ui/flip-card.tsx +6 -5
- package/registry/payment-card/files/src/components/ui/payment-card.tsx +17 -17
- package/registry/payment-card/files/src/components/ui/tilt-card.tsx +76 -91
- package/registry/payment-card/files/src/lib/kit.tsx +363 -11
- package/registry/payment-card.json +1 -0
- package/registry/paywall.json +1 -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 +29 -29
- package/registry/progress/files/src/lib/kit.tsx +363 -11
- package/registry/progress-steps/files/src/components/ui/progress-steps.tsx +20 -20
- package/registry/progress-steps/files/src/lib/kit.tsx +363 -11
- 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 +363 -11
- 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 +98 -82
- package/registry/ruler-time-picker/files/src/lib/kit.tsx +363 -11
- package/registry/ruler-time-picker.json +7 -0
- package/registry/scratch-card.json +1 -0
- package/registry/search-bar/files/src/components/ui/search-bar.tsx +25 -21
- package/registry/search-bar/files/src/lib/kit.tsx +363 -11
- 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 +8 -7
- package/registry/segmented-control/files/src/lib/kit.tsx +363 -11
- package/registry/segmented-control.json +1 -0
- package/registry/select/files/src/components/ui/select.tsx +93 -88
- package/registry/select/files/src/lib/kit.tsx +363 -11
- 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 +363 -11
- package/registry/skeleton.json +1 -0
- package/registry/slide-to-confirm.json +1 -0
- package/registry/slider/files/src/components/ui/slider.tsx +103 -96
- package/registry/slider/files/src/lib/kit.tsx +363 -11
- 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 +363 -11
- 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 +363 -11
- package/registry/stateful-button.json +5 -0
- package/registry/stepper/files/src/components/ui/stepper.tsx +38 -36
- package/registry/stepper/files/src/lib/kit.tsx +363 -11
- 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 +39 -42
- package/registry/switch/files/src/lib/kit.tsx +363 -11
- package/registry/switch.json +1 -0
- package/registry/tabs/files/src/components/ui/tabs.tsx +59 -62
- package/registry/tabs/files/src/lib/kit.tsx +363 -11
- 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 +363 -11
- 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 +363 -11
- package/registry/textarea.json +1 -0
- package/registry/ticket-card.json +1 -0
- package/registry/ticket-shape/files/src/lib/kit.tsx +363 -11
- 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 +76 -91
- package/registry/tilt-card/files/src/lib/kit.tsx +363 -11
- package/registry/tilt-card.json +1 -0
- package/registry/time-picker/files/src/components/ui/time-picker.tsx +122 -119
- package/registry/time-picker/files/src/lib/kit.tsx +363 -11
- package/registry/time-picker.json +1 -0
- package/registry/timeline/files/src/components/ui/avatar.tsx +84 -66
- package/registry/timeline/files/src/components/ui/timeline.tsx +44 -41
- package/registry/timeline/files/src/lib/kit.tsx +363 -11
- package/registry/timeline-chart.json +1 -0
- package/registry/timeline.json +1 -0
- package/registry/toast/files/src/components/ui/toast.tsx +541 -463
- package/registry/toast/files/src/lib/kit.tsx +363 -11
- 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 -290
- package/registry/confetti/files/src/lib/kit.tsx +0 -320
- package/registry/grain/files/src/lib/kit.tsx +0 -320
- package/registry/toast/files/src/components/ui/button.tsx +0 -290
|
@@ -1,5 +1,14 @@
|
|
|
1
|
-
import React, { createContext, useContext, useMemo } from 'react';
|
|
2
|
-
import {
|
|
1
|
+
import React, { createContext, useContext, useLayoutEffect, useMemo, useRef, useState } from 'react';
|
|
2
|
+
import {
|
|
3
|
+
PanResponder,
|
|
4
|
+
Platform,
|
|
5
|
+
useColorScheme,
|
|
6
|
+
useWindowDimensions,
|
|
7
|
+
type GestureResponderHandlers,
|
|
8
|
+
type PanResponderCallbacks,
|
|
9
|
+
type TextStyle,
|
|
10
|
+
type ViewStyle,
|
|
11
|
+
} from 'react-native';
|
|
3
12
|
|
|
4
13
|
/**
|
|
5
14
|
* ExpoKit design tokens. Every component reads them through useKit(), so one
|
|
@@ -9,9 +18,9 @@ import { Platform, useColorScheme, useWindowDimensions, type TextStyle, type Vie
|
|
|
9
18
|
|
|
10
19
|
/**
|
|
11
20
|
* Bumped whenever components start using a new kit API (2: radius.scale / radius.capsule,
|
|
12
|
-
* 3: useLayout / shadow). The CLI won't keep an edited kit.tsx older than the component being added needs.
|
|
21
|
+
* 3: useLayout / shadow, 4: labels, 5: visible labels such as Cancel / Done / Today, AM / PM, usePanResponder / useLatest). The CLI won't keep an edited kit.tsx older than the component being added needs.
|
|
13
22
|
*/
|
|
14
|
-
export const KIT_VERSION =
|
|
23
|
+
export const KIT_VERSION = 5;
|
|
15
24
|
|
|
16
25
|
export type ColorScheme = 'light' | 'dark';
|
|
17
26
|
|
|
@@ -60,10 +69,298 @@ export interface Theme {
|
|
|
60
69
|
space: (n: number) => number;
|
|
61
70
|
/** The same soft shadow on iOS, Android and web: `style={[styles.card, shadow('md')]}`. */
|
|
62
71
|
shadow: (level: ShadowLevel, color?: string) => ViewStyle;
|
|
63
|
-
font: { family?: string; mono: string };
|
|
72
|
+
font: { family?: string | undefined; mono: string };
|
|
64
73
|
text: Record<'xs' | 'sm' | 'md' | 'lg' | 'xl' | 'xxl', TextStyle>;
|
|
74
|
+
/** Text the kit shows or speaks on its own (buttons, placeholders, screen-reader names). Translate with <KitProvider labels={...}>. */
|
|
75
|
+
labels: Labels;
|
|
65
76
|
}
|
|
66
77
|
|
|
78
|
+
/**
|
|
79
|
+
* Every string the kit shows or speaks on its own: button text and placeholders (Cancel, Done, Today,
|
|
80
|
+
* Search…) and what screen readers say (close buttons, grabbers, hints). A prop on a component, like
|
|
81
|
+
* `cancelLabel` or `placeholder`, still wins over these.
|
|
82
|
+
* Pass a partial map to <KitProvider labels={...}>; keys you leave out stay English.
|
|
83
|
+
* Functions get the dynamic part, so word order can change per language.
|
|
84
|
+
*/
|
|
85
|
+
export interface Labels {
|
|
86
|
+
close: string;
|
|
87
|
+
dismiss: string;
|
|
88
|
+
closeSheet: string;
|
|
89
|
+
sheetGrabber: string;
|
|
90
|
+
sheetResizeHint: string;
|
|
91
|
+
sheetResizeOrCloseHint: string;
|
|
92
|
+
navigationDrawer: string;
|
|
93
|
+
closeDrawer: string;
|
|
94
|
+
closeMenu: string;
|
|
95
|
+
switchAccount: string;
|
|
96
|
+
openActions: string;
|
|
97
|
+
closeActions: string;
|
|
98
|
+
action: string;
|
|
99
|
+
loading: string;
|
|
100
|
+
clearSearch: string;
|
|
101
|
+
cancelSearch: string;
|
|
102
|
+
clearField: (field: string) => string;
|
|
103
|
+
back: string;
|
|
104
|
+
previous: string;
|
|
105
|
+
next: string;
|
|
106
|
+
goToToday: string;
|
|
107
|
+
goToSlide: (n: number) => string;
|
|
108
|
+
showAllCards: string;
|
|
109
|
+
expandNotification: string;
|
|
110
|
+
collapseNotification: string;
|
|
111
|
+
moreCount: (n: number) => string;
|
|
112
|
+
peopleCount: (n: number) => string;
|
|
113
|
+
namesAndMore: (names: string, n: number) => string;
|
|
114
|
+
newCount: (badge: string) => string;
|
|
115
|
+
remove: (item: string) => string;
|
|
116
|
+
selectedCount: (n: number) => string;
|
|
117
|
+
noneSelected: string;
|
|
118
|
+
notSet: string;
|
|
119
|
+
date: string;
|
|
120
|
+
time: string;
|
|
121
|
+
hour: string;
|
|
122
|
+
minute: string;
|
|
123
|
+
hours: (n: string) => string;
|
|
124
|
+
minutes: (n: number) => string;
|
|
125
|
+
booked: string;
|
|
126
|
+
unavailable: string;
|
|
127
|
+
timeDialHint: string;
|
|
128
|
+
flipCalendarHint: string;
|
|
129
|
+
increase: string;
|
|
130
|
+
decrease: string;
|
|
131
|
+
increaseItem: (item: string) => string;
|
|
132
|
+
decreaseItem: (item: string) => string;
|
|
133
|
+
range: string;
|
|
134
|
+
rangeMinimum: (name: string) => string;
|
|
135
|
+
rangeMaximum: (name: string) => string;
|
|
136
|
+
ratingStars: string;
|
|
137
|
+
ratingHearts: string;
|
|
138
|
+
showPassword: string;
|
|
139
|
+
hidePassword: string;
|
|
140
|
+
passwordStrength: (level: string) => string;
|
|
141
|
+
ruleMet: (rule: string) => string;
|
|
142
|
+
ruleUnmet: (rule: string) => string;
|
|
143
|
+
stepOf: (step: number, total: number, label: string) => string;
|
|
144
|
+
allStepsComplete: string;
|
|
145
|
+
stepCurrent: string;
|
|
146
|
+
stepCompleted: string;
|
|
147
|
+
stepNeedsAttention: string;
|
|
148
|
+
stepNotStarted: string;
|
|
149
|
+
goBackToStep: string;
|
|
150
|
+
activateCodeField: string;
|
|
151
|
+
qrCodeFor: (value: string) => string;
|
|
152
|
+
undoStroke: string;
|
|
153
|
+
signaturePad: string;
|
|
154
|
+
signaturePadSigned: string;
|
|
155
|
+
flipToBack: string;
|
|
156
|
+
flipToFront: string;
|
|
157
|
+
revealHint: string;
|
|
158
|
+
confirmHint: string;
|
|
159
|
+
prizeWheel: string;
|
|
160
|
+
spinHint: string;
|
|
161
|
+
prizes: (n: number, list: string) => string;
|
|
162
|
+
orbReveal: string;
|
|
163
|
+
orbBack: string;
|
|
164
|
+
// Visible text
|
|
165
|
+
cancel: string;
|
|
166
|
+
done: string;
|
|
167
|
+
doneCount: (n: number) => string;
|
|
168
|
+
doneDays: (n: number) => string;
|
|
169
|
+
clear: string;
|
|
170
|
+
continue: string;
|
|
171
|
+
delete: string;
|
|
172
|
+
failed: string;
|
|
173
|
+
search: string;
|
|
174
|
+
noResults: string;
|
|
175
|
+
noOptions: string;
|
|
176
|
+
nothingMatches: (query: string) => string;
|
|
177
|
+
selectPlaceholder: string;
|
|
178
|
+
selectDate: string;
|
|
179
|
+
selectDates: string;
|
|
180
|
+
pickStartDate: string;
|
|
181
|
+
pickEndDate: string;
|
|
182
|
+
selectTime: string;
|
|
183
|
+
setTime: (time: string) => string;
|
|
184
|
+
/** Clock periods, shown on 12-hour pickers and times. */
|
|
185
|
+
am: string;
|
|
186
|
+
pm: string;
|
|
187
|
+
/** Read for the AM / PM column. */
|
|
188
|
+
amPm: string;
|
|
189
|
+
morning: string;
|
|
190
|
+
afternoon: string;
|
|
191
|
+
evening: string;
|
|
192
|
+
slotsOpen: (n: number) => string;
|
|
193
|
+
full: string;
|
|
194
|
+
today: string;
|
|
195
|
+
tomorrow: string;
|
|
196
|
+
nextMonday: string;
|
|
197
|
+
inAWeek: string;
|
|
198
|
+
inAMonth: string;
|
|
199
|
+
next7Days: string;
|
|
200
|
+
nextWeekend: string;
|
|
201
|
+
thisMonth: string;
|
|
202
|
+
last7Days: string;
|
|
203
|
+
last30Days: string;
|
|
204
|
+
chooseOneOrMore: string;
|
|
205
|
+
/** After a rolling count, as in "2 selected". */
|
|
206
|
+
selectedSuffix: string;
|
|
207
|
+
chooseMonth: string;
|
|
208
|
+
chooseYear: string;
|
|
209
|
+
backToDays: string;
|
|
210
|
+
dayAndWeek: (day: number, week: number) => string;
|
|
211
|
+
daysLeft: (n: number) => string;
|
|
212
|
+
/** Too short, Weak, Okay, Strong, Very strong. */
|
|
213
|
+
passwordLevels: [string, string, string, string, string];
|
|
214
|
+
passwordsMatch: string;
|
|
215
|
+
passwordsDontMatch: string;
|
|
216
|
+
showQrCode: string;
|
|
217
|
+
copy: string;
|
|
218
|
+
copied: string;
|
|
219
|
+
slideToConfirm: string;
|
|
220
|
+
confirmed: string;
|
|
221
|
+
like: string;
|
|
222
|
+
nope: string;
|
|
223
|
+
swipeRight: string;
|
|
224
|
+
swipeLeft: string;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
export const defaultLabels: Labels = {
|
|
228
|
+
close: 'Close',
|
|
229
|
+
dismiss: 'Dismiss',
|
|
230
|
+
closeSheet: 'Close sheet',
|
|
231
|
+
sheetGrabber: 'Sheet grabber',
|
|
232
|
+
sheetResizeHint: 'Swipe up or down to resize',
|
|
233
|
+
sheetResizeOrCloseHint: 'Swipe up or down to resize or close',
|
|
234
|
+
navigationDrawer: 'Navigation drawer',
|
|
235
|
+
closeDrawer: 'Close drawer',
|
|
236
|
+
closeMenu: 'Close menu',
|
|
237
|
+
switchAccount: 'Switch account',
|
|
238
|
+
openActions: 'Open actions',
|
|
239
|
+
closeActions: 'Close actions',
|
|
240
|
+
action: 'Action',
|
|
241
|
+
loading: 'Loading',
|
|
242
|
+
clearSearch: 'Clear search',
|
|
243
|
+
cancelSearch: 'Cancel search',
|
|
244
|
+
clearField: (field) => `Clear ${field}`,
|
|
245
|
+
back: 'Back',
|
|
246
|
+
previous: 'Previous',
|
|
247
|
+
next: 'Next',
|
|
248
|
+
goToToday: 'Go to today',
|
|
249
|
+
goToSlide: (n) => `Go to slide ${n}`,
|
|
250
|
+
showAllCards: 'Shows all cards',
|
|
251
|
+
expandNotification: 'Expands the notification',
|
|
252
|
+
collapseNotification: 'Collapses the notification',
|
|
253
|
+
moreCount: (n) => `${n} more`,
|
|
254
|
+
peopleCount: (n) => `${n} people`,
|
|
255
|
+
namesAndMore: (names, n) => `${names} and ${n} more`,
|
|
256
|
+
newCount: (badge) => `${badge} new`,
|
|
257
|
+
remove: (item) => `Remove ${item}`,
|
|
258
|
+
selectedCount: (n) => `${n} selected`,
|
|
259
|
+
noneSelected: 'None selected',
|
|
260
|
+
notSet: 'Not set',
|
|
261
|
+
date: 'Date',
|
|
262
|
+
time: 'Time',
|
|
263
|
+
hour: 'Hour',
|
|
264
|
+
minute: 'Minute',
|
|
265
|
+
hours: (n) => `${n} hours`,
|
|
266
|
+
minutes: (n) => `${n} minutes`,
|
|
267
|
+
booked: 'booked',
|
|
268
|
+
unavailable: 'unavailable',
|
|
269
|
+
timeDialHint: 'Drag around the dial, or swipe up or down to adjust.',
|
|
270
|
+
flipCalendarHint: 'Swipe up to turn to the next day, down for the previous one.',
|
|
271
|
+
increase: 'Increase',
|
|
272
|
+
decrease: 'Decrease',
|
|
273
|
+
increaseItem: (item) => `Increase ${item}`,
|
|
274
|
+
decreaseItem: (item) => `Decrease ${item}`,
|
|
275
|
+
range: 'Range',
|
|
276
|
+
rangeMinimum: (name) => `${name} minimum`,
|
|
277
|
+
rangeMaximum: (name) => `${name} maximum`,
|
|
278
|
+
ratingStars: 'Rating, stars',
|
|
279
|
+
ratingHearts: 'Rating, hearts',
|
|
280
|
+
showPassword: 'Show password',
|
|
281
|
+
hidePassword: 'Hide password',
|
|
282
|
+
passwordStrength: (level) => `Password strength: ${level}`,
|
|
283
|
+
ruleMet: (rule) => `${rule}: done`,
|
|
284
|
+
ruleUnmet: (rule) => `${rule}: not yet`,
|
|
285
|
+
stepOf: (step, total, label) => `Step ${step} of ${total}, ${label}`,
|
|
286
|
+
allStepsComplete: 'All steps complete',
|
|
287
|
+
stepCurrent: 'current',
|
|
288
|
+
stepCompleted: 'completed',
|
|
289
|
+
stepNeedsAttention: 'needs attention',
|
|
290
|
+
stepNotStarted: 'not started',
|
|
291
|
+
goBackToStep: 'Goes back to this step',
|
|
292
|
+
activateCodeField: 'Activates the code field',
|
|
293
|
+
qrCodeFor: (value) => `QR code for ${value}`,
|
|
294
|
+
undoStroke: 'Undo last stroke',
|
|
295
|
+
signaturePad: 'Signature pad, draw your signature',
|
|
296
|
+
signaturePadSigned: 'Signature pad, signed',
|
|
297
|
+
flipToBack: 'Tap to flip to the back.',
|
|
298
|
+
flipToFront: 'Showing the back. Tap to flip to the front.',
|
|
299
|
+
revealHint: 'Double-tap to reveal',
|
|
300
|
+
confirmHint: 'Double tap to confirm',
|
|
301
|
+
prizeWheel: 'Prize wheel',
|
|
302
|
+
spinHint: 'Spins the wheel for a prize',
|
|
303
|
+
prizes: (n, list) => `${n} prizes: ${list}`,
|
|
304
|
+
orbReveal: 'Reveal',
|
|
305
|
+
orbBack: 'Glass orb, drag down to go back',
|
|
306
|
+
cancel: 'Cancel',
|
|
307
|
+
done: 'Done',
|
|
308
|
+
doneCount: (n) => `Done · ${n}`,
|
|
309
|
+
doneDays: (n) => `Done · ${n} days`,
|
|
310
|
+
clear: 'Clear',
|
|
311
|
+
continue: 'Continue',
|
|
312
|
+
delete: 'Delete',
|
|
313
|
+
failed: 'Failed',
|
|
314
|
+
search: 'Search',
|
|
315
|
+
noResults: 'No results',
|
|
316
|
+
noOptions: 'No options',
|
|
317
|
+
nothingMatches: (query) => `Nothing matches “${query}”.`,
|
|
318
|
+
selectPlaceholder: 'Select…',
|
|
319
|
+
selectDate: 'Select a date',
|
|
320
|
+
selectDates: 'Select dates',
|
|
321
|
+
pickStartDate: 'Pick a start date',
|
|
322
|
+
pickEndDate: 'pick an end date',
|
|
323
|
+
selectTime: 'Select a time',
|
|
324
|
+
setTime: (time) => `Set ${time}`,
|
|
325
|
+
am: 'AM',
|
|
326
|
+
pm: 'PM',
|
|
327
|
+
amPm: 'AM or PM',
|
|
328
|
+
morning: 'Morning',
|
|
329
|
+
afternoon: 'Afternoon',
|
|
330
|
+
evening: 'Evening',
|
|
331
|
+
slotsOpen: (n) => `${n} open`,
|
|
332
|
+
full: 'Full',
|
|
333
|
+
today: 'Today',
|
|
334
|
+
tomorrow: 'Tomorrow',
|
|
335
|
+
nextMonday: 'Next Monday',
|
|
336
|
+
inAWeek: 'In a week',
|
|
337
|
+
inAMonth: 'In a month',
|
|
338
|
+
next7Days: 'Next 7 days',
|
|
339
|
+
nextWeekend: 'Next weekend',
|
|
340
|
+
thisMonth: 'This month',
|
|
341
|
+
last7Days: 'Last 7 days',
|
|
342
|
+
last30Days: 'Last 30 days',
|
|
343
|
+
chooseOneOrMore: 'Choose one or more',
|
|
344
|
+
selectedSuffix: 'selected',
|
|
345
|
+
chooseMonth: 'choose month',
|
|
346
|
+
chooseYear: 'choose year',
|
|
347
|
+
backToDays: 'back to days',
|
|
348
|
+
dayAndWeek: (day, week) => `Day ${day} · Week ${week}`,
|
|
349
|
+
daysLeft: (n) => `${n} left`,
|
|
350
|
+
passwordLevels: ['Too short', 'Weak', 'Okay', 'Strong', 'Very strong'],
|
|
351
|
+
passwordsMatch: 'Passwords match',
|
|
352
|
+
passwordsDontMatch: "Passwords don't match",
|
|
353
|
+
showQrCode: 'Show QR Code',
|
|
354
|
+
copy: 'Copy',
|
|
355
|
+
copied: 'Copied',
|
|
356
|
+
slideToConfirm: 'Slide to confirm',
|
|
357
|
+
confirmed: 'Confirmed',
|
|
358
|
+
like: 'LIKE',
|
|
359
|
+
nope: 'NOPE',
|
|
360
|
+
swipeRight: 'Right',
|
|
361
|
+
swipeLeft: 'Left',
|
|
362
|
+
};
|
|
363
|
+
|
|
67
364
|
const light: Colors = {
|
|
68
365
|
background: '#ffffff',
|
|
69
366
|
foreground: '#09090b',
|
|
@@ -149,20 +446,27 @@ function build(scheme: ColorScheme, overrides?: ThemeOverrides): Theme {
|
|
|
149
446
|
xl: { fontSize: 22, lineHeight: 28, fontWeight: '400', letterSpacing: -0.3 },
|
|
150
447
|
xxl: { fontSize: 30, lineHeight: 36, fontWeight: '400', letterSpacing: -0.8 },
|
|
151
448
|
},
|
|
449
|
+
labels: overrides?.labels ? { ...defaultLabels, ...overrides.labels } : defaultLabels,
|
|
152
450
|
};
|
|
153
451
|
}
|
|
154
452
|
|
|
155
453
|
export interface ThemeOverrides {
|
|
156
454
|
/** Brand colour used for focus rings, selected states and accent buttons. */
|
|
157
|
-
accent?: string;
|
|
455
|
+
accent?: string | undefined;
|
|
456
|
+
|
|
158
457
|
/** Base corner radius in points (default 12). */
|
|
159
|
-
radius?: number;
|
|
160
|
-
|
|
458
|
+
radius?: number | undefined;
|
|
459
|
+
|
|
460
|
+
fontFamily?: string | undefined;
|
|
461
|
+
|
|
161
462
|
/** Override any token per scheme. */
|
|
162
|
-
colors?: Partial<Record<ColorScheme, Partial<Colors
|
|
463
|
+
colors?: Partial<Record<ColorScheme, Partial<Colors>>> | undefined;
|
|
464
|
+
|
|
465
|
+
/** Screen-reader strings in your language, e.g. { close: "बंद करें" }. Keys you leave out stay English. */
|
|
466
|
+
labels?: Partial<Labels> | undefined;
|
|
163
467
|
}
|
|
164
468
|
|
|
165
|
-
const KitContext = createContext<{ scheme?: ColorScheme; overrides?: ThemeOverrides } | null>(null);
|
|
469
|
+
const KitContext = createContext<{ scheme?: ColorScheme | undefined; overrides?: ThemeOverrides | undefined } | null>(null);
|
|
166
470
|
|
|
167
471
|
export function KitProvider({
|
|
168
472
|
scheme,
|
|
@@ -172,7 +476,7 @@ export function KitProvider({
|
|
|
172
476
|
const value = useMemo(
|
|
173
477
|
() => ({ scheme: scheme === 'system' ? undefined : scheme, overrides }),
|
|
174
478
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
175
|
-
[scheme, overrides.accent, overrides.radius, overrides.fontFamily, overrides.colors]
|
|
479
|
+
[scheme, overrides.accent, overrides.radius, overrides.fontFamily, overrides.colors, overrides.labels]
|
|
176
480
|
);
|
|
177
481
|
return <KitContext.Provider value={value}>{children}</KitContext.Provider>;
|
|
178
482
|
}
|
|
@@ -318,3 +622,51 @@ export function isTextNode(node: React.ReactNode): node is string | number | (st
|
|
|
318
622
|
if (typeof node === 'string' || typeof node === 'number') return true;
|
|
319
623
|
return Array.isArray(node) && node.length > 0 && node.every((n) => typeof n === 'string' || typeof n === 'number');
|
|
320
624
|
}
|
|
625
|
+
|
|
626
|
+
/**
|
|
627
|
+
* A ref that always holds the latest `value`, for gesture and timer callbacks that must not go stale.
|
|
628
|
+
* Read `.current` only inside callbacks and effects, never while rendering.
|
|
629
|
+
*/
|
|
630
|
+
export function useLatest<T>(value: T): React.MutableRefObject<T> {
|
|
631
|
+
const ref = useRef(value);
|
|
632
|
+
useLayoutEffect(() => {
|
|
633
|
+
ref.current = value;
|
|
634
|
+
});
|
|
635
|
+
return ref;
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
const panKeys = [
|
|
639
|
+
'onStartShouldSetPanResponder',
|
|
640
|
+
'onStartShouldSetPanResponderCapture',
|
|
641
|
+
'onMoveShouldSetPanResponder',
|
|
642
|
+
'onMoveShouldSetPanResponderCapture',
|
|
643
|
+
'onPanResponderGrant',
|
|
644
|
+
'onPanResponderStart',
|
|
645
|
+
'onPanResponderMove',
|
|
646
|
+
'onPanResponderEnd',
|
|
647
|
+
'onPanResponderRelease',
|
|
648
|
+
'onPanResponderReject',
|
|
649
|
+
'onPanResponderTerminate',
|
|
650
|
+
] as const;
|
|
651
|
+
|
|
652
|
+
/**
|
|
653
|
+
* PanResponder handlers that always call the latest `config`, created once. Write the callbacks inline:
|
|
654
|
+
* they may read refs and props freely, and nothing is recreated between renders, so a drag in progress
|
|
655
|
+
* is never cut off. Spread the result: `<View {...pan} />`.
|
|
656
|
+
*/
|
|
657
|
+
export function usePanResponder(config: PanResponderCallbacks): GestureResponderHandlers {
|
|
658
|
+
const latest = useLatest(config);
|
|
659
|
+
const [handlers] = useState(() => {
|
|
660
|
+
const forward: Record<string, (...args: never[]) => unknown> = {};
|
|
661
|
+
for (const key of panKeys) {
|
|
662
|
+
forward[key] = (...args) => (latest.current[key] as ((...a: never[]) => unknown) | undefined)?.(...args);
|
|
663
|
+
}
|
|
664
|
+
// React Native's defaults when these are left out are `true`, so keep them when the config has none.
|
|
665
|
+
forward.onPanResponderTerminationRequest = (...args) =>
|
|
666
|
+
(latest.current.onPanResponderTerminationRequest as ((...a: never[]) => boolean) | undefined)?.(...args) ?? true;
|
|
667
|
+
forward.onShouldBlockNativeResponder = (...args) =>
|
|
668
|
+
(latest.current.onShouldBlockNativeResponder as ((...a: never[]) => boolean) | undefined)?.(...args) ?? true;
|
|
669
|
+
return PanResponder.create(forward as PanResponderCallbacks).panHandlers;
|
|
670
|
+
});
|
|
671
|
+
return handlers;
|
|
672
|
+
}
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
"title": "Date Picker",
|
|
5
5
|
"description": "A date field with a floating label and glowing focus halo that opens the Calendar in a spring sheet (drag the grabber to dismiss, a dialog on wide screens) or an anchored popover. Single or range mode, one-tap presets like Next 7 days and Next weekend, optional Clear / Done staging with a live summary, Intl formatting, error shake with an icon message and a success check.",
|
|
6
6
|
"tier": "free",
|
|
7
|
+
"aliases": [],
|
|
7
8
|
"dependencies": [
|
|
8
9
|
"react-native-svg"
|
|
9
10
|
],
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as Haptics from 'expo-haptics';
|
|
2
|
-
import React, { useEffect, useId, useRef, useState } from 'react';
|
|
2
|
+
import React, { useEffect, useId, useLayoutEffect, useRef, useState } from 'react';
|
|
3
3
|
import {
|
|
4
4
|
AccessibilityInfo,
|
|
5
5
|
ActivityIndicator,
|
|
@@ -39,68 +39,68 @@ export interface DialogAction {
|
|
|
39
39
|
* Receives the prompt's text (if any). Return a promise to show a spinner on the button until
|
|
40
40
|
* it settles. Return `false` (or resolve to it) to keep the dialog open and shake it as invalid.
|
|
41
41
|
*/
|
|
42
|
-
onPress?: (value: string) => void | boolean | Promise<void | boolean
|
|
42
|
+
onPress?: ((value: string) => void | boolean | Promise<void | boolean>) | undefined;
|
|
43
43
|
/** 'primary' is the main call to action; 'destructive' uses the danger colour; 'cancel' is the quiet one. */
|
|
44
|
-
role?: 'primary' | 'default' | 'destructive' | 'cancel';
|
|
45
|
-
disabled?: boolean;
|
|
44
|
+
role?: 'primary' | 'default' | 'destructive' | 'cancel' | undefined;
|
|
45
|
+
disabled?: boolean | undefined;
|
|
46
46
|
}
|
|
47
47
|
|
|
48
48
|
/** A single-line text field inside the dialog (a prompt). */
|
|
49
49
|
export interface DialogPrompt {
|
|
50
|
-
defaultValue?: string;
|
|
50
|
+
defaultValue?: string | undefined;
|
|
51
51
|
/** Controlled text. */
|
|
52
|
-
value?: string;
|
|
53
|
-
onChangeText?: (value: string) => void;
|
|
54
|
-
placeholder?: string;
|
|
52
|
+
value?: string | undefined;
|
|
53
|
+
onChangeText?: ((value: string) => void) | undefined;
|
|
54
|
+
placeholder?: string | undefined;
|
|
55
55
|
/** Accessibility label of the field (default: the placeholder or title). */
|
|
56
|
-
label?: string;
|
|
57
|
-
secureTextEntry?: boolean;
|
|
58
|
-
keyboardType?: KeyboardTypeOptions;
|
|
59
|
-
autoCapitalize?: TextInputProps['autoCapitalize'];
|
|
60
|
-
maxLength?: number;
|
|
56
|
+
label?: string | undefined;
|
|
57
|
+
secureTextEntry?: boolean | undefined;
|
|
58
|
+
keyboardType?: KeyboardTypeOptions | undefined;
|
|
59
|
+
autoCapitalize?: TextInputProps['autoCapitalize'] | undefined;
|
|
60
|
+
maxLength?: number | undefined;
|
|
61
61
|
/** Select the text when the dialog opens (default true). */
|
|
62
|
-
selectTextOnFocus?: boolean;
|
|
62
|
+
selectTextOnFocus?: boolean | undefined;
|
|
63
63
|
}
|
|
64
64
|
|
|
65
65
|
export interface DialogProps {
|
|
66
|
-
open?: boolean;
|
|
67
|
-
defaultOpen?: boolean;
|
|
68
|
-
onOpenChange?: (open: boolean) => void;
|
|
66
|
+
open?: boolean | undefined;
|
|
67
|
+
defaultOpen?: boolean | undefined;
|
|
68
|
+
onOpenChange?: ((open: boolean) => void) | undefined;
|
|
69
69
|
/** `sheet` is deprecated and renders as `default`. */
|
|
70
|
-
variant?: DialogVariant;
|
|
70
|
+
variant?: DialogVariant | undefined;
|
|
71
71
|
/** Living icon in a halo above the title, plus a soft glow of its colour. Destructive defaults to a warning icon. */
|
|
72
|
-
tone?: DialogTone;
|
|
72
|
+
tone?: DialogTone | undefined;
|
|
73
73
|
/** `modern` pill buttons (default) or `ios` hairline-separated text buttons. */
|
|
74
|
-
appearance?: DialogAppearance;
|
|
75
|
-
title?: string;
|
|
76
|
-
description?: string;
|
|
74
|
+
appearance?: DialogAppearance | undefined;
|
|
75
|
+
title?: string | undefined;
|
|
76
|
+
description?: string | undefined;
|
|
77
77
|
/** Custom content between the description and the actions. */
|
|
78
|
-
children?: React.ReactNode;
|
|
78
|
+
children?: React.ReactNode | undefined;
|
|
79
79
|
/** Turns the dialog into a prompt with a text field; the text is passed to `onConfirm` / `onPress`. */
|
|
80
|
-
prompt?: DialogPrompt;
|
|
80
|
+
prompt?: DialogPrompt | undefined;
|
|
81
81
|
/** Error text under the description / field, in the danger colour. */
|
|
82
|
-
error?: string;
|
|
82
|
+
error?: string | undefined;
|
|
83
83
|
/** Buttons. Defaults to Cancel + Confirm built from the labels below. */
|
|
84
|
-
actions?: DialogAction[];
|
|
85
|
-
confirmLabel?: string;
|
|
84
|
+
actions?: DialogAction[] | undefined;
|
|
85
|
+
confirmLabel?: string | undefined;
|
|
86
86
|
/** Label of the default cancel button; `null` leaves only the confirm button. */
|
|
87
|
-
cancelLabel?: string | null;
|
|
88
|
-
onConfirm?: DialogAction['onPress'];
|
|
87
|
+
cancelLabel?: string | null | undefined;
|
|
88
|
+
onConfirm?: DialogAction['onPress'] | undefined;
|
|
89
89
|
/** Called when the default cancel button, the scrim, back or Escape closes the dialog. */
|
|
90
|
-
onCancel?: () => void;
|
|
90
|
+
onCancel?: (() => void) | undefined;
|
|
91
91
|
/** Spinner on the confirm button and every other button disabled (controlled busy state). */
|
|
92
|
-
loading?: boolean;
|
|
92
|
+
loading?: boolean | undefined;
|
|
93
93
|
/** `auto` puts two short buttons side by side and stacks the rest. */
|
|
94
|
-
actionsLayout?: 'auto' | 'row' | 'stack';
|
|
94
|
+
actionsLayout?: 'auto' | 'row' | 'stack' | undefined;
|
|
95
95
|
/** Custom icon inside the halo, or `false` to hide the tone icon. */
|
|
96
|
-
icon?: React.ReactNode | false;
|
|
96
|
+
icon?: React.ReactNode | false | undefined;
|
|
97
97
|
/** Close when the scrim is tapped, on back and on Escape (default true). */
|
|
98
|
-
dismissible?: boolean;
|
|
98
|
+
dismissible?: boolean | undefined;
|
|
99
99
|
/** Notification haptic on open (warning for destructive, success for success) and error on shake (default true). */
|
|
100
|
-
haptics?: boolean;
|
|
100
|
+
haptics?: boolean | undefined;
|
|
101
101
|
/** Colour of the primary action (default: theme primary; accent in `ios`). */
|
|
102
|
-
accent?: string;
|
|
103
|
-
style?: StyleProp<ViewStyle
|
|
102
|
+
accent?: string | undefined;
|
|
103
|
+
style?: StyleProp<ViewStyle> | undefined;
|
|
104
104
|
}
|
|
105
105
|
|
|
106
106
|
const SHAKE = [10, -9, 7, -5, 3, 0];
|
|
@@ -129,7 +129,7 @@ export function Dialog({
|
|
|
129
129
|
error,
|
|
130
130
|
actions,
|
|
131
131
|
confirmLabel,
|
|
132
|
-
cancelLabel
|
|
132
|
+
cancelLabel: cancelLabelProp,
|
|
133
133
|
onConfirm,
|
|
134
134
|
onCancel,
|
|
135
135
|
loading = false,
|
|
@@ -140,7 +140,8 @@ export function Dialog({
|
|
|
140
140
|
accent,
|
|
141
141
|
style,
|
|
142
142
|
}: DialogProps) {
|
|
143
|
-
const { colors, radius, dark, font } = useKit();
|
|
143
|
+
const { colors, radius, dark, font, labels } = useKit();
|
|
144
|
+
const cancelLabel = cancelLabelProp === undefined ? labels.cancel : cancelLabelProp; // null hides it
|
|
144
145
|
const ff = font.family ? { fontFamily: font.family } : null;
|
|
145
146
|
const uid = useId().replace(/[^a-zA-Z0-9]/g, '');
|
|
146
147
|
const reduceMotion = useReduceMotion();
|
|
@@ -149,9 +150,9 @@ export function Dialog({
|
|
|
149
150
|
const [inner, setInner] = useState(defaultOpen);
|
|
150
151
|
const isOpen = open ?? inner;
|
|
151
152
|
// Stays mounted while the exit animation runs; cleared in its completion callback.
|
|
152
|
-
const keep =
|
|
153
|
-
|
|
154
|
-
const mounted = isOpen || keep
|
|
153
|
+
const [keep, setKeep] = useState(isOpen);
|
|
154
|
+
if (isOpen && !keep) setKeep(true);
|
|
155
|
+
const mounted = isOpen || keep;
|
|
155
156
|
const [busy, setBusy] = useState<number | null>(null);
|
|
156
157
|
// null = untouched since opening, so the field shows `prompt.defaultValue`.
|
|
157
158
|
const [text, setText] = useState<string | null>(null);
|
|
@@ -160,12 +161,12 @@ export function Dialog({
|
|
|
160
161
|
const value = prompt?.value ?? text ?? prompt?.defaultValue ?? '';
|
|
161
162
|
|
|
162
163
|
// Native: card life (opacity + scale), scrim, shake, icon glyph pop, halo ripple. JS: icon outline draw.
|
|
163
|
-
const t =
|
|
164
|
-
const scrim =
|
|
165
|
-
const shake =
|
|
166
|
-
const draw =
|
|
167
|
-
const glyph =
|
|
168
|
-
const ripple =
|
|
164
|
+
const [t] = useState(() => new Animated.Value(0));
|
|
165
|
+
const [scrim] = useState(() => new Animated.Value(0));
|
|
166
|
+
const [shake] = useState(() => new Animated.Value(0));
|
|
167
|
+
const [draw] = useState(() => new Animated.Value(0));
|
|
168
|
+
const [glyph] = useState(() => new Animated.Value(0));
|
|
169
|
+
const [ripple] = useState(() => new Animated.Value(0));
|
|
169
170
|
|
|
170
171
|
const tones: Record<DialogTone, string> = {
|
|
171
172
|
info: dark ? '#60a5fa' : '#2563eb',
|
|
@@ -181,7 +182,7 @@ export function Dialog({
|
|
|
181
182
|
const list: DialogAction[] = actions ?? [
|
|
182
183
|
...(cancelLabel !== null ? [{ label: cancelLabel, role: 'cancel' as const, onPress: () => onCancel?.() }] : []),
|
|
183
184
|
{
|
|
184
|
-
label: confirmLabel ?? (destructive ?
|
|
185
|
+
label: confirmLabel ?? (destructive ? labels.delete : labels.continue),
|
|
185
186
|
role: destructive ? ('destructive' as const) : ('primary' as const),
|
|
186
187
|
onPress: onConfirm,
|
|
187
188
|
},
|
|
@@ -202,7 +203,9 @@ export function Dialog({
|
|
|
202
203
|
setOpen(false);
|
|
203
204
|
};
|
|
204
205
|
const live = useRef({ requestClose });
|
|
205
|
-
|
|
206
|
+
useLayoutEffect(() => {
|
|
207
|
+
live.current = { requestClose };
|
|
208
|
+
});
|
|
206
209
|
|
|
207
210
|
const playIcon = () => {
|
|
208
211
|
if (reduceMotion) {
|
|
@@ -221,7 +224,6 @@ export function Dialog({
|
|
|
221
224
|
|
|
222
225
|
useEffect(() => {
|
|
223
226
|
if (isOpen) {
|
|
224
|
-
keep.current = true;
|
|
225
227
|
shake.setValue(0);
|
|
226
228
|
if (haptics) {
|
|
227
229
|
const type =
|
|
@@ -245,10 +247,9 @@ export function Dialog({
|
|
|
245
247
|
Animated.timing(scrim, { toValue: 0, duration: 200, easing: Easing.in(Easing.quad), useNativeDriver: true }),
|
|
246
248
|
]).start(({ finished }) => {
|
|
247
249
|
if (!finished) return;
|
|
248
|
-
|
|
250
|
+
setKeep(false);
|
|
249
251
|
setText(null);
|
|
250
252
|
setBusy(null);
|
|
251
|
-
rerender((n) => n + 1);
|
|
252
253
|
});
|
|
253
254
|
}
|
|
254
255
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
@@ -330,7 +331,10 @@ export function Dialog({
|
|
|
330
331
|
autoFocus
|
|
331
332
|
maxFontSizeMultiplier={FONT_CAP}
|
|
332
333
|
returnKeyType="done"
|
|
333
|
-
onSubmitEditing={() =>
|
|
334
|
+
onSubmitEditing={() => {
|
|
335
|
+
const confirm = list[confirmIndex];
|
|
336
|
+
if (confirm) run(confirm, confirmIndex);
|
|
337
|
+
}}
|
|
334
338
|
onFocus={() => setFocused(true)}
|
|
335
339
|
onBlur={() => setFocused(false)}
|
|
336
340
|
selectionColor={accent ?? colors.accent}
|
|
@@ -522,7 +526,7 @@ function PillButton({
|
|
|
522
526
|
}) {
|
|
523
527
|
const { font } = useKit();
|
|
524
528
|
const ff = font.family ? { fontFamily: font.family } : null;
|
|
525
|
-
const press =
|
|
529
|
+
const [press] = useState(() => new Animated.Value(0));
|
|
526
530
|
const to = (v: number) => Animated.spring(press, { toValue: v, useNativeDriver: true, ...motion.snappy }).start();
|
|
527
531
|
return (
|
|
528
532
|
<Pressable
|
|
@@ -575,7 +579,7 @@ function IosButton({
|
|
|
575
579
|
}) {
|
|
576
580
|
const { font } = useKit();
|
|
577
581
|
const ff = font.family ? { fontFamily: font.family } : null;
|
|
578
|
-
const hl =
|
|
582
|
+
const [hl] = useState(() => new Animated.Value(0));
|
|
579
583
|
return (
|
|
580
584
|
<Pressable
|
|
581
585
|
onPress={onPress}
|