@proyecto-viviana/solidaria 0.2.2 → 0.2.4

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.
Files changed (210) hide show
  1. package/dist/autocomplete/createAutocomplete.d.ts +2 -2
  2. package/dist/autocomplete/createAutocomplete.d.ts.map +1 -1
  3. package/dist/index.js +233 -234
  4. package/dist/index.js.map +2 -2
  5. package/dist/index.ssr.js +233 -234
  6. package/dist/index.ssr.js.map +2 -2
  7. package/dist/interactions/PressEvent.d.ts +13 -10
  8. package/dist/interactions/PressEvent.d.ts.map +1 -1
  9. package/dist/interactions/createPress.d.ts.map +1 -1
  10. package/dist/interactions/index.d.ts +1 -1
  11. package/dist/interactions/index.d.ts.map +1 -1
  12. package/dist/select/createHiddenSelect.d.ts.map +1 -1
  13. package/dist/toolbar/createToolbar.d.ts.map +1 -1
  14. package/dist/tooltip/createTooltipTrigger.d.ts.map +1 -1
  15. package/package.json +9 -7
  16. package/src/autocomplete/createAutocomplete.ts +341 -0
  17. package/src/autocomplete/index.ts +9 -0
  18. package/src/breadcrumbs/createBreadcrumbs.ts +196 -0
  19. package/src/breadcrumbs/index.ts +8 -0
  20. package/src/button/createButton.ts +142 -0
  21. package/src/button/createToggleButton.ts +101 -0
  22. package/src/button/index.ts +4 -0
  23. package/src/button/types.ts +78 -0
  24. package/src/calendar/createCalendar.ts +138 -0
  25. package/src/calendar/createCalendarCell.ts +187 -0
  26. package/src/calendar/createCalendarGrid.ts +140 -0
  27. package/src/calendar/createRangeCalendar.ts +136 -0
  28. package/src/calendar/createRangeCalendarCell.ts +186 -0
  29. package/src/calendar/index.ts +34 -0
  30. package/src/checkbox/createCheckbox.ts +135 -0
  31. package/src/checkbox/createCheckboxGroup.ts +137 -0
  32. package/src/checkbox/createCheckboxGroupItem.ts +117 -0
  33. package/src/checkbox/createCheckboxGroupState.ts +193 -0
  34. package/src/checkbox/index.ts +13 -0
  35. package/src/color/createColorArea.ts +314 -0
  36. package/src/color/createColorField.ts +137 -0
  37. package/src/color/createColorSlider.ts +197 -0
  38. package/src/color/createColorSwatch.ts +40 -0
  39. package/src/color/createColorWheel.ts +208 -0
  40. package/src/color/index.ts +24 -0
  41. package/src/color/types.ts +116 -0
  42. package/src/combobox/createComboBox.ts +647 -0
  43. package/src/combobox/index.ts +6 -0
  44. package/src/combobox/intl/en-US.json +7 -0
  45. package/src/combobox/intl/es-ES.json +7 -0
  46. package/src/combobox/intl/index.ts +23 -0
  47. package/src/datepicker/createDateField.ts +154 -0
  48. package/src/datepicker/createDatePicker.ts +206 -0
  49. package/src/datepicker/createDateSegment.ts +229 -0
  50. package/src/datepicker/createTimeField.ts +154 -0
  51. package/src/datepicker/index.ts +28 -0
  52. package/src/dialog/createDialog.ts +120 -0
  53. package/src/dialog/index.ts +2 -0
  54. package/src/dialog/types.ts +19 -0
  55. package/src/disclosure/createDisclosure.ts +131 -0
  56. package/src/disclosure/createDisclosureGroup.ts +62 -0
  57. package/src/disclosure/index.ts +11 -0
  58. package/src/dnd/createDrag.ts +209 -0
  59. package/src/dnd/createDraggableCollection.ts +63 -0
  60. package/src/dnd/createDraggableItem.ts +243 -0
  61. package/src/dnd/createDrop.ts +321 -0
  62. package/src/dnd/createDroppableCollection.ts +293 -0
  63. package/src/dnd/createDroppableItem.ts +213 -0
  64. package/src/dnd/index.ts +47 -0
  65. package/src/dnd/types.ts +89 -0
  66. package/src/dnd/utils.ts +294 -0
  67. package/src/focus/FocusScope.tsx +408 -0
  68. package/src/focus/createAutoFocus.ts +321 -0
  69. package/src/focus/createFocusRestore.ts +313 -0
  70. package/src/focus/createVirtualFocus.ts +396 -0
  71. package/src/focus/index.ts +35 -0
  72. package/src/form/createFormReset.ts +51 -0
  73. package/src/form/createFormValidation.ts +224 -0
  74. package/src/form/index.ts +11 -0
  75. package/src/grid/GridKeyboardDelegate.ts +429 -0
  76. package/src/grid/createGrid.ts +261 -0
  77. package/src/grid/createGridCell.ts +182 -0
  78. package/src/grid/createGridRow.ts +153 -0
  79. package/src/grid/index.ts +18 -0
  80. package/src/grid/types.ts +133 -0
  81. package/src/gridlist/createGridList.ts +185 -0
  82. package/src/gridlist/createGridListItem.ts +180 -0
  83. package/src/gridlist/createGridListSelectionCheckbox.ts +59 -0
  84. package/src/gridlist/index.ts +16 -0
  85. package/src/gridlist/types.ts +81 -0
  86. package/src/i18n/NumberFormatter.ts +266 -0
  87. package/src/i18n/createCollator.ts +79 -0
  88. package/src/i18n/createDateFormatter.ts +83 -0
  89. package/src/i18n/createFilter.ts +131 -0
  90. package/src/i18n/createNumberFormatter.ts +52 -0
  91. package/src/i18n/createStringFormatter.ts +87 -0
  92. package/src/i18n/index.ts +40 -0
  93. package/src/i18n/locale.tsx +188 -0
  94. package/src/i18n/utils.ts +99 -0
  95. package/src/index.ts +670 -0
  96. package/src/interactions/FocusableProvider.tsx +44 -0
  97. package/src/interactions/PressEvent.ts +126 -0
  98. package/src/interactions/createFocus.ts +163 -0
  99. package/src/interactions/createFocusRing.ts +89 -0
  100. package/src/interactions/createFocusWithin.ts +206 -0
  101. package/src/interactions/createFocusable.ts +168 -0
  102. package/src/interactions/createHover.ts +254 -0
  103. package/src/interactions/createInteractionModality.ts +424 -0
  104. package/src/interactions/createKeyboard.ts +82 -0
  105. package/src/interactions/createLongPress.ts +174 -0
  106. package/src/interactions/createMove.ts +289 -0
  107. package/src/interactions/createPress.ts +834 -0
  108. package/src/interactions/index.ts +78 -0
  109. package/src/label/createField.ts +145 -0
  110. package/src/label/createLabel.ts +117 -0
  111. package/src/label/createLabels.ts +50 -0
  112. package/src/label/index.ts +19 -0
  113. package/src/landmark/createLandmark.ts +377 -0
  114. package/src/landmark/index.ts +8 -0
  115. package/src/link/createLink.ts +182 -0
  116. package/src/link/index.ts +1 -0
  117. package/src/listbox/createListBox.ts +269 -0
  118. package/src/listbox/createOption.ts +151 -0
  119. package/src/listbox/index.ts +12 -0
  120. package/src/live-announcer/announce.ts +322 -0
  121. package/src/live-announcer/index.ts +9 -0
  122. package/src/menu/createMenu.ts +396 -0
  123. package/src/menu/createMenuItem.ts +149 -0
  124. package/src/menu/createMenuTrigger.ts +88 -0
  125. package/src/menu/index.ts +18 -0
  126. package/src/meter/createMeter.ts +75 -0
  127. package/src/meter/index.ts +1 -0
  128. package/src/numberfield/createNumberField.ts +268 -0
  129. package/src/numberfield/index.ts +5 -0
  130. package/src/overlays/ariaHideOutside.ts +219 -0
  131. package/src/overlays/createInteractOutside.ts +149 -0
  132. package/src/overlays/createModal.tsx +202 -0
  133. package/src/overlays/createOverlay.ts +155 -0
  134. package/src/overlays/createOverlayTrigger.ts +85 -0
  135. package/src/overlays/createPreventScroll.ts +266 -0
  136. package/src/overlays/index.ts +44 -0
  137. package/src/popover/calculatePosition.ts +766 -0
  138. package/src/popover/createOverlayPosition.ts +356 -0
  139. package/src/popover/createPopover.ts +170 -0
  140. package/src/popover/index.ts +24 -0
  141. package/src/progress/createProgressBar.ts +128 -0
  142. package/src/progress/index.ts +5 -0
  143. package/src/radio/createRadio.ts +287 -0
  144. package/src/radio/createRadioGroup.ts +189 -0
  145. package/src/radio/createRadioGroupState.ts +201 -0
  146. package/src/radio/index.ts +23 -0
  147. package/src/searchfield/createSearchField.ts +186 -0
  148. package/src/searchfield/index.ts +2 -0
  149. package/src/select/createHiddenSelect.tsx +236 -0
  150. package/src/select/createSelect.ts +395 -0
  151. package/src/select/index.ts +14 -0
  152. package/src/selection/createTypeSelect.ts +201 -0
  153. package/src/selection/index.ts +6 -0
  154. package/src/separator/createSeparator.ts +82 -0
  155. package/src/separator/index.ts +6 -0
  156. package/src/slider/createSlider.ts +349 -0
  157. package/src/slider/index.ts +2 -0
  158. package/src/ssr/index.tsx +370 -0
  159. package/src/switch/createSwitch.ts +70 -0
  160. package/src/switch/index.ts +1 -0
  161. package/src/table/createTable.ts +526 -0
  162. package/src/table/createTableCell.ts +147 -0
  163. package/src/table/createTableColumnHeader.ts +115 -0
  164. package/src/table/createTableHeaderRow.ts +40 -0
  165. package/src/table/createTableRow.ts +155 -0
  166. package/src/table/createTableRowGroup.ts +32 -0
  167. package/src/table/createTableSelectAllCheckbox.ts +73 -0
  168. package/src/table/createTableSelectionCheckbox.ts +59 -0
  169. package/src/table/index.ts +30 -0
  170. package/src/table/types.ts +165 -0
  171. package/src/tabs/createTabs.ts +472 -0
  172. package/src/tabs/index.ts +14 -0
  173. package/src/tag/createTag.ts +194 -0
  174. package/src/tag/createTagGroup.ts +154 -0
  175. package/src/tag/index.ts +12 -0
  176. package/src/textfield/createTextField.ts +198 -0
  177. package/src/textfield/index.ts +5 -0
  178. package/src/toast/createToast.ts +118 -0
  179. package/src/toast/createToastRegion.ts +100 -0
  180. package/src/toast/index.ts +11 -0
  181. package/src/toggle/createToggle.ts +223 -0
  182. package/src/toggle/createToggleState.ts +94 -0
  183. package/src/toggle/index.ts +7 -0
  184. package/src/toolbar/createToolbar.ts +369 -0
  185. package/src/toolbar/index.ts +6 -0
  186. package/src/tooltip/createTooltip.ts +79 -0
  187. package/src/tooltip/createTooltipTrigger.ts +222 -0
  188. package/src/tooltip/index.ts +6 -0
  189. package/src/tree/createTree.ts +246 -0
  190. package/src/tree/createTreeItem.ts +233 -0
  191. package/src/tree/createTreeSelectionCheckbox.ts +68 -0
  192. package/src/tree/index.ts +16 -0
  193. package/src/tree/types.ts +87 -0
  194. package/src/utils/createDescription.ts +137 -0
  195. package/src/utils/dom.ts +327 -0
  196. package/src/utils/env.ts +54 -0
  197. package/src/utils/events.ts +106 -0
  198. package/src/utils/filterDOMProps.ts +116 -0
  199. package/src/utils/focus.ts +151 -0
  200. package/src/utils/geometry.ts +115 -0
  201. package/src/utils/globalListeners.ts +142 -0
  202. package/src/utils/index.ts +80 -0
  203. package/src/utils/mergeProps.ts +52 -0
  204. package/src/utils/platform.ts +52 -0
  205. package/src/utils/reactivity.ts +36 -0
  206. package/src/utils/textSelection.ts +114 -0
  207. package/src/visually-hidden/createVisuallyHidden.ts +124 -0
  208. package/src/visually-hidden/index.ts +6 -0
  209. package/dist/index.jsx +0 -15845
  210. package/dist/index.jsx.map +0 -7
@@ -0,0 +1,246 @@
1
+ /**
2
+ * createTree - Provides accessibility for a tree component.
3
+ * Based on @react-aria/tree/useTree.
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 { TreeState, TreeCollection, Key } from '@proyecto-viviana/solid-stately';
10
+ import type { AriaTreeProps, TreeAria } from './types';
11
+
12
+ /**
13
+ * Metadata stored for a tree instance.
14
+ */
15
+ interface TreeData {
16
+ /** The generated ID for the tree. */
17
+ treeId: string;
18
+ /** Actions registered for the tree. */
19
+ actions: {
20
+ onAction?: (key: Key) => void;
21
+ };
22
+ }
23
+
24
+ /**
25
+ * WeakMap to store tree data for child components to access.
26
+ */
27
+ const treeDataMap = new WeakMap<object, TreeData>();
28
+
29
+ /**
30
+ * Gets the tree data for a given state.
31
+ */
32
+ export function getTreeData<T extends object, C extends TreeCollection<T>>(
33
+ state: TreeState<T, C>
34
+ ): TreeData | undefined {
35
+ return treeDataMap.get(state);
36
+ }
37
+
38
+ /**
39
+ * Creates accessibility props for a tree.
40
+ */
41
+ export function createTree<T extends object, C extends TreeCollection<T> = TreeCollection<T>>(
42
+ props: Accessor<AriaTreeProps>,
43
+ state: Accessor<TreeState<T, C>>,
44
+ _ref: Accessor<HTMLDivElement | null>
45
+ ): TreeAria {
46
+ // Generate a unique ID for the tree
47
+ const treeId = props().id ?? createId();
48
+
49
+ // Store tree data for child components
50
+ const treeData: TreeData = {
51
+ treeId,
52
+ actions: {
53
+ get onAction() {
54
+ return props().onAction;
55
+ },
56
+ },
57
+ };
58
+
59
+ // Store in WeakMap using the state as key
60
+ treeDataMap.set(state(), treeData);
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 'ArrowRight': {
103
+ e.preventDefault();
104
+ if (focusedKey != null) {
105
+ const node = collection.getItem(focusedKey);
106
+ if (node?.isExpandable) {
107
+ if (!s.isExpanded(focusedKey)) {
108
+ // Expand the node
109
+ s.expandKey(focusedKey);
110
+ } else {
111
+ // Move to first child
112
+ const children = [...collection.getChildren(focusedKey)];
113
+ if (children.length > 0) {
114
+ s.setFocusedKey(children[0].key);
115
+ }
116
+ }
117
+ }
118
+ }
119
+ break;
120
+ }
121
+ case 'ArrowLeft': {
122
+ e.preventDefault();
123
+ if (focusedKey != null) {
124
+ const node = collection.getItem(focusedKey);
125
+ if (node?.isExpandable && s.isExpanded(focusedKey)) {
126
+ // Collapse the node
127
+ s.collapseKey(focusedKey);
128
+ } else if (node?.parentKey != null) {
129
+ // Move to parent
130
+ s.setFocusedKey(node.parentKey);
131
+ }
132
+ }
133
+ break;
134
+ }
135
+ case 'Home': {
136
+ e.preventDefault();
137
+ const firstKey = collection.getFirstKey();
138
+ if (firstKey != null) {
139
+ s.setFocusedKey(firstKey);
140
+ }
141
+ break;
142
+ }
143
+ case 'End': {
144
+ e.preventDefault();
145
+ const lastKey = collection.getLastKey();
146
+ if (lastKey != null) {
147
+ s.setFocusedKey(lastKey);
148
+ }
149
+ break;
150
+ }
151
+ case 'a':
152
+ case 'A': {
153
+ if ((e.ctrlKey || e.metaKey) && s.selectionMode === 'multiple') {
154
+ e.preventDefault();
155
+ s.selectAll();
156
+ }
157
+ break;
158
+ }
159
+ case 'Escape': {
160
+ if (s.selectionMode !== 'none') {
161
+ e.preventDefault();
162
+ s.clearSelection();
163
+ }
164
+ break;
165
+ }
166
+ case '*': {
167
+ // Expand all siblings at current level
168
+ e.preventDefault();
169
+ if (focusedKey != null) {
170
+ const node = collection.getItem(focusedKey);
171
+ if (node) {
172
+ // Find all siblings at the same level
173
+ const parentKey = node.parentKey;
174
+ let siblings: Key[];
175
+ if (parentKey != null) {
176
+ siblings = [...collection.getChildren(parentKey)].map((n) => n.key);
177
+ } else {
178
+ // Root level siblings
179
+ siblings = collection.rows
180
+ .filter((n) => n.level === 0)
181
+ .map((n) => n.key);
182
+ }
183
+ // Expand all expandable siblings
184
+ for (const siblingKey of siblings) {
185
+ const sibling = collection.getItem(siblingKey);
186
+ if (sibling?.isExpandable && !s.isExpanded(siblingKey)) {
187
+ s.expandKey(siblingKey);
188
+ }
189
+ }
190
+ }
191
+ }
192
+ break;
193
+ }
194
+ }
195
+ };
196
+
197
+ const onFocus = () => {
198
+ const s = state();
199
+ s.setFocused(true);
200
+
201
+ // If nothing is focused, focus the first item
202
+ if (s.focusedKey == null) {
203
+ const firstKey = s.collection.getFirstKey();
204
+ if (firstKey != null) {
205
+ s.setFocusedKey(firstKey);
206
+ }
207
+ }
208
+ };
209
+
210
+ const onBlur = () => {
211
+ const s = state();
212
+ s.setFocused(false);
213
+ };
214
+
215
+ const treeProps = createMemo(() => {
216
+ const p = props();
217
+ const s = state();
218
+
219
+ const baseProps: Record<string, unknown> = {
220
+ role: 'treegrid',
221
+ id: treeId,
222
+ 'aria-label': p['aria-label'],
223
+ 'aria-labelledby': p['aria-labelledby'],
224
+ 'aria-describedby': p['aria-describedby'],
225
+ 'aria-multiselectable': s.selectionMode === 'multiple' ? true : undefined,
226
+ 'aria-disabled': p.isDisabled || undefined,
227
+ tabIndex: p.isDisabled ? undefined : 0,
228
+ onKeyDown,
229
+ onFocus,
230
+ onBlur,
231
+ };
232
+
233
+ // Add row count for virtualized trees
234
+ if (p.isVirtualized) {
235
+ baseProps['aria-rowcount'] = s.collection.rowCount;
236
+ }
237
+
238
+ return baseProps as JSX.HTMLAttributes<HTMLDivElement>;
239
+ });
240
+
241
+ return {
242
+ get treeProps() {
243
+ return treeProps();
244
+ },
245
+ };
246
+ }
@@ -0,0 +1,233 @@
1
+ /**
2
+ * createTreeItem - Provides accessibility for a tree item.
3
+ * Based on @react-aria/tree/useTreeItem.
4
+ */
5
+
6
+ import { createMemo, createSignal, type Accessor } from 'solid-js';
7
+ import type { JSX } from 'solid-js';
8
+ import type { TreeState, TreeCollection } from '@proyecto-viviana/solid-stately';
9
+ import type { AriaTreeItemProps, TreeItemAria } from './types';
10
+ import { getTreeData } from './createTree';
11
+
12
+ /**
13
+ * Creates accessibility props for a tree item.
14
+ */
15
+ export function createTreeItem<T extends object, C extends TreeCollection<T> = TreeCollection<T>>(
16
+ props: Accessor<AriaTreeItemProps<T>>,
17
+ state: Accessor<TreeState<T, C>>,
18
+ _ref: Accessor<HTMLDivElement | null>
19
+ ): TreeItemAria {
20
+ const [isPressed, setIsPressed] = createSignal(false);
21
+
22
+ const isSelected = createMemo(() => {
23
+ const s = state();
24
+ const p = props();
25
+ return s.isSelected(p.node.key);
26
+ });
27
+
28
+ const isDisabled = createMemo(() => {
29
+ const s = state();
30
+ const p = props();
31
+ return s.isDisabled(p.node.key);
32
+ });
33
+
34
+ const isFocused = createMemo(() => {
35
+ const s = state();
36
+ const p = props();
37
+ return s.focusedKey === p.node.key;
38
+ });
39
+
40
+ const isExpanded = createMemo(() => {
41
+ const s = state();
42
+ const p = props();
43
+ return s.isExpanded(p.node.key);
44
+ });
45
+
46
+ const isExpandable = createMemo(() => {
47
+ const p = props();
48
+ return p.node.isExpandable ?? false;
49
+ });
50
+
51
+ const level = createMemo(() => {
52
+ const p = props();
53
+ return p.node.level;
54
+ });
55
+
56
+ // Handle click/press for selection and actions
57
+ const onClick = (e: MouseEvent) => {
58
+ const s = state();
59
+ const p = props();
60
+
61
+ if (isDisabled()) return;
62
+
63
+ // Get tree metadata for actions
64
+ const treeData = getTreeData(s);
65
+ const onAction = treeData?.actions.onAction;
66
+
67
+ // Handle selection
68
+ if (s.selectionMode !== 'none') {
69
+ if (e.shiftKey && s.selectionMode === 'multiple') {
70
+ s.extendSelection(p.node.key);
71
+ } else if (e.ctrlKey || e.metaKey) {
72
+ s.toggleSelection(p.node.key);
73
+ } else {
74
+ // Replace selection or toggle if already selected
75
+ if (isSelected() && s.selectedKeys !== 'all') {
76
+ const selectedKeys = s.selectedKeys as Set<unknown>;
77
+ if (selectedKeys.size === 1) {
78
+ // Single selection, trigger action
79
+ if (onAction) {
80
+ onAction(p.node.key);
81
+ }
82
+ if (p.onAction) {
83
+ p.onAction();
84
+ }
85
+ } else {
86
+ s.replaceSelection(p.node.key);
87
+ }
88
+ } else {
89
+ s.replaceSelection(p.node.key);
90
+ }
91
+ }
92
+ } else {
93
+ // No selection mode, just trigger action
94
+ if (onAction) {
95
+ onAction(p.node.key);
96
+ }
97
+ if (p.onAction) {
98
+ p.onAction();
99
+ }
100
+ }
101
+ };
102
+
103
+ const onKeyDown = (e: KeyboardEvent) => {
104
+ const s = state();
105
+ const p = props();
106
+
107
+ if (isDisabled()) return;
108
+
109
+ if (e.key === 'Enter') {
110
+ // Get tree metadata for actions
111
+ const treeData = getTreeData(s);
112
+ const onAction = treeData?.actions.onAction;
113
+
114
+ if (onAction || p.onAction) {
115
+ e.preventDefault();
116
+
117
+ if (onAction) {
118
+ onAction(p.node.key);
119
+ }
120
+
121
+ if (p.onAction) {
122
+ p.onAction();
123
+ }
124
+ }
125
+ } else if (e.key === ' ') {
126
+ // Space toggles selection
127
+ if (s.selectionMode !== 'none') {
128
+ e.preventDefault();
129
+ s.toggleSelection(p.node.key);
130
+ }
131
+ }
132
+ };
133
+
134
+ const onFocus = () => {
135
+ const s = state();
136
+ const p = props();
137
+ s.setFocusedKey(p.node.key);
138
+ };
139
+
140
+ const onPointerDown = () => {
141
+ setIsPressed(true);
142
+ };
143
+
144
+ const onPointerUp = () => {
145
+ setIsPressed(false);
146
+ };
147
+
148
+ const rowProps = createMemo(() => {
149
+ const s = state();
150
+ const p = props();
151
+ const node = p.node;
152
+
153
+ const baseProps: Record<string, unknown> = {
154
+ role: 'row',
155
+ 'aria-selected': s.selectionMode !== 'none' ? isSelected() : undefined,
156
+ 'aria-disabled': isDisabled() || undefined,
157
+ 'aria-expanded': isExpandable() ? isExpanded() : undefined,
158
+ 'aria-level': node.level + 1, // 1-based for ARIA
159
+ tabIndex: isFocused() ? 0 : -1,
160
+ onClick,
161
+ onKeyDown,
162
+ onFocus,
163
+ onPointerDown,
164
+ onPointerUp,
165
+ };
166
+
167
+ // Add aria-rowindex for virtualized trees
168
+ if (p.isVirtualized && node.rowIndex != null) {
169
+ baseProps['aria-rowindex'] = node.rowIndex + 1; // 1-based
170
+ }
171
+
172
+ return baseProps as JSX.HTMLAttributes<HTMLDivElement>;
173
+ });
174
+
175
+ const gridCellProps = createMemo(() => {
176
+ return {
177
+ role: 'gridcell',
178
+ } as JSX.HTMLAttributes<HTMLDivElement>;
179
+ });
180
+
181
+ // Expand button handler
182
+ const onExpandClick = (e: MouseEvent) => {
183
+ e.stopPropagation(); // Don't trigger row click
184
+ const s = state();
185
+ const p = props();
186
+
187
+ if (isDisabled()) return;
188
+
189
+ s.toggleKey(p.node.key);
190
+ };
191
+
192
+ const expandButtonProps = createMemo(() => {
193
+ const baseProps: Record<string, unknown> = {
194
+ type: 'button',
195
+ 'aria-label': isExpanded() ? 'Collapse' : 'Expand',
196
+ onClick: onExpandClick,
197
+ tabIndex: -1, // Not in tab order, use arrow keys
198
+ 'aria-hidden': !isExpandable() ? true : undefined,
199
+ };
200
+
201
+ return baseProps as JSX.ButtonHTMLAttributes<HTMLButtonElement>;
202
+ });
203
+
204
+ return {
205
+ get rowProps() {
206
+ return rowProps();
207
+ },
208
+ get gridCellProps() {
209
+ return gridCellProps();
210
+ },
211
+ get expandButtonProps() {
212
+ return expandButtonProps();
213
+ },
214
+ get isSelected() {
215
+ return isSelected();
216
+ },
217
+ get isDisabled() {
218
+ return isDisabled();
219
+ },
220
+ get isPressed() {
221
+ return isPressed();
222
+ },
223
+ get isExpanded() {
224
+ return isExpanded();
225
+ },
226
+ get isExpandable() {
227
+ return isExpandable();
228
+ },
229
+ get level() {
230
+ return level();
231
+ },
232
+ };
233
+ }
@@ -0,0 +1,68 @@
1
+ /**
2
+ * createTreeSelectionCheckbox - Provides accessibility for a tree item's selection checkbox.
3
+ * Based on @react-aria/gridlist/useGridListSelectionCheckbox.
4
+ */
5
+
6
+ import { createMemo, type Accessor } from 'solid-js';
7
+ import type { JSX } from 'solid-js';
8
+ import type { TreeState, TreeCollection } from '@proyecto-viviana/solid-stately';
9
+ import type { AriaTreeSelectionCheckboxProps, TreeSelectionCheckboxAria } from './types';
10
+
11
+ /**
12
+ * Creates accessibility props for a tree selection checkbox.
13
+ */
14
+ export function createTreeSelectionCheckbox<T extends object, C extends TreeCollection<T> = TreeCollection<T>>(
15
+ props: Accessor<AriaTreeSelectionCheckboxProps>,
16
+ state: Accessor<TreeState<T, C>>
17
+ ): TreeSelectionCheckboxAria {
18
+ const isSelected = createMemo(() => {
19
+ const s = state();
20
+ const p = props();
21
+ return s.isSelected(p.key);
22
+ });
23
+
24
+ const isDisabled = createMemo(() => {
25
+ const s = state();
26
+ const p = props();
27
+ return s.isDisabled(p.key);
28
+ });
29
+
30
+ const onChange = (e: Event) => {
31
+ const s = state();
32
+ const p = props();
33
+ const target = e.target as HTMLInputElement;
34
+
35
+ if (isDisabled()) return;
36
+
37
+ if (target.checked) {
38
+ s.toggleSelection(p.key);
39
+ } else {
40
+ s.toggleSelection(p.key);
41
+ }
42
+ };
43
+
44
+ const onClick = (e: MouseEvent) => {
45
+ // Stop propagation to prevent row click from also firing
46
+ e.stopPropagation();
47
+ };
48
+
49
+ const checkboxProps = createMemo(() => {
50
+ const baseProps: Record<string, unknown> = {
51
+ type: 'checkbox',
52
+ 'aria-label': 'Select',
53
+ checked: isSelected(),
54
+ disabled: isDisabled(),
55
+ onChange,
56
+ onClick,
57
+ tabIndex: -1, // Use arrow keys to navigate, not tab
58
+ };
59
+
60
+ return baseProps as JSX.InputHTMLAttributes<HTMLInputElement>;
61
+ });
62
+
63
+ return {
64
+ get checkboxProps() {
65
+ return checkboxProps();
66
+ },
67
+ };
68
+ }
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Tree ARIA layer exports.
3
+ */
4
+
5
+ export { createTree, getTreeData } from './createTree';
6
+ export { createTreeItem } from './createTreeItem';
7
+ export { createTreeSelectionCheckbox } from './createTreeSelectionCheckbox';
8
+
9
+ export type {
10
+ AriaTreeProps,
11
+ TreeAria,
12
+ AriaTreeItemProps,
13
+ TreeItemAria,
14
+ AriaTreeSelectionCheckboxProps,
15
+ TreeSelectionCheckboxAria,
16
+ } from './types';
@@ -0,0 +1,87 @@
1
+ /**
2
+ * Tree ARIA types.
3
+ * Based on @react-aria/tree.
4
+ */
5
+
6
+ import type { JSX } from 'solid-js';
7
+ import type { Key, TreeNode } from '@proyecto-viviana/solid-stately';
8
+
9
+ /**
10
+ * Props for createTree.
11
+ */
12
+ export interface AriaTreeProps {
13
+ /** The unique id for the tree. */
14
+ id?: string;
15
+ /** Label for accessibility. */
16
+ 'aria-label'?: string;
17
+ /** ID of an element that labels the tree. */
18
+ 'aria-labelledby'?: string;
19
+ /** ID of an element that describes the tree. */
20
+ 'aria-describedby'?: string;
21
+ /** Whether the tree is virtualized. */
22
+ isVirtualized?: boolean;
23
+ /** Handler called when an item action is triggered. */
24
+ onAction?: (key: Key) => void;
25
+ /** Whether the tree is disabled. */
26
+ isDisabled?: boolean;
27
+ }
28
+
29
+ /**
30
+ * Return value for createTree.
31
+ */
32
+ export interface TreeAria {
33
+ /** Props for the tree container (role="treegrid"). */
34
+ treeProps: JSX.HTMLAttributes<HTMLDivElement>;
35
+ }
36
+
37
+ /**
38
+ * Props for createTreeItem.
39
+ */
40
+ export interface AriaTreeItemProps<T = unknown> {
41
+ /** The tree node this item represents. */
42
+ node: TreeNode<T>;
43
+ /** Whether the item is rendered in a virtualized list. */
44
+ isVirtualized?: boolean;
45
+ /** Handler called when this item's action is triggered. */
46
+ onAction?: () => void;
47
+ }
48
+
49
+ /**
50
+ * Return value for createTreeItem.
51
+ */
52
+ export interface TreeItemAria {
53
+ /** Props for the row element. */
54
+ rowProps: JSX.HTMLAttributes<HTMLDivElement>;
55
+ /** Props for the grid cell content wrapper. */
56
+ gridCellProps: JSX.HTMLAttributes<HTMLDivElement>;
57
+ /** Props for the expand button (if the item is expandable). */
58
+ expandButtonProps: JSX.ButtonHTMLAttributes<HTMLButtonElement>;
59
+ /** Whether the item is selected. */
60
+ isSelected: boolean;
61
+ /** Whether the item is disabled. */
62
+ isDisabled: boolean;
63
+ /** Whether the item is being pressed. */
64
+ isPressed: boolean;
65
+ /** Whether the item is expanded. */
66
+ isExpanded: boolean;
67
+ /** Whether the item is expandable (has children). */
68
+ isExpandable: boolean;
69
+ /** The nesting level of the item (0 for root). */
70
+ level: number;
71
+ }
72
+
73
+ /**
74
+ * Props for createTreeSelectionCheckbox.
75
+ */
76
+ export interface AriaTreeSelectionCheckboxProps {
77
+ /** The key of the tree item this checkbox belongs to. */
78
+ key: Key;
79
+ }
80
+
81
+ /**
82
+ * Return value for createTreeSelectionCheckbox.
83
+ */
84
+ export interface TreeSelectionCheckboxAria {
85
+ /** Props for the checkbox input element. */
86
+ checkboxProps: JSX.InputHTMLAttributes<HTMLInputElement>;
87
+ }