@retray-dev/ui-kit 6.1.0 → 7.0.1
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 +447 -13
- package/EXAMPLES.md +248 -0
- package/README.md +11 -10
- package/dist/Accordion.d.mts +28 -0
- package/dist/Accordion.d.ts +28 -0
- package/dist/Accordion.js +340 -0
- package/dist/Accordion.mjs +6 -0
- package/dist/AlertBanner.d.mts +16 -0
- package/dist/AlertBanner.d.ts +16 -0
- package/dist/AlertBanner.js +247 -0
- package/dist/AlertBanner.mjs +5 -0
- package/dist/Avatar.d.mts +20 -0
- package/dist/Avatar.d.ts +20 -0
- package/dist/Avatar.js +234 -0
- package/dist/Avatar.mjs +3 -0
- package/dist/Badge.d.mts +26 -0
- package/dist/Badge.d.ts +26 -0
- package/dist/Badge.js +247 -0
- package/dist/Badge.mjs +4 -0
- package/dist/Button.d.mts +25 -0
- package/dist/Button.d.ts +25 -0
- package/dist/Button.js +414 -0
- package/dist/Button.mjs +8 -0
- package/dist/ButtonGroup.d.mts +26 -0
- package/dist/ButtonGroup.d.ts +26 -0
- package/dist/ButtonGroup.js +52 -0
- package/dist/ButtonGroup.mjs +2 -0
- package/dist/Card.d.mts +39 -0
- package/dist/Card.d.ts +39 -0
- package/dist/Card.js +329 -0
- package/dist/Card.mjs +7 -0
- package/dist/CategoryStrip.d.mts +26 -0
- package/dist/CategoryStrip.d.ts +26 -0
- package/dist/CategoryStrip.js +396 -0
- package/dist/CategoryStrip.mjs +9 -0
- package/dist/Checkbox.d.mts +14 -0
- package/dist/Checkbox.d.ts +14 -0
- package/dist/Checkbox.js +304 -0
- package/dist/Checkbox.mjs +7 -0
- package/dist/Chip.d.mts +31 -0
- package/dist/Chip.d.ts +31 -0
- package/dist/Chip.js +370 -0
- package/dist/Chip.mjs +8 -0
- package/dist/ConfirmDialog.d.mts +15 -0
- package/dist/ConfirmDialog.d.ts +15 -0
- package/dist/ConfirmDialog.js +530 -0
- package/dist/ConfirmDialog.mjs +9 -0
- package/dist/CurrencyDisplay.d.mts +24 -0
- package/dist/CurrencyDisplay.d.ts +24 -0
- package/dist/CurrencyDisplay.js +189 -0
- package/dist/CurrencyDisplay.mjs +3 -0
- package/dist/CurrencyInput.d.mts +26 -0
- package/dist/CurrencyInput.d.ts +26 -0
- package/dist/CurrencyInput.js +404 -0
- package/dist/CurrencyInput.mjs +7 -0
- package/dist/DetailRow.d.mts +32 -0
- package/dist/DetailRow.d.ts +32 -0
- package/dist/DetailRow.js +275 -0
- package/dist/DetailRow.mjs +4 -0
- package/dist/EmptyState.d.mts +27 -0
- package/dist/EmptyState.d.ts +27 -0
- package/dist/EmptyState.js +503 -0
- package/dist/EmptyState.mjs +9 -0
- package/dist/Form.d.mts +52 -0
- package/dist/Form.d.ts +52 -0
- package/dist/Form.js +204 -0
- package/dist/Form.mjs +3 -0
- package/dist/IconButton.d.mts +22 -0
- package/dist/IconButton.d.ts +22 -0
- package/dist/IconButton.js +383 -0
- package/dist/IconButton.mjs +7 -0
- package/dist/Input.d.mts +23 -0
- package/dist/Input.d.ts +23 -0
- package/dist/Input.js +351 -0
- package/dist/Input.mjs +6 -0
- package/dist/LabelValue.d.mts +16 -0
- package/dist/LabelValue.d.ts +16 -0
- package/dist/LabelValue.js +225 -0
- package/dist/LabelValue.mjs +4 -0
- package/dist/ListGroup.d.mts +34 -0
- package/dist/ListGroup.d.ts +34 -0
- package/dist/ListGroup.js +217 -0
- package/dist/ListGroup.mjs +4 -0
- package/dist/ListItem.d.mts +64 -0
- package/dist/ListItem.d.ts +64 -0
- package/dist/ListItem.js +430 -0
- package/dist/ListItem.mjs +8 -0
- package/dist/MediaCard.d.mts +39 -0
- package/dist/MediaCard.d.ts +39 -0
- package/dist/MediaCard.js +427 -0
- package/dist/MediaCard.mjs +8 -0
- package/dist/MenuGroup.d.mts +34 -0
- package/dist/MenuGroup.d.ts +34 -0
- package/dist/MenuGroup.js +217 -0
- package/dist/MenuGroup.mjs +4 -0
- package/dist/MenuItem.d.mts +48 -0
- package/dist/MenuItem.d.ts +48 -0
- package/dist/MenuItem.js +403 -0
- package/dist/MenuItem.mjs +8 -0
- package/dist/MonthPicker.d.mts +20 -0
- package/dist/MonthPicker.d.ts +20 -0
- package/dist/MonthPicker.js +234 -0
- package/dist/MonthPicker.mjs +4 -0
- package/dist/Pressable.d.mts +34 -0
- package/dist/Pressable.d.ts +34 -0
- package/dist/Pressable.js +132 -0
- package/dist/Pressable.mjs +4 -0
- package/dist/Progress.d.mts +14 -0
- package/dist/Progress.d.ts +14 -0
- package/dist/Progress.js +191 -0
- package/dist/Progress.mjs +4 -0
- package/dist/RadioGroup.d.mts +19 -0
- package/dist/RadioGroup.d.ts +19 -0
- package/dist/RadioGroup.js +341 -0
- package/dist/RadioGroup.mjs +7 -0
- package/dist/Select.d.mts +22 -0
- package/dist/Select.d.ts +22 -0
- package/dist/Select.js +441 -0
- package/dist/Select.mjs +6 -0
- package/dist/Separator.d.mts +10 -0
- package/dist/Separator.d.ts +10 -0
- package/dist/Separator.js +156 -0
- package/dist/Separator.mjs +2 -0
- package/dist/Sheet.d.mts +81 -0
- package/dist/Sheet.d.ts +81 -0
- package/dist/Sheet.js +340 -0
- package/dist/Sheet.mjs +4 -0
- package/dist/Skeleton.d.mts +17 -0
- package/dist/Skeleton.d.ts +17 -0
- package/dist/Skeleton.js +205 -0
- package/dist/Skeleton.mjs +4 -0
- package/dist/Slider.d.mts +20 -0
- package/dist/Slider.d.ts +20 -0
- package/dist/Slider.js +232 -0
- package/dist/Slider.mjs +4 -0
- package/dist/Spinner.d.mts +12 -0
- package/dist/Spinner.d.ts +12 -0
- package/dist/Spinner.js +172 -0
- package/dist/Spinner.mjs +3 -0
- package/dist/Switch.d.mts +13 -0
- package/dist/Switch.d.ts +13 -0
- package/dist/Switch.js +261 -0
- package/dist/Switch.mjs +5 -0
- package/dist/Tabs.d.mts +27 -0
- package/dist/Tabs.d.ts +27 -0
- package/dist/Tabs.js +389 -0
- package/dist/Tabs.mjs +6 -0
- package/dist/Text.d.mts +12 -0
- package/dist/Text.d.ts +12 -0
- package/dist/Text.js +311 -0
- package/dist/Text.mjs +4 -0
- package/dist/Textarea.d.mts +16 -0
- package/dist/Textarea.d.ts +16 -0
- package/dist/Textarea.js +333 -0
- package/dist/Textarea.mjs +6 -0
- package/dist/Toast.d.mts +47 -0
- package/dist/Toast.d.ts +47 -0
- package/dist/Toast.js +185 -0
- package/dist/Toast.mjs +3 -0
- package/dist/Toggle.d.mts +33 -0
- package/dist/Toggle.d.ts +33 -0
- package/dist/Toggle.js +397 -0
- package/dist/Toggle.mjs +8 -0
- package/dist/VirtualList.d.mts +19 -0
- package/dist/VirtualList.d.ts +19 -0
- package/dist/VirtualList.js +38 -0
- package/dist/VirtualList.mjs +1 -0
- package/dist/chunk-2CE3TQVY.mjs +11 -0
- package/dist/chunk-2UYENBLV.mjs +49 -0
- package/dist/chunk-3BBOZ3OQ.mjs +41 -0
- package/dist/chunk-5IKW3VNC.mjs +43 -0
- package/dist/chunk-63357L2X.mjs +51 -0
- package/dist/chunk-6LQYY7HC.mjs +127 -0
- package/dist/chunk-6Q64UFIA.mjs +71 -0
- package/dist/chunk-76PFOSM2.mjs +41 -0
- package/dist/chunk-7H2OR44A.mjs +14 -0
- package/dist/chunk-A4MDAP7G.mjs +42 -0
- package/dist/chunk-AU2VDY4P.mjs +190 -0
- package/dist/chunk-BRKYVJVV.mjs +60 -0
- package/dist/chunk-CRYBX2CM.mjs +146 -0
- package/dist/chunk-DITNP6PL.mjs +106 -0
- package/dist/chunk-FTLJOUOQ.mjs +97 -0
- package/dist/chunk-GCWOGZYL.mjs +104 -0
- package/dist/chunk-GNGLDL6Z.mjs +60 -0
- package/dist/chunk-GPOUINK5.mjs +148 -0
- package/dist/chunk-HSPSMN6U.mjs +115 -0
- package/dist/chunk-IRRY3CRZ.mjs +82 -0
- package/dist/chunk-JB67UOB5.mjs +92 -0
- package/dist/chunk-JBLL7U3U.mjs +64 -0
- package/dist/chunk-KWCPOM6W.mjs +136 -0
- package/dist/chunk-KZJRQOIU.mjs +64 -0
- package/dist/chunk-L7E7TVEZ.mjs +145 -0
- package/dist/chunk-LG4DO3DK.mjs +174 -0
- package/dist/chunk-LWG526VX.mjs +139 -0
- package/dist/chunk-MN7OG7IY.mjs +96 -0
- package/dist/chunk-MX6HRKMI.mjs +29 -0
- package/dist/chunk-NC5ZTR2Y.mjs +32 -0
- package/dist/chunk-NQGVLMWG.mjs +90 -0
- package/dist/chunk-QCNARS3X.mjs +46 -0
- package/dist/chunk-QXGYKWI7.mjs +134 -0
- package/dist/chunk-QY3X2UYR.mjs +191 -0
- package/dist/chunk-RKLHUDZS.mjs +92 -0
- package/dist/chunk-RMMK64W5.mjs +54 -0
- package/dist/chunk-RR2VQLKE.mjs +190 -0
- package/dist/chunk-RTC3CFXF.mjs +29 -0
- package/dist/chunk-SBZYEV4S.mjs +61 -0
- package/dist/chunk-SOA2Z4RB.mjs +82 -0
- package/dist/chunk-SOYNZDVY.mjs +151 -0
- package/dist/chunk-T7XZ7H7Y.mjs +57 -0
- package/dist/chunk-TAJ2PQ2O.mjs +163 -0
- package/dist/chunk-U4N7WF4Z.mjs +108 -0
- package/dist/chunk-URDE3EUU.mjs +132 -0
- package/dist/chunk-URLL5JBR.mjs +245 -0
- package/dist/chunk-XDMN67KV.mjs +59 -0
- package/dist/chunk-Y6MXOREN.mjs +120 -0
- package/dist/chunk-YZJAFS4P.mjs +131 -0
- package/dist/index.d.mts +94 -852
- package/dist/index.d.ts +94 -852
- package/dist/index.js +1387 -942
- package/dist/index.mjs +50 -3844
- package/package.json +23 -14
- package/src/assets/fonts/Sohne-Bold.otf +0 -0
- package/src/assets/fonts/Sohne-BoldItalic.otf +0 -0
- package/src/assets/fonts/Sohne-ExtraBold.otf +0 -0
- package/src/assets/fonts/Sohne-ExtraBoldItalic.otf +0 -0
- package/src/assets/fonts/Sohne-ExtraLight.otf +0 -0
- package/src/assets/fonts/Sohne-ExtraLightItalic.otf +0 -0
- package/src/assets/fonts/Sohne-Italic.otf +0 -0
- package/src/assets/fonts/Sohne-Light.otf +0 -0
- package/src/assets/fonts/Sohne-LightItalic.otf +0 -0
- package/src/assets/fonts/Sohne-Medium.otf +0 -0
- package/src/assets/fonts/Sohne-MediumItalic.otf +0 -0
- package/src/assets/fonts/Sohne-Regular.otf +0 -0
- package/src/assets/fonts/Sohne-SemiBold.otf +0 -0
- package/src/assets/fonts/Sohne-SemiBoldItalic.otf +0 -0
- package/src/assets/fonts/SohneMono-Bold.otf +0 -0
- package/src/assets/fonts/SohneMono-BoldItalic.otf +0 -0
- package/src/assets/fonts/SohneMono-ExtraBold.otf +0 -0
- package/src/assets/fonts/SohneMono-ExtraBoldItalic.otf +0 -0
- package/src/assets/fonts/SohneMono-ExtraLight.otf +0 -0
- package/src/assets/fonts/SohneMono-ExtraLightItalic.otf +0 -0
- package/src/assets/fonts/SohneMono-Italic.otf +0 -0
- package/src/assets/fonts/SohneMono-Light.otf +0 -0
- package/src/assets/fonts/SohneMono-LightItalic.otf +0 -0
- package/src/assets/fonts/SohneMono-Medium.otf +0 -0
- package/src/assets/fonts/SohneMono-MediumItalic.otf +0 -0
- package/src/assets/fonts/SohneMono-Regular.otf +0 -0
- package/src/assets/fonts/SohneMono-SemiBold.otf +0 -0
- package/src/assets/fonts/SohneMono-SemiBoldItalic.otf +0 -0
- package/src/components/Accordion/Accordion.tsx +13 -15
- package/src/components/AlertBanner/AlertBanner.tsx +33 -12
- package/src/components/Avatar/Avatar.tsx +4 -2
- package/src/components/Badge/Badge.tsx +4 -2
- package/src/components/Button/Button.tsx +30 -29
- package/src/components/ButtonGroup/ButtonGroup.tsx +13 -10
- package/src/components/Card/Card.tsx +36 -65
- package/src/components/CategoryStrip/CategoryStrip.tsx +68 -58
- package/src/components/Checkbox/Checkbox.tsx +41 -55
- package/src/components/Chip/Chip.tsx +49 -84
- package/src/components/ConfirmDialog/ConfirmDialog.tsx +2 -2
- package/src/components/CurrencyDisplay/CurrencyDisplay.tsx +4 -2
- package/src/components/CurrencyInput/CurrencyInput.tsx +2 -2
- package/src/components/DetailRow/DetailRow.tsx +9 -7
- package/src/components/EmptyState/EmptyState.tsx +2 -2
- package/src/components/Form/Form.tsx +149 -0
- package/src/components/Form/index.ts +1 -0
- package/src/components/IconButton/IconButton.tsx +24 -20
- package/src/components/Input/Input.tsx +63 -50
- package/src/components/LabelValue/LabelValue.tsx +6 -4
- package/src/components/ListGroup/ListGroup.tsx +145 -0
- package/src/components/ListGroup/index.ts +1 -0
- package/src/components/ListItem/ListItem.tsx +30 -43
- package/src/components/MediaCard/MediaCard.tsx +31 -29
- package/src/components/MenuGroup/MenuGroup.tsx +145 -0
- package/src/components/MenuGroup/index.ts +1 -0
- package/src/components/MenuItem/MenuItem.tsx +29 -40
- package/src/components/MonthPicker/MonthPicker.tsx +14 -4
- package/src/components/Pressable/Pressable.tsx +27 -46
- package/src/components/Progress/Progress.tsx +21 -12
- package/src/components/RadioGroup/RadioGroup.tsx +55 -32
- package/src/components/Select/Select.tsx +23 -21
- package/src/components/Separator/Separator.tsx +1 -3
- package/src/components/Sheet/Sheet.tsx +85 -18
- package/src/components/Skeleton/Skeleton.tsx +25 -14
- package/src/components/Slider/Slider.tsx +13 -3
- package/src/components/Spinner/Spinner.tsx +1 -1
- package/src/components/Switch/Switch.tsx +70 -52
- package/src/components/Tabs/Tabs.tsx +59 -47
- package/src/components/Text/Text.tsx +3 -1
- package/src/components/Textarea/Textarea.tsx +44 -23
- package/src/components/Toast/Toast.tsx +6 -6
- package/src/components/Toggle/Toggle.tsx +86 -68
- package/src/components/VirtualList/VirtualList.tsx +60 -0
- package/src/components/VirtualList/index.ts +1 -0
- package/src/fonts.ts +38 -20
- package/src/index.ts +5 -1
- package/src/theme/colors.ts +53 -39
- package/src/theme/types.ts +3 -0
- package/src/tokens.ts +49 -39
- package/src/utils/animations.ts +58 -0
- package/src/utils/icons.ts +47 -20
- package/src/utils/useColorTransition.ts +40 -0
- package/src/utils/usePressScale.ts +75 -0
- package/src/assets/fonts/Poppins-Black.ttf +0 -0
- package/src/assets/fonts/Poppins-BlackItalic.ttf +0 -0
- package/src/assets/fonts/Poppins-Bold.ttf +0 -0
- package/src/assets/fonts/Poppins-BoldItalic.ttf +0 -0
- package/src/assets/fonts/Poppins-ExtraBold.ttf +0 -0
- package/src/assets/fonts/Poppins-ExtraBoldItalic.ttf +0 -0
- package/src/assets/fonts/Poppins-ExtraLight.ttf +0 -0
- package/src/assets/fonts/Poppins-ExtraLightItalic.ttf +0 -0
- package/src/assets/fonts/Poppins-Italic.ttf +0 -0
- package/src/assets/fonts/Poppins-Light.ttf +0 -0
- package/src/assets/fonts/Poppins-LightItalic.ttf +0 -0
- package/src/assets/fonts/Poppins-Medium.ttf +0 -0
- package/src/assets/fonts/Poppins-MediumItalic.ttf +0 -0
- package/src/assets/fonts/Poppins-Regular.ttf +0 -0
- package/src/assets/fonts/Poppins-SemiBold.ttf +0 -0
- package/src/assets/fonts/Poppins-SemiBoldItalic.ttf +0 -0
- package/src/assets/fonts/Poppins-Thin.ttf +0 -0
- package/src/assets/fonts/Poppins-ThinItalic.ttf +0 -0
|
@@ -1,7 +1,13 @@
|
|
|
1
|
-
import React, {
|
|
2
|
-
import { View,
|
|
1
|
+
import React, { useEffect, useState } from 'react'
|
|
2
|
+
import { View, StyleSheet, ViewStyle } from 'react-native'
|
|
3
|
+
import Animated, {
|
|
4
|
+
useSharedValue,
|
|
5
|
+
useAnimatedStyle,
|
|
6
|
+
withSpring,
|
|
7
|
+
} from 'react-native-reanimated'
|
|
3
8
|
import { useTheme } from '../../theme'
|
|
4
9
|
import { vs } from '../../utils/scaling'
|
|
10
|
+
import { SPRINGS } from '../../utils/animations'
|
|
5
11
|
|
|
6
12
|
export type ProgressVariant = 'default' | 'success' | 'warning' | 'destructive'
|
|
7
13
|
|
|
@@ -10,23 +16,23 @@ export interface ProgressProps {
|
|
|
10
16
|
max?: number
|
|
11
17
|
variant?: ProgressVariant
|
|
12
18
|
style?: ViewStyle
|
|
19
|
+
accessibilityLabel?: string
|
|
13
20
|
}
|
|
14
21
|
|
|
15
|
-
export function Progress({ value = 0, max = 100, variant = 'default', style }: ProgressProps) {
|
|
22
|
+
export function Progress({ value = 0, max = 100, variant = 'default', style, accessibilityLabel }: ProgressProps) {
|
|
16
23
|
const { colors } = useTheme()
|
|
17
24
|
const percent = Math.min(Math.max((value / max) * 100, 0), 100)
|
|
18
25
|
const [trackWidth, setTrackWidth] = useState(0)
|
|
19
|
-
const animatedWidth =
|
|
26
|
+
const animatedWidth = useSharedValue(0)
|
|
20
27
|
|
|
21
28
|
useEffect(() => {
|
|
22
29
|
if (trackWidth === 0) return
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
}, [percent, trackWidth])
|
|
30
|
+
animatedWidth.value = withSpring((percent / 100) * trackWidth, SPRINGS.glide)
|
|
31
|
+
}, [percent, trackWidth, animatedWidth])
|
|
32
|
+
|
|
33
|
+
const indicatorAnimatedStyle = useAnimatedStyle(() => ({
|
|
34
|
+
width: animatedWidth.value,
|
|
35
|
+
}))
|
|
30
36
|
|
|
31
37
|
const indicatorColor =
|
|
32
38
|
variant === 'success' ? colors.success
|
|
@@ -38,9 +44,12 @@ export function Progress({ value = 0, max = 100, variant = 'default', style }: P
|
|
|
38
44
|
<View
|
|
39
45
|
style={[styles.track, { backgroundColor: colors.surface }, style]}
|
|
40
46
|
onLayout={(e) => setTrackWidth(e.nativeEvent.layout.width)}
|
|
47
|
+
accessibilityRole="progressbar"
|
|
48
|
+
accessibilityLabel={accessibilityLabel}
|
|
49
|
+
accessibilityValue={{ min: 0, max: 100, now: Math.round(percent) }}
|
|
41
50
|
>
|
|
42
51
|
<Animated.View
|
|
43
|
-
style={[styles.indicator, {
|
|
52
|
+
style={[styles.indicator, { backgroundColor: indicatorColor }, indicatorAnimatedStyle]}
|
|
44
53
|
/>
|
|
45
54
|
</View>
|
|
46
55
|
)
|
|
@@ -1,10 +1,18 @@
|
|
|
1
|
-
import React
|
|
2
|
-
import { TouchableOpacity,
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import { TouchableOpacity, View, Text, StyleSheet, ViewStyle } from 'react-native'
|
|
3
|
+
import Animated, {
|
|
4
|
+
useAnimatedStyle,
|
|
5
|
+
useSharedValue,
|
|
6
|
+
withSpring,
|
|
7
|
+
interpolateColor,
|
|
8
|
+
} from 'react-native-reanimated'
|
|
9
|
+
import { useEffect } from 'react'
|
|
3
10
|
import { selectionAsync as hapticSelection } from '../../utils/haptics'
|
|
4
|
-
|
|
5
|
-
const nativeDriver = Platform.OS !== 'web'
|
|
6
11
|
import { useTheme } from '../../theme'
|
|
7
12
|
import { s, vs, ms, mvs } from '../../utils/scaling'
|
|
13
|
+
import { usePressScale } from '../../utils/usePressScale'
|
|
14
|
+
import { useColorTransition } from '../../utils/useColorTransition'
|
|
15
|
+
import { SPRINGS, PRESS_SCALE } from '../../utils/animations'
|
|
8
16
|
|
|
9
17
|
export interface RadioOption {
|
|
10
18
|
label: string
|
|
@@ -18,6 +26,7 @@ export interface RadioGroupProps {
|
|
|
18
26
|
onValueChange?: (value: string) => void
|
|
19
27
|
orientation?: 'vertical' | 'horizontal'
|
|
20
28
|
style?: ViewStyle
|
|
29
|
+
accessibilityLabel?: string
|
|
21
30
|
}
|
|
22
31
|
|
|
23
32
|
function RadioItem({
|
|
@@ -30,49 +39,54 @@ function RadioItem({
|
|
|
30
39
|
onSelect: () => void
|
|
31
40
|
}) {
|
|
32
41
|
const { colors } = useTheme()
|
|
33
|
-
const
|
|
42
|
+
const { animatedStyle: scaleStyle, onPressIn, onPressOut } = usePressScale({
|
|
43
|
+
pressScale: PRESS_SCALE.button,
|
|
44
|
+
disabled: option.disabled,
|
|
45
|
+
})
|
|
46
|
+
const colorProgress = useColorTransition(selected)
|
|
47
|
+
|
|
48
|
+
const dotScale = useSharedValue(selected ? 1 : 0)
|
|
49
|
+
useEffect(() => {
|
|
50
|
+
dotScale.value = withSpring(selected ? 1 : 0, SPRINGS.elastic)
|
|
51
|
+
}, [selected, dotScale])
|
|
34
52
|
|
|
35
|
-
const
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
}
|
|
53
|
+
const radioStyle = useAnimatedStyle(() => ({
|
|
54
|
+
borderColor: interpolateColor(colorProgress.value, [0, 1], [colors.border, colors.primary]),
|
|
55
|
+
}))
|
|
39
56
|
|
|
40
|
-
const
|
|
41
|
-
|
|
42
|
-
|
|
57
|
+
const dotStyle = useAnimatedStyle(() => ({
|
|
58
|
+
transform: [{ scale: dotScale.value }],
|
|
59
|
+
opacity: dotScale.value,
|
|
60
|
+
}))
|
|
43
61
|
|
|
44
62
|
return (
|
|
63
|
+
// AUDIT FIX: opacity was applied only to the radio circle, leaving the label
|
|
64
|
+
// at full opacity when disabled. The whole row now dims uniformly so users
|
|
65
|
+
// get a single, consistent disabled signal across the entire item.
|
|
45
66
|
<TouchableOpacity
|
|
46
|
-
style={styles.row}
|
|
67
|
+
style={[styles.row, option.disabled && styles.rowDisabled]}
|
|
47
68
|
onPress={() => {
|
|
48
69
|
if (!option.disabled) {
|
|
49
70
|
hapticSelection()
|
|
50
71
|
onSelect()
|
|
51
72
|
}
|
|
52
73
|
}}
|
|
53
|
-
onPressIn={
|
|
54
|
-
onPressOut={
|
|
74
|
+
onPressIn={onPressIn}
|
|
75
|
+
onPressOut={onPressOut}
|
|
55
76
|
activeOpacity={1}
|
|
56
77
|
touchSoundDisabled={true}
|
|
57
78
|
disabled={option.disabled}
|
|
79
|
+
accessibilityRole="radio"
|
|
80
|
+
accessibilityLabel={option.label}
|
|
81
|
+
accessibilityState={{ checked: selected, disabled: !!option.disabled }}
|
|
58
82
|
>
|
|
59
|
-
<Animated.View
|
|
60
|
-
style={[
|
|
61
|
-
styles.
|
|
62
|
-
|
|
63
|
-
borderColor: selected ? colors.primary : colors.border,
|
|
64
|
-
opacity: option.disabled ? 0.45 : 1,
|
|
65
|
-
transform: [{ scale }],
|
|
66
|
-
},
|
|
67
|
-
]}
|
|
68
|
-
>
|
|
69
|
-
{selected ? <View style={[styles.dot, { backgroundColor: colors.primary }]} /> : null}
|
|
83
|
+
<Animated.View style={scaleStyle}>
|
|
84
|
+
<Animated.View style={[styles.radio, radioStyle]}>
|
|
85
|
+
<Animated.View style={[styles.dot, { backgroundColor: colors.primary }, dotStyle]} />
|
|
86
|
+
</Animated.View>
|
|
70
87
|
</Animated.View>
|
|
71
88
|
<Text
|
|
72
|
-
style={[
|
|
73
|
-
styles.label,
|
|
74
|
-
{ color: option.disabled ? colors.foregroundMuted : colors.foreground },
|
|
75
|
-
]}
|
|
89
|
+
style={[styles.label, { color: colors.foreground }]}
|
|
76
90
|
allowFontScaling={true}
|
|
77
91
|
>
|
|
78
92
|
{option.label}
|
|
@@ -87,9 +101,14 @@ export function RadioGroup({
|
|
|
87
101
|
onValueChange,
|
|
88
102
|
orientation = 'vertical',
|
|
89
103
|
style,
|
|
104
|
+
accessibilityLabel,
|
|
90
105
|
}: RadioGroupProps) {
|
|
91
106
|
return (
|
|
92
|
-
<View
|
|
107
|
+
<View
|
|
108
|
+
style={[styles.container, orientation === 'horizontal' && styles.horizontal, style]}
|
|
109
|
+
accessibilityRole="radiogroup"
|
|
110
|
+
accessibilityLabel={accessibilityLabel}
|
|
111
|
+
>
|
|
93
112
|
{options.map((option) => (
|
|
94
113
|
<RadioItem
|
|
95
114
|
key={option.value}
|
|
@@ -115,6 +134,10 @@ const styles = StyleSheet.create({
|
|
|
115
134
|
alignItems: 'center',
|
|
116
135
|
gap: s(12),
|
|
117
136
|
},
|
|
137
|
+
// AUDIT FIX: was opacity on the inner circle only
|
|
138
|
+
rowDisabled: {
|
|
139
|
+
opacity: 0.45,
|
|
140
|
+
},
|
|
118
141
|
radio: {
|
|
119
142
|
width: s(24),
|
|
120
143
|
height: s(24),
|
|
@@ -129,7 +152,7 @@ const styles = StyleSheet.create({
|
|
|
129
152
|
borderRadius: s(5),
|
|
130
153
|
},
|
|
131
154
|
label: {
|
|
132
|
-
fontFamily: '
|
|
155
|
+
fontFamily: 'Sohne-Regular',
|
|
133
156
|
fontSize: ms(14),
|
|
134
157
|
lineHeight: mvs(20),
|
|
135
158
|
},
|
|
@@ -1,15 +1,17 @@
|
|
|
1
1
|
import React, { useRef, useState } from 'react'
|
|
2
|
-
import { View, Text, TouchableOpacity, Modal,
|
|
2
|
+
import { View, Text, TouchableOpacity, Modal, StyleSheet, ViewStyle, Platform } from 'react-native'
|
|
3
|
+
import Animated from 'react-native-reanimated'
|
|
3
4
|
import { Picker } from '@react-native-picker/picker'
|
|
4
5
|
import { Entypo } from '@expo/vector-icons'
|
|
5
6
|
import { selectionAsync as hapticSelection } from '../../utils/haptics'
|
|
6
7
|
import { useTheme } from '../../theme'
|
|
7
8
|
import { s, vs, ms } from '../../utils/scaling'
|
|
9
|
+
import { usePressScale } from '../../utils/usePressScale'
|
|
10
|
+
import { PRESS_SCALE } from '../../utils/animations'
|
|
8
11
|
|
|
9
12
|
const isIOS = Platform.OS === 'ios'
|
|
10
13
|
const isAndroid = Platform.OS === 'android'
|
|
11
14
|
const isWeb = Platform.OS === 'web'
|
|
12
|
-
const nativeDriver = Platform.OS !== 'web'
|
|
13
15
|
|
|
14
16
|
export interface SelectOption {
|
|
15
17
|
label: string
|
|
@@ -26,6 +28,7 @@ export interface SelectProps {
|
|
|
26
28
|
error?: string
|
|
27
29
|
disabled?: boolean
|
|
28
30
|
style?: ViewStyle
|
|
31
|
+
accessibilityLabel?: string
|
|
29
32
|
}
|
|
30
33
|
|
|
31
34
|
export function Select({
|
|
@@ -37,24 +40,19 @@ export function Select({
|
|
|
37
40
|
error,
|
|
38
41
|
disabled,
|
|
39
42
|
style,
|
|
43
|
+
accessibilityLabel,
|
|
40
44
|
}: SelectProps) {
|
|
41
45
|
const { colors } = useTheme()
|
|
42
|
-
const
|
|
46
|
+
const { animatedStyle, onPressIn, onPressOut } = usePressScale({
|
|
47
|
+
pressScale: PRESS_SCALE.button,
|
|
48
|
+
disabled,
|
|
49
|
+
})
|
|
43
50
|
const [pickerVisible, setPickerVisible] = useState(false)
|
|
44
51
|
const [pendingValue, setPendingValue] = useState<string | undefined>(value)
|
|
45
52
|
const pickerRef = useRef<React.ElementRef<typeof Picker>>(null)
|
|
46
53
|
|
|
47
54
|
const selected = options.find((o) => o.value === value)
|
|
48
55
|
|
|
49
|
-
const handlePressIn = () => {
|
|
50
|
-
if (disabled) return
|
|
51
|
-
Animated.spring(scale, { toValue: 0.95, useNativeDriver: nativeDriver, speed: 40, bounciness: 0 }).start()
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
const handlePressOut = () => {
|
|
55
|
-
Animated.spring(scale, { toValue: 1, useNativeDriver: nativeDriver, speed: 40, bounciness: 4 }).start()
|
|
56
|
-
}
|
|
57
|
-
|
|
58
56
|
const handleOpen = () => {
|
|
59
57
|
if (disabled) return
|
|
60
58
|
hapticSelection()
|
|
@@ -62,7 +60,7 @@ export function Select({
|
|
|
62
60
|
setPendingValue(value)
|
|
63
61
|
setPickerVisible(true)
|
|
64
62
|
} else if (isAndroid) {
|
|
65
|
-
;(pickerRef.current as
|
|
63
|
+
;(pickerRef.current as { focus?: () => void })?.focus?.()
|
|
66
64
|
}
|
|
67
65
|
}
|
|
68
66
|
|
|
@@ -84,7 +82,7 @@ export function Select({
|
|
|
84
82
|
|
|
85
83
|
{/* Trigger button — shown on iOS and Android only */}
|
|
86
84
|
{!isWeb ? (
|
|
87
|
-
<Animated.View style={
|
|
85
|
+
<Animated.View style={[animatedStyle, { opacity: disabled ? 0.45 : 1 }]}>
|
|
88
86
|
<TouchableOpacity
|
|
89
87
|
style={[
|
|
90
88
|
styles.trigger,
|
|
@@ -94,10 +92,14 @@ export function Select({
|
|
|
94
92
|
},
|
|
95
93
|
]}
|
|
96
94
|
onPress={handleOpen}
|
|
97
|
-
onPressIn={
|
|
98
|
-
onPressOut={
|
|
95
|
+
onPressIn={onPressIn}
|
|
96
|
+
onPressOut={onPressOut}
|
|
99
97
|
activeOpacity={1}
|
|
100
98
|
touchSoundDisabled={true}
|
|
99
|
+
accessibilityRole="combobox"
|
|
100
|
+
accessibilityLabel={accessibilityLabel ?? label}
|
|
101
|
+
accessibilityValue={{ text: selected?.label ?? placeholder }}
|
|
102
|
+
accessibilityState={{ disabled: !!disabled, expanded: pickerVisible }}
|
|
101
103
|
>
|
|
102
104
|
<Text
|
|
103
105
|
style={[
|
|
@@ -232,7 +234,7 @@ const styles = StyleSheet.create({
|
|
|
232
234
|
gap: vs(8),
|
|
233
235
|
},
|
|
234
236
|
label: {
|
|
235
|
-
fontFamily: '
|
|
237
|
+
fontFamily: 'Sohne-Medium',
|
|
236
238
|
fontSize: ms(13),
|
|
237
239
|
},
|
|
238
240
|
trigger: {
|
|
@@ -245,7 +247,7 @@ const styles = StyleSheet.create({
|
|
|
245
247
|
paddingVertical: vs(11),
|
|
246
248
|
},
|
|
247
249
|
triggerText: {
|
|
248
|
-
fontFamily: '
|
|
250
|
+
fontFamily: 'Sohne-Regular',
|
|
249
251
|
fontSize: ms(15),
|
|
250
252
|
flex: 1,
|
|
251
253
|
},
|
|
@@ -253,7 +255,7 @@ const styles = StyleSheet.create({
|
|
|
253
255
|
marginLeft: s(8),
|
|
254
256
|
},
|
|
255
257
|
helperText: {
|
|
256
|
-
fontFamily: '
|
|
258
|
+
fontFamily: 'Sohne-Regular',
|
|
257
259
|
fontSize: ms(13),
|
|
258
260
|
},
|
|
259
261
|
iosBackdrop: {
|
|
@@ -274,14 +276,14 @@ const styles = StyleSheet.create({
|
|
|
274
276
|
borderBottomWidth: 1,
|
|
275
277
|
},
|
|
276
278
|
iosToolbarTitle: {
|
|
277
|
-
fontFamily: '
|
|
279
|
+
fontFamily: 'Sohne-SemiBold',
|
|
278
280
|
fontSize: ms(17),
|
|
279
281
|
},
|
|
280
282
|
iosDoneBtn: {
|
|
281
283
|
padding: s(4),
|
|
282
284
|
},
|
|
283
285
|
iosDoneBtnText: {
|
|
284
|
-
fontFamily: '
|
|
286
|
+
fontFamily: 'Sohne-SemiBold',
|
|
285
287
|
fontSize: ms(17),
|
|
286
288
|
},
|
|
287
289
|
androidHiddenPicker: {
|
|
@@ -14,7 +14,7 @@ export function Separator({ orientation = 'horizontal', style }: SeparatorProps)
|
|
|
14
14
|
<View
|
|
15
15
|
style={[
|
|
16
16
|
orientation === 'horizontal' ? styles.horizontal : styles.vertical,
|
|
17
|
-
{ backgroundColor: colors.
|
|
17
|
+
{ backgroundColor: colors.separator },
|
|
18
18
|
style,
|
|
19
19
|
]}
|
|
20
20
|
/>
|
|
@@ -25,11 +25,9 @@ const styles = StyleSheet.create({
|
|
|
25
25
|
horizontal: {
|
|
26
26
|
height: 1,
|
|
27
27
|
width: '100%',
|
|
28
|
-
opacity: 0.7,
|
|
29
28
|
},
|
|
30
29
|
vertical: {
|
|
31
30
|
width: 1,
|
|
32
31
|
height: '100%',
|
|
33
|
-
opacity: 0.7,
|
|
34
32
|
},
|
|
35
33
|
})
|
|
@@ -25,6 +25,21 @@ export { BottomSheetModalProvider }
|
|
|
25
25
|
// Re-export BottomSheetTextInput as SheetTextInput for consumer convenience
|
|
26
26
|
export { BottomSheetTextInput as SheetTextInput }
|
|
27
27
|
|
|
28
|
+
export interface SheetHeaderProps {
|
|
29
|
+
children: React.ReactNode
|
|
30
|
+
style?: ViewStyle
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export interface SheetContentProps {
|
|
34
|
+
children: React.ReactNode
|
|
35
|
+
style?: ViewStyle
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export interface SheetFooterProps {
|
|
39
|
+
children: React.ReactNode
|
|
40
|
+
style?: ViewStyle
|
|
41
|
+
}
|
|
42
|
+
|
|
28
43
|
export interface SheetProps {
|
|
29
44
|
open: boolean
|
|
30
45
|
onClose: () => void
|
|
@@ -80,6 +95,23 @@ export interface SheetProps {
|
|
|
80
95
|
snapPoints?: (string | number)[]
|
|
81
96
|
}
|
|
82
97
|
|
|
98
|
+
export function SheetHeader({ children, style }: SheetHeaderProps) {
|
|
99
|
+
return <View style={[styles.header, style]}>{children}</View>
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export function SheetContent({ children, style }: SheetContentProps) {
|
|
103
|
+
return <View style={[styles.sheetContent, style]}>{children}</View>
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export function SheetFooter({ children, style }: SheetFooterProps) {
|
|
107
|
+
const { colors } = useTheme()
|
|
108
|
+
return (
|
|
109
|
+
<View style={[styles.sheetFooter, { backgroundColor: colors.card, borderTopColor: colors.border }, style]}>
|
|
110
|
+
{children}
|
|
111
|
+
</View>
|
|
112
|
+
)
|
|
113
|
+
}
|
|
114
|
+
|
|
83
115
|
export function Sheet({
|
|
84
116
|
open,
|
|
85
117
|
onClose,
|
|
@@ -125,21 +157,26 @@ export function Sheet({
|
|
|
125
157
|
/>
|
|
126
158
|
), [])
|
|
127
159
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
160
|
+
// Detect compound components in children
|
|
161
|
+
const childArray = React.Children.toArray(children)
|
|
162
|
+
const customHeader = childArray.find((child) => React.isValidElement(child) && child.type === SheetHeader)
|
|
163
|
+
const customContent = childArray.find((child) => React.isValidElement(child) && child.type === SheetContent)
|
|
164
|
+
const customFooter = childArray.find((child) => React.isValidElement(child) && child.type === SheetFooter)
|
|
165
|
+
|
|
166
|
+
// If using compound components, filter them out from main children
|
|
167
|
+
const filteredChildren = customHeader || customContent || customFooter
|
|
168
|
+
? childArray.filter(
|
|
169
|
+
(child) =>
|
|
170
|
+
!React.isValidElement(child) ||
|
|
171
|
+
(child.type !== SheetHeader && child.type !== SheetContent && child.type !== SheetFooter)
|
|
172
|
+
)
|
|
173
|
+
: children
|
|
136
174
|
|
|
137
175
|
const effectiveSubtitle = subtitle ?? description
|
|
176
|
+
const showHeader = !!(title || effectiveSubtitle || showCloseButton) && !customHeader
|
|
138
177
|
|
|
139
|
-
const
|
|
140
|
-
|
|
141
|
-
const headerNode = showHeader ? (
|
|
142
|
-
<View style={styles.header}>
|
|
178
|
+
const headerNode = customHeader ? customHeader : (showHeader ? (
|
|
179
|
+
<View style={styles.header} accessibilityRole="header">
|
|
143
180
|
<View style={styles.headerRow}>
|
|
144
181
|
{title ? (
|
|
145
182
|
<Text style={[styles.title, { color: colors.foreground }]} allowFontScaling={true}>
|
|
@@ -152,6 +189,9 @@ export function Sheet({
|
|
|
152
189
|
style={styles.closeButton}
|
|
153
190
|
activeOpacity={0.6}
|
|
154
191
|
touchSoundDisabled={true}
|
|
192
|
+
accessibilityRole="button"
|
|
193
|
+
accessibilityLabel="Close"
|
|
194
|
+
hitSlop={{ top: 12, bottom: 12, left: 12, right: 12 }}
|
|
155
195
|
>
|
|
156
196
|
<AntDesign name="close" size={ms(18)} color={colors.foregroundMuted} />
|
|
157
197
|
</TouchableOpacity>
|
|
@@ -163,7 +203,19 @@ export function Sheet({
|
|
|
163
203
|
</Text>
|
|
164
204
|
) : null}
|
|
165
205
|
</View>
|
|
166
|
-
) : null
|
|
206
|
+
) : null)
|
|
207
|
+
|
|
208
|
+
const contentNode = customContent ? customContent : filteredChildren
|
|
209
|
+
const effectiveFooter = customFooter ? customFooter : footer
|
|
210
|
+
|
|
211
|
+
const renderFooter = useCallback((props: BottomSheetFooterProps) => {
|
|
212
|
+
if (!effectiveFooter) return null
|
|
213
|
+
return (
|
|
214
|
+
<BottomSheetFooter {...props}>
|
|
215
|
+
{effectiveFooter}
|
|
216
|
+
</BottomSheetFooter>
|
|
217
|
+
)
|
|
218
|
+
}, [effectiveFooter])
|
|
167
219
|
|
|
168
220
|
const useScroll = scrollable || !!maxHeight
|
|
169
221
|
const effectiveMaxHeight = maxHeight ?? DEFAULT_MAX_HEIGHT
|
|
@@ -179,7 +231,7 @@ export function Sheet({
|
|
|
179
231
|
maxDynamicContentSize={useDynamicSizing ? effectiveMaxHeight : undefined}
|
|
180
232
|
onDismiss={onClose}
|
|
181
233
|
backdropComponent={renderBackdrop}
|
|
182
|
-
footerComponent={
|
|
234
|
+
footerComponent={effectiveFooter ? renderFooter : undefined}
|
|
183
235
|
backgroundStyle={[styles.background, { backgroundColor: colors.card }]}
|
|
184
236
|
handleIndicatorStyle={[styles.handle, { backgroundColor: colors.border }]}
|
|
185
237
|
enablePanDownToClose
|
|
@@ -201,18 +253,22 @@ export function Sheet({
|
|
|
201
253
|
persistentScrollbar={isAndroid}
|
|
202
254
|
>
|
|
203
255
|
{headerNode}
|
|
204
|
-
{
|
|
256
|
+
{contentNode}
|
|
205
257
|
</BottomSheetScrollView>
|
|
206
258
|
) : (
|
|
207
259
|
<BottomSheetView style={[styles.content, contentStyle, style]}>
|
|
208
260
|
{headerNode}
|
|
209
|
-
{
|
|
261
|
+
{contentNode}
|
|
210
262
|
</BottomSheetView>
|
|
211
263
|
)}
|
|
212
264
|
</BottomSheetModal>
|
|
213
265
|
)
|
|
214
266
|
}
|
|
215
267
|
|
|
268
|
+
Sheet.Header = SheetHeader
|
|
269
|
+
Sheet.Content = SheetContent
|
|
270
|
+
Sheet.Footer = SheetFooter
|
|
271
|
+
|
|
216
272
|
const styles = StyleSheet.create({
|
|
217
273
|
background: {
|
|
218
274
|
borderTopLeftRadius: ms(16),
|
|
@@ -235,12 +291,12 @@ const styles = StyleSheet.create({
|
|
|
235
291
|
justifyContent: 'space-between',
|
|
236
292
|
},
|
|
237
293
|
title: {
|
|
238
|
-
fontFamily: '
|
|
294
|
+
fontFamily: 'Sohne-SemiBold',
|
|
239
295
|
fontSize: ms(18),
|
|
240
296
|
flex: 1,
|
|
241
297
|
},
|
|
242
298
|
subtitle: {
|
|
243
|
-
fontFamily: '
|
|
299
|
+
fontFamily: 'Sohne-Regular',
|
|
244
300
|
fontSize: ms(14),
|
|
245
301
|
lineHeight: mvs(20),
|
|
246
302
|
},
|
|
@@ -257,4 +313,15 @@ const styles = StyleSheet.create({
|
|
|
257
313
|
paddingBottom: vs(32),
|
|
258
314
|
paddingRight: s(16),
|
|
259
315
|
},
|
|
316
|
+
sheetContent: {
|
|
317
|
+
gap: vs(16),
|
|
318
|
+
},
|
|
319
|
+
sheetFooter: {
|
|
320
|
+
paddingHorizontal: s(16),
|
|
321
|
+
paddingVertical: vs(16),
|
|
322
|
+
borderTopWidth: 1,
|
|
323
|
+
flexDirection: 'row',
|
|
324
|
+
gap: s(12),
|
|
325
|
+
},
|
|
260
326
|
})
|
|
327
|
+
|
|
@@ -1,8 +1,16 @@
|
|
|
1
|
-
import React, { useEffect,
|
|
2
|
-
import {
|
|
1
|
+
import React, { useEffect, useState } from 'react'
|
|
2
|
+
import { StyleSheet, View, ViewStyle } from 'react-native'
|
|
3
|
+
import Animated, {
|
|
4
|
+
useSharedValue,
|
|
5
|
+
useAnimatedStyle,
|
|
6
|
+
withRepeat,
|
|
7
|
+
withTiming,
|
|
8
|
+
Easing,
|
|
9
|
+
} from 'react-native-reanimated'
|
|
3
10
|
import { LinearGradient } from 'expo-linear-gradient'
|
|
4
11
|
import { useTheme } from '../../theme'
|
|
5
12
|
import { s } from '../../utils/scaling'
|
|
13
|
+
import { TIMINGS } from '../../utils/animations'
|
|
6
14
|
|
|
7
15
|
// circle: circular avatar placeholder text: short line preset base: custom dimensions
|
|
8
16
|
export type SkeletonPreset = 'base' | 'circle' | 'text'
|
|
@@ -27,24 +35,24 @@ export function Skeleton({
|
|
|
27
35
|
style,
|
|
28
36
|
}: SkeletonProps) {
|
|
29
37
|
const { colors, colorScheme } = useTheme()
|
|
30
|
-
const
|
|
38
|
+
const shimmer = useSharedValue(0)
|
|
31
39
|
const [containerWidth, setContainerWidth] = useState(300)
|
|
32
40
|
|
|
33
41
|
const shimmerHighlight =
|
|
34
42
|
colorScheme === 'dark' ? 'rgba(255,255,255,0.08)' : 'rgba(255,255,255,0.7)'
|
|
35
43
|
|
|
36
44
|
useEffect(() => {
|
|
37
|
-
|
|
38
|
-
|
|
45
|
+
// Repeats indefinitely on the UI thread — zero JS bridge cost per frame.
|
|
46
|
+
shimmer.value = withRepeat(
|
|
47
|
+
withTiming(1, { duration: TIMINGS.shimmer.duration, easing: Easing.linear }),
|
|
48
|
+
-1,
|
|
49
|
+
false,
|
|
39
50
|
)
|
|
40
|
-
|
|
41
|
-
return () => animation.stop()
|
|
42
|
-
}, [shimmerAnim])
|
|
51
|
+
}, [shimmer])
|
|
43
52
|
|
|
44
|
-
const
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
})
|
|
53
|
+
const shimmerStyle = useAnimatedStyle(() => ({
|
|
54
|
+
transform: [{ translateX: -containerWidth + shimmer.value * (containerWidth * 2) }],
|
|
55
|
+
}))
|
|
48
56
|
|
|
49
57
|
// Resolve dimensions by preset
|
|
50
58
|
const resolvedWidth: number | string =
|
|
@@ -66,12 +74,15 @@ export function Skeleton({
|
|
|
66
74
|
<View
|
|
67
75
|
style={[
|
|
68
76
|
styles.base,
|
|
69
|
-
{ width: resolvedWidth as
|
|
77
|
+
{ width: resolvedWidth as number | `${number}%`, height: resolvedHeight, borderRadius: resolvedRadius, backgroundColor: colors.surface },
|
|
70
78
|
style,
|
|
71
79
|
]}
|
|
72
80
|
onLayout={(e) => setContainerWidth(e.nativeEvent.layout.width)}
|
|
81
|
+
accessibilityRole="progressbar"
|
|
82
|
+
accessibilityLabel="Loading"
|
|
83
|
+
accessibilityState={{ busy: true }}
|
|
73
84
|
>
|
|
74
|
-
<Animated.View style={[StyleSheet.absoluteFill,
|
|
85
|
+
<Animated.View style={[StyleSheet.absoluteFill, shimmerStyle]}>
|
|
75
86
|
<LinearGradient
|
|
76
87
|
colors={['transparent', shimmerHighlight, 'transparent']}
|
|
77
88
|
start={{ x: 0, y: 0 }}
|
|
@@ -46,7 +46,17 @@ export function Slider({
|
|
|
46
46
|
}
|
|
47
47
|
|
|
48
48
|
return (
|
|
49
|
-
<View
|
|
49
|
+
<View
|
|
50
|
+
style={[styles.wrapper, style]}
|
|
51
|
+
accessibilityRole="adjustable"
|
|
52
|
+
accessibilityLabel={accessibilityLabel ?? label}
|
|
53
|
+
accessibilityValue={{
|
|
54
|
+
min: minimumValue,
|
|
55
|
+
max: maximumValue,
|
|
56
|
+
now: value,
|
|
57
|
+
text: formatValue(value),
|
|
58
|
+
}}
|
|
59
|
+
>
|
|
50
60
|
{label || showValue ? (
|
|
51
61
|
<View style={styles.header}>
|
|
52
62
|
{label ? (
|
|
@@ -91,11 +101,11 @@ const styles = StyleSheet.create({
|
|
|
91
101
|
alignItems: 'center',
|
|
92
102
|
},
|
|
93
103
|
label: {
|
|
94
|
-
fontFamily: '
|
|
104
|
+
fontFamily: 'Sohne-Medium',
|
|
95
105
|
fontSize: ms(15),
|
|
96
106
|
},
|
|
97
107
|
valueText: {
|
|
98
|
-
fontFamily: '
|
|
108
|
+
fontFamily: 'Sohne-Medium',
|
|
99
109
|
fontSize: ms(14),
|
|
100
110
|
},
|
|
101
111
|
slider: {
|