@proyecto-viviana/solidaria-components 0.2.9 → 0.3.1
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 +23253 -18564
- package/dist/index.js.map +1 -1
- package/dist/index.jsx +18116 -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 +251 -176
- package/src/NumberField.tsx +139 -143
- package/src/Popover.tsx +396 -234
- 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 +216 -158
- 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 +49 -60
- package/src/virtualizer/Layout.ts +14 -22
- package/dist/index.ssr.js +0 -16996
- package/dist/index.ssr.js.map +0 -1
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HiddenDateInput component for solidaria-components
|
|
3
|
+
*
|
|
4
|
+
* A hidden native date/datetime-local input for form submission.
|
|
5
|
+
* Renders server-side with the initial value for SSR safety.
|
|
6
|
+
*/
|
|
7
|
+
import { type JSX, createEffect, createSignal } from "solid-js";
|
|
8
|
+
import { type DateValue, type FormValidationState } from "@proyecto-viviana/solid-stately";
|
|
9
|
+
import { createFormValidation } from "@proyecto-viviana/solidaria";
|
|
10
|
+
|
|
11
|
+
type MaybeAccessor<T> = T | (() => T);
|
|
12
|
+
|
|
13
|
+
export interface HiddenDateInputProps {
|
|
14
|
+
name?: string;
|
|
15
|
+
form?: string;
|
|
16
|
+
value?: MaybeAccessor<DateValue | null | undefined>;
|
|
17
|
+
autoComplete?: string;
|
|
18
|
+
isDisabled?: boolean;
|
|
19
|
+
isRequired?: boolean;
|
|
20
|
+
validationBehavior?: "aria" | "native";
|
|
21
|
+
validationState?: FormValidationState;
|
|
22
|
+
focus?: () => void;
|
|
23
|
+
minValue?: MaybeAccessor<DateValue | undefined>;
|
|
24
|
+
maxValue?: MaybeAccessor<DateValue | undefined>;
|
|
25
|
+
granularity?: "day" | "hour" | "minute" | "second";
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function accessValue<T>(value: MaybeAccessor<T> | undefined): T | undefined {
|
|
29
|
+
return typeof value === "function" ? (value as () => T)() : value;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function formatDateValue(
|
|
33
|
+
value: DateValue | undefined | null,
|
|
34
|
+
granularity: "day" | "hour" | "minute" | "second",
|
|
35
|
+
): string {
|
|
36
|
+
if (!value) return "";
|
|
37
|
+
|
|
38
|
+
if ("timeZone" in value) {
|
|
39
|
+
return String(value);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const dateValue = value;
|
|
43
|
+
|
|
44
|
+
const year = String(dateValue.year).padStart(4, "0");
|
|
45
|
+
const month = String(dateValue.month).padStart(2, "0");
|
|
46
|
+
const day = String(dateValue.day).padStart(2, "0");
|
|
47
|
+
|
|
48
|
+
if (granularity === "day") {
|
|
49
|
+
return `${year}-${month}-${day}`;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const hasHour = "hour" in dateValue;
|
|
53
|
+
const hour = hasHour ? String((dateValue as { hour: number }).hour).padStart(2, "0") : "00";
|
|
54
|
+
const hasMinute = hasHour && "minute" in dateValue;
|
|
55
|
+
const minute = hasMinute
|
|
56
|
+
? String((dateValue as { minute: number }).minute).padStart(2, "0")
|
|
57
|
+
: "00";
|
|
58
|
+
|
|
59
|
+
if (granularity === "second" && hasMinute && "second" in dateValue) {
|
|
60
|
+
const second = String((dateValue as { second: number }).second).padStart(2, "0");
|
|
61
|
+
return `${year}-${month}-${day}T${hour}:${minute}:${second}`;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return `${year}-${month}-${day}T${hour}:${minute}`;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export function HiddenDateInput(props: HiddenDateInputProps): JSX.Element {
|
|
68
|
+
const granularity = () => props.granularity ?? "day";
|
|
69
|
+
const hasValidationBehavior = () => props.validationBehavior != null;
|
|
70
|
+
const validationBehavior = () => props.validationBehavior;
|
|
71
|
+
const usesNativeValidation = () => validationBehavior() === "native";
|
|
72
|
+
const value = () => accessValue(props.value);
|
|
73
|
+
const minValue = () => accessValue(props.minValue);
|
|
74
|
+
const maxValue = () => accessValue(props.maxValue);
|
|
75
|
+
const hasTimeZoneValue = () =>
|
|
76
|
+
Boolean(
|
|
77
|
+
(value() && "timeZone" in value()!) ||
|
|
78
|
+
(minValue() && "timeZone" in minValue()!) ||
|
|
79
|
+
(maxValue() && "timeZone" in maxValue()!),
|
|
80
|
+
);
|
|
81
|
+
const inputType = () =>
|
|
82
|
+
usesNativeValidation()
|
|
83
|
+
? "text"
|
|
84
|
+
: hasValidationBehavior()
|
|
85
|
+
? "hidden"
|
|
86
|
+
: hasTimeZoneValue()
|
|
87
|
+
? "hidden"
|
|
88
|
+
: granularity() === "day"
|
|
89
|
+
? "date"
|
|
90
|
+
: "datetime-local";
|
|
91
|
+
const formattedValue = () => formatDateValue(value(), granularity());
|
|
92
|
+
const formattedMin = () => formatDateValue(minValue(), granularity());
|
|
93
|
+
const formattedMax = () => formatDateValue(maxValue(), granularity());
|
|
94
|
+
|
|
95
|
+
const [inputRef, setInputRef] = createSignal<HTMLInputElement>();
|
|
96
|
+
|
|
97
|
+
if (props.validationState) {
|
|
98
|
+
createFormValidation(
|
|
99
|
+
{
|
|
100
|
+
get validationBehavior() {
|
|
101
|
+
return validationBehavior();
|
|
102
|
+
},
|
|
103
|
+
get focus() {
|
|
104
|
+
return props.focus;
|
|
105
|
+
},
|
|
106
|
+
},
|
|
107
|
+
props.validationState,
|
|
108
|
+
inputRef,
|
|
109
|
+
);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
createEffect(() => {
|
|
113
|
+
const val = formattedValue();
|
|
114
|
+
const input = inputRef();
|
|
115
|
+
if (input && input.value !== val) {
|
|
116
|
+
input.value = val;
|
|
117
|
+
}
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
return (
|
|
121
|
+
<input
|
|
122
|
+
ref={(el) => {
|
|
123
|
+
setInputRef(el);
|
|
124
|
+
}}
|
|
125
|
+
type={inputType()}
|
|
126
|
+
name={props.name}
|
|
127
|
+
form={props.form}
|
|
128
|
+
value={formattedValue()}
|
|
129
|
+
autocomplete={props.autoComplete}
|
|
130
|
+
disabled={props.isDisabled}
|
|
131
|
+
required={usesNativeValidation() && props.isRequired ? true : undefined}
|
|
132
|
+
hidden={usesNativeValidation() || undefined}
|
|
133
|
+
min={hasValidationBehavior() ? undefined : formattedMin() || undefined}
|
|
134
|
+
max={hasValidationBehavior() ? undefined : formattedMax() || undefined}
|
|
135
|
+
onChange={usesNativeValidation() ? () => {} : undefined}
|
|
136
|
+
tabIndex={-1}
|
|
137
|
+
aria-hidden="true"
|
|
138
|
+
style={
|
|
139
|
+
{
|
|
140
|
+
position: "absolute",
|
|
141
|
+
width: "1px",
|
|
142
|
+
height: "1px",
|
|
143
|
+
padding: "0",
|
|
144
|
+
margin: "-1px",
|
|
145
|
+
overflow: "hidden",
|
|
146
|
+
clip: "rect(0, 0, 0, 0)",
|
|
147
|
+
"white-space": "nowrap",
|
|
148
|
+
"border-width": "0",
|
|
149
|
+
} as JSX.CSSProperties
|
|
150
|
+
}
|
|
151
|
+
/>
|
|
152
|
+
);
|
|
153
|
+
}
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HiddenTimeInput component for solidaria-components
|
|
3
|
+
*
|
|
4
|
+
* A hidden native time input for form submission.
|
|
5
|
+
*/
|
|
6
|
+
import { type JSX, createEffect, createSignal } from "solid-js";
|
|
7
|
+
import { type FormValidationState, type TimeValue } from "@proyecto-viviana/solid-stately";
|
|
8
|
+
import { createFormValidation } from "@proyecto-viviana/solidaria";
|
|
9
|
+
|
|
10
|
+
type MaybeAccessor<T> = T | (() => T);
|
|
11
|
+
|
|
12
|
+
export interface HiddenTimeInputProps {
|
|
13
|
+
name?: string;
|
|
14
|
+
form?: string;
|
|
15
|
+
value?: MaybeAccessor<TimeValue | null | undefined>;
|
|
16
|
+
autoComplete?: string;
|
|
17
|
+
isDisabled?: boolean;
|
|
18
|
+
isRequired?: boolean;
|
|
19
|
+
validationBehavior?: "aria" | "native";
|
|
20
|
+
validationState?: FormValidationState;
|
|
21
|
+
focus?: () => void;
|
|
22
|
+
minValue?: MaybeAccessor<TimeValue | undefined>;
|
|
23
|
+
maxValue?: MaybeAccessor<TimeValue | undefined>;
|
|
24
|
+
granularity?: "hour" | "minute" | "second";
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function accessValue<T>(value: MaybeAccessor<T> | undefined): T | undefined {
|
|
28
|
+
return typeof value === "function" ? (value as () => T)() : value;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function formatTimeValue(
|
|
32
|
+
value: TimeValue | undefined | null,
|
|
33
|
+
granularity: "hour" | "minute" | "second",
|
|
34
|
+
): string {
|
|
35
|
+
if (!value) return "";
|
|
36
|
+
|
|
37
|
+
const hour = String(value.hour).padStart(2, "0");
|
|
38
|
+
const minute = String(granularity === "hour" ? 0 : value.minute).padStart(2, "0");
|
|
39
|
+
|
|
40
|
+
if (granularity === "second") {
|
|
41
|
+
const second = String(value.second).padStart(2, "0");
|
|
42
|
+
return `${hour}:${minute}:${second}`;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return `${hour}:${minute}`;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function stepForGranularity(granularity: "hour" | "minute" | "second"): number {
|
|
49
|
+
switch (granularity) {
|
|
50
|
+
case "hour":
|
|
51
|
+
return 3600;
|
|
52
|
+
case "second":
|
|
53
|
+
return 1;
|
|
54
|
+
case "minute":
|
|
55
|
+
default:
|
|
56
|
+
return 60;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export function HiddenTimeInput(props: HiddenTimeInputProps): JSX.Element {
|
|
61
|
+
const granularity = () => props.granularity ?? "minute";
|
|
62
|
+
const hasValidationBehavior = () => props.validationBehavior != null;
|
|
63
|
+
const validationBehavior = () => props.validationBehavior;
|
|
64
|
+
const usesNativeValidation = () => validationBehavior() === "native";
|
|
65
|
+
const value = () => accessValue(props.value);
|
|
66
|
+
const minValue = () => accessValue(props.minValue);
|
|
67
|
+
const maxValue = () => accessValue(props.maxValue);
|
|
68
|
+
const inputType = () =>
|
|
69
|
+
usesNativeValidation() ? "text" : hasValidationBehavior() ? "hidden" : "time";
|
|
70
|
+
const formattedValue = () => formatTimeValue(value(), granularity());
|
|
71
|
+
const formattedMin = () => formatTimeValue(minValue(), granularity());
|
|
72
|
+
const formattedMax = () => formatTimeValue(maxValue(), granularity());
|
|
73
|
+
|
|
74
|
+
const [inputRef, setInputRef] = createSignal<HTMLInputElement>();
|
|
75
|
+
|
|
76
|
+
if (props.validationState) {
|
|
77
|
+
createFormValidation(
|
|
78
|
+
{
|
|
79
|
+
get validationBehavior() {
|
|
80
|
+
return validationBehavior();
|
|
81
|
+
},
|
|
82
|
+
get focus() {
|
|
83
|
+
return props.focus;
|
|
84
|
+
},
|
|
85
|
+
},
|
|
86
|
+
props.validationState,
|
|
87
|
+
inputRef,
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
createEffect(() => {
|
|
92
|
+
const val = formattedValue();
|
|
93
|
+
const input = inputRef();
|
|
94
|
+
if (input && input.value !== val) {
|
|
95
|
+
input.value = val;
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
return (
|
|
100
|
+
<input
|
|
101
|
+
ref={(el) => {
|
|
102
|
+
setInputRef(el);
|
|
103
|
+
}}
|
|
104
|
+
type={inputType()}
|
|
105
|
+
name={props.name}
|
|
106
|
+
form={props.form}
|
|
107
|
+
value={formattedValue()}
|
|
108
|
+
autocomplete={props.autoComplete}
|
|
109
|
+
disabled={props.isDisabled}
|
|
110
|
+
required={usesNativeValidation() && props.isRequired ? true : undefined}
|
|
111
|
+
hidden={usesNativeValidation() || undefined}
|
|
112
|
+
min={hasValidationBehavior() ? undefined : formattedMin() || undefined}
|
|
113
|
+
max={hasValidationBehavior() ? undefined : formattedMax() || undefined}
|
|
114
|
+
step={hasValidationBehavior() ? undefined : stepForGranularity(granularity())}
|
|
115
|
+
onChange={usesNativeValidation() ? () => {} : undefined}
|
|
116
|
+
tabIndex={-1}
|
|
117
|
+
aria-hidden="true"
|
|
118
|
+
style={
|
|
119
|
+
{
|
|
120
|
+
position: "absolute",
|
|
121
|
+
width: "1px",
|
|
122
|
+
height: "1px",
|
|
123
|
+
padding: "0",
|
|
124
|
+
margin: "-1px",
|
|
125
|
+
overflow: "hidden",
|
|
126
|
+
clip: "rect(0, 0, 0, 0)",
|
|
127
|
+
"white-space": "nowrap",
|
|
128
|
+
"border-width": "0",
|
|
129
|
+
} as JSX.CSSProperties
|
|
130
|
+
}
|
|
131
|
+
/>
|
|
132
|
+
);
|
|
133
|
+
}
|
package/src/Icon.tsx
CHANGED
|
@@ -9,13 +9,7 @@
|
|
|
9
9
|
* The UI layer consumes this for styling/composition only.
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
|
-
import {
|
|
13
|
-
type JSX,
|
|
14
|
-
createContext,
|
|
15
|
-
createMemo,
|
|
16
|
-
Show,
|
|
17
|
-
splitProps,
|
|
18
|
-
} from 'solid-js';
|
|
12
|
+
import { type JSX, createContext, createMemo, Show, splitProps } from "solid-js";
|
|
19
13
|
import {
|
|
20
14
|
type RenderChildren,
|
|
21
15
|
type ClassNameOrFunction,
|
|
@@ -23,13 +17,9 @@ import {
|
|
|
23
17
|
type SlotProps,
|
|
24
18
|
useRenderProps,
|
|
25
19
|
filterDOMProps,
|
|
26
|
-
} from
|
|
27
|
-
import { Button } from
|
|
28
|
-
import type { PressEvent } from
|
|
29
|
-
|
|
30
|
-
// ============================================
|
|
31
|
-
// TYPES
|
|
32
|
-
// ============================================
|
|
20
|
+
} from "./utils";
|
|
21
|
+
import { Button } from "./Button";
|
|
22
|
+
import type { PressEvent } from "@proyecto-viviana/solidaria";
|
|
33
23
|
|
|
34
24
|
export interface IconRenderProps {
|
|
35
25
|
/** Whether the icon is purely decorative (no label). */
|
|
@@ -42,9 +32,9 @@ export interface IconProps extends SlotProps {
|
|
|
42
32
|
/** Handler called when the icon is pressed (makes it interactive). */
|
|
43
33
|
onPress?: (e: PressEvent) => void;
|
|
44
34
|
/** Accessible label for the icon. When provided, the icon is informative (role="img"). */
|
|
45
|
-
|
|
35
|
+
"aria-label"?: string;
|
|
46
36
|
/** ID of an element that labels this icon. */
|
|
47
|
-
|
|
37
|
+
"aria-labelledby"?: string;
|
|
48
38
|
/** The children of the component. A function may be provided to receive render props. */
|
|
49
39
|
children?: RenderChildren<IconRenderProps>;
|
|
50
40
|
/** The CSS className for the element. */
|
|
@@ -55,16 +45,8 @@ export interface IconProps extends SlotProps {
|
|
|
55
45
|
id?: string;
|
|
56
46
|
}
|
|
57
47
|
|
|
58
|
-
// ============================================
|
|
59
|
-
// CONTEXT
|
|
60
|
-
// ============================================
|
|
61
|
-
|
|
62
48
|
export const IconContext = createContext<IconProps | null>(null);
|
|
63
49
|
|
|
64
|
-
// ============================================
|
|
65
|
-
// ICON COMPONENT
|
|
66
|
-
// ============================================
|
|
67
|
-
|
|
68
50
|
/**
|
|
69
51
|
* An icon wrapper that provides correct ARIA semantics.
|
|
70
52
|
*
|
|
@@ -86,37 +68,34 @@ export const IconContext = createContext<IconProps | null>(null);
|
|
|
86
68
|
*/
|
|
87
69
|
export function Icon(props: IconProps): JSX.Element {
|
|
88
70
|
const [local, rest] = splitProps(props, [
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
71
|
+
"children",
|
|
72
|
+
"class",
|
|
73
|
+
"style",
|
|
74
|
+
"slot",
|
|
75
|
+
"onPress",
|
|
76
|
+
"aria-label",
|
|
77
|
+
"aria-labelledby",
|
|
96
78
|
]);
|
|
97
79
|
|
|
98
|
-
const hasLabel = () => !!(local[
|
|
80
|
+
const hasLabel = () => !!(local["aria-label"] || local["aria-labelledby"]);
|
|
99
81
|
const isInteractive = () => !!local.onPress;
|
|
100
82
|
const isDecorative = () => !hasLabel();
|
|
101
83
|
|
|
102
|
-
// Render props values
|
|
103
84
|
const renderValues = createMemo<IconRenderProps>(() => ({
|
|
104
85
|
isDecorative: isDecorative(),
|
|
105
86
|
isInteractive: isInteractive(),
|
|
106
87
|
}));
|
|
107
88
|
|
|
108
|
-
// Resolve render props
|
|
109
89
|
const renderProps = useRenderProps(
|
|
110
90
|
{
|
|
111
91
|
children: local.children,
|
|
112
92
|
class: local.class,
|
|
113
93
|
style: local.style,
|
|
114
|
-
defaultClassName:
|
|
94
|
+
defaultClassName: "solidaria-Icon",
|
|
115
95
|
},
|
|
116
|
-
renderValues
|
|
96
|
+
renderValues,
|
|
117
97
|
);
|
|
118
98
|
|
|
119
|
-
// Filter DOM props
|
|
120
99
|
const domProps = createMemo(() => filterDOMProps(rest, { global: true }));
|
|
121
100
|
|
|
122
101
|
return (
|
|
@@ -125,10 +104,10 @@ export function Icon(props: IconProps): JSX.Element {
|
|
|
125
104
|
fallback={
|
|
126
105
|
<span
|
|
127
106
|
{...domProps()}
|
|
128
|
-
role={hasLabel() ?
|
|
129
|
-
aria-hidden={isDecorative() ?
|
|
130
|
-
aria-label={local[
|
|
131
|
-
aria-labelledby={local[
|
|
107
|
+
role={hasLabel() ? "img" : undefined}
|
|
108
|
+
aria-hidden={isDecorative() ? "true" : undefined}
|
|
109
|
+
aria-label={local["aria-label"]}
|
|
110
|
+
aria-labelledby={local["aria-labelledby"]}
|
|
132
111
|
class={renderProps.class()}
|
|
133
112
|
style={renderProps.style()}
|
|
134
113
|
data-interactive={undefined}
|
|
@@ -141,8 +120,8 @@ export function Icon(props: IconProps): JSX.Element {
|
|
|
141
120
|
<Button
|
|
142
121
|
{...domProps()}
|
|
143
122
|
onPress={local.onPress}
|
|
144
|
-
aria-label={local[
|
|
145
|
-
aria-labelledby={local[
|
|
123
|
+
aria-label={local["aria-label"]}
|
|
124
|
+
aria-labelledby={local["aria-labelledby"]}
|
|
146
125
|
class={renderProps.class()}
|
|
147
126
|
style={renderProps.style()}
|
|
148
127
|
data-interactive=""
|
package/src/Keyboard.tsx
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* Port direction: react-aria-components/src/Keyboard.tsx
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import { type JSX, createContext, splitProps, useContext } from
|
|
8
|
+
import { type JSX, createContext, splitProps, useContext } from "solid-js";
|
|
9
9
|
|
|
10
10
|
export interface KeyboardProps extends JSX.HTMLAttributes<HTMLElement> {
|
|
11
11
|
children?: JSX.Element;
|
|
@@ -15,8 +15,8 @@ export const KeyboardContext = createContext<KeyboardProps | null>(null);
|
|
|
15
15
|
|
|
16
16
|
export function Keyboard(props: KeyboardProps): JSX.Element {
|
|
17
17
|
const context = useContext(KeyboardContext);
|
|
18
|
-
const merged = () => ({ ...
|
|
19
|
-
const [local, domProps] = splitProps(merged(), [
|
|
18
|
+
const merged = () => ({ ...context, ...props });
|
|
19
|
+
const [local, domProps] = splitProps(merged(), ["children"]);
|
|
20
20
|
|
|
21
21
|
return (
|
|
22
22
|
<kbd dir="ltr" {...domProps}>
|
package/src/Landmark.tsx
CHANGED
|
@@ -5,38 +5,23 @@
|
|
|
5
5
|
* Enables F6 keyboard navigation between major page sections.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import {
|
|
9
|
-
|
|
10
|
-
createContext,
|
|
11
|
-
createMemo,
|
|
12
|
-
createSignal,
|
|
13
|
-
splitProps,
|
|
14
|
-
} from 'solid-js';
|
|
15
|
-
import { Dynamic } from 'solid-js/web';
|
|
8
|
+
import { type JSX, createContext, createMemo, createSignal, splitProps } from "solid-js";
|
|
9
|
+
import { Dynamic } from "solid-js/web";
|
|
16
10
|
import {
|
|
17
11
|
createLandmark,
|
|
18
12
|
getLandmarkController,
|
|
19
13
|
type AriaLandmarkProps,
|
|
20
14
|
type AriaLandmarkRole,
|
|
21
15
|
type LandmarkController,
|
|
22
|
-
} from
|
|
23
|
-
import {
|
|
24
|
-
type SlotProps,
|
|
25
|
-
filterDOMProps,
|
|
26
|
-
} from './utils';
|
|
27
|
-
|
|
28
|
-
// ============================================
|
|
29
|
-
// TYPES
|
|
30
|
-
// ============================================
|
|
16
|
+
} from "@proyecto-viviana/solidaria";
|
|
17
|
+
import { type SlotProps, filterDOMProps } from "./utils";
|
|
31
18
|
|
|
32
19
|
export interface LandmarkRenderProps {
|
|
33
20
|
/** The ARIA landmark role. */
|
|
34
21
|
role: AriaLandmarkRole;
|
|
35
22
|
}
|
|
36
23
|
|
|
37
|
-
export interface LandmarkProps
|
|
38
|
-
extends AriaLandmarkProps,
|
|
39
|
-
SlotProps {
|
|
24
|
+
export interface LandmarkProps extends AriaLandmarkProps, SlotProps {
|
|
40
25
|
/**
|
|
41
26
|
* The HTML element type to render.
|
|
42
27
|
* @default 'div' (or semantic element based on role)
|
|
@@ -50,38 +35,25 @@ export interface LandmarkProps
|
|
|
50
35
|
children?: JSX.Element;
|
|
51
36
|
}
|
|
52
37
|
|
|
53
|
-
// Re-export types
|
|
54
38
|
export type { AriaLandmarkRole, LandmarkController };
|
|
55
39
|
|
|
56
|
-
// ============================================
|
|
57
|
-
// CONTEXT
|
|
58
|
-
// ============================================
|
|
59
|
-
|
|
60
40
|
export const LandmarkContext = createContext<LandmarkProps | null>(null);
|
|
61
41
|
|
|
62
|
-
// ============================================
|
|
63
|
-
// SEMANTIC ELEMENT MAPPING
|
|
64
|
-
// ============================================
|
|
65
|
-
|
|
66
42
|
/**
|
|
67
43
|
* Maps ARIA landmark roles to their semantic HTML elements.
|
|
68
44
|
* Using semantic elements is preferred when possible.
|
|
69
45
|
*/
|
|
70
46
|
const roleToSemanticElement: Partial<Record<AriaLandmarkRole, keyof JSX.IntrinsicElements>> = {
|
|
71
|
-
main:
|
|
72
|
-
navigation:
|
|
73
|
-
search:
|
|
74
|
-
banner:
|
|
75
|
-
contentinfo:
|
|
76
|
-
complementary:
|
|
77
|
-
form:
|
|
78
|
-
region:
|
|
47
|
+
main: "main",
|
|
48
|
+
navigation: "nav",
|
|
49
|
+
search: "search", // HTML5.3 <search> element
|
|
50
|
+
banner: "header",
|
|
51
|
+
contentinfo: "footer",
|
|
52
|
+
complementary: "aside",
|
|
53
|
+
form: "form",
|
|
54
|
+
region: "section",
|
|
79
55
|
};
|
|
80
56
|
|
|
81
|
-
// ============================================
|
|
82
|
-
// LANDMARK COMPONENT
|
|
83
|
-
// ============================================
|
|
84
|
-
|
|
85
57
|
/**
|
|
86
58
|
* A landmark is a region of the page that helps screen reader users navigate.
|
|
87
59
|
* Press F6 to cycle through landmarks, or Shift+F6 to go backwards.
|
|
@@ -112,11 +84,11 @@ const roleToSemanticElement: Partial<Record<AriaLandmarkRole, keyof JSX.Intrinsi
|
|
|
112
84
|
*/
|
|
113
85
|
export function Landmark(props: LandmarkProps): JSX.Element {
|
|
114
86
|
const [local, ariaProps] = splitProps(props, [
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
87
|
+
"class",
|
|
88
|
+
"style",
|
|
89
|
+
"slot",
|
|
90
|
+
"children",
|
|
91
|
+
"elementType",
|
|
120
92
|
]);
|
|
121
93
|
|
|
122
94
|
// Element ref
|
|
@@ -127,45 +99,51 @@ export function Landmark(props: LandmarkProps): JSX.Element {
|
|
|
127
99
|
if (local.elementType) {
|
|
128
100
|
return local.elementType;
|
|
129
101
|
}
|
|
130
|
-
return roleToSemanticElement[ariaProps.role] ??
|
|
102
|
+
return roleToSemanticElement[ariaProps.role] ?? "div";
|
|
131
103
|
});
|
|
132
104
|
|
|
133
105
|
// Create landmark aria props
|
|
134
106
|
const landmarkAria = createLandmark(
|
|
135
107
|
{
|
|
136
|
-
get role() {
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
get
|
|
140
|
-
|
|
108
|
+
get role() {
|
|
109
|
+
return ariaProps.role;
|
|
110
|
+
},
|
|
111
|
+
get "aria-label"() {
|
|
112
|
+
return ariaProps["aria-label"];
|
|
113
|
+
},
|
|
114
|
+
get "aria-labelledby"() {
|
|
115
|
+
return ariaProps["aria-labelledby"];
|
|
116
|
+
},
|
|
117
|
+
get id() {
|
|
118
|
+
return ariaProps.id;
|
|
119
|
+
},
|
|
120
|
+
get focus() {
|
|
121
|
+
return ariaProps.focus;
|
|
122
|
+
},
|
|
141
123
|
},
|
|
142
|
-
ref
|
|
124
|
+
ref,
|
|
143
125
|
);
|
|
144
126
|
|
|
145
|
-
// Render props values
|
|
146
127
|
const renderValues = createMemo<LandmarkRenderProps>(() => ({
|
|
147
128
|
role: ariaProps.role,
|
|
148
129
|
}));
|
|
149
130
|
|
|
150
|
-
// Resolve class
|
|
151
131
|
const resolvedClass = createMemo(() => {
|
|
152
132
|
const cls = local.class;
|
|
153
|
-
if (typeof cls ===
|
|
133
|
+
if (typeof cls === "function") {
|
|
154
134
|
return cls(renderValues());
|
|
155
135
|
}
|
|
156
136
|
return cls ?? `solidaria-Landmark solidaria-Landmark--${ariaProps.role}`;
|
|
157
137
|
});
|
|
158
138
|
|
|
159
|
-
// Resolve style
|
|
160
139
|
const resolvedStyle = createMemo(() => {
|
|
161
140
|
const style = local.style;
|
|
162
|
-
if (typeof style ===
|
|
141
|
+
if (typeof style === "function") {
|
|
163
142
|
return style(renderValues());
|
|
164
143
|
}
|
|
165
144
|
return style;
|
|
166
145
|
});
|
|
167
146
|
|
|
168
|
-
// Filter DOM props
|
|
169
147
|
const domProps = createMemo(() => filterDOMProps(ariaProps, { global: true }));
|
|
170
148
|
|
|
171
149
|
return (
|
|
@@ -183,10 +161,6 @@ export function Landmark(props: LandmarkProps): JSX.Element {
|
|
|
183
161
|
);
|
|
184
162
|
}
|
|
185
163
|
|
|
186
|
-
// ============================================
|
|
187
|
-
// LANDMARK CONTROLLER EXPORT
|
|
188
|
-
// ============================================
|
|
189
|
-
|
|
190
164
|
/**
|
|
191
165
|
* Returns a controller for programmatic landmark navigation.
|
|
192
166
|
*
|