@proyecto-viviana/solidaria-components 0.2.5 → 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/LICENSE +21 -0
- package/README.md +39 -272
- package/dist/ActionBar.d.ts +79 -0
- package/dist/ActionBar.d.ts.map +1 -0
- package/dist/ActionGroup.d.ts +74 -0
- package/dist/ActionGroup.d.ts.map +1 -0
- package/dist/Alert.d.ts +70 -0
- package/dist/Alert.d.ts.map +1 -0
- package/dist/Autocomplete.d.ts +5 -5
- package/dist/Autocomplete.d.ts.map +1 -1
- package/dist/Breadcrumbs.d.ts +27 -8
- package/dist/Breadcrumbs.d.ts.map +1 -1
- package/dist/Button.d.ts +28 -5
- package/dist/Button.d.ts.map +1 -1
- package/dist/Calendar.d.ts +51 -7
- package/dist/Calendar.d.ts.map +1 -1
- package/dist/Checkbox.d.ts +33 -8
- package/dist/Checkbox.d.ts.map +1 -1
- package/dist/Collection.d.ts +130 -0
- package/dist/Collection.d.ts.map +1 -0
- package/dist/Color.d.ts +210 -9
- package/dist/Color.d.ts.map +1 -1
- package/dist/ColorEditor.d.ts +42 -0
- package/dist/ColorEditor.d.ts.map +1 -0
- package/dist/ComboBox.d.ts +146 -16
- package/dist/ComboBox.d.ts.map +1 -1
- package/dist/ContextualHelpTrigger.d.ts +40 -0
- package/dist/ContextualHelpTrigger.d.ts.map +1 -0
- package/dist/DateField.d.ts +35 -8
- package/dist/DateField.d.ts.map +1 -1
- package/dist/DatePicker.d.ts +101 -5
- 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 +25 -5
- package/dist/Disclosure.d.ts.map +1 -1
- package/dist/DragAndDrop.d.ts +80 -0
- package/dist/DragAndDrop.d.ts.map +1 -0
- package/dist/DragPreview.d.ts +14 -0
- package/dist/DragPreview.d.ts.map +1 -0
- package/dist/DropZone.d.ts +27 -0
- package/dist/DropZone.d.ts.map +1 -0
- package/dist/FieldError.d.ts +27 -0
- package/dist/FieldError.d.ts.map +1 -0
- package/dist/FileTrigger.d.ts +26 -0
- package/dist/FileTrigger.d.ts.map +1 -0
- package/dist/Focusable.d.ts +27 -0
- package/dist/Focusable.d.ts.map +1 -0
- package/dist/Form.d.ts +41 -0
- package/dist/Form.d.ts.map +1 -0
- package/dist/GridList.d.ts +69 -10
- 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 +57 -0
- package/dist/Icon.d.ts.map +1 -0
- package/dist/Keyboard.d.ts +13 -0
- package/dist/Keyboard.d.ts.map +1 -0
- 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 +73 -11
- package/dist/ListBox.d.ts.map +1 -1
- package/dist/ListDropTargetDelegate.d.ts +38 -0
- package/dist/ListDropTargetDelegate.d.ts.map +1 -0
- package/dist/Menu.d.ts +79 -10
- package/dist/Menu.d.ts.map +1 -1
- package/dist/Meter.d.ts +4 -4
- package/dist/Meter.d.ts.map +1 -1
- package/dist/Modal.d.ts +6 -4
- package/dist/Modal.d.ts.map +1 -1
- package/dist/NumberField.d.ts +10 -12
- package/dist/NumberField.d.ts.map +1 -1
- package/dist/Popover.d.ts +32 -7
- package/dist/Popover.d.ts.map +1 -1
- package/dist/Pressable.d.ts +27 -0
- package/dist/Pressable.d.ts.map +1 -0
- package/dist/ProgressBar.d.ts +6 -4
- 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 +39 -7
- package/dist/RangeCalendar.d.ts.map +1 -1
- package/dist/RouterProvider.d.ts +75 -0
- package/dist/RouterProvider.d.ts.map +1 -0
- package/dist/SearchField.d.ts +23 -21
- package/dist/SearchField.d.ts.map +1 -1
- package/dist/Select.d.ts +48 -7
- package/dist/Select.d.ts.map +1 -1
- package/dist/SelectionIndicator.d.ts +30 -0
- package/dist/SelectionIndicator.d.ts.map +1 -0
- package/dist/Separator.d.ts +9 -3
- package/dist/Separator.d.ts.map +1 -1
- package/dist/SharedElementTransition.d.ts +41 -0
- package/dist/SharedElementTransition.d.ts.map +1 -0
- package/dist/Slider.d.ts +15 -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 +222 -19
- package/dist/Table.d.ts.map +1 -1
- package/dist/Tabs.d.ts +47 -10
- package/dist/Tabs.d.ts.map +1 -1
- package/dist/TagGroup.d.ts +22 -10
- package/dist/TagGroup.d.ts.map +1 -1
- package/dist/Text.d.ts +10 -0
- package/dist/Text.d.ts.map +1 -0
- package/dist/TextField.d.ts +19 -11
- package/dist/TextField.d.ts.map +1 -1
- package/dist/TimeField.d.ts +32 -7
- 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 +36 -0
- package/dist/ToggleButton.d.ts.map +1 -0
- package/dist/ToggleButtonGroup.d.ts +33 -0
- package/dist/ToggleButtonGroup.d.ts.map +1 -0
- package/dist/Toolbar.d.ts +7 -3
- package/dist/Toolbar.d.ts.map +1 -1
- package/dist/Tooltip.d.ts +58 -7
- package/dist/Tooltip.d.ts.map +1 -1
- package/dist/Tree.d.ts +102 -11
- package/dist/Tree.d.ts.map +1 -1
- package/dist/Virtualizer.d.ts +61 -0
- package/dist/Virtualizer.d.ts.map +1 -0
- package/dist/VirtualizerLayouts.d.ts +82 -0
- package/dist/VirtualizerLayouts.d.ts.map +1 -0
- package/dist/VisuallyHidden.d.ts +4 -2
- package/dist/VisuallyHidden.d.ts.map +1 -1
- package/dist/contexts.d.ts +6 -1
- package/dist/contexts.d.ts.map +1 -1
- package/dist/index.d.ts +73 -39
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +23342 -10644
- package/dist/index.js.map +1 -7
- package/dist/index.jsx +18110 -0
- package/dist/index.jsx.map +1 -0
- package/dist/useDragAndDrop.d.ts +93 -0
- package/dist/useDragAndDrop.d.ts.map +1 -0
- package/dist/utils.d.ts +8 -2
- package/dist/utils.d.ts.map +1 -1
- package/dist/virtualizer/Layout.d.ts +79 -0
- package/dist/virtualizer/Layout.d.ts.map +1 -0
- package/package.json +33 -32
- package/src/ActionBar.tsx +251 -0
- package/src/ActionGroup.tsx +277 -0
- package/src/Alert.tsx +152 -0
- package/src/Autocomplete.tsx +39 -44
- package/src/Breadcrumbs.tsx +227 -72
- package/src/Button.tsx +315 -74
- package/src/Calendar.tsx +347 -141
- package/src/Checkbox.tsx +414 -123
- package/src/Collection.tsx +350 -0
- package/src/Color.tsx +1325 -284
- package/src/ColorEditor.tsx +213 -0
- package/src/ComboBox.tsx +644 -245
- package/src/ContextualHelpTrigger.tsx +195 -0
- package/src/DateField.tsx +274 -106
- package/src/DatePicker.tsx +892 -111
- package/src/DateRangePickerContext.tsx +44 -0
- package/src/Dialog.tsx +173 -104
- package/src/Disclosure.tsx +158 -105
- package/src/DragAndDrop.tsx +340 -0
- package/src/DragPreview.tsx +47 -0
- package/src/DropZone.tsx +233 -0
- package/src/FieldError.tsx +89 -0
- package/src/FileTrigger.tsx +83 -0
- package/src/Focusable.tsx +103 -0
- package/src/Form.tsx +140 -0
- package/src/GridList.tsx +542 -128
- package/src/HiddenDateInput.tsx +153 -0
- package/src/HiddenTimeInput.tsx +133 -0
- package/src/Icon.tsx +133 -0
- package/src/Keyboard.tsx +26 -0
- package/src/Landmark.tsx +37 -63
- package/src/Link.tsx +132 -69
- package/src/ListBox.tsx +656 -106
- package/src/ListDropTargetDelegate.ts +283 -0
- package/src/Menu.tsx +1234 -132
- package/src/Meter.tsx +44 -58
- package/src/Modal.tsx +262 -166
- package/src/NumberField.tsx +267 -151
- package/src/Popover.tsx +452 -343
- package/src/Pressable.tsx +108 -0
- package/src/ProgressBar.tsx +54 -59
- package/src/RadioGroup.tsx +533 -121
- package/src/RangeCalendar.tsx +249 -150
- package/src/RouterProvider.tsx +223 -0
- package/src/SearchField.tsx +460 -133
- package/src/Select.tsx +804 -233
- package/src/SelectionIndicator.tsx +108 -0
- package/src/Separator.tsx +47 -49
- package/src/SharedElementTransition.tsx +264 -0
- package/src/Slider.tsx +148 -98
- package/src/StepList.tsx +272 -0
- package/src/Switch.tsx +93 -46
- package/src/Table.tsx +1551 -225
- package/src/Tabs.tsx +377 -123
- package/src/TagGroup.tsx +233 -135
- package/src/Text.tsx +18 -0
- package/src/TextField.tsx +413 -86
- package/src/TimeField.tsx +232 -222
- package/src/Toast.tsx +306 -160
- package/src/ToggleButton.tsx +169 -0
- package/src/ToggleButtonGroup.tsx +141 -0
- package/src/Toolbar.tsx +61 -70
- package/src/Tooltip.tsx +473 -116
- package/src/Tree.tsx +1514 -175
- package/src/Virtualizer.tsx +730 -0
- package/src/VirtualizerLayouts.ts +280 -0
- package/src/VisuallyHidden.tsx +32 -38
- package/src/contexts.ts +29 -36
- package/src/index.ts +972 -620
- package/src/useDragAndDrop.ts +367 -0
- package/src/utils.tsx +69 -50
- package/src/virtualizer/Layout.ts +192 -0
- package/dist/index.ssr.js +0 -9785
- package/dist/index.ssr.js.map +0 -7
package/src/TextField.tsx
CHANGED
|
@@ -10,15 +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,
|
|
24
|
+
mergeProps,
|
|
19
25
|
type AriaTextFieldProps,
|
|
20
|
-
} from
|
|
21
|
-
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";
|
|
22
34
|
import {
|
|
23
35
|
type RenderChildren,
|
|
24
36
|
type ClassNameOrFunction,
|
|
@@ -26,11 +38,7 @@ import {
|
|
|
26
38
|
type SlotProps,
|
|
27
39
|
useRenderProps,
|
|
28
40
|
filterDOMProps,
|
|
29
|
-
} from
|
|
30
|
-
|
|
31
|
-
// ============================================
|
|
32
|
-
// TYPES
|
|
33
|
-
// ============================================
|
|
41
|
+
} from "./utils";
|
|
34
42
|
|
|
35
43
|
export interface TextFieldRenderProps {
|
|
36
44
|
/** Whether the text field is disabled. */
|
|
@@ -49,34 +57,85 @@ export interface TextFieldRenderProps {
|
|
|
49
57
|
isFocusVisible: boolean;
|
|
50
58
|
}
|
|
51
59
|
|
|
52
|
-
export interface TextFieldProps
|
|
53
|
-
extends Omit<AriaTextFieldProps, 'children'>,
|
|
54
|
-
SlotProps {
|
|
60
|
+
export interface TextFieldProps extends Omit<AriaTextFieldProps, "children">, SlotProps {
|
|
55
61
|
/** The children of the component. A function may be provided to receive render props. */
|
|
56
62
|
children?: RenderChildren<TextFieldRenderProps>;
|
|
57
63
|
/** The CSS className for the element. */
|
|
58
64
|
class?: ClassNameOrFunction<TextFieldRenderProps>;
|
|
59
65
|
/** The inline style for the element. */
|
|
60
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;
|
|
61
72
|
}
|
|
62
73
|
|
|
63
|
-
// ============================================
|
|
64
|
-
// CONTEXT
|
|
65
|
-
// ============================================
|
|
66
|
-
|
|
67
74
|
export interface TextFieldContextValue {
|
|
68
|
-
labelProps
|
|
69
|
-
inputProps
|
|
70
|
-
descriptionProps
|
|
71
|
-
errorMessageProps
|
|
72
|
-
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;
|
|
73
82
|
}
|
|
74
83
|
|
|
75
84
|
export const TextFieldContext = createContext<TextFieldContextValue | null>(null);
|
|
85
|
+
export const LabelContext = TextFieldContext;
|
|
86
|
+
export const InputContext = TextFieldContext;
|
|
87
|
+
export const TextAreaContext = TextFieldContext;
|
|
88
|
+
export const FieldInputContext = TextFieldContext;
|
|
89
|
+
|
|
90
|
+
function withFormValidationBehavior(
|
|
91
|
+
props: TextFieldProps,
|
|
92
|
+
formContext: FormProps | null,
|
|
93
|
+
): TextFieldProps {
|
|
94
|
+
if (!formContext?.validationBehavior) {
|
|
95
|
+
return props;
|
|
96
|
+
}
|
|
76
97
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
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
|
+
}
|
|
80
139
|
|
|
81
140
|
export interface LabelProps extends JSX.LabelHTMLAttributes<HTMLLabelElement> {
|
|
82
141
|
children?: JSX.Element;
|
|
@@ -92,16 +151,49 @@ export function Label(props: LabelProps): JSX.Element {
|
|
|
92
151
|
// Merge context labelProps with local props (local props take precedence)
|
|
93
152
|
const mergedProps = () => {
|
|
94
153
|
if (context) {
|
|
95
|
-
const { ref: _ref, ...contextLabelProps } = context.labelProps as Record<
|
|
96
|
-
|
|
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;
|
|
97
167
|
}
|
|
98
|
-
return props;
|
|
168
|
+
return props.class == null ? { ...props, class: "solidaria-Label" } : props;
|
|
99
169
|
};
|
|
100
170
|
|
|
101
171
|
return <label {...mergedProps()}>{props.children}</label>;
|
|
102
172
|
}
|
|
103
173
|
|
|
104
|
-
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
|
+
}
|
|
105
197
|
|
|
106
198
|
/**
|
|
107
199
|
* An input element that automatically wires up to the parent TextField context.
|
|
@@ -110,21 +202,85 @@ export interface InputProps extends Omit<JSX.InputHTMLAttributes<HTMLInputElemen
|
|
|
110
202
|
*/
|
|
111
203
|
export function Input(props: InputProps): JSX.Element {
|
|
112
204
|
const context = useContext(TextFieldContext);
|
|
205
|
+
let inputElement: HTMLInputElement | undefined;
|
|
113
206
|
|
|
114
207
|
// Merge context inputProps with local props (local props take precedence)
|
|
115
208
|
const mergedProps = () => {
|
|
116
209
|
if (context) {
|
|
117
210
|
// Remove ref from context props to avoid conflicts
|
|
118
|
-
const { ref: _ref, ...contextInputProps } = context.inputProps as Record<
|
|
119
|
-
|
|
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;
|
|
120
220
|
}
|
|
121
|
-
return props;
|
|
221
|
+
return props.class == null ? { ...props, class: "solidaria-Input" } : props;
|
|
122
222
|
};
|
|
123
223
|
|
|
124
|
-
|
|
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
|
+
);
|
|
125
278
|
}
|
|
126
279
|
|
|
127
|
-
export interface TextAreaProps extends Omit<
|
|
280
|
+
export interface TextAreaProps extends Omit<
|
|
281
|
+
JSX.TextareaHTMLAttributes<HTMLTextAreaElement>,
|
|
282
|
+
"children"
|
|
283
|
+
> {}
|
|
128
284
|
|
|
129
285
|
/**
|
|
130
286
|
* A textarea element that automatically wires up to the parent TextField context.
|
|
@@ -133,23 +289,81 @@ export interface TextAreaProps extends Omit<JSX.TextareaHTMLAttributes<HTMLTextA
|
|
|
133
289
|
*/
|
|
134
290
|
export function TextArea(props: TextAreaProps): JSX.Element {
|
|
135
291
|
const context = useContext(TextFieldContext);
|
|
292
|
+
let textAreaElement: HTMLTextAreaElement | undefined;
|
|
136
293
|
|
|
137
294
|
// Merge context inputProps with local props (local props take precedence)
|
|
138
295
|
// Note: TextArea uses inputProps from context since it's an input variant
|
|
139
296
|
const mergedProps = () => {
|
|
140
297
|
if (context) {
|
|
141
|
-
const {
|
|
142
|
-
|
|
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;
|
|
143
308
|
}
|
|
144
|
-
return props;
|
|
309
|
+
return props.class == null ? { ...props, class: "solidaria-TextArea" } : props;
|
|
145
310
|
};
|
|
146
311
|
|
|
147
|
-
|
|
148
|
-
|
|
312
|
+
onMount(() => {
|
|
313
|
+
const element = textAreaElement;
|
|
314
|
+
if (!element) {
|
|
315
|
+
return;
|
|
316
|
+
}
|
|
149
317
|
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
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
|
+
});
|
|
353
|
+
|
|
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
|
+
}
|
|
153
367
|
|
|
154
368
|
/**
|
|
155
369
|
* A text field allows a user to enter a plain text value with a keyboard.
|
|
@@ -170,40 +384,72 @@ export function TextArea(props: TextAreaProps): JSX.Element {
|
|
|
170
384
|
* ```
|
|
171
385
|
*/
|
|
172
386
|
export function TextField(props: TextFieldProps): JSX.Element {
|
|
173
|
-
|
|
174
|
-
const
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
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",
|
|
179
414
|
]);
|
|
180
415
|
|
|
181
|
-
// Create text field state
|
|
182
416
|
// Use getters to ensure props are read lazily inside reactive contexts
|
|
183
417
|
const state = createTextFieldState({
|
|
184
|
-
get value() {
|
|
185
|
-
|
|
186
|
-
|
|
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;
|
|
187
437
|
});
|
|
188
438
|
|
|
189
|
-
// Create text field aria props
|
|
190
439
|
const textFieldAria = createTextField(() => ({
|
|
191
|
-
...
|
|
440
|
+
...inputAriaProps(),
|
|
192
441
|
value: state.value(),
|
|
193
442
|
onChange: state.setValue,
|
|
194
443
|
}));
|
|
195
444
|
|
|
196
|
-
// Create focus ring
|
|
197
445
|
const { isFocused, isFocusVisible, focusProps } = createFocusRing();
|
|
198
446
|
|
|
199
|
-
// Create hover
|
|
200
447
|
const { isHovered, hoverProps } = createHover({
|
|
201
448
|
get isDisabled() {
|
|
202
449
|
return ariaProps.isDisabled;
|
|
203
450
|
},
|
|
204
451
|
});
|
|
205
452
|
|
|
206
|
-
// Render props values
|
|
207
453
|
const renderValues = createMemo<TextFieldRenderProps>(() => ({
|
|
208
454
|
isDisabled: ariaProps.isDisabled || false,
|
|
209
455
|
isInvalid: textFieldAria.isInvalid,
|
|
@@ -214,58 +460,139 @@ export function TextField(props: TextFieldProps): JSX.Element {
|
|
|
214
460
|
isFocusVisible: isFocusVisible(),
|
|
215
461
|
}));
|
|
216
462
|
|
|
217
|
-
// Resolve render props
|
|
218
463
|
const renderProps = useRenderProps(
|
|
219
464
|
{
|
|
220
|
-
children:
|
|
465
|
+
children: local.children,
|
|
221
466
|
class: local.class,
|
|
222
467
|
style: local.style,
|
|
223
|
-
defaultClassName:
|
|
468
|
+
defaultClassName: "solidaria-TextField",
|
|
224
469
|
},
|
|
225
|
-
renderValues
|
|
470
|
+
renderValues,
|
|
226
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
|
+
};
|
|
227
495
|
|
|
228
|
-
// Filter DOM props
|
|
229
496
|
const domProps = createMemo(() => {
|
|
230
497
|
const filtered = filterDOMProps(ariaProps, { global: true });
|
|
231
498
|
delete (filtered as Record<string, unknown>).id;
|
|
232
499
|
return filtered;
|
|
233
500
|
});
|
|
234
501
|
|
|
235
|
-
// Remove ref from spread props to avoid type conflicts
|
|
236
502
|
const cleanHoverProps = () => {
|
|
237
503
|
const { ref: _ref, ...rest } = hoverProps as Record<string, unknown>;
|
|
238
504
|
return rest;
|
|
239
505
|
};
|
|
240
506
|
|
|
241
|
-
// Context value for sub-components
|
|
242
|
-
//
|
|
243
|
-
|
|
507
|
+
// Context value for sub-components.
|
|
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
|
+
};
|
|
244
530
|
const contextValue: TextFieldContextValue = {
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
531
|
+
get labelProps() {
|
|
532
|
+
return textFieldAria.labelProps as JSX.LabelHTMLAttributes<HTMLLabelElement>;
|
|
533
|
+
},
|
|
534
|
+
get inputProps() {
|
|
535
|
+
return mergeProps(
|
|
536
|
+
textFieldAria.inputProps as Record<string, unknown>,
|
|
537
|
+
focusProps as Record<string, unknown>,
|
|
538
|
+
) as JSX.InputHTMLAttributes<HTMLInputElement>;
|
|
539
|
+
},
|
|
540
|
+
get descriptionProps() {
|
|
541
|
+
return textFieldAria.descriptionProps as JSX.HTMLAttributes<HTMLElement>;
|
|
542
|
+
},
|
|
543
|
+
get errorMessageProps() {
|
|
544
|
+
return textFieldAria.errorMessageProps as JSX.HTMLAttributes<HTMLElement>;
|
|
545
|
+
},
|
|
546
|
+
get isInvalid() {
|
|
547
|
+
return textFieldAria.isInvalid;
|
|
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
|
+
},
|
|
250
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>;
|
|
251
586
|
|
|
252
587
|
return (
|
|
253
|
-
<
|
|
254
|
-
<
|
|
255
|
-
{
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
data-required={ariaProps.isRequired || undefined}
|
|
263
|
-
data-hovered={isHovered() || undefined}
|
|
264
|
-
data-focused={isFocused() || undefined}
|
|
265
|
-
data-focus-visible={isFocusVisible() || undefined}
|
|
266
|
-
>
|
|
267
|
-
{renderProps.renderChildren()}
|
|
268
|
-
</div>
|
|
269
|
-
</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>
|
|
270
597
|
);
|
|
271
598
|
}
|