@retray-dev/ui-kit 6.2.0 → 9.0.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 +997 -20
- package/EXAMPLES.md +250 -2
- package/README.md +21 -14
- package/dist/Accordion.d.mts +28 -0
- package/dist/Accordion.d.ts +28 -0
- package/dist/Accordion.js +392 -0
- package/dist/Accordion.mjs +7 -0
- package/dist/AlertBanner.d.mts +16 -0
- package/dist/AlertBanner.d.ts +16 -0
- package/dist/AlertBanner.js +250 -0
- package/dist/AlertBanner.mjs +6 -0
- 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.d.mts +20 -0
- package/dist/Avatar.d.ts +20 -0
- package/dist/Avatar.js +244 -0
- package/dist/Avatar.mjs +4 -0
- package/dist/Badge.d.mts +26 -0
- package/dist/Badge.d.ts +26 -0
- package/dist/Badge.js +257 -0
- package/dist/Badge.mjs +5 -0
- package/dist/Button.d.mts +30 -0
- package/dist/Button.d.ts +30 -0
- package/dist/Button.js +432 -0
- package/dist/Button.mjs +9 -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 +3 -0
- package/dist/Card.d.mts +39 -0
- package/dist/Card.d.ts +39 -0
- package/dist/Card.js +349 -0
- package/dist/Card.mjs +8 -0
- package/dist/CategoryStrip.d.mts +26 -0
- package/dist/CategoryStrip.d.ts +26 -0
- package/dist/CategoryStrip.js +453 -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 +336 -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 +403 -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 +560 -0
- package/dist/ConfirmDialog.mjs +10 -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 +4 -0
- package/dist/CurrencyInput.d.mts +26 -0
- package/dist/CurrencyInput.d.ts +26 -0
- package/dist/CurrencyInput.js +408 -0
- package/dist/CurrencyInput.mjs +8 -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 +5 -0
- package/dist/EmptyState.d.mts +27 -0
- package/dist/EmptyState.d.ts +27 -0
- package/dist/EmptyState.js +523 -0
- package/dist/EmptyState.mjs +10 -0
- 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.d.mts +52 -0
- package/dist/Form.d.ts +52 -0
- package/dist/Form.js +204 -0
- package/dist/Form.mjs +4 -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 +27 -0
- package/dist/IconButton.d.ts +27 -0
- package/dist/IconButton.js +400 -0
- package/dist/IconButton.mjs +8 -0
- 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.d.mts +23 -0
- package/dist/Input.d.ts +23 -0
- package/dist/Input.js +351 -0
- package/dist/Input.mjs +7 -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 +5 -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 +5 -0
- package/dist/ListItem.d.mts +64 -0
- package/dist/ListItem.d.ts +64 -0
- package/dist/ListItem.js +444 -0
- package/dist/ListItem.mjs +9 -0
- package/dist/MediaCard.d.mts +39 -0
- package/dist/MediaCard.d.ts +39 -0
- package/dist/MediaCard.js +475 -0
- package/dist/MediaCard.mjs +9 -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 +5 -0
- package/dist/MenuItem.d.mts +48 -0
- package/dist/MenuItem.d.ts +48 -0
- package/dist/MenuItem.js +415 -0
- package/dist/MenuItem.mjs +9 -0
- package/dist/MonthPicker.d.mts +28 -0
- package/dist/MonthPicker.d.ts +28 -0
- package/dist/MonthPicker.js +297 -0
- package/dist/MonthPicker.mjs +5 -0
- 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 +34 -0
- package/dist/Pressable.d.ts +34 -0
- package/dist/Pressable.js +143 -0
- package/dist/Pressable.mjs +5 -0
- 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.d.mts +14 -0
- package/dist/Progress.d.ts +14 -0
- package/dist/Progress.js +191 -0
- package/dist/Progress.mjs +5 -0
- package/dist/RadioGroup.d.mts +19 -0
- package/dist/RadioGroup.d.ts +19 -0
- package/dist/RadioGroup.js +392 -0
- package/dist/RadioGroup.mjs +7 -0
- 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.d.mts +22 -0
- package/dist/Select.d.ts +22 -0
- package/dist/Select.js +488 -0
- package/dist/Select.mjs +7 -0
- 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.d.mts +10 -0
- package/dist/Separator.d.ts +10 -0
- package/dist/Separator.js +156 -0
- package/dist/Separator.mjs +3 -0
- package/dist/Sheet.d.mts +93 -0
- package/dist/Sheet.d.ts +93 -0
- package/dist/Sheet.js +450 -0
- package/dist/Sheet.mjs +6 -0
- package/dist/Skeleton.d.mts +67 -0
- package/dist/Skeleton.d.ts +67 -0
- package/dist/Skeleton.js +266 -0
- package/dist/Skeleton.mjs +6 -0
- package/dist/Slider.d.mts +20 -0
- package/dist/Slider.d.ts +20 -0
- package/dist/Slider.js +279 -0
- package/dist/Slider.mjs +5 -0
- package/dist/Spinner.d.mts +12 -0
- package/dist/Spinner.d.ts +12 -0
- package/dist/Spinner.js +193 -0
- package/dist/Spinner.mjs +4 -0
- package/dist/Switch.d.mts +13 -0
- package/dist/Switch.d.ts +13 -0
- package/dist/Switch.js +311 -0
- package/dist/Switch.mjs +6 -0
- 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.d.mts +27 -0
- package/dist/Tabs.d.ts +27 -0
- package/dist/Tabs.js +419 -0
- package/dist/Tabs.mjs +7 -0
- package/dist/Text.d.mts +12 -0
- package/dist/Text.d.ts +12 -0
- package/dist/Text.js +327 -0
- package/dist/Text.mjs +5 -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 +7 -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 +4 -0
- package/dist/Toggle.d.mts +36 -0
- package/dist/Toggle.d.ts +36 -0
- package/dist/Toggle.js +412 -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 +2 -0
- package/dist/chunk-26BCI223.mjs +14 -0
- package/dist/chunk-2CE3TQVY.mjs +11 -0
- package/dist/chunk-2TFTAWVJ.mjs +131 -0
- package/dist/chunk-2UYENBLV.mjs +49 -0
- package/dist/chunk-3BBOZ3OQ.mjs +41 -0
- package/dist/chunk-3DKJ2GIC.mjs +30 -0
- package/dist/chunk-3U4SSNWP.mjs +120 -0
- package/dist/chunk-4I7D47FH.mjs +139 -0
- package/dist/chunk-4K625MVM.mjs +142 -0
- package/dist/chunk-6OAZJ577.mjs +98 -0
- package/dist/chunk-6Q64UFIA.mjs +71 -0
- package/dist/chunk-756RAKE4.mjs +145 -0
- package/dist/chunk-7QHVVCB3.mjs +115 -0
- package/dist/chunk-A3A6KNQN.mjs +245 -0
- package/dist/chunk-A4MDAP7G.mjs +42 -0
- package/dist/chunk-AJ7ZDNBT.mjs +120 -0
- package/dist/chunk-AV4EMIRH.mjs +94 -0
- package/dist/chunk-AZJF2BLK.mjs +115 -0
- package/dist/chunk-BNP626TY.mjs +159 -0
- package/dist/chunk-BRKYVJVV.mjs +60 -0
- package/dist/chunk-DVK4G2GT.mjs +59 -0
- package/dist/chunk-EH745HE5.mjs +127 -0
- package/dist/chunk-EJ7ZPXOH.mjs +163 -0
- package/dist/chunk-GD6KXMG5.mjs +106 -0
- package/dist/chunk-GQYFLP3D.mjs +187 -0
- package/dist/chunk-ID72TK46.mjs +111 -0
- package/dist/chunk-IRRY3CRZ.mjs +82 -0
- package/dist/chunk-JB67UOB5.mjs +92 -0
- package/dist/chunk-JMOZEC77.mjs +90 -0
- package/dist/chunk-JT7HKXRB.mjs +114 -0
- package/dist/chunk-KIHCWCWL.mjs +124 -0
- package/dist/chunk-LXJIIOYQ.mjs +104 -0
- package/dist/chunk-M6ZXVBTK.mjs +64 -0
- package/dist/chunk-MAC465BB.mjs +61 -0
- package/dist/chunk-MBMXYJJV.mjs +36 -0
- package/dist/chunk-MLF3EZFW.mjs +119 -0
- package/dist/chunk-MX6HRKMI.mjs +29 -0
- package/dist/chunk-NA7PARID.mjs +147 -0
- package/dist/chunk-NC5ZTR2Y.mjs +32 -0
- package/dist/chunk-O3HA6TYM.mjs +139 -0
- package/dist/chunk-OB4JUQ3O.mjs +51 -0
- package/dist/chunk-PFZTM6D5.mjs +238 -0
- package/dist/chunk-QKH5ZOD5.mjs +97 -0
- package/dist/chunk-QY3X2UYR.mjs +191 -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-TERDKCLE.mjs +74 -0
- package/dist/chunk-UREA2GYY.mjs +113 -0
- package/dist/chunk-VGTDN7SW.mjs +164 -0
- package/dist/chunk-VQ57HWPL.mjs +144 -0
- package/dist/chunk-WBOOUHSS.mjs +62 -0
- package/dist/chunk-WJLKJMKR.mjs +78 -0
- package/dist/chunk-X4G6APW6.mjs +134 -0
- package/dist/chunk-Y6FXYEAI.mjs +8 -0
- package/dist/chunk-YFZ3ELX5.mjs +16 -0
- package/dist/chunk-YNROWHQJ.mjs +46 -0
- package/dist/chunk-Z4BVUWW6.mjs +196 -0
- package/dist/chunk-ZJKGQMYH.mjs +131 -0
- package/dist/index-wt-orHUi.d.mts +85 -0
- package/dist/index-wt-orHUi.d.ts +85 -0
- package/dist/index.d.mts +149 -920
- package/dist/index.d.ts +149 -920
- package/dist/index.js +2560 -970
- package/dist/index.mjs +60 -3895
- package/package.json +55 -16
- 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 +15 -4
- package/src/components/AlertBanner/AlertBanner.tsx +38 -12
- package/src/components/AppHeader/AppHeader.tsx +172 -0
- package/src/components/AppHeader/index.ts +1 -0
- package/src/components/Avatar/Avatar.tsx +14 -4
- package/src/components/Badge/Badge.tsx +12 -3
- package/src/components/Button/Button.tsx +30 -38
- package/src/components/ButtonGroup/ButtonGroup.tsx +13 -10
- package/src/components/Card/Card.tsx +29 -57
- package/src/components/CategoryStrip/CategoryStrip.tsx +41 -42
- package/src/components/Checkbox/Checkbox.tsx +36 -45
- package/src/components/Chip/Chip.tsx +41 -48
- package/src/components/ConfirmDialog/ConfirmDialog.tsx +2 -2
- package/src/components/CurrencyDisplay/CurrencyDisplay.tsx +4 -2
- package/src/components/CurrencyInput/CurrencyInput.tsx +12 -10
- package/src/components/DetailRow/DetailRow.tsx +9 -7
- package/src/components/EmptyState/EmptyState.tsx +4 -3
- package/src/components/ErrorBoundary/ErrorBoundary.tsx +153 -0
- package/src/components/ErrorBoundary/index.ts +1 -0
- package/src/components/Form/Form.tsx +149 -0
- package/src/components/Form/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 +23 -29
- package/src/components/ImageViewer/ImageViewer.tsx +290 -0
- package/src/components/ImageViewer/index.ts +1 -0
- package/src/components/Input/Input.tsx +27 -31
- 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 +78 -76
- package/src/components/MediaCard/MediaCard.tsx +15 -7
- package/src/components/MenuGroup/MenuGroup.tsx +145 -0
- package/src/components/MenuGroup/index.ts +1 -0
- package/src/components/MenuItem/MenuItem.tsx +16 -33
- package/src/components/MonthPicker/MonthPicker.tsx +41 -15
- 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 +23 -39
- package/src/components/RetrayProvider/RetrayProvider.tsx +59 -0
- package/src/components/RetrayProvider/index.ts +1 -0
- package/src/components/Select/Select.tsx +6 -6
- package/src/components/SelectableGrid/SelectableGrid.tsx +205 -0
- package/src/components/SelectableGrid/index.ts +1 -0
- package/src/components/Separator/Separator.tsx +1 -3
- package/src/components/Sheet/Sheet.tsx +146 -18
- package/src/components/Skeleton/Skeleton.tsx +143 -2
- package/src/components/Slider/Slider.tsx +2 -2
- package/src/components/Spinner/Spinner.tsx +18 -3
- package/src/components/Switch/Switch.tsx +44 -49
- package/src/components/TabBar/TabBar.tsx +169 -0
- package/src/components/TabBar/index.ts +1 -0
- package/src/components/Tabs/Tabs.tsx +45 -44
- package/src/components/Text/Text.tsx +5 -1
- package/src/components/Textarea/Textarea.tsx +18 -14
- package/src/components/Toast/Toast.tsx +6 -6
- package/src/components/Toggle/Toggle.tsx +80 -72
- package/src/components/VirtualList/VirtualList.tsx +60 -0
- package/src/components/VirtualList/index.ts +1 -0
- package/src/fonts.ts +41 -20
- package/src/index.ts +28 -3
- 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 +29 -1
- package/src/utils/fontGuard.ts +34 -0
- package/src/utils/haptics.ts +211 -9
- package/src/utils/icons.ts +47 -20
- package/src/utils/pressable.ts +66 -0
- package/src/utils/usePressScale.ts +2 -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,24 +1,18 @@
|
|
|
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
|
|
17
13
|
selected?: boolean
|
|
18
14
|
onPress?: () => void
|
|
19
|
-
/** JSX icon rendered before the label. */
|
|
20
15
|
icon?: React.ReactNode
|
|
21
|
-
/** Icon name from @expo/vector-icons resolved automatically. */
|
|
22
16
|
iconName?: string
|
|
23
17
|
style?: ViewStyle
|
|
24
18
|
accessibilityLabel?: string
|
|
@@ -27,9 +21,7 @@ export interface ChipProps {
|
|
|
27
21
|
export interface ChipOption {
|
|
28
22
|
label: string
|
|
29
23
|
value: string | number
|
|
30
|
-
/** Icon name resolved via renderIcon (Feather, AntDesign, etc.). */
|
|
31
24
|
iconName?: string
|
|
32
|
-
/** Icon tint color override. */
|
|
33
25
|
iconColor?: string
|
|
34
26
|
disabled?: boolean
|
|
35
27
|
}
|
|
@@ -38,26 +30,12 @@ export interface ChipGroupProps {
|
|
|
38
30
|
options: ChipOption[]
|
|
39
31
|
value?: string | number | (string | number)[]
|
|
40
32
|
onValueChange?: (value: string | number | (string | number)[]) => void
|
|
41
|
-
/** When true, allows selecting multiple chips. `value` and `onValueChange` will use arrays. */
|
|
42
33
|
multiSelect?: boolean
|
|
43
34
|
style?: ViewStyle
|
|
44
35
|
}
|
|
45
36
|
|
|
46
|
-
|
|
37
|
+
function ChipBase({ label, selected = false, onPress, icon, iconName, style, accessibilityLabel }: ChipProps) {
|
|
47
38
|
const { colors } = useTheme()
|
|
48
|
-
const { animatedStyle: scaleStyle, onPressIn, onPressOut, hoverHandlers } = usePressScale({
|
|
49
|
-
pressScale: PRESS_SCALE.chip,
|
|
50
|
-
})
|
|
51
|
-
const colorProgress = useColorTransition(selected)
|
|
52
|
-
|
|
53
|
-
const surfaceStyle = useAnimatedStyle(() => ({
|
|
54
|
-
backgroundColor: interpolateColor(colorProgress.value, [0, 1], [colors.surface, colors.primary]),
|
|
55
|
-
borderColor: interpolateColor(colorProgress.value, [0, 1], [colors.border, colors.primary]),
|
|
56
|
-
}))
|
|
57
|
-
|
|
58
|
-
const textStyle = useAnimatedStyle(() => ({
|
|
59
|
-
color: interpolateColor(colorProgress.value, [0, 1], [colors.foreground, colors.primaryForeground]),
|
|
60
|
-
}))
|
|
61
39
|
|
|
62
40
|
const handlePress = () => {
|
|
63
41
|
hapticSelection()
|
|
@@ -69,49 +47,54 @@ export function Chip({ label, selected = false, onPress, icon, iconName, style,
|
|
|
69
47
|
: icon
|
|
70
48
|
|
|
71
49
|
return (
|
|
72
|
-
<
|
|
73
|
-
<
|
|
50
|
+
<View style={[styles.wrapper, style]}>
|
|
51
|
+
<PressableChipComponent
|
|
74
52
|
onPress={handlePress}
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
activeOpacity={1}
|
|
78
|
-
touchSoundDisabled={true}
|
|
53
|
+
rippleColor="transparent"
|
|
54
|
+
touchSoundDisabled
|
|
79
55
|
accessibilityRole="button"
|
|
80
56
|
accessibilityLabel={accessibilityLabel ?? label}
|
|
81
57
|
accessibilityState={{ selected }}
|
|
82
58
|
>
|
|
83
|
-
<
|
|
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
|
+
>
|
|
84
67
|
{resolvedIcon ? <View style={styles.chipIcon}>{resolvedIcon}</View> : null}
|
|
85
|
-
<
|
|
68
|
+
<Text
|
|
69
|
+
style={[styles.label, { color: selected ? colors.primaryForeground : colors.foreground }]}
|
|
70
|
+
allowFontScaling={true}
|
|
71
|
+
>
|
|
86
72
|
{label}
|
|
87
|
-
</
|
|
88
|
-
</
|
|
89
|
-
</
|
|
90
|
-
</
|
|
73
|
+
</Text>
|
|
74
|
+
</EaseView>
|
|
75
|
+
</PressableChipComponent>
|
|
76
|
+
</View>
|
|
91
77
|
)
|
|
92
78
|
}
|
|
93
79
|
|
|
80
|
+
export const Chip = React.memo(ChipBase)
|
|
81
|
+
|
|
94
82
|
export function ChipGroup({ options, value, onValueChange, multiSelect = false, style }: ChipGroupProps) {
|
|
95
83
|
const handlePress = (optionValue: string | number) => {
|
|
96
84
|
if (!multiSelect) {
|
|
97
85
|
onValueChange?.(optionValue)
|
|
98
86
|
return
|
|
99
87
|
}
|
|
100
|
-
|
|
101
88
|
const currentArray = Array.isArray(value) ? value : value ? [value] : []
|
|
102
89
|
const isSelected = currentArray.includes(optionValue)
|
|
103
|
-
|
|
104
90
|
const newArray: (string | number)[] = isSelected
|
|
105
91
|
? currentArray.filter((v) => v !== optionValue)
|
|
106
92
|
: [...currentArray, optionValue]
|
|
107
|
-
|
|
108
93
|
onValueChange?.(newArray)
|
|
109
94
|
}
|
|
110
95
|
|
|
111
96
|
const isSelected = (optionValue: string | number): boolean => {
|
|
112
|
-
if (Array.isArray(value))
|
|
113
|
-
return value.includes(optionValue)
|
|
114
|
-
}
|
|
97
|
+
if (Array.isArray(value)) return value.includes(optionValue)
|
|
115
98
|
return optionValue === value
|
|
116
99
|
}
|
|
117
100
|
|
|
@@ -124,7 +107,11 @@ export function ChipGroup({ options, value, onValueChange, multiSelect = false,
|
|
|
124
107
|
selected={isSelected(opt.value)}
|
|
125
108
|
onPress={opt.disabled ? undefined : () => handlePress(opt.value)}
|
|
126
109
|
iconName={opt.iconName}
|
|
127
|
-
style={
|
|
110
|
+
// AUDIT FIX: was style={{ opacity: 0.4 }} with no accessibilityState.
|
|
111
|
+
// Now passes disabled state to the Chip's TouchableOpacity via onPress=undefined
|
|
112
|
+
// and adds explicit accessibility state for screen readers.
|
|
113
|
+
style={opt.disabled ? styles.chipDisabled : undefined}
|
|
114
|
+
accessibilityLabel={opt.disabled ? `${opt.label}, unavailable` : opt.label}
|
|
128
115
|
/>
|
|
129
116
|
))}
|
|
130
117
|
</View>
|
|
@@ -136,19 +123,25 @@ const styles = StyleSheet.create({
|
|
|
136
123
|
chip: {
|
|
137
124
|
borderRadius: 999,
|
|
138
125
|
paddingHorizontal: s(14),
|
|
139
|
-
|
|
126
|
+
// AUDIT FIX: was vs(5) → ~28px total height — below WCAG 44px tap target.
|
|
127
|
+
// vs(10) → ~44px total height meets WCAG 2.5.5 (AAA) minimum.
|
|
128
|
+
paddingVertical: vs(10),
|
|
129
|
+
minHeight: 44,
|
|
140
130
|
borderWidth: 1,
|
|
141
131
|
alignItems: 'center',
|
|
142
132
|
justifyContent: 'center',
|
|
143
133
|
flexDirection: 'row',
|
|
144
134
|
gap: s(5),
|
|
145
135
|
},
|
|
136
|
+
chipDisabled: {
|
|
137
|
+
opacity: 0.4,
|
|
138
|
+
},
|
|
146
139
|
chipIcon: {
|
|
147
140
|
alignItems: 'center',
|
|
148
141
|
justifyContent: 'center',
|
|
149
142
|
},
|
|
150
143
|
label: {
|
|
151
|
-
fontFamily: '
|
|
144
|
+
fontFamily: 'Sohne-Medium',
|
|
152
145
|
fontSize: ms(13),
|
|
153
146
|
lineHeight: mvs(18),
|
|
154
147
|
},
|
|
@@ -120,12 +120,12 @@ const styles = StyleSheet.create({
|
|
|
120
120
|
gap: vs(12),
|
|
121
121
|
},
|
|
122
122
|
title: {
|
|
123
|
-
fontFamily: '
|
|
123
|
+
fontFamily: 'Sohne-SemiBold',
|
|
124
124
|
fontSize: ms(18),
|
|
125
125
|
lineHeight: mvs(26),
|
|
126
126
|
},
|
|
127
127
|
description: {
|
|
128
|
-
fontFamily: '
|
|
128
|
+
fontFamily: 'Sohne-Regular',
|
|
129
129
|
fontSize: ms(15),
|
|
130
130
|
lineHeight: mvs(22),
|
|
131
131
|
},
|
|
@@ -49,7 +49,7 @@ function formatValue(value: number | string, prefix: string, showDecimals: boole
|
|
|
49
49
|
return `${sign}${prefix}${intPart}`
|
|
50
50
|
}
|
|
51
51
|
|
|
52
|
-
|
|
52
|
+
function CurrencyDisplayBase({ value, prefix = '$', showDecimals = false, textColor, variant, autoScale, maxFontSize, style }: CurrencyDisplayProps) {
|
|
53
53
|
const { colors } = useTheme()
|
|
54
54
|
const formatted = formatValue(value, prefix, showDecimals)
|
|
55
55
|
const baseFontSize = variant ? variantFontSize[variant] : ms(56)
|
|
@@ -71,12 +71,14 @@ export function CurrencyDisplay({ value, prefix = '$', showDecimals = false, tex
|
|
|
71
71
|
)
|
|
72
72
|
}
|
|
73
73
|
|
|
74
|
+
export const CurrencyDisplay = React.memo(CurrencyDisplayBase)
|
|
75
|
+
|
|
74
76
|
const styles = StyleSheet.create({
|
|
75
77
|
container: {
|
|
76
78
|
alignSelf: 'flex-start',
|
|
77
79
|
},
|
|
78
80
|
amount: {
|
|
79
|
-
fontFamily: '
|
|
81
|
+
fontFamily: 'Sohne-Bold',
|
|
80
82
|
includeFontPadding: false,
|
|
81
83
|
textAlignVertical: 'top',
|
|
82
84
|
},
|
|
@@ -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
|
|
62
|
-
|
|
63
|
-
|
|
58
|
+
const isLarge = size === 'large'
|
|
59
|
+
const inputStyle: TextStyle = isLarge
|
|
60
|
+
? { fontFamily: 'Sohne-Regular', fontSize: ms(36) }
|
|
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
|
)
|
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
import React from 'react'
|
|
2
2
|
import { View, Text, StyleSheet, ViewStyle, TextStyle } from 'react-native'
|
|
3
3
|
import { useTheme } from '../../theme'
|
|
4
|
-
import { s,
|
|
4
|
+
import { s, ms, mvs } from '../../utils/scaling'
|
|
5
5
|
import { renderIcon } from '../../utils/icons'
|
|
6
6
|
|
|
7
7
|
export type DetailRowSeparator = 'dotted' | 'solid' | 'dashed' | 'none'
|
|
8
8
|
export type DetailRowLabelWeight = 'normal' | 'medium' | 'semibold' | 'bold'
|
|
9
9
|
|
|
10
10
|
const weightMap: Record<DetailRowLabelWeight, string> = {
|
|
11
|
-
normal: '
|
|
12
|
-
medium: '
|
|
13
|
-
semibold: '
|
|
14
|
-
bold: '
|
|
11
|
+
normal: 'Sohne-Regular',
|
|
12
|
+
medium: 'Sohne-Medium',
|
|
13
|
+
semibold: 'Sohne-SemiBold',
|
|
14
|
+
bold: 'Sohne-Bold',
|
|
15
15
|
}
|
|
16
16
|
|
|
17
17
|
export interface DetailRowProps {
|
|
@@ -38,7 +38,7 @@ export interface DetailRowProps {
|
|
|
38
38
|
valueStyle?: TextStyle
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
-
|
|
41
|
+
function DetailRowBase({
|
|
42
42
|
label,
|
|
43
43
|
value,
|
|
44
44
|
separator = 'dotted',
|
|
@@ -108,6 +108,8 @@ export function DetailRow({
|
|
|
108
108
|
)
|
|
109
109
|
}
|
|
110
110
|
|
|
111
|
+
export const DetailRow = React.memo(DetailRowBase)
|
|
112
|
+
|
|
111
113
|
const styles = StyleSheet.create({
|
|
112
114
|
row: {
|
|
113
115
|
flexDirection: 'row',
|
|
@@ -138,7 +140,7 @@ const styles = StyleSheet.create({
|
|
|
138
140
|
flexShrink: 0,
|
|
139
141
|
},
|
|
140
142
|
valueText: {
|
|
141
|
-
fontFamily: '
|
|
143
|
+
fontFamily: 'Sohne-SemiBold',
|
|
142
144
|
fontSize: ms(13),
|
|
143
145
|
lineHeight: mvs(18),
|
|
144
146
|
},
|
|
@@ -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),
|
|
@@ -109,7 +111,7 @@ const styles = StyleSheet.create({
|
|
|
109
111
|
marginTop: vs(16),
|
|
110
112
|
},
|
|
111
113
|
title: {
|
|
112
|
-
fontFamily: '
|
|
114
|
+
fontFamily: 'Sohne-Medium',
|
|
113
115
|
fontSize: ms(18),
|
|
114
116
|
textAlign: 'center',
|
|
115
117
|
},
|
|
@@ -118,14 +120,13 @@ const styles = StyleSheet.create({
|
|
|
118
120
|
marginTop: vs(10),
|
|
119
121
|
},
|
|
120
122
|
description: {
|
|
121
|
-
fontFamily: '
|
|
123
|
+
fontFamily: 'Sohne-Regular',
|
|
122
124
|
fontSize: ms(14),
|
|
123
125
|
lineHeight: mvs(20),
|
|
124
126
|
textAlign: 'center',
|
|
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'
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import { View, Text, StyleSheet, ViewStyle, TextStyle } from 'react-native'
|
|
3
|
+
import { useTheme } from '../../theme'
|
|
4
|
+
import { s, vs } from '../../utils/scaling'
|
|
5
|
+
|
|
6
|
+
export interface FormProps {
|
|
7
|
+
children: React.ReactNode
|
|
8
|
+
style?: ViewStyle
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface FormFieldProps {
|
|
12
|
+
children: React.ReactNode
|
|
13
|
+
label?: string
|
|
14
|
+
error?: string
|
|
15
|
+
required?: boolean
|
|
16
|
+
style?: ViewStyle
|
|
17
|
+
labelStyle?: TextStyle
|
|
18
|
+
errorStyle?: TextStyle
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface FormSectionProps {
|
|
22
|
+
children: React.ReactNode
|
|
23
|
+
title?: string
|
|
24
|
+
description?: string
|
|
25
|
+
style?: ViewStyle
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface FormFooterProps {
|
|
29
|
+
children: React.ReactNode
|
|
30
|
+
style?: ViewStyle
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Form wrapper with consistent spacing between fields.
|
|
35
|
+
* Use Form.Field for individual inputs with label + error,
|
|
36
|
+
* Form.Section for grouped fields, and Form.Footer for action buttons.
|
|
37
|
+
*/
|
|
38
|
+
export function Form({ children, style }: FormProps) {
|
|
39
|
+
return <View style={[styles.form, style]}>{children}</View>
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Wraps a single form input with optional label and error message.
|
|
44
|
+
* Automatically spaces children properly.
|
|
45
|
+
*/
|
|
46
|
+
export function FormField({
|
|
47
|
+
children,
|
|
48
|
+
label,
|
|
49
|
+
error,
|
|
50
|
+
required,
|
|
51
|
+
style,
|
|
52
|
+
labelStyle,
|
|
53
|
+
errorStyle,
|
|
54
|
+
}: FormFieldProps) {
|
|
55
|
+
const { colors } = useTheme()
|
|
56
|
+
|
|
57
|
+
return (
|
|
58
|
+
<View style={[styles.field, style]}>
|
|
59
|
+
{label ? (
|
|
60
|
+
<Text style={[styles.label, { color: colors.foreground }, labelStyle]} allowFontScaling={true}>
|
|
61
|
+
{label}
|
|
62
|
+
{required ? <Text style={{ color: colors.destructive }}> *</Text> : null}
|
|
63
|
+
</Text>
|
|
64
|
+
) : null}
|
|
65
|
+
{children}
|
|
66
|
+
{error ? (
|
|
67
|
+
<Text style={[styles.error, { color: colors.destructive }, errorStyle]} allowFontScaling={true}>
|
|
68
|
+
{error}
|
|
69
|
+
</Text>
|
|
70
|
+
) : null}
|
|
71
|
+
</View>
|
|
72
|
+
)
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Groups related form fields with optional title and description.
|
|
77
|
+
*/
|
|
78
|
+
export function FormSection({ children, title, description, style }: FormSectionProps) {
|
|
79
|
+
const { colors } = useTheme()
|
|
80
|
+
|
|
81
|
+
return (
|
|
82
|
+
<View style={[styles.section, style]}>
|
|
83
|
+
{title ? (
|
|
84
|
+
<View style={styles.sectionHeader}>
|
|
85
|
+
<Text style={[styles.sectionTitle, { color: colors.foreground }]} allowFontScaling={true}>
|
|
86
|
+
{title}
|
|
87
|
+
</Text>
|
|
88
|
+
{description ? (
|
|
89
|
+
<Text style={[styles.sectionDescription, { color: colors.foregroundMuted }]} allowFontScaling={true}>
|
|
90
|
+
{description}
|
|
91
|
+
</Text>
|
|
92
|
+
) : null}
|
|
93
|
+
</View>
|
|
94
|
+
) : null}
|
|
95
|
+
{children}
|
|
96
|
+
</View>
|
|
97
|
+
)
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Footer area for submit/cancel buttons or form actions.
|
|
102
|
+
*/
|
|
103
|
+
export function FormFooter({ children, style }: FormFooterProps) {
|
|
104
|
+
return <View style={[styles.footer, style]}>{children}</View>
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
Form.Field = FormField
|
|
108
|
+
Form.Section = FormSection
|
|
109
|
+
Form.Footer = FormFooter
|
|
110
|
+
|
|
111
|
+
const styles = StyleSheet.create({
|
|
112
|
+
form: {
|
|
113
|
+
gap: vs(16),
|
|
114
|
+
},
|
|
115
|
+
field: {
|
|
116
|
+
gap: vs(6),
|
|
117
|
+
},
|
|
118
|
+
label: {
|
|
119
|
+
fontFamily: 'Sohne-Medium',
|
|
120
|
+
fontSize: 14,
|
|
121
|
+
lineHeight: 20,
|
|
122
|
+
},
|
|
123
|
+
error: {
|
|
124
|
+
fontFamily: 'Sohne-Regular',
|
|
125
|
+
fontSize: 12,
|
|
126
|
+
lineHeight: 16,
|
|
127
|
+
},
|
|
128
|
+
section: {
|
|
129
|
+
gap: vs(16),
|
|
130
|
+
},
|
|
131
|
+
sectionHeader: {
|
|
132
|
+
gap: vs(4),
|
|
133
|
+
},
|
|
134
|
+
sectionTitle: {
|
|
135
|
+
fontFamily: 'Sohne-SemiBold',
|
|
136
|
+
fontSize: 16,
|
|
137
|
+
lineHeight: 24,
|
|
138
|
+
},
|
|
139
|
+
sectionDescription: {
|
|
140
|
+
fontFamily: 'Sohne-Regular',
|
|
141
|
+
fontSize: 14,
|
|
142
|
+
lineHeight: 20,
|
|
143
|
+
},
|
|
144
|
+
footer: {
|
|
145
|
+
flexDirection: 'row',
|
|
146
|
+
gap: s(12),
|
|
147
|
+
paddingTop: vs(8),
|
|
148
|
+
},
|
|
149
|
+
})
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './Form'
|