@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
package/src/landmark/index.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
export {
|
|
2
|
-
createLandmark,
|
|
3
|
-
getLandmarkController,
|
|
4
|
-
type AriaLandmarkRole,
|
|
5
|
-
type AriaLandmarkProps,
|
|
6
|
-
type LandmarkAria,
|
|
7
|
-
type LandmarkController,
|
|
8
|
-
} from './createLandmark';
|
|
1
|
+
export {
|
|
2
|
+
createLandmark,
|
|
3
|
+
getLandmarkController,
|
|
4
|
+
type AriaLandmarkRole,
|
|
5
|
+
type AriaLandmarkProps,
|
|
6
|
+
type LandmarkAria,
|
|
7
|
+
type LandmarkController,
|
|
8
|
+
} from './createLandmark';
|
package/src/link/createLink.ts
CHANGED
|
@@ -27,10 +27,29 @@ export interface AriaLinkProps {
|
|
|
27
27
|
elementType?: string;
|
|
28
28
|
/** The URL to link to. */
|
|
29
29
|
href?: string;
|
|
30
|
+
/** Additional options forwarded to client-side router navigation handlers. */
|
|
31
|
+
routerOptions?: Record<string, unknown>;
|
|
30
32
|
/** The target window for the link. */
|
|
31
33
|
target?: string;
|
|
32
34
|
/** The relationship between the linked resource and the current page. */
|
|
33
35
|
rel?: string;
|
|
36
|
+
/** Hints the language of the linked resource. */
|
|
37
|
+
hrefLang?: string;
|
|
38
|
+
/** Instructs the browser to download the URL instead of navigating to it. */
|
|
39
|
+
download?: string | boolean;
|
|
40
|
+
/** Space-separated list of URLs to ping when following the link. */
|
|
41
|
+
ping?: string;
|
|
42
|
+
/** Referrer policy for fetches initiated by this link. */
|
|
43
|
+
referrerPolicy?:
|
|
44
|
+
| ''
|
|
45
|
+
| 'no-referrer'
|
|
46
|
+
| 'no-referrer-when-downgrade'
|
|
47
|
+
| 'origin'
|
|
48
|
+
| 'origin-when-cross-origin'
|
|
49
|
+
| 'same-origin'
|
|
50
|
+
| 'strict-origin'
|
|
51
|
+
| 'strict-origin-when-cross-origin'
|
|
52
|
+
| 'unsafe-url';
|
|
34
53
|
/** Handler that is called when the press is released over the target. */
|
|
35
54
|
onPress?: (e: PressEvent) => void;
|
|
36
55
|
/** Handler that is called when a press interaction starts. */
|
|
@@ -120,13 +139,6 @@ export function createLink(
|
|
|
120
139
|
};
|
|
121
140
|
}
|
|
122
141
|
|
|
123
|
-
// Add link-specific props
|
|
124
|
-
if (elType === 'a') {
|
|
125
|
-
if (p.href) baseProps.href = p.href;
|
|
126
|
-
if (p.target) baseProps.target = p.target;
|
|
127
|
-
if (p.rel) baseProps.rel = p.rel;
|
|
128
|
-
}
|
|
129
|
-
|
|
130
142
|
// ARIA attributes
|
|
131
143
|
const ariaProps: Record<string, unknown> = {
|
|
132
144
|
'aria-disabled': disabled || undefined,
|
|
@@ -164,7 +176,10 @@ export function createLink(
|
|
|
164
176
|
};
|
|
165
177
|
|
|
166
178
|
return mergeProps(
|
|
167
|
-
filterDOMProps(p as Record<string, unknown>, {
|
|
179
|
+
filterDOMProps(p as Record<string, unknown>, {
|
|
180
|
+
labelable: true,
|
|
181
|
+
isLink: elType === 'a',
|
|
182
|
+
}),
|
|
168
183
|
baseProps,
|
|
169
184
|
ariaProps,
|
|
170
185
|
focusableProps as Record<string, unknown>,
|
|
@@ -1,269 +1,308 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Provides the behavior and accessibility implementation for a listbox component.
|
|
3
|
-
* A listbox displays a list of options and allows a user to select one or more of them.
|
|
4
|
-
* Based on @react-aria/listbox useListBox.
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import { createEffect, onCleanup, type JSX } from 'solid-js';
|
|
8
|
-
import { createFocusWithin } from '../interactions/createFocusWithin';
|
|
9
|
-
import { createLabel } from '../label/createLabel';
|
|
10
|
-
import { createTypeSelect } from '../selection/createTypeSelect';
|
|
11
|
-
import { filterDOMProps } from '../utils/filterDOMProps';
|
|
12
|
-
import { mergeProps } from '../utils/mergeProps';
|
|
13
|
-
import { createId } from '../ssr';
|
|
14
|
-
import { access, type MaybeAccessor } from '../utils/reactivity';
|
|
15
|
-
import { isDevEnv } from '../utils/env';
|
|
16
|
-
import type { ListState, Key } from '@proyecto-viviana/solid-stately';
|
|
17
|
-
|
|
18
|
-
export interface AriaListBoxProps {
|
|
19
|
-
/** An ID for the listbox. */
|
|
20
|
-
id?: string;
|
|
21
|
-
/** Whether the listbox is disabled. */
|
|
22
|
-
isDisabled?: boolean;
|
|
23
|
-
/** The label for the listbox. */
|
|
24
|
-
label?: JSX.Element;
|
|
25
|
-
/** An accessible label for the listbox when no visible label is provided. */
|
|
26
|
-
'aria-label'?: string;
|
|
27
|
-
/** The ID of an element that labels the listbox. */
|
|
28
|
-
'aria-labelledby'?: string;
|
|
29
|
-
/** The ID of an element that describes the listbox. */
|
|
30
|
-
'aria-describedby'?: string;
|
|
31
|
-
/** Handler called when focus moves into the listbox. */
|
|
32
|
-
onFocus?: (e: FocusEvent) => void;
|
|
33
|
-
/** Handler called when focus moves out of the listbox. */
|
|
34
|
-
onBlur?: (e: FocusEvent) => void;
|
|
35
|
-
/** Handler called when the focus state changes. */
|
|
36
|
-
onFocusChange?: (isFocused: boolean) => void;
|
|
37
|
-
/** Handler called when an item is activated (pressed). */
|
|
38
|
-
onAction?: (key: Key) => void;
|
|
39
|
-
/** Whether focus should automatically wrap around. */
|
|
40
|
-
shouldFocusWrap?: boolean;
|
|
41
|
-
/** Whether selection should occur on press up. */
|
|
42
|
-
shouldSelectOnPressUp?: boolean;
|
|
43
|
-
/** Whether to focus items on hover. */
|
|
44
|
-
shouldFocusOnHover?: boolean;
|
|
45
|
-
/** Whether type-to-select is disabled. @default false */
|
|
46
|
-
disallowTypeAhead?: boolean;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
export interface ListBoxAria {
|
|
50
|
-
/** Props for the listbox element. */
|
|
51
|
-
listBoxProps: JSX.HTMLAttributes<HTMLElement>;
|
|
52
|
-
/** Props for the listbox's label element (if any). */
|
|
53
|
-
labelProps: JSX.HTMLAttributes<HTMLElement>;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
// Shared data between listbox and options
|
|
57
|
-
const listBoxData = new WeakMap<object, ListBoxData>();
|
|
58
|
-
|
|
59
|
-
interface ListBoxData {
|
|
60
|
-
id: string;
|
|
61
|
-
onAction?: (key: Key) => void;
|
|
62
|
-
shouldSelectOnPressUp?: boolean;
|
|
63
|
-
shouldFocusOnHover?: boolean;
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
}
|
|
146
|
-
});
|
|
147
|
-
|
|
148
|
-
//
|
|
149
|
-
const
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
if (e.
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
if (
|
|
225
|
-
|
|
226
|
-
state.
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Provides the behavior and accessibility implementation for a listbox component.
|
|
3
|
+
* A listbox displays a list of options and allows a user to select one or more of them.
|
|
4
|
+
* Based on @react-aria/listbox useListBox.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { createEffect, onCleanup, type JSX } from 'solid-js';
|
|
8
|
+
import { createFocusWithin } from '../interactions/createFocusWithin';
|
|
9
|
+
import { createLabel } from '../label/createLabel';
|
|
10
|
+
import { createTypeSelect } from '../selection/createTypeSelect';
|
|
11
|
+
import { filterDOMProps } from '../utils/filterDOMProps';
|
|
12
|
+
import { mergeProps } from '../utils/mergeProps';
|
|
13
|
+
import { createId } from '../ssr';
|
|
14
|
+
import { access, type MaybeAccessor } from '../utils/reactivity';
|
|
15
|
+
import { isDevEnv } from '../utils/env';
|
|
16
|
+
import type { ListState, Key } from '@proyecto-viviana/solid-stately';
|
|
17
|
+
|
|
18
|
+
export interface AriaListBoxProps {
|
|
19
|
+
/** An ID for the listbox. */
|
|
20
|
+
id?: string;
|
|
21
|
+
/** Whether the listbox is disabled. */
|
|
22
|
+
isDisabled?: boolean;
|
|
23
|
+
/** The label for the listbox. */
|
|
24
|
+
label?: JSX.Element;
|
|
25
|
+
/** An accessible label for the listbox when no visible label is provided. */
|
|
26
|
+
'aria-label'?: string;
|
|
27
|
+
/** The ID of an element that labels the listbox. */
|
|
28
|
+
'aria-labelledby'?: string;
|
|
29
|
+
/** The ID of an element that describes the listbox. */
|
|
30
|
+
'aria-describedby'?: string;
|
|
31
|
+
/** Handler called when focus moves into the listbox. */
|
|
32
|
+
onFocus?: (e: FocusEvent) => void;
|
|
33
|
+
/** Handler called when focus moves out of the listbox. */
|
|
34
|
+
onBlur?: (e: FocusEvent) => void;
|
|
35
|
+
/** Handler called when the focus state changes. */
|
|
36
|
+
onFocusChange?: (isFocused: boolean) => void;
|
|
37
|
+
/** Handler called when an item is activated (pressed). */
|
|
38
|
+
onAction?: (key: Key) => void;
|
|
39
|
+
/** Whether focus should automatically wrap around. */
|
|
40
|
+
shouldFocusWrap?: boolean;
|
|
41
|
+
/** Whether selection should occur on press up. */
|
|
42
|
+
shouldSelectOnPressUp?: boolean;
|
|
43
|
+
/** Whether to focus items on hover. */
|
|
44
|
+
shouldFocusOnHover?: boolean;
|
|
45
|
+
/** Whether type-to-select is disabled. @default false */
|
|
46
|
+
disallowTypeAhead?: boolean;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export interface ListBoxAria {
|
|
50
|
+
/** Props for the listbox element. */
|
|
51
|
+
listBoxProps: JSX.HTMLAttributes<HTMLElement>;
|
|
52
|
+
/** Props for the listbox's label element (if any). */
|
|
53
|
+
labelProps: JSX.HTMLAttributes<HTMLElement>;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Shared data between listbox and options
|
|
57
|
+
const listBoxData = new WeakMap<object, ListBoxData>();
|
|
58
|
+
|
|
59
|
+
interface ListBoxData {
|
|
60
|
+
id: string;
|
|
61
|
+
onAction?: (key: Key) => void;
|
|
62
|
+
shouldSelectOnPressUp?: boolean;
|
|
63
|
+
shouldFocusOnHover?: boolean;
|
|
64
|
+
isDisabled?: boolean;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export function getListBoxData(state: ListState): ListBoxData | undefined {
|
|
68
|
+
return listBoxData.get(state);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function findNextEnabledKey<T>(
|
|
72
|
+
state: ListState<T>,
|
|
73
|
+
currentKey: Key | null,
|
|
74
|
+
direction: 'next' | 'prev',
|
|
75
|
+
wrap: boolean
|
|
76
|
+
): Key | null {
|
|
77
|
+
const collection = state.collection();
|
|
78
|
+
const getAdjacentKey = direction === 'next'
|
|
79
|
+
? (key: Key) => collection.getKeyAfter(key)
|
|
80
|
+
: (key: Key) => collection.getKeyBefore(key);
|
|
81
|
+
const getBoundaryKey = direction === 'next'
|
|
82
|
+
? () => collection.getFirstKey()
|
|
83
|
+
: () => collection.getLastKey();
|
|
84
|
+
|
|
85
|
+
let key = currentKey != null ? getAdjacentKey(currentKey) : getBoundaryKey();
|
|
86
|
+
while (key != null && state.isDisabled(key)) {
|
|
87
|
+
key = getAdjacentKey(key);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
if (key == null && wrap) {
|
|
91
|
+
key = getBoundaryKey();
|
|
92
|
+
while (key != null && state.isDisabled(key)) {
|
|
93
|
+
key = getAdjacentKey(key);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
return key;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Provides the behavior and accessibility implementation for a listbox component.
|
|
102
|
+
* A listbox displays a list of options and allows a user to select one or more of them.
|
|
103
|
+
*/
|
|
104
|
+
export function createListBox<T>(
|
|
105
|
+
props: MaybeAccessor<AriaListBoxProps>,
|
|
106
|
+
state: ListState<T>,
|
|
107
|
+
_ref?: () => HTMLElement | null
|
|
108
|
+
): ListBoxAria {
|
|
109
|
+
const getProps = () => access(props);
|
|
110
|
+
const id = createId(getProps().id);
|
|
111
|
+
|
|
112
|
+
// Development-time warning for missing accessibility labels
|
|
113
|
+
if (isDevEnv()) {
|
|
114
|
+
const p = getProps();
|
|
115
|
+
if (!p.label && !p['aria-label'] && !p['aria-labelledby']) {
|
|
116
|
+
console.warn(
|
|
117
|
+
'[solidaria] A ListBox requires an aria-label or aria-labelledby attribute for accessibility.'
|
|
118
|
+
);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// Filter DOM props
|
|
123
|
+
const domProps = () => filterDOMProps(getProps() as unknown as Record<string, unknown>, { labelable: true });
|
|
124
|
+
|
|
125
|
+
const updateSharedData = () => {
|
|
126
|
+
const p = getProps();
|
|
127
|
+
listBoxData.set(state, {
|
|
128
|
+
id,
|
|
129
|
+
onAction: p.onAction,
|
|
130
|
+
shouldSelectOnPressUp: p.shouldSelectOnPressUp,
|
|
131
|
+
shouldFocusOnHover: p.shouldFocusOnHover,
|
|
132
|
+
isDisabled: p.isDisabled,
|
|
133
|
+
});
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
// Ensure options created in the same render pass can read parent metadata.
|
|
137
|
+
updateSharedData();
|
|
138
|
+
|
|
139
|
+
// Share data with child options
|
|
140
|
+
createEffect(() => {
|
|
141
|
+
updateSharedData();
|
|
142
|
+
|
|
143
|
+
onCleanup(() => {
|
|
144
|
+
listBoxData.delete(state);
|
|
145
|
+
});
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
// Handle focus within
|
|
149
|
+
const { focusWithinProps } = createFocusWithin({
|
|
150
|
+
onFocusWithin: (e) => getProps().onFocus?.(e),
|
|
151
|
+
onBlurWithin: (e) => getProps().onBlur?.(e),
|
|
152
|
+
onFocusWithinChange: (isFocused) => {
|
|
153
|
+
getProps().onFocusChange?.(isFocused);
|
|
154
|
+
state.setFocused(isFocused);
|
|
155
|
+
},
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
// Label handling
|
|
159
|
+
const { labelProps, fieldProps } = createLabel({
|
|
160
|
+
get id() {
|
|
161
|
+
return id;
|
|
162
|
+
},
|
|
163
|
+
get label() {
|
|
164
|
+
return getProps().label;
|
|
165
|
+
},
|
|
166
|
+
get 'aria-label'() {
|
|
167
|
+
return getProps()['aria-label'];
|
|
168
|
+
},
|
|
169
|
+
get 'aria-labelledby'() {
|
|
170
|
+
return getProps()['aria-labelledby'];
|
|
171
|
+
},
|
|
172
|
+
labelElementType: 'span',
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
// Type-to-select
|
|
176
|
+
const { typeSelectProps } = createTypeSelect({
|
|
177
|
+
collection: () => state.collection(),
|
|
178
|
+
focusedKey: () => state.focusedKey(),
|
|
179
|
+
onFocusedKeyChange: (key) => state.setFocusedKey(key),
|
|
180
|
+
isKeyDisabled: (key) => state.isDisabled(key),
|
|
181
|
+
get isDisabled() {
|
|
182
|
+
return getProps().disallowTypeAhead ?? false;
|
|
183
|
+
},
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
// Keyboard navigation
|
|
187
|
+
const onKeyDown: JSX.EventHandler<HTMLElement, KeyboardEvent> = (e) => {
|
|
188
|
+
const p = getProps();
|
|
189
|
+
if (p.isDisabled) return;
|
|
190
|
+
|
|
191
|
+
const collection = state.collection();
|
|
192
|
+
const shouldWrap = p.shouldFocusWrap ?? false;
|
|
193
|
+
|
|
194
|
+
switch (e.key) {
|
|
195
|
+
case 'ArrowDown': {
|
|
196
|
+
e.preventDefault();
|
|
197
|
+
const nextKey = findNextEnabledKey(state, state.focusedKey(), 'next', shouldWrap);
|
|
198
|
+
if (nextKey != null) {
|
|
199
|
+
state.setFocusedKey(nextKey);
|
|
200
|
+
if (!e.shiftKey && state.selectionMode() === 'single') {
|
|
201
|
+
state.replaceSelection(nextKey);
|
|
202
|
+
} else if (e.shiftKey && state.selectionMode() === 'multiple') {
|
|
203
|
+
state.extendSelection(nextKey, collection);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
break;
|
|
207
|
+
}
|
|
208
|
+
case 'ArrowUp': {
|
|
209
|
+
e.preventDefault();
|
|
210
|
+
const prevKey = findNextEnabledKey(state, state.focusedKey(), 'prev', shouldWrap);
|
|
211
|
+
if (prevKey != null) {
|
|
212
|
+
state.setFocusedKey(prevKey);
|
|
213
|
+
if (!e.shiftKey && state.selectionMode() === 'single') {
|
|
214
|
+
state.replaceSelection(prevKey);
|
|
215
|
+
} else if (e.shiftKey && state.selectionMode() === 'multiple') {
|
|
216
|
+
state.extendSelection(prevKey, collection);
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
break;
|
|
220
|
+
}
|
|
221
|
+
case 'Home': {
|
|
222
|
+
e.preventDefault();
|
|
223
|
+
const firstKey = findNextEnabledKey(state, null, 'next', false);
|
|
224
|
+
if (firstKey != null) {
|
|
225
|
+
state.setFocusedKey(firstKey);
|
|
226
|
+
if (e.ctrlKey && e.shiftKey && state.selectionMode() === 'multiple') {
|
|
227
|
+
// Select from current to first
|
|
228
|
+
state.extendSelection(firstKey, collection);
|
|
229
|
+
} else if (!e.shiftKey && state.selectionMode() === 'single') {
|
|
230
|
+
state.replaceSelection(firstKey);
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
break;
|
|
234
|
+
}
|
|
235
|
+
case 'End': {
|
|
236
|
+
e.preventDefault();
|
|
237
|
+
const lastKey = findNextEnabledKey(state, null, 'prev', false);
|
|
238
|
+
if (lastKey != null) {
|
|
239
|
+
state.setFocusedKey(lastKey);
|
|
240
|
+
if (e.ctrlKey && e.shiftKey && state.selectionMode() === 'multiple') {
|
|
241
|
+
// Select from current to last
|
|
242
|
+
state.extendSelection(lastKey, collection);
|
|
243
|
+
} else if (!e.shiftKey && state.selectionMode() === 'single') {
|
|
244
|
+
state.replaceSelection(lastKey);
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
break;
|
|
248
|
+
}
|
|
249
|
+
case ' ':
|
|
250
|
+
case 'Enter': {
|
|
251
|
+
e.preventDefault();
|
|
252
|
+
const focusedKey = state.focusedKey();
|
|
253
|
+
if (focusedKey != null && !state.isDisabled(focusedKey)) {
|
|
254
|
+
if (state.selectionMode() !== 'none') {
|
|
255
|
+
state.toggleSelection(focusedKey);
|
|
256
|
+
}
|
|
257
|
+
p.onAction?.(focusedKey);
|
|
258
|
+
}
|
|
259
|
+
break;
|
|
260
|
+
}
|
|
261
|
+
case 'a': {
|
|
262
|
+
if ((e.ctrlKey || e.metaKey) && state.selectionMode() === 'multiple') {
|
|
263
|
+
e.preventDefault();
|
|
264
|
+
state.selectAll();
|
|
265
|
+
}
|
|
266
|
+
break;
|
|
267
|
+
}
|
|
268
|
+
case 'Escape': {
|
|
269
|
+
e.preventDefault();
|
|
270
|
+
if (!state.disallowEmptySelection()) {
|
|
271
|
+
state.clearSelection();
|
|
272
|
+
}
|
|
273
|
+
break;
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
};
|
|
277
|
+
|
|
278
|
+
return {
|
|
279
|
+
get labelProps() {
|
|
280
|
+
return labelProps as JSX.HTMLAttributes<HTMLElement>;
|
|
281
|
+
},
|
|
282
|
+
get listBoxProps() {
|
|
283
|
+
const p = getProps();
|
|
284
|
+
const selectionMode = state.selectionMode();
|
|
285
|
+
|
|
286
|
+
const baseProps = mergeProps(
|
|
287
|
+
domProps(),
|
|
288
|
+
focusWithinProps as Record<string, unknown>,
|
|
289
|
+
fieldProps as Record<string, unknown>,
|
|
290
|
+
{
|
|
291
|
+
role: 'listbox',
|
|
292
|
+
tabIndex: p.isDisabled ? undefined : 0,
|
|
293
|
+
'aria-disabled': p.isDisabled || undefined,
|
|
294
|
+
'aria-multiselectable': selectionMode === 'multiple' ? true : undefined,
|
|
295
|
+
'aria-activedescendant': state.focusedKey() != null ? String(state.focusedKey()) : undefined,
|
|
296
|
+
onKeyDown,
|
|
297
|
+
}
|
|
298
|
+
);
|
|
299
|
+
|
|
300
|
+
// Add type-select props if enabled
|
|
301
|
+
if (!p.disallowTypeAhead) {
|
|
302
|
+
return mergeProps(baseProps, typeSelectProps as Record<string, unknown>) as JSX.HTMLAttributes<HTMLElement>;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
return baseProps as JSX.HTMLAttributes<HTMLElement>;
|
|
306
|
+
},
|
|
307
|
+
};
|
|
308
|
+
}
|