@retray-dev/ui-kit 10.2.0 → 12.2.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 +384 -40
- package/README.md +14 -5
- package/dist/Accordion.d.mts +6 -0
- package/dist/Accordion.d.ts +6 -0
- package/dist/Accordion.js +16 -0
- package/dist/Accordion.mjs +2 -2
- package/dist/AlertBanner.js +2 -0
- package/dist/AlertBanner.mjs +2 -2
- package/dist/AppHeader.js +2 -0
- package/dist/AppHeader.mjs +3 -3
- package/dist/Avatar.js +2 -0
- package/dist/Avatar.mjs +2 -2
- package/dist/Badge.js +2 -0
- package/dist/Badge.mjs +2 -2
- package/dist/Button.js +17 -17
- package/dist/Button.mjs +2 -2
- package/dist/Card.js +2 -0
- package/dist/Card.mjs +2 -2
- package/dist/CategoryStrip.js +2 -0
- package/dist/CategoryStrip.mjs +2 -2
- package/dist/Checkbox.js +2 -0
- package/dist/Checkbox.mjs +2 -2
- package/dist/Chip.js +2 -0
- package/dist/Chip.mjs +2 -2
- package/dist/ConfirmDialog.d.mts +1 -6
- package/dist/ConfirmDialog.d.ts +1 -6
- package/dist/ConfirmDialog.js +53 -41
- package/dist/ConfirmDialog.mjs +3 -3
- package/dist/CurrencyDisplay.js +2 -0
- package/dist/CurrencyDisplay.mjs +2 -2
- package/dist/CurrencyInput.d.mts +3 -8
- package/dist/CurrencyInput.d.ts +3 -8
- package/dist/CurrencyInput.js +5 -1
- package/dist/CurrencyInput.mjs +3 -3
- package/dist/DetailRow.js +2 -0
- package/dist/DetailRow.mjs +2 -2
- package/dist/EmptyState.js +17 -17
- package/dist/EmptyState.mjs +3 -3
- package/dist/ErrorBoundary.js +2 -0
- package/dist/ErrorBoundary.mjs +2 -2
- package/dist/Form.js +2 -0
- package/dist/Form.mjs +2 -2
- package/dist/IconButton.js +2 -0
- package/dist/IconButton.mjs +2 -2
- package/dist/IconPicker.js +677 -248
- package/dist/IconPicker.mjs +3 -2
- package/dist/ImageUpload.d.mts +3 -1
- package/dist/ImageUpload.d.ts +3 -1
- package/dist/ImageUpload.js +10 -3
- package/dist/ImageUpload.mjs +3 -3
- package/dist/ImageViewer.js +2 -0
- package/dist/ImageViewer.mjs +4 -4
- package/dist/Input.js +2 -0
- package/dist/Input.mjs +2 -2
- package/dist/LabelValue.js +2 -0
- package/dist/LabelValue.mjs +2 -2
- package/dist/ListGroup.js +2 -0
- package/dist/ListGroup.mjs +2 -2
- package/dist/ListItem.d.mts +7 -7
- package/dist/ListItem.d.ts +7 -7
- package/dist/ListItem.js +14 -7
- package/dist/ListItem.mjs +2 -2
- package/dist/MediaCard.js +2 -0
- package/dist/MediaCard.mjs +2 -2
- package/dist/MenuGroup.js +2 -0
- package/dist/MenuGroup.mjs +2 -2
- package/dist/MenuItem.js +2 -0
- package/dist/MenuItem.mjs +2 -2
- package/dist/MonthPicker.js +2 -0
- package/dist/MonthPicker.mjs +2 -2
- package/dist/NumberStepper.js +2 -0
- package/dist/NumberStepper.mjs +2 -2
- package/dist/PagerDots.js +2 -0
- package/dist/PagerDots.mjs +2 -2
- package/dist/Pressable.d.mts +15 -7
- package/dist/Pressable.d.ts +15 -7
- package/dist/Pressable.js +7 -3
- package/dist/Pressable.mjs +1 -1
- package/dist/PricingCard.js +17 -17
- package/dist/PricingCard.mjs +4 -4
- package/dist/Progress.js +2 -0
- package/dist/Progress.mjs +2 -2
- package/dist/RadioGroup.js +2 -0
- package/dist/RadioGroup.mjs +2 -2
- package/dist/RetrayProvider.d.mts +1 -1
- package/dist/RetrayProvider.d.ts +1 -1
- package/dist/RetrayProvider.js +2 -0
- package/dist/RetrayProvider.mjs +3 -3
- package/dist/Select.js +2 -0
- package/dist/Select.mjs +2 -2
- package/dist/SelectableCard.d.mts +27 -0
- package/dist/SelectableCard.d.ts +27 -0
- package/dist/SelectableCard.js +511 -0
- package/dist/SelectableCard.mjs +8 -0
- package/dist/SelectableGrid.js +2 -0
- package/dist/SelectableGrid.mjs +2 -2
- package/dist/Separator.js +2 -0
- package/dist/Separator.mjs +2 -2
- package/dist/Sheet.d.mts +4 -46
- package/dist/Sheet.d.ts +4 -46
- package/dist/Sheet.js +55 -115
- package/dist/Sheet.mjs +2 -3
- package/dist/SheetSelect.js +2 -0
- package/dist/SheetSelect.mjs +2 -2
- package/dist/Skeleton.d.mts +3 -1
- package/dist/Skeleton.d.ts +3 -1
- package/dist/Skeleton.js +5 -2
- package/dist/Skeleton.mjs +2 -2
- package/dist/Slider.js +2 -0
- package/dist/Slider.mjs +2 -2
- package/dist/Spinner.js +2 -0
- package/dist/Spinner.mjs +2 -2
- package/dist/Stats.d.mts +33 -0
- package/dist/Stats.d.ts +33 -0
- package/dist/Stats.js +453 -0
- package/dist/Stats.mjs +9 -0
- package/dist/Switch.js +2 -0
- package/dist/Switch.mjs +2 -2
- package/dist/TabBar.js +2 -0
- package/dist/TabBar.mjs +2 -2
- package/dist/Tabs.js +2 -0
- package/dist/Tabs.mjs +2 -2
- package/dist/Text.d.mts +3 -1
- package/dist/Text.d.ts +3 -1
- package/dist/Text.js +5 -3
- package/dist/Text.mjs +2 -2
- package/dist/Textarea.js +2 -0
- package/dist/Textarea.mjs +2 -2
- package/dist/Toast.js +2 -0
- package/dist/Toast.mjs +2 -2
- package/dist/Toggle.js +2 -0
- package/dist/Toggle.mjs +2 -2
- package/dist/{chunk-U2XJFYED.mjs → chunk-2BA3JMKK.mjs} +1 -1
- package/dist/{chunk-NMU5FMQJ.mjs → chunk-2HFD4IHU.mjs} +4 -2
- package/dist/{chunk-S2R7UVOE.mjs → chunk-2LG326TT.mjs} +1 -1
- package/dist/chunk-2P2CB235.mjs +236 -0
- package/dist/{chunk-6L4G6PBT.mjs → chunk-3XCFYSX4.mjs} +1 -1
- package/dist/{chunk-HTHGSXFG.mjs → chunk-4J2PXL36.mjs} +16 -18
- package/dist/{chunk-BEMIQXXU.mjs → chunk-4OORJ2DY.mjs} +1 -1
- package/dist/chunk-4XOB5TTD.mjs +166 -0
- package/dist/{chunk-FCSSQK3L.mjs → chunk-57V2LXCK.mjs} +1 -1
- package/dist/{chunk-6Q64UFIA.mjs → chunk-7AFZWSCI.mjs} +1 -1
- package/dist/{chunk-IX3NYLYQ.mjs → chunk-7ELGZ66G.mjs} +1 -1
- package/dist/{chunk-GD6KXMG5.mjs → chunk-AENAVIKT.mjs} +1 -1
- package/dist/{chunk-ID72TK46.mjs → chunk-BXF4AMHY.mjs} +1 -1
- package/dist/{chunk-SOA2Z4RB.mjs → chunk-C43HRKXH.mjs} +1 -1
- package/dist/{chunk-TZDGAP5N.mjs → chunk-CF27NBXO.mjs} +11 -6
- package/dist/{chunk-SXLKNTA4.mjs → chunk-DF7JA72E.mjs} +1 -1
- package/dist/{chunk-AJRVDP2H.mjs → chunk-E5UKLSJZ.mjs} +3 -3
- package/dist/{chunk-MBMXYJJV.mjs → chunk-E7NEHHXV.mjs} +7 -3
- package/dist/{chunk-VKID2D2I.mjs → chunk-EDLCGYIO.mjs} +13 -8
- package/dist/{chunk-BUMAMSTZ.mjs → chunk-ELGEOM7I.mjs} +1 -1
- package/dist/{chunk-DYT7BG5I.mjs → chunk-F3YTWO3T.mjs} +1 -1
- package/dist/{chunk-VF2ATYN3.mjs → chunk-GH67YXG6.mjs} +1 -1
- package/dist/{chunk-WJLKJMKR.mjs → chunk-GUTDFUNF.mjs} +4 -4
- package/dist/{chunk-6SECQ2ZF.mjs → chunk-HC4VVCWY.mjs} +2 -2
- package/dist/{chunk-A3A6KNQN.mjs → chunk-HEDQPK4I.mjs} +1 -1
- package/dist/{chunk-GQYFLP3D.mjs → chunk-IVSRW4HS.mjs} +1 -1
- package/dist/{chunk-KOO4WITD.mjs → chunk-KSUWPU2F.mjs} +1 -1
- package/dist/{chunk-WBOOUHSS.mjs → chunk-LIS6I5UP.mjs} +1 -1
- package/dist/{chunk-X4G6APW6.mjs → chunk-LNPKGWBG.mjs} +1 -1
- package/dist/{chunk-T2KCAHOS.mjs → chunk-LOBLCFMN.mjs} +1 -1
- package/dist/{chunk-ELXBDILQ.mjs → chunk-LPV4NJJK.mjs} +2 -2
- package/dist/{chunk-Y2NS74WS.mjs → chunk-M3C7XM2M.mjs} +53 -99
- package/dist/{chunk-BRKYVJVV.mjs → chunk-MEPSKGBO.mjs} +1 -1
- package/dist/{chunk-TBNZHU6C.mjs → chunk-MVMGPZN6.mjs} +2 -2
- package/dist/{chunk-YJ7I257J.mjs → chunk-NHDI3VQB.mjs} +15 -1
- package/dist/{chunk-Z6SFHN6T.mjs → chunk-NJG7DHVF.mjs} +1 -1
- package/dist/{chunk-RYZC432S.mjs → chunk-NLZY4TXU.mjs} +1 -1
- package/dist/{chunk-ZZ2R6KZ3.mjs → chunk-OLVJFKXS.mjs} +1 -1
- package/dist/{chunk-AJ7ZDNBT.mjs → chunk-QDAZGZUF.mjs} +4 -3
- package/dist/{chunk-JT7HKXRB.mjs → chunk-QOLWA2PW.mjs} +1 -1
- package/dist/{chunk-WYEUNUTP.mjs → chunk-QXDGGOLC.mjs} +38 -25
- package/dist/{chunk-JMOZEC77.mjs → chunk-RJNLAH76.mjs} +1 -1
- package/dist/{chunk-WF2XDFRK.mjs → chunk-RMRS44MQ.mjs} +1 -1
- package/dist/chunk-SAWUXP3A.mjs +1114 -0
- package/dist/{chunk-OB4JUQ3O.mjs → chunk-TS7DGUIR.mjs} +1 -1
- package/dist/{chunk-AV4EMIRH.mjs → chunk-UBUXUMER.mjs} +1 -1
- package/dist/{chunk-IRRY3CRZ.mjs → chunk-ULGNQPNE.mjs} +1 -1
- package/dist/{chunk-7LWRKMF5.mjs → chunk-UNNRUJTM.mjs} +1 -1
- package/dist/{chunk-TB6SD2FT.mjs → chunk-UQ4742ET.mjs} +1 -1
- package/dist/{chunk-MX6HRKMI.mjs → chunk-VJBUCITV.mjs} +1 -1
- package/dist/{chunk-2UYENBLV.mjs → chunk-YMYIEVZP.mjs} +1 -1
- package/dist/{chunk-SOYNZDVY.mjs → chunk-YTXRIXNZ.mjs} +8 -1
- package/dist/{chunk-YFZ3ELX5.mjs → chunk-ZIMY2QUM.mjs} +2 -2
- package/dist/{chunk-Z4VHZ7B5.mjs → chunk-ZR6HSEAB.mjs} +1 -1
- package/dist/fonts.d.mts +1 -7
- package/dist/fonts.d.ts +1 -7
- package/dist/fonts.js +0 -2
- package/dist/fonts.mjs +1 -2
- package/dist/{index-wt-orHUi.d.ts → index-CY34hxPN.d.mts} +1 -0
- package/dist/{index-wt-orHUi.d.mts → index-CY34hxPN.d.ts} +1 -0
- package/dist/index.d.mts +7 -3
- package/dist/index.d.ts +7 -3
- package/dist/index.js +1517 -761
- package/dist/index.mjs +54 -52
- package/package.json +3 -3
- package/src/components/Accordion/Accordion.tsx +20 -0
- package/src/components/Button/Button.tsx +29 -26
- package/src/components/ConfirmDialog/ConfirmDialog.tsx +47 -31
- package/src/components/CurrencyInput/CurrencyInput.tsx +4 -7
- package/src/components/IconPicker/IconPicker.tsx +124 -112
- package/src/components/ImageUpload/ImageUpload.tsx +10 -3
- package/src/components/ListItem/ListItem.tsx +43 -28
- package/src/components/Pressable/Pressable.tsx +20 -8
- package/src/components/SelectableCard/SelectableCard.tsx +304 -0
- package/src/components/SelectableCard/index.ts +1 -0
- package/src/components/Sheet/Sheet.tsx +72 -173
- package/src/components/Skeleton/Skeleton.tsx +5 -2
- package/src/components/Stats/Stats.tsx +254 -0
- package/src/components/Stats/index.ts +2 -0
- package/src/components/Text/Text.tsx +4 -2
- package/src/fonts.ts +0 -7
- package/src/index.ts +5 -0
- package/src/theme/colorUtils.ts +9 -0
- package/src/theme/colors.ts +7 -0
- package/src/theme/types.ts +4 -1
- package/src/utils/curatedIcons.ts +698 -135
- package/src/utils/fontGuard.ts +2 -1
- package/dist/chunk-53Z3NYGE.mjs +0 -742
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React, { useRef, useState, useCallback,
|
|
1
|
+
import React, { useRef, useState, useCallback, useMemo, useId } from 'react'
|
|
2
2
|
import { View, Text, TouchableOpacity, StyleSheet, ViewStyle, Dimensions } from 'react-native'
|
|
3
3
|
import { ScrollView } from 'react-native-gesture-handler'
|
|
4
4
|
import {
|
|
@@ -13,6 +13,7 @@ import { CURATED_ICONS, ALL_CURATED_ICONS } from '../../utils/curatedIcons'
|
|
|
13
13
|
import { selectionAsync as hapticSelection, impactMedium } from '../../utils/haptics'
|
|
14
14
|
import { s, vs, ms } from '../../utils/scaling'
|
|
15
15
|
import { RADIUS } from '../../tokens'
|
|
16
|
+
import { Spinner } from '../Spinner'
|
|
16
17
|
import type { BottomSheetBackdropProps, BottomSheetModal as BottomSheetModalType } from '@gorhom/bottom-sheet'
|
|
17
18
|
|
|
18
19
|
const NUM_COLUMNS = 6
|
|
@@ -74,10 +75,10 @@ export function IconPicker({
|
|
|
74
75
|
const { colors } = useTheme()
|
|
75
76
|
const insets = useSafeAreaInsets()
|
|
76
77
|
const sheetRef = useRef<BottomSheetModalType>(null)
|
|
77
|
-
const catScrollRef = useRef<
|
|
78
|
-
const [open, setOpen] = useState(false)
|
|
78
|
+
const catScrollRef = useRef<ScrollView>(null)
|
|
79
79
|
const [activeCategory, setActiveCategory] = useState<string | null>(null)
|
|
80
80
|
const [containerWidth, setContainerWidth] = useState(() => Dimensions.get('window').width - s(16) * 2)
|
|
81
|
+
const [ready, setReady] = useState(false)
|
|
81
82
|
|
|
82
83
|
const sheetName = useId()
|
|
83
84
|
|
|
@@ -101,35 +102,26 @@ export function IconPicker({
|
|
|
101
102
|
return result
|
|
102
103
|
}, [activeIcons, numColumns])
|
|
103
104
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
} else {
|
|
109
|
-
sheetRef.current?.dismiss()
|
|
110
|
-
}
|
|
111
|
-
}, [open])
|
|
105
|
+
const handleDismiss = useCallback(() => {
|
|
106
|
+
setActiveCategory(null)
|
|
107
|
+
setReady(false)
|
|
108
|
+
}, [])
|
|
112
109
|
|
|
113
110
|
const handleSelect = useCallback(
|
|
114
111
|
(iconName: string) => {
|
|
115
112
|
onChange(iconName)
|
|
116
|
-
setOpen(false)
|
|
117
|
-
setActiveCategory(null)
|
|
118
113
|
},
|
|
119
114
|
[onChange],
|
|
120
115
|
)
|
|
121
116
|
|
|
122
117
|
const handleOpen = useCallback(() => {
|
|
123
118
|
if (disabled) return
|
|
119
|
+
impactMedium()
|
|
124
120
|
setActiveCategory(null)
|
|
125
|
-
|
|
121
|
+
setReady(false)
|
|
122
|
+
sheetRef.current?.present()
|
|
126
123
|
}, [disabled])
|
|
127
124
|
|
|
128
|
-
const handleClose = useCallback(() => {
|
|
129
|
-
setOpen(false)
|
|
130
|
-
setActiveCategory(null)
|
|
131
|
-
}, [])
|
|
132
|
-
|
|
133
125
|
const renderBackdrop = useCallback(
|
|
134
126
|
(props: BottomSheetBackdropProps) => (
|
|
135
127
|
<BottomSheetBackdrop {...props} disappearsOnIndex={-1} appearsOnIndex={0} pressBehavior="close" />
|
|
@@ -185,12 +177,12 @@ export function IconPicker({
|
|
|
185
177
|
<BottomSheetModal
|
|
186
178
|
ref={sheetRef}
|
|
187
179
|
name={sheetName}
|
|
188
|
-
onDismiss={
|
|
180
|
+
onDismiss={handleDismiss}
|
|
189
181
|
enableDynamicSizing={true}
|
|
190
182
|
maxDynamicContentSize={SCREEN_HEIGHT * 0.7}
|
|
191
183
|
backdropComponent={renderBackdrop}
|
|
192
|
-
backgroundStyle={
|
|
193
|
-
handleIndicatorStyle={
|
|
184
|
+
backgroundStyle={{ ...styles.sheetBackground, backgroundColor: colors.card }}
|
|
185
|
+
handleIndicatorStyle={{ ...styles.handle, backgroundColor: colors.border }}
|
|
194
186
|
enablePanDownToClose
|
|
195
187
|
topInset={insets.top}
|
|
196
188
|
android_keyboardInputMode="adjustPan"
|
|
@@ -199,103 +191,118 @@ export function IconPicker({
|
|
|
199
191
|
contentContainerStyle={styles.sheetContent}
|
|
200
192
|
showsVerticalScrollIndicator={true}
|
|
201
193
|
>
|
|
202
|
-
{/*
|
|
203
|
-
<
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
ref={catScrollRef}
|
|
210
|
-
horizontal
|
|
211
|
-
showsHorizontalScrollIndicator={false}
|
|
212
|
-
contentContainerStyle={styles.categoryStrip}
|
|
213
|
-
style={styles.categoryScroll}
|
|
194
|
+
{/* Measuring container — always rendered so onLayout fires */}
|
|
195
|
+
<View
|
|
196
|
+
style={styles.gridContainer}
|
|
197
|
+
onLayout={(e) => {
|
|
198
|
+
setContainerWidth(e.nativeEvent.layout.width)
|
|
199
|
+
setReady(true)
|
|
200
|
+
}}
|
|
214
201
|
>
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
touchSoundDisabled={true}
|
|
219
|
-
accessibilityRole="button"
|
|
220
|
-
accessibilityLabel="Todos"
|
|
221
|
-
accessibilityState={{ selected: activeCategory === null }}
|
|
222
|
-
style={[
|
|
223
|
-
styles.categoryChip,
|
|
224
|
-
{
|
|
225
|
-
backgroundColor: activeCategory === null ? colors.primary : colors.surface,
|
|
226
|
-
borderColor: activeCategory === null ? colors.primary : colors.border,
|
|
227
|
-
},
|
|
228
|
-
]}
|
|
229
|
-
>
|
|
230
|
-
<View style={styles.categoryChipInner}>
|
|
231
|
-
{renderIcon('grid', ms(14), activeCategory === null ? colors.primaryForeground : colors.foregroundSubtle)}
|
|
232
|
-
<Text
|
|
233
|
-
style={[
|
|
234
|
-
styles.categoryChipText,
|
|
235
|
-
{ color: activeCategory === null ? colors.primaryForeground : colors.foreground },
|
|
236
|
-
]}
|
|
237
|
-
allowFontScaling={true}
|
|
238
|
-
numberOfLines={1}
|
|
239
|
-
>
|
|
240
|
-
Todos
|
|
241
|
-
</Text>
|
|
202
|
+
{!ready ? (
|
|
203
|
+
<View style={styles.loader}>
|
|
204
|
+
<Spinner size="md" color={colors.primary} label="Cargando iconos..." />
|
|
242
205
|
</View>
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
{
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
206
|
+
) : (
|
|
207
|
+
<>
|
|
208
|
+
{/* Category section label */}
|
|
209
|
+
<Text style={[styles.sectionLabel, { color: colors.foregroundSubtle }]} allowFontScaling={true}>
|
|
210
|
+
Categorías
|
|
211
|
+
</Text>
|
|
212
|
+
|
|
213
|
+
{/* Horizontal scrollable category chips */}
|
|
214
|
+
<ScrollView
|
|
215
|
+
ref={catScrollRef}
|
|
216
|
+
horizontal
|
|
217
|
+
showsHorizontalScrollIndicator={false}
|
|
218
|
+
contentContainerStyle={styles.categoryStrip}
|
|
219
|
+
style={styles.categoryScroll}
|
|
220
|
+
>
|
|
221
|
+
<TouchableOpacity
|
|
222
|
+
onPress={() => setActiveCategory(null)}
|
|
223
|
+
activeOpacity={0.7}
|
|
224
|
+
touchSoundDisabled={true}
|
|
225
|
+
accessibilityRole="button"
|
|
226
|
+
accessibilityLabel="Todos"
|
|
227
|
+
accessibilityState={{ selected: activeCategory === null }}
|
|
264
228
|
style={[
|
|
265
|
-
styles.
|
|
266
|
-
{
|
|
229
|
+
styles.categoryChip,
|
|
230
|
+
{
|
|
231
|
+
backgroundColor: activeCategory === null ? colors.primary : colors.surface,
|
|
232
|
+
borderColor: activeCategory === null ? colors.primary : colors.border,
|
|
233
|
+
},
|
|
267
234
|
]}
|
|
268
|
-
allowFontScaling={true}
|
|
269
|
-
numberOfLines={1}
|
|
270
235
|
>
|
|
271
|
-
{
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
236
|
+
<View style={styles.categoryChipInner}>
|
|
237
|
+
{renderIcon('grid', ms(14), activeCategory === null ? colors.primaryForeground : colors.foregroundSubtle)}
|
|
238
|
+
<Text
|
|
239
|
+
style={[
|
|
240
|
+
styles.categoryChipText,
|
|
241
|
+
{ color: activeCategory === null ? colors.primaryForeground : colors.foreground },
|
|
242
|
+
]}
|
|
243
|
+
allowFontScaling={true}
|
|
244
|
+
numberOfLines={1}
|
|
245
|
+
>
|
|
246
|
+
Todos
|
|
247
|
+
</Text>
|
|
248
|
+
</View>
|
|
249
|
+
</TouchableOpacity>
|
|
250
|
+
{CURATED_ICONS.map((cat) => (
|
|
251
|
+
<TouchableOpacity
|
|
252
|
+
key={cat.name}
|
|
253
|
+
onPress={() => setActiveCategory(cat.name)}
|
|
254
|
+
activeOpacity={0.7}
|
|
255
|
+
touchSoundDisabled={true}
|
|
256
|
+
accessibilityRole="button"
|
|
257
|
+
accessibilityLabel={cat.labelEs}
|
|
258
|
+
accessibilityState={{ selected: activeCategory === cat.name }}
|
|
259
|
+
style={[
|
|
260
|
+
styles.categoryChip,
|
|
261
|
+
{
|
|
262
|
+
backgroundColor: activeCategory === cat.name ? colors.primary : colors.surface,
|
|
263
|
+
borderColor: activeCategory === cat.name ? colors.primary : colors.border,
|
|
264
|
+
},
|
|
265
|
+
]}
|
|
266
|
+
>
|
|
267
|
+
<View style={styles.categoryChipInner}>
|
|
268
|
+
{renderIcon(cat.categoryIcon, ms(14), activeCategory === cat.name ? colors.primaryForeground : colors.foregroundSubtle)}
|
|
269
|
+
<Text
|
|
270
|
+
style={[
|
|
271
|
+
styles.categoryChipText,
|
|
272
|
+
{ color: activeCategory === cat.name ? colors.primaryForeground : colors.foreground },
|
|
273
|
+
]}
|
|
274
|
+
allowFontScaling={true}
|
|
275
|
+
numberOfLines={1}
|
|
276
|
+
>
|
|
277
|
+
{cat.labelEs}
|
|
278
|
+
</Text>
|
|
279
|
+
</View>
|
|
280
|
+
</TouchableOpacity>
|
|
281
|
+
))}
|
|
282
|
+
</ScrollView>
|
|
277
283
|
|
|
278
|
-
|
|
279
|
-
|
|
284
|
+
{/* Separator */}
|
|
285
|
+
<View style={[styles.separator, { backgroundColor: colors.border }]} />
|
|
280
286
|
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
287
|
+
{/* Icon grid */}
|
|
288
|
+
{cellSize > 0 ? rows.map((row, i) => (
|
|
289
|
+
<View key={row[0] ?? `row-${i}`} style={[styles.row, { marginBottom: gapPx }]}>
|
|
290
|
+
{row.map((name) => (
|
|
291
|
+
<IconCellMemo
|
|
292
|
+
key={name}
|
|
293
|
+
name={name}
|
|
294
|
+
selected={value === name}
|
|
295
|
+
size={cellSize}
|
|
296
|
+
onPress={() => { handleSelect(name); sheetRef.current?.dismiss() }}
|
|
297
|
+
/>
|
|
298
|
+
))}
|
|
299
|
+
{Array.from({ length: numColumns - row.length }).map((_, j) => (
|
|
300
|
+
<View key={`spacer-${j}`} style={{ width: cellSize, height: cellSize }} />
|
|
301
|
+
))}
|
|
302
|
+
</View>
|
|
303
|
+
)) : null}
|
|
304
|
+
</>
|
|
305
|
+
)}
|
|
299
306
|
</View>
|
|
300
307
|
</BottomSheetScrollView>
|
|
301
308
|
</BottomSheetModal>
|
|
@@ -380,4 +387,9 @@ const styles = StyleSheet.create({
|
|
|
380
387
|
alignItems: 'center',
|
|
381
388
|
justifyContent: 'center',
|
|
382
389
|
},
|
|
390
|
+
loader: {
|
|
391
|
+
minHeight: vs(200),
|
|
392
|
+
alignItems: 'center',
|
|
393
|
+
justifyContent: 'center',
|
|
394
|
+
},
|
|
383
395
|
})
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React from 'react'
|
|
1
|
+
import React, { useState } from 'react'
|
|
2
2
|
import { View, Text, Image, StyleSheet, ViewStyle, Platform } from 'react-native'
|
|
3
3
|
import { Feather } from '@expo/vector-icons'
|
|
4
4
|
import { impactLight } from '../../utils/haptics'
|
|
@@ -27,6 +27,8 @@ export interface ImageUploadProps {
|
|
|
27
27
|
borderRadius?: number
|
|
28
28
|
/** Aspect ratio for the selected image. Defaults to 'cover'. */
|
|
29
29
|
resizeMode?: 'cover' | 'contain' | 'stretch'
|
|
30
|
+
/** Whether to allow the user to crop the image after selecting. Defaults to true. */
|
|
31
|
+
allowsEditing?: boolean
|
|
30
32
|
disabled?: boolean
|
|
31
33
|
style?: ViewStyle
|
|
32
34
|
accessibilityLabel?: string
|
|
@@ -42,11 +44,13 @@ export function ImageUpload({
|
|
|
42
44
|
height = 200,
|
|
43
45
|
borderRadius = RADIUS.lg,
|
|
44
46
|
resizeMode = 'cover',
|
|
47
|
+
allowsEditing = true,
|
|
45
48
|
disabled = false,
|
|
46
49
|
style,
|
|
47
50
|
accessibilityLabel,
|
|
48
51
|
}: ImageUploadProps) {
|
|
49
52
|
const { colors } = useTheme()
|
|
53
|
+
const [imageLoaded, setImageLoaded] = useState(false)
|
|
50
54
|
|
|
51
55
|
const handlePress = async () => {
|
|
52
56
|
if (disabled || loading) return
|
|
@@ -81,11 +85,12 @@ export function ImageUpload({
|
|
|
81
85
|
|
|
82
86
|
const result = await picker.launchImageLibraryAsync({
|
|
83
87
|
mediaTypes: ['images'],
|
|
84
|
-
allowsEditing
|
|
88
|
+
allowsEditing,
|
|
85
89
|
quality: 0.8,
|
|
86
90
|
})
|
|
87
91
|
|
|
88
92
|
if (!result.canceled && result.assets?.[0]) {
|
|
93
|
+
setImageLoaded(false)
|
|
89
94
|
onChange?.(result.assets[0].uri)
|
|
90
95
|
}
|
|
91
96
|
}
|
|
@@ -97,7 +102,7 @@ export function ImageUpload({
|
|
|
97
102
|
borderWidth: value ? 0 : 1,
|
|
98
103
|
borderStyle: 'dashed',
|
|
99
104
|
borderColor: colors.border,
|
|
100
|
-
backgroundColor: value ? 'transparent' : colors.surface,
|
|
105
|
+
backgroundColor: (value && imageLoaded) ? 'transparent' : colors.surface,
|
|
101
106
|
overflow: 'hidden',
|
|
102
107
|
}
|
|
103
108
|
|
|
@@ -117,6 +122,8 @@ export function ImageUpload({
|
|
|
117
122
|
source={{ uri: value }}
|
|
118
123
|
style={[StyleSheet.absoluteFillObject, { borderRadius }]}
|
|
119
124
|
resizeMode={resizeMode}
|
|
125
|
+
onLoad={() => setImageLoaded(true)}
|
|
126
|
+
onError={() => setImageLoaded(true)}
|
|
120
127
|
/>
|
|
121
128
|
) : (
|
|
122
129
|
<View style={styles.placeholder}>
|
|
@@ -31,13 +31,13 @@ export interface ListItemProps {
|
|
|
31
31
|
leftRender?: React.ReactNode
|
|
32
32
|
/**
|
|
33
33
|
* Arbitrary content rendered on the right (badge, price, chevron, switch, etc.).
|
|
34
|
-
* Replaces the old `trailing` prop (still accepted as an alias).
|
|
35
34
|
*/
|
|
36
35
|
rightRender?: React.ReactNode | string
|
|
37
|
-
/**
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
36
|
+
/**
|
|
37
|
+
* Multiple action buttons rendered on the right with automatic gap.
|
|
38
|
+
* Takes precedence over `rightRender` and `showChevron`.
|
|
39
|
+
*/
|
|
40
|
+
rightActions?: React.ReactNode[]
|
|
41
41
|
/**
|
|
42
42
|
* Icon name from `@expo/vector-icons` rendered in the left slot.
|
|
43
43
|
* See https://icons.expo.fyi. Takes precedence over `leftRender`.
|
|
@@ -65,7 +65,7 @@ export interface ListItemProps {
|
|
|
65
65
|
*/
|
|
66
66
|
variant?: ListItemVariant
|
|
67
67
|
|
|
68
|
-
/** Show a right-pointing chevron on the far right. Ignored when `rightRender` / `
|
|
68
|
+
/** Show a right-pointing chevron on the far right. Ignored when `rightRender` / `rightActions` / `rightIcon` is set. */
|
|
69
69
|
showChevron?: boolean
|
|
70
70
|
|
|
71
71
|
/** Visual separator line at the bottom of the item. Useful when rendering multiple plain items in a list. */
|
|
@@ -91,8 +91,7 @@ function ListItemBase({
|
|
|
91
91
|
imageSource,
|
|
92
92
|
leftRender,
|
|
93
93
|
rightRender,
|
|
94
|
-
|
|
95
|
-
icon,
|
|
94
|
+
rightActions,
|
|
96
95
|
leftIcon,
|
|
97
96
|
rightIcon,
|
|
98
97
|
leftIconColor,
|
|
@@ -119,16 +118,14 @@ function ListItemBase({
|
|
|
119
118
|
onPress?.()
|
|
120
119
|
}
|
|
121
120
|
|
|
122
|
-
// imageSource takes precedence, then leftIcon, then leftRender
|
|
121
|
+
// imageSource takes precedence, then leftIcon, then leftRender
|
|
123
122
|
const effectiveLeft: React.ReactNode = imageSource
|
|
124
123
|
? <Image source={imageSource} style={styles.image} />
|
|
125
124
|
: leftIcon
|
|
126
125
|
? renderIcon(leftIcon, 24, leftIconColor ?? colors.foreground)
|
|
127
|
-
: leftRender
|
|
126
|
+
: leftRender
|
|
128
127
|
|
|
129
|
-
const
|
|
130
|
-
? renderIcon(rightIcon, 24, rightIconColor ?? colors.foregroundMuted)
|
|
131
|
-
: rightRender ?? trailing
|
|
128
|
+
const hasRightContent = !!(rightIcon || (rightActions && rightActions.length > 0) || rightRender !== undefined || showChevron)
|
|
132
129
|
|
|
133
130
|
const cardStyle: ViewStyle =
|
|
134
131
|
variant === 'card'
|
|
@@ -181,21 +178,33 @@ function ListItemBase({
|
|
|
181
178
|
) : null}
|
|
182
179
|
</View>
|
|
183
180
|
|
|
184
|
-
{
|
|
185
|
-
|
|
186
|
-
{
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
181
|
+
{hasRightContent ? (
|
|
182
|
+
rightIcon ? (
|
|
183
|
+
<View style={styles.rightContainer}>
|
|
184
|
+
{renderIcon(rightIcon, 24, rightIconColor ?? colors.foregroundMuted)}
|
|
185
|
+
</View>
|
|
186
|
+
) : rightActions && rightActions.length > 0 ? (
|
|
187
|
+
<View style={styles.rightActionsContainer}>
|
|
188
|
+
{rightActions.map((action, i) => (
|
|
189
|
+
<React.Fragment key={i}>{action}</React.Fragment>
|
|
190
|
+
))}
|
|
191
|
+
</View>
|
|
192
|
+
) : rightRender !== undefined ? (
|
|
193
|
+
<View style={styles.rightContainer}>
|
|
194
|
+
{typeof rightRender === 'string' ? (
|
|
195
|
+
<Text
|
|
196
|
+
style={[styles.rightText, { color: colors.foregroundMuted }]}
|
|
197
|
+
allowFontScaling={true}
|
|
198
|
+
>
|
|
199
|
+
{rightRender}
|
|
200
|
+
</Text>
|
|
201
|
+
) : (
|
|
202
|
+
rightRender
|
|
203
|
+
)}
|
|
204
|
+
</View>
|
|
205
|
+
) : showChevron ? (
|
|
206
|
+
<Entypo name="chevron-with-circle-right" size={20} color={colors.foregroundMuted} />
|
|
207
|
+
) : null
|
|
199
208
|
) : null}
|
|
200
209
|
</>
|
|
201
210
|
)
|
|
@@ -283,6 +292,12 @@ const styles = StyleSheet.create({
|
|
|
283
292
|
flexShrink: 0,
|
|
284
293
|
maxWidth: s(160),
|
|
285
294
|
},
|
|
295
|
+
rightActionsContainer: {
|
|
296
|
+
flexDirection: 'row',
|
|
297
|
+
alignItems: 'center',
|
|
298
|
+
gap: s(8),
|
|
299
|
+
flexShrink: 0,
|
|
300
|
+
},
|
|
286
301
|
rightText: {
|
|
287
302
|
fontFamily: 'Sohne-Regular',
|
|
288
303
|
fontSize: ms(14),
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React from 'react'
|
|
2
|
-
import { ViewStyle } from 'react-native'
|
|
2
|
+
import { ViewStyle, type AccessibilityRole } from 'react-native'
|
|
3
3
|
import { impactLight } from '../../utils/haptics'
|
|
4
4
|
import { PressableCard } from '../../utils/pressable'
|
|
5
5
|
import { PRESS_SCALE } from '../../utils/animations'
|
|
@@ -11,11 +11,6 @@ export interface PressableProps {
|
|
|
11
11
|
onPress?: () => void
|
|
12
12
|
/** Scale value on press. Defaults to `0.98` (MediaCard-style). */
|
|
13
13
|
pressScale?: number
|
|
14
|
-
/**
|
|
15
|
-
* @deprecated Use Reanimated spring config via `pressOutSpring` instead. Ignored.
|
|
16
|
-
* Kept for backwards compatibility with v6.x consumers.
|
|
17
|
-
*/
|
|
18
|
-
bounciness?: number
|
|
19
14
|
/** Enable haptic feedback on press. Defaults to `true`. */
|
|
20
15
|
haptics?: boolean
|
|
21
16
|
/** Additional style for the wrapper. */
|
|
@@ -24,6 +19,19 @@ export interface PressableProps {
|
|
|
24
19
|
disabled?: boolean
|
|
25
20
|
/** Hover scale (web only). Defaults to `1.02`. Set to `1` to disable. */
|
|
26
21
|
hoverScale?: number
|
|
22
|
+
/**
|
|
23
|
+
* Accessibility role for the pressable element.
|
|
24
|
+
* Defaults to `"button"`.
|
|
25
|
+
*/
|
|
26
|
+
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
|
+
accessibilityState?: Record<string, unknown>
|
|
33
|
+
/** Accessibility label for screen readers. */
|
|
34
|
+
accessibilityLabel?: string
|
|
27
35
|
}
|
|
28
36
|
|
|
29
37
|
/**
|
|
@@ -41,6 +49,9 @@ export function Pressable({
|
|
|
41
49
|
style,
|
|
42
50
|
disabled,
|
|
43
51
|
hoverScale: _hoverScale = 1.02,
|
|
52
|
+
accessibilityRole,
|
|
53
|
+
accessibilityState,
|
|
54
|
+
accessibilityLabel,
|
|
44
55
|
}: PressableProps) {
|
|
45
56
|
const handlePress = () => {
|
|
46
57
|
if (disabled || !onPress) return
|
|
@@ -56,8 +67,9 @@ export function Pressable({
|
|
|
56
67
|
rippleColor="transparent"
|
|
57
68
|
touchSoundDisabled
|
|
58
69
|
activateOnHover
|
|
59
|
-
accessibilityRole=
|
|
60
|
-
accessibilityState={{ disabled: !!disabled }}
|
|
70
|
+
accessibilityRole={accessibilityRole ?? 'button'}
|
|
71
|
+
accessibilityState={accessibilityState ?? { disabled: !!disabled }}
|
|
72
|
+
accessibilityLabel={accessibilityLabel}
|
|
61
73
|
>
|
|
62
74
|
{children}
|
|
63
75
|
</PressableCard>
|