@expokit/cli 0.4.0 → 0.4.2
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 +17 -0
- package/bin/lib.mjs +1 -1
- package/bin/liquidglass.mjs +281 -0
- package/package.json +17 -3
- package/registry/_docs/accordion.json +1 -1
- package/registry/_docs/avatar.json +6 -6
- package/registry/_docs/badge.json +3 -3
- package/registry/_docs/bottom-nav.json +6 -6
- package/registry/_docs/bottom-sheet.json +3 -3
- package/registry/_docs/bullet.json +6 -0
- package/registry/_docs/button-3d.json +2 -2
- package/registry/_docs/button.json +2 -2
- package/registry/_docs/calendar.json +5 -5
- package/registry/_docs/card.json +1 -1
- package/registry/_docs/chip-group.json +1 -1
- package/registry/_docs/confetti.json +3 -3
- package/registry/_docs/date-picker.json +4 -4
- package/registry/_docs/drawer.json +4 -4
- package/registry/_docs/flip-card.json +4 -4
- package/registry/_docs/floating-input.json +1 -1
- package/registry/_docs/grain.json +2 -2
- package/registry/_docs/isometric-bar.json +6 -0
- package/registry/_docs/liquidglass-tab.json +2 -1
- package/registry/_docs/otp-input.json +1 -1
- package/registry/_docs/payment-card.json +3 -3
- package/registry/_docs/pixel-bar.json +6 -0
- package/registry/_docs/polar-area.json +6 -0
- package/registry/_docs/progress-steps.json +4 -4
- package/registry/_docs/progress.json +2 -2
- package/registry/_docs/radio-group.json +1 -1
- package/registry/_docs/search-bar.json +3 -3
- package/registry/_docs/segmented-control.json +1 -1
- package/registry/_docs/select.json +6 -6
- package/registry/_docs/skeleton.json +1 -1
- package/registry/_docs/slider.json +6 -6
- package/registry/_docs/spinner.json +2 -2
- package/registry/_docs/stateful-button.json +1 -1
- package/registry/_docs/stepper.json +3 -3
- package/registry/_docs/tabs.json +2 -2
- package/registry/_docs/text-animation.json +4 -4
- package/registry/_docs/textarea.json +1 -1
- package/registry/_docs/tilt-card.json +3 -3
- package/registry/_docs/time-picker.json +6 -6
- package/registry/_docs/timeline-chart.json +6 -0
- package/registry/_docs/timeline.json +4 -4
- package/registry/_docs/treemap.json +6 -0
- package/registry/_docs/waterfall.json +6 -0
- package/registry/accordion/files/src/components/ui/accordion.tsx +31 -3
- package/registry/action-sheet/files/src/components/ui/action-sheet.tsx +4 -1
- package/registry/alert/files/src/components/ui/alert.tsx +5 -2
- package/registry/alert/files/src/components/ui/button.tsx +32 -4
- package/registry/badge/files/src/components/ui/badge.tsx +2 -2
- package/registry/bottom-nav/files/src/components/ui/bottom-nav.tsx +11 -5
- package/registry/button/files/src/components/ui/button.tsx +32 -4
- package/registry/button-3d/files/src/components/ui/button-3d.tsx +33 -6
- package/registry/calendar/files/src/components/ui/calendar.tsx +62 -17
- package/registry/card/files/src/components/ui/card.tsx +25 -5
- package/registry/checkbox/files/src/components/ui/checkbox.tsx +26 -4
- package/registry/chip-group/files/src/components/ui/chip-group.tsx +35 -5
- package/registry/coupon/files/src/components/ui/coupon.tsx +45 -21
- package/registry/coupon/files/src/components/ui/flip-card.tsx +22 -3
- package/registry/coupon/files/src/components/ui/tilt-card.tsx +57 -16
- package/registry/date-picker/files/src/components/ui/calendar.tsx +62 -17
- package/registry/date-picker/files/src/components/ui/date-picker.tsx +96 -42
- package/registry/dialog/files/src/components/ui/dialog.tsx +9 -5
- package/registry/drawer/files/src/components/ui/drawer.tsx +8 -4
- package/registry/flip-card/files/src/components/ui/flip-card.tsx +22 -3
- package/registry/floating-input/files/src/components/ui/floating-input.tsx +75 -26
- package/registry/liquidglass-tab/files/src/app/(tabs)/_layout.tsx +3 -94
- package/registry/liquidglass-tab/files/src/app/(tabs)/alerts.tsx +2 -1
- package/registry/liquidglass-tab/files/src/app/_layout.tsx +2 -2
- package/registry/liquidglass-tab/files/src/components/GlassTabBar.tsx +2 -1
- package/registry/liquidglass-tab/files/src/components/LiquidGlassTabs.tsx +110 -0
- package/registry/liquidglass-tab/files/src/components/TabScreen.tsx +31 -6
- package/registry/liquidglass-tab/files/src/lib/tab-bar.ts +12 -4
- package/registry/liquidglass-tab/files/src/lib/tab-icons.ts +6 -3
- package/registry/liquidglass-tab/files/src/lib/tabs.ts +4 -2
- package/registry/list-item/files/src/components/ui/list-item.tsx +10 -3
- package/registry/otp-input/files/src/components/ui/otp-input.tsx +50 -19
- package/registry/password-input/files/src/components/ui/floating-input.tsx +75 -26
- package/registry/password-input/files/src/components/ui/password-input.tsx +53 -19
- package/registry/payment-card/files/src/components/ui/flip-card.tsx +22 -3
- package/registry/payment-card/files/src/components/ui/payment-card.tsx +19 -15
- package/registry/payment-card/files/src/components/ui/tilt-card.tsx +57 -16
- package/registry/progress/files/src/components/ui/progress.tsx +9 -2
- package/registry/progress-steps/files/src/components/ui/progress-steps.tsx +11 -4
- package/registry/radio-group/files/src/components/ui/radio-group.tsx +39 -9
- package/registry/search-bar/files/src/components/ui/search-bar.tsx +70 -17
- package/registry/segmented-control/files/src/components/ui/segmented-control.tsx +37 -9
- package/registry/select/files/src/components/ui/select.tsx +158 -83
- package/registry/slider/files/src/components/ui/slider.tsx +53 -25
- package/registry/stateful-button/files/src/components/ui/stateful-button.tsx +12 -4
- package/registry/stepper/files/src/components/ui/stepper.tsx +76 -26
- package/registry/switch/files/src/components/ui/switch.tsx +56 -19
- package/registry/tabs/files/src/components/ui/tabs.tsx +7 -4
- package/registry/text-animation/files/src/components/ui/text-animation.tsx +7 -2
- package/registry/textarea/files/src/components/ui/textarea.tsx +104 -36
- package/registry/tilt-card/files/src/components/ui/tilt-card.tsx +57 -16
- package/registry/time-picker/files/src/components/ui/time-picker.tsx +154 -73
- package/registry/timeline/files/src/components/ui/timeline.tsx +9 -4
- package/registry/toast/files/src/components/ui/button.tsx +32 -4
- package/registry/toast/files/src/components/ui/toast.tsx +15 -6
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import React, { useCallback, useEffect, useMemo, useRef, useState, type ComponentRef } from 'react';
|
|
2
2
|
import {
|
|
3
|
+
AccessibilityInfo,
|
|
3
4
|
Animated,
|
|
4
5
|
Dimensions,
|
|
5
6
|
Easing,
|
|
@@ -135,6 +136,9 @@ const EDGE = 12; // keep the dropdown this far from the window edges
|
|
|
135
136
|
const PANEL_PAD = 6;
|
|
136
137
|
const SEARCH_H = 46;
|
|
137
138
|
const FOOT_H = 48;
|
|
139
|
+
// Controls stop growing at iOS's largest standard text size (xxxL ≈ 1.35×) so they keep their shape;
|
|
140
|
+
// content text scales all the way.
|
|
141
|
+
const FONT_CAP = 1.35;
|
|
138
142
|
|
|
139
143
|
const normalize = <T extends string | number>(o: T | SelectOption<T>): SelectOption<T> =>
|
|
140
144
|
typeof o === 'object' ? o : { label: String(o), value: o };
|
|
@@ -190,6 +194,7 @@ export function Select<T extends string | number = string>({
|
|
|
190
194
|
optionTextStyle,
|
|
191
195
|
}: SelectProps<T>) {
|
|
192
196
|
const { colors, radius, dark, font } = useKit();
|
|
197
|
+
const reduce = useReduceMotion();
|
|
193
198
|
const ff = font.family ? { fontFamily: font.family } : null;
|
|
194
199
|
const tint = accent ?? colors.accent;
|
|
195
200
|
const bg = surface ?? colors.background;
|
|
@@ -226,6 +231,9 @@ export function Select<T extends string | number = string>({
|
|
|
226
231
|
const viewH = useRef(0);
|
|
227
232
|
const closeTimer = useRef<ReturnType<typeof setTimeout> | null>(null);
|
|
228
233
|
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;
|
|
229
237
|
|
|
230
238
|
const floating = open || picked.length > 0;
|
|
231
239
|
const float = useRef(new Animated.Value(floating ? 1 : 0)).current; // native
|
|
@@ -244,29 +252,35 @@ export function Select<T extends string | number = string>({
|
|
|
244
252
|
}, []);
|
|
245
253
|
|
|
246
254
|
useEffect(() => {
|
|
247
|
-
|
|
248
|
-
|
|
255
|
+
if (reduce) float.setValue(floating ? 1 : 0);
|
|
256
|
+
else Animated.spring(float, { toValue: floating ? 1 : 0, useNativeDriver: true, ...motion.snappy }).start();
|
|
257
|
+
}, [floating, float, reduce]);
|
|
249
258
|
|
|
250
259
|
useEffect(() => {
|
|
251
260
|
Animated.timing(focus, { toValue: open ? 1 : 0, duration: 160, easing: Easing.out(Easing.quad), useNativeDriver: false }).start();
|
|
252
|
-
|
|
253
|
-
|
|
261
|
+
// Reduce Motion: the halo only fades (its scale is pinned below).
|
|
262
|
+
if (reduce) Animated.timing(halo, { toValue: open ? 1 : 0, duration: 150, useNativeDriver: true }).start();
|
|
263
|
+
else Animated.spring(halo, { toValue: open ? 1 : 0, useNativeDriver: true, ...motion.snappy }).start();
|
|
264
|
+
}, [open, focus, halo, reduce]);
|
|
254
265
|
|
|
255
266
|
useEffect(() => {
|
|
256
|
-
if (!error) return;
|
|
267
|
+
if (!error || reduce) return;
|
|
257
268
|
shake.setValue(0);
|
|
258
269
|
Animated.sequence(
|
|
259
270
|
[10, -8, 6, -4, 0].map((x) => Animated.timing(shake, { toValue: x, duration: 55, useNativeDriver: true }))
|
|
260
271
|
).start();
|
|
261
|
-
}, [error, shake]);
|
|
272
|
+
}, [error, shake, reduce]);
|
|
262
273
|
|
|
263
274
|
useEffect(() => {
|
|
264
275
|
if (open) {
|
|
265
276
|
drag.setValue(0);
|
|
266
|
-
|
|
277
|
+
// Reduce Motion: the panel's slide / scale is pinned below, so `show` only fades it in place.
|
|
278
|
+
if (reduce) Animated.timing(show, { toValue: 1, duration: 150, easing: Easing.out(Easing.cubic), useNativeDriver: true }).start();
|
|
279
|
+
else if (asSheet) Animated.spring(show, { toValue: 1, useNativeDriver: true, ...motion.gentle }).start();
|
|
267
280
|
else Animated.timing(show, { toValue: 1, duration: 170, easing: Easing.out(Easing.cubic), useNativeDriver: true }).start();
|
|
268
281
|
} else if (mounted) {
|
|
269
|
-
|
|
282
|
+
const duration = asSheet ? (reduce ? 150 : motion.fast + 40) : 120;
|
|
283
|
+
Animated.timing(show, { toValue: 0, duration, easing: Easing.in(Easing.quad), useNativeDriver: true }).start(
|
|
270
284
|
({ finished }) => finished && setMounted(false)
|
|
271
285
|
);
|
|
272
286
|
}
|
|
@@ -451,19 +465,21 @@ export function Select<T extends string | number = string>({
|
|
|
451
465
|
}, [open]);
|
|
452
466
|
|
|
453
467
|
// Drag the sheet's grabber down to dismiss.
|
|
454
|
-
const pan = useMemo(
|
|
455
|
-
() =>
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
468
|
+
const pan = useMemo(() => {
|
|
469
|
+
const back = () =>
|
|
470
|
+
reduceRef.current
|
|
471
|
+
? Animated.timing(drag, { toValue: 0, duration: 150, easing: Easing.bezier(0.32, 0.72, 0, 1), useNativeDriver: true }).start()
|
|
472
|
+
: Animated.spring(drag, { toValue: 0, useNativeDriver: true, ...motion.snappy }).start();
|
|
473
|
+
return PanResponder.create({
|
|
474
|
+
onMoveShouldSetPanResponder: (_, g) => g.dy > 4 && Math.abs(g.dy) > Math.abs(g.dx),
|
|
475
|
+
onPanResponderMove: (_, g) => drag.setValue(Math.max(0, g.dy)),
|
|
476
|
+
onPanResponderRelease: (_, g) => {
|
|
477
|
+
if (g.dy > 110 || g.vy > 1.1) setOpen(false);
|
|
478
|
+
else back();
|
|
479
|
+
},
|
|
480
|
+
onPanResponderTerminate: back,
|
|
481
|
+
});
|
|
482
|
+
}, [drag]);
|
|
467
483
|
|
|
468
484
|
const active = error ? colors.danger : tint;
|
|
469
485
|
const borderColor = focus.interpolate({
|
|
@@ -499,6 +515,7 @@ export function Select<T extends string | number = string>({
|
|
|
499
515
|
autoFocus={isWeb}
|
|
500
516
|
returnKeyType="done"
|
|
501
517
|
accessibilityLabel={searchPlaceholder}
|
|
518
|
+
maxFontSizeMultiplier={FONT_CAP}
|
|
502
519
|
style={[styles.searchInput, ff, { color: colors.foreground, fontSize: dense ? 15 : 16 }, webInput]}
|
|
503
520
|
/>
|
|
504
521
|
{query ? (
|
|
@@ -553,6 +570,7 @@ export function Select<T extends string | number = string>({
|
|
|
553
570
|
rowRadius={rowRadius}
|
|
554
571
|
query={q}
|
|
555
572
|
tint={tint}
|
|
573
|
+
reduce={reduce}
|
|
556
574
|
rowStyle={optionStyle}
|
|
557
575
|
textStyle={optionTextStyle}
|
|
558
576
|
onPress={() => choose(o)}
|
|
@@ -591,17 +609,21 @@ export function Select<T extends string | number = string>({
|
|
|
591
609
|
{ borderRadius: radius.capsule(32), opacity: many.length ? 1 : 0.4, backgroundColor: pressed ? colors.muted : 'transparent' },
|
|
592
610
|
]}
|
|
593
611
|
>
|
|
594
|
-
<Text style={[styles.footDenseText, ff, { color: colors.mutedForeground }]}>
|
|
612
|
+
<Text maxFontSizeMultiplier={FONT_CAP} style={[styles.footDenseText, ff, { color: colors.mutedForeground }]}>
|
|
613
|
+
Clear
|
|
614
|
+
</Text>
|
|
595
615
|
</Pressable>
|
|
596
616
|
<View style={styles.footMid}>
|
|
597
|
-
<CountBadge count={many.length} tint={tint} />
|
|
617
|
+
<CountBadge count={many.length} tint={tint} reduce={reduce} />
|
|
598
618
|
</View>
|
|
599
619
|
<Pressable
|
|
600
620
|
onPress={close}
|
|
601
621
|
accessibilityRole="button"
|
|
602
622
|
style={({ pressed }) => [styles.footSolid, { backgroundColor: tint, borderRadius: radius.capsule(32), opacity: pressed ? 0.85 : 1 }]}
|
|
603
623
|
>
|
|
604
|
-
<Text style={[styles.footDenseText, styles.footStrong, ff, { color: readableOn(tint) }]}>
|
|
624
|
+
<Text maxFontSizeMultiplier={FONT_CAP} style={[styles.footDenseText, styles.footStrong, ff, { color: readableOn(tint) }]}>
|
|
625
|
+
Done
|
|
626
|
+
</Text>
|
|
605
627
|
</Pressable>
|
|
606
628
|
</View>
|
|
607
629
|
) : (
|
|
@@ -612,14 +634,16 @@ export function Select<T extends string | number = string>({
|
|
|
612
634
|
accessibilityRole="button"
|
|
613
635
|
style={({ pressed }) => [styles.footBtn, { borderRadius: radius.md, opacity: many.length ? (pressed ? 0.6 : 1) : 0.4 }]}
|
|
614
636
|
>
|
|
615
|
-
<Text style={[styles.footText, ff, { color: colors.foreground }]}>
|
|
637
|
+
<Text maxFontSizeMultiplier={FONT_CAP} style={[styles.footText, ff, { color: colors.foreground }]}>
|
|
638
|
+
Clear
|
|
639
|
+
</Text>
|
|
616
640
|
</Pressable>
|
|
617
641
|
<Pressable
|
|
618
642
|
onPress={close}
|
|
619
643
|
accessibilityRole="button"
|
|
620
644
|
style={({ pressed }) => [styles.footBtn, styles.footMain, { backgroundColor: tint, borderRadius: radius.md, opacity: pressed ? 0.85 : 1 }]}
|
|
621
645
|
>
|
|
622
|
-
<Text style={[styles.footText, styles.footStrong, styles.tabular, ff, { color: readableOn(tint) }]}>
|
|
646
|
+
<Text maxFontSizeMultiplier={FONT_CAP} style={[styles.footText, styles.footStrong, styles.tabular, ff, { color: readableOn(tint) }]}>
|
|
623
647
|
{many.length ? `Done · ${many.length}` : 'Done'}
|
|
624
648
|
</Text>
|
|
625
649
|
</Pressable>
|
|
@@ -691,14 +715,14 @@ export function Select<T extends string | number = string>({
|
|
|
691
715
|
borderRadius: fieldRadius + 4,
|
|
692
716
|
borderColor: alpha(active, 0.2),
|
|
693
717
|
opacity: halo,
|
|
694
|
-
transform: [{ scale: halo.interpolate({ inputRange: [0, 1], outputRange: [0.97, 1] }) }],
|
|
718
|
+
transform: [{ scale: halo.interpolate({ inputRange: [0, 1], outputRange: [reduce ? 1 : 0.97, 1] }) }],
|
|
695
719
|
},
|
|
696
720
|
]}
|
|
697
721
|
/>
|
|
698
722
|
<Animated.View
|
|
699
723
|
style={[
|
|
700
724
|
styles.field,
|
|
701
|
-
{
|
|
725
|
+
{ minHeight: HEIGHT, borderRadius: fieldRadius, backgroundColor: bg },
|
|
702
726
|
userField,
|
|
703
727
|
{ borderColor: hovered && !open && !error ? colors.mutedForeground : borderColor },
|
|
704
728
|
]}
|
|
@@ -707,13 +731,13 @@ export function Select<T extends string | number = string>({
|
|
|
707
731
|
{showChips ? (
|
|
708
732
|
<View style={[styles.chips, { paddingLeft: sz.pad - 6, paddingRight: sz.pad + sz.icon + 8 }]}>
|
|
709
733
|
{chips.map(({ key, item, leaving, appear }) => (
|
|
710
|
-
<AnimatedChip key={key} leaving={leaving} appear={appear} gap={6} shrink={!!item.option} onGone={() => chipGone(key)}>
|
|
734
|
+
<AnimatedChip key={key} leaving={leaving} appear={appear} gap={6} shrink={!!item.option} reduce={reduce} onGone={() => chipGone(key)}>
|
|
711
735
|
{item.option ? (
|
|
712
736
|
<View
|
|
713
737
|
style={[
|
|
714
738
|
styles.chip,
|
|
715
739
|
{
|
|
716
|
-
|
|
740
|
+
minHeight: sz.chip,
|
|
717
741
|
paddingLeft: item.option.icon ? 3 : 10,
|
|
718
742
|
paddingRight: removableChips && !disabled ? 4 : 10,
|
|
719
743
|
backgroundColor: chipBg,
|
|
@@ -726,7 +750,7 @@ export function Select<T extends string | number = string>({
|
|
|
726
750
|
{item.option.icon}
|
|
727
751
|
</View>
|
|
728
752
|
) : null}
|
|
729
|
-
<Text numberOfLines={1} style={[styles.chipText, ff, { color: tint, fontSize: sz.chipFont }]}>
|
|
753
|
+
<Text numberOfLines={1} maxFontSizeMultiplier={FONT_CAP} style={[styles.chipText, ff, { color: tint, fontSize: sz.chipFont }]}>
|
|
730
754
|
{item.option.label}
|
|
731
755
|
</Text>
|
|
732
756
|
{removableChips && !disabled ? (
|
|
@@ -753,12 +777,18 @@ export function Select<T extends string | number = string>({
|
|
|
753
777
|
<View
|
|
754
778
|
accessible
|
|
755
779
|
accessibilityLabel={maxChips > 0 ? `${item.more} more` : `${item.more} selected`}
|
|
756
|
-
style={[styles.chip, styles.more, {
|
|
780
|
+
style={[styles.chip, styles.more, { minHeight: sz.chip, backgroundColor: chipBg, borderRadius: radius.capsule(sz.chip) }]}
|
|
757
781
|
>
|
|
758
|
-
{maxChips > 0 ?
|
|
759
|
-
|
|
782
|
+
{maxChips > 0 ? (
|
|
783
|
+
<Text maxFontSizeMultiplier={FONT_CAP} style={[styles.chipText, styles.tabular, ff, { color: tint, fontSize: sz.chipFont }]}>
|
|
784
|
+
+
|
|
785
|
+
</Text>
|
|
786
|
+
) : null}
|
|
787
|
+
<RollingNumber value={item.more} distance={sz.chipFont} reduce={reduce} style={[styles.chipText, ff, { color: tint, fontSize: sz.chipFont }]} />
|
|
760
788
|
{maxChips > 0 ? null : (
|
|
761
|
-
<Text style={[styles.chipText, ff, { color: tint, fontSize: sz.chipFont, marginLeft: 3 }]}>
|
|
789
|
+
<Text maxFontSizeMultiplier={FONT_CAP} style={[styles.chipText, ff, { color: tint, fontSize: sz.chipFont, marginLeft: 3 }]}>
|
|
790
|
+
selected
|
|
791
|
+
</Text>
|
|
762
792
|
)}
|
|
763
793
|
</View>
|
|
764
794
|
)}
|
|
@@ -768,6 +798,7 @@ export function Select<T extends string | number = string>({
|
|
|
768
798
|
) : (
|
|
769
799
|
<Text
|
|
770
800
|
numberOfLines={1}
|
|
801
|
+
maxFontSizeMultiplier={FONT_CAP}
|
|
771
802
|
style={[
|
|
772
803
|
ff,
|
|
773
804
|
{
|
|
@@ -802,17 +833,21 @@ export function Select<T extends string | number = string>({
|
|
|
802
833
|
]}
|
|
803
834
|
>
|
|
804
835
|
{/* The notch: the label paints the surface over the border, so it matches the rendered width. */}
|
|
805
|
-
<Animated.Text
|
|
836
|
+
<Animated.Text
|
|
837
|
+
numberOfLines={1}
|
|
838
|
+
maxFontSizeMultiplier={FONT_CAP}
|
|
839
|
+
style={[styles.label, ff, { fontSize: sz.font, backgroundColor: bg }, labelStyle, { color: labelColor }]}
|
|
840
|
+
>
|
|
806
841
|
{label}
|
|
807
842
|
</Animated.Text>
|
|
808
843
|
</Animated.View>
|
|
809
844
|
) : null}
|
|
810
|
-
<Chevron open={open} size={sz.icon} right={sz.pad - 4} color={open ? active : (iconColor ?? colors.mutedForeground)} />
|
|
845
|
+
<Chevron open={open} reduce={reduce} size={sz.icon} right={sz.pad - 4} color={open ? active : (iconColor ?? colors.mutedForeground)} />
|
|
811
846
|
</Animated.View>
|
|
812
847
|
</>
|
|
813
848
|
)}
|
|
814
849
|
</Pressable>
|
|
815
|
-
{message ? <FieldMessage text={message} error={!!error} style={messageStyle} /> : null}
|
|
850
|
+
{message ? <FieldMessage text={message} error={!!error} reduce={reduce} style={messageStyle} /> : null}
|
|
816
851
|
|
|
817
852
|
<Modal visible={mounted} transparent animationType="none" statusBarTranslucent onRequestClose={close}>
|
|
818
853
|
<Animated.View style={[StyleSheet.absoluteFill, { backgroundColor: asSheet ? colors.overlay : 'transparent', opacity: show }]}>
|
|
@@ -838,11 +873,15 @@ export function Select<T extends string | number = string>({
|
|
|
838
873
|
? {
|
|
839
874
|
opacity: show,
|
|
840
875
|
transform: [
|
|
841
|
-
{ scale: show.interpolate({ inputRange: [0, 1], outputRange: [0.94, 1] }) },
|
|
842
|
-
{ translateY: Animated.add(show.interpolate({ inputRange: [0, 1], outputRange: [16, 0] }), drag) },
|
|
876
|
+
{ scale: show.interpolate({ inputRange: [0, 1], outputRange: [reduce ? 1 : 0.94, 1] }) },
|
|
877
|
+
{ translateY: Animated.add(show.interpolate({ inputRange: [0, 1], outputRange: [reduce ? 0 : 16, 0] }), drag) },
|
|
843
878
|
],
|
|
844
879
|
}
|
|
845
|
-
: {
|
|
880
|
+
: {
|
|
881
|
+
// Reduce Motion: fade in place instead of sliding up.
|
|
882
|
+
opacity: reduce ? show : 1,
|
|
883
|
+
transform: [{ translateY: Animated.add(show.interpolate({ inputRange: [0, 1], outputRange: [reduce ? 0 : winH * 0.8, 0] }), drag) }],
|
|
884
|
+
},
|
|
846
885
|
]}
|
|
847
886
|
>
|
|
848
887
|
<View {...pan.panHandlers} style={styles.sheetHeader}>
|
|
@@ -851,7 +890,7 @@ export function Select<T extends string | number = string>({
|
|
|
851
890
|
{multiple ? (
|
|
852
891
|
<View style={styles.subtitleRow}>
|
|
853
892
|
{many.length ? (
|
|
854
|
-
<CountBadge count={many.length} tint={tint} />
|
|
893
|
+
<CountBadge count={many.length} tint={tint} reduce={reduce} />
|
|
855
894
|
) : (
|
|
856
895
|
<Text style={[styles.subtitle, ff, { color: colors.mutedForeground }]}>Choose one or more</Text>
|
|
857
896
|
)}
|
|
@@ -880,8 +919,8 @@ export function Select<T extends string | number = string>({
|
|
|
880
919
|
{
|
|
881
920
|
opacity: show,
|
|
882
921
|
transform: [
|
|
883
|
-
{ translateY: show.interpolate({ inputRange: [0, 1], outputRange: [below ? -6 : 6, 0] }) },
|
|
884
|
-
{ scale: show.interpolate({ inputRange: [0, 1], outputRange: [0.97, 1] }) },
|
|
922
|
+
{ translateY: show.interpolate({ inputRange: [0, 1], outputRange: [reduce ? 0 : below ? -6 : 6, 0] }) },
|
|
923
|
+
{ scale: show.interpolate({ inputRange: [0, 1], outputRange: [reduce ? 1 : 0.97, 1] }) },
|
|
885
924
|
],
|
|
886
925
|
},
|
|
887
926
|
below ? { top: anchor.y + anchor.h + GAP } : { bottom: Math.max(EDGE, winH - anchor.y + GAP) },
|
|
@@ -912,6 +951,7 @@ function OptionRow<T extends string | number>({
|
|
|
912
951
|
rowRadius,
|
|
913
952
|
query,
|
|
914
953
|
tint,
|
|
954
|
+
reduce,
|
|
915
955
|
rowStyle,
|
|
916
956
|
textStyle,
|
|
917
957
|
onPress,
|
|
@@ -926,6 +966,7 @@ function OptionRow<T extends string | number>({
|
|
|
926
966
|
rowRadius: number;
|
|
927
967
|
query: string;
|
|
928
968
|
tint: string;
|
|
969
|
+
reduce: boolean;
|
|
929
970
|
rowStyle?: StyleProp<ViewStyle>;
|
|
930
971
|
textStyle?: StyleProp<TextStyle>;
|
|
931
972
|
onPress: () => void;
|
|
@@ -936,8 +977,10 @@ function OptionRow<T extends string | number>({
|
|
|
936
977
|
const ff = font.family ? { fontFamily: font.family } : null;
|
|
937
978
|
const check = useRef(new Animated.Value(selected ? 1 : 0)).current; // native: scale + opacity
|
|
938
979
|
useEffect(() => {
|
|
939
|
-
|
|
940
|
-
|
|
980
|
+
// Reduce Motion: the check only fades (no pop / twist).
|
|
981
|
+
if (reduce) Animated.timing(check, { toValue: selected ? 1 : 0, duration: 150, useNativeDriver: true }).start();
|
|
982
|
+
else Animated.spring(check, { toValue: selected ? 1 : 0, useNativeDriver: true, ...motion.bouncy }).start();
|
|
983
|
+
}, [selected, check, reduce]);
|
|
941
984
|
const on = readableOn(tint);
|
|
942
985
|
const box = dense ? 18 : 22;
|
|
943
986
|
const hoverBg = alpha(colors.foreground, 0.06);
|
|
@@ -966,7 +1009,7 @@ function OptionRow<T extends string | number>({
|
|
|
966
1009
|
>
|
|
967
1010
|
{multiple ? (
|
|
968
1011
|
<View style={[styles.box, { width: box, height: box, borderColor: selected ? tint : colors.input, borderRadius: radius.scale(5) }]}>
|
|
969
|
-
<Animated.View style={[styles.boxFill, { backgroundColor: tint, borderRadius: radius.scale(5), opacity: check, transform: [{ scale: check }] }]}>
|
|
1012
|
+
<Animated.View style={[styles.boxFill, { backgroundColor: tint, borderRadius: radius.scale(5), opacity: check, transform: [{ scale: reduce ? 1 : check }] }]}>
|
|
970
1013
|
<Icon name="check" size={dense ? 12 : 14} color={on} strokeWidth={2.8} />
|
|
971
1014
|
</Animated.View>
|
|
972
1015
|
</View>
|
|
@@ -994,7 +1037,9 @@ function OptionRow<T extends string | number>({
|
|
|
994
1037
|
styles.checkWrap,
|
|
995
1038
|
{
|
|
996
1039
|
opacity: check,
|
|
997
|
-
transform:
|
|
1040
|
+
transform: reduce
|
|
1041
|
+
? []
|
|
1042
|
+
: [{ scale: check }, { rotate: check.interpolate({ inputRange: [0, 1], outputRange: ['-30deg', '0deg'] }) }],
|
|
998
1043
|
},
|
|
999
1044
|
]}
|
|
1000
1045
|
>
|
|
@@ -1055,6 +1100,7 @@ function AnimatedChip({
|
|
|
1055
1100
|
appear,
|
|
1056
1101
|
gap,
|
|
1057
1102
|
shrink,
|
|
1103
|
+
reduce,
|
|
1058
1104
|
onGone,
|
|
1059
1105
|
children,
|
|
1060
1106
|
}: {
|
|
@@ -1063,6 +1109,8 @@ function AnimatedChip({
|
|
|
1063
1109
|
gap: number;
|
|
1064
1110
|
/** Whether the slot may shrink when the row is tight (chips yes, the "+N" counter no). */
|
|
1065
1111
|
shrink: boolean;
|
|
1112
|
+
/** Reduce Motion: fade only — no pop, and neighbours close the gap at once. */
|
|
1113
|
+
reduce: boolean;
|
|
1066
1114
|
onGone: () => void;
|
|
1067
1115
|
children: React.ReactNode;
|
|
1068
1116
|
}) {
|
|
@@ -1076,25 +1124,27 @@ function AnimatedChip({
|
|
|
1076
1124
|
let alive = true;
|
|
1077
1125
|
let anims: Animated.CompositeAnimation[];
|
|
1078
1126
|
if (leaving) {
|
|
1079
|
-
|
|
1127
|
+
anims = [Animated.timing(pop, { toValue: 0, duration: 150, easing: Easing.out(Easing.quad), useNativeDriver: true })];
|
|
1128
|
+
if (!reduce) anims.push(Animated.timing(size, { toValue: 0, duration: 230, easing: Easing.inOut(Easing.cubic), useNativeDriver: false }));
|
|
1129
|
+
let left = anims.length;
|
|
1080
1130
|
const end = ({ finished }: { finished: boolean }) => {
|
|
1081
1131
|
if (finished && alive && --left === 0) gone.current();
|
|
1082
1132
|
};
|
|
1083
|
-
anims = [
|
|
1084
|
-
Animated.timing(pop, { toValue: 0, duration: 150, easing: Easing.out(Easing.quad), useNativeDriver: true }),
|
|
1085
|
-
Animated.timing(size, { toValue: 0, duration: 230, easing: Easing.inOut(Easing.cubic), useNativeDriver: false }),
|
|
1086
|
-
];
|
|
1087
1133
|
anims.forEach((a) => a.start(end));
|
|
1088
1134
|
} else {
|
|
1089
1135
|
size.setValue(1);
|
|
1090
|
-
anims = [
|
|
1136
|
+
anims = [
|
|
1137
|
+
reduce
|
|
1138
|
+
? Animated.timing(pop, { toValue: 1, duration: 150, useNativeDriver: true })
|
|
1139
|
+
: Animated.spring(pop, { toValue: 1, useNativeDriver: true, ...motion.snappy }),
|
|
1140
|
+
];
|
|
1091
1141
|
anims.forEach((a) => a.start());
|
|
1092
1142
|
}
|
|
1093
1143
|
return () => {
|
|
1094
1144
|
alive = false;
|
|
1095
1145
|
anims.forEach((a) => a.stop());
|
|
1096
1146
|
};
|
|
1097
|
-
}, [leaving, pop, size]);
|
|
1147
|
+
}, [leaving, pop, size, reduce]);
|
|
1098
1148
|
|
|
1099
1149
|
const collapsing = leaving && w > 0;
|
|
1100
1150
|
return (
|
|
@@ -1119,7 +1169,7 @@ function AnimatedChip({
|
|
|
1119
1169
|
collapsing && { width: w, flexShrink: 0 },
|
|
1120
1170
|
{
|
|
1121
1171
|
opacity: pop.interpolate({ inputRange: [0, 1], outputRange: [0, 1], extrapolate: 'clamp' }),
|
|
1122
|
-
transform: [{ scale: pop.interpolate({ inputRange: [0, 1], outputRange: [0.85, 1] }) }],
|
|
1172
|
+
transform: [{ scale: pop.interpolate({ inputRange: [0, 1], outputRange: [reduce ? 1 : 0.85, 1] }) }],
|
|
1123
1173
|
},
|
|
1124
1174
|
]}
|
|
1125
1175
|
>
|
|
@@ -1129,24 +1179,26 @@ function AnimatedChip({
|
|
|
1129
1179
|
);
|
|
1130
1180
|
}
|
|
1131
1181
|
|
|
1132
|
-
/** A number that rolls (native translateY + opacity) up when it grows and down when it shrinks. */
|
|
1133
|
-
function RollingNumber({ value, style, distance = 10 }: { value: number; style?: StyleProp<TextStyle>; distance?: number }) {
|
|
1182
|
+
/** A number that rolls (native translateY + opacity) up when it grows and down when it shrinks. Reduce Motion shows the new value at once. */
|
|
1183
|
+
function RollingNumber({ value, style, distance = 10, reduce }: { value: number; style?: StyleProp<TextStyle>; distance?: number; reduce: boolean }) {
|
|
1134
1184
|
const [state, setState] = useState({ cur: value, prev: value, dir: 1 });
|
|
1135
1185
|
if (state.cur !== value) setState({ cur: value, prev: state.cur, dir: value > state.cur ? 1 : -1 });
|
|
1136
1186
|
// A fresh value per change starts at 0 in the same render, so there's no one-frame flash.
|
|
1137
|
-
const roll = useMemo(() => new Animated.Value(state.cur
|
|
1187
|
+
const roll = useMemo(() => new Animated.Value(state.cur !== state.prev && !reduce ? 0 : 1), [state, reduce]);
|
|
1188
|
+
const moving = state.cur !== state.prev && !reduce;
|
|
1138
1189
|
useEffect(() => {
|
|
1190
|
+
if (!moving) return;
|
|
1139
1191
|
const a = Animated.spring(roll, { toValue: 1, useNativeDriver: true, ...motion.snappy });
|
|
1140
1192
|
a.start();
|
|
1141
1193
|
return () => a.stop();
|
|
1142
|
-
}, [roll]);
|
|
1143
|
-
const moving = state.cur !== state.prev;
|
|
1194
|
+
}, [roll, moving]);
|
|
1144
1195
|
return (
|
|
1145
1196
|
<View style={styles.roll}>
|
|
1146
1197
|
{moving ? (
|
|
1147
1198
|
<Animated.Text
|
|
1148
1199
|
accessible={false}
|
|
1149
1200
|
importantForAccessibility="no"
|
|
1201
|
+
maxFontSizeMultiplier={FONT_CAP}
|
|
1150
1202
|
style={[
|
|
1151
1203
|
style,
|
|
1152
1204
|
styles.tabular,
|
|
@@ -1161,6 +1213,7 @@ function RollingNumber({ value, style, distance = 10 }: { value: number; style?:
|
|
|
1161
1213
|
</Animated.Text>
|
|
1162
1214
|
) : null}
|
|
1163
1215
|
<Animated.Text
|
|
1216
|
+
maxFontSizeMultiplier={FONT_CAP}
|
|
1164
1217
|
style={[
|
|
1165
1218
|
style,
|
|
1166
1219
|
styles.tabular,
|
|
@@ -1177,7 +1230,7 @@ function RollingNumber({ value, style, distance = 10 }: { value: number; style?:
|
|
|
1177
1230
|
}
|
|
1178
1231
|
|
|
1179
1232
|
/** "N selected" pill whose number rolls; it pops out when the count drops to 0. */
|
|
1180
|
-
function CountBadge({ count, tint }: { count: number; tint: string }) {
|
|
1233
|
+
function CountBadge({ count, tint, reduce }: { count: number; tint: string; reduce: boolean }) {
|
|
1181
1234
|
const { radius, font } = useKit();
|
|
1182
1235
|
const ff = font.family ? { fontFamily: font.family } : null;
|
|
1183
1236
|
const on = count > 0;
|
|
@@ -1185,10 +1238,12 @@ function CountBadge({ count, tint }: { count: number; tint: string }) {
|
|
|
1185
1238
|
const last = useRef(count);
|
|
1186
1239
|
if (on) last.current = count;
|
|
1187
1240
|
useEffect(() => {
|
|
1188
|
-
const a =
|
|
1241
|
+
const a = reduce
|
|
1242
|
+
? Animated.timing(vis, { toValue: on ? 1 : 0, duration: 150, useNativeDriver: true })
|
|
1243
|
+
: Animated.spring(vis, { toValue: on ? 1 : 0, useNativeDriver: true, ...motion.snappy });
|
|
1189
1244
|
a.start();
|
|
1190
1245
|
return () => a.stop();
|
|
1191
|
-
}, [on, vis]);
|
|
1246
|
+
}, [on, vis, reduce]);
|
|
1192
1247
|
const text = [styles.badgeText, ff, { color: tint }];
|
|
1193
1248
|
return (
|
|
1194
1249
|
<Animated.View
|
|
@@ -1201,12 +1256,14 @@ function CountBadge({ count, tint }: { count: number; tint: string }) {
|
|
|
1201
1256
|
backgroundColor: alpha(tint, 0.12),
|
|
1202
1257
|
borderRadius: radius.capsule(24),
|
|
1203
1258
|
opacity: vis.interpolate({ inputRange: [0, 1], outputRange: [0, 1], extrapolate: 'clamp' }),
|
|
1204
|
-
transform: [{ scale: vis.interpolate({ inputRange: [0, 1], outputRange: [0.85, 1] }) }],
|
|
1259
|
+
transform: [{ scale: vis.interpolate({ inputRange: [0, 1], outputRange: [reduce ? 1 : 0.85, 1] }) }],
|
|
1205
1260
|
},
|
|
1206
1261
|
]}
|
|
1207
1262
|
>
|
|
1208
|
-
<RollingNumber value={last.current} distance={9} style={text} />
|
|
1209
|
-
<Text style={[text, styles.badgeSuffix]}>
|
|
1263
|
+
<RollingNumber value={last.current} distance={9} reduce={reduce} style={text} />
|
|
1264
|
+
<Text maxFontSizeMultiplier={FONT_CAP} style={[text, styles.badgeSuffix]}>
|
|
1265
|
+
selected
|
|
1266
|
+
</Text>
|
|
1210
1267
|
</Animated.View>
|
|
1211
1268
|
);
|
|
1212
1269
|
}
|
|
@@ -1231,20 +1288,21 @@ function Highlight({ text, query, tint, style }: { text: string; query: string;
|
|
|
1231
1288
|
}
|
|
1232
1289
|
|
|
1233
1290
|
/** Helper / error line that fades and slides in; errors get an icon and are announced. */
|
|
1234
|
-
function FieldMessage({ text, error, style }: { text: string; error: boolean; style?: StyleProp<TextStyle> }) {
|
|
1291
|
+
function FieldMessage({ text, error, reduce, style }: { text: string; error: boolean; reduce: boolean; style?: StyleProp<TextStyle> }) {
|
|
1235
1292
|
const { colors, font } = useKit();
|
|
1236
1293
|
const enter = useRef(new Animated.Value(0)).current; // native
|
|
1237
1294
|
useEffect(() => {
|
|
1238
1295
|
enter.setValue(0);
|
|
1239
|
-
Animated.
|
|
1240
|
-
|
|
1296
|
+
if (reduce) Animated.timing(enter, { toValue: 1, duration: 150, useNativeDriver: true }).start();
|
|
1297
|
+
else Animated.spring(enter, { toValue: 1, useNativeDriver: true, ...motion.snappy }).start();
|
|
1298
|
+
}, [text, error, enter, reduce]);
|
|
1241
1299
|
const color = error ? colors.danger : colors.mutedForeground;
|
|
1242
1300
|
return (
|
|
1243
1301
|
<Animated.View
|
|
1244
1302
|
accessibilityLiveRegion="polite"
|
|
1245
1303
|
style={[
|
|
1246
1304
|
styles.message,
|
|
1247
|
-
{ opacity: enter, transform: [{ translateY: enter.interpolate({ inputRange: [0, 1], outputRange: [4, 0] }) }] },
|
|
1305
|
+
{ opacity: enter, transform: [{ translateY: enter.interpolate({ inputRange: [0, 1], outputRange: [reduce ? 0 : 4, 0] }) }] },
|
|
1248
1306
|
]}
|
|
1249
1307
|
>
|
|
1250
1308
|
{error ? <Icon name="alert" size={14} color={color} /> : null}
|
|
@@ -1253,11 +1311,12 @@ function FieldMessage({ text, error, style }: { text: string; error: boolean; st
|
|
|
1253
1311
|
);
|
|
1254
1312
|
}
|
|
1255
1313
|
|
|
1256
|
-
function Chevron({ open, color, size, right }: { open: boolean; color: string; size: number; right: number }) {
|
|
1314
|
+
function Chevron({ open, reduce, color, size, right }: { open: boolean; reduce: boolean; color: string; size: number; right: number }) {
|
|
1257
1315
|
const turn = useRef(new Animated.Value(open ? 1 : 0)).current;
|
|
1258
1316
|
useEffect(() => {
|
|
1259
|
-
|
|
1260
|
-
|
|
1317
|
+
if (reduce) turn.setValue(open ? 1 : 0);
|
|
1318
|
+
else Animated.spring(turn, { toValue: open ? 1 : 0, useNativeDriver: true, ...motion.snappy }).start();
|
|
1319
|
+
}, [open, turn, reduce]);
|
|
1261
1320
|
return (
|
|
1262
1321
|
<Animated.View
|
|
1263
1322
|
pointerEvents="none"
|
|
@@ -1294,6 +1353,22 @@ function Icon({ name, size, color, strokeWidth = 1.8 }: { name: 'chevron' | 'che
|
|
|
1294
1353
|
);
|
|
1295
1354
|
}
|
|
1296
1355
|
|
|
1356
|
+
function useReduceMotion() {
|
|
1357
|
+
const [reduce, setReduce] = useState(false);
|
|
1358
|
+
useEffect(() => {
|
|
1359
|
+
let alive = true;
|
|
1360
|
+
AccessibilityInfo.isReduceMotionEnabled()
|
|
1361
|
+
.then((r) => alive && setReduce(r))
|
|
1362
|
+
.catch(() => {});
|
|
1363
|
+
const sub = AccessibilityInfo.addEventListener('reduceMotionChanged', setReduce);
|
|
1364
|
+
return () => {
|
|
1365
|
+
alive = false;
|
|
1366
|
+
sub.remove();
|
|
1367
|
+
};
|
|
1368
|
+
}, []);
|
|
1369
|
+
return reduce;
|
|
1370
|
+
}
|
|
1371
|
+
|
|
1297
1372
|
const styles = StyleSheet.create({
|
|
1298
1373
|
field: { borderWidth: 1.5, justifyContent: 'center' },
|
|
1299
1374
|
halo: { position: 'absolute', top: -5, left: -5, right: -5, bottom: -5, borderWidth: 3.5 },
|
|
@@ -1308,7 +1383,7 @@ const styles = StyleSheet.create({
|
|
|
1308
1383
|
tabular: { fontVariant: ['tabular-nums'], fontWeight: '400' },
|
|
1309
1384
|
roll: { overflow: 'hidden' },
|
|
1310
1385
|
rollPrev: { position: 'absolute', left: 0, top: 0 },
|
|
1311
|
-
badge: { flexDirection: 'row', alignItems: 'center',
|
|
1386
|
+
badge: { flexDirection: 'row', alignItems: 'center', minHeight: 24, paddingHorizontal: 9 },
|
|
1312
1387
|
badgeText: { fontSize: 12.5, fontWeight: '400' },
|
|
1313
1388
|
badgeSuffix: { marginLeft: 3 },
|
|
1314
1389
|
chipX: { alignItems: 'center', justifyContent: 'center' },
|
|
@@ -1332,11 +1407,11 @@ const styles = StyleSheet.create({
|
|
|
1332
1407
|
sheetHeader: { alignItems: 'center', paddingTop: 10, paddingBottom: 12 },
|
|
1333
1408
|
grabber: { width: 40, height: 5, borderRadius: 3, marginBottom: 12 },
|
|
1334
1409
|
title: { fontSize: 17, fontWeight: '400', letterSpacing: -0.2, marginTop: 4 },
|
|
1335
|
-
subtitleRow: {
|
|
1410
|
+
subtitleRow: { minHeight: 26, marginTop: 4, alignItems: 'center', justifyContent: 'center' },
|
|
1336
1411
|
subtitle: { fontSize: 13 },
|
|
1337
|
-
search: {
|
|
1412
|
+
search: { minHeight: 44, marginBottom: 8, marginHorizontal: 4, flexDirection: 'row', alignItems: 'center', paddingLeft: 12, paddingRight: 10, gap: 8 },
|
|
1338
1413
|
searchDense: {
|
|
1339
|
-
|
|
1414
|
+
minHeight: SEARCH_H,
|
|
1340
1415
|
flexDirection: 'row',
|
|
1341
1416
|
alignItems: 'center',
|
|
1342
1417
|
paddingLeft: 13,
|
|
@@ -1344,7 +1419,7 @@ const styles = StyleSheet.create({
|
|
|
1344
1419
|
gap: 8,
|
|
1345
1420
|
borderBottomWidth: StyleSheet.hairlineWidth,
|
|
1346
1421
|
},
|
|
1347
|
-
searchInput: { flex: 1,
|
|
1422
|
+
searchInput: { flex: 1, minHeight: 44, paddingVertical: 0 },
|
|
1348
1423
|
clear: { width: 20, height: 20, borderRadius: 10, alignItems: 'center', justifyContent: 'center' },
|
|
1349
1424
|
scroll: { flexGrow: 0, flexShrink: 1 },
|
|
1350
1425
|
listContent: { paddingBottom: 6 },
|
|
@@ -1359,20 +1434,20 @@ const styles = StyleSheet.create({
|
|
|
1359
1434
|
emptyTitle: { fontWeight: '400', textAlign: 'center' },
|
|
1360
1435
|
emptyText: { fontSize: 13, marginTop: 3, textAlign: 'center' },
|
|
1361
1436
|
footer: { flexDirection: 'row', gap: 8, paddingTop: 10, paddingHorizontal: 4, borderTopWidth: StyleSheet.hairlineWidth },
|
|
1362
|
-
footBtn: {
|
|
1437
|
+
footBtn: { minHeight: 44, paddingHorizontal: 18, alignItems: 'center', justifyContent: 'center' },
|
|
1363
1438
|
footMain: { flex: 1 },
|
|
1364
1439
|
footText: { fontSize: 15, fontWeight: '400' },
|
|
1365
1440
|
footStrong: { fontWeight: '400' },
|
|
1366
1441
|
footDense: {
|
|
1367
|
-
|
|
1442
|
+
minHeight: FOOT_H,
|
|
1368
1443
|
flexDirection: 'row',
|
|
1369
1444
|
alignItems: 'center',
|
|
1370
1445
|
paddingHorizontal: 8,
|
|
1371
1446
|
gap: 8,
|
|
1372
1447
|
borderTopWidth: StyleSheet.hairlineWidth,
|
|
1373
1448
|
},
|
|
1374
|
-
footGhost: {
|
|
1375
|
-
footSolid: {
|
|
1449
|
+
footGhost: { minHeight: 32, paddingHorizontal: 12, alignItems: 'center', justifyContent: 'center' },
|
|
1450
|
+
footSolid: { minHeight: 32, paddingHorizontal: 16, alignItems: 'center', justifyContent: 'center' },
|
|
1376
1451
|
footMid: { flex: 1, alignItems: 'center', justifyContent: 'center' },
|
|
1377
1452
|
footDenseText: { fontSize: 14, fontWeight: '400' },
|
|
1378
1453
|
dropdown: {
|