@retray-dev/ui-kit 13.0.0 → 13.4.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/CHANGELOG.md +680 -0
- package/CONSUMER.md +26 -9
- package/README.md +11 -12
- package/{COMPONENTS.md → SKILL.md} +515 -815
- package/dist/Accordion.d.mts +8 -6
- package/dist/Accordion.d.ts +8 -6
- package/dist/Accordion.js +40 -40
- package/dist/Accordion.mjs +4 -5
- package/dist/AlertBanner.d.mts +3 -3
- package/dist/AlertBanner.d.ts +3 -3
- package/dist/AlertBanner.js +7 -13
- package/dist/AlertBanner.mjs +4 -5
- package/dist/AppHeader.d.mts +8 -5
- package/dist/AppHeader.d.ts +8 -5
- package/dist/AppHeader.js +44 -31
- package/dist/AppHeader.mjs +6 -7
- package/dist/Avatar.d.mts +4 -4
- package/dist/Avatar.d.ts +4 -4
- package/dist/Avatar.mjs +2 -3
- package/dist/Badge.d.mts +5 -5
- package/dist/Badge.d.ts +5 -5
- package/dist/Badge.js +7 -13
- package/dist/Badge.mjs +3 -4
- package/dist/Button.d.mts +5 -5
- package/dist/Button.d.ts +5 -5
- package/dist/Button.js +31 -29
- package/dist/Button.mjs +5 -6
- package/dist/ButtonGroup.d.mts +3 -3
- package/dist/ButtonGroup.d.ts +3 -3
- package/dist/ButtonGroup.mjs +0 -1
- package/dist/Card.d.mts +13 -13
- package/dist/Card.d.ts +13 -13
- package/dist/Card.js +23 -14
- package/dist/Card.mjs +4 -5
- package/dist/CategoryStrip.d.mts +3 -3
- package/dist/CategoryStrip.d.ts +3 -3
- package/dist/CategoryStrip.js +30 -27
- package/dist/CategoryStrip.mjs +5 -6
- package/dist/Checkbox.d.mts +3 -2
- package/dist/Checkbox.d.ts +3 -2
- package/dist/Checkbox.js +26 -15
- package/dist/Checkbox.mjs +3 -4
- package/dist/Chip.d.mts +5 -5
- package/dist/Chip.d.ts +5 -5
- package/dist/Chip.js +30 -27
- package/dist/Chip.mjs +5 -6
- package/dist/ConfirmDialog.d.mts +3 -2
- package/dist/ConfirmDialog.d.ts +3 -2
- package/dist/ConfirmDialog.js +67 -49
- package/dist/ConfirmDialog.mjs +7 -7
- package/dist/CurrencyDisplay.d.mts +3 -3
- package/dist/CurrencyDisplay.d.ts +3 -3
- package/dist/CurrencyDisplay.mjs +2 -3
- package/dist/CurrencyInput.d.mts +2 -2
- package/dist/CurrencyInput.d.ts +2 -2
- package/dist/CurrencyInput.js +7 -13
- package/dist/CurrencyInput.mjs +4 -5
- package/dist/DetailRow.d.mts +6 -6
- package/dist/DetailRow.d.ts +6 -6
- package/dist/DetailRow.js +7 -13
- package/dist/DetailRow.mjs +3 -4
- package/dist/EmptyState.d.mts +4 -4
- package/dist/EmptyState.d.ts +4 -4
- package/dist/EmptyState.js +31 -29
- package/dist/EmptyState.mjs +6 -7
- package/dist/ErrorBoundary.d.mts +9 -7
- package/dist/ErrorBoundary.d.ts +9 -7
- package/dist/ErrorBoundary.js +33 -29
- package/dist/ErrorBoundary.mjs +5 -6
- package/dist/Form.d.mts +9 -9
- package/dist/Form.d.ts +9 -9
- package/dist/Form.mjs +2 -3
- package/dist/HolographicCard.d.mts +2 -2
- package/dist/HolographicCard.d.ts +2 -2
- package/dist/HolographicCard.js +23 -14
- package/dist/HolographicCard.mjs +2 -3
- package/dist/IconButton.d.mts +4 -4
- package/dist/IconButton.d.ts +4 -4
- package/dist/IconButton.js +30 -27
- package/dist/IconButton.mjs +4 -5
- package/dist/IconPicker.d.mts +2 -2
- package/dist/IconPicker.d.ts +2 -2
- package/dist/IconPicker.js +40 -45
- package/dist/IconPicker.mjs +6 -7
- package/dist/Image.d.mts +18 -0
- package/dist/Image.d.ts +18 -0
- package/dist/Image.js +53 -0
- package/dist/Image.mjs +2 -0
- package/dist/ImageUpload.d.mts +2 -4
- package/dist/ImageUpload.d.ts +2 -4
- package/dist/ImageUpload.js +50 -40
- package/dist/ImageUpload.mjs +5 -6
- package/dist/ImageViewer.d.mts +2 -2
- package/dist/ImageViewer.d.ts +2 -2
- package/dist/ImageViewer.js +31 -28
- package/dist/ImageViewer.mjs +6 -7
- package/dist/Input.d.mts +4 -4
- package/dist/Input.d.ts +4 -4
- package/dist/Input.js +7 -13
- package/dist/Input.mjs +3 -4
- package/dist/ItemGroup.d.mts +23 -0
- package/dist/ItemGroup.d.ts +23 -0
- package/dist/{ListGroup.js → ItemGroup.js} +11 -13
- package/dist/ItemGroup.mjs +4 -0
- package/dist/LabelValue.d.mts +4 -4
- package/dist/LabelValue.d.ts +4 -4
- package/dist/LabelValue.js +7 -13
- package/dist/LabelValue.mjs +3 -4
- package/dist/ListItem.d.mts +7 -6
- package/dist/ListItem.d.ts +7 -6
- package/dist/ListItem.js +33 -28
- package/dist/ListItem.mjs +5 -6
- package/dist/MediaCard.d.mts +6 -6
- package/dist/MediaCard.d.ts +6 -6
- package/dist/MediaCard.js +30 -27
- package/dist/MediaCard.mjs +5 -6
- package/dist/MenuItem.d.mts +6 -5
- package/dist/MenuItem.d.ts +6 -5
- package/dist/MenuItem.js +33 -28
- package/dist/MenuItem.mjs +5 -6
- package/dist/MonthPicker.d.mts +2 -2
- package/dist/MonthPicker.d.ts +2 -2
- package/dist/MonthPicker.js +23 -14
- package/dist/MonthPicker.mjs +3 -4
- package/dist/NumberStepper.d.mts +4 -3
- package/dist/NumberStepper.d.ts +4 -3
- package/dist/NumberStepper.js +34 -28
- package/dist/NumberStepper.mjs +5 -6
- package/dist/PagerDots.d.mts +2 -2
- package/dist/PagerDots.d.ts +2 -2
- package/dist/PagerDots.js +30 -27
- package/dist/PagerDots.mjs +4 -5
- package/dist/Pressable.d.mts +3 -27
- package/dist/Pressable.d.ts +3 -27
- package/dist/Pressable.js +23 -14
- package/dist/Pressable.mjs +2 -3
- package/dist/PricingCard.d.mts +2 -2
- package/dist/PricingCard.d.ts +2 -2
- package/dist/PricingCard.js +31 -29
- package/dist/PricingCard.mjs +7 -8
- package/dist/Progress.d.mts +2 -2
- package/dist/Progress.d.ts +2 -2
- package/dist/Progress.mjs +2 -3
- package/dist/RadioGroup.d.mts +2 -2
- package/dist/RadioGroup.d.ts +2 -2
- package/dist/RadioGroup.js +23 -14
- package/dist/RadioGroup.mjs +3 -4
- package/dist/RetrayProvider.d.mts +1 -1
- package/dist/RetrayProvider.d.ts +1 -1
- package/dist/RetrayProvider.js +14 -34
- package/dist/RetrayProvider.mjs +3 -4
- package/dist/ScreenContainer.d.mts +24 -0
- package/dist/ScreenContainer.d.ts +24 -0
- package/dist/ScreenContainer.js +85 -0
- package/dist/ScreenContainer.mjs +3 -0
- package/dist/Select.d.mts +3 -2
- package/dist/Select.d.ts +3 -2
- package/dist/Select.js +41 -46
- package/dist/Select.mjs +3 -4
- package/dist/SelectableCard.d.mts +5 -5
- package/dist/SelectableCard.d.ts +5 -5
- package/dist/SelectableCard.js +30 -27
- package/dist/SelectableCard.mjs +5 -6
- package/dist/SelectableGrid.d.mts +5 -4
- package/dist/SelectableGrid.d.ts +5 -4
- package/dist/SelectableGrid.js +80 -45
- package/dist/SelectableGrid.mjs +5 -6
- package/dist/Separator.d.mts +4 -2
- package/dist/Separator.d.ts +4 -2
- package/dist/Separator.js +29 -1
- package/dist/Separator.mjs +3 -3
- package/dist/Sheet.d.mts +11 -11
- package/dist/Sheet.d.ts +11 -11
- package/dist/Sheet.js +62 -34
- package/dist/Sheet.mjs +4 -4
- package/dist/SheetSelect.d.mts +2 -2
- package/dist/SheetSelect.d.ts +2 -2
- package/dist/SheetSelect.js +30 -27
- package/dist/SheetSelect.mjs +5 -6
- package/dist/Skeleton.d.mts +5 -5
- package/dist/Skeleton.d.ts +5 -5
- package/dist/Skeleton.mjs +3 -4
- package/dist/Slider.d.mts +3 -2
- package/dist/Slider.d.ts +3 -2
- package/dist/Slider.js +25 -14
- package/dist/Slider.mjs +3 -4
- package/dist/Spinner.d.mts +2 -2
- package/dist/Spinner.d.ts +2 -2
- package/dist/Spinner.mjs +2 -3
- package/dist/Stats.d.mts +6 -6
- package/dist/Stats.d.ts +6 -6
- package/dist/Stats.js +30 -27
- package/dist/Stats.mjs +5 -6
- package/dist/Switch.d.mts +3 -2
- package/dist/Switch.d.ts +3 -2
- package/dist/Switch.js +25 -15
- package/dist/Switch.mjs +3 -4
- package/dist/TabBar.d.mts +3 -3
- package/dist/TabBar.d.ts +3 -3
- package/dist/TabBar.js +30 -27
- package/dist/TabBar.mjs +4 -5
- package/dist/Tabs.d.mts +13 -13
- package/dist/Tabs.d.ts +13 -13
- package/dist/Tabs.js +23 -14
- package/dist/Tabs.mjs +3 -4
- package/dist/Text.d.mts +4 -4
- package/dist/Text.d.ts +4 -4
- package/dist/Text.js +20 -2
- package/dist/Text.mjs +3 -4
- package/dist/Textarea.d.mts +3 -3
- package/dist/Textarea.d.ts +3 -3
- package/dist/Textarea.js +7 -13
- package/dist/Textarea.mjs +3 -4
- package/dist/Toast.d.mts +15 -13
- package/dist/Toast.d.ts +15 -13
- package/dist/Toast.mjs +2 -3
- package/dist/Toggle.d.mts +4 -4
- package/dist/Toggle.d.ts +4 -4
- package/dist/Toggle.js +30 -27
- package/dist/Toggle.mjs +4 -5
- package/dist/VirtualizedList.d.mts +28 -0
- package/dist/VirtualizedList.d.ts +28 -0
- package/dist/VirtualizedList.js +130 -0
- package/dist/VirtualizedList.mjs +3 -0
- package/dist/{chunk-MZ6WRTD2.mjs → chunk-24JTXQ2M.mjs} +7 -13
- package/dist/{chunk-OBV72JD4.mjs → chunk-2DDJ53DK.mjs} +9 -11
- package/dist/{chunk-6CR4S6W2.mjs → chunk-2J5OZOMX.mjs} +19 -8
- package/dist/{chunk-4NQFTHN3.mjs → chunk-3GE4UFV5.mjs} +2 -2
- package/dist/{chunk-KAGADD2O.mjs → chunk-3RIZCKRM.mjs} +2 -2
- package/dist/{chunk-DE25XTVQ.mjs → chunk-3VHFOSZR.mjs} +2 -2
- package/dist/{chunk-UOKFSFNJ.mjs → chunk-4PF4LKNT.mjs} +4 -2
- package/dist/{chunk-5MYNAAFE.mjs → chunk-5J7VKFSZ.mjs} +4 -4
- package/dist/{chunk-BTUW5LSG.mjs → chunk-5TNQ573V.mjs} +4 -3
- package/dist/{chunk-6QLBHUEG.mjs → chunk-6T2DVIQT.mjs} +7 -5
- package/dist/{chunk-L3YKPTJQ.mjs → chunk-7CE6PDCQ.mjs} +2 -2
- package/dist/{chunk-Y6YS33GM.mjs → chunk-AHFEAY6M.mjs} +4 -4
- package/dist/{chunk-4ZO5PTKF.mjs → chunk-AZRATPNP.mjs} +5 -3
- package/dist/{chunk-V2ZB2XNS.mjs → chunk-BGXOEFDM.mjs} +9 -22
- package/dist/{chunk-KC5QDYGZ.mjs → chunk-BMAAAJWN.mjs} +2 -2
- package/dist/{chunk-IJCMPVW5.mjs → chunk-BQMJQMWY.mjs} +2 -2
- package/dist/{chunk-E4EQSCKR.mjs → chunk-BTPCY4C7.mjs} +7 -5
- package/dist/chunk-BVJAYPAD.mjs +55 -0
- package/dist/{chunk-RA6SAAFE.mjs → chunk-BWLVX2SQ.mjs} +4 -4
- package/dist/{chunk-EROPDCB5.mjs → chunk-CCEM3HIJ.mjs} +30 -25
- package/dist/chunk-CTUFFKGS.mjs +30 -0
- package/dist/{chunk-EHGBHFMH.mjs → chunk-CYGYC7XT.mjs} +8 -4
- package/dist/{chunk-ESQDPO5E.mjs → chunk-DLAOTHHS.mjs} +7 -6
- package/dist/{chunk-QY3X2UYR.mjs → chunk-DYYPDQA2.mjs} +21 -7
- package/dist/{chunk-S44XWTTC.mjs → chunk-E4BJ5WXG.mjs} +3 -3
- package/dist/{chunk-HUSSF6TF.mjs → chunk-EQNCMDZC.mjs} +1 -1
- package/dist/{chunk-PI6RULJX.mjs → chunk-EQYTDFDD.mjs} +1 -1
- package/dist/{chunk-BULKGOIZ.mjs → chunk-FE26TPCI.mjs} +4 -4
- package/dist/{chunk-DBHSUUKU.mjs → chunk-FOUSI6JD.mjs} +1 -1
- package/dist/{chunk-KPTY7UYQ.mjs → chunk-GR7PKEKD.mjs} +1 -1
- package/dist/{chunk-RRKM4MKB.mjs → chunk-HLWGFBIF.mjs} +3 -3
- package/dist/chunk-HMKJGVXA.mjs +35 -0
- package/dist/{chunk-U6DEBYU5.mjs → chunk-IFGZUJFH.mjs} +3 -3
- package/dist/{chunk-2VIDP72N.mjs → chunk-K3V6OTVB.mjs} +1 -1
- package/dist/{chunk-K7TKID3V.mjs → chunk-K4YFTUMC.mjs} +3 -3
- package/dist/{chunk-NGEN2EES.mjs → chunk-MQAK2W6L.mjs} +14 -22
- package/dist/{chunk-CM2DG4MR.mjs → chunk-MSS3CD6F.mjs} +4 -4
- package/dist/{chunk-TETMEKZE.mjs → chunk-NQYS6RPX.mjs} +8 -5
- package/dist/{chunk-62BBSSUF.mjs → chunk-P5KC3RTG.mjs} +1 -1
- package/dist/{chunk-K3QX2M26.mjs → chunk-PPKCGCZ3.mjs} +5 -5
- package/dist/{chunk-ITG4JQM3.mjs → chunk-QEE3EQ3N.mjs} +2 -2
- package/dist/{chunk-URIH43IJ.mjs → chunk-RLPPRIJ7.mjs} +20 -34
- package/dist/{chunk-XCIG6HT2.mjs → chunk-S433IOQE.mjs} +2 -2
- package/dist/{chunk-IGU223UM.mjs → chunk-SWUZKVYO.mjs} +1 -1
- package/dist/{chunk-MP7GLMIR.mjs → chunk-T4KMKHTI.mjs} +55 -23
- package/dist/{chunk-2QOHHBJC.mjs → chunk-UBTP4NPP.mjs} +5 -21
- package/dist/{chunk-TMH263OK.mjs → chunk-UEA2VYGW.mjs} +3 -3
- package/dist/chunk-VISIOH33.mjs +37 -0
- package/dist/{chunk-SZEKQAOY.mjs → chunk-VSKBODEY.mjs} +1 -1
- package/dist/{chunk-FTTI6T5Q.mjs → chunk-W422TEH2.mjs} +3 -3
- package/dist/{chunk-WIPEDNSD.mjs → chunk-WD5LBXPR.mjs} +4 -4
- package/dist/chunk-WFNGSYS4.mjs +111 -0
- package/dist/chunk-WR6DCNAE.mjs +65 -0
- package/dist/{chunk-ERWJPVX7.mjs → chunk-XKBB2UZU.mjs} +2 -2
- package/dist/{chunk-CBIZLRYH.mjs → chunk-Y5TPAKOS.mjs} +14 -17
- package/dist/{chunk-AZV7KNJI.mjs → chunk-YKWIMVGU.mjs} +2 -2
- package/dist/{chunk-ZKDKKQCE.mjs → chunk-YOXSXHDE.mjs} +4 -4
- package/dist/{chunk-PGQ6FMXS.mjs → chunk-ZO5BRTCW.mjs} +2 -2
- package/dist/{chunk-KSSVIFYR.mjs → chunk-ZQGCQ7SA.mjs} +14 -34
- package/dist/{chunk-ZTPYUU5C.mjs → chunk-ZRUUUVOO.mjs} +3 -3
- package/dist/fonts.mjs +0 -2
- package/dist/{index-CY34hxPN.d.ts → index-CinAt5Uo.d.mts} +3 -3
- package/dist/{index-CY34hxPN.d.mts → index-CinAt5Uo.d.ts} +3 -3
- package/dist/index.d.mts +69 -19
- package/dist/index.d.ts +69 -19
- package/dist/index.js +1023 -839
- package/dist/index.mjs +76 -70
- package/package.json +15 -12
- package/src/components/Accordion/Accordion.tsx +12 -18
- package/src/components/AppHeader/AppHeader.tsx +33 -10
- package/src/components/Checkbox/Checkbox.tsx +3 -0
- package/src/components/ConfirmDialog/ConfirmDialog.tsx +7 -21
- package/src/components/ErrorBoundary/ErrorBoundary.tsx +5 -2
- package/src/components/Image/Image.tsx +50 -0
- package/src/components/Image/index.ts +2 -0
- package/src/components/ImageUpload/ImageUpload.tsx +34 -26
- package/src/components/{ListGroup/ListGroup.tsx → ItemGroup/ItemGroup.tsx} +15 -29
- package/src/components/ItemGroup/index.ts +2 -0
- package/src/components/ListGroup/index.tsx +20 -0
- package/src/components/ListItem/ListItem.tsx +3 -0
- package/src/components/MenuGroup/index.tsx +20 -0
- package/src/components/MenuItem/MenuItem.tsx +3 -0
- package/src/components/NumberStepper/NumberStepper.tsx +4 -0
- package/src/components/Pressable/Pressable.tsx +0 -24
- package/src/components/ScreenContainer/ScreenContainer.tsx +94 -0
- package/src/components/ScreenContainer/index.ts +2 -0
- package/src/components/Select/Select.tsx +25 -30
- package/src/components/SelectableGrid/SelectableGrid.tsx +51 -20
- package/src/components/Separator/Separator.tsx +35 -2
- package/src/components/Sheet/Sheet.tsx +3 -21
- package/src/components/Sheet/index.ts +2 -2
- package/src/components/Slider/Slider.tsx +3 -0
- package/src/components/Switch/Switch.tsx +3 -1
- package/src/components/Tabs/Tabs.tsx +9 -9
- package/src/components/Tabs/index.ts +1 -1
- package/src/components/Text/Text.tsx +7 -0
- package/src/components/VirtualizedList/VirtualizedList.tsx +154 -0
- package/src/components/VirtualizedList/index.ts +2 -0
- package/src/hooks/useConfirmDialog.ts +2 -11
- package/src/hooks/useSheetModal.ts +40 -0
- package/src/index.ts +5 -1
- package/src/theme/colors.ts +19 -57
- package/src/tokens.ts +21 -7
- package/src/utils/curatedIcons.ts +9 -18
- package/src/utils/haptics.ts +10 -21
- package/src/utils/icons.ts +7 -14
- package/dist/ListGroup.d.mts +0 -34
- package/dist/ListGroup.d.ts +0 -34
- package/dist/ListGroup.mjs +0 -5
- package/dist/MenuGroup.d.mts +0 -34
- package/dist/MenuGroup.d.ts +0 -34
- package/dist/MenuGroup.js +0 -106
- package/dist/MenuGroup.mjs +0 -5
- package/dist/chunk-ARONDO7M.mjs +0 -40
- package/dist/chunk-EW2FIDSM.mjs +0 -29
- package/dist/chunk-S2VGME7X.mjs +0 -82
- package/dist/chunk-Y6FXYEAI.mjs +0 -8
- package/src/components/ListGroup/index.ts +0 -1
- package/src/components/MenuGroup/MenuGroup.tsx +0 -145
- package/src/components/MenuGroup/index.ts +0 -1
|
@@ -28,8 +28,6 @@ export interface ImageUploadProps {
|
|
|
28
28
|
borderRadius?: number
|
|
29
29
|
/** Aspect ratio for the selected image. Defaults to 'cover'. */
|
|
30
30
|
resizeMode?: 'cover' | 'contain' | 'stretch'
|
|
31
|
-
/** Whether to allow the user to crop the image after selecting. Defaults to true. */
|
|
32
|
-
allowsEditing?: boolean
|
|
33
31
|
disabled?: boolean
|
|
34
32
|
style?: ViewStyle
|
|
35
33
|
accessibilityLabel?: string
|
|
@@ -47,7 +45,6 @@ export function ImageUpload({
|
|
|
47
45
|
height = 200,
|
|
48
46
|
borderRadius = RADIUS.lg,
|
|
49
47
|
resizeMode = 'cover',
|
|
50
|
-
allowsEditing = true,
|
|
51
48
|
disabled = false,
|
|
52
49
|
style,
|
|
53
50
|
accessibilityLabel,
|
|
@@ -55,32 +52,43 @@ export function ImageUpload({
|
|
|
55
52
|
}: ImageUploadProps) {
|
|
56
53
|
const { colors } = useTheme()
|
|
57
54
|
const [imageLoaded, setImageLoaded] = useState(false)
|
|
55
|
+
const [isPicking, setIsPicking] = useState(false)
|
|
58
56
|
|
|
59
57
|
const handlePress = async () => {
|
|
60
|
-
if (disabled || loading) return
|
|
58
|
+
if (disabled || loading || isPicking) return
|
|
61
59
|
impactLight()
|
|
62
60
|
|
|
63
61
|
onPickerStarting?.()
|
|
62
|
+
setIsPicking(true)
|
|
64
63
|
|
|
65
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
66
|
-
let picker: any
|
|
67
64
|
try {
|
|
68
|
-
|
|
69
|
-
picker = require('expo-image-picker')
|
|
70
|
-
} catch {
|
|
71
|
-
if (__DEV__) console.warn('[ImageUpload] expo-image-picker not installed.')
|
|
72
|
-
return
|
|
73
|
-
}
|
|
65
|
+
const { launchImageLibrary } = await import('react-native-image-picker')
|
|
74
66
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
67
|
+
const result = await new Promise<{
|
|
68
|
+
didCancel?: boolean
|
|
69
|
+
errorCode?: string
|
|
70
|
+
errorMessage?: string
|
|
71
|
+
assets?: { uri?: string }[]
|
|
72
|
+
}>((resolve) => {
|
|
73
|
+
launchImageLibrary(
|
|
74
|
+
{
|
|
75
|
+
mediaType: 'photo',
|
|
76
|
+
quality: 0.8,
|
|
77
|
+
selectionLimit: 1,
|
|
78
|
+
includeBase64: false,
|
|
79
|
+
},
|
|
80
|
+
resolve,
|
|
81
|
+
)
|
|
82
|
+
})
|
|
80
83
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
+
if (!result.didCancel && !result.errorCode && result.assets?.[0]?.uri) {
|
|
85
|
+
setImageLoaded(false)
|
|
86
|
+
onChange?.(result.assets[0].uri)
|
|
87
|
+
}
|
|
88
|
+
} catch {
|
|
89
|
+
// Module load failed or picker errored — silently return
|
|
90
|
+
} finally {
|
|
91
|
+
setIsPicking(false)
|
|
84
92
|
}
|
|
85
93
|
}
|
|
86
94
|
|
|
@@ -128,8 +136,8 @@ export function ImageUpload({
|
|
|
128
136
|
) : null}
|
|
129
137
|
</View>
|
|
130
138
|
)}
|
|
131
|
-
{loading ? (
|
|
132
|
-
<View style={[styles.loadingOverlay, { backgroundColor: colors.overlay }]}>
|
|
139
|
+
{loading || isPicking ? (
|
|
140
|
+
<View style={[styles.loadingOverlay, { backgroundColor: colors.overlay, borderRadius }]}>
|
|
133
141
|
<Spinner size="md" />
|
|
134
142
|
</View>
|
|
135
143
|
) : null}
|
|
@@ -163,12 +171,12 @@ const styles = StyleSheet.create({
|
|
|
163
171
|
},
|
|
164
172
|
editBadge: {
|
|
165
173
|
position: 'absolute',
|
|
166
|
-
bottom: vs(
|
|
167
|
-
right: s(
|
|
174
|
+
bottom: vs(14),
|
|
175
|
+
right: s(14),
|
|
168
176
|
},
|
|
169
177
|
editBadgeInner: {
|
|
170
|
-
width: s(
|
|
171
|
-
height: s(
|
|
178
|
+
width: s(24),
|
|
179
|
+
height: s(24),
|
|
172
180
|
borderRadius: 999,
|
|
173
181
|
alignItems: 'center',
|
|
174
182
|
justifyContent: 'center',
|
|
@@ -4,52 +4,41 @@ import { useTheme } from '../../theme'
|
|
|
4
4
|
import { s, vs } from '../../utils/scaling'
|
|
5
5
|
import { RADIUS } from '../../tokens'
|
|
6
6
|
|
|
7
|
-
export type
|
|
7
|
+
export type ItemGroupVariant = 'plain' | 'card'
|
|
8
8
|
|
|
9
|
-
export interface
|
|
9
|
+
export interface ItemGroupProps {
|
|
10
10
|
children: React.ReactNode
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
* - `card`: card surface with background + border wrapping plain ListItems.
|
|
14
|
-
*/
|
|
15
|
-
variant?: ListGroupVariant
|
|
11
|
+
variant?: ItemGroupVariant
|
|
12
|
+
childPropKey: 'title' | 'onPress'
|
|
16
13
|
style?: ViewStyle
|
|
17
14
|
}
|
|
18
15
|
|
|
19
|
-
export interface
|
|
16
|
+
export interface ItemGroupHeaderProps {
|
|
20
17
|
children: React.ReactNode
|
|
21
18
|
style?: ViewStyle
|
|
22
19
|
}
|
|
23
20
|
|
|
24
|
-
export interface
|
|
21
|
+
export interface ItemGroupFooterProps {
|
|
25
22
|
children: React.ReactNode
|
|
26
23
|
style?: ViewStyle
|
|
27
24
|
}
|
|
28
25
|
|
|
29
|
-
|
|
30
|
-
* ListGroup wraps multiple ListItems and auto-adds separators between them.
|
|
31
|
-
* Use variant="card" for a standalone surface or "plain" for items inside another container.
|
|
32
|
-
*/
|
|
33
|
-
export function ListGroup({ children, variant = 'plain', style }: ListGroupProps) {
|
|
26
|
+
export function ItemGroup({ children, variant = 'plain', childPropKey, style }: ItemGroupProps) {
|
|
34
27
|
const { colors } = useTheme()
|
|
35
28
|
|
|
36
|
-
// Auto-inject showSeparator={true} to all ListItem children except the last
|
|
37
29
|
const processedChildren = React.Children.map(children, (child, index) => {
|
|
38
30
|
if (!React.isValidElement(child)) return child
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
if (child.type === ListGroupHeader || child.type === ListGroupFooter) {
|
|
31
|
+
|
|
32
|
+
if (child.type === ItemGroupHeader || child.type === ItemGroupFooter) {
|
|
42
33
|
return child
|
|
43
34
|
}
|
|
44
35
|
|
|
45
|
-
// Check if it's a ListItem (has title prop as a heuristic)
|
|
46
36
|
const childProps = child.props as Record<string, unknown>
|
|
47
|
-
const
|
|
48
|
-
if (!
|
|
37
|
+
const isTargetChild = childPropKey in childProps
|
|
38
|
+
if (!isTargetChild) return child
|
|
49
39
|
|
|
50
40
|
const isLast = index === React.Children.count(children) - 1
|
|
51
41
|
|
|
52
|
-
// Only add separator if not already explicitly set and not last item
|
|
53
42
|
if (childProps['showSeparator'] === undefined && !isLast) {
|
|
54
43
|
return React.cloneElement(child as React.ReactElement<Record<string, unknown>>, {
|
|
55
44
|
showSeparator: true,
|
|
@@ -82,9 +71,9 @@ export function ListGroup({ children, variant = 'plain', style }: ListGroupProps
|
|
|
82
71
|
)
|
|
83
72
|
}
|
|
84
73
|
|
|
85
|
-
export function
|
|
74
|
+
export function ItemGroupHeader({ children, style }: ItemGroupHeaderProps) {
|
|
86
75
|
const { colors } = useTheme()
|
|
87
|
-
|
|
76
|
+
|
|
88
77
|
if (typeof children === 'string') {
|
|
89
78
|
return (
|
|
90
79
|
<View style={[styles.header, { borderBottomColor: colors.separator }, style]}>
|
|
@@ -98,9 +87,9 @@ export function ListGroupHeader({ children, style }: ListGroupHeaderProps) {
|
|
|
98
87
|
return <View style={[styles.header, { borderBottomColor: colors.separator }, style]}>{children}</View>
|
|
99
88
|
}
|
|
100
89
|
|
|
101
|
-
export function
|
|
90
|
+
export function ItemGroupFooter({ children, style }: ItemGroupFooterProps) {
|
|
102
91
|
const { colors } = useTheme()
|
|
103
|
-
|
|
92
|
+
|
|
104
93
|
if (typeof children === 'string') {
|
|
105
94
|
return (
|
|
106
95
|
<View style={[styles.footer, style]}>
|
|
@@ -114,9 +103,6 @@ export function ListGroupFooter({ children, style }: ListGroupFooterProps) {
|
|
|
114
103
|
return <View style={[styles.footer, style]}>{children}</View>
|
|
115
104
|
}
|
|
116
105
|
|
|
117
|
-
ListGroup.Header = ListGroupHeader
|
|
118
|
-
ListGroup.Footer = ListGroupFooter
|
|
119
|
-
|
|
120
106
|
const styles = StyleSheet.create({
|
|
121
107
|
container: {
|
|
122
108
|
overflow: 'hidden',
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import { ItemGroup as BaseItemGroup, ItemGroupHeader, ItemGroupFooter } from '../ItemGroup'
|
|
3
|
+
import type { ItemGroupProps, ItemGroupVariant, ItemGroupHeaderProps, ItemGroupFooterProps } from '../ItemGroup'
|
|
4
|
+
|
|
5
|
+
export type ListGroupVariant = ItemGroupVariant
|
|
6
|
+
|
|
7
|
+
export interface ListGroupProps extends Omit<ItemGroupProps, 'childPropKey'> {}
|
|
8
|
+
|
|
9
|
+
export interface ListGroupHeaderProps extends ItemGroupHeaderProps {}
|
|
10
|
+
|
|
11
|
+
export interface ListGroupFooterProps extends ItemGroupFooterProps {}
|
|
12
|
+
|
|
13
|
+
export function ListGroup(props: ListGroupProps) {
|
|
14
|
+
return <BaseItemGroup childPropKey="title" {...props} />
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
ListGroup.Header = ItemGroupHeader
|
|
18
|
+
ListGroup.Footer = ItemGroupFooter
|
|
19
|
+
|
|
20
|
+
export { ItemGroupHeader as ListGroupHeader, ItemGroupFooter as ListGroupFooter }
|
|
@@ -85,6 +85,7 @@ export interface ListItemProps {
|
|
|
85
85
|
captionStyle?: TextStyle
|
|
86
86
|
/** Accessibility label override. Defaults to the title. */
|
|
87
87
|
accessibilityLabel?: string
|
|
88
|
+
accessibilityHint?: string
|
|
88
89
|
}
|
|
89
90
|
|
|
90
91
|
function ListItemBase({
|
|
@@ -110,6 +111,7 @@ function ListItemBase({
|
|
|
110
111
|
subtitleNumberOfLines = 2,
|
|
111
112
|
captionStyle,
|
|
112
113
|
accessibilityLabel,
|
|
114
|
+
accessibilityHint,
|
|
113
115
|
}: ListItemProps) {
|
|
114
116
|
const { colors } = useTheme()
|
|
115
117
|
|
|
@@ -221,6 +223,7 @@ function ListItemBase({
|
|
|
221
223
|
activateOnHover
|
|
222
224
|
accessibilityRole="button"
|
|
223
225
|
accessibilityLabel={a11yLabel}
|
|
226
|
+
accessibilityHint={accessibilityHint}
|
|
224
227
|
accessibilityState={{ disabled: !!disabled }}
|
|
225
228
|
>
|
|
226
229
|
{content}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import { ItemGroup as BaseItemGroup, ItemGroupHeader, ItemGroupFooter } from '../ItemGroup'
|
|
3
|
+
import type { ItemGroupProps, ItemGroupVariant, ItemGroupHeaderProps, ItemGroupFooterProps } from '../ItemGroup'
|
|
4
|
+
|
|
5
|
+
export type MenuGroupVariant = ItemGroupVariant
|
|
6
|
+
|
|
7
|
+
export interface MenuGroupProps extends Omit<ItemGroupProps, 'childPropKey'> {}
|
|
8
|
+
|
|
9
|
+
export interface MenuGroupHeaderProps extends ItemGroupHeaderProps {}
|
|
10
|
+
|
|
11
|
+
export interface MenuGroupFooterProps extends ItemGroupFooterProps {}
|
|
12
|
+
|
|
13
|
+
export function MenuGroup(props: MenuGroupProps) {
|
|
14
|
+
return <BaseItemGroup childPropKey="onPress" {...props} />
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
MenuGroup.Header = ItemGroupHeader
|
|
18
|
+
MenuGroup.Footer = ItemGroupFooter
|
|
19
|
+
|
|
20
|
+
export { ItemGroupHeader as MenuGroupHeader, ItemGroupFooter as MenuGroupFooter }
|
|
@@ -55,6 +55,7 @@ export interface MenuItemProps {
|
|
|
55
55
|
labelStyle?: TextStyle
|
|
56
56
|
/** Accessibility label override. Defaults to label. */
|
|
57
57
|
accessibilityLabel?: string
|
|
58
|
+
accessibilityHint?: string
|
|
58
59
|
}
|
|
59
60
|
|
|
60
61
|
function MenuItemBase({
|
|
@@ -72,6 +73,7 @@ function MenuItemBase({
|
|
|
72
73
|
style,
|
|
73
74
|
labelStyle,
|
|
74
75
|
accessibilityLabel,
|
|
76
|
+
accessibilityHint,
|
|
75
77
|
}: MenuItemProps) {
|
|
76
78
|
const { colors } = useTheme()
|
|
77
79
|
|
|
@@ -112,6 +114,7 @@ function MenuItemBase({
|
|
|
112
114
|
activateOnHover
|
|
113
115
|
accessibilityRole="button"
|
|
114
116
|
accessibilityLabel={a11yLabel}
|
|
117
|
+
accessibilityHint={accessibilityHint}
|
|
115
118
|
accessibilityState={{ disabled }}
|
|
116
119
|
>
|
|
117
120
|
{resolvedIcon ? (
|
|
@@ -19,6 +19,7 @@ export interface NumberStepperProps {
|
|
|
19
19
|
disabled?: boolean
|
|
20
20
|
style?: ViewStyle
|
|
21
21
|
accessibilityLabel?: string
|
|
22
|
+
accessibilityHint?: string
|
|
22
23
|
}
|
|
23
24
|
|
|
24
25
|
const sizeConfig: Record<NumberStepperSize, { button: number; icon: number; valueFontSize: number; valueLineHeight: number; valueMinWidth: number }> = {
|
|
@@ -37,6 +38,7 @@ function NumberStepperBase({
|
|
|
37
38
|
disabled = false,
|
|
38
39
|
style,
|
|
39
40
|
accessibilityLabel,
|
|
41
|
+
accessibilityHint,
|
|
40
42
|
}: NumberStepperProps) {
|
|
41
43
|
const { colors } = useTheme()
|
|
42
44
|
|
|
@@ -78,6 +80,7 @@ function NumberStepperBase({
|
|
|
78
80
|
touchSoundDisabled
|
|
79
81
|
accessibilityRole="button"
|
|
80
82
|
accessibilityLabel={`Disminuir, valor actual ${displayValue}`}
|
|
83
|
+
accessibilityHint={accessibilityHint}
|
|
81
84
|
accessibilityState={{ disabled: !canDecrement }}
|
|
82
85
|
>
|
|
83
86
|
<Icon name="minus" size={iconSize} color={canDecrement ? colors.foreground : colors.foregroundMuted} />
|
|
@@ -115,6 +118,7 @@ function NumberStepperBase({
|
|
|
115
118
|
touchSoundDisabled
|
|
116
119
|
accessibilityRole="button"
|
|
117
120
|
accessibilityLabel={`Aumentar, valor actual ${displayValue}`}
|
|
121
|
+
accessibilityHint={accessibilityHint}
|
|
118
122
|
accessibilityState={{ disabled: !canIncrement }}
|
|
119
123
|
>
|
|
120
124
|
<Icon name="plus" size={iconSize} color={canIncrement ? colors.foreground : colors.foregroundMuted} />
|
|
@@ -5,42 +5,18 @@ import { PressableCard } from '../../utils/pressable'
|
|
|
5
5
|
import { PRESS_SCALE } from '../../utils/animations'
|
|
6
6
|
|
|
7
7
|
export interface PressableProps {
|
|
8
|
-
/** Children content to render inside the pressable. */
|
|
9
8
|
children: React.ReactNode
|
|
10
|
-
/** Called when pressed. */
|
|
11
9
|
onPress?: () => void
|
|
12
|
-
/** Scale value on press. Defaults to `0.98` (MediaCard-style). */
|
|
13
10
|
pressScale?: number
|
|
14
|
-
/** Enable haptic feedback on press. Defaults to `true`. */
|
|
15
11
|
haptics?: boolean
|
|
16
|
-
/** Additional style for the wrapper. */
|
|
17
12
|
style?: ViewStyle
|
|
18
|
-
/** Disable interaction. */
|
|
19
13
|
disabled?: boolean
|
|
20
|
-
/** Hover scale (web only). Defaults to `1.02`. Set to `1` to disable. */
|
|
21
14
|
hoverScale?: number
|
|
22
|
-
/**
|
|
23
|
-
* Accessibility role for the pressable element.
|
|
24
|
-
* Defaults to `"button"`.
|
|
25
|
-
*/
|
|
26
15
|
accessibilityRole?: AccessibilityRole
|
|
27
|
-
/**
|
|
28
|
-
* Accessibility state for screen readers.
|
|
29
|
-
* Used to communicate states like `selected`, `expanded`, `checked`, etc.
|
|
30
|
-
* Defaults to `{ disabled: !!disabled }`.
|
|
31
|
-
*/
|
|
32
16
|
accessibilityState?: Record<string, unknown>
|
|
33
|
-
/** Accessibility label for screen readers. */
|
|
34
17
|
accessibilityLabel?: string
|
|
35
18
|
}
|
|
36
19
|
|
|
37
|
-
/**
|
|
38
|
-
* Generic pressable with a calibrated spring bounce — Apple HIG / Airbnb feel.
|
|
39
|
-
* All animation runs on the UI thread via pressto (Reanimated v4 worklets).
|
|
40
|
-
*
|
|
41
|
-
* Use this for any custom pressable surface that needs consistent press feel
|
|
42
|
-
* (cards, list rows, image tiles, etc).
|
|
43
|
-
*/
|
|
44
20
|
export function Pressable({
|
|
45
21
|
children,
|
|
46
22
|
onPress,
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import { View, ScrollView, KeyboardAvoidingView, Platform, StyleSheet, ViewStyle } from 'react-native'
|
|
3
|
+
import { useSafeAreaInsets } from 'react-native-safe-area-context'
|
|
4
|
+
import { useTheme } from '../../theme'
|
|
5
|
+
import { BREAKPOINTS } from '../../tokens'
|
|
6
|
+
|
|
7
|
+
export interface ScreenContainerProps {
|
|
8
|
+
children: React.ReactNode
|
|
9
|
+
/** Maximum width in points. Defaults to BREAKPOINTS.wide (700). Set to 0 to disable. */
|
|
10
|
+
maxWidth?: number
|
|
11
|
+
/** Enable scroll. Default false. */
|
|
12
|
+
scroll?: boolean
|
|
13
|
+
/** Background color. Defaults to theme `background`. */
|
|
14
|
+
backgroundColor?: string
|
|
15
|
+
/** Padding top override. Defaults to safe area top. */
|
|
16
|
+
paddingTop?: number
|
|
17
|
+
/** Padding bottom override. Defaults to safe area bottom. */
|
|
18
|
+
paddingBottom?: number
|
|
19
|
+
/** Horizontal padding. Defaults to 0. */
|
|
20
|
+
paddingHorizontal?: number
|
|
21
|
+
/** Enable keyboard avoidance. Default false. */
|
|
22
|
+
keyboard?: boolean
|
|
23
|
+
style?: ViewStyle
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function ScreenContainer({
|
|
27
|
+
children,
|
|
28
|
+
maxWidth = BREAKPOINTS.wide,
|
|
29
|
+
scroll = false,
|
|
30
|
+
backgroundColor,
|
|
31
|
+
paddingTop,
|
|
32
|
+
paddingBottom,
|
|
33
|
+
paddingHorizontal,
|
|
34
|
+
keyboard = false,
|
|
35
|
+
style: _style,
|
|
36
|
+
}: ScreenContainerProps) {
|
|
37
|
+
const insets = useSafeAreaInsets()
|
|
38
|
+
const { colors } = useTheme()
|
|
39
|
+
|
|
40
|
+
const wrapperStyle: ViewStyle = {
|
|
41
|
+
flex: 1,
|
|
42
|
+
backgroundColor: backgroundColor ?? colors.background,
|
|
43
|
+
paddingTop: paddingTop ?? insets.top,
|
|
44
|
+
paddingBottom: paddingBottom ?? insets.bottom,
|
|
45
|
+
paddingHorizontal: paddingHorizontal ?? 0,
|
|
46
|
+
..._style,
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const inner = (
|
|
50
|
+
<View style={[styles.inner, { maxWidth: maxWidth || undefined }]}>
|
|
51
|
+
{children}
|
|
52
|
+
</View>
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
const content = scroll ? (
|
|
56
|
+
<ScrollView
|
|
57
|
+
style={styles.scroll}
|
|
58
|
+
contentContainerStyle={styles.scrollContent}
|
|
59
|
+
keyboardShouldPersistTaps="handled"
|
|
60
|
+
>
|
|
61
|
+
{inner}
|
|
62
|
+
</ScrollView>
|
|
63
|
+
) : (
|
|
64
|
+
inner
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
if (keyboard) {
|
|
68
|
+
return (
|
|
69
|
+
<KeyboardAvoidingView
|
|
70
|
+
style={wrapperStyle}
|
|
71
|
+
behavior={Platform.OS === 'ios' ? 'padding' : undefined}
|
|
72
|
+
keyboardVerticalOffset={Platform.OS === 'ios' ? 0 : 0}
|
|
73
|
+
>
|
|
74
|
+
{content}
|
|
75
|
+
</KeyboardAvoidingView>
|
|
76
|
+
)
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
return <View style={wrapperStyle}>{content}</View>
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const styles = StyleSheet.create({
|
|
83
|
+
scroll: {
|
|
84
|
+
flex: 1,
|
|
85
|
+
},
|
|
86
|
+
scrollContent: {
|
|
87
|
+
flexGrow: 1,
|
|
88
|
+
},
|
|
89
|
+
inner: {
|
|
90
|
+
flex: 1,
|
|
91
|
+
alignSelf: 'center',
|
|
92
|
+
width: '100%',
|
|
93
|
+
},
|
|
94
|
+
})
|
|
@@ -28,6 +28,7 @@ export interface SelectProps {
|
|
|
28
28
|
disabled?: boolean
|
|
29
29
|
style?: ViewStyle
|
|
30
30
|
accessibilityLabel?: string
|
|
31
|
+
accessibilityHint?: string
|
|
31
32
|
}
|
|
32
33
|
|
|
33
34
|
export function Select({
|
|
@@ -41,6 +42,7 @@ export function Select({
|
|
|
41
42
|
disabled,
|
|
42
43
|
style,
|
|
43
44
|
accessibilityLabel,
|
|
45
|
+
accessibilityHint,
|
|
44
46
|
}: SelectProps) {
|
|
45
47
|
const { colors } = useTheme()
|
|
46
48
|
const [pickerVisible, setPickerVisible] = useState(false)
|
|
@@ -49,6 +51,25 @@ export function Select({
|
|
|
49
51
|
|
|
50
52
|
const selected = options.find((o) => o.value === value)
|
|
51
53
|
|
|
54
|
+
function renderPickerItems(includePlaceholder: boolean, itemColor?: string, disabledColor?: string) {
|
|
55
|
+
return (
|
|
56
|
+
<>
|
|
57
|
+
{includePlaceholder ? (
|
|
58
|
+
<Picker.Item label={placeholder} value="" enabled={false} color={disabledColor} />
|
|
59
|
+
) : null}
|
|
60
|
+
{options.map((o) => (
|
|
61
|
+
<Picker.Item
|
|
62
|
+
key={o.value}
|
|
63
|
+
label={o.label}
|
|
64
|
+
value={o.value}
|
|
65
|
+
enabled={!o.disabled}
|
|
66
|
+
color={o.disabled ? disabledColor : itemColor}
|
|
67
|
+
/>
|
|
68
|
+
))}
|
|
69
|
+
</>
|
|
70
|
+
)
|
|
71
|
+
}
|
|
72
|
+
|
|
52
73
|
const handleOpen = () => {
|
|
53
74
|
if (disabled) return
|
|
54
75
|
hapticSelection()
|
|
@@ -93,6 +114,7 @@ export function Select({
|
|
|
93
114
|
touchSoundDisabled
|
|
94
115
|
accessibilityRole="combobox"
|
|
95
116
|
accessibilityLabel={accessibilityLabel ?? label}
|
|
117
|
+
accessibilityHint={accessibilityHint}
|
|
96
118
|
accessibilityValue={{ text: selected?.label ?? placeholder }}
|
|
97
119
|
accessibilityState={{ disabled: !!disabled, expanded: pickerVisible }}
|
|
98
120
|
>
|
|
@@ -139,18 +161,7 @@ export function Select({
|
|
|
139
161
|
onValueChange={(val) => setPendingValue(val as string)}
|
|
140
162
|
itemStyle={{ color: colors.foreground }}
|
|
141
163
|
>
|
|
142
|
-
{!value
|
|
143
|
-
<Picker.Item label={placeholder} value="" color={colors.foregroundMuted} enabled={false} />
|
|
144
|
-
) : null}
|
|
145
|
-
{options.map((o) => (
|
|
146
|
-
<Picker.Item
|
|
147
|
-
key={o.value}
|
|
148
|
-
label={o.label}
|
|
149
|
-
value={o.value}
|
|
150
|
-
enabled={!o.disabled}
|
|
151
|
-
color={o.disabled ? colors.foregroundMuted : colors.foreground}
|
|
152
|
-
/>
|
|
153
|
-
))}
|
|
164
|
+
{renderPickerItems(!value, colors.foreground, colors.foregroundMuted)}
|
|
154
165
|
</Picker>
|
|
155
166
|
</View>
|
|
156
167
|
</Modal>
|
|
@@ -172,15 +183,7 @@ export function Select({
|
|
|
172
183
|
prompt={label}
|
|
173
184
|
style={styles.androidHiddenPicker}
|
|
174
185
|
>
|
|
175
|
-
{!value
|
|
176
|
-
{options.map((o) => (
|
|
177
|
-
<Picker.Item
|
|
178
|
-
key={o.value}
|
|
179
|
-
label={o.label}
|
|
180
|
-
value={o.value}
|
|
181
|
-
enabled={!o.disabled}
|
|
182
|
-
/>
|
|
183
|
-
))}
|
|
186
|
+
{renderPickerItems(!value)}
|
|
184
187
|
</Picker>
|
|
185
188
|
) : null}
|
|
186
189
|
|
|
@@ -204,15 +207,7 @@ export function Select({
|
|
|
204
207
|
},
|
|
205
208
|
]}
|
|
206
209
|
>
|
|
207
|
-
|
|
208
|
-
{options.map((o) => (
|
|
209
|
-
<Picker.Item
|
|
210
|
-
key={o.value}
|
|
211
|
-
label={o.label}
|
|
212
|
-
value={o.value}
|
|
213
|
-
enabled={!o.disabled}
|
|
214
|
-
/>
|
|
215
|
-
))}
|
|
210
|
+
{renderPickerItems(true)}
|
|
216
211
|
</Picker>
|
|
217
212
|
) : null}
|
|
218
213
|
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import React, { useState } from 'react'
|
|
2
|
-
import { View, Text, StyleSheet, ViewStyle, ScrollView } from 'react-native'
|
|
2
|
+
import { View, Text, StyleSheet, ViewStyle, ScrollView, ImageSourcePropType } from 'react-native'
|
|
3
|
+
import { Image } from 'expo-image'
|
|
3
4
|
import { useTheme } from '../../theme'
|
|
4
5
|
import { Icon } from '../../utils/icons'
|
|
5
6
|
import { PressableChip } from '../../utils/pressable'
|
|
@@ -12,6 +13,7 @@ export interface SelectableGridItem<T extends string | number = string> {
|
|
|
12
13
|
label?: string
|
|
13
14
|
iconName?: string
|
|
14
15
|
icon?: React.ReactNode
|
|
16
|
+
imageUrl?: ImageSourcePropType
|
|
15
17
|
disabled?: boolean
|
|
16
18
|
}
|
|
17
19
|
|
|
@@ -40,10 +42,13 @@ interface CellProps<T extends string | number> {
|
|
|
40
42
|
|
|
41
43
|
function Cell<T extends string | number>({ item, selected, width, onPress }: CellProps<T>) {
|
|
42
44
|
const { colors } = useTheme()
|
|
45
|
+
const hasImage = !!item.imageUrl
|
|
43
46
|
|
|
44
47
|
const iconColor = selected ? colors.primary : colors.foregroundSubtle
|
|
45
48
|
const iconNode = item.icon ?? (item.iconName ? <Icon name={item.iconName} size={ms(24)} color={iconColor} /> : null)
|
|
46
49
|
|
|
50
|
+
const imageSize = width - 4
|
|
51
|
+
|
|
47
52
|
return (
|
|
48
53
|
<PressableChip
|
|
49
54
|
onPress={onPress}
|
|
@@ -54,26 +59,40 @@ function Cell<T extends string | number>({ item, selected, width, onPress }: Cel
|
|
|
54
59
|
accessibilityRole="button"
|
|
55
60
|
accessibilityState={{ selected, disabled: item.disabled }}
|
|
56
61
|
accessibilityLabel={item.label ?? String(item.value)}
|
|
57
|
-
style={[
|
|
58
|
-
{ width },
|
|
59
|
-
styles.cell,
|
|
60
|
-
{
|
|
61
|
-
backgroundColor: selected ? colors.primary + '14' : colors.surface,
|
|
62
|
-
borderColor: selected ? colors.primary : 'transparent',
|
|
63
|
-
},
|
|
64
|
-
item.disabled && styles.cellDisabled,
|
|
65
|
-
]}
|
|
66
62
|
>
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
63
|
+
<View
|
|
64
|
+
style={[
|
|
65
|
+
{ width },
|
|
66
|
+
styles.cell,
|
|
67
|
+
hasImage && styles.cellImage,
|
|
68
|
+
{
|
|
69
|
+
borderColor: selected ? colors.primary : 'transparent',
|
|
70
|
+
},
|
|
71
|
+
item.disabled && styles.cellDisabled,
|
|
72
|
+
]}
|
|
73
|
+
>
|
|
74
|
+
{hasImage ? (
|
|
75
|
+
<Image
|
|
76
|
+
source={item.imageUrl}
|
|
77
|
+
style={{ width: imageSize, height: imageSize }}
|
|
78
|
+
contentFit="cover"
|
|
79
|
+
/>
|
|
80
|
+
) : null}
|
|
81
|
+
{iconNode && !hasImage ? iconNode : null}
|
|
82
|
+
{item.label ? (
|
|
83
|
+
<Text
|
|
84
|
+
style={[
|
|
85
|
+
styles.label,
|
|
86
|
+
hasImage && styles.labelImage,
|
|
87
|
+
{ color: selected ? colors.primary : colors.foreground },
|
|
88
|
+
]}
|
|
89
|
+
numberOfLines={2}
|
|
90
|
+
allowFontScaling={true}
|
|
91
|
+
>
|
|
92
|
+
{item.label}
|
|
93
|
+
</Text>
|
|
94
|
+
) : null}
|
|
95
|
+
</View>
|
|
77
96
|
</PressableChip>
|
|
78
97
|
)
|
|
79
98
|
}
|
|
@@ -159,6 +178,13 @@ const styles = StyleSheet.create({
|
|
|
159
178
|
paddingHorizontal: s(8),
|
|
160
179
|
paddingVertical: vs(10),
|
|
161
180
|
},
|
|
181
|
+
cellImage: {
|
|
182
|
+
padding: 0,
|
|
183
|
+
gap: 0,
|
|
184
|
+
overflow: 'hidden',
|
|
185
|
+
alignItems: 'stretch',
|
|
186
|
+
justifyContent: 'flex-start',
|
|
187
|
+
},
|
|
162
188
|
cellDisabled: {
|
|
163
189
|
opacity: 0.4,
|
|
164
190
|
},
|
|
@@ -168,4 +194,9 @@ const styles = StyleSheet.create({
|
|
|
168
194
|
lineHeight: mvs(14),
|
|
169
195
|
textAlign: 'center',
|
|
170
196
|
},
|
|
197
|
+
labelImage: {
|
|
198
|
+
paddingHorizontal: s(6),
|
|
199
|
+
paddingBottom: vs(6),
|
|
200
|
+
paddingTop: vs(3),
|
|
201
|
+
},
|
|
171
202
|
})
|