@retray-dev/ui-kit 9.1.0 → 9.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.
Files changed (56) hide show
  1. package/COMPONENTS.md +165 -4
  2. package/CONSUMER.md +247 -0
  3. package/DESIGN.md +668 -0
  4. package/FONTS.md +107 -0
  5. package/README.md +3 -3
  6. package/dist/AlertBanner.d.mts +3 -1
  7. package/dist/AlertBanner.d.ts +3 -1
  8. package/dist/AlertBanner.js +18 -2
  9. package/dist/AlertBanner.mjs +1 -1
  10. package/dist/ConfirmDialog.d.mts +3 -1
  11. package/dist/ConfirmDialog.d.ts +3 -1
  12. package/dist/ConfirmDialog.js +3 -0
  13. package/dist/ConfirmDialog.mjs +1 -1
  14. package/dist/CurrencyInput.d.mts +3 -1
  15. package/dist/CurrencyInput.d.ts +3 -1
  16. package/dist/CurrencyInput.js +31 -4
  17. package/dist/CurrencyInput.mjs +2 -2
  18. package/dist/ImageUpload.d.mts +27 -0
  19. package/dist/ImageUpload.d.ts +27 -0
  20. package/dist/ImageUpload.js +399 -0
  21. package/dist/ImageUpload.mjs +9 -0
  22. package/dist/Input.d.mts +3 -1
  23. package/dist/Input.d.ts +3 -1
  24. package/dist/Input.js +27 -2
  25. package/dist/Input.mjs +1 -1
  26. package/dist/ListItem.d.mts +3 -1
  27. package/dist/ListItem.d.ts +3 -1
  28. package/dist/ListItem.js +2 -1
  29. package/dist/ListItem.mjs +1 -1
  30. package/dist/SheetSelect.d.mts +25 -0
  31. package/dist/SheetSelect.d.ts +25 -0
  32. package/dist/SheetSelect.js +440 -0
  33. package/dist/SheetSelect.mjs +9 -0
  34. package/dist/{chunk-M6ZXVBTK.mjs → chunk-6MKGPAR2.mjs} +21 -5
  35. package/dist/{chunk-7QHVVCB3.mjs → chunk-FZZLPJ6B.mjs} +3 -0
  36. package/dist/{chunk-MAC465BB.mjs → chunk-KNSENOV4.mjs} +5 -3
  37. package/dist/{chunk-756RAKE4.mjs → chunk-LVYEU5ZK.mjs} +27 -2
  38. package/dist/{chunk-BNP626TY.mjs → chunk-T4I5WVHA.mjs} +2 -1
  39. package/dist/chunk-URI2WBIV.mjs +147 -0
  40. package/dist/chunk-Y4GL2MHX.mjs +112 -0
  41. package/dist/index.d.mts +26 -1
  42. package/dist/index.d.ts +26 -1
  43. package/dist/index.js +327 -8
  44. package/dist/index.mjs +51 -12
  45. package/package.json +18 -5
  46. package/src/components/AlertBanner/AlertBanner.tsx +21 -3
  47. package/src/components/ConfirmDialog/ConfirmDialog.tsx +5 -0
  48. package/src/components/CurrencyInput/CurrencyInput.tsx +4 -0
  49. package/src/components/ImageUpload/ImageUpload.tsx +158 -0
  50. package/src/components/ImageUpload/index.ts +1 -0
  51. package/src/components/Input/Input.tsx +51 -23
  52. package/src/components/ListItem/ListItem.tsx +4 -1
  53. package/src/components/SheetSelect/SheetSelect.tsx +192 -0
  54. package/src/components/SheetSelect/index.ts +1 -0
  55. package/src/hooks/useConfirmDialog.ts +67 -0
  56. package/src/index.ts +6 -0
@@ -0,0 +1,147 @@
1
+ import { PressableChip } from './chunk-3DKJ2GIC.mjs';
2
+ import { selectionAsync } from './chunk-EJ7ZPXOH.mjs';
3
+ import { COLOR_TRANSITION } from './chunk-DVK4G2GT.mjs';
4
+ import { RADIUS } from './chunk-QY3X2UYR.mjs';
5
+ import { renderIcon } from './chunk-T7XZ7H7Y.mjs';
6
+ import { useTheme } from './chunk-SOYNZDVY.mjs';
7
+ import { ms, mvs, s, vs } from './chunk-2CE3TQVY.mjs';
8
+ import React from 'react';
9
+ import { StyleSheet, View, Text, ScrollView } from 'react-native';
10
+ import { EaseView } from 'react-native-ease';
11
+
12
+ function SheetSelectChip({
13
+ option,
14
+ selected,
15
+ onPress
16
+ }) {
17
+ const { colors } = useTheme();
18
+ const handlePress = () => {
19
+ selectionAsync();
20
+ onPress();
21
+ };
22
+ const iconColor = selected ? colors.primaryForeground : colors.foreground;
23
+ const resolvedIcon = option.iconName ? renderIcon(option.iconName, ms(13), iconColor) : null;
24
+ return /* @__PURE__ */ React.createElement(
25
+ PressableChip,
26
+ {
27
+ onPress: option.disabled ? void 0 : handlePress,
28
+ rippleColor: "transparent",
29
+ touchSoundDisabled: true,
30
+ accessibilityRole: "button",
31
+ accessibilityLabel: option.disabled ? `${option.label}, unavailable` : option.label,
32
+ accessibilityState: { selected, disabled: option.disabled }
33
+ },
34
+ /* @__PURE__ */ React.createElement(
35
+ EaseView,
36
+ {
37
+ style: [styles.chip, option.disabled && styles.chipDisabled],
38
+ animate: {
39
+ backgroundColor: selected ? colors.primary : colors.surface,
40
+ borderColor: selected ? colors.primary : colors.border
41
+ },
42
+ transition: COLOR_TRANSITION
43
+ },
44
+ resolvedIcon ? /* @__PURE__ */ React.createElement(View, { style: styles.chipIcon }, resolvedIcon) : null,
45
+ /* @__PURE__ */ React.createElement(
46
+ Text,
47
+ {
48
+ style: [styles.chipLabel, { color: selected ? colors.primaryForeground : colors.foreground }],
49
+ allowFontScaling: true
50
+ },
51
+ option.label
52
+ )
53
+ )
54
+ );
55
+ }
56
+ function SheetSelect({
57
+ options,
58
+ value,
59
+ onValueChange,
60
+ multiSelect = false,
61
+ label,
62
+ error,
63
+ wrap = false,
64
+ style,
65
+ accessibilityLabel
66
+ }) {
67
+ const { colors } = useTheme();
68
+ const isSelected = (optionValue) => {
69
+ if (Array.isArray(value)) return value.includes(optionValue);
70
+ return optionValue === value;
71
+ };
72
+ const handlePress = (optionValue) => {
73
+ if (!multiSelect) {
74
+ onValueChange?.(optionValue);
75
+ return;
76
+ }
77
+ const currentArray = Array.isArray(value) ? value : value != null ? [value] : [];
78
+ const alreadySelected = currentArray.includes(optionValue);
79
+ const newArray = alreadySelected ? currentArray.filter((v) => v !== optionValue) : [...currentArray, optionValue];
80
+ onValueChange?.(newArray);
81
+ };
82
+ const chips = options.map((opt) => /* @__PURE__ */ React.createElement(
83
+ SheetSelectChip,
84
+ {
85
+ key: opt.value,
86
+ option: opt,
87
+ selected: isSelected(opt.value),
88
+ onPress: () => handlePress(opt.value)
89
+ }
90
+ ));
91
+ return /* @__PURE__ */ React.createElement(View, { style: [styles.container, style], accessibilityLabel }, label ? /* @__PURE__ */ React.createElement(Text, { style: [styles.label, { color: colors.foreground }], allowFontScaling: true }, label) : null, wrap ? /* @__PURE__ */ React.createElement(View, { style: styles.wrapContainer }, chips) : /* @__PURE__ */ React.createElement(
92
+ ScrollView,
93
+ {
94
+ horizontal: true,
95
+ showsHorizontalScrollIndicator: false,
96
+ contentContainerStyle: styles.scrollContent
97
+ },
98
+ chips
99
+ ), error ? /* @__PURE__ */ React.createElement(Text, { style: [styles.error, { color: colors.destructive }], allowFontScaling: true, accessibilityLiveRegion: "polite" }, error) : null);
100
+ }
101
+ var styles = StyleSheet.create({
102
+ container: {
103
+ gap: vs(8)
104
+ },
105
+ label: {
106
+ fontFamily: "Sohne-Medium",
107
+ fontSize: ms(14)
108
+ },
109
+ scrollContent: {
110
+ flexDirection: "row",
111
+ gap: s(8)
112
+ },
113
+ wrapContainer: {
114
+ flexDirection: "row",
115
+ flexWrap: "wrap",
116
+ gap: s(8)
117
+ },
118
+ chip: {
119
+ borderRadius: RADIUS.full,
120
+ paddingHorizontal: s(14),
121
+ paddingVertical: vs(10),
122
+ minHeight: 44,
123
+ borderWidth: 1,
124
+ alignItems: "center",
125
+ justifyContent: "center",
126
+ flexDirection: "row",
127
+ gap: s(5)
128
+ },
129
+ chipDisabled: {
130
+ opacity: 0.4
131
+ },
132
+ chipIcon: {
133
+ alignItems: "center",
134
+ justifyContent: "center"
135
+ },
136
+ chipLabel: {
137
+ fontFamily: "Sohne-Medium",
138
+ fontSize: ms(13),
139
+ lineHeight: mvs(18)
140
+ },
141
+ error: {
142
+ fontFamily: "Sohne-Regular",
143
+ fontSize: ms(13)
144
+ }
145
+ });
146
+
147
+ export { SheetSelect };
@@ -0,0 +1,112 @@
1
+ import { Spinner } from './chunk-WBOOUHSS.mjs';
2
+ import { PressableCard } from './chunk-3DKJ2GIC.mjs';
3
+ import { impactLight } from './chunk-EJ7ZPXOH.mjs';
4
+ import { RADIUS } from './chunk-QY3X2UYR.mjs';
5
+ import { useTheme } from './chunk-SOYNZDVY.mjs';
6
+ import { s, vs, ms } from './chunk-2CE3TQVY.mjs';
7
+ import React from 'react';
8
+ import { StyleSheet, Image, View, Text, Platform } from 'react-native';
9
+ import { Feather } from '@expo/vector-icons';
10
+
11
+ function ImageUpload({
12
+ value,
13
+ onChange,
14
+ loading = false,
15
+ placeholder = "Tap to add image",
16
+ width,
17
+ height = 200,
18
+ borderRadius = RADIUS.lg,
19
+ resizeMode = "cover",
20
+ disabled = false,
21
+ style,
22
+ accessibilityLabel
23
+ }) {
24
+ const { colors } = useTheme();
25
+ const handlePress = async () => {
26
+ if (disabled || loading) return;
27
+ impactLight();
28
+ let ImagePicker;
29
+ try {
30
+ ImagePicker = await import('expo-image-picker');
31
+ } catch {
32
+ if (__DEV__) console.warn("[ImageUpload] expo-image-picker not installed. Add it as a dependency.");
33
+ return;
34
+ }
35
+ if (Platform.OS !== "web") {
36
+ const { status } = await ImagePicker.requestMediaLibraryPermissionsAsync();
37
+ if (status !== "granted") return;
38
+ }
39
+ const result = await ImagePicker.launchImageLibraryAsync({
40
+ mediaTypes: ["images"],
41
+ allowsEditing: true,
42
+ quality: 0.8
43
+ });
44
+ if (!result.canceled && result.assets[0]) {
45
+ onChange?.(result.assets[0].uri);
46
+ }
47
+ };
48
+ const containerStyle = {
49
+ width,
50
+ height,
51
+ borderRadius,
52
+ borderWidth: value ? 0 : 1,
53
+ borderStyle: "dashed",
54
+ borderColor: colors.border,
55
+ backgroundColor: value ? "transparent" : colors.surface,
56
+ overflow: "hidden"
57
+ };
58
+ return /* @__PURE__ */ React.createElement(
59
+ PressableCard,
60
+ {
61
+ onPress: handlePress,
62
+ enabled: !disabled && !loading,
63
+ rippleColor: "transparent",
64
+ touchSoundDisabled: true,
65
+ accessibilityRole: "button",
66
+ accessibilityLabel: accessibilityLabel ?? (value ? "Change image" : placeholder),
67
+ accessibilityState: { disabled: disabled || loading },
68
+ style: [containerStyle, style]
69
+ },
70
+ value ? /* @__PURE__ */ React.createElement(
71
+ Image,
72
+ {
73
+ source: { uri: value },
74
+ style: [StyleSheet.absoluteFillObject, { borderRadius }],
75
+ resizeMode
76
+ }
77
+ ) : /* @__PURE__ */ React.createElement(View, { style: styles.placeholder }, /* @__PURE__ */ React.createElement(Feather, { name: "image", size: ms(28), color: colors.foregroundMuted }), /* @__PURE__ */ React.createElement(Text, { style: [styles.placeholderText, { color: colors.foregroundMuted }], allowFontScaling: true }, placeholder)),
78
+ loading ? /* @__PURE__ */ React.createElement(View, { style: [styles.loadingOverlay, { backgroundColor: colors.overlay }] }, /* @__PURE__ */ React.createElement(Spinner, { size: "md" })) : null,
79
+ value && !loading ? /* @__PURE__ */ React.createElement(View, { style: styles.editBadge, pointerEvents: "none" }, /* @__PURE__ */ React.createElement(View, { style: [styles.editBadgeInner, { backgroundColor: colors.overlay }] }, /* @__PURE__ */ React.createElement(Feather, { name: "edit-2", size: ms(12), color: "#fff" }))) : null
80
+ );
81
+ }
82
+ var styles = StyleSheet.create({
83
+ placeholder: {
84
+ flex: 1,
85
+ alignItems: "center",
86
+ justifyContent: "center",
87
+ gap: vs(8)
88
+ },
89
+ placeholderText: {
90
+ fontFamily: "Sohne-Regular",
91
+ fontSize: ms(13)
92
+ },
93
+ loadingOverlay: {
94
+ ...StyleSheet.absoluteFillObject,
95
+ alignItems: "center",
96
+ justifyContent: "center"
97
+ },
98
+ editBadge: {
99
+ position: "absolute",
100
+ bottom: vs(8),
101
+ right: s(8)
102
+ },
103
+ editBadgeInner: {
104
+ width: s(28),
105
+ height: s(28),
106
+ borderRadius: 999,
107
+ alignItems: "center",
108
+ justifyContent: "center"
109
+ }
110
+ });
111
+
112
+ export { ImageUpload };
package/dist/index.d.mts CHANGED
@@ -49,6 +49,8 @@ export { SelectableGrid, SelectableGridItem, SelectableGridProps } from './Selec
49
49
  export { PricingCard, PricingCardProps, PricingFeature } from './PricingCard.mjs';
50
50
  export { TabBar, TabBarItem, TabBarProps } from './TabBar.mjs';
51
51
  export { ImageViewer, ImageViewerProps } from './ImageViewer.mjs';
52
+ export { SheetSelect, SheetSelectOption, SheetSelectProps } from './SheetSelect.mjs';
53
+ export { ImageUpload, ImageUploadProps } from './ImageUpload.mjs';
52
54
  export { BottomSheetModalProvider, BottomSheetTextInput as SheetTextInput } from '@gorhom/bottom-sheet';
53
55
  export { toast } from 'sonner-native';
54
56
  import 'react-native';
@@ -158,6 +160,29 @@ declare const richHaptics: {
158
160
  rigid: () => void;
159
161
  };
160
162
 
163
+ interface UseConfirmDialogOptions {
164
+ onConfirm: () => void | Promise<void>;
165
+ onCancel?: () => void;
166
+ }
167
+ interface UseConfirmDialogResult<T> {
168
+ /** Pass to ConfirmDialog `visible` prop. */
169
+ visible: boolean;
170
+ /** The value passed to `open()` — available during the confirmation flow. */
171
+ target: T | null;
172
+ /** Whether `onConfirm` is currently executing. Pass to ConfirmDialog `loading` prop. */
173
+ loading: boolean;
174
+ /** Open the dialog, optionally with an associated value (e.g. the item to delete). */
175
+ open: (target?: T) => void;
176
+ /** Handlers to pass directly to ConfirmDialog. */
177
+ dialogProps: {
178
+ visible: boolean;
179
+ loading: boolean;
180
+ onConfirm: () => void;
181
+ onCancel: () => void;
182
+ };
183
+ }
184
+ declare function useConfirmDialog<T = undefined>(options: UseConfirmDialogOptions): UseConfirmDialogResult<T>;
185
+
161
186
  declare const SPACING: {
162
187
  readonly xxs: 2;
163
188
  readonly xs: 4;
@@ -361,4 +386,4 @@ type RadiusKey = keyof Radius;
361
386
  type Typography = typeof TYPOGRAPHY;
362
387
  type TypographyKey = keyof Typography;
363
388
 
364
- export { BREAKPOINTS, ColorScheme, ICON_SIZES, Icon, type IconFamily, type IconProps, type IconSize, type IconSizeKey, RADIUS, type Radius, type RadiusKey, ResolvedColors, SHADOWS, SPACING, type Spacing, type SpacingKey, TYPOGRAPHY, Theme, ThemeColors, ThemeProvider, type ThemeProviderProps, type Typography, type TypographyKey, configureIconFamilies, defaultDark, defaultLight, deriveColors, getResponsiveFontSize, impactHeavy, impactLight, impactMedium, notificationError, notificationSuccess, notificationWarning, renderIcon, richHaptics, selectionAsync, useTheme };
389
+ export { BREAKPOINTS, ColorScheme, ICON_SIZES, Icon, type IconFamily, type IconProps, type IconSize, type IconSizeKey, RADIUS, type Radius, type RadiusKey, ResolvedColors, SHADOWS, SPACING, type Spacing, type SpacingKey, TYPOGRAPHY, Theme, ThemeColors, ThemeProvider, type ThemeProviderProps, type Typography, type TypographyKey, type UseConfirmDialogOptions, type UseConfirmDialogResult, configureIconFamilies, defaultDark, defaultLight, deriveColors, getResponsiveFontSize, impactHeavy, impactLight, impactMedium, notificationError, notificationSuccess, notificationWarning, renderIcon, richHaptics, selectionAsync, useConfirmDialog, useTheme };
package/dist/index.d.ts CHANGED
@@ -49,6 +49,8 @@ export { SelectableGrid, SelectableGridItem, SelectableGridProps } from './Selec
49
49
  export { PricingCard, PricingCardProps, PricingFeature } from './PricingCard.js';
50
50
  export { TabBar, TabBarItem, TabBarProps } from './TabBar.js';
51
51
  export { ImageViewer, ImageViewerProps } from './ImageViewer.js';
52
+ export { SheetSelect, SheetSelectOption, SheetSelectProps } from './SheetSelect.js';
53
+ export { ImageUpload, ImageUploadProps } from './ImageUpload.js';
52
54
  export { BottomSheetModalProvider, BottomSheetTextInput as SheetTextInput } from '@gorhom/bottom-sheet';
53
55
  export { toast } from 'sonner-native';
54
56
  import 'react-native';
@@ -158,6 +160,29 @@ declare const richHaptics: {
158
160
  rigid: () => void;
159
161
  };
160
162
 
163
+ interface UseConfirmDialogOptions {
164
+ onConfirm: () => void | Promise<void>;
165
+ onCancel?: () => void;
166
+ }
167
+ interface UseConfirmDialogResult<T> {
168
+ /** Pass to ConfirmDialog `visible` prop. */
169
+ visible: boolean;
170
+ /** The value passed to `open()` — available during the confirmation flow. */
171
+ target: T | null;
172
+ /** Whether `onConfirm` is currently executing. Pass to ConfirmDialog `loading` prop. */
173
+ loading: boolean;
174
+ /** Open the dialog, optionally with an associated value (e.g. the item to delete). */
175
+ open: (target?: T) => void;
176
+ /** Handlers to pass directly to ConfirmDialog. */
177
+ dialogProps: {
178
+ visible: boolean;
179
+ loading: boolean;
180
+ onConfirm: () => void;
181
+ onCancel: () => void;
182
+ };
183
+ }
184
+ declare function useConfirmDialog<T = undefined>(options: UseConfirmDialogOptions): UseConfirmDialogResult<T>;
185
+
161
186
  declare const SPACING: {
162
187
  readonly xxs: 2;
163
188
  readonly xs: 4;
@@ -361,4 +386,4 @@ type RadiusKey = keyof Radius;
361
386
  type Typography = typeof TYPOGRAPHY;
362
387
  type TypographyKey = keyof Typography;
363
388
 
364
- export { BREAKPOINTS, ColorScheme, ICON_SIZES, Icon, type IconFamily, type IconProps, type IconSize, type IconSizeKey, RADIUS, type Radius, type RadiusKey, ResolvedColors, SHADOWS, SPACING, type Spacing, type SpacingKey, TYPOGRAPHY, Theme, ThemeColors, ThemeProvider, type ThemeProviderProps, type Typography, type TypographyKey, configureIconFamilies, defaultDark, defaultLight, deriveColors, getResponsiveFontSize, impactHeavy, impactLight, impactMedium, notificationError, notificationSuccess, notificationWarning, renderIcon, richHaptics, selectionAsync, useTheme };
389
+ export { BREAKPOINTS, ColorScheme, ICON_SIZES, Icon, type IconFamily, type IconProps, type IconSize, type IconSizeKey, RADIUS, type Radius, type RadiusKey, ResolvedColors, SHADOWS, SPACING, type Spacing, type SpacingKey, TYPOGRAPHY, Theme, ThemeColors, ThemeProvider, type ThemeProviderProps, type Typography, type TypographyKey, type UseConfirmDialogOptions, type UseConfirmDialogResult, configureIconFamilies, defaultDark, defaultLight, deriveColors, getResponsiveFontSize, impactHeavy, impactLight, impactMedium, notificationError, notificationSuccess, notificationWarning, renderIcon, richHaptics, selectionAsync, useConfirmDialog, useTheme };