@proyecto-viviana/solidaria-components 0.2.9 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +39 -272
- package/dist/ActionBar.d.ts +21 -13
- package/dist/ActionBar.d.ts.map +1 -1
- package/dist/ActionGroup.d.ts +8 -8
- package/dist/ActionGroup.d.ts.map +1 -1
- package/dist/Alert.d.ts +5 -5
- package/dist/Alert.d.ts.map +1 -1
- package/dist/Autocomplete.d.ts +5 -5
- package/dist/Autocomplete.d.ts.map +1 -1
- package/dist/Breadcrumbs.d.ts +18 -7
- package/dist/Breadcrumbs.d.ts.map +1 -1
- package/dist/Button.d.ts +24 -5
- package/dist/Button.d.ts.map +1 -1
- package/dist/Calendar.d.ts +38 -7
- package/dist/Calendar.d.ts.map +1 -1
- package/dist/Checkbox.d.ts +32 -7
- package/dist/Checkbox.d.ts.map +1 -1
- package/dist/Collection.d.ts +19 -14
- package/dist/Collection.d.ts.map +1 -1
- package/dist/Color.d.ts +103 -14
- package/dist/Color.d.ts.map +1 -1
- package/dist/ColorEditor.d.ts +6 -6
- package/dist/ColorEditor.d.ts.map +1 -1
- package/dist/ComboBox.d.ts +85 -19
- package/dist/ComboBox.d.ts.map +1 -1
- package/dist/ContextualHelpTrigger.d.ts +2 -2
- package/dist/ContextualHelpTrigger.d.ts.map +1 -1
- package/dist/DateField.d.ts +8 -6
- package/dist/DateField.d.ts.map +1 -1
- package/dist/DatePicker.d.ts +53 -22
- package/dist/DatePicker.d.ts.map +1 -1
- package/dist/DateRangePickerContext.d.ts +30 -0
- package/dist/DateRangePickerContext.d.ts.map +1 -0
- package/dist/Dialog.d.ts +5 -5
- package/dist/Dialog.d.ts.map +1 -1
- package/dist/Disclosure.d.ts +23 -5
- package/dist/Disclosure.d.ts.map +1 -1
- package/dist/DragAndDrop.d.ts +6 -6
- package/dist/DragAndDrop.d.ts.map +1 -1
- package/dist/DragPreview.d.ts +2 -2
- package/dist/DragPreview.d.ts.map +1 -1
- package/dist/DropZone.d.ts +4 -4
- package/dist/DropZone.d.ts.map +1 -1
- package/dist/FieldError.d.ts +9 -5
- package/dist/FieldError.d.ts.map +1 -1
- package/dist/FileTrigger.d.ts +3 -3
- package/dist/FileTrigger.d.ts.map +1 -1
- package/dist/Focusable.d.ts +2 -2
- package/dist/Focusable.d.ts.map +1 -1
- package/dist/Form.d.ts +18 -4
- package/dist/Form.d.ts.map +1 -1
- package/dist/GridList.d.ts +32 -12
- package/dist/GridList.d.ts.map +1 -1
- package/dist/HiddenDateInput.d.ts +26 -0
- package/dist/HiddenDateInput.d.ts.map +1 -0
- package/dist/HiddenTimeInput.d.ts +25 -0
- package/dist/HiddenTimeInput.d.ts.map +1 -0
- package/dist/Icon.d.ts +5 -5
- package/dist/Icon.d.ts.map +1 -1
- package/dist/Keyboard.d.ts +1 -1
- package/dist/Landmark.d.ts +3 -3
- package/dist/Landmark.d.ts.map +1 -1
- package/dist/Link.d.ts +10 -4
- package/dist/Link.d.ts.map +1 -1
- package/dist/ListBox.d.ts +32 -12
- package/dist/ListBox.d.ts.map +1 -1
- package/dist/ListDropTargetDelegate.d.ts +6 -6
- package/dist/ListDropTargetDelegate.d.ts.map +1 -1
- package/dist/Menu.d.ts +65 -14
- package/dist/Menu.d.ts.map +1 -1
- package/dist/Meter.d.ts +3 -3
- package/dist/Meter.d.ts.map +1 -1
- package/dist/Modal.d.ts +5 -5
- package/dist/Modal.d.ts.map +1 -1
- package/dist/NumberField.d.ts +8 -12
- package/dist/NumberField.d.ts.map +1 -1
- package/dist/Popover.d.ts +28 -5
- package/dist/Popover.d.ts.map +1 -1
- package/dist/Pressable.d.ts +2 -2
- package/dist/Pressable.d.ts.map +1 -1
- package/dist/ProgressBar.d.ts +5 -3
- package/dist/ProgressBar.d.ts.map +1 -1
- package/dist/RadioGroup.d.ts +43 -9
- package/dist/RadioGroup.d.ts.map +1 -1
- package/dist/RangeCalendar.d.ts +34 -7
- package/dist/RangeCalendar.d.ts.map +1 -1
- package/dist/RouterProvider.d.ts +2 -2
- package/dist/RouterProvider.d.ts.map +1 -1
- package/dist/SearchField.d.ts +23 -20
- package/dist/SearchField.d.ts.map +1 -1
- package/dist/Select.d.ts +41 -11
- package/dist/Select.d.ts.map +1 -1
- package/dist/SelectionIndicator.d.ts +3 -3
- package/dist/SelectionIndicator.d.ts.map +1 -1
- package/dist/Separator.d.ts +9 -3
- package/dist/Separator.d.ts.map +1 -1
- package/dist/SharedElementTransition.d.ts +6 -4
- package/dist/SharedElementTransition.d.ts.map +1 -1
- package/dist/Slider.d.ts +12 -8
- package/dist/Slider.d.ts.map +1 -1
- package/dist/StepList.d.ts +90 -0
- package/dist/StepList.d.ts.map +1 -0
- package/dist/Switch.d.ts +11 -5
- package/dist/Switch.d.ts.map +1 -1
- package/dist/Table.d.ts +187 -23
- package/dist/Table.d.ts.map +1 -1
- package/dist/Tabs.d.ts +45 -9
- package/dist/Tabs.d.ts.map +1 -1
- package/dist/TagGroup.d.ts +12 -10
- package/dist/TagGroup.d.ts.map +1 -1
- package/dist/Text.d.ts +2 -2
- package/dist/TextField.d.ts +15 -11
- package/dist/TextField.d.ts.map +1 -1
- package/dist/TimeField.d.ts +6 -6
- package/dist/TimeField.d.ts.map +1 -1
- package/dist/Toast.d.ts +29 -14
- package/dist/Toast.d.ts.map +1 -1
- package/dist/ToggleButton.d.ts +11 -5
- package/dist/ToggleButton.d.ts.map +1 -1
- package/dist/ToggleButtonGroup.d.ts +7 -7
- package/dist/ToggleButtonGroup.d.ts.map +1 -1
- package/dist/Toolbar.d.ts +7 -3
- package/dist/Toolbar.d.ts.map +1 -1
- package/dist/Tooltip.d.ts +50 -8
- package/dist/Tooltip.d.ts.map +1 -1
- package/dist/Tree.d.ts +66 -17
- package/dist/Tree.d.ts.map +1 -1
- package/dist/Virtualizer.d.ts +12 -12
- package/dist/Virtualizer.d.ts.map +1 -1
- package/dist/VirtualizerLayouts.d.ts +2 -2
- package/dist/VirtualizerLayouts.d.ts.map +1 -1
- package/dist/VisuallyHidden.d.ts +1 -1
- package/dist/VisuallyHidden.d.ts.map +1 -1
- package/dist/contexts.d.ts +5 -1
- package/dist/contexts.d.ts.map +1 -1
- package/dist/index.d.ts +73 -71
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +23247 -18564
- package/dist/index.js.map +1 -1
- package/dist/index.jsx +18110 -0
- package/dist/index.jsx.map +1 -0
- package/dist/useDragAndDrop.d.ts +13 -13
- package/dist/useDragAndDrop.d.ts.map +1 -1
- package/dist/utils.d.ts +2 -2
- package/dist/utils.d.ts.map +1 -1
- package/dist/virtualizer/Layout.d.ts +1 -1
- package/dist/virtualizer/Layout.d.ts.map +1 -1
- package/package.json +31 -32
- package/src/ActionBar.tsx +75 -72
- package/src/ActionGroup.tsx +53 -61
- package/src/Alert.tsx +17 -42
- package/src/Autocomplete.tsx +39 -44
- package/src/Breadcrumbs.tsx +149 -80
- package/src/Button.tsx +267 -70
- package/src/Calendar.tsx +218 -138
- package/src/Checkbox.tsx +413 -121
- package/src/Collection.tsx +67 -58
- package/src/Color.tsx +803 -380
- package/src/ColorEditor.tsx +131 -149
- package/src/ComboBox.tsx +414 -249
- package/src/ContextualHelpTrigger.tsx +86 -74
- package/src/DateField.tsx +185 -91
- package/src/DatePicker.tsx +524 -213
- package/src/DateRangePickerContext.tsx +44 -0
- package/src/Dialog.tsx +156 -118
- package/src/Disclosure.tsx +127 -80
- package/src/DragAndDrop.tsx +60 -54
- package/src/DragPreview.tsx +13 -11
- package/src/DropZone.tsx +42 -22
- package/src/FieldError.tsx +45 -23
- package/src/FileTrigger.tsx +19 -19
- package/src/Focusable.tsx +21 -24
- package/src/Form.tsx +71 -16
- package/src/GridList.tsx +273 -197
- package/src/HiddenDateInput.tsx +153 -0
- package/src/HiddenTimeInput.tsx +133 -0
- package/src/Icon.tsx +22 -43
- package/src/Keyboard.tsx +3 -3
- package/src/Landmark.tsx +37 -63
- package/src/Link.tsx +125 -75
- package/src/ListBox.tsx +332 -233
- package/src/ListDropTargetDelegate.ts +81 -80
- package/src/Menu.tsx +1023 -274
- package/src/Meter.tsx +38 -56
- package/src/Modal.tsx +243 -175
- package/src/NumberField.tsx +139 -143
- package/src/Popover.tsx +386 -233
- package/src/Pressable.tsx +21 -21
- package/src/ProgressBar.tsx +48 -57
- package/src/RadioGroup.tsx +524 -122
- package/src/RangeCalendar.tsx +157 -90
- package/src/RouterProvider.tsx +30 -47
- package/src/SearchField.tsx +362 -143
- package/src/Select.tsx +656 -233
- package/src/SelectionIndicator.tsx +18 -15
- package/src/Separator.tsx +47 -49
- package/src/SharedElementTransition.tsx +103 -97
- package/src/Slider.tsx +138 -98
- package/src/StepList.tsx +272 -0
- package/src/Switch.tsx +93 -46
- package/src/Table.tsx +1308 -342
- package/src/Tabs.tsx +324 -103
- package/src/TagGroup.tsx +139 -126
- package/src/Text.tsx +3 -3
- package/src/TextField.tsx +389 -79
- package/src/TimeField.tsx +136 -76
- package/src/Toast.tsx +209 -157
- package/src/ToggleButton.tsx +47 -37
- package/src/ToggleButtonGroup.tsx +39 -34
- package/src/Toolbar.tsx +54 -69
- package/src/Tooltip.tsx +387 -119
- package/src/Tree.tsx +651 -368
- package/src/Virtualizer.tsx +208 -180
- package/src/VirtualizerLayouts.ts +45 -30
- package/src/VisuallyHidden.tsx +19 -19
- package/src/contexts.ts +29 -37
- package/src/index.ts +110 -195
- package/src/useDragAndDrop.ts +87 -71
- package/src/utils.tsx +40 -55
- package/src/virtualizer/Layout.ts +14 -22
- package/dist/index.ssr.js +0 -16996
- package/dist/index.ssr.js.map +0 -1
package/src/TextField.tsx
CHANGED
|
@@ -10,16 +10,27 @@ import {
|
|
|
10
10
|
createContext,
|
|
11
11
|
useContext,
|
|
12
12
|
createMemo,
|
|
13
|
+
createSignal,
|
|
14
|
+
createEffect,
|
|
15
|
+
onCleanup,
|
|
16
|
+
onMount,
|
|
13
17
|
splitProps,
|
|
14
|
-
|
|
18
|
+
untrack,
|
|
19
|
+
} from "solid-js";
|
|
15
20
|
import {
|
|
16
21
|
createTextField,
|
|
17
22
|
createFocusRing,
|
|
18
23
|
createHover,
|
|
19
24
|
mergeProps,
|
|
20
25
|
type AriaTextFieldProps,
|
|
21
|
-
} from
|
|
22
|
-
import {
|
|
26
|
+
} from "@proyecto-viviana/solidaria";
|
|
27
|
+
import {
|
|
28
|
+
createTextFieldState,
|
|
29
|
+
VALID_VALIDITY_STATE,
|
|
30
|
+
type ValidationResult,
|
|
31
|
+
} from "@proyecto-viviana/solid-stately";
|
|
32
|
+
import { FormContext, type FormProps } from "./Form";
|
|
33
|
+
import { FieldErrorContext, type FieldErrorContextValue } from "./FieldError";
|
|
23
34
|
import {
|
|
24
35
|
type RenderChildren,
|
|
25
36
|
type ClassNameOrFunction,
|
|
@@ -27,11 +38,7 @@ import {
|
|
|
27
38
|
type SlotProps,
|
|
28
39
|
useRenderProps,
|
|
29
40
|
filterDOMProps,
|
|
30
|
-
} from
|
|
31
|
-
|
|
32
|
-
// ============================================
|
|
33
|
-
// TYPES
|
|
34
|
-
// ============================================
|
|
41
|
+
} from "./utils";
|
|
35
42
|
|
|
36
43
|
export interface TextFieldRenderProps {
|
|
37
44
|
/** Whether the text field is disabled. */
|
|
@@ -50,27 +57,28 @@ export interface TextFieldRenderProps {
|
|
|
50
57
|
isFocusVisible: boolean;
|
|
51
58
|
}
|
|
52
59
|
|
|
53
|
-
export interface TextFieldProps
|
|
54
|
-
extends Omit<AriaTextFieldProps, 'children'>,
|
|
55
|
-
SlotProps {
|
|
60
|
+
export interface TextFieldProps extends Omit<AriaTextFieldProps, "children">, SlotProps {
|
|
56
61
|
/** The children of the component. A function may be provided to receive render props. */
|
|
57
62
|
children?: RenderChildren<TextFieldRenderProps>;
|
|
58
63
|
/** The CSS className for the element. */
|
|
59
64
|
class?: ClassNameOrFunction<TextFieldRenderProps>;
|
|
60
65
|
/** The inline style for the element. */
|
|
61
66
|
style?: StyleOrFunction<TextFieldRenderProps>;
|
|
67
|
+
/** Custom renderer for the outer text field element. */
|
|
68
|
+
render?: (
|
|
69
|
+
props: JSX.HTMLAttributes<HTMLDivElement>,
|
|
70
|
+
renderProps: TextFieldRenderProps,
|
|
71
|
+
) => JSX.Element;
|
|
62
72
|
}
|
|
63
73
|
|
|
64
|
-
// ============================================
|
|
65
|
-
// CONTEXT
|
|
66
|
-
// ============================================
|
|
67
|
-
|
|
68
74
|
export interface TextFieldContextValue {
|
|
69
|
-
labelProps
|
|
70
|
-
inputProps
|
|
71
|
-
descriptionProps
|
|
72
|
-
errorMessageProps
|
|
73
|
-
isInvalid
|
|
75
|
+
labelProps?: JSX.LabelHTMLAttributes<HTMLLabelElement>;
|
|
76
|
+
inputProps?: JSX.InputHTMLAttributes<HTMLInputElement>;
|
|
77
|
+
descriptionProps?: JSX.HTMLAttributes<HTMLElement>;
|
|
78
|
+
errorMessageProps?: JSX.HTMLAttributes<HTMLElement>;
|
|
79
|
+
isInvalid?: boolean;
|
|
80
|
+
slots?: Record<string, TextFieldProps>;
|
|
81
|
+
inputId?: string;
|
|
74
82
|
}
|
|
75
83
|
|
|
76
84
|
export const TextFieldContext = createContext<TextFieldContextValue | null>(null);
|
|
@@ -79,9 +87,55 @@ export const InputContext = TextFieldContext;
|
|
|
79
87
|
export const TextAreaContext = TextFieldContext;
|
|
80
88
|
export const FieldInputContext = TextFieldContext;
|
|
81
89
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
90
|
+
function withFormValidationBehavior(
|
|
91
|
+
props: TextFieldProps,
|
|
92
|
+
formContext: FormProps | null,
|
|
93
|
+
): TextFieldProps {
|
|
94
|
+
if (!formContext?.validationBehavior) {
|
|
95
|
+
return props;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
return new Proxy(props, {
|
|
99
|
+
get(target, property, receiver) {
|
|
100
|
+
const localValue = Reflect.get(target, property, receiver);
|
|
101
|
+
if (property === "validationBehavior" && localValue === undefined) {
|
|
102
|
+
return formContext.validationBehavior;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
return localValue;
|
|
106
|
+
},
|
|
107
|
+
has(target, property) {
|
|
108
|
+
return (
|
|
109
|
+
Reflect.has(target, property) ||
|
|
110
|
+
(property === "validationBehavior" && formContext.validationBehavior !== undefined)
|
|
111
|
+
);
|
|
112
|
+
},
|
|
113
|
+
ownKeys(target) {
|
|
114
|
+
const keys = new Set(Reflect.ownKeys(target));
|
|
115
|
+
if (formContext.validationBehavior !== undefined) {
|
|
116
|
+
keys.add("validationBehavior");
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
return Array.from(keys);
|
|
120
|
+
},
|
|
121
|
+
getOwnPropertyDescriptor(target, property) {
|
|
122
|
+
const descriptor = Reflect.getOwnPropertyDescriptor(target, property);
|
|
123
|
+
if (descriptor) {
|
|
124
|
+
return descriptor;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
if (property === "validationBehavior" && formContext.validationBehavior !== undefined) {
|
|
128
|
+
return {
|
|
129
|
+
enumerable: true,
|
|
130
|
+
configurable: true,
|
|
131
|
+
get: () => formContext.validationBehavior,
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
return undefined;
|
|
136
|
+
},
|
|
137
|
+
});
|
|
138
|
+
}
|
|
85
139
|
|
|
86
140
|
export interface LabelProps extends JSX.LabelHTMLAttributes<HTMLLabelElement> {
|
|
87
141
|
children?: JSX.Element;
|
|
@@ -97,16 +151,49 @@ export function Label(props: LabelProps): JSX.Element {
|
|
|
97
151
|
// Merge context labelProps with local props (local props take precedence)
|
|
98
152
|
const mergedProps = () => {
|
|
99
153
|
if (context) {
|
|
100
|
-
const { ref: _ref, ...contextLabelProps } = context.labelProps as Record<
|
|
101
|
-
|
|
154
|
+
const { ref: _ref, ...contextLabelProps } = (context.labelProps ?? {}) as Record<
|
|
155
|
+
string,
|
|
156
|
+
unknown
|
|
157
|
+
>;
|
|
158
|
+
const merged = {
|
|
159
|
+
...contextLabelProps,
|
|
160
|
+
...(context.inputId ? { for: context.inputId } : {}),
|
|
161
|
+
...props,
|
|
162
|
+
} as Record<string, unknown>;
|
|
163
|
+
if (merged.class == null) {
|
|
164
|
+
merged.class = "solidaria-Label";
|
|
165
|
+
}
|
|
166
|
+
return merged;
|
|
102
167
|
}
|
|
103
|
-
return props;
|
|
168
|
+
return props.class == null ? { ...props, class: "solidaria-Label" } : props;
|
|
104
169
|
};
|
|
105
170
|
|
|
106
171
|
return <label {...mergedProps()}>{props.children}</label>;
|
|
107
172
|
}
|
|
108
173
|
|
|
109
|
-
export interface InputProps extends Omit<JSX.InputHTMLAttributes<HTMLInputElement>,
|
|
174
|
+
export interface InputProps extends Omit<JSX.InputHTMLAttributes<HTMLInputElement>, "children"> {}
|
|
175
|
+
|
|
176
|
+
function eventWithCurrentTarget<T extends HTMLElement>(event: Event, element: T): Event {
|
|
177
|
+
return new Proxy(event, {
|
|
178
|
+
get(target, property, receiver) {
|
|
179
|
+
if (property === "target" || property === "currentTarget") {
|
|
180
|
+
return element;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
const value = Reflect.get(target, property, receiver);
|
|
184
|
+
return typeof value === "function" ? value.bind(target) : value;
|
|
185
|
+
},
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
function clearDelegatedTextEntryHandlers(element: HTMLElement) {
|
|
190
|
+
const delegatedElement = element as HTMLElement & {
|
|
191
|
+
$$input?: unknown;
|
|
192
|
+
$$change?: unknown;
|
|
193
|
+
};
|
|
194
|
+
delete delegatedElement.$$input;
|
|
195
|
+
delete delegatedElement.$$change;
|
|
196
|
+
}
|
|
110
197
|
|
|
111
198
|
/**
|
|
112
199
|
* An input element that automatically wires up to the parent TextField context.
|
|
@@ -115,21 +202,85 @@ export interface InputProps extends Omit<JSX.InputHTMLAttributes<HTMLInputElemen
|
|
|
115
202
|
*/
|
|
116
203
|
export function Input(props: InputProps): JSX.Element {
|
|
117
204
|
const context = useContext(TextFieldContext);
|
|
205
|
+
let inputElement: HTMLInputElement | undefined;
|
|
118
206
|
|
|
119
207
|
// Merge context inputProps with local props (local props take precedence)
|
|
120
208
|
const mergedProps = () => {
|
|
121
209
|
if (context) {
|
|
122
210
|
// Remove ref from context props to avoid conflicts
|
|
123
|
-
const { ref: _ref, ...contextInputProps } = context.inputProps as Record<
|
|
124
|
-
|
|
211
|
+
const { ref: _ref, ...contextInputProps } = (context.inputProps ?? {}) as Record<
|
|
212
|
+
string,
|
|
213
|
+
unknown
|
|
214
|
+
>;
|
|
215
|
+
const merged = { ...contextInputProps, ...props } as Record<string, unknown>;
|
|
216
|
+
if (merged.class == null) {
|
|
217
|
+
merged.class = "solidaria-Input";
|
|
218
|
+
}
|
|
219
|
+
return merged;
|
|
125
220
|
}
|
|
126
|
-
return props;
|
|
221
|
+
return props.class == null ? { ...props, class: "solidaria-Input" } : props;
|
|
127
222
|
};
|
|
128
223
|
|
|
129
|
-
|
|
224
|
+
onMount(() => {
|
|
225
|
+
const element = inputElement;
|
|
226
|
+
if (!element) {
|
|
227
|
+
return;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
const inputHandler = (event: Event) => {
|
|
231
|
+
const handler = mergedProps().onInput as
|
|
232
|
+
| JSX.EventHandler<HTMLInputElement, InputEvent>
|
|
233
|
+
| undefined;
|
|
234
|
+
handler?.(
|
|
235
|
+
eventWithCurrentTarget(event, element) as InputEvent & {
|
|
236
|
+
currentTarget: HTMLInputElement;
|
|
237
|
+
target: Element;
|
|
238
|
+
},
|
|
239
|
+
);
|
|
240
|
+
clearDelegatedTextEntryHandlers(element);
|
|
241
|
+
event.stopPropagation();
|
|
242
|
+
};
|
|
243
|
+
const changeHandler = (event: Event) => {
|
|
244
|
+
const handler = mergedProps().onChange as
|
|
245
|
+
| JSX.EventHandler<HTMLInputElement, Event>
|
|
246
|
+
| undefined;
|
|
247
|
+
handler?.(
|
|
248
|
+
eventWithCurrentTarget(event, element) as Event & {
|
|
249
|
+
currentTarget: HTMLInputElement;
|
|
250
|
+
target: Element;
|
|
251
|
+
},
|
|
252
|
+
);
|
|
253
|
+
clearDelegatedTextEntryHandlers(element);
|
|
254
|
+
event.stopPropagation();
|
|
255
|
+
};
|
|
256
|
+
|
|
257
|
+
element.addEventListener("input", inputHandler);
|
|
258
|
+
element.addEventListener("change", changeHandler);
|
|
259
|
+
clearDelegatedTextEntryHandlers(element);
|
|
260
|
+
onCleanup(() => {
|
|
261
|
+
element.removeEventListener("input", inputHandler);
|
|
262
|
+
element.removeEventListener("change", changeHandler);
|
|
263
|
+
});
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
return (
|
|
267
|
+
<input
|
|
268
|
+
{...mergedProps()}
|
|
269
|
+
ref={(element) => {
|
|
270
|
+
inputElement = element;
|
|
271
|
+
const ref = props.ref;
|
|
272
|
+
if (typeof ref === "function") {
|
|
273
|
+
ref(element);
|
|
274
|
+
}
|
|
275
|
+
}}
|
|
276
|
+
/>
|
|
277
|
+
);
|
|
130
278
|
}
|
|
131
279
|
|
|
132
|
-
export interface TextAreaProps extends Omit<
|
|
280
|
+
export interface TextAreaProps extends Omit<
|
|
281
|
+
JSX.TextareaHTMLAttributes<HTMLTextAreaElement>,
|
|
282
|
+
"children"
|
|
283
|
+
> {}
|
|
133
284
|
|
|
134
285
|
/**
|
|
135
286
|
* A textarea element that automatically wires up to the parent TextField context.
|
|
@@ -138,23 +289,81 @@ export interface TextAreaProps extends Omit<JSX.TextareaHTMLAttributes<HTMLTextA
|
|
|
138
289
|
*/
|
|
139
290
|
export function TextArea(props: TextAreaProps): JSX.Element {
|
|
140
291
|
const context = useContext(TextFieldContext);
|
|
292
|
+
let textAreaElement: HTMLTextAreaElement | undefined;
|
|
141
293
|
|
|
142
294
|
// Merge context inputProps with local props (local props take precedence)
|
|
143
295
|
// Note: TextArea uses inputProps from context since it's an input variant
|
|
144
296
|
const mergedProps = () => {
|
|
145
297
|
if (context) {
|
|
146
|
-
const {
|
|
147
|
-
|
|
298
|
+
const {
|
|
299
|
+
ref: _ref,
|
|
300
|
+
type: _type,
|
|
301
|
+
...contextInputProps
|
|
302
|
+
} = (context.inputProps ?? {}) as Record<string, unknown>;
|
|
303
|
+
const merged = { ...contextInputProps, ...props } as Record<string, unknown>;
|
|
304
|
+
if (merged.class == null) {
|
|
305
|
+
merged.class = "solidaria-TextArea";
|
|
306
|
+
}
|
|
307
|
+
return merged;
|
|
148
308
|
}
|
|
149
|
-
return props;
|
|
309
|
+
return props.class == null ? { ...props, class: "solidaria-TextArea" } : props;
|
|
150
310
|
};
|
|
151
311
|
|
|
152
|
-
|
|
153
|
-
|
|
312
|
+
onMount(() => {
|
|
313
|
+
const element = textAreaElement;
|
|
314
|
+
if (!element) {
|
|
315
|
+
return;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
const inputHandler = (event: Event) => {
|
|
319
|
+
const handler = mergedProps().onInput as
|
|
320
|
+
| JSX.EventHandler<HTMLTextAreaElement, InputEvent>
|
|
321
|
+
| undefined;
|
|
322
|
+
handler?.(
|
|
323
|
+
eventWithCurrentTarget(event, element) as InputEvent & {
|
|
324
|
+
currentTarget: HTMLTextAreaElement;
|
|
325
|
+
target: Element;
|
|
326
|
+
},
|
|
327
|
+
);
|
|
328
|
+
clearDelegatedTextEntryHandlers(element);
|
|
329
|
+
event.stopPropagation();
|
|
330
|
+
};
|
|
331
|
+
const changeHandler = (event: Event) => {
|
|
332
|
+
const handler = mergedProps().onChange as
|
|
333
|
+
| JSX.EventHandler<HTMLTextAreaElement, Event>
|
|
334
|
+
| undefined;
|
|
335
|
+
handler?.(
|
|
336
|
+
eventWithCurrentTarget(event, element) as Event & {
|
|
337
|
+
currentTarget: HTMLTextAreaElement;
|
|
338
|
+
target: Element;
|
|
339
|
+
},
|
|
340
|
+
);
|
|
341
|
+
clearDelegatedTextEntryHandlers(element);
|
|
342
|
+
event.stopPropagation();
|
|
343
|
+
};
|
|
344
|
+
|
|
345
|
+
element.addEventListener("input", inputHandler);
|
|
346
|
+
element.addEventListener("change", changeHandler);
|
|
347
|
+
clearDelegatedTextEntryHandlers(element);
|
|
348
|
+
onCleanup(() => {
|
|
349
|
+
element.removeEventListener("input", inputHandler);
|
|
350
|
+
element.removeEventListener("change", changeHandler);
|
|
351
|
+
});
|
|
352
|
+
});
|
|
154
353
|
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
354
|
+
return (
|
|
355
|
+
<textarea
|
|
356
|
+
{...mergedProps()}
|
|
357
|
+
ref={(element) => {
|
|
358
|
+
textAreaElement = element;
|
|
359
|
+
const ref = props.ref;
|
|
360
|
+
if (typeof ref === "function") {
|
|
361
|
+
ref(element);
|
|
362
|
+
}
|
|
363
|
+
}}
|
|
364
|
+
/>
|
|
365
|
+
);
|
|
366
|
+
}
|
|
158
367
|
|
|
159
368
|
/**
|
|
160
369
|
* A text field allows a user to enter a plain text value with a keyboard.
|
|
@@ -175,40 +384,72 @@ export function TextArea(props: TextAreaProps): JSX.Element {
|
|
|
175
384
|
* ```
|
|
176
385
|
*/
|
|
177
386
|
export function TextField(props: TextFieldProps): JSX.Element {
|
|
178
|
-
|
|
179
|
-
const
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
387
|
+
const formContext = useContext(FormContext);
|
|
388
|
+
const contextProps = useContext(TextFieldContext);
|
|
389
|
+
const contextSlotProps = contextProps?.slots?.[props.slot ?? "default"];
|
|
390
|
+
const contextBaseProps = createMemo<TextFieldProps>(() => {
|
|
391
|
+
if (!contextProps) return {};
|
|
392
|
+
const {
|
|
393
|
+
labelProps: _labelProps,
|
|
394
|
+
inputProps: _inputProps,
|
|
395
|
+
descriptionProps: _descriptionProps,
|
|
396
|
+
errorMessageProps: _errorMessageProps,
|
|
397
|
+
isInvalid: _isInvalid,
|
|
398
|
+
slots: _slots,
|
|
399
|
+
...rest
|
|
400
|
+
} = contextProps;
|
|
401
|
+
return rest as TextFieldProps;
|
|
402
|
+
});
|
|
403
|
+
const baseProps = (
|
|
404
|
+
contextProps ? mergeProps(contextBaseProps(), contextSlotProps ?? {}, props) : props
|
|
405
|
+
) as TextFieldProps;
|
|
406
|
+
const mergedProps = withFormValidationBehavior(baseProps, formContext);
|
|
407
|
+
|
|
408
|
+
const [local, ariaProps] = splitProps(mergedProps, [
|
|
409
|
+
"children",
|
|
410
|
+
"class",
|
|
411
|
+
"style",
|
|
412
|
+
"render",
|
|
413
|
+
"slot",
|
|
184
414
|
]);
|
|
185
415
|
|
|
186
|
-
// Create text field state
|
|
187
416
|
// Use getters to ensure props are read lazily inside reactive contexts
|
|
188
417
|
const state = createTextFieldState({
|
|
189
|
-
get value() {
|
|
190
|
-
|
|
191
|
-
|
|
418
|
+
get value() {
|
|
419
|
+
return ariaProps.value;
|
|
420
|
+
},
|
|
421
|
+
get defaultValue() {
|
|
422
|
+
return ariaProps.defaultValue;
|
|
423
|
+
},
|
|
424
|
+
get onChange() {
|
|
425
|
+
return ariaProps.onChange;
|
|
426
|
+
},
|
|
427
|
+
});
|
|
428
|
+
|
|
429
|
+
const inputAriaProps = createMemo(() => {
|
|
430
|
+
const clean: Record<string, unknown> = {};
|
|
431
|
+
for (const key in ariaProps as Record<string, unknown>) {
|
|
432
|
+
if (!key.startsWith("data-")) {
|
|
433
|
+
clean[key] = (ariaProps as Record<string, unknown>)[key];
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
return clean as typeof ariaProps;
|
|
192
437
|
});
|
|
193
438
|
|
|
194
|
-
// Create text field aria props
|
|
195
439
|
const textFieldAria = createTextField(() => ({
|
|
196
|
-
...
|
|
440
|
+
...inputAriaProps(),
|
|
197
441
|
value: state.value(),
|
|
198
442
|
onChange: state.setValue,
|
|
199
443
|
}));
|
|
200
444
|
|
|
201
|
-
// Create focus ring
|
|
202
445
|
const { isFocused, isFocusVisible, focusProps } = createFocusRing();
|
|
203
446
|
|
|
204
|
-
// Create hover
|
|
205
447
|
const { isHovered, hoverProps } = createHover({
|
|
206
448
|
get isDisabled() {
|
|
207
449
|
return ariaProps.isDisabled;
|
|
208
450
|
},
|
|
209
451
|
});
|
|
210
452
|
|
|
211
|
-
// Render props values
|
|
212
453
|
const renderValues = createMemo<TextFieldRenderProps>(() => ({
|
|
213
454
|
isDisabled: ariaProps.isDisabled || false,
|
|
214
455
|
isInvalid: textFieldAria.isInvalid,
|
|
@@ -219,25 +460,45 @@ export function TextField(props: TextFieldProps): JSX.Element {
|
|
|
219
460
|
isFocusVisible: isFocusVisible(),
|
|
220
461
|
}));
|
|
221
462
|
|
|
222
|
-
// Resolve render props
|
|
223
463
|
const renderProps = useRenderProps(
|
|
224
464
|
{
|
|
225
|
-
children:
|
|
465
|
+
children: local.children,
|
|
226
466
|
class: local.class,
|
|
227
467
|
style: local.style,
|
|
228
|
-
defaultClassName:
|
|
468
|
+
defaultClassName: "solidaria-TextField",
|
|
229
469
|
},
|
|
230
|
-
renderValues
|
|
470
|
+
renderValues,
|
|
231
471
|
);
|
|
472
|
+
const childRenderValues: TextFieldRenderProps = {
|
|
473
|
+
get isDisabled() {
|
|
474
|
+
return ariaProps.isDisabled || false;
|
|
475
|
+
},
|
|
476
|
+
get isInvalid() {
|
|
477
|
+
return textFieldAria.isInvalid;
|
|
478
|
+
},
|
|
479
|
+
get isReadOnly() {
|
|
480
|
+
return ariaProps.isReadOnly || false;
|
|
481
|
+
},
|
|
482
|
+
get isRequired() {
|
|
483
|
+
return ariaProps.isRequired || false;
|
|
484
|
+
},
|
|
485
|
+
get isHovered() {
|
|
486
|
+
return isHovered();
|
|
487
|
+
},
|
|
488
|
+
get isFocused() {
|
|
489
|
+
return isFocused();
|
|
490
|
+
},
|
|
491
|
+
get isFocusVisible() {
|
|
492
|
+
return isFocusVisible();
|
|
493
|
+
},
|
|
494
|
+
};
|
|
232
495
|
|
|
233
|
-
// Filter DOM props
|
|
234
496
|
const domProps = createMemo(() => {
|
|
235
497
|
const filtered = filterDOMProps(ariaProps, { global: true });
|
|
236
498
|
delete (filtered as Record<string, unknown>).id;
|
|
237
499
|
return filtered;
|
|
238
500
|
});
|
|
239
501
|
|
|
240
|
-
// Remove ref from spread props to avoid type conflicts
|
|
241
502
|
const cleanHoverProps = () => {
|
|
242
503
|
const { ref: _ref, ...rest } = hoverProps as Record<string, unknown>;
|
|
243
504
|
return rest;
|
|
@@ -245,6 +506,27 @@ export function TextField(props: TextFieldProps): JSX.Element {
|
|
|
245
506
|
|
|
246
507
|
// Context value for sub-components.
|
|
247
508
|
// Use property getters so sub-components always read the latest aria/focus state.
|
|
509
|
+
const fieldValidation = createMemo<ValidationResult>(() => {
|
|
510
|
+
const isInvalid = textFieldAria.isInvalid;
|
|
511
|
+
const errorMessage = ariaProps.errorMessage;
|
|
512
|
+
const validationErrors = isInvalid && typeof errorMessage === "string" ? [errorMessage] : [];
|
|
513
|
+
|
|
514
|
+
return {
|
|
515
|
+
isInvalid,
|
|
516
|
+
validationErrors,
|
|
517
|
+
validationDetails: isInvalid
|
|
518
|
+
? { ...VALID_VALIDITY_STATE, customError: true, valid: false }
|
|
519
|
+
: VALID_VALIDITY_STATE,
|
|
520
|
+
};
|
|
521
|
+
});
|
|
522
|
+
const fieldErrorContext: FieldErrorContextValue = {
|
|
523
|
+
get validation() {
|
|
524
|
+
return fieldValidation();
|
|
525
|
+
},
|
|
526
|
+
get errorMessageProps() {
|
|
527
|
+
return textFieldAria.errorMessageProps as JSX.HTMLAttributes<HTMLElement>;
|
|
528
|
+
},
|
|
529
|
+
};
|
|
248
530
|
const contextValue: TextFieldContextValue = {
|
|
249
531
|
get labelProps() {
|
|
250
532
|
return textFieldAria.labelProps as JSX.LabelHTMLAttributes<HTMLLabelElement>;
|
|
@@ -252,7 +534,7 @@ export function TextField(props: TextFieldProps): JSX.Element {
|
|
|
252
534
|
get inputProps() {
|
|
253
535
|
return mergeProps(
|
|
254
536
|
textFieldAria.inputProps as Record<string, unknown>,
|
|
255
|
-
focusProps as Record<string, unknown
|
|
537
|
+
focusProps as Record<string, unknown>,
|
|
256
538
|
) as JSX.InputHTMLAttributes<HTMLInputElement>;
|
|
257
539
|
},
|
|
258
540
|
get descriptionProps() {
|
|
@@ -264,25 +546,53 @@ export function TextField(props: TextFieldProps): JSX.Element {
|
|
|
264
546
|
get isInvalid() {
|
|
265
547
|
return textFieldAria.isInvalid;
|
|
266
548
|
},
|
|
549
|
+
// Deterministic at render (createTextField generates it via createUniqueId),
|
|
550
|
+
// so the Label's `for` is stable across SSR + hydration. A signal set from an
|
|
551
|
+
// onMount effect would flip undefined->id post-mount and re-execute the Label
|
|
552
|
+
// template — crashing hydration if the re-run lands mid-hydration (dev).
|
|
553
|
+
get inputId() {
|
|
554
|
+
return (textFieldAria.inputProps as { id?: string }).id;
|
|
555
|
+
},
|
|
267
556
|
};
|
|
557
|
+
// Resolve the render-prop children ONCE (untracked). Re-invoking it on a
|
|
558
|
+
// reactive update re-clones its templates; if that lands mid-hydration it
|
|
559
|
+
// throws a Hydration Mismatch (worst in dev, where slow unbundled modules widen
|
|
560
|
+
// the hydration window). The children carry their own fine-grained reactivity
|
|
561
|
+
// (render-value getters + <Show>s), so they update without being re-created.
|
|
562
|
+
const fieldChildren = untrack(() => {
|
|
563
|
+
const children = local.children;
|
|
564
|
+
return typeof children === "function" ? children(childRenderValues) : children;
|
|
565
|
+
});
|
|
566
|
+
const rootProps = () =>
|
|
567
|
+
({
|
|
568
|
+
...domProps(),
|
|
569
|
+
...cleanHoverProps(),
|
|
570
|
+
class: renderProps.class(),
|
|
571
|
+
style: renderProps.style(),
|
|
572
|
+
slot: local.slot,
|
|
573
|
+
"data-disabled": ariaProps.isDisabled || undefined,
|
|
574
|
+
"data-invalid": textFieldAria.isInvalid || undefined,
|
|
575
|
+
"data-readonly": ariaProps.isReadOnly || undefined,
|
|
576
|
+
"data-required": ariaProps.isRequired || undefined,
|
|
577
|
+
"data-hovered": isHovered() || undefined,
|
|
578
|
+
"data-focused": isFocused() || undefined,
|
|
579
|
+
"data-focus-visible": isFocusVisible() || undefined,
|
|
580
|
+
}) as JSX.HTMLAttributes<HTMLDivElement>;
|
|
581
|
+
const customRootProps = () =>
|
|
582
|
+
({
|
|
583
|
+
...rootProps(),
|
|
584
|
+
children: fieldChildren,
|
|
585
|
+
}) as JSX.HTMLAttributes<HTMLDivElement>;
|
|
268
586
|
|
|
269
587
|
return (
|
|
270
|
-
<
|
|
271
|
-
<
|
|
272
|
-
{
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
data-required={ariaProps.isRequired || undefined}
|
|
280
|
-
data-hovered={isHovered() || undefined}
|
|
281
|
-
data-focused={isFocused() || undefined}
|
|
282
|
-
data-focus-visible={isFocusVisible() || undefined}
|
|
283
|
-
>
|
|
284
|
-
{renderProps.renderChildren()}
|
|
285
|
-
</div>
|
|
286
|
-
</TextFieldContext.Provider>
|
|
588
|
+
<FieldErrorContext.Provider value={fieldErrorContext}>
|
|
589
|
+
<TextFieldContext.Provider value={contextValue}>
|
|
590
|
+
{local.render ? (
|
|
591
|
+
local.render(customRootProps(), renderValues())
|
|
592
|
+
) : (
|
|
593
|
+
<div {...rootProps()}>{fieldChildren}</div>
|
|
594
|
+
)}
|
|
595
|
+
</TextFieldContext.Provider>
|
|
596
|
+
</FieldErrorContext.Provider>
|
|
287
597
|
);
|
|
288
598
|
}
|