@retray-dev/ui-kit 12.2.0 → 13.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 +85 -143
- package/CONSUMER.md +2 -2
- package/DESIGN.md +2 -2
- package/README.md +11 -6
- package/dist/Accordion.js +48 -210
- package/dist/Accordion.mjs +6 -5
- package/dist/AlertBanner.js +29 -153
- package/dist/AlertBanner.mjs +3 -3
- package/dist/AppHeader.js +37 -235
- package/dist/AppHeader.mjs +6 -7
- package/dist/Avatar.d.mts +17 -1
- package/dist/Avatar.d.ts +17 -1
- package/dist/Avatar.js +80 -115
- package/dist/Avatar.mjs +2 -2
- package/dist/Badge.js +24 -149
- package/dist/Badge.mjs +3 -3
- package/dist/Button.js +79 -267
- package/dist/Button.mjs +6 -6
- package/dist/Card.js +15 -200
- package/dist/Card.mjs +4 -5
- package/dist/CategoryStrip.d.mts +0 -5
- package/dist/CategoryStrip.d.ts +0 -5
- package/dist/CategoryStrip.js +47 -265
- package/dist/CategoryStrip.mjs +6 -6
- package/dist/Checkbox.js +15 -200
- package/dist/Checkbox.mjs +5 -5
- package/dist/Chip.js +44 -236
- package/dist/Chip.mjs +7 -6
- package/dist/ConfirmDialog.js +84 -286
- package/dist/ConfirmDialog.mjs +7 -7
- package/dist/CurrencyDisplay.js +1 -114
- package/dist/CurrencyDisplay.mjs +2 -2
- package/dist/CurrencyInput.js +35 -162
- package/dist/CurrencyInput.mjs +5 -5
- package/dist/DetailRow.js +25 -150
- package/dist/DetailRow.mjs +3 -3
- package/dist/EmptyState.js +80 -268
- package/dist/EmptyState.mjs +7 -7
- package/dist/ErrorBoundary.js +32 -199
- package/dist/ErrorBoundary.mjs +4 -4
- package/dist/Form.js +1 -114
- package/dist/Form.mjs +2 -2
- package/dist/HolographicCard.d.mts +0 -28
- package/dist/HolographicCard.d.ts +0 -28
- package/dist/HolographicCard.js +20 -130
- package/dist/HolographicCard.mjs +9 -32
- package/dist/IconButton.js +36 -234
- package/dist/IconButton.mjs +5 -6
- package/dist/IconPicker.js +222 -929
- package/dist/IconPicker.mjs +5 -5
- package/dist/ImageUpload.d.mts +3 -1
- package/dist/ImageUpload.d.ts +3 -1
- package/dist/ImageUpload.js +25 -215
- package/dist/ImageUpload.mjs +5 -6
- package/dist/ImageViewer.js +75 -266
- package/dist/ImageViewer.mjs +8 -8
- package/dist/Input.d.mts +1 -1
- package/dist/Input.d.ts +1 -1
- package/dist/Input.js +35 -162
- package/dist/Input.mjs +4 -4
- package/dist/LabelValue.js +24 -149
- package/dist/LabelValue.mjs +3 -3
- package/dist/ListGroup.js +1 -114
- package/dist/ListGroup.mjs +2 -2
- package/dist/ListItem.js +38 -235
- package/dist/ListItem.mjs +5 -6
- package/dist/MediaCard.d.mts +0 -14
- package/dist/MediaCard.d.ts +0 -14
- package/dist/MediaCard.js +69 -315
- package/dist/MediaCard.mjs +5 -6
- package/dist/MenuGroup.js +1 -114
- package/dist/MenuGroup.mjs +2 -2
- package/dist/MenuItem.js +36 -234
- package/dist/MenuItem.mjs +5 -6
- package/dist/MonthPicker.js +8 -163
- package/dist/MonthPicker.mjs +3 -3
- package/dist/NumberStepper.js +40 -238
- package/dist/NumberStepper.mjs +5 -6
- package/dist/PagerDots.d.mts +1 -1
- package/dist/PagerDots.d.ts +1 -1
- package/dist/PagerDots.js +69 -224
- package/dist/PagerDots.mjs +6 -5
- package/dist/Pressable.js +14 -85
- package/dist/Pressable.mjs +4 -4
- package/dist/PricingCard.js +87 -272
- package/dist/PricingCard.mjs +8 -8
- package/dist/Progress.js +3 -123
- package/dist/Progress.mjs +3 -3
- package/dist/RadioGroup.js +52 -265
- package/dist/RadioGroup.mjs +5 -5
- package/dist/RetrayProvider.js +3 -6
- package/dist/RetrayProvider.mjs +3 -3
- package/dist/Select.d.mts +2 -1
- package/dist/Select.d.ts +2 -1
- package/dist/Select.js +24 -232
- package/dist/Select.mjs +4 -5
- package/dist/SelectableCard.js +33 -209
- package/dist/SelectableCard.mjs +5 -5
- package/dist/SelectableGrid.d.mts +0 -21
- package/dist/SelectableGrid.d.ts +0 -21
- package/dist/SelectableGrid.js +49 -271
- package/dist/SelectableGrid.mjs +5 -6
- package/dist/Separator.js +1 -114
- package/dist/Separator.mjs +2 -2
- package/dist/Sheet.js +7 -162
- package/dist/Sheet.mjs +3 -3
- package/dist/SheetSelect.js +39 -236
- package/dist/SheetSelect.mjs +6 -6
- package/dist/Skeleton.js +4 -124
- package/dist/Skeleton.mjs +3 -3
- package/dist/Slider.js +6 -161
- package/dist/Slider.mjs +3 -3
- package/dist/Spinner.js +3 -116
- package/dist/Spinner.mjs +2 -2
- package/dist/Stats.js +36 -234
- package/dist/Stats.mjs +5 -6
- package/dist/Switch.js +24 -175
- package/dist/Switch.mjs +5 -4
- package/dist/TabBar.js +43 -200
- package/dist/TabBar.mjs +5 -4
- package/dist/Tabs.js +15 -199
- package/dist/Tabs.mjs +5 -5
- package/dist/Text.js +9 -130
- package/dist/Text.mjs +2 -2
- package/dist/Textarea.d.mts +2 -1
- package/dist/Textarea.d.ts +2 -1
- package/dist/Textarea.js +71 -219
- package/dist/Textarea.mjs +4 -4
- package/dist/Toast.js +1 -114
- package/dist/Toast.mjs +2 -2
- package/dist/Toggle.js +39 -236
- package/dist/Toggle.mjs +6 -6
- package/dist/{chunk-M3C7XM2M.mjs → chunk-2QOHHBJC.mjs} +3 -3
- package/dist/{chunk-LIS6I5UP.mjs → chunk-2VIDP72N.mjs} +3 -3
- package/dist/{chunk-DF7JA72E.mjs → chunk-4NQFTHN3.mjs} +13 -7
- package/dist/{chunk-UBUXUMER.mjs → chunk-4ZO5PTKF.mjs} +4 -4
- package/dist/{chunk-3XCFYSX4.mjs → chunk-5MYNAAFE.mjs} +13 -17
- package/dist/{chunk-E7NEHHXV.mjs → chunk-62BBSSUF.mjs} +3 -3
- package/dist/{chunk-MVMGPZN6.mjs → chunk-6CR4S6W2.mjs} +3 -3
- package/dist/{chunk-EDLCGYIO.mjs → chunk-6QLBHUEG.mjs} +8 -7
- package/dist/chunk-ARONDO7M.mjs +40 -0
- package/dist/{chunk-GH67YXG6.mjs → chunk-AZV7KNJI.mjs} +3 -3
- package/dist/{chunk-RMRS44MQ.mjs → chunk-BTUW5LSG.mjs} +11 -8
- package/dist/{chunk-2P2CB235.mjs → chunk-BULKGOIZ.mjs} +7 -8
- package/dist/{chunk-NHDI3VQB.mjs → chunk-CBIZLRYH.mjs} +15 -12
- package/dist/chunk-CM2DG4MR.mjs +142 -0
- package/dist/{chunk-TS7DGUIR.mjs → chunk-DBHSUUKU.mjs} +2 -2
- package/dist/{chunk-57V2LXCK.mjs → chunk-DE25XTVQ.mjs} +3 -3
- package/dist/{chunk-UQ4742ET.mjs → chunk-E4EQSCKR.mjs} +5 -5
- package/dist/{chunk-GUTDFUNF.mjs → chunk-EHGBHFMH.mjs} +9 -17
- package/dist/{chunk-CF27NBXO.mjs → chunk-EROPDCB5.mjs} +16 -24
- package/dist/{chunk-ZIMY2QUM.mjs → chunk-ERWJPVX7.mjs} +2 -2
- package/dist/{chunk-NLZY4TXU.mjs → chunk-ESQDPO5E.mjs} +7 -7
- package/dist/{chunk-VJBUCITV.mjs → chunk-EW2FIDSM.mjs} +1 -1
- package/dist/{chunk-HC4VVCWY.mjs → chunk-FTTI6T5Q.mjs} +4 -4
- package/dist/{chunk-2HFD4IHU.mjs → chunk-HUSSF6TF.mjs} +1 -1
- package/dist/chunk-IFYMBOEN.mjs +14 -0
- package/dist/{chunk-QOLWA2PW.mjs → chunk-IGU223UM.mjs} +80 -4
- package/dist/chunk-IJCMPVW5.mjs +121 -0
- package/dist/{chunk-AENAVIKT.mjs → chunk-ITG4JQM3.mjs} +4 -4
- package/dist/{chunk-E5UKLSJZ.mjs → chunk-K3QX2M26.mjs} +11 -8
- package/dist/{chunk-4OORJ2DY.mjs → chunk-K7TKID3V.mjs} +8 -7
- package/dist/{chunk-2LG326TT.mjs → chunk-KAGADD2O.mjs} +4 -4
- package/dist/{chunk-IVSRW4HS.mjs → chunk-KC5QDYGZ.mjs} +4 -4
- package/dist/{chunk-7AFZWSCI.mjs → chunk-KPTY7UYQ.mjs} +1 -1
- package/dist/{chunk-YTXRIXNZ.mjs → chunk-KSSVIFYR.mjs} +9 -12
- package/dist/chunk-L3YKPTJQ.mjs +119 -0
- package/dist/chunk-M53LC4Q7.mjs +35 -0
- package/dist/{chunk-ZR6HSEAB.mjs → chunk-MP7GLMIR.mjs} +17 -25
- package/dist/chunk-MZ6WRTD2.mjs +40 -0
- package/dist/chunk-NGEN2EES.mjs +581 -0
- package/dist/{chunk-C43HRKXH.mjs → chunk-OBV72JD4.mjs} +1 -1
- package/dist/{chunk-LPV4NJJK.mjs → chunk-PGQ6FMXS.mjs} +6 -5
- package/dist/{chunk-MEPSKGBO.mjs → chunk-PI6RULJX.mjs} +1 -1
- package/dist/{chunk-F3YTWO3T.mjs → chunk-RA6SAAFE.mjs} +9 -8
- package/dist/{chunk-UNNRUJTM.mjs → chunk-RRKM4MKB.mjs} +7 -7
- package/dist/{chunk-ULGNQPNE.mjs → chunk-S2VGME7X.mjs} +1 -1
- package/dist/{chunk-OLVJFKXS.mjs → chunk-S44XWTTC.mjs} +35 -25
- package/dist/{chunk-YMYIEVZP.mjs → chunk-SZEKQAOY.mjs} +1 -1
- package/dist/{chunk-ELGEOM7I.mjs → chunk-TETMEKZE.mjs} +9 -9
- package/dist/{chunk-BXF4AMHY.mjs → chunk-TMH263OK.mjs} +5 -4
- package/dist/{chunk-NJG7DHVF.mjs → chunk-U6DEBYU5.mjs} +10 -9
- package/dist/{chunk-RJNLAH76.mjs → chunk-UOKFSFNJ.mjs} +2 -2
- package/dist/{chunk-HEDQPK4I.mjs → chunk-URIH43IJ.mjs} +13 -21
- package/dist/{chunk-QXDGGOLC.mjs → chunk-V2ZB2XNS.mjs} +6 -6
- package/dist/{chunk-KSUWPU2F.mjs → chunk-WIPEDNSD.mjs} +7 -7
- package/dist/{chunk-QDAZGZUF.mjs → chunk-XCIG6HT2.mjs} +3 -3
- package/dist/{chunk-4J2PXL36.mjs → chunk-Y6YS33GM.mjs} +40 -38
- package/dist/{chunk-4XOB5TTD.mjs → chunk-ZKDKKQCE.mjs} +5 -5
- package/dist/{chunk-LOBLCFMN.mjs → chunk-ZTPYUU5C.mjs} +5 -5
- package/dist/index.d.mts +12 -72
- package/dist/index.d.ts +12 -72
- package/dist/index.js +1051 -1838
- package/dist/index.mjs +81 -85
- package/package.json +8 -10
- package/src/components/Accordion/Accordion.tsx +12 -9
- package/src/components/AlertBanner/AlertBanner.tsx +7 -6
- package/src/components/AppHeader/AppHeader.tsx +1 -1
- package/src/components/Avatar/Avatar.tsx +92 -1
- package/src/components/Avatar/index.ts +2 -2
- package/src/components/Badge/Badge.tsx +2 -2
- package/src/components/Button/Button.tsx +50 -46
- package/src/components/Card/Card.tsx +1 -0
- package/src/components/CategoryStrip/CategoryStrip.tsx +36 -49
- package/src/components/Chip/Chip.tsx +5 -4
- package/src/components/ConfirmDialog/ConfirmDialog.tsx +3 -3
- package/src/components/DetailRow/DetailRow.tsx +3 -3
- package/src/components/EmptyState/EmptyState.tsx +2 -2
- package/src/components/ErrorBoundary/ErrorBoundary.tsx +6 -6
- package/src/components/HolographicCard/HolographicCard.tsx +14 -95
- package/src/components/IconButton/IconButton.tsx +2 -2
- package/src/components/IconPicker/IconPicker.tsx +13 -12
- package/src/components/ImageUpload/ImageUpload.tsx +14 -25
- package/src/components/ImageViewer/ImageViewer.tsx +3 -3
- package/src/components/Input/Input.tsx +11 -5
- package/src/components/LabelValue/LabelValue.tsx +2 -2
- package/src/components/ListItem/ListItem.tsx +4 -4
- package/src/components/MediaCard/MediaCard.tsx +21 -59
- package/src/components/MenuItem/MenuItem.tsx +2 -2
- package/src/components/MonthPicker/MonthPicker.tsx +2 -2
- package/src/components/NumberStepper/NumberStepper.tsx +6 -6
- package/src/components/PagerDots/PagerDots.tsx +38 -28
- package/src/components/PricingCard/PricingCard.tsx +6 -6
- package/src/components/RadioGroup/RadioGroup.tsx +18 -31
- package/src/components/Select/Select.tsx +32 -39
- package/src/components/SelectableCard/SelectableCard.tsx +4 -6
- package/src/components/SelectableGrid/SelectableGrid.tsx +38 -72
- package/src/components/Sheet/Sheet.tsx +1 -1
- package/src/components/SheetSelect/SheetSelect.tsx +3 -3
- package/src/components/Skeleton/Skeleton.tsx +1 -1
- package/src/components/Spinner/Spinner.tsx +2 -2
- package/src/components/Stats/Stats.tsx +2 -2
- package/src/components/Switch/Switch.tsx +9 -6
- package/src/components/TabBar/TabBar.tsx +9 -8
- package/src/components/Text/Text.tsx +12 -1
- package/src/components/Textarea/Textarea.tsx +18 -32
- package/src/components/Toggle/Toggle.tsx +3 -3
- package/src/hooks/useConfirmDialog.ts +31 -42
- package/src/index.ts +3 -4
- package/src/theme/ThemeProvider.tsx +1 -4
- package/src/theme/colorUtils.ts +1 -72
- package/src/theme/colors.ts +40 -1
- package/src/theme/types.ts +2 -2
- package/src/utils/animations.ts +0 -47
- package/src/utils/curatedIcons.ts +93 -801
- package/src/utils/haptics.ts +13 -208
- package/src/utils/icons.ts +27 -91
- package/src/utils/pressable.ts +10 -61
- package/dist/VirtualList.d.mts +0 -19
- package/dist/VirtualList.d.ts +0 -19
- package/dist/VirtualList.js +0 -38
- package/dist/VirtualList.mjs +0 -2
- package/dist/chunk-2BA3JMKK.mjs +0 -136
- package/dist/chunk-3DKJ2GIC.mjs +0 -30
- package/dist/chunk-7ELGZ66G.mjs +0 -164
- package/dist/chunk-DVK4G2GT.mjs +0 -59
- package/dist/chunk-EJ7ZPXOH.mjs +0 -163
- package/dist/chunk-KA7LTET3.mjs +0 -71
- package/dist/chunk-LNPKGWBG.mjs +0 -134
- package/dist/chunk-NC5ZTR2Y.mjs +0 -32
- package/dist/chunk-SAWUXP3A.mjs +0 -1114
- package/dist/chunk-YNROWHQJ.mjs +0 -46
- package/src/components/VirtualList/VirtualList.tsx +0 -60
- package/src/components/VirtualList/index.ts +0 -1
- package/src/utils/fontGuard.ts +0 -35
- package/src/utils/hover.ts +0 -25
- package/src/utils/useColorTransition.ts +0 -40
- package/src/utils/usePressScale.ts +0 -75
|
@@ -5,7 +5,7 @@ import { impactLight } from '../../utils/haptics'
|
|
|
5
5
|
import { useTheme } from '../../theme'
|
|
6
6
|
import { s, vs, ms, mvs } from '../../utils/scaling'
|
|
7
7
|
import { RADIUS } from '../../tokens'
|
|
8
|
-
import {
|
|
8
|
+
import { Icon } from '../../utils/icons'
|
|
9
9
|
import { COLOR_TRANSITION, OPACITY_TRANSITION, SPRING_ELASTIC } from '../../utils/animations'
|
|
10
10
|
|
|
11
11
|
type SelectType = 'radio' | 'checkbox'
|
|
@@ -146,20 +146,18 @@ export function SelectableCard({
|
|
|
146
146
|
})()
|
|
147
147
|
|
|
148
148
|
const resolvedIcon = iconName
|
|
149
|
-
?
|
|
149
|
+
? <Icon name={iconName} size={ms(22)} color={disabled ? colors.foregroundMuted : colors.foregroundMuted} />
|
|
150
150
|
: icon
|
|
151
151
|
|
|
152
152
|
const resolvedIconElement = resolvedIcon ? (
|
|
153
153
|
<View style={[styles.iconWrapper, disabled && { opacity: 0.45 }]}>{resolvedIcon}</View>
|
|
154
154
|
) : null
|
|
155
155
|
|
|
156
|
-
const selectorAccessibilityRole = type === 'radio' ? 'radio' : 'checkbox'
|
|
157
|
-
|
|
158
156
|
return (
|
|
159
157
|
<Pressable
|
|
160
158
|
onPress={handlePress}
|
|
161
159
|
disabled={disabled}
|
|
162
|
-
accessibilityRole=
|
|
160
|
+
accessibilityRole={type === 'radio' ? 'radio' : 'checkbox'}
|
|
163
161
|
accessibilityLabel={`${title}${description ? `, ${description}` : ''}`}
|
|
164
162
|
accessibilityState={{ selected: isSelected, disabled }}
|
|
165
163
|
style={[
|
|
@@ -171,7 +169,7 @@ export function SelectableCard({
|
|
|
171
169
|
>
|
|
172
170
|
<View style={styles.row}>
|
|
173
171
|
{/* Selection indicator */}
|
|
174
|
-
<View style={styles.selectorContainer}
|
|
172
|
+
<View style={styles.selectorContainer}>
|
|
175
173
|
{type === 'radio' ? (
|
|
176
174
|
<EaseView
|
|
177
175
|
style={styles.radioCircle}
|
|
@@ -1,38 +1,27 @@
|
|
|
1
1
|
import React, { useState } from 'react'
|
|
2
|
-
import { View, Text,
|
|
3
|
-
import Animated from 'react-native-reanimated'
|
|
2
|
+
import { View, Text, StyleSheet, ViewStyle, ScrollView } from 'react-native'
|
|
4
3
|
import { useTheme } from '../../theme'
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
4
|
+
import { Icon } from '../../utils/icons'
|
|
5
|
+
import { PressableChip } from '../../utils/pressable'
|
|
7
6
|
import { selectionAsync as hapticSelection } from '../../utils/haptics'
|
|
8
|
-
import { PRESS_SCALE } from '../../utils/animations'
|
|
9
7
|
import { s, vs, ms, mvs } from '../../utils/scaling'
|
|
10
8
|
import { RADIUS } from '../../tokens'
|
|
11
9
|
|
|
12
10
|
export interface SelectableGridItem<T extends string | number = string> {
|
|
13
|
-
/** Unique value emitted on selection. */
|
|
14
11
|
value: T
|
|
15
|
-
/** Label rendered under the icon. */
|
|
16
12
|
label?: string
|
|
17
|
-
/** Icon name resolved via the icon registry. */
|
|
18
13
|
iconName?: string
|
|
19
|
-
/** Custom icon node — overrides `iconName`. */
|
|
20
14
|
icon?: React.ReactNode
|
|
21
15
|
disabled?: boolean
|
|
22
16
|
}
|
|
23
17
|
|
|
24
18
|
export interface SelectableGridProps<T extends string | number = string> {
|
|
25
19
|
items: SelectableGridItem<T>[]
|
|
26
|
-
/** Selected value(s). Array when `multiple`. */
|
|
27
20
|
value: T | T[] | null
|
|
28
21
|
onChange: (value: T) => void
|
|
29
|
-
/** Allow multiple selections. `value` should be an array. Defaults to false. */
|
|
30
22
|
multiple?: boolean
|
|
31
|
-
/** Columns per row. Defaults to 4. Ignored when `orientation='horizontal'`. */
|
|
32
23
|
numColumns?: number
|
|
33
|
-
/** Gap between cells (dp). Defaults to 12. */
|
|
34
24
|
gap?: number
|
|
35
|
-
/** Layout orientation. 'grid' (default) wraps into rows. 'horizontal' creates a single scrollable row. */
|
|
36
25
|
orientation?: 'grid' | 'horizontal'
|
|
37
26
|
style?: ViewStyle
|
|
38
27
|
}
|
|
@@ -51,63 +40,44 @@ interface CellProps<T extends string | number> {
|
|
|
51
40
|
|
|
52
41
|
function Cell<T extends string | number>({ item, selected, width, onPress }: CellProps<T>) {
|
|
53
42
|
const { colors } = useTheme()
|
|
54
|
-
const { animatedStyle, onPressIn, onPressOut, hoverHandlers } = usePressScale({
|
|
55
|
-
pressScale: PRESS_SCALE.chip,
|
|
56
|
-
disabled: item.disabled,
|
|
57
|
-
})
|
|
58
43
|
|
|
59
44
|
const iconColor = selected ? colors.primary : colors.foregroundSubtle
|
|
60
|
-
const iconNode = item.icon ?? (item.iconName ?
|
|
45
|
+
const iconNode = item.icon ?? (item.iconName ? <Icon name={item.iconName} size={ms(24)} color={iconColor} /> : null)
|
|
61
46
|
|
|
62
47
|
return (
|
|
63
|
-
<
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
) : null}
|
|
94
|
-
</TouchableOpacity>
|
|
95
|
-
</Animated.View>
|
|
48
|
+
<PressableChip
|
|
49
|
+
onPress={onPress}
|
|
50
|
+
enabled={!item.disabled}
|
|
51
|
+
rippleColor="transparent"
|
|
52
|
+
touchSoundDisabled
|
|
53
|
+
activateOnHover
|
|
54
|
+
accessibilityRole="button"
|
|
55
|
+
accessibilityState={{ selected, disabled: item.disabled }}
|
|
56
|
+
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
|
+
>
|
|
67
|
+
{iconNode}
|
|
68
|
+
{item.label ? (
|
|
69
|
+
<Text
|
|
70
|
+
style={[styles.label, { color: selected ? colors.primary : colors.foreground }]}
|
|
71
|
+
numberOfLines={1}
|
|
72
|
+
allowFontScaling={true}
|
|
73
|
+
>
|
|
74
|
+
{item.label}
|
|
75
|
+
</Text>
|
|
76
|
+
) : null}
|
|
77
|
+
</PressableChip>
|
|
96
78
|
)
|
|
97
79
|
}
|
|
98
80
|
|
|
99
|
-
/**
|
|
100
|
-
* Grid of selectable cells (icon + label) — for store / category / emoji pickers
|
|
101
|
-
* where a list would be the wrong shape. Single or multi select.
|
|
102
|
-
*
|
|
103
|
-
* @example
|
|
104
|
-
* <SelectableGrid
|
|
105
|
-
* items={categories}
|
|
106
|
-
* value={selected}
|
|
107
|
-
* onChange={setSelected}
|
|
108
|
-
* numColumns={4}
|
|
109
|
-
* />
|
|
110
|
-
*/
|
|
111
81
|
export function SelectableGrid<T extends string | number = string>({
|
|
112
82
|
items,
|
|
113
83
|
value,
|
|
@@ -120,10 +90,7 @@ export function SelectableGrid<T extends string | number = string>({
|
|
|
120
90
|
}: SelectableGridProps<T>) {
|
|
121
91
|
const [containerWidth, setContainerWidth] = useState(0)
|
|
122
92
|
const gapPx = s(gap)
|
|
123
|
-
// Compute exact cell width so `numColumns` always fits — percentage widths + gap
|
|
124
|
-
// overflow and wrap one short. -0.5 guards against sub-pixel rounding overflow.
|
|
125
93
|
const cellWidth = containerWidth > 0 ? (containerWidth - gapPx * (numColumns - 1)) / numColumns - 0.5 : 0
|
|
126
|
-
// Horizontal mode: fixed 72dp cell width (same scale as grid cells)
|
|
127
94
|
const horizCellWidth = s(72)
|
|
128
95
|
|
|
129
96
|
const handlePress = (item: SelectableGridItem<T>) => {
|
|
@@ -184,22 +151,21 @@ const styles = StyleSheet.create({
|
|
|
184
151
|
paddingHorizontal: s(4),
|
|
185
152
|
},
|
|
186
153
|
cell: {
|
|
187
|
-
flex: 1,
|
|
188
154
|
borderRadius: RADIUS.md,
|
|
189
155
|
borderWidth: 2,
|
|
190
156
|
alignItems: 'center',
|
|
191
157
|
justifyContent: 'center',
|
|
192
158
|
gap: vs(4),
|
|
193
|
-
paddingHorizontal: s(
|
|
194
|
-
paddingVertical: vs(
|
|
159
|
+
paddingHorizontal: s(8),
|
|
160
|
+
paddingVertical: vs(10),
|
|
195
161
|
},
|
|
196
162
|
cellDisabled: {
|
|
197
163
|
opacity: 0.4,
|
|
198
164
|
},
|
|
199
165
|
label: {
|
|
200
166
|
fontFamily: 'Sohne-Medium',
|
|
201
|
-
fontSize: ms(
|
|
202
|
-
lineHeight: mvs(
|
|
167
|
+
fontSize: ms(11),
|
|
168
|
+
lineHeight: mvs(14),
|
|
203
169
|
textAlign: 'center',
|
|
204
170
|
},
|
|
205
171
|
})
|
|
@@ -163,7 +163,7 @@ export function Sheet({
|
|
|
163
163
|
activeOpacity={0.6}
|
|
164
164
|
touchSoundDisabled={true}
|
|
165
165
|
accessibilityRole="button"
|
|
166
|
-
accessibilityLabel="
|
|
166
|
+
accessibilityLabel="Cerrar"
|
|
167
167
|
hitSlop={{ top: 12, bottom: 12, left: 12, right: 12 }}
|
|
168
168
|
>
|
|
169
169
|
<AntDesign name="close" size={ms(18)} color={colors.foregroundMuted} />
|
|
@@ -4,7 +4,7 @@ import { EaseView } from 'react-native-ease'
|
|
|
4
4
|
import { selectionAsync as hapticSelection } from '../../utils/haptics'
|
|
5
5
|
import { useTheme } from '../../theme'
|
|
6
6
|
import { s, vs, ms, mvs } from '../../utils/scaling'
|
|
7
|
-
import {
|
|
7
|
+
import { Icon } from '../../utils/icons'
|
|
8
8
|
import { COLOR_TRANSITION } from '../../utils/animations'
|
|
9
9
|
import { PressableChip } from '../../utils/pressable'
|
|
10
10
|
import { RADIUS } from '../../tokens'
|
|
@@ -47,7 +47,7 @@ function SheetSelectChip({
|
|
|
47
47
|
}
|
|
48
48
|
|
|
49
49
|
const iconColor = selected ? colors.primaryForeground : colors.foreground
|
|
50
|
-
const resolvedIcon = option.iconName ?
|
|
50
|
+
const resolvedIcon = option.iconName ? <Icon name={option.iconName} size={ms(13)} color={iconColor} /> : null
|
|
51
51
|
|
|
52
52
|
return (
|
|
53
53
|
<PressableChip
|
|
@@ -55,7 +55,7 @@ function SheetSelectChip({
|
|
|
55
55
|
rippleColor="transparent"
|
|
56
56
|
touchSoundDisabled
|
|
57
57
|
accessibilityRole="button"
|
|
58
|
-
accessibilityLabel={option.disabled ? `${option.label},
|
|
58
|
+
accessibilityLabel={option.disabled ? `${option.label}, no disponible` : option.label}
|
|
59
59
|
accessibilityState={{ selected, disabled: option.disabled }}
|
|
60
60
|
>
|
|
61
61
|
<EaseView
|
|
@@ -83,7 +83,7 @@ export function Skeleton({
|
|
|
83
83
|
]}
|
|
84
84
|
onLayout={(e) => setContainerWidth(e.nativeEvent.layout.width)}
|
|
85
85
|
accessibilityRole="progressbar"
|
|
86
|
-
accessibilityLabel="
|
|
86
|
+
accessibilityLabel="Cargando"
|
|
87
87
|
accessibilityState={{ busy: true }}
|
|
88
88
|
>
|
|
89
89
|
<Animated.View style={[StyleSheet.absoluteFill, shimmerStyle]}>
|
|
@@ -13,7 +13,7 @@ export interface SpinnerProps extends Omit<ActivityIndicatorProps, 'size'> {
|
|
|
13
13
|
|
|
14
14
|
const sizeMap: Record<SpinnerSize, 'small' | 'large'> = {
|
|
15
15
|
sm: 'small',
|
|
16
|
-
md: '
|
|
16
|
+
md: 'large',
|
|
17
17
|
lg: 'large',
|
|
18
18
|
}
|
|
19
19
|
|
|
@@ -25,7 +25,7 @@ const labelFontSize: Record<SpinnerSize, number> = {
|
|
|
25
25
|
|
|
26
26
|
export function Spinner({ size = 'md', color, label, ...props }: SpinnerProps) {
|
|
27
27
|
const { colors } = useTheme()
|
|
28
|
-
const a11yLabel = label || '
|
|
28
|
+
const a11yLabel = label || 'Cargando'
|
|
29
29
|
|
|
30
30
|
if (label) {
|
|
31
31
|
return (
|
|
@@ -4,7 +4,7 @@ import { impactLight } from '../../utils/haptics'
|
|
|
4
4
|
import { useTheme } from '../../theme'
|
|
5
5
|
import { s, vs, ms, mvs } from '../../utils/scaling'
|
|
6
6
|
import { RADIUS } from '../../tokens'
|
|
7
|
-
import {
|
|
7
|
+
import { Icon } from '../../utils/icons'
|
|
8
8
|
import { PressableCard } from '../../utils/pressable'
|
|
9
9
|
|
|
10
10
|
export type StatsVariant = 'elevated' | 'outlined' | 'filled'
|
|
@@ -114,7 +114,7 @@ function StatsComponent({
|
|
|
114
114
|
|
|
115
115
|
const iconColorResolved = iconColor ?? colors.primary
|
|
116
116
|
|
|
117
|
-
const resolvedIcon = iconName ?
|
|
117
|
+
const resolvedIcon = iconName ? <Icon name={iconName} size={sizeStyles.iconSize} color={iconColorResolved} /> : icon
|
|
118
118
|
|
|
119
119
|
const iconElement = resolvedIcon ? (
|
|
120
120
|
<View style={styles.iconWrapper}>{resolvedIcon}</View>
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import React from 'react'
|
|
2
|
-
import {
|
|
2
|
+
import { StyleSheet, ViewStyle, View } from 'react-native'
|
|
3
3
|
import { EaseView } from 'react-native-ease'
|
|
4
4
|
import { Feather } from '@expo/vector-icons'
|
|
5
5
|
import { selectionAsync as hapticSelection } from '../../utils/haptics'
|
|
6
6
|
import { useTheme } from '../../theme'
|
|
7
7
|
import { s } from '../../utils/scaling'
|
|
8
8
|
import { COLOR_TRANSITION, OPACITY_TRANSITION, SPRING_ELASTIC } from '../../utils/animations'
|
|
9
|
+
import { PressableButton } from '../../utils/pressable'
|
|
9
10
|
|
|
10
11
|
const TRACK_WIDTH = s(52)
|
|
11
12
|
const TRACK_HEIGHT = s(30)
|
|
@@ -30,14 +31,14 @@ export function Switch({ checked = false, onCheckedChange, disabled, style, acce
|
|
|
30
31
|
|
|
31
32
|
return (
|
|
32
33
|
<View style={[{ alignSelf: 'flex-start' }, style]}>
|
|
33
|
-
<
|
|
34
|
+
<PressableButton
|
|
34
35
|
onPress={() => {
|
|
35
36
|
hapticSelection()
|
|
36
37
|
onCheckedChange?.(!checked)
|
|
37
38
|
}}
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
touchSoundDisabled
|
|
39
|
+
enabled={!disabled}
|
|
40
|
+
rippleColor="transparent"
|
|
41
|
+
touchSoundDisabled
|
|
41
42
|
accessibilityRole="switch"
|
|
42
43
|
accessibilityLabel={accessibilityLabel}
|
|
43
44
|
accessibilityState={{ checked, disabled: isDisabled }}
|
|
@@ -76,7 +77,7 @@ export function Switch({ checked = false, onCheckedChange, disabled, style, acce
|
|
|
76
77
|
</EaseView>
|
|
77
78
|
</EaseView>
|
|
78
79
|
</View>
|
|
79
|
-
</
|
|
80
|
+
</PressableButton>
|
|
80
81
|
</View>
|
|
81
82
|
)
|
|
82
83
|
}
|
|
@@ -84,6 +85,8 @@ export function Switch({ checked = false, onCheckedChange, disabled, style, acce
|
|
|
84
85
|
const styles = StyleSheet.create({
|
|
85
86
|
touchable: {
|
|
86
87
|
alignSelf: 'flex-start',
|
|
88
|
+
minHeight: 44,
|
|
89
|
+
justifyContent: 'center',
|
|
87
90
|
},
|
|
88
91
|
trackContainer: {
|
|
89
92
|
position: 'relative',
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import React from 'react'
|
|
2
|
-
import { View, Text,
|
|
2
|
+
import { View, Text, StyleSheet, ViewStyle } from 'react-native'
|
|
3
3
|
import { useSafeAreaInsets } from 'react-native-safe-area-context'
|
|
4
4
|
import { useTheme } from '../../theme'
|
|
5
|
-
import {
|
|
5
|
+
import { Icon } from '../../utils/icons'
|
|
6
6
|
import { selectionAsync as hapticSelection } from '../../utils/haptics'
|
|
7
7
|
import { s, vs, ms, mvs } from '../../utils/scaling'
|
|
8
|
+
import { PressableTab } from '../../utils/pressable'
|
|
8
9
|
|
|
9
10
|
export interface TabBarItem {
|
|
10
11
|
/** Unique key for the tab. */
|
|
@@ -74,23 +75,23 @@ export function TabBar({
|
|
|
74
75
|
{items.map((item) => {
|
|
75
76
|
const active = item.key === activeKey
|
|
76
77
|
const tint = active ? resolvedActive : resolvedInactive
|
|
77
|
-
const iconNode = item.icon ?? (item.iconName ?
|
|
78
|
+
const iconNode = item.icon ?? (item.iconName ? <Icon name={item.iconName} size={ms(24)} color={tint} /> : null)
|
|
78
79
|
const showBadge = item.badge !== undefined && item.badge !== false
|
|
79
80
|
const badgeCount = typeof item.badge === 'number' ? item.badge : undefined
|
|
80
81
|
|
|
81
82
|
return (
|
|
82
|
-
<
|
|
83
|
+
<PressableTab
|
|
83
84
|
key={item.key}
|
|
84
|
-
style={styles.tab}
|
|
85
85
|
onPress={() => {
|
|
86
86
|
if (!active) hapticSelection()
|
|
87
87
|
onTabPress(item.key)
|
|
88
88
|
}}
|
|
89
|
-
|
|
90
|
-
touchSoundDisabled
|
|
89
|
+
rippleColor="transparent"
|
|
90
|
+
touchSoundDisabled
|
|
91
91
|
accessibilityRole="tab"
|
|
92
92
|
accessibilityState={{ selected: active }}
|
|
93
93
|
accessibilityLabel={item.label ?? item.key}
|
|
94
|
+
style={styles.tab}
|
|
94
95
|
>
|
|
95
96
|
<View>
|
|
96
97
|
{iconNode}
|
|
@@ -115,7 +116,7 @@ export function TabBar({
|
|
|
115
116
|
{item.label}
|
|
116
117
|
</Text>
|
|
117
118
|
) : null}
|
|
118
|
-
</
|
|
119
|
+
</PressableTab>
|
|
119
120
|
)
|
|
120
121
|
})}
|
|
121
122
|
</View>
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import React from 'react'
|
|
2
2
|
import { Text as RNText, TextProps as RNTextProps, TextStyle } from 'react-native'
|
|
3
|
+
|
|
4
|
+
declare const __DEV__: boolean | undefined
|
|
3
5
|
import { useTheme } from '../../theme'
|
|
4
6
|
import { TYPOGRAPHY } from '../../tokens'
|
|
5
7
|
import { ms, mvs } from '../../utils/scaling'
|
|
6
|
-
import {
|
|
8
|
+
import { isLoaded as expoFontIsLoaded } from 'expo-font'
|
|
7
9
|
|
|
8
10
|
export type TextVariant =
|
|
9
11
|
| 'display-hero'
|
|
@@ -70,6 +72,15 @@ const defaultColorVariant: Partial<Record<TextVariant, 'foreground' | 'foregroun
|
|
|
70
72
|
'button-sm': 'foreground',
|
|
71
73
|
}
|
|
72
74
|
|
|
75
|
+
let fontWarned = false
|
|
76
|
+
function warnIfFontsMissing(): void {
|
|
77
|
+
if (fontWarned || typeof __DEV__ === 'undefined' || !__DEV__) return
|
|
78
|
+
fontWarned = true
|
|
79
|
+
if (!expoFontIsLoaded('Sohne-Regular')) {
|
|
80
|
+
console.warn('[retray-ui-kit] Sohne fonts not loaded — text falls back to system font.')
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
73
84
|
function TextBase({ variant = 'body-md', color, style, uppercase, children, ...props }: TextProps) {
|
|
74
85
|
warnIfFontsMissing()
|
|
75
86
|
const { colors } = useTheme()
|
|
@@ -1,15 +1,10 @@
|
|
|
1
1
|
import React, { useState } from 'react'
|
|
2
2
|
import { TextInput, View, Text, StyleSheet, TextInputProps, ViewStyle, Platform } from 'react-native'
|
|
3
|
-
import
|
|
4
|
-
useAnimatedStyle,
|
|
5
|
-
interpolateColor,
|
|
6
|
-
interpolate,
|
|
7
|
-
} from 'react-native-reanimated'
|
|
3
|
+
import { EaseView } from 'react-native-ease'
|
|
8
4
|
import { useTheme } from '../../theme'
|
|
9
5
|
import { s, vs, ms } from '../../utils/scaling'
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import { TIMINGS } from '../../utils/animations'
|
|
6
|
+
import { Icon } from '../../utils/icons'
|
|
7
|
+
import { COLOR_TRANSITION } from '../../utils/animations'
|
|
13
8
|
|
|
14
9
|
const webInputResetStyle: Record<string, unknown> =
|
|
15
10
|
Platform.OS === 'web'
|
|
@@ -20,6 +15,7 @@ export interface TextareaProps extends TextInputProps {
|
|
|
20
15
|
label?: string
|
|
21
16
|
error?: string
|
|
22
17
|
hint?: string
|
|
18
|
+
disabled?: boolean
|
|
23
19
|
rows?: number
|
|
24
20
|
prefixIcon?: string
|
|
25
21
|
prefixIconNode?: React.ReactNode
|
|
@@ -31,6 +27,7 @@ export function Textarea({
|
|
|
31
27
|
label,
|
|
32
28
|
error,
|
|
33
29
|
hint,
|
|
30
|
+
disabled,
|
|
34
31
|
rows = 4,
|
|
35
32
|
prefixIcon,
|
|
36
33
|
prefixIconNode,
|
|
@@ -44,46 +41,36 @@ export function Textarea({
|
|
|
44
41
|
}: TextareaProps) {
|
|
45
42
|
const { colors } = useTheme()
|
|
46
43
|
const [focused, setFocused] = useState(false)
|
|
47
|
-
const focusProgress = useColorTransition(focused, {
|
|
48
|
-
duration: focused ? TIMINGS.focusIn.duration : TIMINGS.focusOut.duration,
|
|
49
|
-
})
|
|
50
44
|
|
|
51
45
|
const resolvedPrefixIcon = prefixIcon
|
|
52
|
-
?
|
|
46
|
+
? <Icon name={prefixIcon} size={ms(16)} color={prefixIconColor ?? colors.foregroundMuted} />
|
|
53
47
|
: prefixIconNode
|
|
54
48
|
|
|
55
|
-
|
|
56
|
-
// focus weight change never resizes the box / reflows content.
|
|
57
|
-
const borderAnimStyle = useAnimatedStyle(() => ({
|
|
58
|
-
borderColor: error
|
|
59
|
-
? colors.destructive
|
|
60
|
-
: interpolateColor(focusProgress.value, [0, 1], [colors.border, colors.primary]),
|
|
61
|
-
borderWidth: error
|
|
62
|
-
? 2
|
|
63
|
-
: interpolate(focusProgress.value, [0, 1], [1, 2]),
|
|
64
|
-
}))
|
|
49
|
+
const borderColor = error ? colors.destructive : (focused ? colors.primary : colors.border)
|
|
65
50
|
|
|
66
51
|
return (
|
|
67
52
|
<View style={[styles.container, containerStyle]}>
|
|
68
53
|
{label ? <Text style={[styles.label, { color: colors.foreground }]} allowFontScaling={true}>{label}</Text> : null}
|
|
69
|
-
<
|
|
70
|
-
|
|
71
|
-
styles.
|
|
72
|
-
{
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
54
|
+
<View style={[styles.inputWrapper, { backgroundColor: colors.background }]}>
|
|
55
|
+
<EaseView
|
|
56
|
+
style={[styles.borderOverlay, { borderWidth: error ? 2 : 1 }]}
|
|
57
|
+
animate={{ borderColor }}
|
|
58
|
+
transition={COLOR_TRANSITION}
|
|
59
|
+
pointerEvents="none"
|
|
60
|
+
/>
|
|
76
61
|
{resolvedPrefixIcon ? <View style={styles.prefixIcon}>{resolvedPrefixIcon}</View> : null}
|
|
77
62
|
<TextInput
|
|
78
63
|
multiline
|
|
79
64
|
numberOfLines={rows}
|
|
80
65
|
textAlignVertical="top"
|
|
66
|
+
editable={!disabled}
|
|
81
67
|
style={[
|
|
82
68
|
styles.input,
|
|
83
69
|
{
|
|
84
70
|
color: colors.foreground,
|
|
85
71
|
minHeight: rows * vs(30),
|
|
86
72
|
},
|
|
73
|
+
disabled && { opacity: 0.45 },
|
|
87
74
|
webInputResetStyle,
|
|
88
75
|
style,
|
|
89
76
|
]}
|
|
@@ -98,9 +85,10 @@ export function Textarea({
|
|
|
98
85
|
placeholderTextColor={colors.foregroundMuted}
|
|
99
86
|
allowFontScaling={true}
|
|
100
87
|
accessibilityLabel={accessibilityLabel ?? label}
|
|
88
|
+
accessibilityState={{ disabled: !!disabled }}
|
|
101
89
|
{...props}
|
|
102
90
|
/>
|
|
103
|
-
</
|
|
91
|
+
</View>
|
|
104
92
|
{error ? (
|
|
105
93
|
<Text
|
|
106
94
|
style={[styles.helperText, { color: colors.destructive }]}
|
|
@@ -128,8 +116,6 @@ const styles = StyleSheet.create({
|
|
|
128
116
|
marginBottom: vs(2),
|
|
129
117
|
},
|
|
130
118
|
inputWrapper: {
|
|
131
|
-
// Border lives on borderOverlay (absolute); wrapper carries none so the
|
|
132
|
-
// focus weight change never reflows content.
|
|
133
119
|
borderRadius: 8,
|
|
134
120
|
paddingHorizontal: s(14),
|
|
135
121
|
paddingVertical: vs(11),
|
|
@@ -5,7 +5,7 @@ import { FontAwesome5 } from '@expo/vector-icons'
|
|
|
5
5
|
import { selectionAsync as hapticSelection } from '../../utils/haptics'
|
|
6
6
|
import { useTheme } from '../../theme'
|
|
7
7
|
import { s, vs, ms } from '../../utils/scaling'
|
|
8
|
-
import {
|
|
8
|
+
import { Icon } from '../../utils/icons'
|
|
9
9
|
import { COLOR_TRANSITION } from '../../utils/animations'
|
|
10
10
|
import { PressableButton } from '../../utils/pressable'
|
|
11
11
|
|
|
@@ -30,13 +30,13 @@ function ToggleIcon({ pressed, iconName, activeIconName, icon, activeIcon, iconC
|
|
|
30
30
|
}
|
|
31
31
|
|
|
32
32
|
if (pressed) {
|
|
33
|
-
if (activeIconName) return
|
|
33
|
+
if (activeIconName) return <Icon name={activeIconName} size={iconSize} color={activeIconColor ?? primaryColor} />
|
|
34
34
|
const active = renderProp(activeIcon)
|
|
35
35
|
if (active) return <>{active}</>
|
|
36
36
|
return <FontAwesome5 name="check-circle" size={iconSize} color={primaryColor} />
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
-
if (iconName) return
|
|
39
|
+
if (iconName) return <Icon name={iconName} size={iconSize} color={iconColor ?? mutedColor} />
|
|
40
40
|
const custom = renderProp(icon)
|
|
41
41
|
if (custom) return <>{custom}</>
|
|
42
42
|
|