@thewhileloop/whileui 2.0.0 → 4.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.
Files changed (74) hide show
  1. package/README.md +135 -70
  2. package/dist/blocks/commerce/metric-card.d.ts +9 -29
  3. package/dist/blocks/datepicker/date-range-picker-modal.d.ts +1 -1
  4. package/dist/blocks/datepicker/date-range-picker-modal.js +6 -14
  5. package/dist/blocks/datepicker/datepicker-modal.d.ts +1 -1
  6. package/dist/blocks/datepicker/datepicker-modal.js +6 -12
  7. package/dist/blocks/datepicker/index.d.ts +1 -0
  8. package/dist/blocks/datepicker/index.js +1 -0
  9. package/dist/blocks/datepicker/time-picker-modal.d.ts +43 -0
  10. package/dist/blocks/datepicker/time-picker-modal.js +187 -0
  11. package/dist/blocks/layout/smart-input.d.ts +3 -2
  12. package/dist/blocks/layout/smart-input.js +33 -18
  13. package/dist/blocks/media/smart-image.d.ts +39 -98
  14. package/dist/blocks/navigation/drawer-menu.js +88 -72
  15. package/dist/components/alert/alert.d.ts +23 -55
  16. package/dist/components/alert/alert.js +1 -1
  17. package/dist/components/alert-dialog/alert-dialog.js +1 -4
  18. package/dist/components/avatar/avatar.d.ts +18 -45
  19. package/dist/components/avatar/avatar.js +1 -1
  20. package/dist/components/badge/badge.d.ts +84 -212
  21. package/dist/components/button/button.d.ts +69 -188
  22. package/dist/components/card/card.d.ts +29 -73
  23. package/dist/components/card/card.js +1 -1
  24. package/dist/components/container/container.d.ts +24 -96
  25. package/dist/components/data-row/data-row.d.ts +18 -38
  26. package/dist/components/data-row/data-row.js +1 -1
  27. package/dist/components/fab/fab.d.ts +87 -0
  28. package/dist/components/fab/fab.js +59 -0
  29. package/dist/components/fab/index.d.ts +1 -0
  30. package/dist/components/fab/index.js +1 -0
  31. package/dist/components/form-field/form-field.d.ts +31 -87
  32. package/dist/components/form-field/form-field.js +1 -1
  33. package/dist/components/hover-card/hover-card.d.ts +3 -2
  34. package/dist/components/input/input.d.ts +18 -31
  35. package/dist/components/input/input.js +20 -8
  36. package/dist/components/label/label.d.ts +3 -3
  37. package/dist/components/labeled-field/labeled-field.d.ts +3 -2
  38. package/dist/components/numeric-input/numeric-input.d.ts +27 -82
  39. package/dist/components/numeric-input/numeric-input.js +79 -80
  40. package/dist/components/otp-input/otp-input.d.ts +21 -65
  41. package/dist/components/popover/popover.d.ts +4 -3
  42. package/dist/components/pressable/pressable.d.ts +3 -3
  43. package/dist/components/progress/progress.d.ts +15 -39
  44. package/dist/components/row/row.d.ts +63 -163
  45. package/dist/components/segmented-control/segmented-control.d.ts +69 -241
  46. package/dist/components/select/select.d.ts +10 -22
  47. package/dist/components/select/select.js +2 -7
  48. package/dist/components/separator/separator.d.ts +3 -3
  49. package/dist/components/skeleton/skeleton.d.ts +9 -29
  50. package/dist/components/spinner/spinner.d.ts +12 -36
  51. package/dist/components/stack/stack.d.ts +63 -163
  52. package/dist/components/tabs/tabs.d.ts +9 -29
  53. package/dist/components/text/text.d.ts +51 -131
  54. package/dist/components/textarea/textarea.d.ts +15 -39
  55. package/dist/components/textarea/textarea.js +3 -3
  56. package/dist/components/toast/toast.d.ts +12 -36
  57. package/dist/components/toggle/toggle.d.ts +42 -138
  58. package/dist/components/toggle-group/toggle-group.d.ts +42 -138
  59. package/dist/components/tooltip/tooltip.d.ts +3 -2
  60. package/dist/components/view/view.d.ts +3 -3
  61. package/dist/index.d.ts +2 -1
  62. package/dist/index.js +2 -1
  63. package/dist/lib/focus-ring.d.ts +10 -0
  64. package/dist/lib/focus-ring.js +10 -0
  65. package/dist/lib/interaction-tokens.d.ts +24 -1
  66. package/dist/lib/interaction-tokens.js +26 -44
  67. package/dist/lib/motion-policy.js +2 -2
  68. package/dist/lib/ref-types.d.ts +3 -0
  69. package/dist/lib/ref-types.js +1 -0
  70. package/dist/lib/theme-bridge.d.ts +4 -2
  71. package/dist/lib/theme-bridge.js +13 -9
  72. package/dist/lib/theme-presets.d.ts +26 -0
  73. package/dist/lib/theme-presets.js +28 -0
  74. package/package.json +29 -28
@@ -0,0 +1,187 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { useCallback, useEffect, useRef, useState } from 'react';
3
+ import { Modal, Platform, Pressable, ScrollView, View, } from 'react-native';
4
+ import { useSafeAreaInsets } from 'react-native-safe-area-context';
5
+ import { Text } from '../../components/text';
6
+ import { Button, ButtonText } from '../../components/button';
7
+ import { cn } from '../../lib/cn';
8
+ import { focusRingInset } from '../../lib/focus-ring';
9
+ import { useThemeColors } from '../../lib/theme-colors';
10
+ import { useVisualTokens } from '../../lib/visual-tokens';
11
+ import { fieldRecipe, surfacePadding, surfaceRadius, typographyStyle } from '../../lib/recipes';
12
+ import { useFrostedSurface } from '../../lib/frosted-surface';
13
+ const ITEM_HEIGHT = 44;
14
+ /** Rows visible in a wheel. Odd, so one row sits centred under the selection band. */
15
+ const VISIBLE_ROWS = 5;
16
+ const WHEEL_HEIGHT = ITEM_HEIGHT * VISIBLE_ROWS;
17
+ const WHEEL_PADDING = ITEM_HEIGHT * Math.floor(VISIBLE_ROWS / 2);
18
+ const pad2 = (n) => String(n).padStart(2, '0');
19
+ /** The locale's clock, so a US device gets AM/PM and most of the world does not. */
20
+ function localeHourCycle() {
21
+ try {
22
+ const cycle = new Intl.DateTimeFormat(undefined, { hour: 'numeric' }).resolvedOptions()
23
+ .hourCycle;
24
+ return cycle === 'h11' || cycle === 'h12' ? 'h12' : 'h23';
25
+ }
26
+ catch {
27
+ return 'h23';
28
+ }
29
+ }
30
+ function parseTime(value) {
31
+ if (!value)
32
+ return null;
33
+ const match = /^(\d{1,2}):(\d{2})$/.exec(value.trim());
34
+ if (!match)
35
+ return null;
36
+ const hour = Number(match[1]);
37
+ const minute = Number(match[2]);
38
+ if (hour > 23 || minute > 59)
39
+ return null;
40
+ return { hour, minute };
41
+ }
42
+ /** `HH:MM` as the locale writes it — "10:30 PM" or "22:30". */
43
+ function formatDisplayTime(value, hourCycle) {
44
+ const parsed = parseTime(value);
45
+ if (!parsed)
46
+ return 'Select time';
47
+ const date = new Date(2000, 0, 1, parsed.hour, parsed.minute);
48
+ return date.toLocaleTimeString(undefined, {
49
+ hour: 'numeric',
50
+ minute: '2-digit',
51
+ ...(hourCycle ? { hour12: hourCycle === 'h12' } : {}),
52
+ });
53
+ }
54
+ /**
55
+ * One scrolling column. Scrolls snap to a row, a tap selects a row, and the whole
56
+ * column is a single adjustable element for assistive tech and the keyboard — sixty
57
+ * minute rows as sixty tab stops would be nobody's idea of operable.
58
+ */
59
+ function Wheel({ label, options, selected, onSelect }) {
60
+ const visual = useVisualTokens();
61
+ const colors = useThemeColors();
62
+ const ref = useRef(null);
63
+ const index = Math.max(0, options.findIndex((option) => option.value === selected));
64
+ // Keep the band on the selected row when the value changes from outside the wheel
65
+ // (a tap, a keyboard step, the initial value), not after the user's own scroll.
66
+ const lastScrolledIndex = useRef(-1);
67
+ useEffect(() => {
68
+ if (lastScrolledIndex.current === index)
69
+ return;
70
+ lastScrolledIndex.current = index;
71
+ ref.current?.scrollTo({ y: index * ITEM_HEIGHT, animated: Platform.OS !== 'web' });
72
+ }, [index]);
73
+ const settle = useCallback((event) => {
74
+ const next = Math.round(event.nativeEvent.contentOffset.y / ITEM_HEIGHT);
75
+ const clamped = Math.min(options.length - 1, Math.max(0, next));
76
+ lastScrolledIndex.current = clamped;
77
+ const option = options[clamped];
78
+ if (option && option.value !== selected)
79
+ onSelect(option.value);
80
+ }, [onSelect, options, selected]);
81
+ const step = useCallback((delta) => {
82
+ const next = (index + delta + options.length) % options.length;
83
+ onSelect(options[next].value);
84
+ }, [index, onSelect, options]);
85
+ const current = options[index]?.label ?? '';
86
+ return (_jsxs(View, { accessible: true, accessibilityRole: "adjustable", accessibilityLabel: label, accessibilityValue: { text: current }, accessibilityActions: [{ name: 'increment' }, { name: 'decrement' }], onAccessibilityAction: (event) => {
87
+ if (event.nativeEvent.actionName === 'increment')
88
+ step(1);
89
+ if (event.nativeEvent.actionName === 'decrement')
90
+ step(-1);
91
+ }, ...(Platform.OS === 'web'
92
+ ? {
93
+ focusable: true,
94
+ onKeyDown: (event) => {
95
+ if (event.nativeEvent.key === 'ArrowUp') {
96
+ event.preventDefault();
97
+ step(-1);
98
+ }
99
+ else if (event.nativeEvent.key === 'ArrowDown') {
100
+ event.preventDefault();
101
+ step(1);
102
+ }
103
+ },
104
+ }
105
+ : null), className: cn('flex-1 overflow-hidden', focusRingInset), style: { height: WHEEL_HEIGHT, borderRadius: surfaceRadius(visual, 'lg') }, children: [_jsx(View, { pointerEvents: "none", className: "absolute left-0 right-0 bg-muted", style: {
106
+ top: WHEEL_PADDING,
107
+ height: ITEM_HEIGHT,
108
+ borderRadius: surfaceRadius(visual, 'md'),
109
+ } }), _jsx(ScrollView, { ref: ref, showsVerticalScrollIndicator: false, snapToInterval: ITEM_HEIGHT, decelerationRate: "fast", nestedScrollEnabled: true, contentContainerStyle: { paddingVertical: WHEEL_PADDING }, contentOffset: { x: 0, y: index * ITEM_HEIGHT }, onMomentumScrollEnd: settle, onScrollEndDrag: Platform.OS === 'web' ? settle : undefined, importantForAccessibility: "no-hide-descendants", accessibilityElementsHidden: true, children: options.map((option) => {
110
+ const on = option.value === selected;
111
+ return (_jsx(Pressable, { onPress: () => onSelect(option.value), focusable: false, className: "items-center justify-center", style: { height: ITEM_HEIGHT }, children: _jsx(Text, { className: on ? 'font-medium text-foreground' : 'text-muted-foreground', style: {
112
+ ...typographyStyle(visual, on ? 'title' : 'body'),
113
+ color: on ? colors.foreground : colors.mutedForeground,
114
+ }, children: option.label }) }, option.value));
115
+ }) })] }));
116
+ }
117
+ function TimePickerModalSession({ value, onValueChange, open, onOpenChange, minuteStep = 1, hourCycle: hourCycleProp, trigger, title = 'Select time', confirmLabel = 'Done', className, frosted = false, blurIntensity, blurTintToken, }) {
118
+ const insets = useSafeAreaInsets();
119
+ const colors = useThemeColors();
120
+ const visual = useVisualTokens();
121
+ const frostedSurface = useFrostedSurface({
122
+ frosted,
123
+ blurIntensity,
124
+ blurTintToken,
125
+ defaultTintToken: 'surfaceTranslucent',
126
+ defaultBlurPreset: 'medium',
127
+ });
128
+ const hourCycle = hourCycleProp ?? localeHourCycle();
129
+ const step = Math.min(30, Math.max(1, Math.round(minuteStep)));
130
+ // The draft opens on the current value, or on the current clock time rounded to the
131
+ // step, so the first confirm without touching anything records "now".
132
+ const initial = (() => {
133
+ const parsed = parseTime(value);
134
+ if (parsed)
135
+ return parsed;
136
+ const now = new Date();
137
+ return { hour: now.getHours(), minute: Math.floor(now.getMinutes() / step) * step };
138
+ })();
139
+ const [hour, setHour] = useState(initial.hour);
140
+ const [minute, setMinute] = useState(initial.minute);
141
+ const hours = hourCycle === 'h12'
142
+ ? Array.from({ length: 12 }, (_, i) => ({ value: i + 1, label: String(i + 1) }))
143
+ : Array.from({ length: 24 }, (_, i) => ({ value: i, label: pad2(i) }));
144
+ const minutes = Array.from({ length: Math.ceil(60 / step) }, (_, i) => ({
145
+ value: i * step,
146
+ label: pad2(i * step),
147
+ }));
148
+ const periods = [
149
+ { value: 0, label: 'AM' },
150
+ { value: 1, label: 'PM' },
151
+ ];
152
+ const period = hour >= 12 ? 1 : 0;
153
+ const hour12 = hour % 12 === 0 ? 12 : hour % 12;
154
+ const handleConfirm = useCallback(() => {
155
+ onValueChange?.(`${pad2(hour)}:${pad2(minute)}`);
156
+ onOpenChange(false);
157
+ }, [hour, minute, onOpenChange, onValueChange]);
158
+ const handleDismiss = useCallback(() => onOpenChange(false), [onOpenChange]);
159
+ const handleOpen = useCallback(() => onOpenChange(true), [onOpenChange]);
160
+ return (_jsxs(_Fragment, { children: [trigger ? (_jsx(Pressable, { accessibilityRole: "button", onPress: handleOpen, className: cn('w-full flex-row items-center border-border bg-muted', className), style: fieldRecipe(visual, 'default'), children: trigger })) : null, _jsx(Modal, { visible: open, transparent: true, animationType: "slide", statusBarTranslucent: true, navigationBarTranslucent: true, onRequestClose: handleDismiss, children: _jsx(Pressable, { accessible: false, importantForAccessibility: "no", className: "flex-1 justify-end", style: { backgroundColor: colors.overlay }, onPress: handleDismiss, children: _jsxs(Pressable, { accessible: false, className: cn('border border-border relative overflow-hidden', frosted ? 'bg-transparent' : 'bg-background'), style: [
161
+ {
162
+ borderTopLeftRadius: surfaceRadius(visual, 'xl'),
163
+ borderTopRightRadius: surfaceRadius(visual, 'xl'),
164
+ paddingBottom: Math.max(insets.bottom, surfacePadding(visual, 'sm')),
165
+ },
166
+ frostedSurface.surfaceStyle,
167
+ ], onPress: (e) => e.stopPropagation(), children: [frostedSurface.overlay, _jsxs(View, { className: "flex-row items-center justify-between border-b border-border", style: {
168
+ paddingHorizontal: surfacePadding(visual, 'sm'),
169
+ paddingVertical: visual.controlPaddingYDefault,
170
+ }, children: [_jsx(Text, { accessibilityRole: "header", className: "font-medium text-foreground", style: typographyStyle(visual, 'body'), children: title }), _jsx(Button, { size: "sm", onPress: handleConfirm, children: _jsx(ButtonText, { children: confirmLabel }) })] }), _jsxs(View, { className: "flex-row items-center", style: { padding: surfacePadding(visual, 'sm'), gap: visual.controlPaddingYDefault }, children: [_jsx(Wheel, { label: "Hour", options: hours, selected: hourCycle === 'h12' ? hour12 : hour, onSelect: (next) => setHour(hourCycle === 'h12' ? (next % 12) + (period === 1 ? 12 : 0) : next) }), _jsx(Text, { className: "text-muted-foreground", style: typographyStyle(visual, 'title'), accessibilityElementsHidden: true, importantForAccessibility: "no", children: ":" }), _jsx(Wheel, { label: "Minute", options: minutes, selected: minute, onSelect: setMinute }), hourCycle === 'h12' ? (_jsx(Wheel, { label: "AM or PM", options: periods, selected: period, onSelect: (next) => setHour((hour % 12) + (next === 1 ? 12 : 0)) })) : null] })] }) }) })] }));
171
+ }
172
+ /**
173
+ * A time chosen on two wheels — three with AM/PM — in the same bottom sheet as
174
+ * `DatePickerModal`, so a form's date and time fields open the same way.
175
+ */
176
+ export function TimePickerModal(props) {
177
+ // Remount per opening so the draft starts from the value each time, as the date
178
+ // picker does; a draft that outlives a cancel would reopen on the abandoned pick.
179
+ const sessionKey = props.open ? 'open' : 'closed';
180
+ return _jsx(TimePickerModalSession, { ...props }, sessionKey);
181
+ }
182
+ /** Compact trigger showing the formatted time. Use as TimePickerModal's trigger prop. */
183
+ export function TimePickerTrigger({ value, placeholder = 'Select time', hourCycle, className, }) {
184
+ const visual = useVisualTokens();
185
+ return (_jsxs(View, { className: cn('flex-1 flex-row items-center min-w-0', className), children: [_jsx(Text, { className: cn('flex-1', value ? 'text-foreground' : 'text-muted-foreground'), style: typographyStyle(visual, 'body'), numberOfLines: 1, children: value ? formatDisplayTime(value, hourCycle) : placeholder }), _jsx(View, { className: "ml-2 shrink-0 h-2 w-2 rotate-45 border-b-2 border-r-2 border-muted-foreground" })] }));
186
+ }
187
+ export { formatDisplayTime, parseTime };
@@ -1,5 +1,6 @@
1
1
  import React from 'react';
2
- import { TextInput, type TextInputProps } from 'react-native';
2
+ import { type TextInputInstance, type TextInputProps } from 'react-native';
3
+ import type { ForwardRefComponent } from '../../lib/ref-types';
3
4
  export interface SmartInputProps extends TextInputProps {
4
5
  /** Left slot: emoji, attach, etc. */
5
6
  leftSlot?: React.ReactNode;
@@ -24,4 +25,4 @@ export interface SmartInputProps extends TextInputProps {
24
25
  placeholderPauseDuration?: number;
25
26
  placeholderClearInterval?: number;
26
27
  }
27
- export declare const SmartInput: React.ForwardRefExoticComponent<SmartInputProps & React.RefAttributes<TextInput>>;
28
+ export declare const SmartInput: ForwardRefComponent<TextInputInstance, SmartInputProps>;
@@ -1,6 +1,6 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { forwardRef, useEffect, useRef, useState } from 'react';
3
- import { AccessibilityInfo, View, TextInput } from 'react-native';
3
+ import { AccessibilityInfo, View, TextInput, } from 'react-native';
4
4
  import { useSafeAreaInsets } from 'react-native-safe-area-context';
5
5
  import { cn } from '../../lib/cn';
6
6
  import { focusRing } from '../../lib/focus-ring';
@@ -79,6 +79,30 @@ function useRotatingPlaceholder({ active, examplesKey, prefix, typingInterval, p
79
79
  }
80
80
  return animatedPlaceholder || prefix;
81
81
  }
82
+ function useSmartInputPlaceholder({ value, defaultValue, placeholder, examples, prefix, typingInterval, pauseDuration, clearInterval, onChangeText, }) {
83
+ const [uncontrolledHasText, setUncontrolledHasText] = useState(() => Boolean(defaultValue));
84
+ const hasText = value !== undefined ? Boolean(value) : uncontrolledHasText;
85
+ const examplesKey = examples?.join(SEPARATOR) ?? '';
86
+ const animatedPlaceholder = useRotatingPlaceholder({
87
+ active: placeholder === undefined && !hasText && Boolean(examplesKey),
88
+ examplesKey,
89
+ prefix,
90
+ typingInterval,
91
+ pauseDuration,
92
+ clearInterval,
93
+ });
94
+ return {
95
+ placeholder: placeholder ?? (hasText || !examplesKey ? undefined : animatedPlaceholder),
96
+ onChangeText: (text) => {
97
+ if (value === undefined)
98
+ setUncontrolledHasText(Boolean(text));
99
+ onChangeText?.(text);
100
+ },
101
+ };
102
+ }
103
+ function SmartInputSlot({ children }) {
104
+ return children ? _jsx(View, { className: "shrink-0", children: children }) : null;
105
+ }
82
106
  export const SmartInput = forwardRef(function SmartInput({ leftSlot, centerSlot, rightSlot, variant = 'bar', className, inputClassName, submitBehavior = 'newline', safeArea = true, placeholderExamples, placeholderPrefix = 'e.g. ', placeholderTypingInterval = 65, placeholderPauseDuration = 2200, placeholderClearInterval = 25, placeholderTextColor, placeholder, value, defaultValue, onChangeText, editable = true, style, ...props }, ref) {
83
107
  const insets = useSafeAreaInsets();
84
108
  const colors = useThemeColors();
@@ -87,22 +111,17 @@ export const SmartInput = forwardRef(function SmartInput({ leftSlot, centerSlot,
87
111
  const bottomPadding = safeArea
88
112
  ? Math.max(insets.bottom, visual.controlPaddingYSm)
89
113
  : visual.controlPaddingYSm;
90
- // When the field is controlled its owner already holds the answer, so derive it rather
91
- // than mirroring it into state — mirroring cost a frame in which the rotating
92
- // placeholder kept animating over text the user had already typed.
93
- const [uncontrolledHasText, setUncontrolledHasText] = useState(() => Boolean(defaultValue));
94
- const hasText = value !== undefined ? Boolean(value) : uncontrolledHasText;
95
- const examplesKey = placeholderExamples?.join(SEPARATOR) ?? '';
96
- const animatePlaceholder = placeholder === undefined && !hasText && Boolean(examplesKey);
97
- const animatedPlaceholder = useRotatingPlaceholder({
98
- active: animatePlaceholder,
99
- examplesKey,
114
+ const placeholderState = useSmartInputPlaceholder({
115
+ value,
116
+ defaultValue,
117
+ placeholder,
118
+ examples: placeholderExamples,
100
119
  prefix: placeholderPrefix,
101
120
  typingInterval: placeholderTypingInterval,
102
121
  pauseDuration: placeholderPauseDuration,
103
122
  clearInterval: placeholderClearInterval,
123
+ onChangeText,
104
124
  });
105
- const resolvedPlaceholder = placeholder ?? (hasText || !examplesKey ? undefined : animatedPlaceholder);
106
125
  return (_jsxs(View, { className: cn('w-full flex-row items-center gap-2 bg-background', variant === 'bar' && 'border-border', variant === 'card' && 'border-border', className), style: [
107
126
  {
108
127
  paddingHorizontal: visual.controlPaddingXSm,
@@ -117,16 +136,12 @@ export const SmartInput = forwardRef(function SmartInput({ leftSlot, centerSlot,
117
136
  ...shadowStyle(visual, colors, 'md'),
118
137
  }
119
138
  : null,
120
- ], children: [leftSlot && _jsx(View, { className: "shrink-0", children: leftSlot }), centerSlot && _jsx(View, { className: "shrink-0", children: centerSlot }), _jsx(TextInput, { ref: ref, style: [
139
+ ], children: [_jsx(SmartInputSlot, { children: leftSlot }), _jsx(SmartInputSlot, { children: centerSlot }), _jsx(TextInput, { ref: ref, style: [
121
140
  controlRecipe(visual, 'default'),
122
141
  typographyStyle(visual, 'label'),
123
142
  variant === 'bar' ? { borderWidth: visual.borderWidthControl } : null,
124
143
  { maxHeight: visual.smartInputMaxHeight },
125
144
  !editable ? { opacity: interaction.disabledOpacity } : null,
126
145
  style,
127
- ], className: cn('flex-1 text-foreground placeholder:text-muted-foreground', focusRing, variant === 'bar' && 'border-border bg-muted', variant === 'card' && 'bg-transparent', inputClassName), multiline: true, submitBehavior: submitBehavior, textAlignVertical: "top", placeholderTextColor: placeholderTextColor ?? colors.placeholder, placeholder: resolvedPlaceholder, value: value, defaultValue: defaultValue, onChangeText: (text) => {
128
- if (value === undefined)
129
- setUncontrolledHasText(Boolean(text));
130
- onChangeText?.(text);
131
- }, editable: editable, ...props }), rightSlot && _jsx(View, { className: "shrink-0", children: rightSlot })] }));
146
+ ], className: cn('flex-1 text-foreground placeholder:text-muted-foreground', focusRing, variant === 'bar' && 'border-border bg-muted', variant === 'card' && 'bg-transparent', inputClassName), multiline: true, submitBehavior: submitBehavior, textAlignVertical: "top", placeholderTextColor: placeholderTextColor ?? colors.placeholder, placeholder: placeholderState.placeholder, value: value, defaultValue: defaultValue, onChangeText: placeholderState.onChangeText, editable: editable, ...props }), _jsx(SmartInputSlot, { children: rightSlot })] }));
132
147
  });
@@ -1,122 +1,63 @@
1
- import React from 'react';
2
- import { View, type ViewProps, type ImageProps as RNImageProps } from 'react-native';
1
+ import { type ViewInstance, type ViewProps } from 'react-native';
2
+ import { type ImageProps } from 'expo-image';
3
3
  import { type VariantProps } from '../../lib/tv';
4
+ import type { ForwardRefComponent } from '../../lib/ref-types';
4
5
  declare const smartImageVariants: import("tailwind-variants").TVReturnType<{
5
6
  radius: {
6
- none: string;
7
- sm: string;
8
- md: string;
9
- lg: string;
10
- xl: string;
11
- full: string;
7
+ none: "";
8
+ sm: "";
9
+ md: "";
10
+ lg: "";
11
+ xl: "";
12
+ full: "rounded-full";
12
13
  };
13
14
  objectFit: {
14
- cover: string;
15
- contain: string;
16
- fill: string;
17
- 'scale-down': string;
15
+ cover: "object-cover";
16
+ contain: "object-contain";
17
+ fill: "object-fill";
18
+ 'scale-down': "object-scale-down";
18
19
  };
19
- }, undefined, "overflow-hidden", import("tailwind-variants/dist/config.js").TVConfig<{
20
+ }, undefined, "overflow-hidden", {
20
21
  radius: {
21
- none: string;
22
- sm: string;
23
- md: string;
24
- lg: string;
25
- xl: string;
26
- full: string;
22
+ none: "";
23
+ sm: "";
24
+ md: "";
25
+ lg: "";
26
+ xl: "";
27
+ full: "rounded-full";
27
28
  };
28
29
  objectFit: {
29
- cover: string;
30
- contain: string;
31
- fill: string;
32
- 'scale-down': string;
30
+ cover: "object-cover";
31
+ contain: "object-contain";
32
+ fill: "object-fill";
33
+ 'scale-down': "object-scale-down";
33
34
  };
34
- }, {
35
+ }, undefined, import("tailwind-variants").TVReturnTypeLike<{
35
36
  radius: {
36
- none: string;
37
- sm: string;
38
- md: string;
39
- lg: string;
40
- xl: string;
41
- full: string;
37
+ none: "";
38
+ sm: "";
39
+ md: "";
40
+ lg: "";
41
+ xl: "";
42
+ full: "rounded-full";
42
43
  };
43
44
  objectFit: {
44
- cover: string;
45
- contain: string;
46
- fill: string;
47
- 'scale-down': string;
45
+ cover: "object-cover";
46
+ contain: "object-contain";
47
+ fill: "object-fill";
48
+ 'scale-down': "object-scale-down";
48
49
  };
49
- }>, {
50
- radius: {
51
- none: string;
52
- sm: string;
53
- md: string;
54
- lg: string;
55
- xl: string;
56
- full: string;
57
- };
58
- objectFit: {
59
- cover: string;
60
- contain: string;
61
- fill: string;
62
- 'scale-down': string;
63
- };
64
- }, undefined, import("tailwind-variants").TVReturnType<{
65
- radius: {
66
- none: string;
67
- sm: string;
68
- md: string;
69
- lg: string;
70
- xl: string;
71
- full: string;
72
- };
73
- objectFit: {
74
- cover: string;
75
- contain: string;
76
- fill: string;
77
- 'scale-down': string;
78
- };
79
- }, undefined, "overflow-hidden", import("tailwind-variants/dist/config.js").TVConfig<{
80
- radius: {
81
- none: string;
82
- sm: string;
83
- md: string;
84
- lg: string;
85
- xl: string;
86
- full: string;
87
- };
88
- objectFit: {
89
- cover: string;
90
- contain: string;
91
- fill: string;
92
- 'scale-down': string;
93
- };
94
- }, {
95
- radius: {
96
- none: string;
97
- sm: string;
98
- md: string;
99
- lg: string;
100
- xl: string;
101
- full: string;
102
- };
103
- objectFit: {
104
- cover: string;
105
- contain: string;
106
- fill: string;
107
- 'scale-down': string;
108
- };
109
- }>, unknown, unknown, undefined>>;
50
+ }, undefined>>;
110
51
  export interface SmartImageProps extends Omit<ViewProps, 'className'>, VariantProps<typeof smartImageVariants> {
111
52
  className?: string;
112
- source: RNImageProps['source'];
53
+ source: ImageProps['source'];
113
54
  alt: string;
114
55
  skeleton?: boolean;
115
56
  skeletonClassName?: string;
116
57
  transition?: number;
117
58
  onLoad?: () => void;
118
59
  onError?: () => void;
119
- fallbackSource?: RNImageProps['source'];
60
+ fallbackSource?: ImageProps['source'];
120
61
  }
121
- declare const SmartImage: React.ForwardRefExoticComponent<SmartImageProps & React.RefAttributes<View>>;
62
+ declare const SmartImage: ForwardRefComponent<ViewInstance, SmartImageProps>;
122
63
  export { SmartImage, smartImageVariants };