@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/StepList.tsx
ADDED
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* StepList component for solidaria-components
|
|
3
|
+
*
|
|
4
|
+
* A pre-wired headless step list component that combines state + aria hooks.
|
|
5
|
+
* Renders an ordered list of steps with completion tracking.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { type JSX, createContext, createMemo, splitProps, useContext, For } from "solid-js";
|
|
9
|
+
import {
|
|
10
|
+
createStepListState,
|
|
11
|
+
type StepListState,
|
|
12
|
+
type StepListStateProps,
|
|
13
|
+
type Key,
|
|
14
|
+
} from "@proyecto-viviana/solid-stately";
|
|
15
|
+
import { createStepList, type AriaStepListProps } from "@proyecto-viviana/solidaria";
|
|
16
|
+
import {
|
|
17
|
+
type ClassNameOrFunction,
|
|
18
|
+
type StyleOrFunction,
|
|
19
|
+
useRenderProps,
|
|
20
|
+
filterDOMProps,
|
|
21
|
+
} from "./utils";
|
|
22
|
+
|
|
23
|
+
export interface StepListItemRenderProps {
|
|
24
|
+
/** Whether this step is currently selected. */
|
|
25
|
+
isSelected: boolean;
|
|
26
|
+
/** Whether this step is completed. */
|
|
27
|
+
isCompleted: boolean;
|
|
28
|
+
/** Whether this step is disabled. */
|
|
29
|
+
isDisabled: boolean;
|
|
30
|
+
/** Whether this step can be selected. */
|
|
31
|
+
isSelectable: boolean;
|
|
32
|
+
/** The 1-based step number. */
|
|
33
|
+
stepNumber: number;
|
|
34
|
+
/** Accessible text describing the step state. */
|
|
35
|
+
stepStateText: string;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export interface StepListRenderProps {
|
|
39
|
+
/** Whether the step list is disabled. */
|
|
40
|
+
isDisabled: boolean;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export interface StepListProps<T extends { key: Key; label: string }> extends AriaStepListProps {
|
|
44
|
+
/** The step items. */
|
|
45
|
+
items: T[];
|
|
46
|
+
/** The currently selected step key (controlled). */
|
|
47
|
+
selectedKey?: Key;
|
|
48
|
+
/** The default selected step key (uncontrolled). */
|
|
49
|
+
defaultSelectedKey?: Key;
|
|
50
|
+
/** Called when the selected step changes. */
|
|
51
|
+
onSelectionChange?: (key: Key) => void;
|
|
52
|
+
/** The last completed step key (controlled). */
|
|
53
|
+
lastCompletedStep?: Key;
|
|
54
|
+
/** The default last completed step key (uncontrolled). */
|
|
55
|
+
defaultLastCompletedStep?: Key;
|
|
56
|
+
/** Called when last completed step changes. */
|
|
57
|
+
onLastCompletedStepChange?: (key: Key | null) => void;
|
|
58
|
+
/** Whether all steps are disabled. */
|
|
59
|
+
isDisabled?: boolean;
|
|
60
|
+
/** Whether all steps are read-only. */
|
|
61
|
+
isReadOnly?: boolean;
|
|
62
|
+
/** Keys of individually disabled steps. */
|
|
63
|
+
disabledKeys?: Iterable<Key>;
|
|
64
|
+
/** Render function for each step item. */
|
|
65
|
+
children: (item: T, state: StepListItemRenderProps) => JSX.Element;
|
|
66
|
+
/** The CSS className for the element. */
|
|
67
|
+
class?: ClassNameOrFunction<StepListRenderProps>;
|
|
68
|
+
/** The inline style for the element. */
|
|
69
|
+
style?: StyleOrFunction<StepListRenderProps>;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export interface StepProps {
|
|
73
|
+
/** The step item. */
|
|
74
|
+
item: { key: Key; label: string };
|
|
75
|
+
/** The 1-based step number. */
|
|
76
|
+
stepNumber: number;
|
|
77
|
+
/** The children to render inside the step. */
|
|
78
|
+
children?: JSX.Element;
|
|
79
|
+
/** The CSS className for the element. */
|
|
80
|
+
class?: string;
|
|
81
|
+
/** The inline style for the element. */
|
|
82
|
+
style?: JSX.CSSProperties;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export const StepListStateContext = createContext<StepListState | null>(null);
|
|
86
|
+
export const StepListContext = createContext<{} | null>(null);
|
|
87
|
+
|
|
88
|
+
export function useStepListState(): StepListState {
|
|
89
|
+
const ctx = useContext(StepListStateContext);
|
|
90
|
+
if (!ctx) throw new Error("useStepListState must be used within a StepList");
|
|
91
|
+
return ctx;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* StepList displays a sequence of steps with completion tracking and selection.
|
|
96
|
+
*/
|
|
97
|
+
export function StepList<T extends { key: Key; label: string }>(
|
|
98
|
+
props: StepListProps<T>,
|
|
99
|
+
): JSX.Element {
|
|
100
|
+
const [local, ariaProps, domRest] = splitProps(
|
|
101
|
+
props,
|
|
102
|
+
[
|
|
103
|
+
"items",
|
|
104
|
+
"selectedKey",
|
|
105
|
+
"defaultSelectedKey",
|
|
106
|
+
"onSelectionChange",
|
|
107
|
+
"lastCompletedStep",
|
|
108
|
+
"defaultLastCompletedStep",
|
|
109
|
+
"onLastCompletedStepChange",
|
|
110
|
+
"isDisabled",
|
|
111
|
+
"isReadOnly",
|
|
112
|
+
"disabledKeys",
|
|
113
|
+
"children",
|
|
114
|
+
"class",
|
|
115
|
+
"style",
|
|
116
|
+
],
|
|
117
|
+
["aria-label", "aria-labelledby"],
|
|
118
|
+
);
|
|
119
|
+
|
|
120
|
+
const stateProps = createMemo<StepListStateProps>(() => ({
|
|
121
|
+
items: local.items,
|
|
122
|
+
selectedKey: local.selectedKey,
|
|
123
|
+
defaultSelectedKey: local.defaultSelectedKey,
|
|
124
|
+
onSelectionChange: local.onSelectionChange,
|
|
125
|
+
lastCompletedStep: local.lastCompletedStep,
|
|
126
|
+
defaultLastCompletedStep: local.defaultLastCompletedStep,
|
|
127
|
+
onLastCompletedStepChange: local.onLastCompletedStepChange,
|
|
128
|
+
isDisabled: local.isDisabled,
|
|
129
|
+
isReadOnly: local.isReadOnly,
|
|
130
|
+
disabledKeys: local.disabledKeys,
|
|
131
|
+
}));
|
|
132
|
+
|
|
133
|
+
const state = createStepListState(stateProps());
|
|
134
|
+
|
|
135
|
+
// Create ARIA props
|
|
136
|
+
const { stepListProps } = createStepList(
|
|
137
|
+
{
|
|
138
|
+
get "aria-label"() {
|
|
139
|
+
return ariaProps["aria-label"];
|
|
140
|
+
},
|
|
141
|
+
get "aria-labelledby"() {
|
|
142
|
+
return ariaProps["aria-labelledby"];
|
|
143
|
+
},
|
|
144
|
+
},
|
|
145
|
+
state,
|
|
146
|
+
);
|
|
147
|
+
|
|
148
|
+
const renderValues = createMemo<StepListRenderProps>(() => ({
|
|
149
|
+
isDisabled: state.isDisabled(),
|
|
150
|
+
}));
|
|
151
|
+
|
|
152
|
+
const renderProps = useRenderProps(
|
|
153
|
+
{
|
|
154
|
+
class: local.class,
|
|
155
|
+
style: local.style,
|
|
156
|
+
defaultClassName: "solidaria-StepList",
|
|
157
|
+
},
|
|
158
|
+
renderValues,
|
|
159
|
+
);
|
|
160
|
+
|
|
161
|
+
const domProps = createMemo(() =>
|
|
162
|
+
filterDOMProps(domRest as Record<string, unknown>, { global: true }),
|
|
163
|
+
);
|
|
164
|
+
|
|
165
|
+
return (
|
|
166
|
+
<StepListStateContext.Provider value={state}>
|
|
167
|
+
<ol
|
|
168
|
+
{...stepListProps}
|
|
169
|
+
{...domProps()}
|
|
170
|
+
class={renderProps.class()}
|
|
171
|
+
style={renderProps.style()}
|
|
172
|
+
data-disabled={state.isDisabled() || undefined}
|
|
173
|
+
>
|
|
174
|
+
<For each={local.items}>
|
|
175
|
+
{(item, index) => {
|
|
176
|
+
const stepNumber = () => index() + 1;
|
|
177
|
+
|
|
178
|
+
// Build render props as a static snapshot for the initial render.
|
|
179
|
+
const renderProps: StepListItemRenderProps = {
|
|
180
|
+
get isSelected() {
|
|
181
|
+
return state.selectedKey() === item.key;
|
|
182
|
+
},
|
|
183
|
+
get isCompleted() {
|
|
184
|
+
return state.isCompleted(item.key);
|
|
185
|
+
},
|
|
186
|
+
get isDisabled() {
|
|
187
|
+
return state.isDisabled() || !state.isSelectable(item.key);
|
|
188
|
+
},
|
|
189
|
+
get isSelectable() {
|
|
190
|
+
return state.isSelectable(item.key);
|
|
191
|
+
},
|
|
192
|
+
get stepNumber() {
|
|
193
|
+
return stepNumber();
|
|
194
|
+
},
|
|
195
|
+
get stepStateText() {
|
|
196
|
+
if (state.selectedKey() === item.key) return "Current";
|
|
197
|
+
if (state.isCompleted(item.key)) return "Completed";
|
|
198
|
+
return "Not completed";
|
|
199
|
+
},
|
|
200
|
+
};
|
|
201
|
+
|
|
202
|
+
return local.children(item, renderProps);
|
|
203
|
+
}}
|
|
204
|
+
</For>
|
|
205
|
+
</ol>
|
|
206
|
+
</StepListStateContext.Provider>
|
|
207
|
+
);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* Step represents an individual step within a StepList.
|
|
212
|
+
* Renders as an `<li>` wrapping an `<a>` with accessible step props.
|
|
213
|
+
*/
|
|
214
|
+
export function Step(props: StepProps): JSX.Element {
|
|
215
|
+
const [local, domProps] = splitProps(props, ["item", "stepNumber", "children", "class", "style"]);
|
|
216
|
+
|
|
217
|
+
const state = useStepListState();
|
|
218
|
+
|
|
219
|
+
const isSelected = () => state.selectedKey() === local.item.key;
|
|
220
|
+
const isCompleted = () => state.isCompleted(local.item.key);
|
|
221
|
+
const selectable = () => state.isSelectable(local.item.key);
|
|
222
|
+
|
|
223
|
+
const handleClick = (e: MouseEvent) => {
|
|
224
|
+
e.preventDefault();
|
|
225
|
+
if (selectable()) {
|
|
226
|
+
state.setSelectedKey(local.item.key);
|
|
227
|
+
}
|
|
228
|
+
};
|
|
229
|
+
|
|
230
|
+
const handleKeyDown = (e: KeyboardEvent) => {
|
|
231
|
+
if (e.key === "ArrowUp" || e.key === "ArrowDown") {
|
|
232
|
+
e.preventDefault();
|
|
233
|
+
return;
|
|
234
|
+
}
|
|
235
|
+
if (e.key === "Enter" || e.key === " ") {
|
|
236
|
+
e.preventDefault();
|
|
237
|
+
if (selectable()) {
|
|
238
|
+
state.setSelectedKey(local.item.key);
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
};
|
|
242
|
+
|
|
243
|
+
const stepStateText = () => {
|
|
244
|
+
if (isSelected()) return "Current";
|
|
245
|
+
if (isCompleted()) return "Completed";
|
|
246
|
+
return "Not completed";
|
|
247
|
+
};
|
|
248
|
+
|
|
249
|
+
return (
|
|
250
|
+
<li
|
|
251
|
+
{...domProps}
|
|
252
|
+
class={local.class}
|
|
253
|
+
style={local.style}
|
|
254
|
+
data-selected={isSelected() || undefined}
|
|
255
|
+
data-completed={isCompleted() || undefined}
|
|
256
|
+
data-disabled={!selectable() || undefined}
|
|
257
|
+
data-selectable={selectable() || undefined}
|
|
258
|
+
>
|
|
259
|
+
<a
|
|
260
|
+
role="link"
|
|
261
|
+
aria-current={isSelected() ? "step" : undefined}
|
|
262
|
+
aria-disabled={!selectable() ? true : undefined}
|
|
263
|
+
tabIndex={selectable() ? 0 : undefined}
|
|
264
|
+
onClick={handleClick}
|
|
265
|
+
on:keydown={handleKeyDown}
|
|
266
|
+
aria-label={`Step ${local.stepNumber}: ${local.item.label}, ${stepStateText()}`}
|
|
267
|
+
>
|
|
268
|
+
{local.children}
|
|
269
|
+
</a>
|
|
270
|
+
</li>
|
|
271
|
+
);
|
|
272
|
+
}
|
package/src/Switch.tsx
CHANGED
|
@@ -11,16 +11,20 @@ import {
|
|
|
11
11
|
type JSX,
|
|
12
12
|
createContext,
|
|
13
13
|
createMemo,
|
|
14
|
+
createSignal,
|
|
15
|
+
createUniqueId,
|
|
14
16
|
splitProps,
|
|
15
|
-
|
|
17
|
+
untrack,
|
|
18
|
+
Show,
|
|
19
|
+
} from "solid-js";
|
|
16
20
|
import {
|
|
17
21
|
createSwitch,
|
|
18
22
|
createFocusRing,
|
|
19
23
|
createHover,
|
|
20
24
|
type AriaSwitchProps,
|
|
21
|
-
} from
|
|
22
|
-
import { createToggleState, type ToggleState } from
|
|
23
|
-
import { VisuallyHidden } from
|
|
25
|
+
} from "@proyecto-viviana/solidaria";
|
|
26
|
+
import { createToggleState, type ToggleState } from "@proyecto-viviana/solid-stately";
|
|
27
|
+
import { VisuallyHidden } from "./VisuallyHidden";
|
|
24
28
|
import {
|
|
25
29
|
type RenderChildren,
|
|
26
30
|
type ClassNameOrFunction,
|
|
@@ -28,11 +32,7 @@ import {
|
|
|
28
32
|
type SlotProps,
|
|
29
33
|
useRenderProps,
|
|
30
34
|
filterDOMProps,
|
|
31
|
-
} from
|
|
32
|
-
|
|
33
|
-
// ============================================
|
|
34
|
-
// TYPES
|
|
35
|
-
// ============================================
|
|
35
|
+
} from "./utils";
|
|
36
36
|
|
|
37
37
|
export interface ToggleSwitchRenderProps {
|
|
38
38
|
/** Whether the switch is selected. */
|
|
@@ -49,31 +49,27 @@ export interface ToggleSwitchRenderProps {
|
|
|
49
49
|
isDisabled: boolean;
|
|
50
50
|
/** Whether the switch is read only. */
|
|
51
51
|
isReadOnly: boolean;
|
|
52
|
+
/** Whether the switch is invalid. */
|
|
53
|
+
isInvalid: boolean;
|
|
52
54
|
/** State of the switch. */
|
|
53
55
|
state: ToggleState;
|
|
54
56
|
}
|
|
55
57
|
|
|
56
|
-
export interface ToggleSwitchProps
|
|
57
|
-
extends Omit<AriaSwitchProps, 'children'>,
|
|
58
|
-
SlotProps {
|
|
58
|
+
export interface ToggleSwitchProps extends Omit<AriaSwitchProps, "children">, SlotProps {
|
|
59
59
|
/** The children of the component. A function may be provided to receive render props. */
|
|
60
60
|
children?: RenderChildren<ToggleSwitchRenderProps>;
|
|
61
61
|
/** The CSS className for the element. */
|
|
62
62
|
class?: ClassNameOrFunction<ToggleSwitchRenderProps>;
|
|
63
63
|
/** The inline style for the element. */
|
|
64
64
|
style?: StyleOrFunction<ToggleSwitchRenderProps>;
|
|
65
|
+
/** A description for the switch. */
|
|
66
|
+
description?: JSX.Element;
|
|
67
|
+
/** An error message for the switch. */
|
|
68
|
+
errorMessage?: JSX.Element;
|
|
65
69
|
}
|
|
66
70
|
|
|
67
|
-
// ============================================
|
|
68
|
-
// CONTEXT
|
|
69
|
-
// ============================================
|
|
70
|
-
|
|
71
71
|
export const ToggleSwitchContext = createContext<ToggleSwitchProps | null>(null);
|
|
72
72
|
|
|
73
|
-
// ============================================
|
|
74
|
-
// COMPONENT
|
|
75
|
-
// ============================================
|
|
76
|
-
|
|
77
73
|
/**
|
|
78
74
|
* A switch allows a user to turn a setting on or off.
|
|
79
75
|
*
|
|
@@ -97,45 +93,51 @@ export const ToggleSwitchContext = createContext<ToggleSwitchProps | null>(null)
|
|
|
97
93
|
* ```
|
|
98
94
|
*/
|
|
99
95
|
export function ToggleSwitch(props: ToggleSwitchProps): JSX.Element {
|
|
100
|
-
|
|
96
|
+
const [inputElement, setInputElement] = createSignal<HTMLInputElement | null>(null);
|
|
101
97
|
|
|
102
|
-
// Split props
|
|
103
98
|
const [local, ariaProps] = splitProps(props, [
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
99
|
+
"class",
|
|
100
|
+
"style",
|
|
101
|
+
"slot",
|
|
102
|
+
"description",
|
|
103
|
+
"errorMessage",
|
|
107
104
|
]);
|
|
105
|
+
const descriptionId = createUniqueId();
|
|
106
|
+
const errorMessageId = createUniqueId();
|
|
108
107
|
|
|
109
|
-
// Create toggle state
|
|
110
108
|
// Use getters to ensure props are read lazily inside reactive contexts
|
|
111
|
-
const state = createToggleState({
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
});
|
|
109
|
+
const state = createToggleState(() => ({
|
|
110
|
+
isSelected: ariaProps.isSelected,
|
|
111
|
+
defaultSelected: ariaProps.defaultSelected,
|
|
112
|
+
onChange: ariaProps.onChange,
|
|
113
|
+
isReadOnly: ariaProps.isReadOnly,
|
|
114
|
+
}));
|
|
117
115
|
|
|
118
|
-
// Create switch aria props
|
|
119
116
|
const switchAria = createSwitch(
|
|
120
117
|
() => ({
|
|
121
118
|
...ariaProps,
|
|
122
|
-
children: typeof props.children ===
|
|
119
|
+
children: typeof props.children === "function" ? true : props.children,
|
|
123
120
|
}),
|
|
124
121
|
state,
|
|
125
|
-
|
|
122
|
+
inputElement,
|
|
126
123
|
);
|
|
124
|
+
const describedBy = () => {
|
|
125
|
+
const ids = [
|
|
126
|
+
ariaProps["aria-describedby"],
|
|
127
|
+
local.description ? descriptionId : undefined,
|
|
128
|
+
switchAria.isInvalid && local.errorMessage ? errorMessageId : undefined,
|
|
129
|
+
].filter(Boolean);
|
|
130
|
+
return ids.length ? ids.join(" ") : undefined;
|
|
131
|
+
};
|
|
127
132
|
|
|
128
|
-
// Create focus ring
|
|
129
133
|
const { isFocused, isFocusVisible, focusProps } = createFocusRing();
|
|
130
134
|
|
|
131
|
-
// Create hover
|
|
132
135
|
const { isHovered, hoverProps } = createHover({
|
|
133
136
|
get isDisabled() {
|
|
134
137
|
return ariaProps.isDisabled || ariaProps.isReadOnly;
|
|
135
138
|
},
|
|
136
139
|
});
|
|
137
140
|
|
|
138
|
-
// Render props values
|
|
139
141
|
const renderValues = createMemo<ToggleSwitchRenderProps>(() => ({
|
|
140
142
|
isSelected: switchAria.isSelected(),
|
|
141
143
|
isHovered: isHovered(),
|
|
@@ -144,21 +146,49 @@ export function ToggleSwitch(props: ToggleSwitchProps): JSX.Element {
|
|
|
144
146
|
isFocusVisible: isFocusVisible(),
|
|
145
147
|
isDisabled: switchAria.isDisabled,
|
|
146
148
|
isReadOnly: switchAria.isReadOnly,
|
|
149
|
+
isInvalid: switchAria.isInvalid,
|
|
147
150
|
state,
|
|
148
151
|
}));
|
|
149
152
|
|
|
150
|
-
// Resolve render props
|
|
151
153
|
const renderProps = useRenderProps(
|
|
152
154
|
{
|
|
153
155
|
children: props.children,
|
|
154
156
|
class: local.class,
|
|
155
157
|
style: local.style,
|
|
156
|
-
defaultClassName:
|
|
158
|
+
defaultClassName: "solidaria-ToggleSwitch",
|
|
157
159
|
},
|
|
158
|
-
renderValues
|
|
160
|
+
renderValues,
|
|
159
161
|
);
|
|
162
|
+
const childRenderValues: ToggleSwitchRenderProps = {
|
|
163
|
+
get isSelected() {
|
|
164
|
+
return switchAria.isSelected();
|
|
165
|
+
},
|
|
166
|
+
get isHovered() {
|
|
167
|
+
return isHovered();
|
|
168
|
+
},
|
|
169
|
+
get isPressed() {
|
|
170
|
+
return switchAria.isPressed();
|
|
171
|
+
},
|
|
172
|
+
get isFocused() {
|
|
173
|
+
return isFocused();
|
|
174
|
+
},
|
|
175
|
+
get isFocusVisible() {
|
|
176
|
+
return isFocusVisible();
|
|
177
|
+
},
|
|
178
|
+
get isDisabled() {
|
|
179
|
+
return switchAria.isDisabled;
|
|
180
|
+
},
|
|
181
|
+
get isReadOnly() {
|
|
182
|
+
return switchAria.isReadOnly;
|
|
183
|
+
},
|
|
184
|
+
get isInvalid() {
|
|
185
|
+
return switchAria.isInvalid;
|
|
186
|
+
},
|
|
187
|
+
get state() {
|
|
188
|
+
return state;
|
|
189
|
+
},
|
|
190
|
+
};
|
|
160
191
|
|
|
161
|
-
// Filter DOM props
|
|
162
192
|
const domProps = createMemo(() => {
|
|
163
193
|
const filtered = filterDOMProps(ariaProps, { global: true });
|
|
164
194
|
delete (filtered as Record<string, unknown>).id;
|
|
@@ -166,7 +196,6 @@ export function ToggleSwitch(props: ToggleSwitchProps): JSX.Element {
|
|
|
166
196
|
return filtered;
|
|
167
197
|
});
|
|
168
198
|
|
|
169
|
-
// Remove ref from spread props to avoid type conflicts
|
|
170
199
|
const cleanLabelProps = () => {
|
|
171
200
|
const { ref: _ref1, ...rest } = switchAria.labelProps as Record<string, unknown>;
|
|
172
201
|
return rest;
|
|
@@ -183,6 +212,14 @@ export function ToggleSwitch(props: ToggleSwitchProps): JSX.Element {
|
|
|
183
212
|
const { ref: _ref4, ...rest } = focusProps as Record<string, unknown>;
|
|
184
213
|
return rest;
|
|
185
214
|
};
|
|
215
|
+
// Resolve the render-prop children ONCE (untracked) — see TextField. Re-invoking
|
|
216
|
+
// it on a reactive update re-clones its templates and, mid-hydration, throws a
|
|
217
|
+
// Hydration Mismatch. The children keep fine-grained reactivity via the
|
|
218
|
+
// childRenderValues getters + <Show>s.
|
|
219
|
+
const switchChildren = untrack(() => {
|
|
220
|
+
const children = props.children;
|
|
221
|
+
return typeof children === "function" ? children(childRenderValues) : children;
|
|
222
|
+
});
|
|
186
223
|
|
|
187
224
|
return (
|
|
188
225
|
<label
|
|
@@ -201,13 +238,23 @@ export function ToggleSwitch(props: ToggleSwitchProps): JSX.Element {
|
|
|
201
238
|
>
|
|
202
239
|
<VisuallyHidden>
|
|
203
240
|
<input
|
|
204
|
-
ref={
|
|
241
|
+
ref={setInputElement}
|
|
205
242
|
{...cleanInputProps()}
|
|
206
243
|
{...cleanFocusProps()}
|
|
244
|
+
aria-describedby={describedBy()}
|
|
207
245
|
/>
|
|
208
246
|
</VisuallyHidden>
|
|
209
|
-
{
|
|
247
|
+
{switchChildren}
|
|
248
|
+
<Show when={local.description}>
|
|
249
|
+
<span id={descriptionId} slot="description">
|
|
250
|
+
{local.description}
|
|
251
|
+
</span>
|
|
252
|
+
</Show>
|
|
253
|
+
<Show when={switchAria.isInvalid && local.errorMessage}>
|
|
254
|
+
<span id={errorMessageId} slot="errorMessage">
|
|
255
|
+
{local.errorMessage}
|
|
256
|
+
</span>
|
|
257
|
+
</Show>
|
|
210
258
|
</label>
|
|
211
259
|
);
|
|
212
260
|
}
|
|
213
|
-
|