@proyecto-viviana/solidaria 0.2.2 → 0.2.3
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/dist/autocomplete/createAutocomplete.d.ts +2 -2
- package/dist/autocomplete/createAutocomplete.d.ts.map +1 -1
- package/dist/index.js +233 -234
- package/dist/index.js.map +2 -2
- package/dist/index.ssr.js +233 -234
- package/dist/index.ssr.js.map +2 -2
- package/dist/interactions/PressEvent.d.ts +13 -10
- package/dist/interactions/PressEvent.d.ts.map +1 -1
- package/dist/interactions/createPress.d.ts.map +1 -1
- package/dist/interactions/index.d.ts +1 -1
- package/dist/interactions/index.d.ts.map +1 -1
- package/dist/select/createHiddenSelect.d.ts.map +1 -1
- package/dist/toolbar/createToolbar.d.ts.map +1 -1
- package/dist/tooltip/createTooltipTrigger.d.ts.map +1 -1
- package/package.json +9 -7
- package/src/autocomplete/createAutocomplete.ts +341 -0
- package/src/autocomplete/index.ts +9 -0
- package/src/breadcrumbs/createBreadcrumbs.ts +196 -0
- package/src/breadcrumbs/index.ts +8 -0
- package/src/button/createButton.ts +142 -0
- package/src/button/createToggleButton.ts +101 -0
- package/src/button/index.ts +4 -0
- package/src/button/types.ts +78 -0
- package/src/calendar/createCalendar.ts +138 -0
- package/src/calendar/createCalendarCell.ts +187 -0
- package/src/calendar/createCalendarGrid.ts +140 -0
- package/src/calendar/createRangeCalendar.ts +136 -0
- package/src/calendar/createRangeCalendarCell.ts +186 -0
- package/src/calendar/index.ts +34 -0
- package/src/checkbox/createCheckbox.ts +135 -0
- package/src/checkbox/createCheckboxGroup.ts +137 -0
- package/src/checkbox/createCheckboxGroupItem.ts +117 -0
- package/src/checkbox/createCheckboxGroupState.ts +193 -0
- package/src/checkbox/index.ts +13 -0
- package/src/color/createColorArea.ts +314 -0
- package/src/color/createColorField.ts +137 -0
- package/src/color/createColorSlider.ts +197 -0
- package/src/color/createColorSwatch.ts +40 -0
- package/src/color/createColorWheel.ts +208 -0
- package/src/color/index.ts +24 -0
- package/src/color/types.ts +116 -0
- package/src/combobox/createComboBox.ts +647 -0
- package/src/combobox/index.ts +6 -0
- package/src/combobox/intl/en-US.json +7 -0
- package/src/combobox/intl/es-ES.json +7 -0
- package/src/combobox/intl/index.ts +23 -0
- package/src/datepicker/createDateField.ts +154 -0
- package/src/datepicker/createDatePicker.ts +206 -0
- package/src/datepicker/createDateSegment.ts +229 -0
- package/src/datepicker/createTimeField.ts +154 -0
- package/src/datepicker/index.ts +28 -0
- package/src/dialog/createDialog.ts +120 -0
- package/src/dialog/index.ts +2 -0
- package/src/dialog/types.ts +19 -0
- package/src/disclosure/createDisclosure.ts +131 -0
- package/src/disclosure/createDisclosureGroup.ts +62 -0
- package/src/disclosure/index.ts +11 -0
- package/src/dnd/createDrag.ts +209 -0
- package/src/dnd/createDraggableCollection.ts +63 -0
- package/src/dnd/createDraggableItem.ts +243 -0
- package/src/dnd/createDrop.ts +321 -0
- package/src/dnd/createDroppableCollection.ts +293 -0
- package/src/dnd/createDroppableItem.ts +213 -0
- package/src/dnd/index.ts +47 -0
- package/src/dnd/types.ts +89 -0
- package/src/dnd/utils.ts +294 -0
- package/src/focus/FocusScope.tsx +408 -0
- package/src/focus/createAutoFocus.ts +321 -0
- package/src/focus/createFocusRestore.ts +313 -0
- package/src/focus/createVirtualFocus.ts +396 -0
- package/src/focus/index.ts +35 -0
- package/src/form/createFormReset.ts +51 -0
- package/src/form/createFormValidation.ts +224 -0
- package/src/form/index.ts +11 -0
- package/src/grid/GridKeyboardDelegate.ts +429 -0
- package/src/grid/createGrid.ts +261 -0
- package/src/grid/createGridCell.ts +182 -0
- package/src/grid/createGridRow.ts +153 -0
- package/src/grid/index.ts +18 -0
- package/src/grid/types.ts +133 -0
- package/src/gridlist/createGridList.ts +185 -0
- package/src/gridlist/createGridListItem.ts +180 -0
- package/src/gridlist/createGridListSelectionCheckbox.ts +59 -0
- package/src/gridlist/index.ts +16 -0
- package/src/gridlist/types.ts +81 -0
- package/src/i18n/NumberFormatter.ts +266 -0
- package/src/i18n/createCollator.ts +79 -0
- package/src/i18n/createDateFormatter.ts +83 -0
- package/src/i18n/createFilter.ts +131 -0
- package/src/i18n/createNumberFormatter.ts +52 -0
- package/src/i18n/createStringFormatter.ts +87 -0
- package/src/i18n/index.ts +40 -0
- package/src/i18n/locale.tsx +188 -0
- package/src/i18n/utils.ts +99 -0
- package/src/index.ts +670 -0
- package/src/interactions/FocusableProvider.tsx +44 -0
- package/src/interactions/PressEvent.ts +126 -0
- package/src/interactions/createFocus.ts +163 -0
- package/src/interactions/createFocusRing.ts +89 -0
- package/src/interactions/createFocusWithin.ts +206 -0
- package/src/interactions/createFocusable.ts +168 -0
- package/src/interactions/createHover.ts +254 -0
- package/src/interactions/createInteractionModality.ts +424 -0
- package/src/interactions/createKeyboard.ts +82 -0
- package/src/interactions/createLongPress.ts +174 -0
- package/src/interactions/createMove.ts +289 -0
- package/src/interactions/createPress.ts +834 -0
- package/src/interactions/index.ts +78 -0
- package/src/label/createField.ts +145 -0
- package/src/label/createLabel.ts +117 -0
- package/src/label/createLabels.ts +50 -0
- package/src/label/index.ts +19 -0
- package/src/landmark/createLandmark.ts +377 -0
- package/src/landmark/index.ts +8 -0
- package/src/link/createLink.ts +182 -0
- package/src/link/index.ts +1 -0
- package/src/listbox/createListBox.ts +269 -0
- package/src/listbox/createOption.ts +151 -0
- package/src/listbox/index.ts +12 -0
- package/src/live-announcer/announce.ts +322 -0
- package/src/live-announcer/index.ts +9 -0
- package/src/menu/createMenu.ts +396 -0
- package/src/menu/createMenuItem.ts +149 -0
- package/src/menu/createMenuTrigger.ts +88 -0
- package/src/menu/index.ts +18 -0
- package/src/meter/createMeter.ts +75 -0
- package/src/meter/index.ts +1 -0
- package/src/numberfield/createNumberField.ts +268 -0
- package/src/numberfield/index.ts +5 -0
- package/src/overlays/ariaHideOutside.ts +219 -0
- package/src/overlays/createInteractOutside.ts +149 -0
- package/src/overlays/createModal.tsx +202 -0
- package/src/overlays/createOverlay.ts +155 -0
- package/src/overlays/createOverlayTrigger.ts +85 -0
- package/src/overlays/createPreventScroll.ts +266 -0
- package/src/overlays/index.ts +44 -0
- package/src/popover/calculatePosition.ts +766 -0
- package/src/popover/createOverlayPosition.ts +356 -0
- package/src/popover/createPopover.ts +170 -0
- package/src/popover/index.ts +24 -0
- package/src/progress/createProgressBar.ts +128 -0
- package/src/progress/index.ts +5 -0
- package/src/radio/createRadio.ts +287 -0
- package/src/radio/createRadioGroup.ts +189 -0
- package/src/radio/createRadioGroupState.ts +201 -0
- package/src/radio/index.ts +23 -0
- package/src/searchfield/createSearchField.ts +186 -0
- package/src/searchfield/index.ts +2 -0
- package/src/select/createHiddenSelect.tsx +236 -0
- package/src/select/createSelect.ts +395 -0
- package/src/select/index.ts +14 -0
- package/src/selection/createTypeSelect.ts +201 -0
- package/src/selection/index.ts +6 -0
- package/src/separator/createSeparator.ts +82 -0
- package/src/separator/index.ts +6 -0
- package/src/slider/createSlider.ts +349 -0
- package/src/slider/index.ts +2 -0
- package/src/ssr/index.tsx +370 -0
- package/src/switch/createSwitch.ts +70 -0
- package/src/switch/index.ts +1 -0
- package/src/table/createTable.ts +526 -0
- package/src/table/createTableCell.ts +147 -0
- package/src/table/createTableColumnHeader.ts +115 -0
- package/src/table/createTableHeaderRow.ts +40 -0
- package/src/table/createTableRow.ts +155 -0
- package/src/table/createTableRowGroup.ts +32 -0
- package/src/table/createTableSelectAllCheckbox.ts +73 -0
- package/src/table/createTableSelectionCheckbox.ts +59 -0
- package/src/table/index.ts +30 -0
- package/src/table/types.ts +165 -0
- package/src/tabs/createTabs.ts +472 -0
- package/src/tabs/index.ts +14 -0
- package/src/tag/createTag.ts +194 -0
- package/src/tag/createTagGroup.ts +154 -0
- package/src/tag/index.ts +12 -0
- package/src/textfield/createTextField.ts +198 -0
- package/src/textfield/index.ts +5 -0
- package/src/toast/createToast.ts +118 -0
- package/src/toast/createToastRegion.ts +100 -0
- package/src/toast/index.ts +11 -0
- package/src/toggle/createToggle.ts +223 -0
- package/src/toggle/createToggleState.ts +94 -0
- package/src/toggle/index.ts +7 -0
- package/src/toolbar/createToolbar.ts +369 -0
- package/src/toolbar/index.ts +6 -0
- package/src/tooltip/createTooltip.ts +79 -0
- package/src/tooltip/createTooltipTrigger.ts +222 -0
- package/src/tooltip/index.ts +6 -0
- package/src/tree/createTree.ts +246 -0
- package/src/tree/createTreeItem.ts +233 -0
- package/src/tree/createTreeSelectionCheckbox.ts +68 -0
- package/src/tree/index.ts +16 -0
- package/src/tree/types.ts +87 -0
- package/src/utils/createDescription.ts +137 -0
- package/src/utils/dom.ts +327 -0
- package/src/utils/env.ts +54 -0
- package/src/utils/events.ts +106 -0
- package/src/utils/filterDOMProps.ts +116 -0
- package/src/utils/focus.ts +151 -0
- package/src/utils/geometry.ts +115 -0
- package/src/utils/globalListeners.ts +142 -0
- package/src/utils/index.ts +80 -0
- package/src/utils/mergeProps.ts +52 -0
- package/src/utils/platform.ts +52 -0
- package/src/utils/reactivity.ts +36 -0
- package/src/utils/textSelection.ts +114 -0
- package/src/visually-hidden/createVisuallyHidden.ts +124 -0
- package/src/visually-hidden/index.ts +6 -0
- package/dist/index.jsx +0 -15845
- package/dist/index.jsx.map +0 -7
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* createGridCell - Provides accessibility for a grid cell.
|
|
3
|
+
* Based on @react-aria/grid/useGridCell.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { createMemo, createSignal, type Accessor } from 'solid-js';
|
|
7
|
+
import type { JSX } from 'solid-js';
|
|
8
|
+
import type { GridState, GridCollection } from '@proyecto-viviana/solid-stately';
|
|
9
|
+
import type { GridCellProps, GridCellAria } from './types';
|
|
10
|
+
import { getGridData } from './createGrid';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Creates accessibility props for a grid cell.
|
|
14
|
+
*/
|
|
15
|
+
export function createGridCell<T extends object>(
|
|
16
|
+
props: Accessor<GridCellProps>,
|
|
17
|
+
state: Accessor<GridState<T, GridCollection<T>>>,
|
|
18
|
+
_ref: Accessor<HTMLElement | null>
|
|
19
|
+
): GridCellAria {
|
|
20
|
+
const [isPressed, setIsPressed] = createSignal(false);
|
|
21
|
+
|
|
22
|
+
const isSelected = createMemo(() => {
|
|
23
|
+
const s = state();
|
|
24
|
+
const p = props();
|
|
25
|
+
// Check if parent row is selected
|
|
26
|
+
const node = s.collection.getItem(p.key);
|
|
27
|
+
if (node?.parentKey != null) {
|
|
28
|
+
return s.isSelected(node.parentKey);
|
|
29
|
+
}
|
|
30
|
+
return false;
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
const isDisabled = createMemo(() => {
|
|
34
|
+
const s = state();
|
|
35
|
+
const p = props();
|
|
36
|
+
// Check if parent row is disabled
|
|
37
|
+
const node = s.collection.getItem(p.key);
|
|
38
|
+
if (node?.parentKey != null) {
|
|
39
|
+
return s.isDisabled(node.parentKey);
|
|
40
|
+
}
|
|
41
|
+
return false;
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
const isFocused = createMemo(() => {
|
|
45
|
+
const s = state();
|
|
46
|
+
const p = props();
|
|
47
|
+
return s.focusedKey === p.key;
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
// Handle click/press for cell actions
|
|
51
|
+
const onClick = (e: MouseEvent) => {
|
|
52
|
+
const s = state();
|
|
53
|
+
const p = props();
|
|
54
|
+
|
|
55
|
+
if (isDisabled()) return;
|
|
56
|
+
|
|
57
|
+
// Get grid metadata for actions
|
|
58
|
+
const gridData = getGridData(s);
|
|
59
|
+
const onCellAction = gridData?.actions.onCellAction;
|
|
60
|
+
|
|
61
|
+
// Get parent row key for selection
|
|
62
|
+
const node = s.collection.getItem(p.key);
|
|
63
|
+
const rowKey = node?.parentKey;
|
|
64
|
+
|
|
65
|
+
// Handle selection on parent row
|
|
66
|
+
if (rowKey != null && s.selectionMode !== 'none') {
|
|
67
|
+
if (e.shiftKey && s.selectionMode === 'multiple') {
|
|
68
|
+
s.extendSelection(rowKey);
|
|
69
|
+
} else if (e.ctrlKey || e.metaKey) {
|
|
70
|
+
s.toggleSelection(rowKey);
|
|
71
|
+
} else {
|
|
72
|
+
// Replace selection
|
|
73
|
+
s.replaceSelection(rowKey);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Call cell action handler
|
|
78
|
+
if (onCellAction) {
|
|
79
|
+
onCellAction(p.key);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if (p.onAction) {
|
|
83
|
+
p.onAction();
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
const onKeyDown = (e: KeyboardEvent) => {
|
|
88
|
+
const s = state();
|
|
89
|
+
const p = props();
|
|
90
|
+
|
|
91
|
+
if (isDisabled()) return;
|
|
92
|
+
|
|
93
|
+
// Enter or Space triggers cell action
|
|
94
|
+
if (e.key === 'Enter' || e.key === ' ') {
|
|
95
|
+
// Only handle if there's an action to trigger
|
|
96
|
+
const gridData = getGridData(s);
|
|
97
|
+
const onCellAction = gridData?.actions.onCellAction;
|
|
98
|
+
|
|
99
|
+
if (onCellAction || p.onAction) {
|
|
100
|
+
e.preventDefault();
|
|
101
|
+
|
|
102
|
+
if (onCellAction) {
|
|
103
|
+
onCellAction(p.key);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
if (p.onAction) {
|
|
107
|
+
p.onAction();
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
const onFocus = () => {
|
|
114
|
+
const s = state();
|
|
115
|
+
const p = props();
|
|
116
|
+
s.setFocusedKey(p.key);
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
const onPointerDown = () => {
|
|
120
|
+
setIsPressed(true);
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
const onPointerUp = () => {
|
|
124
|
+
setIsPressed(false);
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
const cellProps = createMemo(() => {
|
|
128
|
+
const s = state();
|
|
129
|
+
const p = props();
|
|
130
|
+
const node = s.collection.getItem(p.key);
|
|
131
|
+
|
|
132
|
+
// Determine the role based on node type
|
|
133
|
+
let role: string = 'gridcell';
|
|
134
|
+
if (node?.type === 'rowheader') {
|
|
135
|
+
role = 'rowheader';
|
|
136
|
+
} else if (node?.type === 'column') {
|
|
137
|
+
role = 'columnheader';
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
const baseProps: Record<string, unknown> = {
|
|
141
|
+
role,
|
|
142
|
+
'aria-disabled': isDisabled() || undefined,
|
|
143
|
+
'aria-selected': s.selectionMode !== 'none' ? isSelected() : undefined,
|
|
144
|
+
tabIndex: isFocused() ? 0 : -1,
|
|
145
|
+
onClick,
|
|
146
|
+
onKeyDown,
|
|
147
|
+
onFocus,
|
|
148
|
+
onPointerDown,
|
|
149
|
+
onPointerUp,
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
// Add column index for virtualized grids
|
|
153
|
+
if (p.isVirtualized && node?.column != null) {
|
|
154
|
+
baseProps['aria-colindex'] = node.column + 1; // aria-colindex is 1-based
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// Add colspan if present
|
|
158
|
+
if (node?.colspan != null && node.colspan > 1) {
|
|
159
|
+
baseProps['aria-colspan'] = node.colspan;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
return baseProps as JSX.HTMLAttributes<HTMLElement>;
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
return {
|
|
166
|
+
get cellProps() {
|
|
167
|
+
return cellProps();
|
|
168
|
+
},
|
|
169
|
+
get isSelected() {
|
|
170
|
+
return isSelected();
|
|
171
|
+
},
|
|
172
|
+
get isDisabled() {
|
|
173
|
+
return isDisabled();
|
|
174
|
+
},
|
|
175
|
+
get isPressed() {
|
|
176
|
+
return isPressed();
|
|
177
|
+
},
|
|
178
|
+
get isFocused() {
|
|
179
|
+
return isFocused();
|
|
180
|
+
},
|
|
181
|
+
};
|
|
182
|
+
}
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* createGridRow - Provides accessibility for a grid row.
|
|
3
|
+
* Based on @react-aria/grid/useGridRow.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { createMemo, createSignal, type Accessor } from 'solid-js';
|
|
7
|
+
import type { JSX } from 'solid-js';
|
|
8
|
+
import type { GridState, GridCollection } from '@proyecto-viviana/solid-stately';
|
|
9
|
+
import type { GridRowProps, GridRowAria } from './types';
|
|
10
|
+
import { getGridData } from './createGrid';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Creates accessibility props for a grid row.
|
|
14
|
+
*/
|
|
15
|
+
export function createGridRow<T extends object>(
|
|
16
|
+
props: Accessor<GridRowProps>,
|
|
17
|
+
state: Accessor<GridState<T, GridCollection<T>>>,
|
|
18
|
+
_ref: Accessor<HTMLElement | null>
|
|
19
|
+
): GridRowAria {
|
|
20
|
+
const [isPressed, setIsPressed] = createSignal(false);
|
|
21
|
+
|
|
22
|
+
const isSelected = createMemo(() => {
|
|
23
|
+
const s = state();
|
|
24
|
+
const p = props();
|
|
25
|
+
return s.isSelected(p.key);
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
const isDisabled = createMemo(() => {
|
|
29
|
+
const s = state();
|
|
30
|
+
const p = props();
|
|
31
|
+
return s.isDisabled(p.key);
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
const isFocused = createMemo(() => {
|
|
35
|
+
const s = state();
|
|
36
|
+
const p = props();
|
|
37
|
+
return s.focusedKey === p.key;
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
// Handle click/press for selection
|
|
41
|
+
const onClick = (e: MouseEvent) => {
|
|
42
|
+
const s = state();
|
|
43
|
+
const p = props();
|
|
44
|
+
|
|
45
|
+
if (isDisabled()) return;
|
|
46
|
+
|
|
47
|
+
// Get grid metadata for actions
|
|
48
|
+
const gridData = getGridData(s);
|
|
49
|
+
const onRowAction = gridData?.actions.onRowAction;
|
|
50
|
+
|
|
51
|
+
// Handle selection
|
|
52
|
+
if (s.selectionMode !== 'none') {
|
|
53
|
+
if (e.shiftKey && s.selectionMode === 'multiple') {
|
|
54
|
+
s.extendSelection(p.key);
|
|
55
|
+
} else if (e.ctrlKey || e.metaKey) {
|
|
56
|
+
s.toggleSelection(p.key);
|
|
57
|
+
} else {
|
|
58
|
+
// Replace selection
|
|
59
|
+
s.replaceSelection(p.key);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Call action handler
|
|
64
|
+
if (onRowAction) {
|
|
65
|
+
onRowAction(p.key);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if (p.onAction) {
|
|
69
|
+
p.onAction();
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
const onKeyDown = (e: KeyboardEvent) => {
|
|
74
|
+
const s = state();
|
|
75
|
+
const p = props();
|
|
76
|
+
|
|
77
|
+
if (isDisabled()) return;
|
|
78
|
+
|
|
79
|
+
if (e.key === 'Enter' || e.key === ' ') {
|
|
80
|
+
e.preventDefault();
|
|
81
|
+
|
|
82
|
+
// Get grid metadata for actions
|
|
83
|
+
const gridData = getGridData(s);
|
|
84
|
+
const onRowAction = gridData?.actions.onRowAction;
|
|
85
|
+
|
|
86
|
+
// Handle selection
|
|
87
|
+
if (s.selectionMode !== 'none') {
|
|
88
|
+
s.toggleSelection(p.key);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// Call action handler
|
|
92
|
+
if (onRowAction) {
|
|
93
|
+
onRowAction(p.key);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
if (p.onAction) {
|
|
97
|
+
p.onAction();
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
const onFocus = () => {
|
|
103
|
+
const s = state();
|
|
104
|
+
const p = props();
|
|
105
|
+
s.setFocusedKey(p.key);
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
const onPointerDown = () => {
|
|
109
|
+
setIsPressed(true);
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
const onPointerUp = () => {
|
|
113
|
+
setIsPressed(false);
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
const rowProps = createMemo(() => {
|
|
117
|
+
const s = state();
|
|
118
|
+
const p = props();
|
|
119
|
+
|
|
120
|
+
const baseProps: Record<string, unknown> = {
|
|
121
|
+
role: 'row',
|
|
122
|
+
'aria-selected': s.selectionMode !== 'none' ? isSelected() : undefined,
|
|
123
|
+
'aria-disabled': isDisabled() || undefined,
|
|
124
|
+
tabIndex: isFocused() ? 0 : -1,
|
|
125
|
+
onClick,
|
|
126
|
+
onKeyDown,
|
|
127
|
+
onFocus,
|
|
128
|
+
onPointerDown,
|
|
129
|
+
onPointerUp,
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
if (p.isVirtualized && p.index != null) {
|
|
133
|
+
baseProps['aria-rowindex'] = p.index + 1; // aria-rowindex is 1-based
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
return baseProps as JSX.HTMLAttributes<HTMLElement>;
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
return {
|
|
140
|
+
get rowProps() {
|
|
141
|
+
return rowProps();
|
|
142
|
+
},
|
|
143
|
+
get isSelected() {
|
|
144
|
+
return isSelected();
|
|
145
|
+
},
|
|
146
|
+
get isDisabled() {
|
|
147
|
+
return isDisabled();
|
|
148
|
+
},
|
|
149
|
+
get isPressed() {
|
|
150
|
+
return isPressed();
|
|
151
|
+
},
|
|
152
|
+
};
|
|
153
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Grid accessibility primitives for Table and GridList components.
|
|
3
|
+
* Based on @react-aria/grid.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export { createGrid, getGridData } from './createGrid';
|
|
7
|
+
export { createGridRow } from './createGridRow';
|
|
8
|
+
export { createGridCell } from './createGridCell';
|
|
9
|
+
export { GridKeyboardDelegate } from './GridKeyboardDelegate';
|
|
10
|
+
export type {
|
|
11
|
+
KeyboardDelegate,
|
|
12
|
+
GridProps,
|
|
13
|
+
GridAria,
|
|
14
|
+
GridRowProps,
|
|
15
|
+
GridRowAria,
|
|
16
|
+
GridCellProps,
|
|
17
|
+
GridCellAria,
|
|
18
|
+
} from './types';
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Grid ARIA types for Table and GridList components.
|
|
3
|
+
* Based on @react-aria/grid types.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { JSX } from 'solid-js';
|
|
7
|
+
import type { Key } from '@proyecto-viviana/solid-stately';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Keyboard delegate interface for grid navigation.
|
|
11
|
+
*/
|
|
12
|
+
export interface KeyboardDelegate {
|
|
13
|
+
/** Get the key above the given key. */
|
|
14
|
+
getKeyAbove?(key: Key): Key | null;
|
|
15
|
+
/** Get the key below the given key. */
|
|
16
|
+
getKeyBelow?(key: Key): Key | null;
|
|
17
|
+
/** Get the key to the left of the given key. */
|
|
18
|
+
getKeyLeftOf?(key: Key): Key | null;
|
|
19
|
+
/** Get the key to the right of the given key. */
|
|
20
|
+
getKeyRightOf?(key: Key): Key | null;
|
|
21
|
+
/** Get the first key in the collection. */
|
|
22
|
+
getFirstKey?(key?: Key, global?: boolean): Key | null;
|
|
23
|
+
/** Get the last key in the collection. */
|
|
24
|
+
getLastKey?(key?: Key, global?: boolean): Key | null;
|
|
25
|
+
/** Get the key for a page up action. */
|
|
26
|
+
getKeyPageAbove?(key: Key): Key | null;
|
|
27
|
+
/** Get the key for a page down action. */
|
|
28
|
+
getKeyPageBelow?(key: Key): Key | null;
|
|
29
|
+
/** Get the key that matches the search string. */
|
|
30
|
+
getKeyForSearch?(search: string, fromKey?: Key): Key | null;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Props for the createGrid hook.
|
|
35
|
+
*/
|
|
36
|
+
export interface GridProps {
|
|
37
|
+
/** ID for the grid element. */
|
|
38
|
+
id?: string;
|
|
39
|
+
/** Whether the grid uses virtual scrolling. */
|
|
40
|
+
isVirtualized?: boolean;
|
|
41
|
+
/** Whether typeahead navigation is disabled. */
|
|
42
|
+
disallowTypeAhead?: boolean;
|
|
43
|
+
/** Custom keyboard delegate for navigation. */
|
|
44
|
+
keyboardDelegate?: KeyboardDelegate;
|
|
45
|
+
/** Whether focus should be on row or cell. */
|
|
46
|
+
focusMode?: 'row' | 'cell';
|
|
47
|
+
/** Handler for row actions. */
|
|
48
|
+
onRowAction?: (key: Key) => void;
|
|
49
|
+
/** Handler for cell actions. */
|
|
50
|
+
onCellAction?: (key: Key) => void;
|
|
51
|
+
/** Escape key behavior. */
|
|
52
|
+
escapeKeyBehavior?: 'clearSelection' | 'none';
|
|
53
|
+
/** Whether selection should occur on press up. */
|
|
54
|
+
shouldSelectOnPressUp?: boolean;
|
|
55
|
+
/** ARIA label for the grid. */
|
|
56
|
+
'aria-label'?: string;
|
|
57
|
+
/** ARIA labelledby for the grid. */
|
|
58
|
+
'aria-labelledby'?: string;
|
|
59
|
+
/** ARIA describedby for the grid. */
|
|
60
|
+
'aria-describedby'?: string;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Return value from createGrid.
|
|
65
|
+
*/
|
|
66
|
+
export interface GridAria {
|
|
67
|
+
/** Props to spread on the grid element. */
|
|
68
|
+
gridProps: JSX.HTMLAttributes<HTMLElement>;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Props for the createGridRow hook.
|
|
73
|
+
*/
|
|
74
|
+
export interface GridRowProps {
|
|
75
|
+
/** The key of the row. */
|
|
76
|
+
key: Key;
|
|
77
|
+
/** The index of the row (for virtualized grids). */
|
|
78
|
+
index?: number;
|
|
79
|
+
/** Whether the grid is virtualized. */
|
|
80
|
+
isVirtualized?: boolean;
|
|
81
|
+
/** Handler for row action. */
|
|
82
|
+
onAction?: () => void;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Return value from createGridRow.
|
|
87
|
+
*/
|
|
88
|
+
export interface GridRowAria {
|
|
89
|
+
/** Props to spread on the row element. */
|
|
90
|
+
rowProps: JSX.HTMLAttributes<HTMLElement>;
|
|
91
|
+
/** Whether the row is selected. */
|
|
92
|
+
isSelected: boolean;
|
|
93
|
+
/** Whether the row is disabled. */
|
|
94
|
+
isDisabled: boolean;
|
|
95
|
+
/** Whether the row is pressed. */
|
|
96
|
+
isPressed: boolean;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Props for the createGridCell hook.
|
|
101
|
+
*/
|
|
102
|
+
export interface GridCellProps {
|
|
103
|
+
/** The key of the cell. */
|
|
104
|
+
key: Key;
|
|
105
|
+
/** The key of the parent row. */
|
|
106
|
+
parentKey: Key;
|
|
107
|
+
/** The column index of the cell. */
|
|
108
|
+
colIndex?: number;
|
|
109
|
+
/** Column span. */
|
|
110
|
+
colSpan?: number;
|
|
111
|
+
/** Whether the grid is virtualized. */
|
|
112
|
+
isVirtualized?: boolean;
|
|
113
|
+
/** Whether to focus cell or child element. */
|
|
114
|
+
focusMode?: 'child' | 'cell';
|
|
115
|
+
/** Handler for cell action. */
|
|
116
|
+
onAction?: () => void;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Return value from createGridCell.
|
|
121
|
+
*/
|
|
122
|
+
export interface GridCellAria {
|
|
123
|
+
/** Props to spread on the cell element. */
|
|
124
|
+
cellProps: JSX.HTMLAttributes<HTMLElement>;
|
|
125
|
+
/** Whether the cell's row is selected. */
|
|
126
|
+
isSelected: boolean;
|
|
127
|
+
/** Whether the cell's row is disabled. */
|
|
128
|
+
isDisabled: boolean;
|
|
129
|
+
/** Whether the cell is pressed. */
|
|
130
|
+
isPressed: boolean;
|
|
131
|
+
/** Whether the cell is focused. */
|
|
132
|
+
isFocused: boolean;
|
|
133
|
+
}
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* createGridList - Provides accessibility for a grid list.
|
|
3
|
+
* Based on @react-aria/gridlist/useGridList.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { createMemo, type Accessor } from 'solid-js';
|
|
7
|
+
import type { JSX } from 'solid-js';
|
|
8
|
+
import { createId } from '@proyecto-viviana/solid-stately';
|
|
9
|
+
import type { GridState, GridCollection, Key } from '@proyecto-viviana/solid-stately';
|
|
10
|
+
import type { AriaGridListProps, GridListAria } from './types';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Metadata stored for a grid list instance.
|
|
14
|
+
*/
|
|
15
|
+
interface GridListData {
|
|
16
|
+
/** The generated ID for the grid list. */
|
|
17
|
+
gridListId: string;
|
|
18
|
+
/** Actions registered for the grid list. */
|
|
19
|
+
actions: {
|
|
20
|
+
onAction?: (key: Key) => void;
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* WeakMap to store grid list data for child components to access.
|
|
26
|
+
*/
|
|
27
|
+
const gridListDataMap = new WeakMap<object, GridListData>();
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Gets the grid list data for a given state.
|
|
31
|
+
*/
|
|
32
|
+
export function getGridListData<T extends object, C extends GridCollection<T>>(
|
|
33
|
+
state: GridState<T, C>
|
|
34
|
+
): GridListData | undefined {
|
|
35
|
+
return gridListDataMap.get(state);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Creates accessibility props for a grid list.
|
|
40
|
+
*/
|
|
41
|
+
export function createGridList<T extends object, C extends GridCollection<T> = GridCollection<T>>(
|
|
42
|
+
props: Accessor<AriaGridListProps>,
|
|
43
|
+
state: Accessor<GridState<T, C>>,
|
|
44
|
+
_ref: Accessor<HTMLUListElement | null>
|
|
45
|
+
): GridListAria {
|
|
46
|
+
// Generate a unique ID for the grid list
|
|
47
|
+
const gridListId = props().id ?? createId();
|
|
48
|
+
|
|
49
|
+
// Store grid list data for child components
|
|
50
|
+
const gridListData: GridListData = {
|
|
51
|
+
gridListId,
|
|
52
|
+
actions: {
|
|
53
|
+
get onAction() {
|
|
54
|
+
return props().onAction;
|
|
55
|
+
},
|
|
56
|
+
},
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
// Store in WeakMap using the state as key
|
|
60
|
+
gridListDataMap.set(state(), gridListData);
|
|
61
|
+
|
|
62
|
+
// Handle keyboard navigation
|
|
63
|
+
const onKeyDown = (e: KeyboardEvent) => {
|
|
64
|
+
const s = state();
|
|
65
|
+
const p = props();
|
|
66
|
+
const collection = s.collection;
|
|
67
|
+
const focusedKey = s.focusedKey;
|
|
68
|
+
|
|
69
|
+
if (p.isDisabled) return;
|
|
70
|
+
|
|
71
|
+
switch (e.key) {
|
|
72
|
+
case 'ArrowDown': {
|
|
73
|
+
e.preventDefault();
|
|
74
|
+
if (focusedKey != null) {
|
|
75
|
+
const nextKey = collection.getKeyAfter(focusedKey);
|
|
76
|
+
if (nextKey != null) {
|
|
77
|
+
s.setFocusedKey(nextKey);
|
|
78
|
+
}
|
|
79
|
+
} else {
|
|
80
|
+
const firstKey = collection.getFirstKey();
|
|
81
|
+
if (firstKey != null) {
|
|
82
|
+
s.setFocusedKey(firstKey);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
break;
|
|
86
|
+
}
|
|
87
|
+
case 'ArrowUp': {
|
|
88
|
+
e.preventDefault();
|
|
89
|
+
if (focusedKey != null) {
|
|
90
|
+
const prevKey = collection.getKeyBefore(focusedKey);
|
|
91
|
+
if (prevKey != null) {
|
|
92
|
+
s.setFocusedKey(prevKey);
|
|
93
|
+
}
|
|
94
|
+
} else {
|
|
95
|
+
const lastKey = collection.getLastKey();
|
|
96
|
+
if (lastKey != null) {
|
|
97
|
+
s.setFocusedKey(lastKey);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
break;
|
|
101
|
+
}
|
|
102
|
+
case 'Home': {
|
|
103
|
+
e.preventDefault();
|
|
104
|
+
const firstKey = collection.getFirstKey();
|
|
105
|
+
if (firstKey != null) {
|
|
106
|
+
s.setFocusedKey(firstKey);
|
|
107
|
+
}
|
|
108
|
+
break;
|
|
109
|
+
}
|
|
110
|
+
case 'End': {
|
|
111
|
+
e.preventDefault();
|
|
112
|
+
const lastKey = collection.getLastKey();
|
|
113
|
+
if (lastKey != null) {
|
|
114
|
+
s.setFocusedKey(lastKey);
|
|
115
|
+
}
|
|
116
|
+
break;
|
|
117
|
+
}
|
|
118
|
+
case 'a':
|
|
119
|
+
case 'A': {
|
|
120
|
+
if ((e.ctrlKey || e.metaKey) && s.selectionMode === 'multiple') {
|
|
121
|
+
e.preventDefault();
|
|
122
|
+
s.selectAll();
|
|
123
|
+
}
|
|
124
|
+
break;
|
|
125
|
+
}
|
|
126
|
+
case 'Escape': {
|
|
127
|
+
if (s.selectionMode !== 'none') {
|
|
128
|
+
e.preventDefault();
|
|
129
|
+
s.clearSelection();
|
|
130
|
+
}
|
|
131
|
+
break;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
const onFocus = () => {
|
|
137
|
+
const s = state();
|
|
138
|
+
s.setFocused(true);
|
|
139
|
+
|
|
140
|
+
// If nothing is focused, focus the first item
|
|
141
|
+
if (s.focusedKey == null) {
|
|
142
|
+
const firstKey = s.collection.getFirstKey();
|
|
143
|
+
if (firstKey != null) {
|
|
144
|
+
s.setFocusedKey(firstKey);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
const onBlur = () => {
|
|
150
|
+
const s = state();
|
|
151
|
+
s.setFocused(false);
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
const gridProps = createMemo(() => {
|
|
155
|
+
const p = props();
|
|
156
|
+
const s = state();
|
|
157
|
+
|
|
158
|
+
const baseProps: Record<string, unknown> = {
|
|
159
|
+
role: 'grid',
|
|
160
|
+
id: gridListId,
|
|
161
|
+
'aria-label': p['aria-label'],
|
|
162
|
+
'aria-labelledby': p['aria-labelledby'],
|
|
163
|
+
'aria-describedby': p['aria-describedby'],
|
|
164
|
+
'aria-multiselectable': s.selectionMode === 'multiple' ? true : undefined,
|
|
165
|
+
'aria-disabled': p.isDisabled || undefined,
|
|
166
|
+
tabIndex: p.isDisabled ? undefined : 0,
|
|
167
|
+
onKeyDown,
|
|
168
|
+
onFocus,
|
|
169
|
+
onBlur,
|
|
170
|
+
};
|
|
171
|
+
|
|
172
|
+
// Add row count for virtualized lists
|
|
173
|
+
if (p.isVirtualized) {
|
|
174
|
+
baseProps['aria-rowcount'] = s.collection.rowCount;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
return baseProps as JSX.HTMLAttributes<HTMLUListElement>;
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
return {
|
|
181
|
+
get gridProps() {
|
|
182
|
+
return gridProps();
|
|
183
|
+
},
|
|
184
|
+
};
|
|
185
|
+
}
|