@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,12 +1,51 @@
|
|
|
1
|
-
import React
|
|
2
|
-
import { TouchableOpacity,
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import { TouchableOpacity, StyleSheet, TouchableOpacityProps, ViewStyle, View } from 'react-native'
|
|
3
|
+
import Animated, {
|
|
4
|
+
useAnimatedStyle,
|
|
5
|
+
interpolateColor,
|
|
6
|
+
} from 'react-native-reanimated'
|
|
5
7
|
import { FontAwesome5 } from '@expo/vector-icons'
|
|
6
8
|
import { selectionAsync as hapticSelection } from '../../utils/haptics'
|
|
7
9
|
import { useTheme } from '../../theme'
|
|
8
10
|
import { s, vs, ms } from '../../utils/scaling'
|
|
9
11
|
import { renderIcon } from '../../utils/icons'
|
|
12
|
+
import { usePressScale } from '../../utils/usePressScale'
|
|
13
|
+
import { useColorTransition } from '../../utils/useColorTransition'
|
|
14
|
+
import { PRESS_SCALE } from '../../utils/animations'
|
|
15
|
+
|
|
16
|
+
interface ToggleIconProps {
|
|
17
|
+
pressed: boolean
|
|
18
|
+
iconName?: string
|
|
19
|
+
activeIconName?: string
|
|
20
|
+
icon?: React.ReactNode | ((pressed: boolean) => React.ReactNode)
|
|
21
|
+
activeIcon?: React.ReactNode | ((pressed: boolean) => React.ReactNode)
|
|
22
|
+
iconColor?: string
|
|
23
|
+
activeIconColor?: string
|
|
24
|
+
iconSize: number
|
|
25
|
+
primaryColor: string
|
|
26
|
+
mutedColor: string
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function ToggleIcon({ pressed, iconName, activeIconName, icon, activeIcon, iconColor, activeIconColor, iconSize, primaryColor, mutedColor }: ToggleIconProps) {
|
|
30
|
+
const renderProp = (prop?: React.ReactNode | ((p: boolean) => React.ReactNode)) => {
|
|
31
|
+
if (!prop) return null
|
|
32
|
+
if (typeof prop === 'function') return (prop as (p: boolean) => React.ReactNode)(pressed)
|
|
33
|
+
return prop
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if (pressed) {
|
|
37
|
+
if (activeIconName) return <>{renderIcon(activeIconName, iconSize, activeIconColor ?? primaryColor)}</>
|
|
38
|
+
const active = renderProp(activeIcon)
|
|
39
|
+
if (active) return <>{active}</>
|
|
40
|
+
return <FontAwesome5 name="check-circle" size={iconSize} color={primaryColor} />
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
if (iconName) return <>{renderIcon(iconName, iconSize, iconColor ?? mutedColor)}</>
|
|
44
|
+
const custom = renderProp(icon)
|
|
45
|
+
if (custom) return <>{custom}</>
|
|
46
|
+
|
|
47
|
+
return <FontAwesome5 name="circle" size={iconSize} color={mutedColor} />
|
|
48
|
+
}
|
|
10
49
|
|
|
11
50
|
export type ToggleVariant = 'default' | 'outline'
|
|
12
51
|
export type ToggleSize = 'sm' | 'md' | 'lg'
|
|
@@ -59,96 +98,75 @@ export function Toggle({
|
|
|
59
98
|
activeIconColor,
|
|
60
99
|
disabled,
|
|
61
100
|
style,
|
|
101
|
+
accessibilityLabel,
|
|
62
102
|
...props
|
|
63
103
|
}: ToggleProps) {
|
|
64
104
|
const { colors } = useTheme()
|
|
65
|
-
const
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
useEffect(() => {
|
|
70
|
-
Animated.timing(pressAnim, {
|
|
71
|
-
toValue: pressed ? 1 : 0,
|
|
72
|
-
duration: 150,
|
|
73
|
-
easing: Easing.out(Easing.ease),
|
|
74
|
-
useNativeDriver: false,
|
|
75
|
-
}).start()
|
|
76
|
-
}, [pressed, pressAnim])
|
|
77
|
-
|
|
78
|
-
const handlePressIn = () => {
|
|
79
|
-
if (disabled) return
|
|
80
|
-
Animated.spring(scale, { toValue: 0.95, useNativeDriver: nativeDriver, stiffness: 600, damping: 35, mass: 0.8 }).start()
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
const handlePressOut = () => {
|
|
84
|
-
Animated.spring(scale, { toValue: 1, useNativeDriver: nativeDriver, stiffness: 280, damping: 22, mass: 0.8 }).start()
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
// Keep borderWidth constant at 2 to prevent layout jumps when pressing.
|
|
88
|
-
// Animate borderColor and backgroundColor instead.
|
|
89
|
-
const borderColor = pressAnim.interpolate({
|
|
90
|
-
inputRange: [0, 1],
|
|
91
|
-
outputRange: [variant === 'outline' ? colors.border : 'transparent', colors.primary],
|
|
105
|
+
const { animatedStyle: scaleStyle, onPressIn, onPressOut, hoverHandlers } = usePressScale({
|
|
106
|
+
pressScale: PRESS_SCALE.button,
|
|
107
|
+
disabled,
|
|
92
108
|
})
|
|
109
|
+
const progress = useColorTransition(pressed)
|
|
93
110
|
|
|
94
|
-
const
|
|
95
|
-
inputRange: [0, 1],
|
|
96
|
-
outputRange: ['transparent', colors.surfaceStrong],
|
|
97
|
-
})
|
|
111
|
+
const inactiveBorder = variant === 'outline' ? colors.border : 'transparent'
|
|
98
112
|
|
|
99
|
-
const
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
})
|
|
113
|
+
const surfaceStyle = useAnimatedStyle(() => ({
|
|
114
|
+
borderColor: interpolateColor(progress.value, [0, 1], [inactiveBorder, colors.primary]),
|
|
115
|
+
backgroundColor: interpolateColor(progress.value, [0, 1], ['transparent', colors.surfaceStrong]),
|
|
116
|
+
}))
|
|
103
117
|
|
|
104
|
-
const
|
|
118
|
+
const textStyle = useAnimatedStyle(() => ({
|
|
119
|
+
color: interpolateColor(progress.value, [0, 1], [colors.foreground, colors.primary]),
|
|
120
|
+
}))
|
|
105
121
|
|
|
106
|
-
const
|
|
107
|
-
const renderProp = (prop?: any) => {
|
|
108
|
-
if (!prop) return null
|
|
109
|
-
if (typeof prop === 'function') return prop(pressed)
|
|
110
|
-
return prop
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
if (pressed) {
|
|
114
|
-
if (activeIconName) return <>{renderIcon(activeIconName, iconSize, activeIconColor ?? colors.primary)}</>
|
|
115
|
-
const active = renderProp(activeIcon)
|
|
116
|
-
if (active) return <>{active}</>
|
|
117
|
-
return <FontAwesome5 name="check-circle" size={iconSize} color={colors.primary} />
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
if (iconName) return <>{renderIcon(iconName, iconSize, iconColor ?? colors.foregroundMuted)}</>
|
|
121
|
-
const custom = renderProp(icon)
|
|
122
|
-
if (custom) return <>{custom}</>
|
|
123
|
-
|
|
124
|
-
// Default: empty circle to signal an action is available
|
|
125
|
-
return <FontAwesome5 name="circle" size={iconSize} color={colors.foregroundMuted} />
|
|
126
|
-
}
|
|
122
|
+
const iconSize = iconSizeMap[size]
|
|
127
123
|
|
|
128
124
|
return (
|
|
129
|
-
<Animated.View
|
|
125
|
+
<Animated.View
|
|
126
|
+
style={[scaleStyle, disabled && styles.disabled, style]}
|
|
127
|
+
{...hoverHandlers}
|
|
128
|
+
>
|
|
130
129
|
<TouchableOpacity
|
|
131
130
|
onPress={() => {
|
|
132
131
|
hapticSelection()
|
|
133
132
|
onPressedChange?.(!pressed)
|
|
134
133
|
}}
|
|
135
|
-
onPressIn={
|
|
136
|
-
onPressOut={
|
|
134
|
+
onPressIn={onPressIn}
|
|
135
|
+
onPressOut={onPressOut}
|
|
137
136
|
disabled={disabled}
|
|
138
137
|
activeOpacity={1}
|
|
139
138
|
touchSoundDisabled={true}
|
|
139
|
+
accessibilityRole="button"
|
|
140
|
+
accessibilityLabel={accessibilityLabel ?? label}
|
|
141
|
+
accessibilityState={{ selected: pressed, disabled: !!disabled }}
|
|
140
142
|
{...props}
|
|
141
143
|
>
|
|
142
144
|
<Animated.View
|
|
143
145
|
style={[
|
|
144
146
|
styles.base,
|
|
145
147
|
sizeStyles[size],
|
|
146
|
-
{
|
|
148
|
+
{ borderWidth: 2 },
|
|
149
|
+
surfaceStyle,
|
|
147
150
|
]}
|
|
148
151
|
>
|
|
149
152
|
<View style={styles.inner}>
|
|
150
|
-
<
|
|
151
|
-
|
|
153
|
+
<ToggleIcon
|
|
154
|
+
pressed={pressed}
|
|
155
|
+
iconName={iconName}
|
|
156
|
+
activeIconName={activeIconName}
|
|
157
|
+
icon={icon}
|
|
158
|
+
activeIcon={activeIcon}
|
|
159
|
+
iconColor={iconColor}
|
|
160
|
+
activeIconColor={activeIconColor}
|
|
161
|
+
iconSize={iconSize}
|
|
162
|
+
primaryColor={colors.primary}
|
|
163
|
+
mutedColor={colors.foregroundMuted}
|
|
164
|
+
/>
|
|
165
|
+
{label ? (
|
|
166
|
+
<Animated.Text style={[styles.label, textStyle]} allowFontScaling={true}>
|
|
167
|
+
{label}
|
|
168
|
+
</Animated.Text>
|
|
169
|
+
) : null}
|
|
152
170
|
</View>
|
|
153
171
|
</Animated.View>
|
|
154
172
|
</TouchableOpacity>
|
|
@@ -170,7 +188,7 @@ const styles = StyleSheet.create({
|
|
|
170
188
|
opacity: 0.45,
|
|
171
189
|
},
|
|
172
190
|
label: {
|
|
173
|
-
fontFamily: '
|
|
191
|
+
fontFamily: 'Sohne-Medium',
|
|
174
192
|
fontSize: ms(14),
|
|
175
193
|
},
|
|
176
194
|
})
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import React, { useCallback } from 'react'
|
|
2
|
+
import { FlatList, FlatListProps, ListRenderItem } from 'react-native'
|
|
3
|
+
|
|
4
|
+
export interface VirtualListItem {
|
|
5
|
+
id?: string | number
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export interface VirtualListProps<T> extends Omit<FlatListProps<T>, 'getItemLayout'> {
|
|
9
|
+
/**
|
|
10
|
+
* Fixed row height in px. When provided, enables `getItemLayout` so the list
|
|
11
|
+
* skips async measurement — large datasets scroll and `scrollToIndex` jump
|
|
12
|
+
* without layout passes. Omit only for variable-height rows.
|
|
13
|
+
*/
|
|
14
|
+
itemHeight?: number
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const defaultKeyExtractor = <T,>(item: T, index: number): string => {
|
|
18
|
+
const id = (item as VirtualListItem | null)?.id
|
|
19
|
+
return id !== undefined ? String(id) : String(index)
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Virtualized list primitive. Thin wrapper over `FlatList` with sane defaults
|
|
24
|
+
* for the common case: stable keys + optional fixed-height fast path.
|
|
25
|
+
*
|
|
26
|
+
* For 10k+ rows, pass `itemHeight` and a `React.memo`-wrapped `renderItem` so
|
|
27
|
+
* only on-screen rows mount and re-render.
|
|
28
|
+
*
|
|
29
|
+
* @example
|
|
30
|
+
* const renderItem = useCallback(({ item }) => <ListItem title={item.title} onPress={...} />, [])
|
|
31
|
+
* <VirtualList data={rows} renderItem={renderItem} itemHeight={64} />
|
|
32
|
+
*/
|
|
33
|
+
function VirtualListInner<T>(
|
|
34
|
+
{ itemHeight, keyExtractor, renderItem, ...props }: VirtualListProps<T>,
|
|
35
|
+
ref: React.Ref<FlatList<T>>,
|
|
36
|
+
) {
|
|
37
|
+
const getItemLayout = useCallback(
|
|
38
|
+
(_data: ArrayLike<T> | null | undefined, index: number) => ({
|
|
39
|
+
length: itemHeight ?? 0,
|
|
40
|
+
offset: (itemHeight ?? 0) * index,
|
|
41
|
+
index,
|
|
42
|
+
}),
|
|
43
|
+
[itemHeight],
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
return (
|
|
47
|
+
<FlatList<T>
|
|
48
|
+
ref={ref}
|
|
49
|
+
keyExtractor={keyExtractor ?? defaultKeyExtractor}
|
|
50
|
+
renderItem={renderItem as ListRenderItem<T>}
|
|
51
|
+
getItemLayout={itemHeight !== undefined ? getItemLayout : undefined}
|
|
52
|
+
removeClippedSubviews
|
|
53
|
+
{...props}
|
|
54
|
+
/>
|
|
55
|
+
)
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export const VirtualList = React.forwardRef(VirtualListInner) as <T>(
|
|
59
|
+
props: VirtualListProps<T> & { ref?: React.Ref<FlatList<T>> },
|
|
60
|
+
) => React.ReactElement
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './VirtualList'
|
package/src/fonts.ts
CHANGED
|
@@ -1,30 +1,48 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
2
|
+
* Sohne font family required by @retray-dev/ui-kit components.
|
|
3
|
+
*
|
|
4
4
|
* Consumer apps must load these fonts at app root using expo-font:
|
|
5
|
-
*
|
|
5
|
+
*
|
|
6
6
|
* @example
|
|
7
7
|
* import { useFonts } from 'expo-font'
|
|
8
|
-
* import {
|
|
9
|
-
*
|
|
8
|
+
* import { SohneFonts } from '@retray-dev/ui-kit/fonts'
|
|
9
|
+
*
|
|
10
10
|
* function App() {
|
|
11
|
-
* const [fontsLoaded] = useFonts(
|
|
11
|
+
* const [fontsLoaded] = useFonts(SohneFonts)
|
|
12
12
|
* if (!fontsLoaded) return null
|
|
13
13
|
* // render app
|
|
14
14
|
* }
|
|
15
15
|
*/
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
'
|
|
20
|
-
'
|
|
21
|
-
'
|
|
22
|
-
'
|
|
23
|
-
'
|
|
24
|
-
'
|
|
25
|
-
'
|
|
26
|
-
'
|
|
27
|
-
'
|
|
28
|
-
'
|
|
29
|
-
'
|
|
16
|
+
/* eslint-disable @typescript-eslint/no-require-imports */
|
|
17
|
+
export const SohneFonts = {
|
|
18
|
+
// Sohne base
|
|
19
|
+
'Sohne-ExtraLight': require('./assets/fonts/Sohne-ExtraLight.otf'),
|
|
20
|
+
'Sohne-ExtraLightItalic': require('./assets/fonts/Sohne-ExtraLightItalic.otf'),
|
|
21
|
+
'Sohne-Light': require('./assets/fonts/Sohne-Light.otf'),
|
|
22
|
+
'Sohne-LightItalic': require('./assets/fonts/Sohne-LightItalic.otf'),
|
|
23
|
+
'Sohne-Regular': require('./assets/fonts/Sohne-Regular.otf'),
|
|
24
|
+
'Sohne-Italic': require('./assets/fonts/Sohne-Italic.otf'),
|
|
25
|
+
'Sohne-Medium': require('./assets/fonts/Sohne-Medium.otf'),
|
|
26
|
+
'Sohne-MediumItalic': require('./assets/fonts/Sohne-MediumItalic.otf'),
|
|
27
|
+
'Sohne-SemiBold': require('./assets/fonts/Sohne-SemiBold.otf'),
|
|
28
|
+
'Sohne-SemiBoldItalic': require('./assets/fonts/Sohne-SemiBoldItalic.otf'),
|
|
29
|
+
'Sohne-Bold': require('./assets/fonts/Sohne-Bold.otf'),
|
|
30
|
+
'Sohne-BoldItalic': require('./assets/fonts/Sohne-BoldItalic.otf'),
|
|
31
|
+
'Sohne-ExtraBold': require('./assets/fonts/Sohne-ExtraBold.otf'),
|
|
32
|
+
'Sohne-ExtraBoldItalic': require('./assets/fonts/Sohne-ExtraBoldItalic.otf'),
|
|
33
|
+
// SohneMono
|
|
34
|
+
'SohneMono-ExtraLight': require('./assets/fonts/SohneMono-ExtraLight.otf'),
|
|
35
|
+
'SohneMono-ExtraLightItalic': require('./assets/fonts/SohneMono-ExtraLightItalic.otf'),
|
|
36
|
+
'SohneMono-Light': require('./assets/fonts/SohneMono-Light.otf'),
|
|
37
|
+
'SohneMono-LightItalic': require('./assets/fonts/SohneMono-LightItalic.otf'),
|
|
38
|
+
'SohneMono-Regular': require('./assets/fonts/SohneMono-Regular.otf'),
|
|
39
|
+
'SohneMono-Italic': require('./assets/fonts/SohneMono-Italic.otf'),
|
|
40
|
+
'SohneMono-Medium': require('./assets/fonts/SohneMono-Medium.otf'),
|
|
41
|
+
'SohneMono-MediumItalic': require('./assets/fonts/SohneMono-MediumItalic.otf'),
|
|
42
|
+
'SohneMono-SemiBold': require('./assets/fonts/SohneMono-SemiBold.otf'),
|
|
43
|
+
'SohneMono-SemiBoldItalic': require('./assets/fonts/SohneMono-SemiBoldItalic.otf'),
|
|
44
|
+
'SohneMono-Bold': require('./assets/fonts/SohneMono-Bold.otf'),
|
|
45
|
+
'SohneMono-BoldItalic': require('./assets/fonts/SohneMono-BoldItalic.otf'),
|
|
46
|
+
'SohneMono-ExtraBold': require('./assets/fonts/SohneMono-ExtraBold.otf'),
|
|
47
|
+
'SohneMono-ExtraBoldItalic': require('./assets/fonts/SohneMono-ExtraBoldItalic.otf'),
|
|
30
48
|
} as const
|
package/src/index.ts
CHANGED
|
@@ -34,7 +34,9 @@ export * from './components/CurrencyDisplay'
|
|
|
34
34
|
// CurrencyInputLarge is deprecated — use <CurrencyInput size="large" /> instead
|
|
35
35
|
export { CurrencyInput as CurrencyInputLarge } from './components/CurrencyInput'
|
|
36
36
|
export * from './components/ListItem'
|
|
37
|
+
export * from './components/ListGroup'
|
|
37
38
|
export * from './components/MenuItem'
|
|
39
|
+
export * from './components/MenuGroup'
|
|
38
40
|
export * from './components/Chip'
|
|
39
41
|
export * from './components/ConfirmDialog'
|
|
40
42
|
export * from './components/LabelValue'
|
|
@@ -43,9 +45,11 @@ export * from './components/MediaCard'
|
|
|
43
45
|
export * from './components/CategoryStrip'
|
|
44
46
|
export * from './components/Pressable'
|
|
45
47
|
export * from './components/DetailRow'
|
|
48
|
+
export * from './components/Form'
|
|
49
|
+
export * from './components/VirtualList'
|
|
46
50
|
|
|
47
51
|
// Icon utility
|
|
48
|
-
export { Icon, renderIcon } from './utils/icons'
|
|
52
|
+
export { Icon, renderIcon, configureIconFamilies } from './utils/icons'
|
|
49
53
|
|
|
50
54
|
// Typography utilities
|
|
51
55
|
export { getResponsiveFontSize } from './utils/typography'
|
package/src/theme/colors.ts
CHANGED
|
@@ -1,61 +1,74 @@
|
|
|
1
1
|
import { ThemeColors, ResolvedColors } from './types'
|
|
2
|
-
import { mixWithBackground, withAlphaOnWhite, withAlphaOnDark, lighten, darken
|
|
2
|
+
import { mixWithBackground, withAlphaOnWhite, withAlphaOnDark, lighten, darken } from './colorUtils'
|
|
3
3
|
|
|
4
|
-
// ─── Default palettes
|
|
5
|
-
//
|
|
6
|
-
//
|
|
4
|
+
// ─── Default palettes ─────────────────────────────────────────────────────────
|
|
5
|
+
// AUDIT FIXES applied:
|
|
6
|
+
// · accent + accentForeground added — system was fully achromatic
|
|
7
|
+
// · warning darkened: #e67e00 → #9a5200 (white text: 2.86:1 → 5.86:1 ✓ AA)
|
|
8
|
+
// · destructive darkened: #e53935 → #c72828 (white text: 4.22:1 → 5.59:1 ✓ AA)
|
|
9
|
+
// · foreground tweaked #222222 → #1a1a1a (matches primary, 16.1:1 on white ✓)
|
|
7
10
|
|
|
8
11
|
export const defaultLight: ThemeColors = {
|
|
9
|
-
background:
|
|
10
|
-
foreground:
|
|
11
|
-
card:
|
|
12
|
-
primary:
|
|
13
|
-
primaryForeground:
|
|
14
|
-
|
|
15
|
-
|
|
12
|
+
background: '#ffffff',
|
|
13
|
+
foreground: '#1a1a1a',
|
|
14
|
+
card: '#ffffff',
|
|
15
|
+
primary: '#1a1a1a',
|
|
16
|
+
primaryForeground: '#ffffff',
|
|
17
|
+
// AUDIT FIX: brand accent — was undefined; falls back to primary when omitted
|
|
18
|
+
accent: '#d4561d',
|
|
19
|
+
accentForeground: '#ffffff',
|
|
20
|
+
border: '#dddddd',
|
|
21
|
+
// AUDIT FIX: was #e53935 (4.22:1 on white — fails AA); #c72828 = 5.59:1 ✓
|
|
22
|
+
destructive: '#c72828',
|
|
16
23
|
destructiveForeground: '#ffffff',
|
|
17
|
-
success:
|
|
18
|
-
successForeground:
|
|
19
|
-
|
|
20
|
-
|
|
24
|
+
success: '#1a7a45',
|
|
25
|
+
successForeground: '#ffffff',
|
|
26
|
+
// AUDIT FIX: was #e67e00 (2.86:1 — severe fail); #9a5200 = 5.86:1 ✓ AAA-near
|
|
27
|
+
warning: '#9a5200',
|
|
28
|
+
warningForeground: '#ffffff',
|
|
21
29
|
}
|
|
22
30
|
|
|
23
31
|
export const defaultDark: ThemeColors = {
|
|
24
|
-
background:
|
|
25
|
-
foreground:
|
|
26
|
-
card:
|
|
27
|
-
primary:
|
|
28
|
-
primaryForeground:
|
|
29
|
-
|
|
30
|
-
|
|
32
|
+
background: '#0f0f0f',
|
|
33
|
+
foreground: '#fafafa',
|
|
34
|
+
card: '#1c1c1c',
|
|
35
|
+
primary: '#fafafa',
|
|
36
|
+
primaryForeground: '#0f0f0f',
|
|
37
|
+
// AUDIT FIX: lighter accent for dark surfaces (warm amber-orange)
|
|
38
|
+
accent: '#e87645',
|
|
39
|
+
accentForeground: '#ffffff',
|
|
40
|
+
border: '#303030',
|
|
41
|
+
destructive: '#ef5350',
|
|
31
42
|
destructiveForeground: '#ffffff',
|
|
32
|
-
success:
|
|
33
|
-
successForeground:
|
|
34
|
-
|
|
35
|
-
|
|
43
|
+
success: '#2e7d52',
|
|
44
|
+
successForeground: '#ffffff',
|
|
45
|
+
// AUDIT FIX: brighter amber for dark-bg visibility; dark text for contrast
|
|
46
|
+
// #f5a623 on #0f0f0f = 8.6:1 ✓ as indicator; #0f0f0f text on #f5a623 = 8.6:1 ✓
|
|
47
|
+
warning: '#f5a623',
|
|
48
|
+
warningForeground: '#0f0f0f',
|
|
36
49
|
}
|
|
37
50
|
|
|
38
51
|
// ─── Color derivation ─────────────────────────────────────────────────────────
|
|
39
|
-
// Takes 12 public tokens → produces full ResolvedColors for component consumption.
|
|
40
|
-
// Dark mode uses bg-blended tints instead of white-blended to stay on-palette.
|
|
41
|
-
|
|
42
52
|
export function deriveColors(t: ThemeColors, scheme: 'light' | 'dark'): ResolvedColors {
|
|
43
53
|
const dark = scheme === 'dark'
|
|
44
54
|
const bg = t.background
|
|
45
55
|
|
|
46
|
-
// Text hierarchy
|
|
47
|
-
|
|
48
|
-
|
|
56
|
+
// AUDIT FIX: Text hierarchy opacities raised to pass WCAG AA.
|
|
57
|
+
// foregroundSubtle was 0.55 → ~#858585 (3.5:1 fail on white)
|
|
58
|
+
// foregroundMuted was 0.38 → ~#ababab (2.2:1 critical fail on white)
|
|
59
|
+
// New values on light (#1a1a1a on #ffffff):
|
|
60
|
+
// foregroundSubtle 0.70 → ~#646464 (5.9:1 ✓ AA)
|
|
61
|
+
// foregroundMuted 0.62 → ~#767676 (4.5:1 ✓ AA minimum)
|
|
62
|
+
const foregroundSubtle = mixWithBackground(t.foreground, bg, 0.70)
|
|
63
|
+
const foregroundMuted = mixWithBackground(t.foreground, bg, 0.62)
|
|
49
64
|
|
|
50
|
-
// Surface fills: slight offset from background
|
|
51
65
|
const surface = dark
|
|
52
|
-
? lighten(bg, -0.06)
|
|
53
|
-
: darken(bg, 0.04)
|
|
66
|
+
? lighten(bg, -0.06)
|
|
67
|
+
: darken(bg, 0.04)
|
|
54
68
|
const surfaceStrong = dark
|
|
55
69
|
? lighten(bg, -0.12)
|
|
56
|
-
: darken(bg, 0.08)
|
|
70
|
+
: darken(bg, 0.08)
|
|
57
71
|
|
|
58
|
-
// Semantic tints: color blended toward background
|
|
59
72
|
const destructiveTint = dark
|
|
60
73
|
? withAlphaOnDark(t.destructive, 0.15, bg)
|
|
61
74
|
: withAlphaOnWhite(t.destructive, 0.08)
|
|
@@ -92,7 +105,8 @@ export function deriveColors(t: ThemeColors, scheme: 'light' | 'dark'): Resolved
|
|
|
92
105
|
overlay: t.overlay ?? 'rgba(0,0,0,0.45)',
|
|
93
106
|
accentResolved: t.accent ?? t.primary,
|
|
94
107
|
accentForegroundResolved: t.accentForeground ?? t.primaryForeground,
|
|
95
|
-
ring: t.
|
|
96
|
-
input: t.border,
|
|
108
|
+
ring: t.accent ?? t.primary,
|
|
109
|
+
input: t.border,
|
|
110
|
+
separator: dark ? lighten(t.border, 0.22) : darken(t.border, 0.16),
|
|
97
111
|
}
|
|
98
112
|
}
|
package/src/theme/types.ts
CHANGED
|
@@ -49,6 +49,9 @@ export type ResolvedColors = ThemeColors & {
|
|
|
49
49
|
// Aliases (ring + input always equal primary + border for coherence)
|
|
50
50
|
ring: string // = primary
|
|
51
51
|
input: string // = border
|
|
52
|
+
|
|
53
|
+
// Divider/separator line — deliberately darker than border for visibility
|
|
54
|
+
separator: string
|
|
52
55
|
}
|
|
53
56
|
|
|
54
57
|
export type Theme = {
|