@retray-dev/ui-kit 9.1.0 → 9.3.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 +166 -4
- package/CONSUMER.md +247 -0
- package/DESIGN.md +668 -0
- package/FONTS.md +107 -0
- package/README.md +3 -3
- package/dist/AlertBanner.d.mts +3 -1
- package/dist/AlertBanner.d.ts +3 -1
- package/dist/AlertBanner.js +18 -2
- package/dist/AlertBanner.mjs +1 -1
- package/dist/ConfirmDialog.d.mts +3 -1
- package/dist/ConfirmDialog.d.ts +3 -1
- package/dist/ConfirmDialog.js +3 -0
- package/dist/ConfirmDialog.mjs +1 -1
- package/dist/CurrencyInput.d.mts +3 -1
- package/dist/CurrencyInput.d.ts +3 -1
- package/dist/CurrencyInput.js +52 -39
- package/dist/CurrencyInput.mjs +2 -3
- package/dist/ImageUpload.d.mts +27 -0
- package/dist/ImageUpload.d.ts +27 -0
- package/dist/ImageUpload.js +399 -0
- package/dist/ImageUpload.mjs +9 -0
- package/dist/Input.d.mts +3 -1
- package/dist/Input.d.ts +3 -1
- package/dist/Input.js +48 -37
- package/dist/Input.mjs +1 -2
- package/dist/ListItem.d.mts +9 -2
- package/dist/ListItem.d.ts +9 -2
- package/dist/ListItem.js +9 -2
- package/dist/ListItem.mjs +1 -1
- package/dist/SheetSelect.d.mts +25 -0
- package/dist/SheetSelect.d.ts +25 -0
- package/dist/SheetSelect.js +440 -0
- package/dist/SheetSelect.mjs +9 -0
- package/dist/Textarea.mjs +1 -2
- package/dist/{chunk-M6ZXVBTK.mjs → chunk-6MKGPAR2.mjs} +21 -5
- package/dist/{chunk-EH745HE5.mjs → chunk-CZCQZHG6.mjs} +13 -4
- package/dist/{chunk-7QHVVCB3.mjs → chunk-FZZLPJ6B.mjs} +3 -0
- package/dist/{chunk-MAC465BB.mjs → chunk-JUXSWN54.mjs} +5 -3
- package/dist/{chunk-BNP626TY.mjs → chunk-OHBNABL5.mjs} +10 -3
- package/dist/chunk-URI2WBIV.mjs +147 -0
- package/dist/chunk-Y4GL2MHX.mjs +112 -0
- package/dist/{chunk-756RAKE4.mjs → chunk-ZUR7AU5R.mjs} +38 -20
- package/dist/fonts.d.mts +32 -0
- package/dist/fonts.d.ts +32 -0
- package/dist/fonts.js +44 -0
- package/dist/fonts.mjs +37 -0
- package/dist/index.d.mts +26 -1
- package/dist/index.d.ts +26 -1
- package/dist/index.js +425 -106
- package/dist/index.mjs +55 -17
- package/package.json +23 -6
- package/src/components/AlertBanner/AlertBanner.tsx +21 -3
- package/src/components/ConfirmDialog/ConfirmDialog.tsx +5 -0
- package/src/components/CurrencyInput/CurrencyInput.tsx +4 -0
- package/src/components/ImageUpload/ImageUpload.tsx +158 -0
- package/src/components/ImageUpload/index.ts +1 -0
- package/src/components/Input/Input.tsx +64 -53
- package/src/components/ListItem/ListItem.tsx +23 -4
- package/src/components/SheetSelect/SheetSelect.tsx +192 -0
- package/src/components/SheetSelect/index.ts +1 -0
- package/src/fonts.ts +30 -29
- package/src/hooks/useConfirmDialog.ts +67 -0
- package/src/index.ts +6 -0
- package/dist/chunk-26BCI223.mjs +0 -14
|
@@ -5,6 +5,8 @@ import {
|
|
|
5
5
|
StyleSheet,
|
|
6
6
|
ViewStyle,
|
|
7
7
|
TextStyle,
|
|
8
|
+
Image,
|
|
9
|
+
ImageSourcePropType,
|
|
8
10
|
} from 'react-native'
|
|
9
11
|
import { Entypo } from '@expo/vector-icons'
|
|
10
12
|
import { selectionAsync as hapticSelection } from '../../utils/haptics'
|
|
@@ -17,6 +19,11 @@ import { PressableRow } from '../../utils/pressable'
|
|
|
17
19
|
export type ListItemVariant = 'plain' | 'card'
|
|
18
20
|
|
|
19
21
|
export interface ListItemProps {
|
|
22
|
+
/**
|
|
23
|
+
* Image source for the left slot. If provided, renders an Image (40×40, borderRadius 8).
|
|
24
|
+
* Takes precedence over `leftRender` and `leftIcon`.
|
|
25
|
+
*/
|
|
26
|
+
imageSource?: ImageSourcePropType
|
|
20
27
|
/**
|
|
21
28
|
* Arbitrary content rendered on the left (avatar, icon, image, etc.).
|
|
22
29
|
* Rendered inside a 44×44 aligned container.
|
|
@@ -72,6 +79,8 @@ export interface ListItemProps {
|
|
|
72
79
|
titleStyle?: TextStyle
|
|
73
80
|
/** Style applied to the subtitle Text. */
|
|
74
81
|
subtitleStyle?: TextStyle
|
|
82
|
+
/** Max lines for the subtitle. Defaults to 2. */
|
|
83
|
+
subtitleNumberOfLines?: number
|
|
75
84
|
/** Style applied to the caption Text. */
|
|
76
85
|
captionStyle?: TextStyle
|
|
77
86
|
/** Accessibility label override. Defaults to the title. */
|
|
@@ -79,6 +88,7 @@ export interface ListItemProps {
|
|
|
79
88
|
}
|
|
80
89
|
|
|
81
90
|
function ListItemBase({
|
|
91
|
+
imageSource,
|
|
82
92
|
leftRender,
|
|
83
93
|
rightRender,
|
|
84
94
|
trailing,
|
|
@@ -98,6 +108,7 @@ function ListItemBase({
|
|
|
98
108
|
style,
|
|
99
109
|
titleStyle,
|
|
100
110
|
subtitleStyle,
|
|
111
|
+
subtitleNumberOfLines = 2,
|
|
101
112
|
captionStyle,
|
|
102
113
|
accessibilityLabel,
|
|
103
114
|
}: ListItemProps) {
|
|
@@ -108,9 +119,12 @@ function ListItemBase({
|
|
|
108
119
|
onPress?.()
|
|
109
120
|
}
|
|
110
121
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
122
|
+
// imageSource takes precedence, then leftIcon, then leftRender/icon
|
|
123
|
+
const effectiveLeft: React.ReactNode = imageSource
|
|
124
|
+
? <Image source={imageSource} style={styles.image} />
|
|
125
|
+
: leftIcon
|
|
126
|
+
? renderIcon(leftIcon, 24, leftIconColor ?? colors.foreground)
|
|
127
|
+
: leftRender ?? icon
|
|
114
128
|
|
|
115
129
|
const effectiveRight: React.ReactNode | string | undefined = rightIcon
|
|
116
130
|
? renderIcon(rightIcon, 24, rightIconColor ?? colors.foregroundMuted)
|
|
@@ -150,7 +164,7 @@ function ListItemBase({
|
|
|
150
164
|
{subtitle ? (
|
|
151
165
|
<Text
|
|
152
166
|
style={[styles.subtitle, { color: colors.foregroundMuted }, subtitleStyle]}
|
|
153
|
-
numberOfLines={
|
|
167
|
+
numberOfLines={subtitleNumberOfLines}
|
|
154
168
|
allowFontScaling={true}
|
|
155
169
|
>
|
|
156
170
|
{subtitle}
|
|
@@ -238,6 +252,11 @@ const styles = StyleSheet.create({
|
|
|
238
252
|
justifyContent: 'center',
|
|
239
253
|
flexShrink: 0,
|
|
240
254
|
},
|
|
255
|
+
image: {
|
|
256
|
+
width: s(40),
|
|
257
|
+
height: s(40),
|
|
258
|
+
borderRadius: 8,
|
|
259
|
+
},
|
|
241
260
|
content: {
|
|
242
261
|
flex: 1,
|
|
243
262
|
gap: vs(4),
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import { View, Text, ScrollView, StyleSheet, ViewStyle } from 'react-native'
|
|
3
|
+
import { EaseView } from 'react-native-ease'
|
|
4
|
+
import { selectionAsync as hapticSelection } from '../../utils/haptics'
|
|
5
|
+
import { useTheme } from '../../theme'
|
|
6
|
+
import { s, vs, ms, mvs } from '../../utils/scaling'
|
|
7
|
+
import { renderIcon } from '../../utils/icons'
|
|
8
|
+
import { COLOR_TRANSITION } from '../../utils/animations'
|
|
9
|
+
import { PressableChip } from '../../utils/pressable'
|
|
10
|
+
import { RADIUS } from '../../tokens'
|
|
11
|
+
|
|
12
|
+
export interface SheetSelectOption {
|
|
13
|
+
label: string
|
|
14
|
+
value: string | number
|
|
15
|
+
iconName?: string
|
|
16
|
+
disabled?: boolean
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface SheetSelectProps {
|
|
20
|
+
options: SheetSelectOption[]
|
|
21
|
+
value?: string | number | (string | number)[]
|
|
22
|
+
onValueChange?: (value: string | number | (string | number)[]) => void
|
|
23
|
+
/** Allow multiple simultaneous selections. Defaults to false (radio). */
|
|
24
|
+
multiSelect?: boolean
|
|
25
|
+
label?: string
|
|
26
|
+
error?: string
|
|
27
|
+
/** Wrap chips into multiple rows instead of a single horizontal scroll. Defaults to false. */
|
|
28
|
+
wrap?: boolean
|
|
29
|
+
style?: ViewStyle
|
|
30
|
+
accessibilityLabel?: string
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function SheetSelectChip({
|
|
34
|
+
option,
|
|
35
|
+
selected,
|
|
36
|
+
onPress,
|
|
37
|
+
}: {
|
|
38
|
+
option: SheetSelectOption
|
|
39
|
+
selected: boolean
|
|
40
|
+
onPress: () => void
|
|
41
|
+
}) {
|
|
42
|
+
const { colors } = useTheme()
|
|
43
|
+
|
|
44
|
+
const handlePress = () => {
|
|
45
|
+
hapticSelection()
|
|
46
|
+
onPress()
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const iconColor = selected ? colors.primaryForeground : colors.foreground
|
|
50
|
+
const resolvedIcon = option.iconName ? renderIcon(option.iconName, ms(13), iconColor) : null
|
|
51
|
+
|
|
52
|
+
return (
|
|
53
|
+
<PressableChip
|
|
54
|
+
onPress={option.disabled ? undefined : handlePress}
|
|
55
|
+
rippleColor="transparent"
|
|
56
|
+
touchSoundDisabled
|
|
57
|
+
accessibilityRole="button"
|
|
58
|
+
accessibilityLabel={option.disabled ? `${option.label}, unavailable` : option.label}
|
|
59
|
+
accessibilityState={{ selected, disabled: option.disabled }}
|
|
60
|
+
>
|
|
61
|
+
<EaseView
|
|
62
|
+
style={[styles.chip, option.disabled && styles.chipDisabled]}
|
|
63
|
+
animate={{
|
|
64
|
+
backgroundColor: selected ? colors.primary : colors.surface,
|
|
65
|
+
borderColor: selected ? colors.primary : colors.border,
|
|
66
|
+
}}
|
|
67
|
+
transition={COLOR_TRANSITION}
|
|
68
|
+
>
|
|
69
|
+
{resolvedIcon ? <View style={styles.chipIcon}>{resolvedIcon}</View> : null}
|
|
70
|
+
<Text
|
|
71
|
+
style={[styles.chipLabel, { color: selected ? colors.primaryForeground : colors.foreground }]}
|
|
72
|
+
allowFontScaling={true}
|
|
73
|
+
>
|
|
74
|
+
{option.label}
|
|
75
|
+
</Text>
|
|
76
|
+
</EaseView>
|
|
77
|
+
</PressableChip>
|
|
78
|
+
)
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export function SheetSelect({
|
|
82
|
+
options,
|
|
83
|
+
value,
|
|
84
|
+
onValueChange,
|
|
85
|
+
multiSelect = false,
|
|
86
|
+
label,
|
|
87
|
+
error,
|
|
88
|
+
wrap = false,
|
|
89
|
+
style,
|
|
90
|
+
accessibilityLabel,
|
|
91
|
+
}: SheetSelectProps) {
|
|
92
|
+
const { colors } = useTheme()
|
|
93
|
+
|
|
94
|
+
const isSelected = (optionValue: string | number): boolean => {
|
|
95
|
+
if (Array.isArray(value)) return value.includes(optionValue)
|
|
96
|
+
return optionValue === value
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const handlePress = (optionValue: string | number) => {
|
|
100
|
+
if (!multiSelect) {
|
|
101
|
+
onValueChange?.(optionValue)
|
|
102
|
+
return
|
|
103
|
+
}
|
|
104
|
+
const currentArray = Array.isArray(value) ? value : value != null ? [value] : []
|
|
105
|
+
const alreadySelected = currentArray.includes(optionValue)
|
|
106
|
+
const newArray: (string | number)[] = alreadySelected
|
|
107
|
+
? currentArray.filter((v) => v !== optionValue)
|
|
108
|
+
: [...currentArray, optionValue]
|
|
109
|
+
onValueChange?.(newArray)
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const chips = options.map((opt) => (
|
|
113
|
+
<SheetSelectChip
|
|
114
|
+
key={opt.value}
|
|
115
|
+
option={opt}
|
|
116
|
+
selected={isSelected(opt.value)}
|
|
117
|
+
onPress={() => handlePress(opt.value)}
|
|
118
|
+
/>
|
|
119
|
+
))
|
|
120
|
+
|
|
121
|
+
return (
|
|
122
|
+
<View style={[styles.container, style]} accessibilityLabel={accessibilityLabel}>
|
|
123
|
+
{label ? (
|
|
124
|
+
<Text style={[styles.label, { color: colors.foreground }]} allowFontScaling={true}>
|
|
125
|
+
{label}
|
|
126
|
+
</Text>
|
|
127
|
+
) : null}
|
|
128
|
+
{wrap ? (
|
|
129
|
+
<View style={styles.wrapContainer}>{chips}</View>
|
|
130
|
+
) : (
|
|
131
|
+
<ScrollView
|
|
132
|
+
horizontal
|
|
133
|
+
showsHorizontalScrollIndicator={false}
|
|
134
|
+
contentContainerStyle={styles.scrollContent}
|
|
135
|
+
>
|
|
136
|
+
{chips}
|
|
137
|
+
</ScrollView>
|
|
138
|
+
)}
|
|
139
|
+
{error ? (
|
|
140
|
+
<Text style={[styles.error, { color: colors.destructive }]} allowFontScaling={true} accessibilityLiveRegion="polite">
|
|
141
|
+
{error}
|
|
142
|
+
</Text>
|
|
143
|
+
) : null}
|
|
144
|
+
</View>
|
|
145
|
+
)
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
const styles = StyleSheet.create({
|
|
149
|
+
container: {
|
|
150
|
+
gap: vs(8),
|
|
151
|
+
},
|
|
152
|
+
label: {
|
|
153
|
+
fontFamily: 'Sohne-Medium',
|
|
154
|
+
fontSize: ms(14),
|
|
155
|
+
},
|
|
156
|
+
scrollContent: {
|
|
157
|
+
flexDirection: 'row',
|
|
158
|
+
gap: s(8),
|
|
159
|
+
},
|
|
160
|
+
wrapContainer: {
|
|
161
|
+
flexDirection: 'row',
|
|
162
|
+
flexWrap: 'wrap',
|
|
163
|
+
gap: s(8),
|
|
164
|
+
},
|
|
165
|
+
chip: {
|
|
166
|
+
borderRadius: RADIUS.full,
|
|
167
|
+
paddingHorizontal: s(14),
|
|
168
|
+
paddingVertical: vs(10),
|
|
169
|
+
minHeight: 44,
|
|
170
|
+
borderWidth: 1,
|
|
171
|
+
alignItems: 'center',
|
|
172
|
+
justifyContent: 'center',
|
|
173
|
+
flexDirection: 'row',
|
|
174
|
+
gap: s(5),
|
|
175
|
+
},
|
|
176
|
+
chipDisabled: {
|
|
177
|
+
opacity: 0.4,
|
|
178
|
+
},
|
|
179
|
+
chipIcon: {
|
|
180
|
+
alignItems: 'center',
|
|
181
|
+
justifyContent: 'center',
|
|
182
|
+
},
|
|
183
|
+
chipLabel: {
|
|
184
|
+
fontFamily: 'Sohne-Medium',
|
|
185
|
+
fontSize: ms(13),
|
|
186
|
+
lineHeight: mvs(18),
|
|
187
|
+
},
|
|
188
|
+
error: {
|
|
189
|
+
fontFamily: 'Sohne-Regular',
|
|
190
|
+
fontSize: ms(13),
|
|
191
|
+
},
|
|
192
|
+
})
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './SheetSelect'
|
package/src/fonts.ts
CHANGED
|
@@ -14,38 +14,39 @@
|
|
|
14
14
|
* }
|
|
15
15
|
*/
|
|
16
16
|
// `.otf` assets resolve to a Metro asset module id (number) via require() at the
|
|
17
|
-
// consumer's build time.
|
|
17
|
+
// consumer's build time. Paths are relative to dist/fonts.js (the compiled output).
|
|
18
|
+
// Both dist/ and src/ are published in the package, so ../src/assets/fonts/ resolves correctly.
|
|
18
19
|
declare const require: (path: string) => number
|
|
19
20
|
|
|
20
21
|
export const SohneFonts = {
|
|
21
22
|
// Sohne base
|
|
22
|
-
'Sohne-ExtraLight': require('
|
|
23
|
-
'Sohne-ExtraLightItalic': require('
|
|
24
|
-
'Sohne-Light': require('
|
|
25
|
-
'Sohne-LightItalic': require('
|
|
26
|
-
'Sohne-Regular': require('
|
|
27
|
-
'Sohne-Italic': require('
|
|
28
|
-
'Sohne-Medium': require('
|
|
29
|
-
'Sohne-MediumItalic': require('
|
|
30
|
-
'Sohne-SemiBold': require('
|
|
31
|
-
'Sohne-SemiBoldItalic': require('
|
|
32
|
-
'Sohne-Bold': require('
|
|
33
|
-
'Sohne-BoldItalic': require('
|
|
34
|
-
'Sohne-ExtraBold': require('
|
|
35
|
-
'Sohne-ExtraBoldItalic': require('
|
|
23
|
+
'Sohne-ExtraLight': require('../src/assets/fonts/Sohne-ExtraLight.otf'),
|
|
24
|
+
'Sohne-ExtraLightItalic': require('../src/assets/fonts/Sohne-ExtraLightItalic.otf'),
|
|
25
|
+
'Sohne-Light': require('../src/assets/fonts/Sohne-Light.otf'),
|
|
26
|
+
'Sohne-LightItalic': require('../src/assets/fonts/Sohne-LightItalic.otf'),
|
|
27
|
+
'Sohne-Regular': require('../src/assets/fonts/Sohne-Regular.otf'),
|
|
28
|
+
'Sohne-Italic': require('../src/assets/fonts/Sohne-Italic.otf'),
|
|
29
|
+
'Sohne-Medium': require('../src/assets/fonts/Sohne-Medium.otf'),
|
|
30
|
+
'Sohne-MediumItalic': require('../src/assets/fonts/Sohne-MediumItalic.otf'),
|
|
31
|
+
'Sohne-SemiBold': require('../src/assets/fonts/Sohne-SemiBold.otf'),
|
|
32
|
+
'Sohne-SemiBoldItalic': require('../src/assets/fonts/Sohne-SemiBoldItalic.otf'),
|
|
33
|
+
'Sohne-Bold': require('../src/assets/fonts/Sohne-Bold.otf'),
|
|
34
|
+
'Sohne-BoldItalic': require('../src/assets/fonts/Sohne-BoldItalic.otf'),
|
|
35
|
+
'Sohne-ExtraBold': require('../src/assets/fonts/Sohne-ExtraBold.otf'),
|
|
36
|
+
'Sohne-ExtraBoldItalic': require('../src/assets/fonts/Sohne-ExtraBoldItalic.otf'),
|
|
36
37
|
// SohneMono
|
|
37
|
-
'SohneMono-ExtraLight': require('
|
|
38
|
-
'SohneMono-ExtraLightItalic': require('
|
|
39
|
-
'SohneMono-Light': require('
|
|
40
|
-
'SohneMono-LightItalic': require('
|
|
41
|
-
'SohneMono-Regular': require('
|
|
42
|
-
'SohneMono-Italic': require('
|
|
43
|
-
'SohneMono-Medium': require('
|
|
44
|
-
'SohneMono-MediumItalic': require('
|
|
45
|
-
'SohneMono-SemiBold': require('
|
|
46
|
-
'SohneMono-SemiBoldItalic': require('
|
|
47
|
-
'SohneMono-Bold': require('
|
|
48
|
-
'SohneMono-BoldItalic': require('
|
|
49
|
-
'SohneMono-ExtraBold': require('
|
|
50
|
-
'SohneMono-ExtraBoldItalic': require('
|
|
38
|
+
'SohneMono-ExtraLight': require('../src/assets/fonts/SohneMono-ExtraLight.otf'),
|
|
39
|
+
'SohneMono-ExtraLightItalic': require('../src/assets/fonts/SohneMono-ExtraLightItalic.otf'),
|
|
40
|
+
'SohneMono-Light': require('../src/assets/fonts/SohneMono-Light.otf'),
|
|
41
|
+
'SohneMono-LightItalic': require('../src/assets/fonts/SohneMono-LightItalic.otf'),
|
|
42
|
+
'SohneMono-Regular': require('../src/assets/fonts/SohneMono-Regular.otf'),
|
|
43
|
+
'SohneMono-Italic': require('../src/assets/fonts/SohneMono-Italic.otf'),
|
|
44
|
+
'SohneMono-Medium': require('../src/assets/fonts/SohneMono-Medium.otf'),
|
|
45
|
+
'SohneMono-MediumItalic': require('../src/assets/fonts/SohneMono-MediumItalic.otf'),
|
|
46
|
+
'SohneMono-SemiBold': require('../src/assets/fonts/SohneMono-SemiBold.otf'),
|
|
47
|
+
'SohneMono-SemiBoldItalic': require('../src/assets/fonts/SohneMono-SemiBoldItalic.otf'),
|
|
48
|
+
'SohneMono-Bold': require('../src/assets/fonts/SohneMono-Bold.otf'),
|
|
49
|
+
'SohneMono-BoldItalic': require('../src/assets/fonts/SohneMono-BoldItalic.otf'),
|
|
50
|
+
'SohneMono-ExtraBold': require('../src/assets/fonts/SohneMono-ExtraBold.otf'),
|
|
51
|
+
'SohneMono-ExtraBoldItalic': require('../src/assets/fonts/SohneMono-ExtraBoldItalic.otf'),
|
|
51
52
|
} as const
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { useState, useCallback } from 'react'
|
|
2
|
+
|
|
3
|
+
export interface UseConfirmDialogOptions {
|
|
4
|
+
onConfirm: () => void | Promise<void>
|
|
5
|
+
onCancel?: () => void
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export interface UseConfirmDialogResult<T> {
|
|
9
|
+
/** Pass to ConfirmDialog `visible` prop. */
|
|
10
|
+
visible: boolean
|
|
11
|
+
/** The value passed to `open()` — available during the confirmation flow. */
|
|
12
|
+
target: T | null
|
|
13
|
+
/** Whether `onConfirm` is currently executing. Pass to ConfirmDialog `loading` prop. */
|
|
14
|
+
loading: boolean
|
|
15
|
+
/** Open the dialog, optionally with an associated value (e.g. the item to delete). */
|
|
16
|
+
open: (target?: T) => void
|
|
17
|
+
/** Handlers to pass directly to ConfirmDialog. */
|
|
18
|
+
dialogProps: {
|
|
19
|
+
visible: boolean
|
|
20
|
+
loading: boolean
|
|
21
|
+
onConfirm: () => void
|
|
22
|
+
onCancel: () => void
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function useConfirmDialog<T = undefined>(
|
|
27
|
+
options: UseConfirmDialogOptions,
|
|
28
|
+
): UseConfirmDialogResult<T> {
|
|
29
|
+
const [visible, setVisible] = useState(false)
|
|
30
|
+
const [target, setTarget] = useState<T | null>(null)
|
|
31
|
+
const [loading, setLoading] = useState(false)
|
|
32
|
+
|
|
33
|
+
const open = useCallback((t?: T) => {
|
|
34
|
+
setTarget(t ?? null)
|
|
35
|
+
setVisible(true)
|
|
36
|
+
}, [])
|
|
37
|
+
|
|
38
|
+
const handleConfirm = useCallback(async () => {
|
|
39
|
+
setLoading(true)
|
|
40
|
+
try {
|
|
41
|
+
await options.onConfirm()
|
|
42
|
+
} finally {
|
|
43
|
+
setLoading(false)
|
|
44
|
+
setVisible(false)
|
|
45
|
+
setTarget(null)
|
|
46
|
+
}
|
|
47
|
+
}, [options])
|
|
48
|
+
|
|
49
|
+
const handleCancel = useCallback(() => {
|
|
50
|
+
setVisible(false)
|
|
51
|
+
setTarget(null)
|
|
52
|
+
options.onCancel?.()
|
|
53
|
+
}, [options])
|
|
54
|
+
|
|
55
|
+
return {
|
|
56
|
+
visible,
|
|
57
|
+
target,
|
|
58
|
+
loading,
|
|
59
|
+
open,
|
|
60
|
+
dialogProps: {
|
|
61
|
+
visible,
|
|
62
|
+
loading,
|
|
63
|
+
onConfirm: handleConfirm,
|
|
64
|
+
onCancel: handleCancel,
|
|
65
|
+
},
|
|
66
|
+
}
|
|
67
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -53,6 +53,8 @@ export * from './components/SelectableGrid'
|
|
|
53
53
|
export * from './components/PricingCard'
|
|
54
54
|
export * from './components/TabBar'
|
|
55
55
|
export * from './components/ImageViewer'
|
|
56
|
+
export * from './components/SheetSelect'
|
|
57
|
+
export * from './components/ImageUpload'
|
|
56
58
|
// HolographicCard is intentionally NOT re-exported here — it depends on the
|
|
57
59
|
// optional peer @shopify/react-native-skia, so it must stay out of the main
|
|
58
60
|
// barrel's module graph. Deep-import it: '@retray-dev/ui-kit/HolographicCard'.
|
|
@@ -76,6 +78,10 @@ export {
|
|
|
76
78
|
richHaptics,
|
|
77
79
|
} from './utils/haptics'
|
|
78
80
|
|
|
81
|
+
// Hooks
|
|
82
|
+
export { useConfirmDialog } from './hooks/useConfirmDialog'
|
|
83
|
+
export type { UseConfirmDialogOptions, UseConfirmDialogResult } from './hooks/useConfirmDialog'
|
|
84
|
+
|
|
79
85
|
// Design tokens
|
|
80
86
|
export {
|
|
81
87
|
SPACING,
|
package/dist/chunk-26BCI223.mjs
DELETED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import { TIMINGS, EASINGS } from './chunk-DVK4G2GT.mjs';
|
|
2
|
-
import { useEffect } from 'react';
|
|
3
|
-
import { useSharedValue, withTiming } from 'react-native-reanimated';
|
|
4
|
-
|
|
5
|
-
function useColorTransition(active, options = {}) {
|
|
6
|
-
const { duration = TIMINGS.state.duration } = options;
|
|
7
|
-
const progress = useSharedValue(active ? 1 : 0);
|
|
8
|
-
useEffect(() => {
|
|
9
|
-
progress.value = withTiming(active ? 1 : 0, { duration, easing: EASINGS.standard });
|
|
10
|
-
}, [active, duration, progress]);
|
|
11
|
-
return progress;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
export { useColorTransition };
|