@retray-dev/ui-kit 7.0.1 → 9.1.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/COMPONENTS.md +567 -14
- package/EXAMPLES.md +21 -14
- package/README.md +14 -8
- package/dist/Accordion.js +57 -5
- package/dist/Accordion.mjs +4 -3
- package/dist/AlertBanner.js +4 -1
- package/dist/AlertBanner.mjs +3 -2
- package/dist/AppHeader.d.mts +40 -0
- package/dist/AppHeader.d.ts +40 -0
- package/dist/AppHeader.js +515 -0
- package/dist/AppHeader.mjs +10 -0
- package/dist/Avatar.js +39 -29
- package/dist/Avatar.mjs +2 -1
- package/dist/Badge.js +11 -1
- package/dist/Badge.mjs +2 -1
- package/dist/Button.d.mts +8 -3
- package/dist/Button.d.ts +8 -3
- package/dist/Button.js +126 -108
- package/dist/Button.mjs +6 -5
- package/dist/ButtonGroup.mjs +1 -0
- package/dist/Card.js +90 -70
- package/dist/Card.mjs +5 -4
- package/dist/CategoryStrip.js +79 -22
- package/dist/CategoryStrip.mjs +6 -6
- package/dist/Checkbox.js +118 -86
- package/dist/Checkbox.mjs +5 -5
- package/dist/Chip.js +113 -80
- package/dist/Chip.mjs +5 -5
- package/dist/ConfirmDialog.js +140 -110
- package/dist/ConfirmDialog.mjs +7 -6
- package/dist/CurrencyDisplay.mjs +1 -0
- package/dist/CurrencyInput.d.mts +1 -1
- package/dist/CurrencyInput.d.ts +1 -1
- package/dist/CurrencyInput.js +9 -5
- package/dist/CurrencyInput.mjs +5 -4
- package/dist/DetailRow.mjs +1 -0
- package/dist/EmptyState.js +131 -111
- package/dist/EmptyState.mjs +7 -6
- package/dist/ErrorBoundary.d.mts +42 -0
- package/dist/ErrorBoundary.d.ts +42 -0
- package/dist/ErrorBoundary.js +351 -0
- package/dist/ErrorBoundary.mjs +7 -0
- package/dist/Form.mjs +1 -0
- package/dist/HolographicCard.d.mts +55 -0
- package/dist/HolographicCard.d.ts +55 -0
- package/dist/HolographicCard.js +316 -0
- package/dist/HolographicCard.mjs +191 -0
- package/dist/IconButton.d.mts +8 -3
- package/dist/IconButton.d.ts +8 -3
- package/dist/IconButton.js +115 -98
- package/dist/IconButton.mjs +5 -4
- package/dist/ImageViewer.d.mts +23 -0
- package/dist/ImageViewer.d.ts +23 -0
- package/dist/ImageViewer.js +582 -0
- package/dist/ImageViewer.mjs +8 -0
- package/dist/Input.mjs +4 -3
- package/dist/LabelValue.mjs +1 -0
- package/dist/ListGroup.mjs +1 -0
- package/dist/ListItem.js +131 -117
- package/dist/ListItem.mjs +6 -5
- package/dist/MediaCard.js +54 -6
- package/dist/MediaCard.mjs +6 -5
- package/dist/MenuGroup.mjs +1 -0
- package/dist/MenuItem.js +91 -79
- package/dist/MenuItem.mjs +6 -5
- package/dist/MonthPicker.d.mts +10 -2
- package/dist/MonthPicker.d.ts +10 -2
- package/dist/MonthPicker.js +80 -17
- package/dist/MonthPicker.mjs +3 -2
- package/dist/PagerDots.d.mts +35 -0
- package/dist/PagerDots.d.ts +35 -0
- package/dist/PagerDots.js +392 -0
- package/dist/PagerDots.mjs +7 -0
- package/dist/Pressable.d.mts +5 -5
- package/dist/Pressable.d.ts +5 -5
- package/dist/Pressable.js +97 -86
- package/dist/Pressable.mjs +5 -4
- package/dist/PricingCard.d.mts +50 -0
- package/dist/PricingCard.d.ts +50 -0
- package/dist/PricingCard.js +636 -0
- package/dist/PricingCard.mjs +11 -0
- package/dist/Progress.mjs +3 -2
- package/dist/RadioGroup.js +81 -30
- package/dist/RadioGroup.mjs +5 -5
- package/dist/RetrayProvider.d.mts +2 -0
- package/dist/RetrayProvider.d.ts +2 -0
- package/dist/RetrayProvider.js +214 -0
- package/dist/RetrayProvider.mjs +5 -0
- package/dist/Select.js +51 -4
- package/dist/Select.mjs +5 -4
- package/dist/SelectableGrid.d.mts +44 -0
- package/dist/SelectableGrid.d.ts +44 -0
- package/dist/SelectableGrid.js +448 -0
- package/dist/SelectableGrid.mjs +9 -0
- package/dist/Separator.mjs +1 -0
- package/dist/Sheet.d.mts +13 -1
- package/dist/Sheet.d.ts +13 -1
- package/dist/Sheet.js +115 -5
- package/dist/Sheet.mjs +4 -2
- package/dist/Skeleton.d.mts +50 -0
- package/dist/Skeleton.d.ts +50 -0
- package/dist/Skeleton.js +61 -0
- package/dist/Skeleton.mjs +4 -2
- package/dist/Slider.js +51 -4
- package/dist/Slider.mjs +3 -2
- package/dist/Spinner.js +28 -7
- package/dist/Spinner.mjs +2 -1
- package/dist/Switch.js +98 -48
- package/dist/Switch.mjs +4 -3
- package/dist/TabBar.d.mts +42 -0
- package/dist/TabBar.d.ts +42 -0
- package/dist/TabBar.js +361 -0
- package/dist/TabBar.mjs +6 -0
- package/dist/Tabs.js +92 -62
- package/dist/Tabs.mjs +5 -4
- package/dist/Text.js +16 -0
- package/dist/Text.mjs +2 -1
- package/dist/Textarea.mjs +4 -3
- package/dist/Toast.d.mts +7 -7
- package/dist/Toast.d.ts +7 -7
- package/dist/Toast.mjs +1 -0
- package/dist/Toggle.d.mts +6 -3
- package/dist/Toggle.d.ts +6 -3
- package/dist/Toggle.js +135 -120
- package/dist/Toggle.mjs +5 -5
- package/dist/VirtualList.mjs +1 -0
- package/dist/{chunk-7H2OR44A.mjs → chunk-26BCI223.mjs} +1 -1
- package/dist/{chunk-CRYBX2CM.mjs → chunk-2TFTAWVJ.mjs} +44 -59
- package/dist/chunk-3DKJ2GIC.mjs +30 -0
- package/dist/{chunk-KWCPOM6W.mjs → chunk-3U4SSNWP.mjs} +32 -48
- package/dist/chunk-4I7D47FH.mjs +139 -0
- package/dist/chunk-4K625MVM.mjs +142 -0
- package/dist/{chunk-MN7OG7IY.mjs → chunk-6OAZJ577.mjs} +6 -4
- package/dist/{chunk-L7E7TVEZ.mjs → chunk-756RAKE4.mjs} +2 -2
- package/dist/{chunk-HSPSMN6U.mjs → chunk-7QHVVCB3.mjs} +2 -2
- package/dist/{chunk-URLL5JBR.mjs → chunk-A3A6KNQN.mjs} +3 -3
- package/dist/chunk-AJ7ZDNBT.mjs +120 -0
- package/dist/{chunk-FTLJOUOQ.mjs → chunk-AV4EMIRH.mjs} +25 -28
- package/dist/chunk-AZJF2BLK.mjs +115 -0
- package/dist/chunk-BNP626TY.mjs +159 -0
- package/dist/{chunk-5IKW3VNC.mjs → chunk-DVK4G2GT.mjs} +17 -1
- package/dist/{chunk-6LQYY7HC.mjs → chunk-EH745HE5.mjs} +2 -2
- package/dist/chunk-EJ7ZPXOH.mjs +163 -0
- package/dist/{chunk-RKLHUDZS.mjs → chunk-GD6KXMG5.mjs} +29 -15
- package/dist/{chunk-RR2VQLKE.mjs → chunk-GQYFLP3D.mjs} +14 -17
- package/dist/{chunk-Y6MXOREN.mjs → chunk-ID72TK46.mjs} +8 -17
- package/dist/{chunk-NQGVLMWG.mjs → chunk-JMOZEC77.mjs} +1 -1
- package/dist/{chunk-GCWOGZYL.mjs → chunk-JT7HKXRB.mjs} +39 -29
- package/dist/{chunk-LWG526VX.mjs → chunk-KIHCWCWL.mjs} +47 -62
- package/dist/chunk-LXJIIOYQ.mjs +104 -0
- package/dist/{chunk-SBZYEV4S.mjs → chunk-M6ZXVBTK.mjs} +5 -2
- package/dist/{chunk-XDMN67KV.mjs → chunk-MAC465BB.mjs} +10 -8
- package/dist/chunk-MBMXYJJV.mjs +36 -0
- package/dist/chunk-MLF3EZFW.mjs +119 -0
- package/dist/chunk-NA7PARID.mjs +147 -0
- package/dist/{chunk-QXGYKWI7.mjs → chunk-O3HA6TYM.mjs} +9 -4
- package/dist/{chunk-63357L2X.mjs → chunk-OB4JUQ3O.mjs} +1 -1
- package/dist/{chunk-AU2VDY4P.mjs → chunk-PFZTM6D5.mjs} +52 -4
- package/dist/chunk-QKH5ZOD5.mjs +97 -0
- package/dist/{chunk-KZJRQOIU.mjs → chunk-TERDKCLE.mjs} +11 -1
- package/dist/{chunk-U4N7WF4Z.mjs → chunk-UREA2GYY.mjs} +28 -23
- package/dist/{chunk-TAJ2PQ2O.mjs → chunk-VGTDN7SW.mjs} +7 -6
- package/dist/{chunk-URDE3EUU.mjs → chunk-VQ57HWPL.mjs} +27 -15
- package/dist/chunk-WBOOUHSS.mjs +62 -0
- package/dist/{chunk-GNGLDL6Z.mjs → chunk-WJLKJMKR.mjs} +18 -0
- package/dist/{chunk-YZJAFS4P.mjs → chunk-X4G6APW6.mjs} +22 -19
- package/dist/chunk-Y6FXYEAI.mjs +8 -0
- package/dist/chunk-YFZ3ELX5.mjs +16 -0
- package/dist/{chunk-QCNARS3X.mjs → chunk-YNROWHQJ.mjs} +1 -1
- package/dist/chunk-Z4BVUWW6.mjs +196 -0
- package/dist/{chunk-GPOUINK5.mjs → chunk-ZJKGQMYH.mjs} +10 -27
- package/dist/index-wt-orHUi.d.mts +85 -0
- package/dist/index-wt-orHUi.d.ts +85 -0
- package/dist/index.d.mts +59 -51
- package/dist/index.d.ts +59 -51
- package/dist/index.js +1940 -744
- package/dist/index.mjs +49 -39
- package/package.json +35 -5
- package/src/components/Accordion/Accordion.tsx +12 -1
- package/src/components/AlertBanner/AlertBanner.tsx +5 -0
- package/src/components/AppHeader/AppHeader.tsx +172 -0
- package/src/components/AppHeader/index.ts +1 -0
- package/src/components/Avatar/Avatar.tsx +10 -2
- package/src/components/Badge/Badge.tsx +8 -1
- package/src/components/Button/Button.tsx +20 -27
- package/src/components/Card/Card.tsx +12 -23
- package/src/components/CategoryStrip/CategoryStrip.tsx +17 -21
- package/src/components/Checkbox/Checkbox.tsx +26 -40
- package/src/components/Chip/Chip.tsx +24 -33
- package/src/components/CurrencyInput/CurrencyInput.tsx +10 -8
- package/src/components/EmptyState/EmptyState.tsx +2 -1
- package/src/components/ErrorBoundary/ErrorBoundary.tsx +153 -0
- package/src/components/ErrorBoundary/index.ts +1 -0
- package/src/components/HolographicCard/HolographicCard.tsx +315 -0
- package/src/components/HolographicCard/index.ts +1 -0
- package/src/components/IconButton/IconButton.tsx +19 -27
- package/src/components/ImageViewer/ImageViewer.tsx +290 -0
- package/src/components/ImageViewer/index.ts +1 -0
- package/src/components/ListItem/ListItem.tsx +70 -67
- package/src/components/MediaCard/MediaCard.tsx +8 -2
- package/src/components/MenuItem/MenuItem.tsx +10 -25
- package/src/components/MonthPicker/MonthPicker.tsx +39 -13
- package/src/components/MonthPicker/index.ts +1 -1
- package/src/components/PagerDots/PagerDots.tsx +200 -0
- package/src/components/PagerDots/index.ts +1 -0
- package/src/components/Pressable/Pressable.tsx +19 -35
- package/src/components/PricingCard/PricingCard.tsx +220 -0
- package/src/components/PricingCard/index.ts +1 -0
- package/src/components/RadioGroup/RadioGroup.tsx +14 -27
- package/src/components/RetrayProvider/RetrayProvider.tsx +59 -0
- package/src/components/RetrayProvider/index.ts +1 -0
- package/src/components/SelectableGrid/SelectableGrid.tsx +205 -0
- package/src/components/SelectableGrid/index.ts +1 -0
- package/src/components/Sheet/Sheet.tsx +65 -1
- package/src/components/Skeleton/Skeleton.tsx +142 -1
- package/src/components/Spinner/Spinner.tsx +17 -2
- package/src/components/Switch/Switch.tsx +30 -58
- package/src/components/TabBar/TabBar.tsx +169 -0
- package/src/components/TabBar/index.ts +1 -0
- package/src/components/Tabs/Tabs.tsx +23 -26
- package/src/components/Text/Text.tsx +2 -0
- package/src/components/Toggle/Toggle.tsx +35 -51
- package/src/fonts.ts +4 -1
- package/src/index.ts +23 -2
- package/src/utils/animations.ts +29 -1
- package/src/utils/fontGuard.ts +34 -0
- package/src/utils/haptics.ts +211 -9
- package/src/utils/pressable.ts +66 -0
- package/dist/chunk-76PFOSM2.mjs +0 -41
- package/dist/chunk-DITNP6PL.mjs +0 -106
- package/dist/chunk-JBLL7U3U.mjs +0 -64
- package/dist/chunk-LG4DO3DK.mjs +0 -174
- package/dist/chunk-RMMK64W5.mjs +0 -54
- package/dist/chunk-RTC3CFXF.mjs +0 -29
|
@@ -7,17 +7,14 @@ import {
|
|
|
7
7
|
StyleSheet,
|
|
8
8
|
ViewStyle,
|
|
9
9
|
} from 'react-native'
|
|
10
|
-
import Animated
|
|
11
|
-
|
|
12
|
-
interpolateColor,
|
|
13
|
-
} from 'react-native-reanimated'
|
|
10
|
+
import Animated from 'react-native-reanimated'
|
|
11
|
+
import { EaseView } from 'react-native-ease'
|
|
14
12
|
import { selectionAsync as hapticSelection } from '../../utils/haptics'
|
|
15
13
|
import { useTheme } from '../../theme'
|
|
16
14
|
import { s, vs, ms } from '../../utils/scaling'
|
|
17
15
|
import { renderIcon } from '../../utils/icons'
|
|
18
16
|
import { usePressScale } from '../../utils/usePressScale'
|
|
19
|
-
import {
|
|
20
|
-
import { PRESS_SCALE } from '../../utils/animations'
|
|
17
|
+
import { COLOR_TRANSITION, PRESS_SCALE } from '../../utils/animations'
|
|
21
18
|
import { RADIUS } from '../../tokens'
|
|
22
19
|
|
|
23
20
|
export interface CategoryItem {
|
|
@@ -55,17 +52,6 @@ const CategoryChip = React.memo(function CategoryChip({
|
|
|
55
52
|
const { animatedStyle: scaleStyle, onPressIn, onPressOut, hoverHandlers } = usePressScale({
|
|
56
53
|
pressScale: PRESS_SCALE.chip,
|
|
57
54
|
})
|
|
58
|
-
const progress = useColorTransition(selected)
|
|
59
|
-
|
|
60
|
-
const surfaceStyle = useAnimatedStyle(() => ({
|
|
61
|
-
backgroundColor: interpolateColor(progress.value, [0, 1], [colors.surface, colors.primary]),
|
|
62
|
-
borderColor: interpolateColor(progress.value, [0, 1], [colors.border, colors.primary]),
|
|
63
|
-
}))
|
|
64
|
-
|
|
65
|
-
const textColorStyle = useAnimatedStyle(() => ({
|
|
66
|
-
color: interpolateColor(progress.value, [0, 1], [colors.foregroundSubtle, colors.primaryForeground]),
|
|
67
|
-
}))
|
|
68
|
-
|
|
69
55
|
// Static color for icon — icon families take a static color prop, not animated.
|
|
70
56
|
const iconColor = selected ? colors.primaryForeground : colors.foregroundSubtle
|
|
71
57
|
const resolvedIcon =
|
|
@@ -85,11 +71,21 @@ const CategoryChip = React.memo(function CategoryChip({
|
|
|
85
71
|
accessibilityLabel={item.label}
|
|
86
72
|
accessibilityState={{ selected }}
|
|
87
73
|
>
|
|
88
|
-
<
|
|
74
|
+
<EaseView
|
|
75
|
+
style={styles.chip}
|
|
76
|
+
animate={{
|
|
77
|
+
backgroundColor: selected ? colors.primary : colors.surface,
|
|
78
|
+
borderColor: selected ? colors.primary : colors.border,
|
|
79
|
+
}}
|
|
80
|
+
transition={COLOR_TRANSITION}
|
|
81
|
+
>
|
|
89
82
|
{resolvedIcon && <View style={styles.chipIcon}>{resolvedIcon}</View>}
|
|
90
|
-
<
|
|
83
|
+
<Text
|
|
84
|
+
style={[styles.chipLabel, { color: selected ? colors.primaryForeground : colors.foregroundSubtle }]}
|
|
85
|
+
allowFontScaling={true}
|
|
86
|
+
>
|
|
91
87
|
{item.label}
|
|
92
|
-
</
|
|
88
|
+
</Text>
|
|
93
89
|
{item.badge !== undefined && item.badge > 0 && (
|
|
94
90
|
<View style={[styles.chipBadge, { backgroundColor: colors.primary }]}>
|
|
95
91
|
<Text style={[styles.chipBadgeText, { color: colors.primaryForeground }]}>
|
|
@@ -97,7 +93,7 @@ const CategoryChip = React.memo(function CategoryChip({
|
|
|
97
93
|
</Text>
|
|
98
94
|
</View>
|
|
99
95
|
)}
|
|
100
|
-
</
|
|
96
|
+
</EaseView>
|
|
101
97
|
</TouchableOpacity>
|
|
102
98
|
</Animated.View>
|
|
103
99
|
)
|
|
@@ -1,16 +1,11 @@
|
|
|
1
1
|
import React from 'react'
|
|
2
|
-
import {
|
|
3
|
-
import
|
|
4
|
-
useAnimatedStyle,
|
|
5
|
-
interpolateColor,
|
|
6
|
-
withTiming,
|
|
7
|
-
} from 'react-native-reanimated'
|
|
2
|
+
import { View, Text, StyleSheet, ViewStyle } from 'react-native'
|
|
3
|
+
import { EaseView } from 'react-native-ease'
|
|
8
4
|
import { selectionAsync as hapticSelection } from '../../utils/haptics'
|
|
9
5
|
import { useTheme } from '../../theme'
|
|
10
6
|
import { s, vs, ms, mvs } from '../../utils/scaling'
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
import { PRESS_SCALE, TIMINGS, EASINGS } from '../../utils/animations'
|
|
7
|
+
import { COLOR_TRANSITION, OPACITY_TRANSITION } from '../../utils/animations'
|
|
8
|
+
import { PressableButton } from '../../utils/pressable'
|
|
14
9
|
|
|
15
10
|
export interface CheckboxProps {
|
|
16
11
|
checked?: boolean
|
|
@@ -30,47 +25,38 @@ export function Checkbox({
|
|
|
30
25
|
accessibilityLabel,
|
|
31
26
|
}: CheckboxProps) {
|
|
32
27
|
const { colors } = useTheme()
|
|
33
|
-
const { animatedStyle: scaleStyle, onPressIn, onPressOut } = usePressScale({
|
|
34
|
-
pressScale: PRESS_SCALE.button,
|
|
35
|
-
disabled,
|
|
36
|
-
})
|
|
37
|
-
const progress = useColorTransition(checked)
|
|
38
28
|
|
|
39
|
-
const
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
const checkStyle = useAnimatedStyle(() => ({
|
|
45
|
-
opacity: withTiming(checked ? 1 : 0, { duration: TIMINGS.state.duration, easing: EASINGS.standard }),
|
|
46
|
-
}))
|
|
29
|
+
const handlePress = () => {
|
|
30
|
+
hapticSelection()
|
|
31
|
+
onCheckedChange?.(!checked)
|
|
32
|
+
}
|
|
47
33
|
|
|
48
34
|
return (
|
|
49
35
|
// AUDIT FIX: opacity was applied only to the box, leaving the label at full
|
|
50
36
|
// opacity when disabled — a contradictory visual signal. Now the entire row
|
|
51
37
|
// dims uniformly so label and control communicate the same disabled state.
|
|
52
|
-
<
|
|
38
|
+
<PressableButton
|
|
53
39
|
style={[styles.row, disabled && styles.rowDisabled, style]}
|
|
54
|
-
onPress={
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
onPressIn={onPressIn}
|
|
59
|
-
onPressOut={onPressOut}
|
|
60
|
-
disabled={disabled}
|
|
61
|
-
activeOpacity={1}
|
|
62
|
-
touchSoundDisabled={true}
|
|
40
|
+
onPress={handlePress}
|
|
41
|
+
enabled={!disabled}
|
|
42
|
+
rippleColor="transparent"
|
|
43
|
+
touchSoundDisabled
|
|
63
44
|
accessibilityRole="checkbox"
|
|
64
45
|
accessibilityLabel={accessibilityLabel ?? label}
|
|
65
46
|
accessibilityState={{ checked, disabled: !!disabled }}
|
|
66
47
|
>
|
|
67
|
-
<
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
48
|
+
<EaseView
|
|
49
|
+
style={styles.box}
|
|
50
|
+
animate={{
|
|
51
|
+
borderColor: checked ? colors.primary : colors.border,
|
|
52
|
+
backgroundColor: checked ? colors.primary : 'transparent',
|
|
53
|
+
}}
|
|
54
|
+
transition={COLOR_TRANSITION}
|
|
55
|
+
>
|
|
56
|
+
<EaseView animate={{ opacity: checked ? 1 : 0 }} transition={OPACITY_TRANSITION}>
|
|
57
|
+
<View style={[styles.checkmark, { borderColor: colors.primaryForeground }]} />
|
|
58
|
+
</EaseView>
|
|
59
|
+
</EaseView>
|
|
74
60
|
{label ? (
|
|
75
61
|
<Text
|
|
76
62
|
style={[styles.label, { color: colors.foreground }]}
|
|
@@ -79,7 +65,7 @@ export function Checkbox({
|
|
|
79
65
|
{label}
|
|
80
66
|
</Text>
|
|
81
67
|
) : null}
|
|
82
|
-
</
|
|
68
|
+
</PressableButton>
|
|
83
69
|
)
|
|
84
70
|
}
|
|
85
71
|
|
|
@@ -1,16 +1,12 @@
|
|
|
1
1
|
import React from 'react'
|
|
2
|
-
import {
|
|
3
|
-
import
|
|
4
|
-
useAnimatedStyle,
|
|
5
|
-
interpolateColor,
|
|
6
|
-
} from 'react-native-reanimated'
|
|
2
|
+
import { View, Text, StyleSheet, ViewStyle } from 'react-native'
|
|
3
|
+
import { EaseView } from 'react-native-ease'
|
|
7
4
|
import { selectionAsync as hapticSelection } from '../../utils/haptics'
|
|
8
5
|
import { useTheme } from '../../theme'
|
|
9
6
|
import { s, vs, ms, mvs } from '../../utils/scaling'
|
|
10
7
|
import { renderIcon } from '../../utils/icons'
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
import { PRESS_SCALE } from '../../utils/animations'
|
|
8
|
+
import { COLOR_TRANSITION } from '../../utils/animations'
|
|
9
|
+
import { PressableChip as PressableChipComponent } from '../../utils/pressable'
|
|
14
10
|
|
|
15
11
|
export interface ChipProps {
|
|
16
12
|
label: string
|
|
@@ -40,19 +36,6 @@ export interface ChipGroupProps {
|
|
|
40
36
|
|
|
41
37
|
function ChipBase({ label, selected = false, onPress, icon, iconName, style, accessibilityLabel }: ChipProps) {
|
|
42
38
|
const { colors } = useTheme()
|
|
43
|
-
const { animatedStyle: scaleStyle, onPressIn, onPressOut, hoverHandlers } = usePressScale({
|
|
44
|
-
pressScale: PRESS_SCALE.chip,
|
|
45
|
-
})
|
|
46
|
-
const colorProgress = useColorTransition(selected)
|
|
47
|
-
|
|
48
|
-
const surfaceStyle = useAnimatedStyle(() => ({
|
|
49
|
-
backgroundColor: interpolateColor(colorProgress.value, [0, 1], [colors.surface, colors.primary]),
|
|
50
|
-
borderColor: interpolateColor(colorProgress.value, [0, 1], [colors.border, colors.primary]),
|
|
51
|
-
}))
|
|
52
|
-
|
|
53
|
-
const textStyle = useAnimatedStyle(() => ({
|
|
54
|
-
color: interpolateColor(colorProgress.value, [0, 1], [colors.foreground, colors.primaryForeground]),
|
|
55
|
-
}))
|
|
56
39
|
|
|
57
40
|
const handlePress = () => {
|
|
58
41
|
hapticSelection()
|
|
@@ -64,25 +47,33 @@ function ChipBase({ label, selected = false, onPress, icon, iconName, style, acc
|
|
|
64
47
|
: icon
|
|
65
48
|
|
|
66
49
|
return (
|
|
67
|
-
<
|
|
68
|
-
<
|
|
50
|
+
<View style={[styles.wrapper, style]}>
|
|
51
|
+
<PressableChipComponent
|
|
69
52
|
onPress={handlePress}
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
activeOpacity={1}
|
|
73
|
-
touchSoundDisabled={true}
|
|
53
|
+
rippleColor="transparent"
|
|
54
|
+
touchSoundDisabled
|
|
74
55
|
accessibilityRole="button"
|
|
75
56
|
accessibilityLabel={accessibilityLabel ?? label}
|
|
76
57
|
accessibilityState={{ selected }}
|
|
77
58
|
>
|
|
78
|
-
<
|
|
59
|
+
<EaseView
|
|
60
|
+
style={styles.chip}
|
|
61
|
+
animate={{
|
|
62
|
+
backgroundColor: selected ? colors.primary : colors.surface,
|
|
63
|
+
borderColor: selected ? colors.primary : colors.border,
|
|
64
|
+
}}
|
|
65
|
+
transition={COLOR_TRANSITION}
|
|
66
|
+
>
|
|
79
67
|
{resolvedIcon ? <View style={styles.chipIcon}>{resolvedIcon}</View> : null}
|
|
80
|
-
<
|
|
68
|
+
<Text
|
|
69
|
+
style={[styles.label, { color: selected ? colors.primaryForeground : colors.foreground }]}
|
|
70
|
+
allowFontScaling={true}
|
|
71
|
+
>
|
|
81
72
|
{label}
|
|
82
|
-
</
|
|
83
|
-
</
|
|
84
|
-
</
|
|
85
|
-
</
|
|
73
|
+
</Text>
|
|
74
|
+
</EaseView>
|
|
75
|
+
</PressableChipComponent>
|
|
76
|
+
</View>
|
|
86
77
|
)
|
|
87
78
|
}
|
|
88
79
|
|
|
@@ -2,15 +2,13 @@ import React from 'react'
|
|
|
2
2
|
import { ViewStyle, TextStyle } from 'react-native'
|
|
3
3
|
import { Input } from '../Input'
|
|
4
4
|
import { ms, vs } from '../../utils/scaling'
|
|
5
|
-
import { renderIcon } from '../../utils/icons'
|
|
6
|
-
import { useTheme } from '../../theme'
|
|
7
5
|
|
|
8
6
|
export interface CurrencyInputProps {
|
|
9
7
|
value?: string
|
|
10
8
|
onChangeText?: (formatted: string) => void
|
|
11
9
|
/** Called with the parsed numeric value (no separators, no prefix). */
|
|
12
10
|
onChangeValue?: (raw: number) => void
|
|
13
|
-
/**
|
|
11
|
+
/** Currency symbol shown left of the value. Any string (`'$'`, `'€'`, `'£'`, `'COP '`). Defaults to `'$'`. */
|
|
14
12
|
prefix?: string
|
|
15
13
|
/** Character used to separate groups of three digits. Defaults to `'.'`. */
|
|
16
14
|
thousandsSeparator?: '.' | ','
|
|
@@ -47,7 +45,6 @@ export function CurrencyInput({
|
|
|
47
45
|
containerStyle,
|
|
48
46
|
style,
|
|
49
47
|
}: CurrencyInputProps) {
|
|
50
|
-
const { colors } = useTheme()
|
|
51
48
|
const handleChange = (text: string) => {
|
|
52
49
|
const withoutPrefix = prefix && text.startsWith(prefix) ? text.slice(prefix.length) : text
|
|
53
50
|
const formatted = formatCurrency(withoutPrefix, thousandsSeparator)
|
|
@@ -58,11 +55,15 @@ export function CurrencyInput({
|
|
|
58
55
|
onChangeValue?.(isNaN(raw) ? 0 : raw)
|
|
59
56
|
}
|
|
60
57
|
|
|
61
|
-
const
|
|
58
|
+
const isLarge = size === 'large'
|
|
59
|
+
const inputStyle: TextStyle = isLarge
|
|
62
60
|
? { fontFamily: 'Sohne-Regular', fontSize: ms(36) }
|
|
63
61
|
: { fontFamily: 'Sohne-Regular' }
|
|
64
62
|
|
|
65
|
-
const
|
|
63
|
+
const prefixStyle: TextStyle = {
|
|
64
|
+
fontFamily: 'Sohne-Regular',
|
|
65
|
+
fontSize: isLarge ? ms(32) : ms(17),
|
|
66
|
+
}
|
|
66
67
|
|
|
67
68
|
// Remove prefix from display value if present
|
|
68
69
|
const displayValue = value && prefix && value.startsWith(prefix) ? value.slice(prefix.length) : value
|
|
@@ -77,9 +78,10 @@ export function CurrencyInput({
|
|
|
77
78
|
hint={hint}
|
|
78
79
|
placeholder={placeholder ?? '0'}
|
|
79
80
|
editable={editable}
|
|
80
|
-
prefix={
|
|
81
|
+
prefix={prefix}
|
|
82
|
+
prefixStyle={prefixStyle}
|
|
81
83
|
containerStyle={containerStyle}
|
|
82
|
-
inputWrapperStyle={
|
|
84
|
+
inputWrapperStyle={isLarge ? { paddingVertical: vs(16), minHeight: 72 } : undefined}
|
|
83
85
|
style={[inputStyle, style]}
|
|
84
86
|
/>
|
|
85
87
|
)
|
|
@@ -84,8 +84,10 @@ const styles = StyleSheet.create({
|
|
|
84
84
|
borderWidth: 1,
|
|
85
85
|
borderStyle: 'dashed',
|
|
86
86
|
borderRadius: ms(12),
|
|
87
|
+
paddingBottom: vs(32),
|
|
87
88
|
},
|
|
88
89
|
containerCompact: {
|
|
90
|
+
paddingBottom: vs(20),
|
|
89
91
|
},
|
|
90
92
|
iconWrapper: {
|
|
91
93
|
width: s(80),
|
|
@@ -125,7 +127,6 @@ const styles = StyleSheet.create({
|
|
|
125
127
|
},
|
|
126
128
|
action: {
|
|
127
129
|
marginTop: vs(8),
|
|
128
|
-
marginBottom: s(32),
|
|
129
130
|
paddingHorizontal: s(32),
|
|
130
131
|
},
|
|
131
132
|
})
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import { View, Text, TouchableOpacity, StyleSheet } from 'react-native'
|
|
3
|
+
import { useTheme } from '../../theme'
|
|
4
|
+
import { renderIcon } from '../../utils/icons'
|
|
5
|
+
import { s, vs, ms, mvs } from '../../utils/scaling'
|
|
6
|
+
import { RADIUS } from '../../tokens'
|
|
7
|
+
import { impactLight } from '../../utils/haptics'
|
|
8
|
+
|
|
9
|
+
export interface ErrorFallbackProps {
|
|
10
|
+
error: Error
|
|
11
|
+
/** Reset the boundary and attempt to re-render children. */
|
|
12
|
+
reset: () => void
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface ErrorBoundaryProps {
|
|
16
|
+
children: React.ReactNode
|
|
17
|
+
/**
|
|
18
|
+
* Custom fallback. Either a React node, or a render function receiving the
|
|
19
|
+
* caught error and a `reset` callback. Defaults to a themed error card.
|
|
20
|
+
*/
|
|
21
|
+
fallback?: React.ReactNode | ((props: ErrorFallbackProps) => React.ReactNode)
|
|
22
|
+
/** Title shown by the default fallback. */
|
|
23
|
+
title?: string
|
|
24
|
+
/** Body message shown by the default fallback. */
|
|
25
|
+
message?: string
|
|
26
|
+
/** Called whenever an error is caught — wire your crash reporter here. */
|
|
27
|
+
onError?: (error: Error, info: React.ErrorInfo) => void
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
interface ErrorBoundaryState {
|
|
31
|
+
error: Error | null
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/** Default themed fallback — uses `useTheme`, so it must be a component (not inline). */
|
|
35
|
+
function DefaultErrorFallback({
|
|
36
|
+
error,
|
|
37
|
+
reset,
|
|
38
|
+
title = 'Something went wrong',
|
|
39
|
+
message,
|
|
40
|
+
}: ErrorFallbackProps & { title?: string; message?: string }) {
|
|
41
|
+
const { colors } = useTheme()
|
|
42
|
+
return (
|
|
43
|
+
<View style={[styles.container, { backgroundColor: colors.background }]} accessibilityRole="alert">
|
|
44
|
+
<View style={[styles.iconCircle, { backgroundColor: colors.destructiveTint }]}>
|
|
45
|
+
{renderIcon('alert-triangle', ms(28), colors.destructive)}
|
|
46
|
+
</View>
|
|
47
|
+
<Text style={[styles.title, { color: colors.foreground }]} allowFontScaling={true}>
|
|
48
|
+
{title}
|
|
49
|
+
</Text>
|
|
50
|
+
<Text style={[styles.message, { color: colors.foregroundMuted }]} allowFontScaling={true}>
|
|
51
|
+
{message ?? error.message ?? 'An unexpected error occurred.'}
|
|
52
|
+
</Text>
|
|
53
|
+
<TouchableOpacity
|
|
54
|
+
style={[styles.button, { backgroundColor: colors.primary }]}
|
|
55
|
+
onPress={() => {
|
|
56
|
+
impactLight()
|
|
57
|
+
reset()
|
|
58
|
+
}}
|
|
59
|
+
activeOpacity={0.85}
|
|
60
|
+
touchSoundDisabled={true}
|
|
61
|
+
accessibilityRole="button"
|
|
62
|
+
accessibilityLabel="Try again"
|
|
63
|
+
>
|
|
64
|
+
<Text style={[styles.buttonLabel, { color: colors.primaryForeground }]} allowFontScaling={true}>
|
|
65
|
+
Try again
|
|
66
|
+
</Text>
|
|
67
|
+
</TouchableOpacity>
|
|
68
|
+
</View>
|
|
69
|
+
)
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Catches render-time errors in its subtree and shows a themed fallback instead
|
|
74
|
+
* of unmounting the whole app. Wrap screens or risky subtrees.
|
|
75
|
+
*
|
|
76
|
+
* @example
|
|
77
|
+
* <ErrorBoundary onError={reportCrash}>
|
|
78
|
+
* <DocumentViewer />
|
|
79
|
+
* </ErrorBoundary>
|
|
80
|
+
*/
|
|
81
|
+
export class ErrorBoundary extends React.Component<ErrorBoundaryProps, ErrorBoundaryState> {
|
|
82
|
+
state: ErrorBoundaryState = { error: null }
|
|
83
|
+
|
|
84
|
+
static getDerivedStateFromError(error: Error): ErrorBoundaryState {
|
|
85
|
+
return { error }
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
componentDidCatch(error: Error, info: React.ErrorInfo) {
|
|
89
|
+
this.props.onError?.(error, info)
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
reset = () => {
|
|
93
|
+
this.setState({ error: null })
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
render() {
|
|
97
|
+
const { error } = this.state
|
|
98
|
+
if (error) {
|
|
99
|
+
const { fallback, title, message } = this.props
|
|
100
|
+
if (typeof fallback === 'function') {
|
|
101
|
+
return fallback({ error, reset: this.reset })
|
|
102
|
+
}
|
|
103
|
+
if (fallback !== undefined) {
|
|
104
|
+
return fallback
|
|
105
|
+
}
|
|
106
|
+
return <DefaultErrorFallback error={error} reset={this.reset} title={title} message={message} />
|
|
107
|
+
}
|
|
108
|
+
return this.props.children
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const styles = StyleSheet.create({
|
|
113
|
+
container: {
|
|
114
|
+
flex: 1,
|
|
115
|
+
alignItems: 'center',
|
|
116
|
+
justifyContent: 'center',
|
|
117
|
+
paddingHorizontal: s(32),
|
|
118
|
+
gap: vs(12),
|
|
119
|
+
},
|
|
120
|
+
iconCircle: {
|
|
121
|
+
width: s(64),
|
|
122
|
+
height: s(64),
|
|
123
|
+
borderRadius: RADIUS.full,
|
|
124
|
+
alignItems: 'center',
|
|
125
|
+
justifyContent: 'center',
|
|
126
|
+
marginBottom: vs(4),
|
|
127
|
+
},
|
|
128
|
+
title: {
|
|
129
|
+
fontFamily: 'Sohne-SemiBold',
|
|
130
|
+
fontSize: ms(18),
|
|
131
|
+
lineHeight: mvs(24),
|
|
132
|
+
textAlign: 'center',
|
|
133
|
+
},
|
|
134
|
+
message: {
|
|
135
|
+
fontFamily: 'Sohne-Regular',
|
|
136
|
+
fontSize: ms(14),
|
|
137
|
+
lineHeight: mvs(20),
|
|
138
|
+
textAlign: 'center',
|
|
139
|
+
},
|
|
140
|
+
button: {
|
|
141
|
+
marginTop: vs(8),
|
|
142
|
+
paddingHorizontal: s(20),
|
|
143
|
+
paddingVertical: vs(10),
|
|
144
|
+
borderRadius: RADIUS.md,
|
|
145
|
+
minHeight: vs(44),
|
|
146
|
+
alignItems: 'center',
|
|
147
|
+
justifyContent: 'center',
|
|
148
|
+
},
|
|
149
|
+
buttonLabel: {
|
|
150
|
+
fontFamily: 'Sohne-Medium',
|
|
151
|
+
fontSize: ms(15),
|
|
152
|
+
},
|
|
153
|
+
})
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './ErrorBoundary'
|