@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.
Files changed (64) hide show
  1. package/COMPONENTS.md +166 -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 +52 -39
  17. package/dist/CurrencyInput.mjs +2 -3
  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 +48 -37
  25. package/dist/Input.mjs +1 -2
  26. package/dist/ListItem.d.mts +9 -2
  27. package/dist/ListItem.d.ts +9 -2
  28. package/dist/ListItem.js +9 -2
  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/Textarea.mjs +1 -2
  35. package/dist/{chunk-M6ZXVBTK.mjs → chunk-6MKGPAR2.mjs} +21 -5
  36. package/dist/{chunk-EH745HE5.mjs → chunk-CZCQZHG6.mjs} +13 -4
  37. package/dist/{chunk-7QHVVCB3.mjs → chunk-FZZLPJ6B.mjs} +3 -0
  38. package/dist/{chunk-MAC465BB.mjs → chunk-JUXSWN54.mjs} +5 -3
  39. package/dist/{chunk-BNP626TY.mjs → chunk-OHBNABL5.mjs} +10 -3
  40. package/dist/chunk-URI2WBIV.mjs +147 -0
  41. package/dist/chunk-Y4GL2MHX.mjs +112 -0
  42. package/dist/{chunk-756RAKE4.mjs → chunk-ZUR7AU5R.mjs} +38 -20
  43. package/dist/fonts.d.mts +32 -0
  44. package/dist/fonts.d.ts +32 -0
  45. package/dist/fonts.js +44 -0
  46. package/dist/fonts.mjs +37 -0
  47. package/dist/index.d.mts +26 -1
  48. package/dist/index.d.ts +26 -1
  49. package/dist/index.js +425 -106
  50. package/dist/index.mjs +55 -17
  51. package/package.json +23 -6
  52. package/src/components/AlertBanner/AlertBanner.tsx +21 -3
  53. package/src/components/ConfirmDialog/ConfirmDialog.tsx +5 -0
  54. package/src/components/CurrencyInput/CurrencyInput.tsx +4 -0
  55. package/src/components/ImageUpload/ImageUpload.tsx +158 -0
  56. package/src/components/ImageUpload/index.ts +1 -0
  57. package/src/components/Input/Input.tsx +64 -53
  58. package/src/components/ListItem/ListItem.tsx +23 -4
  59. package/src/components/SheetSelect/SheetSelect.tsx +192 -0
  60. package/src/components/SheetSelect/index.ts +1 -0
  61. package/src/fonts.ts +30 -29
  62. package/src/hooks/useConfirmDialog.ts +67 -0
  63. package/src/index.ts +6 -0
  64. package/dist/chunk-26BCI223.mjs +0 -14
@@ -0,0 +1,399 @@
1
+ 'use strict';
2
+
3
+ var React3 = require('react');
4
+ var reactNative = require('react-native');
5
+ var vectorIcons = require('@expo/vector-icons');
6
+ var reactNativeSizeMatters = require('react-native-size-matters');
7
+ var pressto = require('pressto');
8
+ var reactNativeReanimated = require('react-native-reanimated');
9
+
10
+ function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
11
+
12
+ var React3__default = /*#__PURE__*/_interopDefault(React3);
13
+
14
+ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
15
+ get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
16
+ }) : x)(function(x) {
17
+ if (typeof require !== "undefined") return require.apply(this, arguments);
18
+ throw Error('Dynamic require of "' + x + '" is not supported');
19
+ });
20
+ var _haptics = null;
21
+ var _hapticsLoaded = false;
22
+ async function getHaptics() {
23
+ if (reactNative.Platform.OS === "web") return null;
24
+ if (!_hapticsLoaded) {
25
+ _hapticsLoaded = true;
26
+ try {
27
+ _haptics = await import('expo-haptics');
28
+ } catch {
29
+ _haptics = null;
30
+ }
31
+ }
32
+ return _haptics;
33
+ }
34
+ var _pulsar = null;
35
+ var _pulsarChecked = false;
36
+ var _pulsarAvailable = false;
37
+ function isPulsarNativeRegistered() {
38
+ try {
39
+ const g = globalThis;
40
+ if (typeof g.__turboModuleProxy === "function") {
41
+ return g.__turboModuleProxy("RNPulsar") != null;
42
+ }
43
+ return reactNative.NativeModules?.RNPulsar != null;
44
+ } catch {
45
+ return false;
46
+ }
47
+ }
48
+ function getPulsar() {
49
+ if (reactNative.Platform.OS === "web") return null;
50
+ if (!_pulsarChecked) {
51
+ _pulsarChecked = true;
52
+ try {
53
+ if (isPulsarNativeRegistered()) {
54
+ _pulsar = __require("react-native-pulsar");
55
+ _pulsarAvailable = true;
56
+ }
57
+ } catch {
58
+ _pulsar = null;
59
+ _pulsarAvailable = false;
60
+ }
61
+ }
62
+ return _pulsarAvailable ? _pulsar : null;
63
+ }
64
+ function impactLight() {
65
+ if (reactNative.Platform.OS === "web") return;
66
+ getHaptics().then((h) => {
67
+ if (h) {
68
+ h.impactAsync(h.ImpactFeedbackStyle.Light);
69
+ } else {
70
+ getPulsar()?.Presets.System.impactLight();
71
+ }
72
+ });
73
+ }
74
+
75
+ // src/theme/colorUtils.ts
76
+ function hexToRgb(hex) {
77
+ const clean = hex.replace("#", "");
78
+ const full = clean.length === 3 ? clean.split("").map((c) => c + c).join("") : clean;
79
+ if (full.length !== 6) return null;
80
+ return {
81
+ r: parseInt(full.slice(0, 2), 16),
82
+ g: parseInt(full.slice(2, 4), 16),
83
+ b: parseInt(full.slice(4, 6), 16)
84
+ };
85
+ }
86
+ function componentToHex(c) {
87
+ return Math.round(Math.max(0, Math.min(255, c))).toString(16).padStart(2, "0");
88
+ }
89
+ function rgbToHex(r, g, b) {
90
+ return `#${componentToHex(r)}${componentToHex(g)}${componentToHex(b)}`;
91
+ }
92
+ function withAlphaOnWhite(hex, alpha) {
93
+ const rgb = hexToRgb(hex);
94
+ if (!rgb) return hex;
95
+ const r = rgb.r * alpha + 255 * (1 - alpha);
96
+ const g = rgb.g * alpha + 255 * (1 - alpha);
97
+ const b = rgb.b * alpha + 255 * (1 - alpha);
98
+ return rgbToHex(r, g, b);
99
+ }
100
+ function withAlphaOnDark(hex, alpha, bgHex = "#0f0f0f") {
101
+ const rgb = hexToRgb(hex);
102
+ const bg = hexToRgb(bgHex);
103
+ if (!rgb || !bg) return hex;
104
+ const r = rgb.r * alpha + bg.r * (1 - alpha);
105
+ const g = rgb.g * alpha + bg.g * (1 - alpha);
106
+ const b = rgb.b * alpha + bg.b * (1 - alpha);
107
+ return rgbToHex(r, g, b);
108
+ }
109
+ function mixWithBackground(fgHex, bgHex, opacity) {
110
+ const fg = hexToRgb(fgHex);
111
+ const bg = hexToRgb(bgHex);
112
+ if (!fg || !bg) return fgHex;
113
+ const r = fg.r * opacity + bg.r * (1 - opacity);
114
+ const g = fg.g * opacity + bg.g * (1 - opacity);
115
+ const b = fg.b * opacity + bg.b * (1 - opacity);
116
+ return rgbToHex(r, g, b);
117
+ }
118
+ function lighten(hex, amount) {
119
+ return withAlphaOnWhite(hex, 1 - amount);
120
+ }
121
+ function darken(hex, amount) {
122
+ const rgb = hexToRgb(hex);
123
+ if (!rgb) return hex;
124
+ return rgbToHex(rgb.r * (1 - amount), rgb.g * (1 - amount), rgb.b * (1 - amount));
125
+ }
126
+
127
+ // src/theme/colors.ts
128
+ var defaultLight = {
129
+ background: "#ffffff",
130
+ foreground: "#1a1a1a",
131
+ card: "#ffffff",
132
+ primary: "#1a1a1a",
133
+ primaryForeground: "#ffffff",
134
+ // AUDIT FIX: brand accent — was undefined; falls back to primary when omitted
135
+ accent: "#d4561d",
136
+ accentForeground: "#ffffff",
137
+ border: "#dddddd",
138
+ // AUDIT FIX: was #e53935 (4.22:1 on white — fails AA); #c72828 = 5.59:1 ✓
139
+ destructive: "#c72828",
140
+ destructiveForeground: "#ffffff",
141
+ success: "#1a7a45",
142
+ successForeground: "#ffffff",
143
+ // AUDIT FIX: was #e67e00 (2.86:1 — severe fail); #9a5200 = 5.86:1 ✓ AAA-near
144
+ warning: "#9a5200",
145
+ warningForeground: "#ffffff"
146
+ };
147
+ function deriveColors(t, scheme) {
148
+ const dark = scheme === "dark";
149
+ const bg = t.background;
150
+ const foregroundSubtle = mixWithBackground(t.foreground, bg, 0.7);
151
+ const foregroundMuted = mixWithBackground(t.foreground, bg, 0.62);
152
+ const surface = dark ? lighten(bg, -0.06) : darken(bg, 0.04);
153
+ const surfaceStrong = dark ? lighten(bg, -0.12) : darken(bg, 0.08);
154
+ const destructiveTint = dark ? withAlphaOnDark(t.destructive, 0.15, bg) : withAlphaOnWhite(t.destructive, 0.08);
155
+ const destructiveBorder = dark ? withAlphaOnDark(t.destructive, 0.45, bg) : withAlphaOnWhite(t.destructive, 0.3);
156
+ const successTint = dark ? withAlphaOnDark(t.success, 0.15, bg) : withAlphaOnWhite(t.success, 0.08);
157
+ const successBorder = dark ? withAlphaOnDark(t.success, 0.45, bg) : withAlphaOnWhite(t.success, 0.3);
158
+ const warningTint = dark ? withAlphaOnDark(t.warning, 0.15, bg) : withAlphaOnWhite(t.warning, 0.08);
159
+ const warningBorder = dark ? withAlphaOnDark(t.warning, 0.45, bg) : withAlphaOnWhite(t.warning, 0.3);
160
+ return {
161
+ ...t,
162
+ foregroundSubtle,
163
+ foregroundMuted,
164
+ surface,
165
+ surfaceStrong,
166
+ destructiveTint,
167
+ destructiveBorder,
168
+ successTint,
169
+ successBorder,
170
+ warningTint,
171
+ warningBorder,
172
+ overlay: t.overlay ?? "rgba(0,0,0,0.45)",
173
+ accentResolved: t.accent ?? t.primary,
174
+ accentForegroundResolved: t.accentForeground ?? t.primaryForeground,
175
+ ring: t.accent ?? t.primary,
176
+ input: t.border,
177
+ separator: dark ? lighten(t.border, 0.22) : darken(t.border, 0.16)
178
+ };
179
+ }
180
+
181
+ // src/theme/ThemeProvider.tsx
182
+ var ThemeContext = React3.createContext({
183
+ colors: deriveColors(defaultLight, "light"),
184
+ colorScheme: "light"
185
+ });
186
+ function useTheme() {
187
+ const context = React3.useContext(ThemeContext);
188
+ if (!context) {
189
+ throw new Error("useTheme must be used within a ThemeProvider");
190
+ }
191
+ return context;
192
+ }
193
+ var isWeb = reactNative.Platform.OS === "web";
194
+ var s = isWeb ? (n) => n : reactNativeSizeMatters.scale;
195
+ var vs = isWeb ? (n) => n : reactNativeSizeMatters.verticalScale;
196
+ var ms = isWeb ? (n, _factor) => n : reactNativeSizeMatters.moderateScale;
197
+ var mvs = isWeb ? (n, _factor) => n : reactNativeSizeMatters.moderateVerticalScale;
198
+
199
+ // src/tokens.ts
200
+ var RADIUS = {
201
+ lg: 20};
202
+ var sizeMap = {
203
+ sm: "small",
204
+ md: "small",
205
+ lg: "large"
206
+ };
207
+ var labelFontSize = {
208
+ sm: ms(11),
209
+ md: ms(13),
210
+ lg: ms(14)
211
+ };
212
+ function Spinner({ size = "md", color, label, ...props }) {
213
+ const { colors } = useTheme();
214
+ const a11yLabel = label || "Loading";
215
+ if (label) {
216
+ return /* @__PURE__ */ React3__default.default.createElement(
217
+ reactNative.View,
218
+ {
219
+ style: styles.wrapper,
220
+ accessibilityRole: "progressbar",
221
+ accessibilityLabel: a11yLabel,
222
+ accessibilityState: { busy: true }
223
+ },
224
+ /* @__PURE__ */ React3__default.default.createElement(reactNative.ActivityIndicator, { size: sizeMap[size], color: color ?? colors.primary, ...props }),
225
+ /* @__PURE__ */ React3__default.default.createElement(
226
+ reactNative.Text,
227
+ {
228
+ style: [styles.label, { color: colors.foregroundMuted, fontSize: labelFontSize[size] }],
229
+ allowFontScaling: true
230
+ },
231
+ label
232
+ )
233
+ );
234
+ }
235
+ return /* @__PURE__ */ React3__default.default.createElement(
236
+ reactNative.ActivityIndicator,
237
+ {
238
+ size: sizeMap[size],
239
+ color: color ?? colors.primary,
240
+ accessibilityRole: "progressbar",
241
+ accessibilityLabel: a11yLabel,
242
+ accessibilityState: { busy: true },
243
+ ...props
244
+ }
245
+ );
246
+ }
247
+ var styles = reactNative.StyleSheet.create({
248
+ wrapper: {
249
+ alignItems: "center",
250
+ gap: vs(6)
251
+ },
252
+ label: {
253
+ fontFamily: "Sohne-Regular",
254
+ lineHeight: mvs(18)
255
+ }
256
+ });
257
+ ({
258
+ /** Material-style ease-out — natural deceleration for state changes. */
259
+ standard: reactNativeReanimated.Easing.bezier(0.2, 0, 0, 1),
260
+ /** Strong ease-out for expanding surfaces (Accordion open). */
261
+ expand: reactNativeReanimated.Easing.bezier(0.23, 1, 0.32, 1),
262
+ /** Quick ease-in for collapsing. */
263
+ collapse: reactNativeReanimated.Easing.in(reactNativeReanimated.Easing.ease)
264
+ });
265
+ var PRESS_SCALE = {
266
+ button: 0.95,
267
+ card: 0.98,
268
+ row: 0.97,
269
+ chip: 0.94
270
+ };
271
+ pressto.createAnimatedPressable((progress) => {
272
+ "worklet";
273
+ const scale2 = 1 - (1 - PRESS_SCALE.button) * progress;
274
+ return { transform: [{ scale: scale2 }] };
275
+ });
276
+ var PressableCard = pressto.createAnimatedPressable((progress) => {
277
+ "worklet";
278
+ const scale2 = 1 - (1 - PRESS_SCALE.card) * progress;
279
+ return { transform: [{ scale: scale2 }] };
280
+ });
281
+ pressto.createAnimatedPressable((progress) => {
282
+ "worklet";
283
+ const scale2 = 1 - (1 - PRESS_SCALE.row) * progress;
284
+ return { transform: [{ scale: scale2 }] };
285
+ });
286
+ pressto.createAnimatedPressable((progress) => {
287
+ "worklet";
288
+ const scale2 = 1 - (1 - PRESS_SCALE.chip) * progress;
289
+ return { transform: [{ scale: scale2 }] };
290
+ });
291
+ pressto.createAnimatedPressable((progress) => {
292
+ "worklet";
293
+ const scale2 = 1 - (1 - PRESS_SCALE.button) * progress;
294
+ return { transform: [{ scale: scale2 }] };
295
+ });
296
+
297
+ // src/components/ImageUpload/ImageUpload.tsx
298
+ function ImageUpload({
299
+ value,
300
+ onChange,
301
+ loading = false,
302
+ placeholder = "Tap to add image",
303
+ width,
304
+ height = 200,
305
+ borderRadius = RADIUS.lg,
306
+ resizeMode = "cover",
307
+ disabled = false,
308
+ style,
309
+ accessibilityLabel
310
+ }) {
311
+ const { colors } = useTheme();
312
+ const handlePress = async () => {
313
+ if (disabled || loading) return;
314
+ impactLight();
315
+ let ImagePicker;
316
+ try {
317
+ ImagePicker = await import('expo-image-picker');
318
+ } catch {
319
+ if (__DEV__) console.warn("[ImageUpload] expo-image-picker not installed. Add it as a dependency.");
320
+ return;
321
+ }
322
+ if (reactNative.Platform.OS !== "web") {
323
+ const { status } = await ImagePicker.requestMediaLibraryPermissionsAsync();
324
+ if (status !== "granted") return;
325
+ }
326
+ const result = await ImagePicker.launchImageLibraryAsync({
327
+ mediaTypes: ["images"],
328
+ allowsEditing: true,
329
+ quality: 0.8
330
+ });
331
+ if (!result.canceled && result.assets[0]) {
332
+ onChange?.(result.assets[0].uri);
333
+ }
334
+ };
335
+ const containerStyle = {
336
+ width,
337
+ height,
338
+ borderRadius,
339
+ borderWidth: value ? 0 : 1,
340
+ borderStyle: "dashed",
341
+ borderColor: colors.border,
342
+ backgroundColor: value ? "transparent" : colors.surface,
343
+ overflow: "hidden"
344
+ };
345
+ return /* @__PURE__ */ React3__default.default.createElement(
346
+ PressableCard,
347
+ {
348
+ onPress: handlePress,
349
+ enabled: !disabled && !loading,
350
+ rippleColor: "transparent",
351
+ touchSoundDisabled: true,
352
+ accessibilityRole: "button",
353
+ accessibilityLabel: accessibilityLabel ?? (value ? "Change image" : placeholder),
354
+ accessibilityState: { disabled: disabled || loading },
355
+ style: [containerStyle, style]
356
+ },
357
+ value ? /* @__PURE__ */ React3__default.default.createElement(
358
+ reactNative.Image,
359
+ {
360
+ source: { uri: value },
361
+ style: [reactNative.StyleSheet.absoluteFillObject, { borderRadius }],
362
+ resizeMode
363
+ }
364
+ ) : /* @__PURE__ */ React3__default.default.createElement(reactNative.View, { style: styles2.placeholder }, /* @__PURE__ */ React3__default.default.createElement(vectorIcons.Feather, { name: "image", size: ms(28), color: colors.foregroundMuted }), /* @__PURE__ */ React3__default.default.createElement(reactNative.Text, { style: [styles2.placeholderText, { color: colors.foregroundMuted }], allowFontScaling: true }, placeholder)),
365
+ loading ? /* @__PURE__ */ React3__default.default.createElement(reactNative.View, { style: [styles2.loadingOverlay, { backgroundColor: colors.overlay }] }, /* @__PURE__ */ React3__default.default.createElement(Spinner, { size: "md" })) : null,
366
+ value && !loading ? /* @__PURE__ */ React3__default.default.createElement(reactNative.View, { style: styles2.editBadge, pointerEvents: "none" }, /* @__PURE__ */ React3__default.default.createElement(reactNative.View, { style: [styles2.editBadgeInner, { backgroundColor: colors.overlay }] }, /* @__PURE__ */ React3__default.default.createElement(vectorIcons.Feather, { name: "edit-2", size: ms(12), color: "#fff" }))) : null
367
+ );
368
+ }
369
+ var styles2 = reactNative.StyleSheet.create({
370
+ placeholder: {
371
+ flex: 1,
372
+ alignItems: "center",
373
+ justifyContent: "center",
374
+ gap: vs(8)
375
+ },
376
+ placeholderText: {
377
+ fontFamily: "Sohne-Regular",
378
+ fontSize: ms(13)
379
+ },
380
+ loadingOverlay: {
381
+ ...reactNative.StyleSheet.absoluteFillObject,
382
+ alignItems: "center",
383
+ justifyContent: "center"
384
+ },
385
+ editBadge: {
386
+ position: "absolute",
387
+ bottom: vs(8),
388
+ right: s(8)
389
+ },
390
+ editBadgeInner: {
391
+ width: s(28),
392
+ height: s(28),
393
+ borderRadius: 999,
394
+ alignItems: "center",
395
+ justifyContent: "center"
396
+ }
397
+ });
398
+
399
+ exports.ImageUpload = ImageUpload;
@@ -0,0 +1,9 @@
1
+ export { ImageUpload } from './chunk-Y4GL2MHX.mjs';
2
+ import './chunk-WBOOUHSS.mjs';
3
+ import './chunk-3DKJ2GIC.mjs';
4
+ import './chunk-EJ7ZPXOH.mjs';
5
+ import './chunk-DVK4G2GT.mjs';
6
+ import './chunk-QY3X2UYR.mjs';
7
+ import './chunk-SOYNZDVY.mjs';
8
+ import './chunk-2CE3TQVY.mjs';
9
+ import './chunk-Y6FXYEAI.mjs';
package/dist/Input.d.mts CHANGED
@@ -17,7 +17,9 @@ interface InputProps extends TextInputProps {
17
17
  type?: 'text' | 'password';
18
18
  containerStyle?: ViewStyle;
19
19
  inputWrapperStyle?: ViewStyle;
20
+ /** Use inside a Sheet/BottomSheet — swaps TextInput for BottomSheetTextInput to fix keyboard handling. */
21
+ sheetMode?: boolean;
20
22
  }
21
- declare function Input({ label, error, hint, disabled, prefix, suffix, prefixStyle, suffixStyle, prefixIcon, suffixIcon, prefixIconColor, suffixIconColor, type, containerStyle, inputWrapperStyle, style, onFocus, onBlur, secureTextEntry, editable, accessibilityLabel, ...props }: InputProps): React.JSX.Element;
23
+ declare function Input({ label, error, hint, disabled, prefix, suffix, prefixStyle, suffixStyle, prefixIcon, suffixIcon, prefixIconColor, suffixIconColor, type, containerStyle, inputWrapperStyle, sheetMode, style, onFocus, onBlur, secureTextEntry, editable, accessibilityLabel, ...props }: InputProps): React.JSX.Element;
22
24
 
23
25
  export { Input, type InputProps };
package/dist/Input.d.ts CHANGED
@@ -17,7 +17,9 @@ interface InputProps extends TextInputProps {
17
17
  type?: 'text' | 'password';
18
18
  containerStyle?: ViewStyle;
19
19
  inputWrapperStyle?: ViewStyle;
20
+ /** Use inside a Sheet/BottomSheet — swaps TextInput for BottomSheetTextInput to fix keyboard handling. */
21
+ sheetMode?: boolean;
20
22
  }
21
- declare function Input({ label, error, hint, disabled, prefix, suffix, prefixStyle, suffixStyle, prefixIcon, suffixIcon, prefixIconColor, suffixIconColor, type, containerStyle, inputWrapperStyle, style, onFocus, onBlur, secureTextEntry, editable, accessibilityLabel, ...props }: InputProps): React.JSX.Element;
23
+ declare function Input({ label, error, hint, disabled, prefix, suffix, prefixStyle, suffixStyle, prefixIcon, suffixIcon, prefixIconColor, suffixIconColor, type, containerStyle, inputWrapperStyle, sheetMode, style, onFocus, onBlur, secureTextEntry, editable, accessibilityLabel, ...props }: InputProps): React.JSX.Element;
22
24
 
23
25
  export { Input, type InputProps };
package/dist/Input.js CHANGED
@@ -2,7 +2,8 @@
2
2
 
3
3
  var React3 = require('react');
4
4
  var reactNative = require('react-native');
5
- var Animated = require('react-native-reanimated');
5
+ var bottomSheet = require('@gorhom/bottom-sheet');
6
+ var reactNativeEase = require('react-native-ease');
6
7
  var vectorIcons = require('@expo/vector-icons');
7
8
  var reactNativeSizeMatters = require('react-native-size-matters');
8
9
  var AntDesign = require('@expo/vector-icons/AntDesign');
@@ -11,11 +12,11 @@ var Feather = require('@expo/vector-icons/Feather');
11
12
  var FontAwesome5 = require('@expo/vector-icons/FontAwesome5');
12
13
  var MaterialIcons = require('@expo/vector-icons/MaterialIcons');
13
14
  var Ionicons = require('@expo/vector-icons/Ionicons');
15
+ var reactNativeReanimated = require('react-native-reanimated');
14
16
 
15
17
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
16
18
 
17
19
  var React3__default = /*#__PURE__*/_interopDefault(React3);
18
- var Animated__default = /*#__PURE__*/_interopDefault(Animated);
19
20
  var AntDesign__default = /*#__PURE__*/_interopDefault(AntDesign);
20
21
  var Entypo__default = /*#__PURE__*/_interopDefault(Entypo);
21
22
  var Feather__default = /*#__PURE__*/_interopDefault(Feather);
@@ -190,38 +191,27 @@ function renderIcon(name, size, color) {
190
191
  }
191
192
  var TIMINGS = {
192
193
  /** Color/opacity transitions on toggles, checkboxes, switches. */
193
- state: { duration: 160 },
194
- /** Focus ring on inputs. */
195
- focusIn: { duration: 140 },
196
- focusOut: { duration: 100 }};
197
- var EASINGS = {
194
+ state: { duration: 160 }};
195
+ ({
198
196
  /** Material-style ease-out — natural deceleration for state changes. */
199
- standard: Animated.Easing.bezier(0.2, 0, 0, 1),
197
+ standard: reactNativeReanimated.Easing.bezier(0.2, 0, 0, 1),
200
198
  /** Strong ease-out for expanding surfaces (Accordion open). */
201
- expand: Animated.Easing.bezier(0.23, 1, 0.32, 1),
199
+ expand: reactNativeReanimated.Easing.bezier(0.23, 1, 0.32, 1),
202
200
  /** Quick ease-in for collapsing. */
203
- collapse: Animated.Easing.in(Animated.Easing.ease)
201
+ collapse: reactNativeReanimated.Easing.in(reactNativeReanimated.Easing.ease)
202
+ });
203
+ var COLOR_TRANSITION = {
204
+ type: "timing",
205
+ duration: TIMINGS.state.duration,
206
+ easing: [0.2, 0, 0, 1]
204
207
  };
205
208
 
206
- // src/utils/useColorTransition.ts
207
- function useColorTransition(active, options = {}) {
208
- const { duration = TIMINGS.state.duration } = options;
209
- const progress = Animated.useSharedValue(active ? 1 : 0);
210
- React3.useEffect(() => {
211
- progress.value = Animated.withTiming(active ? 1 : 0, { duration, easing: EASINGS.standard });
212
- }, [active, duration, progress]);
213
- return progress;
214
- }
215
-
216
209
  // src/components/Input/Input.tsx
217
210
  var webInputResetStyle = reactNative.Platform.OS === "web" ? { outlineStyle: "none", outlineWidth: 0, outlineColor: "transparent", boxShadow: "none" } : {};
218
- function Input({ label, error, hint, disabled, prefix, suffix, prefixStyle, suffixStyle, prefixIcon, suffixIcon, prefixIconColor, suffixIconColor, type = "text", containerStyle, inputWrapperStyle, style, onFocus, onBlur, secureTextEntry, editable, accessibilityLabel, ...props }) {
211
+ function Input({ label, error, hint, disabled, prefix, suffix, prefixStyle, suffixStyle, prefixIcon, suffixIcon, prefixIconColor, suffixIconColor, type = "text", containerStyle, inputWrapperStyle, sheetMode = false, style, onFocus, onBlur, secureTextEntry, editable, accessibilityLabel, ...props }) {
219
212
  const { colors } = useTheme();
220
213
  const [focused, setFocused] = React3.useState(false);
221
214
  const [showPassword, setShowPassword] = React3.useState(false);
222
- const focusProgress = useColorTransition(focused, {
223
- duration: focused ? TIMINGS.focusIn.duration : TIMINGS.focusOut.duration
224
- });
225
215
  const isDisabled = disabled || editable === false;
226
216
  const isPassword = type === "password";
227
217
  const effectiveSecure = isPassword ? !showPassword : secureTextEntry;
@@ -237,22 +227,45 @@ function Input({ label, error, hint, disabled, prefix, suffix, prefixStyle, suff
237
227
  },
238
228
  /* @__PURE__ */ React3__default.default.createElement(vectorIcons.AntDesign, { name: showPassword ? "eye" : "eye-invisible", size: 20, color: colors.foregroundMuted })
239
229
  ) : suffixIcon ? renderIcon(suffixIcon, 20, suffixIconColor ?? colors.foregroundMuted) : suffix;
240
- const borderAnimStyle = Animated.useAnimatedStyle(() => ({
241
- borderColor: error ? colors.destructive : Animated.interpolateColor(focusProgress.value, [0, 1], [colors.border, colors.primary]),
242
- borderWidth: error ? 2 : Animated.interpolate(focusProgress.value, [0, 1], [1, 2])
243
- }));
230
+ const borderColor = error ? colors.destructive : focused ? colors.primary : colors.border;
244
231
  return /* @__PURE__ */ React3__default.default.createElement(reactNative.View, { style: [styles.container, isDisabled && styles.containerDisabled, containerStyle] }, label ? /* @__PURE__ */ React3__default.default.createElement(reactNative.Text, { style: [styles.label, { color: colors.foreground }], allowFontScaling: true }, label) : null, /* @__PURE__ */ React3__default.default.createElement(
245
- Animated__default.default.View,
232
+ reactNativeEase.EaseView,
246
233
  {
247
234
  style: [
248
235
  styles.inputWrapper,
249
236
  { backgroundColor: isDisabled ? colors.surface : colors.background },
237
+ error && styles.inputWrapperError,
250
238
  inputWrapperStyle
251
- ]
239
+ ],
240
+ animate: { borderColor },
241
+ transition: COLOR_TRANSITION
252
242
  },
253
- /* @__PURE__ */ React3__default.default.createElement(Animated__default.default.View, { style: [styles.borderOverlay, borderAnimStyle], pointerEvents: "none" }),
254
243
  effectivePrefix ? typeof effectivePrefix === "string" ? /* @__PURE__ */ React3__default.default.createElement(reactNative.Text, { style: [styles.prefixText, { color: colors.foregroundMuted }, prefixStyle], allowFontScaling: true }, effectivePrefix) : /* @__PURE__ */ React3__default.default.createElement(reactNative.View, { style: styles.prefixContainer }, effectivePrefix) : null,
255
- /* @__PURE__ */ React3__default.default.createElement(
244
+ sheetMode ? /* @__PURE__ */ React3__default.default.createElement(
245
+ bottomSheet.BottomSheetTextInput,
246
+ {
247
+ style: [
248
+ styles.input,
249
+ { color: colors.foreground },
250
+ webInputResetStyle,
251
+ style
252
+ ],
253
+ onFocus: (e) => {
254
+ setFocused(true);
255
+ onFocus?.(e);
256
+ },
257
+ onBlur: (e) => {
258
+ setFocused(false);
259
+ onBlur?.(e);
260
+ },
261
+ placeholderTextColor: colors.foregroundMuted,
262
+ allowFontScaling: true,
263
+ secureTextEntry: effectiveSecure,
264
+ editable: isDisabled ? false : editable,
265
+ accessibilityLabel: accessibilityLabel ?? label,
266
+ ...props
267
+ }
268
+ ) : /* @__PURE__ */ React3__default.default.createElement(
256
269
  reactNative.TextInput,
257
270
  {
258
271
  style: [
@@ -302,16 +315,14 @@ var styles = reactNative.StyleSheet.create({
302
315
  inputWrapper: {
303
316
  flexDirection: "row",
304
317
  alignItems: "center",
305
- // Border lives on borderOverlay (absolute) so its 1px→2px focus change
306
- // never resizes this box. Wrapper itself carries no border.
307
318
  borderRadius: 8,
319
+ borderWidth: 1,
308
320
  paddingHorizontal: s(14),
309
321
  paddingVertical: vs(11),
310
322
  minHeight: 48
311
323
  },
312
- borderOverlay: {
313
- ...reactNative.StyleSheet.absoluteFillObject,
314
- borderRadius: 8
324
+ inputWrapperError: {
325
+ borderWidth: 2
315
326
  },
316
327
  input: {
317
328
  fontFamily: "Sohne-Regular",
package/dist/Input.mjs CHANGED
@@ -1,5 +1,4 @@
1
- export { Input } from './chunk-756RAKE4.mjs';
2
- import './chunk-26BCI223.mjs';
1
+ export { Input } from './chunk-ZUR7AU5R.mjs';
3
2
  import './chunk-DVK4G2GT.mjs';
4
3
  import './chunk-T7XZ7H7Y.mjs';
5
4
  import './chunk-SOYNZDVY.mjs';
@@ -1,8 +1,13 @@
1
1
  import React from 'react';
2
- import { ViewStyle, TextStyle } from 'react-native';
2
+ import { ImageSourcePropType, ViewStyle, TextStyle } from 'react-native';
3
3
 
4
4
  type ListItemVariant = 'plain' | 'card';
5
5
  interface ListItemProps {
6
+ /**
7
+ * Image source for the left slot. If provided, renders an Image (40×40, borderRadius 8).
8
+ * Takes precedence over `leftRender` and `leftIcon`.
9
+ */
10
+ imageSource?: ImageSourcePropType;
6
11
  /**
7
12
  * Arbitrary content rendered on the left (avatar, icon, image, etc.).
8
13
  * Rendered inside a 44×44 aligned container.
@@ -53,12 +58,14 @@ interface ListItemProps {
53
58
  titleStyle?: TextStyle;
54
59
  /** Style applied to the subtitle Text. */
55
60
  subtitleStyle?: TextStyle;
61
+ /** Max lines for the subtitle. Defaults to 2. */
62
+ subtitleNumberOfLines?: number;
56
63
  /** Style applied to the caption Text. */
57
64
  captionStyle?: TextStyle;
58
65
  /** Accessibility label override. Defaults to the title. */
59
66
  accessibilityLabel?: string;
60
67
  }
61
- declare function ListItemBase({ leftRender, rightRender, trailing, icon, leftIcon, rightIcon, leftIconColor, rightIconColor, title, subtitle, caption, variant, showChevron, showSeparator, onPress, disabled, style, titleStyle, subtitleStyle, captionStyle, accessibilityLabel, }: ListItemProps): React.JSX.Element;
68
+ declare function ListItemBase({ imageSource, leftRender, rightRender, trailing, icon, leftIcon, rightIcon, leftIconColor, rightIconColor, title, subtitle, caption, variant, showChevron, showSeparator, onPress, disabled, style, titleStyle, subtitleStyle, subtitleNumberOfLines, captionStyle, accessibilityLabel, }: ListItemProps): React.JSX.Element;
62
69
  declare const ListItem: React.MemoExoticComponent<typeof ListItemBase>;
63
70
 
64
71
  export { ListItem, type ListItemProps };
@@ -1,8 +1,13 @@
1
1
  import React from 'react';
2
- import { ViewStyle, TextStyle } from 'react-native';
2
+ import { ImageSourcePropType, ViewStyle, TextStyle } from 'react-native';
3
3
 
4
4
  type ListItemVariant = 'plain' | 'card';
5
5
  interface ListItemProps {
6
+ /**
7
+ * Image source for the left slot. If provided, renders an Image (40×40, borderRadius 8).
8
+ * Takes precedence over `leftRender` and `leftIcon`.
9
+ */
10
+ imageSource?: ImageSourcePropType;
6
11
  /**
7
12
  * Arbitrary content rendered on the left (avatar, icon, image, etc.).
8
13
  * Rendered inside a 44×44 aligned container.
@@ -53,12 +58,14 @@ interface ListItemProps {
53
58
  titleStyle?: TextStyle;
54
59
  /** Style applied to the subtitle Text. */
55
60
  subtitleStyle?: TextStyle;
61
+ /** Max lines for the subtitle. Defaults to 2. */
62
+ subtitleNumberOfLines?: number;
56
63
  /** Style applied to the caption Text. */
57
64
  captionStyle?: TextStyle;
58
65
  /** Accessibility label override. Defaults to the title. */
59
66
  accessibilityLabel?: string;
60
67
  }
61
- declare function ListItemBase({ leftRender, rightRender, trailing, icon, leftIcon, rightIcon, leftIconColor, rightIconColor, title, subtitle, caption, variant, showChevron, showSeparator, onPress, disabled, style, titleStyle, subtitleStyle, captionStyle, accessibilityLabel, }: ListItemProps): React.JSX.Element;
68
+ declare function ListItemBase({ imageSource, leftRender, rightRender, trailing, icon, leftIcon, rightIcon, leftIconColor, rightIconColor, title, subtitle, caption, variant, showChevron, showSeparator, onPress, disabled, style, titleStyle, subtitleStyle, subtitleNumberOfLines, captionStyle, accessibilityLabel, }: ListItemProps): React.JSX.Element;
62
69
  declare const ListItem: React.MemoExoticComponent<typeof ListItemBase>;
63
70
 
64
71
  export { ListItem, type ListItemProps };