@proyecto-viviana/solidaria 0.2.4 → 0.2.8
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/dist/actiongroup/createActionGroup.d.ts +29 -0
- package/dist/actiongroup/createActionGroup.d.ts.map +1 -0
- package/dist/actiongroup/index.d.ts +2 -0
- package/dist/actiongroup/index.d.ts.map +1 -0
- package/dist/autocomplete/createAutocomplete.d.ts +6 -2
- package/dist/autocomplete/createAutocomplete.d.ts.map +1 -1
- package/dist/breadcrumbs/createBreadcrumbs.d.ts +2 -0
- package/dist/breadcrumbs/createBreadcrumbs.d.ts.map +1 -1
- package/dist/button/createToggleButtonGroup.d.ts +32 -0
- package/dist/button/createToggleButtonGroup.d.ts.map +1 -0
- package/dist/button/index.d.ts +2 -0
- package/dist/button/index.d.ts.map +1 -1
- package/dist/calendar/createCalendarCell.d.ts +2 -0
- package/dist/calendar/createCalendarCell.d.ts.map +1 -1
- package/dist/calendar/createCalendarGrid.d.ts.map +1 -1
- package/dist/calendar/createRangeCalendarCell.d.ts +3 -1
- package/dist/calendar/createRangeCalendarCell.d.ts.map +1 -1
- package/dist/checkbox/createCheckboxGroup.d.ts +5 -1
- package/dist/checkbox/createCheckboxGroup.d.ts.map +1 -1
- package/dist/collections/index.d.ts +56 -0
- package/dist/collections/index.d.ts.map +1 -0
- package/dist/color/createColorArea.d.ts.map +1 -1
- package/dist/color/createColorSlider.d.ts.map +1 -1
- package/dist/color/createColorWheel.d.ts.map +1 -1
- package/dist/combobox/createComboBox.d.ts +6 -0
- package/dist/combobox/createComboBox.d.ts.map +1 -1
- package/dist/datepicker/createDatePicker.d.ts +6 -0
- package/dist/datepicker/createDatePicker.d.ts.map +1 -1
- package/dist/datepicker/createDateRangePicker.d.ts +40 -0
- package/dist/datepicker/createDateRangePicker.d.ts.map +1 -0
- package/dist/datepicker/createDateSegment.d.ts +1 -1
- package/dist/datepicker/createDateSegment.d.ts.map +1 -1
- package/dist/datepicker/createTimeSegment.d.ts +29 -0
- package/dist/datepicker/createTimeSegment.d.ts.map +1 -0
- package/dist/datepicker/index.d.ts +2 -0
- package/dist/datepicker/index.d.ts.map +1 -1
- package/dist/disclosure/createDisclosureGroup.d.ts +2 -1
- package/dist/disclosure/createDisclosureGroup.d.ts.map +1 -1
- package/dist/dnd/createDrag.d.ts.map +1 -1
- package/dist/dnd/createDraggableCollection.d.ts +4 -0
- package/dist/dnd/createDraggableCollection.d.ts.map +1 -1
- package/dist/dnd/createDraggableItem.d.ts.map +1 -1
- package/dist/dnd/createDrop.d.ts.map +1 -1
- package/dist/dnd/createDroppableCollection.d.ts +32 -1
- package/dist/dnd/createDroppableCollection.d.ts.map +1 -1
- package/dist/dnd/createDroppableItem.d.ts.map +1 -1
- package/dist/dnd/index.d.ts +1 -1
- package/dist/dnd/index.d.ts.map +1 -1
- package/dist/grid/createGrid.d.ts.map +1 -1
- package/dist/gridlist/createGridList.d.ts.map +1 -1
- package/dist/index.d.ts +6 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4659 -3452
- package/dist/index.js.map +1 -7
- package/dist/index.ssr.js +4659 -3452
- package/dist/index.ssr.js.map +1 -7
- package/dist/interactions/createFocus.d.ts.map +1 -1
- package/dist/interactions/createFocusWithin.d.ts.map +1 -1
- package/dist/link/createLink.d.ts +10 -0
- package/dist/link/createLink.d.ts.map +1 -1
- package/dist/listbox/createListBox.d.ts +1 -0
- package/dist/listbox/createListBox.d.ts.map +1 -1
- package/dist/listbox/createOption.d.ts.map +1 -1
- package/dist/menu/createMenu.d.ts +1 -0
- package/dist/menu/createMenu.d.ts.map +1 -1
- package/dist/meter/createMeter.d.ts.map +1 -1
- package/dist/numberfield/createNumberField.d.ts +18 -0
- package/dist/numberfield/createNumberField.d.ts.map +1 -1
- package/dist/overlays/createModal.d.ts +16 -0
- package/dist/overlays/createModal.d.ts.map +1 -1
- package/dist/overlays/createOverlay.d.ts.map +1 -1
- package/dist/overlays/index.d.ts +1 -1
- package/dist/overlays/index.d.ts.map +1 -1
- package/dist/popover/createOverlayPosition.d.ts.map +1 -1
- package/dist/popover/createPopover.d.ts.map +1 -1
- package/dist/progress/createProgressBar.d.ts.map +1 -1
- package/dist/radio/createRadioGroup.d.ts +2 -2
- package/dist/radio/createRadioGroup.d.ts.map +1 -1
- package/dist/searchfield/createSearchField.d.ts.map +1 -1
- package/dist/select/createHiddenSelect.d.ts.map +1 -1
- package/dist/select/createSelect.d.ts.map +1 -1
- package/dist/slider/createSlider.d.ts.map +1 -1
- package/dist/table/createTable.d.ts.map +1 -1
- package/dist/tabs/createTabs.d.ts +1 -1
- package/dist/tabs/createTabs.d.ts.map +1 -1
- package/dist/tag/createTag.d.ts.map +1 -1
- package/dist/tag/createTagGroup.d.ts.map +1 -1
- package/dist/toast/createToast.d.ts +4 -0
- package/dist/toast/createToast.d.ts.map +1 -1
- package/dist/toast/createToastRegion.d.ts.map +1 -1
- package/dist/toolbar/createToolbar.d.ts.map +1 -1
- package/dist/tooltip/createTooltipTrigger.d.ts.map +1 -1
- package/dist/tree/createTree.d.ts.map +1 -1
- package/dist/tree/createTreeItem.d.ts.map +1 -1
- package/dist/tree/types.d.ts +4 -0
- package/dist/tree/types.d.ts.map +1 -1
- package/dist/utils/env.d.ts +1 -1
- package/dist/utils/env.d.ts.map +1 -1
- package/dist/utils/platform.d.ts.map +1 -1
- package/dist/visually-hidden/createVisuallyHidden.d.ts.map +1 -1
- package/package.json +8 -6
- package/src/actiongroup/createActionGroup.ts +324 -0
- package/src/actiongroup/index.ts +8 -0
- package/src/autocomplete/createAutocomplete.ts +32 -9
- package/src/breadcrumbs/createBreadcrumbs.ts +10 -15
- package/src/button/createButton.ts +1 -1
- package/src/button/createToggleButtonGroup.ts +128 -0
- package/src/button/index.ts +9 -0
- package/src/calendar/createCalendarCell.ts +6 -4
- package/src/calendar/createCalendarGrid.ts +27 -18
- package/src/calendar/createRangeCalendarCell.ts +26 -9
- package/src/checkbox/createCheckboxGroup.ts +21 -4
- package/src/collections/index.ts +242 -0
- package/src/color/createColorArea.ts +380 -314
- package/src/color/createColorField.ts +137 -137
- package/src/color/createColorSlider.ts +286 -197
- package/src/color/createColorSwatch.ts +40 -40
- package/src/color/createColorWheel.ts +218 -208
- package/src/color/index.ts +24 -24
- package/src/color/types.ts +116 -116
- package/src/combobox/createComboBox.ts +670 -647
- package/src/combobox/index.ts +6 -6
- package/src/datepicker/createDatePicker.ts +54 -16
- package/src/datepicker/createDateRangePicker.ts +246 -0
- package/src/datepicker/createDateSegment.ts +185 -31
- package/src/datepicker/createTimeSegment.ts +370 -0
- package/src/datepicker/index.ts +14 -0
- package/src/dialog/createDialog.ts +120 -120
- package/src/dialog/index.ts +2 -2
- package/src/dialog/types.ts +19 -19
- package/src/disclosure/createDisclosureGroup.ts +5 -2
- package/src/dnd/createDrag.ts +224 -209
- package/src/dnd/createDraggableCollection.ts +96 -63
- package/src/dnd/createDraggableItem.ts +259 -243
- package/src/dnd/createDrop.ts +322 -321
- package/src/dnd/createDroppableCollection.ts +682 -293
- package/src/dnd/createDroppableItem.ts +215 -213
- package/src/dnd/index.ts +55 -47
- package/src/dnd/types.ts +89 -89
- package/src/dnd/utils.ts +294 -294
- package/src/focus/createAutoFocus.ts +321 -321
- package/src/focus/createFocusRestore.ts +313 -313
- package/src/focus/createVirtualFocus.ts +396 -396
- package/src/form/createFormValidation.ts +224 -224
- package/src/form/index.ts +11 -11
- package/src/grid/createGrid.ts +3 -1
- package/src/gridlist/createGridList.ts +16 -0
- package/src/gridlist/createGridListItem.ts +1 -1
- package/src/i18n/NumberFormatter.ts +266 -266
- package/src/i18n/createCollator.ts +79 -79
- package/src/i18n/createDateFormatter.ts +83 -83
- package/src/i18n/createFilter.ts +131 -131
- package/src/i18n/createNumberFormatter.ts +52 -52
- package/src/i18n/index.ts +40 -40
- package/src/i18n/locale.tsx +188 -188
- package/src/i18n/utils.ts +99 -99
- package/src/index.ts +51 -0
- package/src/interactions/createFocus.ts +6 -5
- package/src/interactions/createFocusWithin.ts +6 -5
- package/src/interactions/createLongPress.ts +174 -174
- package/src/interactions/createMove.ts +289 -289
- package/src/interactions/createPress.ts +5 -5
- package/src/landmark/createLandmark.ts +377 -377
- package/src/landmark/index.ts +8 -8
- package/src/link/createLink.ts +23 -8
- package/src/listbox/createListBox.ts +308 -269
- package/src/listbox/createOption.ts +162 -151
- package/src/listbox/index.ts +12 -12
- package/src/live-announcer/announce.ts +322 -322
- package/src/live-announcer/index.ts +9 -9
- package/src/menu/createMenu.ts +405 -396
- package/src/menu/createMenuItem.ts +149 -149
- package/src/menu/createMenuTrigger.ts +88 -88
- package/src/menu/index.ts +18 -18
- package/src/meter/createMeter.ts +1 -6
- package/src/numberfield/createNumberField.ts +311 -268
- package/src/numberfield/index.ts +5 -5
- package/src/overlays/ariaHideOutside.ts +219 -219
- package/src/overlays/createInteractOutside.ts +149 -149
- package/src/overlays/createModal.tsx +238 -202
- package/src/overlays/createOverlay.ts +165 -155
- package/src/overlays/createOverlayTrigger.ts +85 -85
- package/src/overlays/createPreventScroll.ts +266 -266
- package/src/overlays/index.ts +48 -44
- package/src/popover/calculatePosition.ts +6 -6
- package/src/popover/createOverlayPosition.ts +7 -4
- package/src/popover/createPopover.ts +21 -7
- package/src/progress/createProgressBar.ts +6 -1
- package/src/radio/createRadioGroup.ts +88 -14
- package/src/searchfield/createSearchField.ts +241 -186
- package/src/searchfield/index.ts +2 -2
- package/src/select/createHiddenSelect.tsx +263 -236
- package/src/select/createSelect.ts +373 -395
- package/src/select/index.ts +14 -14
- package/src/slider/createSlider.ts +364 -349
- package/src/slider/index.ts +2 -2
- package/src/ssr/index.tsx +370 -370
- package/src/table/createTable.ts +3 -1
- package/src/table/createTableColumnHeader.ts +1 -1
- package/src/table/createTableRow.ts +1 -1
- package/src/tabs/createTabs.ts +80 -51
- package/src/tag/createTag.ts +135 -6
- package/src/tag/createTagGroup.ts +7 -2
- package/src/toast/createToast.ts +8 -2
- package/src/toast/createToastRegion.ts +0 -1
- package/src/toolbar/createToolbar.ts +75 -1
- package/src/tooltip/createTooltip.ts +79 -79
- package/src/tooltip/createTooltipTrigger.ts +226 -222
- package/src/tooltip/index.ts +6 -6
- package/src/tree/createTree.ts +261 -246
- package/src/tree/createTreeItem.ts +282 -233
- package/src/tree/createTreeSelectionCheckbox.ts +68 -68
- package/src/tree/index.ts +16 -16
- package/src/tree/types.ts +91 -87
- package/src/utils/env.ts +55 -54
- package/src/utils/platform.ts +16 -6
- package/src/visually-hidden/createVisuallyHidden.ts +139 -124
- package/src/visually-hidden/index.ts +6 -6
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* Based on @react-aria/calendar useCalendarGrid
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import { createMemo
|
|
8
|
+
import { createMemo } from 'solid-js';
|
|
9
9
|
import { type MaybeAccessor } from '../utils/reactivity';
|
|
10
10
|
import type { CalendarState, CalendarDate } from '@proyecto-viviana/solid-stately';
|
|
11
11
|
|
|
@@ -47,17 +47,32 @@ export function createCalendarGrid<T extends CalendarState>(
|
|
|
47
47
|
const weekDays = createMemo(() => state.weekDays());
|
|
48
48
|
|
|
49
49
|
// Handle keyboard navigation
|
|
50
|
+
const isRTL = (): boolean => {
|
|
51
|
+
const element = ref?.();
|
|
52
|
+
const scopedDirection = element?.closest('[dir]')?.getAttribute('dir');
|
|
53
|
+
const documentDirection = typeof document !== 'undefined' ? document.dir : '';
|
|
54
|
+
return (scopedDirection ?? documentDirection) === 'rtl';
|
|
55
|
+
};
|
|
56
|
+
|
|
50
57
|
const handleKeyDown = (e: KeyboardEvent) => {
|
|
51
58
|
if (state.isDisabled()) return;
|
|
52
59
|
|
|
53
60
|
switch (e.key) {
|
|
54
61
|
case 'ArrowLeft':
|
|
55
62
|
e.preventDefault();
|
|
56
|
-
|
|
63
|
+
if (isRTL()) {
|
|
64
|
+
state.focusNextDay();
|
|
65
|
+
} else {
|
|
66
|
+
state.focusPreviousDay();
|
|
67
|
+
}
|
|
57
68
|
break;
|
|
58
69
|
case 'ArrowRight':
|
|
59
70
|
e.preventDefault();
|
|
60
|
-
|
|
71
|
+
if (isRTL()) {
|
|
72
|
+
state.focusPreviousDay();
|
|
73
|
+
} else {
|
|
74
|
+
state.focusNextDay();
|
|
75
|
+
}
|
|
61
76
|
break;
|
|
62
77
|
case 'ArrowUp':
|
|
63
78
|
e.preventDefault();
|
|
@@ -96,20 +111,15 @@ export function createCalendarGrid<T extends CalendarState>(
|
|
|
96
111
|
e.preventDefault();
|
|
97
112
|
state.selectFocusedDate();
|
|
98
113
|
break;
|
|
114
|
+
case 'Escape':
|
|
115
|
+
if ('setAnchorDate' in state && typeof state.setAnchorDate === 'function') {
|
|
116
|
+
e.preventDefault();
|
|
117
|
+
state.setAnchorDate(null);
|
|
118
|
+
}
|
|
119
|
+
break;
|
|
99
120
|
}
|
|
100
121
|
};
|
|
101
122
|
|
|
102
|
-
// Register keyboard listener
|
|
103
|
-
onMount(() => {
|
|
104
|
-
const element = ref?.();
|
|
105
|
-
if (element) {
|
|
106
|
-
element.addEventListener('keydown', handleKeyDown);
|
|
107
|
-
onCleanup(() => {
|
|
108
|
-
element.removeEventListener('keydown', handleKeyDown);
|
|
109
|
-
});
|
|
110
|
-
}
|
|
111
|
-
});
|
|
112
|
-
|
|
113
123
|
// Grid props
|
|
114
124
|
const gridProps = createMemo(() => ({
|
|
115
125
|
role: 'grid',
|
|
@@ -121,10 +131,9 @@ export function createCalendarGrid<T extends CalendarState>(
|
|
|
121
131
|
onKeyDown: handleKeyDown,
|
|
122
132
|
}));
|
|
123
133
|
|
|
124
|
-
// Header props
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
}));
|
|
134
|
+
// Header props are intentionally empty. Consumers render this on <thead>,
|
|
135
|
+
// which already has correct table semantics.
|
|
136
|
+
const headerProps = createMemo(() => ({}));
|
|
128
137
|
|
|
129
138
|
return {
|
|
130
139
|
get gridProps() {
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* Based on @react-aria/calendar useCalendarCell (with range support)
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import { createSignal, createMemo } from 'solid-js';
|
|
8
|
+
import { createSignal, createMemo, createEffect } from 'solid-js';
|
|
9
9
|
import { access, type MaybeAccessor } from '../utils/reactivity';
|
|
10
10
|
import type { RangeCalendarState, CalendarDate, DateValue } from '@proyecto-viviana/solid-stately';
|
|
11
11
|
import { isToday as isTodayUtil, DateFormatter, getLocalTimeZone } from '@internationalized/date';
|
|
@@ -19,6 +19,8 @@ export interface AriaRangeCalendarCellProps {
|
|
|
19
19
|
date: DateValue;
|
|
20
20
|
/** Whether the cell is disabled. */
|
|
21
21
|
isDisabled?: boolean;
|
|
22
|
+
/** Whether the date is outside the current month grid. */
|
|
23
|
+
isOutsideMonth?: boolean;
|
|
22
24
|
}
|
|
23
25
|
|
|
24
26
|
export interface RangeCalendarCellAria {
|
|
@@ -58,7 +60,7 @@ export interface RangeCalendarCellAria {
|
|
|
58
60
|
export function createRangeCalendarCell<T extends RangeCalendarState>(
|
|
59
61
|
props: MaybeAccessor<AriaRangeCalendarCellProps>,
|
|
60
62
|
state: T,
|
|
61
|
-
|
|
63
|
+
ref?: () => HTMLElement | null
|
|
62
64
|
): RangeCalendarCellAria {
|
|
63
65
|
const getProps = () => access(props);
|
|
64
66
|
const [isPressed, setIsPressed] = createSignal(false);
|
|
@@ -76,7 +78,9 @@ export function createRangeCalendarCell<T extends RangeCalendarState>(
|
|
|
76
78
|
return getProps().isDisabled || state.isCellDisabled(date());
|
|
77
79
|
});
|
|
78
80
|
const isUnavailable = createMemo(() => state.isCellUnavailable(date()));
|
|
79
|
-
const isOutsideMonth = createMemo(() =>
|
|
81
|
+
const isOutsideMonth = createMemo(() => {
|
|
82
|
+
return getProps().isOutsideMonth ?? state.isOutsideVisibleRange(date());
|
|
83
|
+
});
|
|
80
84
|
const isToday = createMemo(() => isTodayUtil(date(), timeZone));
|
|
81
85
|
|
|
82
86
|
// Format the date for display
|
|
@@ -84,17 +88,20 @@ export function createRangeCalendarCell<T extends RangeCalendarState>(
|
|
|
84
88
|
return date().day.toString();
|
|
85
89
|
});
|
|
86
90
|
|
|
87
|
-
// Handle
|
|
88
|
-
|
|
91
|
+
// Handle pointer down - selection on pointerdown avoids losing selection when
|
|
92
|
+
// hover/focus updates re-render cells before click fires.
|
|
93
|
+
const handlePointerDown = (e: PointerEvent) => {
|
|
89
94
|
if (!isDisabled() && !isUnavailable()) {
|
|
95
|
+
setIsPressed(true);
|
|
90
96
|
state.selectDate(date());
|
|
97
|
+
e.preventDefault();
|
|
91
98
|
}
|
|
92
99
|
};
|
|
93
100
|
|
|
94
|
-
// Handle
|
|
95
|
-
const
|
|
101
|
+
// Handle click for keyboard activation (Enter/Space).
|
|
102
|
+
const handleClick = () => {
|
|
96
103
|
if (!isDisabled() && !isUnavailable()) {
|
|
97
|
-
|
|
104
|
+
state.selectDate(date());
|
|
98
105
|
}
|
|
99
106
|
};
|
|
100
107
|
|
|
@@ -109,6 +116,14 @@ export function createRangeCalendarCell<T extends RangeCalendarState>(
|
|
|
109
116
|
}
|
|
110
117
|
};
|
|
111
118
|
|
|
119
|
+
// Keep DOM focus synchronized with focused date updates.
|
|
120
|
+
createEffect(() => {
|
|
121
|
+
const element = ref?.();
|
|
122
|
+
if (element && isFocused()) {
|
|
123
|
+
element.focus();
|
|
124
|
+
}
|
|
125
|
+
});
|
|
126
|
+
|
|
112
127
|
// Cell props (for the td element)
|
|
113
128
|
const cellProps = createMemo(() => ({
|
|
114
129
|
role: 'gridcell',
|
|
@@ -139,7 +154,9 @@ export function createRangeCalendarCell<T extends RangeCalendarState>(
|
|
|
139
154
|
onPointerLeave: handlePointerUp,
|
|
140
155
|
onPointerEnter: handlePointerEnter,
|
|
141
156
|
onFocus: () => {
|
|
142
|
-
state.
|
|
157
|
+
if (!state.isCellFocused(d)) {
|
|
158
|
+
state.setFocusedDate(d);
|
|
159
|
+
}
|
|
143
160
|
state.setFocused(true);
|
|
144
161
|
},
|
|
145
162
|
};
|
|
@@ -13,7 +13,7 @@ import { createFocusWithin } from '../interactions/createFocusWithin';
|
|
|
13
13
|
import { filterDOMProps } from '../utils/filterDOMProps';
|
|
14
14
|
import { mergeProps } from '../utils/mergeProps';
|
|
15
15
|
import { type MaybeAccessor, access } from '../utils/reactivity';
|
|
16
|
-
import { type CheckboxGroupState, type CheckboxGroupProps } from '@proyecto-viviana/solid-stately';
|
|
16
|
+
import { type CheckboxGroupState, type CheckboxGroupProps, type ValidityState } from '@proyecto-viviana/solid-stately';
|
|
17
17
|
|
|
18
18
|
// ============================================
|
|
19
19
|
// TYPES
|
|
@@ -45,6 +45,10 @@ export interface CheckboxGroupAria {
|
|
|
45
45
|
errorMessageProps: JSX.HTMLAttributes<HTMLElement>;
|
|
46
46
|
/** Whether the checkbox group is invalid. */
|
|
47
47
|
isInvalid: boolean;
|
|
48
|
+
/** Validation errors, if any. */
|
|
49
|
+
validationErrors: string[];
|
|
50
|
+
/** Validation details, if any. */
|
|
51
|
+
validationDetails: ValidityState;
|
|
48
52
|
}
|
|
49
53
|
|
|
50
54
|
// WeakMap to share data between checkbox group and checkbox group items
|
|
@@ -75,8 +79,15 @@ export function createCheckboxGroup(
|
|
|
75
79
|
state: CheckboxGroupState
|
|
76
80
|
): CheckboxGroupAria {
|
|
77
81
|
const getProps = () => access(props);
|
|
82
|
+
const displayValidation = () => state.displayValidation();
|
|
83
|
+
const validationErrors = () => displayValidation().validationErrors;
|
|
84
|
+
const validationDetails = () => displayValidation().validationDetails;
|
|
78
85
|
|
|
79
|
-
const isInvalid = () =>
|
|
86
|
+
const isInvalid = () => displayValidation().isInvalid;
|
|
87
|
+
const fallbackErrorMessage = () => {
|
|
88
|
+
const errors = validationErrors();
|
|
89
|
+
return errors.length > 0 ? errors : undefined;
|
|
90
|
+
};
|
|
80
91
|
|
|
81
92
|
// Use field for label association
|
|
82
93
|
const { labelProps, fieldProps, descriptionProps, errorMessageProps } = createField({
|
|
@@ -86,7 +97,7 @@ export function createCheckboxGroup(
|
|
|
86
97
|
get 'aria-describedby'() { return getProps()['aria-describedby']; },
|
|
87
98
|
get 'aria-details'() { return getProps()['aria-details']; },
|
|
88
99
|
get description() { return getProps().description; },
|
|
89
|
-
get errorMessage() { return getProps().errorMessage ?? (
|
|
100
|
+
get errorMessage() { return getProps().errorMessage ?? fallbackErrorMessage(); },
|
|
90
101
|
get isInvalid() { return isInvalid(); },
|
|
91
102
|
// Checkbox group is not an HTML input element so it
|
|
92
103
|
// shouldn't be labeled by a <label> element.
|
|
@@ -99,7 +110,7 @@ export function createCheckboxGroup(
|
|
|
99
110
|
form: getProps().form,
|
|
100
111
|
descriptionId: descriptionProps.id,
|
|
101
112
|
errorMessageId: errorMessageProps.id,
|
|
102
|
-
validationBehavior: 'aria',
|
|
113
|
+
validationBehavior: getProps().validationBehavior ?? 'aria',
|
|
103
114
|
});
|
|
104
115
|
|
|
105
116
|
// Filter DOM props
|
|
@@ -133,5 +144,11 @@ export function createCheckboxGroup(
|
|
|
133
144
|
get isInvalid() {
|
|
134
145
|
return isInvalid();
|
|
135
146
|
},
|
|
147
|
+
get validationErrors() {
|
|
148
|
+
return validationErrors();
|
|
149
|
+
},
|
|
150
|
+
get validationDetails() {
|
|
151
|
+
return validationDetails();
|
|
152
|
+
},
|
|
136
153
|
};
|
|
137
154
|
}
|
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
import { createContext, createMemo, useContext, type Accessor } from 'solid-js';
|
|
2
|
+
import { access, type MaybeAccessor } from '../utils';
|
|
3
|
+
import {
|
|
4
|
+
ListCollection,
|
|
5
|
+
type Collection as StatelyCollection,
|
|
6
|
+
type CollectionNode as StatelyCollectionNode,
|
|
7
|
+
type Key,
|
|
8
|
+
} from '@proyecto-viviana/solid-stately';
|
|
9
|
+
|
|
10
|
+
export interface CachedChildrenOptions<T> {
|
|
11
|
+
items?: Iterable<T>;
|
|
12
|
+
children?: ((item: T) => unknown) | unknown;
|
|
13
|
+
dependencies?: ReadonlyArray<unknown>;
|
|
14
|
+
getKey?: (item: T) => Key;
|
|
15
|
+
idScope?: Key;
|
|
16
|
+
addIdAndValue?: boolean;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface CollectionBuilderProps<T> extends CachedChildrenOptions<T> {}
|
|
20
|
+
|
|
21
|
+
export interface CollectionProps<T> extends CollectionBuilderProps<T> {}
|
|
22
|
+
|
|
23
|
+
export interface CollectionCompatNode<TProps = unknown, TValue = unknown> {
|
|
24
|
+
key: Key;
|
|
25
|
+
value: TValue;
|
|
26
|
+
props: TProps;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const COLLECTION_NODE_PROP = '__collectionNode';
|
|
30
|
+
|
|
31
|
+
function applyCollectionMetadata<T>(
|
|
32
|
+
rendered: unknown,
|
|
33
|
+
item: T,
|
|
34
|
+
key: Key,
|
|
35
|
+
addIdAndValue: boolean
|
|
36
|
+
): unknown {
|
|
37
|
+
if (typeof rendered !== 'object' || rendered === null) return rendered;
|
|
38
|
+
const next = {
|
|
39
|
+
...(rendered as Record<string, unknown>),
|
|
40
|
+
key,
|
|
41
|
+
[COLLECTION_NODE_PROP]: {
|
|
42
|
+
key,
|
|
43
|
+
value: item,
|
|
44
|
+
props: rendered as Record<string, unknown>,
|
|
45
|
+
} satisfies CollectionCompatNode<Record<string, unknown>, T>,
|
|
46
|
+
} as Record<string, unknown>;
|
|
47
|
+
if (addIdAndValue) {
|
|
48
|
+
next.id = key;
|
|
49
|
+
next.value = item;
|
|
50
|
+
}
|
|
51
|
+
return next;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Compatibility helper mirroring React Aria collections API shape.
|
|
56
|
+
* For Solid, this is a lightweight mapper over item arrays.
|
|
57
|
+
*/
|
|
58
|
+
export function CollectionBuilder<T>(props: CollectionBuilderProps<T>): unknown {
|
|
59
|
+
if (typeof props.children === 'function' && props.items) {
|
|
60
|
+
const children = props.children as (item: T) => unknown;
|
|
61
|
+
const mapped: unknown[] = [];
|
|
62
|
+
let index = 0;
|
|
63
|
+
|
|
64
|
+
for (const item of props.items) {
|
|
65
|
+
const baseKey = getResolvedItemKey(item, index, props.getKey);
|
|
66
|
+
if (baseKey == null) {
|
|
67
|
+
throw new Error('Could not determine key for item');
|
|
68
|
+
}
|
|
69
|
+
const key = props.idScope != null ? `${String(props.idScope)}:${String(baseKey)}` : baseKey;
|
|
70
|
+
const rendered = children(item);
|
|
71
|
+
const withMeta = applyCollectionMetadata(
|
|
72
|
+
rendered,
|
|
73
|
+
item,
|
|
74
|
+
key,
|
|
75
|
+
props.addIdAndValue ?? false
|
|
76
|
+
);
|
|
77
|
+
mapped.push(withMeta);
|
|
78
|
+
index += 1;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return mapped;
|
|
82
|
+
}
|
|
83
|
+
return props.children ?? null;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export function Collection<T>(props: CollectionProps<T>): unknown {
|
|
87
|
+
return CollectionBuilder({
|
|
88
|
+
...props,
|
|
89
|
+
addIdAndValue: props.addIdAndValue ?? true,
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Identity helper retained for API compatibility.
|
|
95
|
+
*/
|
|
96
|
+
export function createLeafComponent<TProps>(
|
|
97
|
+
component: (props: TProps, node?: CollectionCompatNode<TProps, unknown>) => unknown
|
|
98
|
+
): (props: TProps) => unknown {
|
|
99
|
+
return (props: TProps) => {
|
|
100
|
+
const node = (props as Record<string, unknown>)[COLLECTION_NODE_PROP] as CollectionCompatNode<TProps, unknown> | undefined;
|
|
101
|
+
if (component.length >= 2 && !node) {
|
|
102
|
+
throw new Error(`${component.name || 'Component'} cannot be rendered outside a collection.`);
|
|
103
|
+
}
|
|
104
|
+
return component(props, node);
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Identity helper retained for API compatibility.
|
|
110
|
+
*/
|
|
111
|
+
export function createBranchComponent<TProps>(
|
|
112
|
+
component: (props: TProps, node?: CollectionCompatNode<TProps, unknown>) => unknown
|
|
113
|
+
): (props: TProps) => unknown {
|
|
114
|
+
return (props: TProps) => {
|
|
115
|
+
const node = (props as Record<string, unknown>)[COLLECTION_NODE_PROP] as CollectionCompatNode<TProps, unknown> | undefined;
|
|
116
|
+
if (component.length >= 2 && !node) {
|
|
117
|
+
throw new Error(`${component.name || 'Component'} cannot be rendered outside a collection.`);
|
|
118
|
+
}
|
|
119
|
+
return component(props, node);
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const HiddenContext = createContext<Accessor<boolean>>(() => false);
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Wraps a component and suppresses rendering when the hidden context is true.
|
|
127
|
+
*/
|
|
128
|
+
export function createHideableComponent<TProps>(component: (props: TProps) => unknown): (props: TProps) => unknown {
|
|
129
|
+
return (props: TProps) => {
|
|
130
|
+
const isHidden = useIsHidden();
|
|
131
|
+
if (isHidden()) return null;
|
|
132
|
+
return component(props);
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
export function useIsHidden(): Accessor<boolean> {
|
|
137
|
+
return useContext(HiddenContext) ?? (() => false);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Memoized item renderer for dynamic child mapping.
|
|
142
|
+
*/
|
|
143
|
+
export function useCachedChildren<T>(options: MaybeAccessor<CachedChildrenOptions<T>>): Accessor<unknown[]> {
|
|
144
|
+
let objectCache = new WeakMap<object, unknown>();
|
|
145
|
+
const primitiveCache = new Map<Key, unknown>();
|
|
146
|
+
let lastDependencies: ReadonlyArray<unknown> | undefined;
|
|
147
|
+
let lastIdScope: Key | undefined;
|
|
148
|
+
let lastAddIdAndValue: boolean | undefined;
|
|
149
|
+
let lastGetKey: ((item: T) => Key) | undefined;
|
|
150
|
+
|
|
151
|
+
const clearCaches = (): void => {
|
|
152
|
+
objectCache = new WeakMap<object, unknown>();
|
|
153
|
+
primitiveCache.clear();
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
const getItemKey = (item: T, index: number, getKey?: (item: T) => Key): Key | undefined => {
|
|
157
|
+
return getResolvedItemKey(item, index, getKey);
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
return createMemo(() => {
|
|
161
|
+
const resolved = access(options);
|
|
162
|
+
const resolvedAddIdAndValue = resolved.addIdAndValue ?? false;
|
|
163
|
+
const resolvedGetKey = resolved.getKey;
|
|
164
|
+
const resolvedIdScope = resolved.idScope;
|
|
165
|
+
|
|
166
|
+
if (resolved.dependencies && resolved.dependencies !== lastDependencies) {
|
|
167
|
+
clearCaches();
|
|
168
|
+
lastDependencies = resolved.dependencies;
|
|
169
|
+
}
|
|
170
|
+
if (
|
|
171
|
+
resolvedIdScope !== lastIdScope ||
|
|
172
|
+
resolvedAddIdAndValue !== lastAddIdAndValue ||
|
|
173
|
+
resolvedGetKey !== lastGetKey
|
|
174
|
+
) {
|
|
175
|
+
clearCaches();
|
|
176
|
+
lastIdScope = resolvedIdScope;
|
|
177
|
+
lastAddIdAndValue = resolvedAddIdAndValue;
|
|
178
|
+
lastGetKey = resolvedGetKey;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
if (typeof resolved.children === 'function' && resolved.items) {
|
|
182
|
+
const children = resolved.children as (item: T) => unknown;
|
|
183
|
+
const rendered: unknown[] = [];
|
|
184
|
+
let index = 0;
|
|
185
|
+
|
|
186
|
+
for (const item of resolved.items) {
|
|
187
|
+
const baseKey = getItemKey(item, index, resolvedGetKey);
|
|
188
|
+
if (baseKey == null) {
|
|
189
|
+
throw new Error('Could not determine key for item');
|
|
190
|
+
}
|
|
191
|
+
const key = resolvedIdScope != null ? `${String(resolvedIdScope)}:${String(baseKey)}` : baseKey;
|
|
192
|
+
let child: unknown;
|
|
193
|
+
|
|
194
|
+
if (typeof item === 'object' && item !== null) {
|
|
195
|
+
child = objectCache.get(item as object);
|
|
196
|
+
if (child === undefined) {
|
|
197
|
+
child = applyCollectionMetadata(children(item), item, key, resolvedAddIdAndValue);
|
|
198
|
+
objectCache.set(item as object, child);
|
|
199
|
+
}
|
|
200
|
+
} else {
|
|
201
|
+
if (primitiveCache.has(key)) {
|
|
202
|
+
child = primitiveCache.get(key);
|
|
203
|
+
} else {
|
|
204
|
+
child = applyCollectionMetadata(children(item), item, key, resolvedAddIdAndValue);
|
|
205
|
+
primitiveCache.set(key, child);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
rendered.push(child);
|
|
210
|
+
index += 1;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
return rendered;
|
|
214
|
+
}
|
|
215
|
+
const child = resolved.children;
|
|
216
|
+
return child == null ? [] : [child];
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* Minimal BaseCollection compatibility class.
|
|
222
|
+
*/
|
|
223
|
+
export class BaseCollection<T = unknown> extends ListCollection<T> {}
|
|
224
|
+
|
|
225
|
+
export type CollectionNode<T = unknown> = StatelyCollectionNode<T>;
|
|
226
|
+
export type ItemNode<T = unknown> = StatelyCollectionNode<T>;
|
|
227
|
+
export type SectionNode<T = unknown> = StatelyCollectionNode<T>;
|
|
228
|
+
export type FilterableNode<T = unknown> = StatelyCollectionNode<T>;
|
|
229
|
+
export type LoaderNode<T = unknown> = StatelyCollectionNode<T>;
|
|
230
|
+
export type HeaderNode<T = unknown> = StatelyCollectionNode<T>;
|
|
231
|
+
export type CollectionType<T = unknown> = StatelyCollection<T>;
|
|
232
|
+
|
|
233
|
+
function getResolvedItemKey<T>(item: T, index: number, getKey?: (item: T) => Key): Key | undefined {
|
|
234
|
+
if (getKey) return getKey(item);
|
|
235
|
+
if (typeof item === 'object' && item !== null) {
|
|
236
|
+
const keyed = item as Record<string, unknown>;
|
|
237
|
+
const keyValue = keyed.key ?? keyed.id;
|
|
238
|
+
if (typeof keyValue === 'string' || typeof keyValue === 'number') return keyValue;
|
|
239
|
+
}
|
|
240
|
+
if (typeof item === 'string' || typeof item === 'number') return index;
|
|
241
|
+
return undefined;
|
|
242
|
+
}
|