@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,321 @@
1
+ /**
2
+ * createDrop - ARIA hook for drop operations.
3
+ *
4
+ * Provides accessibility props for drop target elements with support for
5
+ * mouse, touch, and keyboard interactions.
6
+ */
7
+
8
+ import { createMemo, type Accessor } from 'solid-js';
9
+ import { createDropState } from '@proyecto-viviana/solid-stately';
10
+ import type { AriaDropOptions, DropAria } from './types';
11
+ import {
12
+ readFromDataTransfer,
13
+ DragTypesImpl,
14
+ DROP_OPERATION,
15
+ DROP_OPERATION_ALLOWED,
16
+ DROP_OPERATION_TO_DROP_EFFECT,
17
+ DROP_EFFECT_TO_DROP_OPERATION,
18
+ setGlobalDropEffect,
19
+ getGlobalAllowedDropOperations,
20
+ } from './utils';
21
+ import type { DropOperation } from '@proyecto-viviana/solid-stately';
22
+
23
+ const DROP_ACTIVATE_TIMEOUT = 800;
24
+
25
+ /**
26
+ * Creates ARIA props for a drop target element.
27
+ *
28
+ * @param props - Accessor returning drop options
29
+ * @returns Drop ARIA props and state
30
+ */
31
+ export function createDrop(props: Accessor<AriaDropOptions>): DropAria {
32
+ const getProps = createMemo(() => props());
33
+
34
+ // Create drop state
35
+ const state = createDropState(() => ({
36
+ getDropOperation: getProps().getDropOperation,
37
+ onDropEnter: getProps().onDropEnter,
38
+ onDropMove: getProps().onDropMove,
39
+ onDropActivate: getProps().onDropActivate,
40
+ onDropExit: getProps().onDropExit,
41
+ onDrop: getProps().onDrop,
42
+ isDisabled: getProps().isDisabled,
43
+ }));
44
+
45
+ // Track internal state
46
+ let x = 0;
47
+ let y = 0;
48
+ let dragOverElements = new Set<Element>();
49
+ let dropEffect: DataTransfer['dropEffect'] = 'none';
50
+ let allowedOperations = DROP_OPERATION.all;
51
+ let dropActivateTimer: ReturnType<typeof setTimeout> | undefined;
52
+
53
+ const fireDropEnter = (e: DragEvent) => {
54
+ const rect = (e.currentTarget as HTMLElement).getBoundingClientRect();
55
+ state.enterTarget(e.clientX - rect.x, e.clientY - rect.y);
56
+ };
57
+
58
+ const fireDropExit = (e: DragEvent) => {
59
+ const rect = (e.currentTarget as HTMLElement).getBoundingClientRect();
60
+ state.exitTarget(e.clientX - rect.x, e.clientY - rect.y);
61
+ };
62
+
63
+ const getAllowedOperations = (e: DragEvent): number => {
64
+ let allowed = DROP_OPERATION_ALLOWED[e.dataTransfer?.effectAllowed ?? 'none'] ?? DROP_OPERATION.none;
65
+
66
+ // Use global allowed operations if set (for internal drags)
67
+ const globalAllowed = getGlobalAllowedDropOperations();
68
+ if (globalAllowed) {
69
+ allowed &= globalAllowed;
70
+ }
71
+
72
+ // Handle modifier keys for operation switching
73
+ let modifierAllowed = DROP_OPERATION.none;
74
+
75
+ // macOS: Alt=copy, Ctrl=link, Cmd=move
76
+ // Windows/Linux: Alt=link, Shift=move, Ctrl=copy
77
+ const isMac = typeof navigator !== 'undefined' && /mac/i.test(navigator.platform);
78
+
79
+ if (isMac) {
80
+ if (e.altKey) modifierAllowed |= DROP_OPERATION.copy;
81
+ if (e.ctrlKey) modifierAllowed |= DROP_OPERATION.link;
82
+ if (e.metaKey) modifierAllowed |= DROP_OPERATION.move;
83
+ } else {
84
+ if (e.altKey) modifierAllowed |= DROP_OPERATION.link;
85
+ if (e.shiftKey) modifierAllowed |= DROP_OPERATION.move;
86
+ if (e.ctrlKey) modifierAllowed |= DROP_OPERATION.copy;
87
+ }
88
+
89
+ if (modifierAllowed) {
90
+ return allowed & modifierAllowed;
91
+ }
92
+
93
+ return allowed;
94
+ };
95
+
96
+ const allowedOperationsToArray = (ops: number): DropOperation[] => {
97
+ const result: DropOperation[] = [];
98
+ if (ops & DROP_OPERATION.move) result.push('move');
99
+ if (ops & DROP_OPERATION.copy) result.push('copy');
100
+ if (ops & DROP_OPERATION.link) result.push('link');
101
+ return result;
102
+ };
103
+
104
+ const getDropOperationForAllowed = (allowed: number, operation: DropOperation): DropOperation => {
105
+ const op = DROP_OPERATION[operation];
106
+ return allowed & op ? operation : 'cancel';
107
+ };
108
+
109
+ const onDragEnter = (e: DragEvent) => {
110
+ e.preventDefault();
111
+ e.stopPropagation();
112
+
113
+ dragOverElements.add(e.target as Element);
114
+ if (dragOverElements.size > 1) {
115
+ return;
116
+ }
117
+
118
+ const p = getProps();
119
+ const allowedOpsBits = getAllowedOperations(e);
120
+ const allowedOps = allowedOperationsToArray(allowedOpsBits);
121
+ let dropOp: DropOperation = allowedOps[0] ?? 'cancel';
122
+
123
+ if (typeof p.getDropOperation === 'function' && e.dataTransfer) {
124
+ const types = new DragTypesImpl(e.dataTransfer);
125
+ dropOp = getDropOperationForAllowed(
126
+ allowedOpsBits,
127
+ p.getDropOperation(types, allowedOps)
128
+ );
129
+ }
130
+
131
+ if (typeof p.getDropOperationForPoint === 'function' && e.dataTransfer) {
132
+ const types = new DragTypesImpl(e.dataTransfer);
133
+ const rect = (e.currentTarget as HTMLElement).getBoundingClientRect();
134
+ dropOp = getDropOperationForAllowed(
135
+ allowedOpsBits,
136
+ p.getDropOperationForPoint(types, allowedOps, e.clientX - rect.x, e.clientY - rect.y)
137
+ );
138
+ }
139
+
140
+ x = e.clientX;
141
+ y = e.clientY;
142
+ allowedOperations = allowedOpsBits;
143
+ dropEffect = (DROP_OPERATION_TO_DROP_EFFECT[dropOp] || 'none') as DataTransfer['dropEffect'];
144
+
145
+ if (e.dataTransfer) {
146
+ e.dataTransfer.dropEffect = dropEffect;
147
+ }
148
+
149
+ if (dropOp !== 'cancel') {
150
+ fireDropEnter(e);
151
+ }
152
+ };
153
+
154
+ const onDragOver = (e: DragEvent) => {
155
+ e.preventDefault();
156
+ e.stopPropagation();
157
+
158
+ const allowedOpsBits = getAllowedOperations(e);
159
+
160
+ // Skip if position and operations haven't changed
161
+ if (e.clientX === x && e.clientY === y && allowedOpsBits === allowedOperations) {
162
+ if (e.dataTransfer) {
163
+ e.dataTransfer.dropEffect = dropEffect;
164
+ }
165
+ return;
166
+ }
167
+
168
+ x = e.clientX;
169
+ y = e.clientY;
170
+
171
+ const prevDropEffect = dropEffect;
172
+ const p = getProps();
173
+
174
+ // Update drop effect if allowed operations changed
175
+ if (allowedOpsBits !== allowedOperations) {
176
+ const allowedOps = allowedOperationsToArray(allowedOpsBits);
177
+ let dropOp: DropOperation = allowedOps[0] ?? 'cancel';
178
+
179
+ if (typeof p.getDropOperation === 'function' && e.dataTransfer) {
180
+ const types = new DragTypesImpl(e.dataTransfer);
181
+ dropOp = getDropOperationForAllowed(
182
+ allowedOpsBits,
183
+ p.getDropOperation(types, allowedOps)
184
+ );
185
+ }
186
+ dropEffect = (DROP_OPERATION_TO_DROP_EFFECT[dropOp] || 'none') as DataTransfer['dropEffect'];
187
+ }
188
+
189
+ // Check point-specific operation
190
+ if (typeof p.getDropOperationForPoint === 'function' && e.dataTransfer) {
191
+ const types = new DragTypesImpl(e.dataTransfer);
192
+ const rect = (e.currentTarget as HTMLElement).getBoundingClientRect();
193
+ const dropOp = getDropOperationForAllowed(
194
+ allowedOpsBits,
195
+ p.getDropOperationForPoint(types, allowedOperationsToArray(allowedOpsBits), x - rect.x, y - rect.y)
196
+ );
197
+ dropEffect = (DROP_OPERATION_TO_DROP_EFFECT[dropOp] || 'none') as DataTransfer['dropEffect'];
198
+ }
199
+
200
+ allowedOperations = allowedOpsBits;
201
+
202
+ if (e.dataTransfer) {
203
+ e.dataTransfer.dropEffect = dropEffect;
204
+ }
205
+
206
+ // Fire enter/exit events on drop effect change
207
+ if (dropEffect === 'none' && prevDropEffect !== 'none') {
208
+ fireDropExit(e);
209
+ } else if (dropEffect !== 'none' && prevDropEffect === 'none') {
210
+ fireDropEnter(e);
211
+ }
212
+
213
+ // Fire move event
214
+ if (dropEffect !== 'none') {
215
+ const rect = (e.currentTarget as HTMLElement).getBoundingClientRect();
216
+ state.moveInTarget(x - rect.x, y - rect.y);
217
+ }
218
+
219
+ // Handle drop activate timer
220
+ clearTimeout(dropActivateTimer);
221
+
222
+ if (typeof p.onDropActivate === 'function' && dropEffect !== 'none') {
223
+ const rect = (e.currentTarget as HTMLElement).getBoundingClientRect();
224
+ const activateX = x - rect.x;
225
+ const activateY = y - rect.y;
226
+ dropActivateTimer = setTimeout(() => {
227
+ state.activateTarget(activateX, activateY);
228
+ }, DROP_ACTIVATE_TIMEOUT);
229
+ }
230
+ };
231
+
232
+ const onDragLeave = (e: DragEvent) => {
233
+ e.preventDefault();
234
+ e.stopPropagation();
235
+
236
+ // Track drag over elements (WebKit workaround for relatedTarget being null)
237
+ dragOverElements.delete(e.target as Element);
238
+
239
+ // Remove elements no longer in DOM
240
+ for (const element of dragOverElements) {
241
+ if (!e.currentTarget || !(e.currentTarget as Element).contains(element)) {
242
+ dragOverElements.delete(element);
243
+ }
244
+ }
245
+
246
+ if (dragOverElements.size > 0) {
247
+ return;
248
+ }
249
+
250
+ if (dropEffect !== 'none') {
251
+ fireDropExit(e);
252
+ }
253
+
254
+ clearTimeout(dropActivateTimer);
255
+ };
256
+
257
+ const onDropHandler = (e: DragEvent) => {
258
+ e.preventDefault();
259
+ e.stopPropagation();
260
+
261
+ // Track drop effect globally for Chrome Android
262
+ setGlobalDropEffect(dropEffect);
263
+
264
+ const p = getProps();
265
+ if (typeof p.onDrop === 'function' && e.dataTransfer) {
266
+ const items = readFromDataTransfer(e.dataTransfer);
267
+ const dropOperation = DROP_EFFECT_TO_DROP_OPERATION[dropEffect];
268
+ const rect = (e.currentTarget as HTMLElement).getBoundingClientRect();
269
+
270
+ state.drop(e.clientX - rect.x, e.clientY - rect.y, items, dropOperation);
271
+ }
272
+
273
+ dragOverElements.clear();
274
+ fireDropExit(e);
275
+ clearTimeout(dropActivateTimer);
276
+ };
277
+
278
+ const dropProps = createMemo(() => {
279
+ const p = getProps();
280
+
281
+ if (p.isDisabled) {
282
+ return {};
283
+ }
284
+
285
+ const baseProps: Record<string, unknown> = {
286
+ onDragEnter,
287
+ onDragOver,
288
+ onDragLeave,
289
+ onDrop: onDropHandler,
290
+ };
291
+
292
+ return baseProps;
293
+ });
294
+
295
+ const dropButtonProps = createMemo(() => {
296
+ const p = getProps();
297
+
298
+ if (p.isDisabled) {
299
+ return {
300
+ disabled: true,
301
+ };
302
+ }
303
+
304
+ return {
305
+ type: 'button' as const,
306
+ 'aria-label': 'Drop',
307
+ };
308
+ });
309
+
310
+ return {
311
+ get dropProps() {
312
+ return dropProps() as DropAria['dropProps'];
313
+ },
314
+ get isDropTarget() {
315
+ return state.isDropTarget;
316
+ },
317
+ get dropButtonProps() {
318
+ return dropButtonProps() as DropAria['dropButtonProps'];
319
+ },
320
+ };
321
+ }
@@ -0,0 +1,293 @@
1
+ /**
2
+ * createDroppableCollection - ARIA hook for droppable collection targets.
3
+ *
4
+ * Provides accessibility support for dropping items into a collection
5
+ * component like ListBox, GridList, or Table.
6
+ */
7
+
8
+ import { createMemo, onCleanup, type Accessor } from 'solid-js';
9
+ import type { JSX } from 'solid-js';
10
+ import type {
11
+ DroppableCollectionState,
12
+ DropTarget,
13
+ DropOperation,
14
+ DropItem,
15
+ DragTypes,
16
+ } from '@proyecto-viviana/solid-stately';
17
+ import { createDrop } from './createDrop';
18
+ import { getGlobalDraggingCollectionRef } from './createDraggableCollection';
19
+
20
+ // Global state for tracking the drop collection
21
+ let globalDropCollectionRef: HTMLElement | null = null;
22
+
23
+ export function setGlobalDropCollectionRef(ref: HTMLElement | null): void {
24
+ globalDropCollectionRef = ref;
25
+ }
26
+
27
+ export function getGlobalDropCollectionRef(): HTMLElement | null {
28
+ return globalDropCollectionRef;
29
+ }
30
+
31
+ export interface DropTargetDelegate {
32
+ /**
33
+ * Returns a drop target from a point within the collection.
34
+ */
35
+ getDropTargetFromPoint(
36
+ x: number,
37
+ y: number,
38
+ isValidDropTarget: (target: DropTarget) => boolean
39
+ ): DropTarget | null;
40
+ }
41
+
42
+ export interface DroppableCollectionOptions {
43
+ /** Reference to the collection element. */
44
+ ref: Accessor<HTMLElement | null>;
45
+ /** A delegate that provides drop targets for pointer coordinates. */
46
+ dropTargetDelegate: DropTargetDelegate;
47
+ /** Handler called when items are dropped to be inserted. */
48
+ onInsert?: (e: {
49
+ items: DropItem[];
50
+ target: DropTarget;
51
+ dropOperation: DropOperation;
52
+ }) => void;
53
+ /** Handler called when items are dropped on the root. */
54
+ onRootDrop?: (e: { items: DropItem[]; dropOperation: DropOperation }) => void;
55
+ /** Handler called when items are dropped on an item. */
56
+ onItemDrop?: (e: {
57
+ items: DropItem[];
58
+ target: DropTarget;
59
+ dropOperation: DropOperation;
60
+ isInternal: boolean;
61
+ }) => void;
62
+ /** Handler called when items are reordered within the collection. */
63
+ onReorder?: (e: {
64
+ keys: Set<string | number>;
65
+ target: DropTarget;
66
+ dropOperation: DropOperation;
67
+ }) => void;
68
+ /** Handler called when items are moved within/between collections. */
69
+ onMove?: (e: {
70
+ keys: Set<string | number>;
71
+ target: DropTarget;
72
+ dropOperation: DropOperation;
73
+ }) => void;
74
+ /** Handler called when the drop target is activated (held over). */
75
+ onDropActivate?: (e: { target: DropTarget; x: number; y: number }) => void;
76
+ /** Whether the collection is disabled for dropping. */
77
+ isDisabled?: boolean;
78
+ /** Accepted drag types. 'all' accepts any type. */
79
+ acceptedDragTypes?: 'all' | string[];
80
+ }
81
+
82
+ export interface DroppableCollectionAria {
83
+ /** Props to spread on the collection element. */
84
+ collectionProps: JSX.HTMLAttributes<HTMLElement>;
85
+ }
86
+
87
+ /**
88
+ * Creates ARIA support for a droppable collection.
89
+ *
90
+ * @param options - Collection options accessor
91
+ * @param state - Droppable collection state
92
+ * @returns Droppable collection ARIA result
93
+ */
94
+ export function createDroppableCollection(
95
+ options: Accessor<DroppableCollectionOptions>,
96
+ state: DroppableCollectionState
97
+ ): DroppableCollectionAria {
98
+ const getOptions = createMemo(() => options());
99
+
100
+ // Track the next target during drag operations
101
+ let nextTarget: DropTarget | null = null;
102
+ let currentDropOperation: DropOperation | null = null;
103
+
104
+ const isInternalDropOperation = (): boolean => {
105
+ const ref = getOptions().ref();
106
+ const draggingRef = getGlobalDraggingCollectionRef();
107
+ return ref !== null && draggingRef === ref;
108
+ };
109
+
110
+ const getDropOperationForTarget = (
111
+ target: DropTarget,
112
+ types: DragTypes,
113
+ allowedOperations: DropOperation[]
114
+ ): DropOperation => {
115
+ return state.getDropOperation(target, types, allowedOperations);
116
+ };
117
+
118
+ // Create base drop behavior
119
+ const drop = createDrop(() => ({
120
+ isDisabled: getOptions().isDisabled,
121
+ getDropOperationForPoint: (types, allowedOperations, x, y) => {
122
+ const opts = getOptions();
123
+ const isValidDropTarget = (target: DropTarget) =>
124
+ getDropOperationForTarget(target, types, allowedOperations) !== 'cancel';
125
+
126
+ const target = opts.dropTargetDelegate.getDropTargetFromPoint(
127
+ x,
128
+ y,
129
+ isValidDropTarget
130
+ );
131
+
132
+ if (!target) {
133
+ currentDropOperation = 'cancel';
134
+ nextTarget = null;
135
+ return 'cancel';
136
+ }
137
+
138
+ currentDropOperation = getDropOperationForTarget(
139
+ target,
140
+ types,
141
+ allowedOperations
142
+ );
143
+
144
+ // If target doesn't accept, try root
145
+ if (currentDropOperation === 'cancel') {
146
+ const rootTarget: DropTarget = { type: 'root' };
147
+ const rootOp = getDropOperationForTarget(
148
+ rootTarget,
149
+ types,
150
+ allowedOperations
151
+ );
152
+ if (rootOp !== 'cancel') {
153
+ nextTarget = rootTarget;
154
+ currentDropOperation = rootOp;
155
+ return currentDropOperation;
156
+ }
157
+ }
158
+
159
+ // Update drop collection ref
160
+ const ref = opts.ref();
161
+ if (target && currentDropOperation !== 'cancel' && ref !== globalDropCollectionRef) {
162
+ setGlobalDropCollectionRef(ref);
163
+ }
164
+
165
+ nextTarget = currentDropOperation === 'cancel' ? null : target;
166
+ return currentDropOperation;
167
+ },
168
+ onDropEnter: () => {
169
+ if (nextTarget) {
170
+ state.setTarget(nextTarget);
171
+ }
172
+ },
173
+ onDropMove: () => {
174
+ if (nextTarget) {
175
+ state.setTarget(nextTarget);
176
+ }
177
+ },
178
+ onDropExit: () => {
179
+ setGlobalDropCollectionRef(null);
180
+ state.setTarget(null);
181
+ },
182
+ onDropActivate: (e) => {
183
+ const opts = getOptions();
184
+ if (state.target?.type === 'item' && typeof opts.onDropActivate === 'function') {
185
+ opts.onDropActivate({
186
+ target: state.target,
187
+ x: e.x,
188
+ y: e.y,
189
+ });
190
+ }
191
+ },
192
+ onDrop: (e) => {
193
+ const opts = getOptions();
194
+ setGlobalDropCollectionRef(opts.ref());
195
+
196
+ if (state.target) {
197
+ handleDrop(e.items, state.target, e.dropOperation);
198
+ }
199
+ },
200
+ }));
201
+
202
+ const handleDrop = async (
203
+ items: DropItem[],
204
+ target: DropTarget,
205
+ dropOperation: DropOperation
206
+ ) => {
207
+ const opts = getOptions();
208
+ const isInternal = isInternalDropOperation();
209
+
210
+ // Filter items by accepted types
211
+ let filteredItems = items;
212
+ const acceptedTypes = opts.acceptedDragTypes;
213
+ if (acceptedTypes && acceptedTypes !== 'all') {
214
+ filteredItems = items.filter((item) => {
215
+ const itemTypes =
216
+ item.kind === 'file'
217
+ ? new Set([item.type])
218
+ : item.kind === 'text'
219
+ ? item.types
220
+ : new Set<string>();
221
+ return acceptedTypes.some((type) => itemTypes.has(type));
222
+ });
223
+ }
224
+
225
+ if (filteredItems.length === 0) return;
226
+
227
+ // Call appropriate handlers based on target type
228
+ if (target.type === 'root' && opts.onRootDrop) {
229
+ await opts.onRootDrop({ items: filteredItems, dropOperation });
230
+ }
231
+
232
+ if (target.type === 'item') {
233
+ if (target.dropPosition === 'on' && opts.onItemDrop) {
234
+ await opts.onItemDrop({
235
+ items: filteredItems,
236
+ target,
237
+ dropOperation,
238
+ isInternal,
239
+ });
240
+ }
241
+
242
+ // Handle move for internal operations
243
+ if (opts.onMove && isInternal) {
244
+ // Would get dragging keys from global state
245
+ await opts.onMove({
246
+ keys: new Set(),
247
+ target,
248
+ dropOperation,
249
+ });
250
+ }
251
+
252
+ if (target.dropPosition !== 'on') {
253
+ if (!isInternal && opts.onInsert) {
254
+ await opts.onInsert({
255
+ items: filteredItems,
256
+ target,
257
+ dropOperation,
258
+ });
259
+ }
260
+
261
+ if (isInternal && opts.onReorder) {
262
+ // Would get dragging keys from global state
263
+ await opts.onReorder({
264
+ keys: new Set(),
265
+ target,
266
+ dropOperation,
267
+ });
268
+ }
269
+ }
270
+ }
271
+ };
272
+
273
+ // Clean up on unmount
274
+ onCleanup(() => {
275
+ const ref = getOptions().ref();
276
+ if (globalDropCollectionRef === ref) {
277
+ setGlobalDropCollectionRef(null);
278
+ }
279
+ });
280
+
281
+ const collectionProps = createMemo(() => {
282
+ const baseDropProps = drop.dropProps;
283
+ return {
284
+ ...baseDropProps,
285
+ };
286
+ });
287
+
288
+ return {
289
+ get collectionProps() {
290
+ return collectionProps() as DroppableCollectionAria['collectionProps'];
291
+ },
292
+ };
293
+ }