@idealyst/components 1.2.106 → 1.2.108
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/package.json +4 -4
- package/src/Dialog/Dialog.native.tsx +1 -1
- package/src/Form/Form.native.tsx +38 -0
- package/src/Form/Form.styles.tsx +14 -0
- package/src/Form/Form.web.tsx +50 -0
- package/src/Form/FormContext.ts +12 -0
- package/src/Form/fields/FormCheckbox.tsx +27 -0
- package/src/Form/fields/FormField.tsx +11 -0
- package/src/Form/fields/FormRadioGroup.tsx +24 -0
- package/src/Form/fields/FormSelect.tsx +27 -0
- package/src/Form/fields/FormSlider.tsx +26 -0
- package/src/Form/fields/FormSwitch.tsx +26 -0
- package/src/Form/fields/FormTextArea.tsx +27 -0
- package/src/Form/fields/FormTextInput.tsx +55 -0
- package/src/Form/index.native.ts +35 -0
- package/src/Form/index.ts +35 -0
- package/src/Form/index.web.ts +35 -0
- package/src/Form/types.ts +105 -0
- package/src/Form/useForm.ts +279 -0
- package/src/Form/useFormField.ts +21 -0
- package/src/Popover/Popover.native.tsx +1 -1
- package/src/RadioButton/RadioButton.styles.tsx +28 -0
- package/src/RadioButton/RadioGroup.native.tsx +28 -3
- package/src/RadioButton/RadioGroup.web.tsx +37 -1
- package/src/RadioButton/types.ts +16 -0
- package/src/Screen/Screen.native.tsx +12 -3
- package/src/Select/Select.native.tsx +10 -6
- package/src/Select/Select.styles.tsx +8 -8
- package/src/Select/Select.web.tsx +11 -7
- package/src/Select/types.ts +4 -2
- package/src/Slider/Slider.native.tsx +122 -0
- package/src/Slider/Slider.styles.tsx +48 -11
- package/src/Slider/Slider.web.tsx +54 -5
- package/src/Slider/types.ts +15 -0
- package/src/Switch/Switch.native.tsx +48 -20
- package/src/Switch/Switch.styles.tsx +28 -0
- package/src/Switch/Switch.web.tsx +55 -16
- package/src/Switch/types.ts +10 -0
- package/src/TextInput/TextInput.native.tsx +123 -40
- package/src/TextInput/TextInput.styles.tsx +47 -9
- package/src/TextInput/TextInput.web.tsx +163 -51
- package/src/TextInput/types.ts +16 -1
- package/src/hooks/useSmartPosition.native.ts +1 -1
- package/src/index.native.ts +19 -0
- package/src/index.ts +19 -0
- package/src/internal/BoundedModalContent.native.tsx +1 -1
- package/src/internal/SafeAreaDebugOverlay.native.tsx +1 -1
|
@@ -17,6 +17,8 @@ const Switch = forwardRef<IdealystElement, SwitchProps>(({
|
|
|
17
17
|
checked = false,
|
|
18
18
|
onChange,
|
|
19
19
|
disabled = false,
|
|
20
|
+
error,
|
|
21
|
+
helperText,
|
|
20
22
|
label,
|
|
21
23
|
labelPosition = 'right',
|
|
22
24
|
intent = 'primary',
|
|
@@ -40,6 +42,10 @@ const Switch = forwardRef<IdealystElement, SwitchProps>(({
|
|
|
40
42
|
accessibilityDescribedBy,
|
|
41
43
|
accessibilityChecked,
|
|
42
44
|
}, ref) => {
|
|
45
|
+
// Derive hasError from error prop
|
|
46
|
+
const hasError = Boolean(error);
|
|
47
|
+
// Determine if we need a wrapper (when error or helperText is present)
|
|
48
|
+
const needsWrapper = Boolean(error) || Boolean(helperText);
|
|
43
49
|
const handleClick = (e: React.MouseEvent) => {
|
|
44
50
|
e.preventDefault();
|
|
45
51
|
e.stopPropagation();
|
|
@@ -86,6 +92,7 @@ const Switch = forwardRef<IdealystElement, SwitchProps>(({
|
|
|
86
92
|
size,
|
|
87
93
|
checked,
|
|
88
94
|
disabled: disabled as boolean,
|
|
95
|
+
hasError,
|
|
89
96
|
labelPosition,
|
|
90
97
|
margin,
|
|
91
98
|
intent,
|
|
@@ -98,6 +105,14 @@ const Switch = forwardRef<IdealystElement, SwitchProps>(({
|
|
|
98
105
|
const thumbIconProps = getWebProps([switchStyles.thumbIcon as any]);
|
|
99
106
|
const labelProps = getWebProps([switchStyles.label as any]);
|
|
100
107
|
|
|
108
|
+
// Wrapper and helperText styles
|
|
109
|
+
const wrapperStyleComputed = (switchStyles.wrapper as any)({});
|
|
110
|
+
const helperTextStyleComputed = (switchStyles.helperText as any)({ hasError });
|
|
111
|
+
const wrapperProps = getWebProps([wrapperStyleComputed, flattenStyle(style)]);
|
|
112
|
+
const helperTextProps = getWebProps([helperTextStyleComputed]);
|
|
113
|
+
|
|
114
|
+
const showFooter = Boolean(error) || Boolean(helperText);
|
|
115
|
+
|
|
101
116
|
// Helper to render icon
|
|
102
117
|
const renderIcon = () => {
|
|
103
118
|
const iconToRender = checked ? onIcon : offIcon;
|
|
@@ -122,7 +137,7 @@ const Switch = forwardRef<IdealystElement, SwitchProps>(({
|
|
|
122
137
|
const computedButtonProps = getWebProps([switchStyles.switchContainer as any]);
|
|
123
138
|
|
|
124
139
|
// Computed container props (for when label exists)
|
|
125
|
-
const computedContainerProps = getWebProps([switchStyles.container as any]);
|
|
140
|
+
const computedContainerProps = getWebProps([switchStyles.container as any, !needsWrapper && flattenStyle(style)].filter(Boolean));
|
|
126
141
|
|
|
127
142
|
const mergedButtonRef = useMergeRefs(ref as React.Ref<HTMLButtonElement>, computedButtonProps.ref);
|
|
128
143
|
const mergedContainerRef = useMergeRefs(ref as React.Ref<HTMLDivElement>, computedContainerProps.ref);
|
|
@@ -131,12 +146,12 @@ const Switch = forwardRef<IdealystElement, SwitchProps>(({
|
|
|
131
146
|
<button
|
|
132
147
|
{...computedButtonProps}
|
|
133
148
|
{...ariaProps}
|
|
134
|
-
style={flattenStyle(style)}
|
|
135
|
-
ref={mergedButtonRef}
|
|
149
|
+
style={!label && !needsWrapper ? flattenStyle(style) : undefined}
|
|
150
|
+
ref={!label && !needsWrapper ? mergedButtonRef : undefined}
|
|
136
151
|
onClick={handleClick}
|
|
137
152
|
disabled={disabled}
|
|
138
153
|
id={switchId}
|
|
139
|
-
data-testid={testID}
|
|
154
|
+
data-testid={!label && !needsWrapper ? testID : undefined}
|
|
140
155
|
>
|
|
141
156
|
<div {...trackProps}>
|
|
142
157
|
<div {...thumbProps}>
|
|
@@ -146,24 +161,48 @@ const Switch = forwardRef<IdealystElement, SwitchProps>(({
|
|
|
146
161
|
</button>
|
|
147
162
|
);
|
|
148
163
|
|
|
149
|
-
|
|
164
|
+
// The switch + label row
|
|
165
|
+
const switchWithLabel = label ? (
|
|
166
|
+
<div
|
|
167
|
+
{...computedContainerProps}
|
|
168
|
+
ref={!needsWrapper ? mergedContainerRef : undefined}
|
|
169
|
+
id={!needsWrapper ? id : undefined}
|
|
170
|
+
data-testid={!needsWrapper ? testID : undefined}
|
|
171
|
+
>
|
|
172
|
+
{labelPosition === 'left' && (
|
|
173
|
+
<span {...labelProps}>{label}</span>
|
|
174
|
+
)}
|
|
175
|
+
{switchElement}
|
|
176
|
+
{labelPosition === 'right' && (
|
|
177
|
+
<span {...labelProps}>{label}</span>
|
|
178
|
+
)}
|
|
179
|
+
</div>
|
|
180
|
+
) : switchElement;
|
|
181
|
+
|
|
182
|
+
// If wrapper needed for error/helperText
|
|
183
|
+
if (needsWrapper) {
|
|
150
184
|
return (
|
|
151
|
-
<div
|
|
152
|
-
{
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
185
|
+
<div {...wrapperProps} id={id} data-testid={testID}>
|
|
186
|
+
{switchWithLabel}
|
|
187
|
+
{showFooter && (
|
|
188
|
+
<div style={{ flex: 1 }}>
|
|
189
|
+
{error && (
|
|
190
|
+
<span {...helperTextProps} role="alert">
|
|
191
|
+
{error}
|
|
192
|
+
</span>
|
|
193
|
+
)}
|
|
194
|
+
{!error && helperText && (
|
|
195
|
+
<span {...helperTextProps}>
|
|
196
|
+
{helperText}
|
|
197
|
+
</span>
|
|
198
|
+
)}
|
|
199
|
+
</div>
|
|
161
200
|
)}
|
|
162
201
|
</div>
|
|
163
202
|
);
|
|
164
203
|
}
|
|
165
204
|
|
|
166
|
-
return
|
|
205
|
+
return switchWithLabel;
|
|
167
206
|
});
|
|
168
207
|
|
|
169
208
|
Switch.displayName = 'Switch';
|
package/src/Switch/types.ts
CHANGED
|
@@ -28,6 +28,16 @@ export interface SwitchProps extends FormInputStyleProps, SelectionAccessibility
|
|
|
28
28
|
*/
|
|
29
29
|
disabled?: boolean;
|
|
30
30
|
|
|
31
|
+
/**
|
|
32
|
+
* Error message to display below the switch. When set, shows error styling.
|
|
33
|
+
*/
|
|
34
|
+
error?: string;
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Helper text to display below the switch. Hidden when error is set.
|
|
38
|
+
*/
|
|
39
|
+
helperText?: string;
|
|
40
|
+
|
|
31
41
|
/**
|
|
32
42
|
* Label text to display next to the switch
|
|
33
43
|
*/
|
|
@@ -6,6 +6,7 @@ import { TextInputProps } from './types';
|
|
|
6
6
|
import { textInputStyles } from './TextInput.styles';
|
|
7
7
|
import { getNativeFormAccessibilityProps } from '../utils/accessibility';
|
|
8
8
|
import type { IdealystElement } from '../utils/refTypes';
|
|
9
|
+
import Text from '../Text';
|
|
9
10
|
|
|
10
11
|
// Inner TextInput component that can be memoized to prevent re-renders
|
|
11
12
|
// for Android secure text entry
|
|
@@ -68,6 +69,9 @@ const TextInput = React.forwardRef<IdealystElement, TextInputProps>(({
|
|
|
68
69
|
size = 'md',
|
|
69
70
|
type = 'outlined',
|
|
70
71
|
hasError = false,
|
|
72
|
+
error,
|
|
73
|
+
helperText,
|
|
74
|
+
label,
|
|
71
75
|
// Spacing variants from FormInputStyleProps
|
|
72
76
|
margin,
|
|
73
77
|
marginVertical,
|
|
@@ -91,6 +95,12 @@ const TextInput = React.forwardRef<IdealystElement, TextInputProps>(({
|
|
|
91
95
|
const [isFocused, setIsFocused] = useState(false);
|
|
92
96
|
const [isPasswordVisible, setIsPasswordVisible] = useState(false);
|
|
93
97
|
|
|
98
|
+
// Derive hasError from error prop or hasError boolean
|
|
99
|
+
const computedHasError = Boolean(error) || hasError;
|
|
100
|
+
|
|
101
|
+
// Determine if we need a wrapper (when label, error, or helperText is present)
|
|
102
|
+
const needsWrapper = Boolean(label) || Boolean(error) || Boolean(helperText);
|
|
103
|
+
|
|
94
104
|
// Track if this is a secure field that needs Android workaround
|
|
95
105
|
const isSecureField = inputMode === 'password' || secureTextEntry;
|
|
96
106
|
const needsAndroidSecureWorkaround = Platform.OS === 'android' && isSecureField && !isPasswordVisible;
|
|
@@ -160,12 +170,13 @@ const TextInput = React.forwardRef<IdealystElement, TextInputProps>(({
|
|
|
160
170
|
|
|
161
171
|
// Generate native accessibility props
|
|
162
172
|
const nativeA11yProps = useMemo(() => {
|
|
163
|
-
// Derive invalid state from
|
|
164
|
-
const isInvalid = accessibilityInvalid ??
|
|
173
|
+
// Derive invalid state from computedHasError or explicit accessibilityInvalid
|
|
174
|
+
const isInvalid = accessibilityInvalid ?? computedHasError;
|
|
175
|
+
const computedLabel = accessibilityLabel ?? label ?? placeholder;
|
|
165
176
|
|
|
166
177
|
return getNativeFormAccessibilityProps({
|
|
167
|
-
accessibilityLabel,
|
|
168
|
-
accessibilityHint,
|
|
178
|
+
accessibilityLabel: computedLabel,
|
|
179
|
+
accessibilityHint: accessibilityHint ?? (error || helperText),
|
|
169
180
|
accessibilityDisabled: accessibilityDisabled ?? disabled,
|
|
170
181
|
accessibilityHidden,
|
|
171
182
|
accessibilityRole: accessibilityRole ?? 'textbox',
|
|
@@ -174,14 +185,18 @@ const TextInput = React.forwardRef<IdealystElement, TextInputProps>(({
|
|
|
174
185
|
});
|
|
175
186
|
}, [
|
|
176
187
|
accessibilityLabel,
|
|
188
|
+
label,
|
|
189
|
+
placeholder,
|
|
177
190
|
accessibilityHint,
|
|
191
|
+
error,
|
|
192
|
+
helperText,
|
|
178
193
|
accessibilityDisabled,
|
|
179
194
|
disabled,
|
|
180
195
|
accessibilityHidden,
|
|
181
196
|
accessibilityRole,
|
|
182
197
|
accessibilityRequired,
|
|
183
198
|
accessibilityInvalid,
|
|
184
|
-
|
|
199
|
+
computedHasError,
|
|
185
200
|
]);
|
|
186
201
|
|
|
187
202
|
// Determine the textContentType for iOS AutoFill
|
|
@@ -224,7 +239,7 @@ const TextInput = React.forwardRef<IdealystElement, TextInputProps>(({
|
|
|
224
239
|
size,
|
|
225
240
|
type,
|
|
226
241
|
focused: isFocused,
|
|
227
|
-
hasError,
|
|
242
|
+
hasError: computedHasError,
|
|
228
243
|
disabled,
|
|
229
244
|
margin,
|
|
230
245
|
marginVertical,
|
|
@@ -232,11 +247,19 @@ const TextInput = React.forwardRef<IdealystElement, TextInputProps>(({
|
|
|
232
247
|
});
|
|
233
248
|
|
|
234
249
|
// Compute dynamic styles - call as functions for theme reactivity
|
|
235
|
-
const containerStyle = (textInputStyles.container as any)({ type, focused: isFocused, hasError, disabled });
|
|
250
|
+
const containerStyle = (textInputStyles.container as any)({ type, focused: isFocused, hasError: computedHasError, disabled });
|
|
236
251
|
const leftIconContainerStyle = (textInputStyles.leftIconContainer as any)({});
|
|
237
252
|
const rightIconContainerStyle = (textInputStyles.rightIconContainer as any)({});
|
|
238
253
|
const passwordToggleStyle = (textInputStyles.passwordToggle as any)({});
|
|
239
254
|
|
|
255
|
+
// Wrapper, label, and footer styles
|
|
256
|
+
const wrapperStyle = (textInputStyles.wrapper as any)({});
|
|
257
|
+
const labelStyle = (textInputStyles.label as any)({ disabled });
|
|
258
|
+
const footerStyle = (textInputStyles.footer as any)({});
|
|
259
|
+
const helperTextStyle = (textInputStyles.helperText as any)({ hasError: computedHasError });
|
|
260
|
+
|
|
261
|
+
const showFooter = Boolean(error) || Boolean(helperText);
|
|
262
|
+
|
|
240
263
|
// Helper to render left icon
|
|
241
264
|
const renderLeftIcon = () => {
|
|
242
265
|
if (!leftIcon) return null;
|
|
@@ -275,44 +298,104 @@ const TextInput = React.forwardRef<IdealystElement, TextInputProps>(({
|
|
|
275
298
|
return null;
|
|
276
299
|
};
|
|
277
300
|
|
|
301
|
+
// If no wrapper needed, return flat input container
|
|
302
|
+
if (!needsWrapper) {
|
|
303
|
+
return (
|
|
304
|
+
<View style={[containerStyle, style]} testID={testID} nativeID={id}>
|
|
305
|
+
{/* Left Icon */}
|
|
306
|
+
{leftIcon && (
|
|
307
|
+
<View style={leftIconContainerStyle}>
|
|
308
|
+
{renderLeftIcon()}
|
|
309
|
+
</View>
|
|
310
|
+
)}
|
|
311
|
+
|
|
312
|
+
{/* Input */}
|
|
313
|
+
<InnerRNTextInput
|
|
314
|
+
inputRef={ref}
|
|
315
|
+
value={value}
|
|
316
|
+
onChangeText={handleChangeText}
|
|
317
|
+
isAndroidSecure={needsAndroidSecureWorkaround}
|
|
318
|
+
inputStyle={inputStyle}
|
|
319
|
+
textInputProps={textInputProps}
|
|
320
|
+
/>
|
|
321
|
+
|
|
322
|
+
{/* Right Icon or Password Toggle */}
|
|
323
|
+
{shouldShowPasswordToggle ? (
|
|
324
|
+
<TouchableOpacity
|
|
325
|
+
style={passwordToggleStyle}
|
|
326
|
+
onPress={togglePasswordVisibility}
|
|
327
|
+
disabled={disabled}
|
|
328
|
+
accessibilityLabel={isPasswordVisible ? 'Hide password' : 'Show password'}
|
|
329
|
+
>
|
|
330
|
+
<MaterialDesignIcons
|
|
331
|
+
name={isPasswordVisible ? 'eye-off' : 'eye'}
|
|
332
|
+
size={iconSize}
|
|
333
|
+
color={iconColor}
|
|
334
|
+
/>
|
|
335
|
+
</TouchableOpacity>
|
|
336
|
+
) : rightIcon ? (
|
|
337
|
+
<View style={rightIconContainerStyle}>
|
|
338
|
+
{renderRightIcon()}
|
|
339
|
+
</View>
|
|
340
|
+
) : null}
|
|
341
|
+
</View>
|
|
342
|
+
);
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
// With wrapper for label/error/helperText
|
|
278
346
|
return (
|
|
279
|
-
<View style={[
|
|
280
|
-
{
|
|
281
|
-
|
|
282
|
-
<View style={leftIconContainerStyle}>
|
|
283
|
-
{renderLeftIcon()}
|
|
284
|
-
</View>
|
|
347
|
+
<View style={[wrapperStyle, style]} testID={testID} nativeID={id}>
|
|
348
|
+
{label && (
|
|
349
|
+
<Text style={labelStyle}>{label}</Text>
|
|
285
350
|
)}
|
|
286
351
|
|
|
287
|
-
{
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
352
|
+
<View style={containerStyle}>
|
|
353
|
+
{/* Left Icon */}
|
|
354
|
+
{leftIcon && (
|
|
355
|
+
<View style={leftIconContainerStyle}>
|
|
356
|
+
{renderLeftIcon()}
|
|
357
|
+
</View>
|
|
358
|
+
)}
|
|
359
|
+
|
|
360
|
+
{/* Input */}
|
|
361
|
+
<InnerRNTextInput
|
|
362
|
+
inputRef={ref}
|
|
363
|
+
value={value}
|
|
364
|
+
onChangeText={handleChangeText}
|
|
365
|
+
isAndroidSecure={needsAndroidSecureWorkaround}
|
|
366
|
+
inputStyle={inputStyle}
|
|
367
|
+
textInputProps={textInputProps}
|
|
368
|
+
/>
|
|
296
369
|
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
370
|
+
{/* Right Icon or Password Toggle */}
|
|
371
|
+
{shouldShowPasswordToggle ? (
|
|
372
|
+
<TouchableOpacity
|
|
373
|
+
style={passwordToggleStyle}
|
|
374
|
+
onPress={togglePasswordVisibility}
|
|
375
|
+
disabled={disabled}
|
|
376
|
+
accessibilityLabel={isPasswordVisible ? 'Hide password' : 'Show password'}
|
|
377
|
+
>
|
|
378
|
+
<MaterialDesignIcons
|
|
379
|
+
name={isPasswordVisible ? 'eye-off' : 'eye'}
|
|
380
|
+
size={iconSize}
|
|
381
|
+
color={iconColor}
|
|
382
|
+
/>
|
|
383
|
+
</TouchableOpacity>
|
|
384
|
+
) : rightIcon ? (
|
|
385
|
+
<View style={rightIconContainerStyle}>
|
|
386
|
+
{renderRightIcon()}
|
|
387
|
+
</View>
|
|
388
|
+
) : null}
|
|
389
|
+
</View>
|
|
390
|
+
|
|
391
|
+
{showFooter && (
|
|
392
|
+
<View style={footerStyle}>
|
|
393
|
+
<View style={{ flex: 1 }}>
|
|
394
|
+
{error && <Text style={helperTextStyle}>{error}</Text>}
|
|
395
|
+
{!error && helperText && <Text style={helperTextStyle}>{helperText}</Text>}
|
|
396
|
+
</View>
|
|
314
397
|
</View>
|
|
315
|
-
)
|
|
398
|
+
)}
|
|
316
399
|
</View>
|
|
317
400
|
);
|
|
318
401
|
});
|
|
@@ -75,15 +75,6 @@ export const textInputStyles = defineStyle('TextInput', (theme: Theme) => ({
|
|
|
75
75
|
height: theme.sizes.$input.height,
|
|
76
76
|
paddingHorizontal: theme.sizes.$input.paddingHorizontal,
|
|
77
77
|
},
|
|
78
|
-
margin: {
|
|
79
|
-
margin: theme.sizes.$view.padding,
|
|
80
|
-
},
|
|
81
|
-
marginVertical: {
|
|
82
|
-
marginVertical: theme.sizes.$view.padding,
|
|
83
|
-
},
|
|
84
|
-
marginHorizontal: {
|
|
85
|
-
marginHorizontal: theme.sizes.$view.padding,
|
|
86
|
-
},
|
|
87
78
|
},
|
|
88
79
|
_web: {
|
|
89
80
|
boxSizing: 'border-box',
|
|
@@ -208,6 +199,53 @@ export const textInputStyles = defineStyle('TextInput', (theme: Theme) => ({
|
|
|
208
199
|
},
|
|
209
200
|
},
|
|
210
201
|
}),
|
|
202
|
+
|
|
203
|
+
wrapper: (_props: TextInputDynamicProps) => ({
|
|
204
|
+
display: 'flex' as const,
|
|
205
|
+
flexDirection: 'column' as const,
|
|
206
|
+
gap: 4,
|
|
207
|
+
variants: {
|
|
208
|
+
margin: {
|
|
209
|
+
margin: theme.sizes.$view.padding,
|
|
210
|
+
},
|
|
211
|
+
marginVertical: {
|
|
212
|
+
marginVertical: theme.sizes.$view.padding,
|
|
213
|
+
},
|
|
214
|
+
marginHorizontal: {
|
|
215
|
+
marginHorizontal: theme.sizes.$view.padding,
|
|
216
|
+
},
|
|
217
|
+
},
|
|
218
|
+
}),
|
|
219
|
+
|
|
220
|
+
label: (_props: TextInputDynamicProps) => ({
|
|
221
|
+
fontSize: 14,
|
|
222
|
+
fontWeight: '500' as const,
|
|
223
|
+
color: theme.colors.text.primary,
|
|
224
|
+
variants: {
|
|
225
|
+
disabled: {
|
|
226
|
+
true: { opacity: 0.5 },
|
|
227
|
+
false: { opacity: 1 },
|
|
228
|
+
},
|
|
229
|
+
},
|
|
230
|
+
}),
|
|
231
|
+
|
|
232
|
+
helperText: (_props: TextInputDynamicProps) => ({
|
|
233
|
+
fontSize: 12,
|
|
234
|
+
variants: {
|
|
235
|
+
hasError: {
|
|
236
|
+
true: { color: theme.intents.danger.primary },
|
|
237
|
+
false: { color: theme.colors.text.secondary },
|
|
238
|
+
},
|
|
239
|
+
},
|
|
240
|
+
}),
|
|
241
|
+
|
|
242
|
+
footer: (_props: TextInputDynamicProps) => ({
|
|
243
|
+
display: 'flex' as const,
|
|
244
|
+
flexDirection: 'row' as const,
|
|
245
|
+
justifyContent: 'space-between' as const,
|
|
246
|
+
alignItems: 'center' as const,
|
|
247
|
+
gap: 4,
|
|
248
|
+
}),
|
|
211
249
|
}));
|
|
212
250
|
|
|
213
251
|
// Legacy export for backwards compatibility
|