@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,16 +1,14 @@
|
|
|
1
|
-
import React, { memo, useEffect, useMemo, useRef, useState, type ComponentRef } from 'react';
|
|
1
|
+
import React, { memo, useEffect, useLayoutEffect, useMemo, useRef, useState, type ComponentRef } from 'react';
|
|
2
2
|
import {
|
|
3
3
|
AccessibilityInfo,
|
|
4
4
|
Animated,
|
|
5
5
|
Easing,
|
|
6
6
|
Modal,
|
|
7
|
-
PanResponder,
|
|
8
7
|
Pressable,
|
|
9
8
|
ScrollView,
|
|
10
9
|
StyleSheet,
|
|
11
10
|
Text,
|
|
12
11
|
View,
|
|
13
|
-
useWindowDimensions,
|
|
14
12
|
type NativeScrollEvent,
|
|
15
13
|
type NativeSyntheticEvent,
|
|
16
14
|
type StyleProp,
|
|
@@ -19,7 +17,7 @@ import {
|
|
|
19
17
|
} from 'react-native';
|
|
20
18
|
import Svg, { Circle, Line, Path } from 'react-native-svg';
|
|
21
19
|
|
|
22
|
-
import { alpha, motion, readableOn, useKit } from '@/lib/kit';
|
|
20
|
+
import { alpha, motion, readableOn, useKit, useLayout, usePanResponder } from '@/lib/kit';
|
|
23
21
|
|
|
24
22
|
/** A wall-clock time, 24-hour: `{ hours: 0–23, minutes: 0–59 }`. */
|
|
25
23
|
export interface TimeValue {
|
|
@@ -144,11 +142,11 @@ export function uses12Hour(locale?: string) {
|
|
|
144
142
|
}
|
|
145
143
|
}
|
|
146
144
|
|
|
147
|
-
/** '9:30 AM' or '09:30'. */
|
|
148
|
-
export function formatTime(t: TimeValue, hour12 = true) {
|
|
145
|
+
/** '9:30 AM' or '09:30'. `periods` names AM / PM (default English; pass `useKit().labels` to localise). */
|
|
146
|
+
export function formatTime(t: TimeValue, hour12 = true, periods: { am: string; pm: string } = { am: 'AM', pm: 'PM' }) {
|
|
149
147
|
if (!hour12) return `${pad2(t.hours)}:${pad2(t.minutes)}`;
|
|
150
148
|
const h = t.hours % 12 || 12;
|
|
151
|
-
return `${h}:${pad2(t.minutes)} ${t.hours < 12 ?
|
|
149
|
+
return `${h}:${pad2(t.minutes)} ${t.hours < 12 ? periods.am : periods.pm}`;
|
|
152
150
|
}
|
|
153
151
|
|
|
154
152
|
/* ───────────────────────────── TimePicker ───────────────────────────── */
|
|
@@ -195,18 +193,18 @@ interface WheelProps {
|
|
|
195
193
|
step: number;
|
|
196
194
|
value: TimeValue | null;
|
|
197
195
|
onChange: (t: TimeValue) => void;
|
|
198
|
-
accent?: string;
|
|
199
|
-
style?: StyleProp<ViewStyle
|
|
196
|
+
accent?: string | undefined;
|
|
197
|
+
style?: StyleProp<ViewStyle> | undefined;
|
|
200
198
|
reduce: boolean;
|
|
201
199
|
}
|
|
202
200
|
|
|
203
201
|
function Wheel({ hour12, step, value, onChange, accent, style, reduce }: WheelProps) {
|
|
204
|
-
const { colors, radius, font, dark } = useKit();
|
|
202
|
+
const { colors, radius, font, dark, labels } = useKit();
|
|
205
203
|
const tint = accent ?? colors.accent;
|
|
206
204
|
const t = value ?? { hours: 9, minutes: 0 };
|
|
207
205
|
const hours = useMemo(() => (hour12 ? ['12', ...Array.from({ length: 11 }, (_, i) => String(i + 1))] : Array.from({ length: 24 }, (_, i) => pad2(i))), [hour12]);
|
|
208
206
|
const minutes = useMemo(() => Array.from({ length: Math.ceil(60 / step) }, (_, i) => pad2(i * step)), [step]);
|
|
209
|
-
const periods = useMemo(() => [
|
|
207
|
+
const periods = useMemo(() => [labels.am, labels.pm], [labels.am, labels.pm]);
|
|
210
208
|
|
|
211
209
|
const hIdx = hour12 ? t.hours % 12 : t.hours;
|
|
212
210
|
const mIdx = Math.min(minutes.length - 1, Math.round(t.minutes / step));
|
|
@@ -214,7 +212,9 @@ function Wheel({ hour12, step, value, onChange, accent, style, reduce }: WheelPr
|
|
|
214
212
|
|
|
215
213
|
// Keep the latest value for column callbacks that fire after a scroll settles.
|
|
216
214
|
const latest = useRef(t);
|
|
217
|
-
|
|
215
|
+
useLayoutEffect(() => {
|
|
216
|
+
latest.current = t;
|
|
217
|
+
});
|
|
218
218
|
const set = (patch: Partial<{ h: number; m: number; p: number }>) => {
|
|
219
219
|
const cur = latest.current;
|
|
220
220
|
const curP = cur.hours >= 12 ? 1 : 0;
|
|
@@ -234,17 +234,17 @@ function Wheel({ hour12, step, value, onChange, accent, style, reduce }: WheelPr
|
|
|
234
234
|
style={[styles.band, { top: PAD, height: ITEM, borderRadius: radius.scale(12), backgroundColor: alpha(tint, dark ? 0.16 : 0.11) }]}
|
|
235
235
|
/>
|
|
236
236
|
<View style={styles.wheelGroup}>
|
|
237
|
-
<WheelColumn items={hours} index={hIdx} onIndexChange={(h) => set({ h })} label=
|
|
237
|
+
<WheelColumn items={hours} index={hIdx} onIndexChange={(h) => set({ h })} label={labels.hour} width={64} align="right" reduce={reduce} />
|
|
238
238
|
<View pointerEvents="none" style={styles.colon}>
|
|
239
239
|
<Text maxFontSizeMultiplier={FONT_CAP} style={[styles.colonText, font.family ? { fontFamily: font.family } : null, { color: colors.mutedForeground }]}>:</Text>
|
|
240
240
|
</View>
|
|
241
|
-
<WheelColumn items={minutes} index={mIdx} onIndexChange={(m) => set({ m })} label=
|
|
241
|
+
<WheelColumn items={minutes} index={mIdx} onIndexChange={(m) => set({ m })} label={labels.minute} width={64} align="left" reduce={reduce} />
|
|
242
242
|
{hour12 ? (
|
|
243
243
|
<WheelColumn
|
|
244
244
|
items={periods}
|
|
245
245
|
index={pIdx}
|
|
246
246
|
onIndexChange={(p) => set({ p })}
|
|
247
|
-
label=
|
|
247
|
+
label={labels.amPm}
|
|
248
248
|
width={60}
|
|
249
249
|
align="center"
|
|
250
250
|
small
|
|
@@ -273,17 +273,21 @@ interface WheelColumnProps {
|
|
|
273
273
|
const WheelColumn = memo(function WheelColumn({ items, index, onIndexChange, label, width, align, small, style, reduce }: WheelColumnProps) {
|
|
274
274
|
const { colors, font } = useKit();
|
|
275
275
|
const ff = font.family ? { fontFamily: font.family } : null;
|
|
276
|
-
const scrollY =
|
|
276
|
+
const [scrollY] = useState(() => new Animated.Value(index * ITEM)); // native, driven by the scroll view
|
|
277
277
|
const ref = useRef<ComponentRef<typeof ScrollView>>(null);
|
|
278
278
|
const settled = useRef(index);
|
|
279
279
|
const dragging = useRef(false);
|
|
280
280
|
const lastY = useRef(index * ITEM);
|
|
281
281
|
const timer = useRef<ReturnType<typeof setTimeout> | null>(null);
|
|
282
282
|
const cb = useRef(onIndexChange);
|
|
283
|
-
|
|
283
|
+
useLayoutEffect(() => {
|
|
284
|
+
cb.current = onIndexChange;
|
|
285
|
+
});
|
|
284
286
|
// Read by settle(), which also runs from the scroll listener created once.
|
|
285
287
|
const animated = useRef(!reduce);
|
|
286
|
-
|
|
288
|
+
useLayoutEffect(() => {
|
|
289
|
+
animated.current = !reduce;
|
|
290
|
+
});
|
|
287
291
|
|
|
288
292
|
useEffect(() => {
|
|
289
293
|
if (index !== settled.current && !dragging.current) {
|
|
@@ -317,6 +321,7 @@ const WheelColumn = memo(function WheelColumn({ items, index, onIndexChange, lab
|
|
|
317
321
|
|
|
318
322
|
const onScroll = useMemo(
|
|
319
323
|
() =>
|
|
324
|
+
// eslint-disable-next-line react-hooks/refs -- the listener reads refs on scroll events, never during render
|
|
320
325
|
Animated.event([{ nativeEvent: { contentOffset: { y: scrollY } } }], {
|
|
321
326
|
useNativeDriver: true,
|
|
322
327
|
listener: (e: NativeSyntheticEvent<NativeScrollEvent>) => {
|
|
@@ -403,8 +408,8 @@ interface ClockProps {
|
|
|
403
408
|
step: number;
|
|
404
409
|
value: TimeValue | null;
|
|
405
410
|
onChange: (t: TimeValue) => void;
|
|
406
|
-
accent?: string;
|
|
407
|
-
style?: StyleProp<ViewStyle
|
|
411
|
+
accent?: string | undefined;
|
|
412
|
+
style?: StyleProp<ViewStyle> | undefined;
|
|
408
413
|
reduce: boolean;
|
|
409
414
|
}
|
|
410
415
|
|
|
@@ -424,7 +429,7 @@ const handAngle = (t: TimeValue, mode: ClockMode) => (mode === 'hour' ? (t.hours
|
|
|
424
429
|
* native driver and snaps to hours, then auto-advances to minutes when you let go.
|
|
425
430
|
*/
|
|
426
431
|
function Clock({ hour12, step, value, onChange, accent, style, reduce }: ClockProps) {
|
|
427
|
-
const { colors, radius, font, dark } = useKit();
|
|
432
|
+
const { colors, radius, font, dark, labels } = useKit();
|
|
428
433
|
const ff = font.family ? { fontFamily: font.family } : null;
|
|
429
434
|
const tint = accent ?? colors.accent;
|
|
430
435
|
const ink = readableOn(tint);
|
|
@@ -442,9 +447,9 @@ function Clock({ hour12, step, value, onChange, accent, style, reduce }: ClockPr
|
|
|
442
447
|
const handR = mode === 'hour' && innerHour ? inner : outer;
|
|
443
448
|
|
|
444
449
|
// Hand rotation in degrees (native). Kept continuous so 55 → 00 turns forward, not all the way back.
|
|
445
|
-
const rot =
|
|
450
|
+
const [rot] = useState(() => new Animated.Value(handAngle(t, 'hour')));
|
|
446
451
|
const angle = useRef(handAngle(t, 'hour'));
|
|
447
|
-
const modeAnim =
|
|
452
|
+
const [modeAnim] = useState(() => new Animated.Value(0)); // native: number crossfade
|
|
448
453
|
useEffect(() => {
|
|
449
454
|
const target = handAngle(t, mode);
|
|
450
455
|
const d = ((((target - angle.current) % 360) + 540) % 360) - 180;
|
|
@@ -460,48 +465,48 @@ function Clock({ hour12, step, value, onChange, accent, style, reduce }: ClockPr
|
|
|
460
465
|
|
|
461
466
|
// Latest values for the pan responder, which is created once.
|
|
462
467
|
const live = useRef({ t, mode, hour12, step, onChange, c, outer, inner, touched: value !== null });
|
|
463
|
-
|
|
468
|
+
useLayoutEffect(() => {
|
|
469
|
+
live.current = { t, mode, hour12, step, onChange, c, outer, inner, touched: value !== null };
|
|
470
|
+
});
|
|
464
471
|
const origin = useRef({ x: 0, y: 0 });
|
|
465
472
|
|
|
466
|
-
const
|
|
467
|
-
const
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
next = { hours: innerRing ? (i === 0 ? 0 : i + 12) : i === 0 ? 12 : i, minutes: cur.minutes };
|
|
481
|
-
}
|
|
482
|
-
} else {
|
|
483
|
-
next = { hours: cur.hours, minutes: (Math.round(deg / 6 / L.step) * L.step) % 60 };
|
|
484
|
-
}
|
|
485
|
-
if (next.hours !== cur.hours || next.minutes !== cur.minutes || !L.touched) {
|
|
486
|
-
L.t = next;
|
|
487
|
-
L.touched = true;
|
|
488
|
-
L.onChange(next);
|
|
473
|
+
const pick = (x: number, y: number) => {
|
|
474
|
+
const L = live.current;
|
|
475
|
+
const dx = x - L.c;
|
|
476
|
+
const dy = y - L.c;
|
|
477
|
+
let deg = (Math.atan2(dx, -dy) * 180) / Math.PI;
|
|
478
|
+
if (deg < 0) deg += 360;
|
|
479
|
+
const cur = L.t;
|
|
480
|
+
let next: TimeValue;
|
|
481
|
+
if (L.mode === 'hour') {
|
|
482
|
+
const i = Math.round(deg / 30) % 12;
|
|
483
|
+
if (L.hour12) next = { hours: i + (cur.hours >= 12 ? 12 : 0), minutes: cur.minutes };
|
|
484
|
+
else {
|
|
485
|
+
const innerRing = Math.hypot(dx, dy) < (L.outer + L.inner) / 2;
|
|
486
|
+
next = { hours: innerRing ? (i === 0 ? 0 : i + 12) : i === 0 ? 12 : i, minutes: cur.minutes };
|
|
489
487
|
}
|
|
490
|
-
}
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
488
|
+
} else {
|
|
489
|
+
next = { hours: cur.hours, minutes: (Math.round(deg / 6 / L.step) * L.step) % 60 };
|
|
490
|
+
}
|
|
491
|
+
if (next.hours !== cur.hours || next.minutes !== cur.minutes || !L.touched) {
|
|
492
|
+
L.t = next;
|
|
493
|
+
L.touched = true;
|
|
494
|
+
L.onChange(next);
|
|
495
|
+
}
|
|
496
|
+
};
|
|
497
|
+
const pan = usePanResponder({
|
|
498
|
+
onStartShouldSetPanResponder: () => true,
|
|
499
|
+
onMoveShouldSetPanResponder: () => true,
|
|
500
|
+
onPanResponderTerminationRequest: () => false,
|
|
501
|
+
onPanResponderGrant: (e) => {
|
|
502
|
+
origin.current = { x: e.nativeEvent.locationX, y: e.nativeEvent.locationY };
|
|
503
|
+
pick(origin.current.x, origin.current.y);
|
|
504
|
+
},
|
|
505
|
+
onPanResponderMove: (_, g) => pick(origin.current.x + g.dx, origin.current.y + g.dy),
|
|
506
|
+
onPanResponderRelease: () => {
|
|
507
|
+
if (live.current.mode === 'hour') setMode('minute');
|
|
508
|
+
},
|
|
509
|
+
});
|
|
505
510
|
|
|
506
511
|
const adjust = (dir: 1 | -1) => {
|
|
507
512
|
if (mode === 'hour') {
|
|
@@ -555,22 +560,22 @@ function Clock({ hour12, step, value, onChange, accent, style, reduce }: ClockPr
|
|
|
555
560
|
return (
|
|
556
561
|
<View style={[styles.clock, style]} onLayout={(e) => setWidth(e.nativeEvent.layout.width)}>
|
|
557
562
|
<View style={styles.clockHead}>
|
|
558
|
-
{segment('hour', hourText,
|
|
563
|
+
{segment('hour', hourText, labels.hour)}
|
|
559
564
|
<Text maxFontSizeMultiplier={FONT_CAP} style={[styles.clockColon, ff, { fontSize: segFont, color: colors.mutedForeground }]}>:</Text>
|
|
560
|
-
{segment('minute', pad2(t.minutes),
|
|
565
|
+
{segment('minute', pad2(t.minutes), labels.minute)}
|
|
561
566
|
{hour12 ? (
|
|
562
567
|
<View
|
|
563
568
|
accessibilityRole="radiogroup"
|
|
564
569
|
style={[styles.period, { height: segH, borderRadius: radius.scale(10), borderColor: colors.border }]}
|
|
565
570
|
>
|
|
566
|
-
{(['
|
|
567
|
-
const on = (p === '
|
|
571
|
+
{(['am', 'pm'] as const).map((p, i) => {
|
|
572
|
+
const on = (p === 'pm') === pm;
|
|
568
573
|
return (
|
|
569
574
|
<Pressable
|
|
570
575
|
key={p}
|
|
571
|
-
onPress={() => setPeriod(p === '
|
|
576
|
+
onPress={() => setPeriod(p === 'pm')}
|
|
572
577
|
accessibilityRole="radio"
|
|
573
|
-
accessibilityLabel={p}
|
|
578
|
+
accessibilityLabel={labels[p]}
|
|
574
579
|
accessibilityState={{ checked: on }}
|
|
575
580
|
style={[
|
|
576
581
|
styles.periodBtn,
|
|
@@ -578,7 +583,7 @@ function Clock({ hour12, step, value, onChange, accent, style, reduce }: ClockPr
|
|
|
578
583
|
i === 1 && { borderTopWidth: StyleSheet.hairlineWidth * 2, borderTopColor: colors.border },
|
|
579
584
|
]}
|
|
580
585
|
>
|
|
581
|
-
<Text maxFontSizeMultiplier={FONT_CAP} style={[styles.periodText, ff, { color: on ? tint : colors.mutedForeground }]}>{p}</Text>
|
|
586
|
+
<Text maxFontSizeMultiplier={FONT_CAP} style={[styles.periodText, ff, { color: on ? tint : colors.mutedForeground }]}>{labels[p]}</Text>
|
|
582
587
|
</Pressable>
|
|
583
588
|
);
|
|
584
589
|
})}
|
|
@@ -587,12 +592,12 @@ function Clock({ hour12, step, value, onChange, accent, style, reduce }: ClockPr
|
|
|
587
592
|
</View>
|
|
588
593
|
|
|
589
594
|
<View
|
|
590
|
-
{...pan
|
|
595
|
+
{...pan}
|
|
591
596
|
accessible
|
|
592
597
|
accessibilityRole="adjustable"
|
|
593
|
-
accessibilityLabel={mode === 'hour' ?
|
|
594
|
-
accessibilityHint=
|
|
595
|
-
accessibilityValue={{ text: mode === 'hour' ? `${knobLabel || hourText} ${
|
|
598
|
+
accessibilityLabel={mode === 'hour' ? labels.hour : labels.minute}
|
|
599
|
+
accessibilityHint={labels.timeDialHint}
|
|
600
|
+
accessibilityValue={{ text: mode === 'hour' ? (hour12 ? `${knobLabel || hourText} ${pm ? labels.pm : labels.am}` : labels.hours(knobLabel || hourText)) : labels.minutes(t.minutes) }}
|
|
596
601
|
accessibilityActions={[{ name: 'increment' }, { name: 'decrement' }]}
|
|
597
602
|
onAccessibilityAction={(e) => adjust(e.nativeEvent.actionName === 'increment' ? 1 : -1)}
|
|
598
603
|
style={[styles.face, { width: size, height: size }]}
|
|
@@ -667,7 +672,7 @@ function SlotGrid({
|
|
|
667
672
|
onPick,
|
|
668
673
|
reduce,
|
|
669
674
|
}: SlotGridProps) {
|
|
670
|
-
const { colors, font } = useKit();
|
|
675
|
+
const { colors, font, labels } = useKit();
|
|
671
676
|
const ff = font.family ? { fontFamily: font.family } : null;
|
|
672
677
|
const tint = accent ?? colors.accent;
|
|
673
678
|
const list = useMemo(() => {
|
|
@@ -683,12 +688,12 @@ function SlotGrid({
|
|
|
683
688
|
const groups = useMemo(() => {
|
|
684
689
|
if (!groupByPeriod) return [{ title: '', items: list }];
|
|
685
690
|
const g = [
|
|
686
|
-
{ title:
|
|
687
|
-
{ title:
|
|
688
|
-
{ title:
|
|
691
|
+
{ title: labels.morning, items: list.filter((n) => n < 720) },
|
|
692
|
+
{ title: labels.afternoon, items: list.filter((n) => n >= 720 && n < 1020) },
|
|
693
|
+
{ title: labels.evening, items: list.filter((n) => n >= 1020) },
|
|
689
694
|
];
|
|
690
695
|
return g.filter((x) => x.items.length);
|
|
691
|
-
}, [list, groupByPeriod]);
|
|
696
|
+
}, [list, groupByPeriod, labels]);
|
|
692
697
|
|
|
693
698
|
return (
|
|
694
699
|
<View style={[styles.slots, style]} accessibilityRole="radiogroup">
|
|
@@ -699,7 +704,7 @@ function SlotGrid({
|
|
|
699
704
|
{g.title ? (
|
|
700
705
|
<View style={styles.groupHead}>
|
|
701
706
|
<Text style={[styles.groupTitle, ff, { color: colors.foreground }]}>{g.title}</Text>
|
|
702
|
-
<Text style={[styles.groupCount, ff, { color: colors.mutedForeground }]}>{free ?
|
|
707
|
+
<Text style={[styles.groupCount, ff, { color: colors.mutedForeground }]}>{free ? labels.slotsOpen(free) : labels.full}</Text>
|
|
703
708
|
</View>
|
|
704
709
|
) : null}
|
|
705
710
|
<View style={styles.grid}>
|
|
@@ -707,7 +712,7 @@ function SlotGrid({
|
|
|
707
712
|
<View key={n} style={[styles.chipCell, { width: `${100 / columns}%` }]}>
|
|
708
713
|
<Chip
|
|
709
714
|
minutes={n}
|
|
710
|
-
label={formatTime(fromMin(n), hour12)}
|
|
715
|
+
label={formatTime(fromMin(n), hour12, labels)}
|
|
711
716
|
selected={n === selected}
|
|
712
717
|
booked={booked.has(n)}
|
|
713
718
|
disabled={off.has(n)}
|
|
@@ -738,9 +743,9 @@ interface ChipProps {
|
|
|
738
743
|
}
|
|
739
744
|
|
|
740
745
|
const Chip = memo(function Chip({ minutes, label, selected, booked, disabled, tint, reduce, onPick }: ChipProps) {
|
|
741
|
-
const { colors, radius, font } = useKit();
|
|
742
|
-
const pop =
|
|
743
|
-
const on =
|
|
746
|
+
const { colors, radius, font, labels } = useKit();
|
|
747
|
+
const [pop] = useState(() => new Animated.Value(1)); // native: press + select spring
|
|
748
|
+
const [on] = useState(() => new Animated.Value(selected ? 1 : 0)); // native: fill opacity
|
|
744
749
|
useEffect(() => {
|
|
745
750
|
Animated.timing(on, { toValue: selected ? 1 : 0, duration: motion.fast, useNativeDriver: true }).start();
|
|
746
751
|
if (selected && !reduce) {
|
|
@@ -757,7 +762,7 @@ const Chip = memo(function Chip({ minutes, label, selected, booked, disabled, ti
|
|
|
757
762
|
onPressOut={reduce ? undefined : () => Animated.spring(pop, { toValue: 1, useNativeDriver: true, ...motion.bouncy }).start()}
|
|
758
763
|
disabled={blocked}
|
|
759
764
|
accessibilityRole="radio"
|
|
760
|
-
accessibilityLabel={`${label}${booked ?
|
|
765
|
+
accessibilityLabel={`${label}${booked ? `, ${labels.booked}` : disabled ? `, ${labels.unavailable}` : ''}`}
|
|
761
766
|
accessibilityState={{ checked: selected, disabled: blocked }}
|
|
762
767
|
>
|
|
763
768
|
<Animated.View
|
|
@@ -807,10 +812,10 @@ function TimeField({
|
|
|
807
812
|
current,
|
|
808
813
|
commit,
|
|
809
814
|
reduce,
|
|
810
|
-
label
|
|
815
|
+
label: labelProp,
|
|
811
816
|
floatingLabel = true,
|
|
812
817
|
title,
|
|
813
|
-
placeholder
|
|
818
|
+
placeholder: placeholderProp,
|
|
814
819
|
size = 'md',
|
|
815
820
|
confirm,
|
|
816
821
|
error,
|
|
@@ -829,7 +834,9 @@ function TimeField({
|
|
|
829
834
|
menuStyle,
|
|
830
835
|
...chipProps
|
|
831
836
|
}: TimeFieldProps) {
|
|
832
|
-
const { colors, radius, font } = useKit();
|
|
837
|
+
const { colors, radius, font, dark, shadow, labels } = useKit();
|
|
838
|
+
const label = labelProp ?? labels.time;
|
|
839
|
+
const placeholder = placeholderProp ?? labels.selectTime;
|
|
833
840
|
const ff = font.family ? { fontFamily: font.family } : null;
|
|
834
841
|
const tint = accent ?? colors.accent;
|
|
835
842
|
const bg = surface ?? colors.background;
|
|
@@ -840,27 +847,26 @@ function TimeField({
|
|
|
840
847
|
const sz = typeof userField.height === 'number' ? { ...base, h: userField.height } : base;
|
|
841
848
|
const fieldRadius =
|
|
842
849
|
typeof userField.borderRadius === 'number' ? userField.borderRadius : size === 'sm' ? radius.md - 2 : size === 'lg' ? radius.lg : radius.md;
|
|
843
|
-
const
|
|
844
|
-
const
|
|
850
|
+
const layout = useLayout();
|
|
851
|
+
const { height: winH } = layout;
|
|
852
|
+
// A centred card whenever the kit caps the sheet (foldables, tablets, landscape phones).
|
|
853
|
+
const wide = layout.sheetWidth < layout.width;
|
|
845
854
|
const staged = confirm ?? variant !== 'chips';
|
|
846
855
|
|
|
847
856
|
const [open, setOpen] = useState(false);
|
|
848
857
|
const [mounted, setMounted] = useState(false);
|
|
849
858
|
const [draft, setDraft] = useState<TimeValue | null>(current);
|
|
850
859
|
const closeTimer = useRef<ReturnType<typeof setTimeout> | null>(null);
|
|
851
|
-
// Read by the sheet's pan responder, which is created once.
|
|
852
|
-
const reduceRef = useRef(reduce);
|
|
853
|
-
reduceRef.current = reduce;
|
|
854
860
|
|
|
855
861
|
const ok = !error && !!success;
|
|
856
862
|
const floating = open || !!current;
|
|
857
|
-
const float =
|
|
858
|
-
const focus =
|
|
859
|
-
const halo =
|
|
860
|
-
const show =
|
|
861
|
-
const drag =
|
|
862
|
-
const shake =
|
|
863
|
-
const check =
|
|
863
|
+
const [float] = useState(() => new Animated.Value(floating ? 1 : 0)); // native
|
|
864
|
+
const [focus] = useState(() => new Animated.Value(0)); // JS: colours
|
|
865
|
+
const [halo] = useState(() => new Animated.Value(0)); // native
|
|
866
|
+
const [show] = useState(() => new Animated.Value(0)); // native
|
|
867
|
+
const [drag] = useState(() => new Animated.Value(0)); // native
|
|
868
|
+
const [shake] = useState(() => new Animated.Value(0)); // native
|
|
869
|
+
const [check] = useState(() => new Animated.Value(ok ? 1 : 0)); // native
|
|
864
870
|
|
|
865
871
|
useEffect(() => {
|
|
866
872
|
if (reduce) float.setValue(floating ? 1 : 0);
|
|
@@ -917,21 +923,19 @@ function TimeField({
|
|
|
917
923
|
}
|
|
918
924
|
};
|
|
919
925
|
|
|
920
|
-
const
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
});
|
|
934
|
-
}, [drag]);
|
|
926
|
+
const back = () =>
|
|
927
|
+
reduce
|
|
928
|
+
? Animated.timing(drag, { toValue: 0, duration: 150, easing: Easing.bezier(0.32, 0.72, 0, 1), useNativeDriver: true }).start()
|
|
929
|
+
: Animated.spring(drag, { toValue: 0, useNativeDriver: true, ...motion.snappy }).start();
|
|
930
|
+
const pan = usePanResponder({
|
|
931
|
+
onMoveShouldSetPanResponder: (_, g) => g.dy > 4 && Math.abs(g.dy) > Math.abs(g.dx),
|
|
932
|
+
onPanResponderMove: (_, g) => drag.setValue(Math.max(0, g.dy)),
|
|
933
|
+
onPanResponderRelease: (_, g) => {
|
|
934
|
+
if (g.dy > 110 || g.vy > 1.1) setOpen(false);
|
|
935
|
+
else back();
|
|
936
|
+
},
|
|
937
|
+
onPanResponderTerminate: back,
|
|
938
|
+
});
|
|
935
939
|
|
|
936
940
|
const active = error ? colors.danger : tint;
|
|
937
941
|
const rest = error ? colors.danger : ok ? colors.success : ((userBorder as string | undefined) ?? colors.input);
|
|
@@ -941,7 +945,7 @@ function TimeField({
|
|
|
941
945
|
outputRange: [error ? colors.danger : ok ? colors.success : (userLabelColor ?? colors.mutedForeground), active],
|
|
942
946
|
});
|
|
943
947
|
const topLabelColor = focus.interpolate({ inputRange: [0, 1], outputRange: [error ? colors.danger : (userLabelColor ?? colors.foreground), active] });
|
|
944
|
-
const text = current ? formatTime(current, hour12) : '';
|
|
948
|
+
const text = current ? formatTime(current, hour12, labels) : '';
|
|
945
949
|
const successText = typeof success === 'string' && ok ? success : undefined;
|
|
946
950
|
const message = error ?? successText ?? helper;
|
|
947
951
|
const tone = error ? 'error' : successText ? 'success' : 'helper';
|
|
@@ -965,7 +969,7 @@ function TimeField({
|
|
|
965
969
|
disabled={disabled}
|
|
966
970
|
accessibilityRole="button"
|
|
967
971
|
accessibilityLabel={label}
|
|
968
|
-
accessibilityValue={{ text: text ||
|
|
972
|
+
accessibilityValue={{ text: text || labels.notSet }}
|
|
969
973
|
accessibilityState={{ expanded: open, disabled: !!disabled }}
|
|
970
974
|
style={{ opacity: disabled ? 0.5 : 1 }}
|
|
971
975
|
>
|
|
@@ -1031,7 +1035,7 @@ function TimeField({
|
|
|
1031
1035
|
|
|
1032
1036
|
<Modal visible={mounted} transparent animationType="none" statusBarTranslucent onRequestClose={close}>
|
|
1033
1037
|
<Animated.View style={[StyleSheet.absoluteFill, { backgroundColor: colors.overlay, opacity: show }]}>
|
|
1034
|
-
<Pressable style={StyleSheet.absoluteFill} onPress={close} accessibilityLabel=
|
|
1038
|
+
<Pressable style={StyleSheet.absoluteFill} onPress={close} accessibilityLabel={labels.close} accessibilityRole="button" />
|
|
1035
1039
|
</Animated.View>
|
|
1036
1040
|
<View style={[styles.sheetHost, wide && styles.centered]} pointerEvents="box-none">
|
|
1037
1041
|
<Animated.View
|
|
@@ -1040,8 +1044,14 @@ function TimeField({
|
|
|
1040
1044
|
styles.sheet,
|
|
1041
1045
|
{ backgroundColor: colors.popover, borderColor: colors.border, maxHeight: winH * 0.9 },
|
|
1042
1046
|
wide
|
|
1043
|
-
? { width: Math.min(420,
|
|
1044
|
-
: {
|
|
1047
|
+
? { width: Math.min(420, layout.sheetWidth), borderRadius: radius.xl, paddingBottom: 14, ...shadow('xl') }
|
|
1048
|
+
: {
|
|
1049
|
+
alignSelf: 'stretch',
|
|
1050
|
+
borderTopLeftRadius: radius.xl + 4,
|
|
1051
|
+
borderTopRightRadius: radius.xl + 4,
|
|
1052
|
+
paddingBottom: 30,
|
|
1053
|
+
boxShadow: `0px -4px 32px rgba(0,0,0,${dark ? 0.45 : 0.18})`,
|
|
1054
|
+
},
|
|
1045
1055
|
menuStyle,
|
|
1046
1056
|
wide
|
|
1047
1057
|
? {
|
|
@@ -1058,11 +1068,11 @@ function TimeField({
|
|
|
1058
1068
|
},
|
|
1059
1069
|
]}
|
|
1060
1070
|
>
|
|
1061
|
-
<View {...pan
|
|
1071
|
+
<View {...pan} style={styles.sheetHeader}>
|
|
1062
1072
|
{!wide ? <View style={[styles.grabber, { backgroundColor: colors.border }]} /> : null}
|
|
1063
1073
|
<Text style={[styles.title, ff, { color: colors.foreground }]}>{title ?? label}</Text>
|
|
1064
1074
|
{variant === 'clock' ? null : (
|
|
1065
|
-
<Text style={[styles.preview, ff, { color: draft ? tint : colors.mutedForeground }]}>{draft ? formatTime(draft, hour12) : placeholder}</Text>
|
|
1075
|
+
<Text style={[styles.preview, ff, { color: draft ? tint : colors.mutedForeground }]}>{draft ? formatTime(draft, hour12, labels) : placeholder}</Text>
|
|
1066
1076
|
)}
|
|
1067
1077
|
</View>
|
|
1068
1078
|
<ScrollView style={styles.sheetBody} contentContainerStyle={styles.sheetContent} bounces={false} scrollEnabled={variant === 'chips'}>
|
|
@@ -1077,7 +1087,7 @@ function TimeField({
|
|
|
1077
1087
|
{staged ? (
|
|
1078
1088
|
<View style={[styles.footer, { borderTopColor: colors.border }]}>
|
|
1079
1089
|
<Pressable onPress={close} accessibilityRole="button" style={({ pressed }) => [styles.footBtn, { borderRadius: radius.md, opacity: pressed ? 0.6 : 1 }]}>
|
|
1080
|
-
<Text maxFontSizeMultiplier={FONT_CAP} style={[styles.footText, ff, { color: colors.foreground }]}>
|
|
1090
|
+
<Text maxFontSizeMultiplier={FONT_CAP} style={[styles.footText, ff, { color: colors.foreground }]}>{labels.cancel}</Text>
|
|
1081
1091
|
</Pressable>
|
|
1082
1092
|
<Pressable
|
|
1083
1093
|
onPress={() => {
|
|
@@ -1092,7 +1102,7 @@ function TimeField({
|
|
|
1092
1102
|
{ backgroundColor: tint, borderRadius: radius.md, opacity: !draft ? 0.4 : pressed ? 0.85 : 1 },
|
|
1093
1103
|
]}
|
|
1094
1104
|
>
|
|
1095
|
-
<Text maxFontSizeMultiplier={FONT_CAP} style={[styles.footText, ff, { color: readableOn(tint) }]}>{draft ?
|
|
1105
|
+
<Text maxFontSizeMultiplier={FONT_CAP} style={[styles.footText, ff, { color: readableOn(tint) }]}>{draft ? labels.setTime(formatTime(draft, hour12, labels)) : labels.done}</Text>
|
|
1096
1106
|
</Pressable>
|
|
1097
1107
|
</View>
|
|
1098
1108
|
) : null}
|
|
@@ -1116,7 +1126,7 @@ function FieldMessage({
|
|
|
1116
1126
|
style?: StyleProp<TextStyle>;
|
|
1117
1127
|
}) {
|
|
1118
1128
|
const { colors, font } = useKit();
|
|
1119
|
-
const enter =
|
|
1129
|
+
const [enter] = useState(() => new Animated.Value(tone === 'helper' ? 1 : 0)); // native
|
|
1120
1130
|
useEffect(() => {
|
|
1121
1131
|
if (reduce) Animated.timing(enter, { toValue: 1, duration: 150, useNativeDriver: true }).start();
|
|
1122
1132
|
else Animated.spring(enter, { toValue: 1, useNativeDriver: true, ...motion.snappy }).start();
|
|
@@ -1193,7 +1203,7 @@ const styles = StyleSheet.create({
|
|
|
1193
1203
|
clockColon: { width: 22, textAlign: 'center', fontWeight: '400', includeFontPadding: false, transform: [{ translateY: -3 }] },
|
|
1194
1204
|
period: { width: 52, marginLeft: 12, borderWidth: StyleSheet.hairlineWidth * 2, overflow: 'hidden' },
|
|
1195
1205
|
periodBtn: { flex: 1, alignItems: 'center', justifyContent: 'center' },
|
|
1196
|
-
periodText: { fontSize: 14, fontWeight: '400', letterSpacing: 0.3 },
|
|
1206
|
+
periodText: { fontSize: 14, fontWeight: '400', letterSpacing: 0.3, includeFontPadding: false, textAlignVertical: 'center' },
|
|
1197
1207
|
face: { alignSelf: 'center' },
|
|
1198
1208
|
num: { position: 'absolute', width: KNOB * 2, height: KNOB * 2, alignItems: 'center', justifyContent: 'center' },
|
|
1199
1209
|
numText: { fontSize: 16, fontWeight: '400', fontVariant: ['tabular-nums'], includeFontPadding: false },
|
|
@@ -1206,7 +1216,7 @@ const styles = StyleSheet.create({
|
|
|
1206
1216
|
grid: { flexDirection: 'row', flexWrap: 'wrap', marginHorizontal: -4 },
|
|
1207
1217
|
chipCell: { padding: 4 },
|
|
1208
1218
|
chip: { minHeight: 44, borderWidth: 1, alignItems: 'center', justifyContent: 'center', overflow: 'hidden' },
|
|
1209
|
-
chipText: { fontSize: 14.5, fontWeight: '400', fontVariant: ['tabular-nums'] },
|
|
1219
|
+
chipText: { fontSize: 14.5, fontWeight: '400', fontVariant: ['tabular-nums'], includeFontPadding: false, textAlignVertical: 'center' },
|
|
1210
1220
|
struck: { textDecorationLine: 'line-through' },
|
|
1211
1221
|
field: { borderWidth: 1.5, justifyContent: 'center' },
|
|
1212
1222
|
halo: { position: 'absolute', top: -4, left: -4, right: -4, bottom: -4, borderWidth: 4 },
|
|
@@ -1221,11 +1231,6 @@ const styles = StyleSheet.create({
|
|
|
1221
1231
|
centered: { justifyContent: 'center', alignItems: 'center' },
|
|
1222
1232
|
sheet: {
|
|
1223
1233
|
borderWidth: StyleSheet.hairlineWidth,
|
|
1224
|
-
shadowColor: '#000',
|
|
1225
|
-
shadowOpacity: 0.18,
|
|
1226
|
-
shadowRadius: 24,
|
|
1227
|
-
shadowOffset: { width: 0, height: -4 },
|
|
1228
|
-
elevation: 16,
|
|
1229
1234
|
},
|
|
1230
1235
|
sheetHeader: { alignItems: 'center', paddingTop: 10, paddingBottom: 10, paddingHorizontal: 16 },
|
|
1231
1236
|
grabber: { width: 40, height: 5, borderRadius: 3, marginBottom: 12 },
|