@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.
Files changed (219) hide show
  1. package/LICENSE +21 -0
  2. package/dist/actiongroup/createActionGroup.d.ts +29 -0
  3. package/dist/actiongroup/createActionGroup.d.ts.map +1 -0
  4. package/dist/actiongroup/index.d.ts +2 -0
  5. package/dist/actiongroup/index.d.ts.map +1 -0
  6. package/dist/autocomplete/createAutocomplete.d.ts +6 -2
  7. package/dist/autocomplete/createAutocomplete.d.ts.map +1 -1
  8. package/dist/breadcrumbs/createBreadcrumbs.d.ts +2 -0
  9. package/dist/breadcrumbs/createBreadcrumbs.d.ts.map +1 -1
  10. package/dist/button/createToggleButtonGroup.d.ts +32 -0
  11. package/dist/button/createToggleButtonGroup.d.ts.map +1 -0
  12. package/dist/button/index.d.ts +2 -0
  13. package/dist/button/index.d.ts.map +1 -1
  14. package/dist/calendar/createCalendarCell.d.ts +2 -0
  15. package/dist/calendar/createCalendarCell.d.ts.map +1 -1
  16. package/dist/calendar/createCalendarGrid.d.ts.map +1 -1
  17. package/dist/calendar/createRangeCalendarCell.d.ts +3 -1
  18. package/dist/calendar/createRangeCalendarCell.d.ts.map +1 -1
  19. package/dist/checkbox/createCheckboxGroup.d.ts +5 -1
  20. package/dist/checkbox/createCheckboxGroup.d.ts.map +1 -1
  21. package/dist/collections/index.d.ts +56 -0
  22. package/dist/collections/index.d.ts.map +1 -0
  23. package/dist/color/createColorArea.d.ts.map +1 -1
  24. package/dist/color/createColorSlider.d.ts.map +1 -1
  25. package/dist/color/createColorWheel.d.ts.map +1 -1
  26. package/dist/combobox/createComboBox.d.ts +6 -0
  27. package/dist/combobox/createComboBox.d.ts.map +1 -1
  28. package/dist/datepicker/createDatePicker.d.ts +6 -0
  29. package/dist/datepicker/createDatePicker.d.ts.map +1 -1
  30. package/dist/datepicker/createDateRangePicker.d.ts +40 -0
  31. package/dist/datepicker/createDateRangePicker.d.ts.map +1 -0
  32. package/dist/datepicker/createDateSegment.d.ts +1 -1
  33. package/dist/datepicker/createDateSegment.d.ts.map +1 -1
  34. package/dist/datepicker/createTimeSegment.d.ts +29 -0
  35. package/dist/datepicker/createTimeSegment.d.ts.map +1 -0
  36. package/dist/datepicker/index.d.ts +2 -0
  37. package/dist/datepicker/index.d.ts.map +1 -1
  38. package/dist/disclosure/createDisclosureGroup.d.ts +2 -1
  39. package/dist/disclosure/createDisclosureGroup.d.ts.map +1 -1
  40. package/dist/dnd/createDrag.d.ts.map +1 -1
  41. package/dist/dnd/createDraggableCollection.d.ts +4 -0
  42. package/dist/dnd/createDraggableCollection.d.ts.map +1 -1
  43. package/dist/dnd/createDraggableItem.d.ts.map +1 -1
  44. package/dist/dnd/createDrop.d.ts.map +1 -1
  45. package/dist/dnd/createDroppableCollection.d.ts +32 -1
  46. package/dist/dnd/createDroppableCollection.d.ts.map +1 -1
  47. package/dist/dnd/createDroppableItem.d.ts.map +1 -1
  48. package/dist/dnd/index.d.ts +1 -1
  49. package/dist/dnd/index.d.ts.map +1 -1
  50. package/dist/grid/createGrid.d.ts.map +1 -1
  51. package/dist/gridlist/createGridList.d.ts.map +1 -1
  52. package/dist/index.d.ts +6 -4
  53. package/dist/index.d.ts.map +1 -1
  54. package/dist/index.js +4659 -3452
  55. package/dist/index.js.map +1 -7
  56. package/dist/index.ssr.js +4659 -3452
  57. package/dist/index.ssr.js.map +1 -7
  58. package/dist/interactions/createFocus.d.ts.map +1 -1
  59. package/dist/interactions/createFocusWithin.d.ts.map +1 -1
  60. package/dist/link/createLink.d.ts +10 -0
  61. package/dist/link/createLink.d.ts.map +1 -1
  62. package/dist/listbox/createListBox.d.ts +1 -0
  63. package/dist/listbox/createListBox.d.ts.map +1 -1
  64. package/dist/listbox/createOption.d.ts.map +1 -1
  65. package/dist/menu/createMenu.d.ts +1 -0
  66. package/dist/menu/createMenu.d.ts.map +1 -1
  67. package/dist/meter/createMeter.d.ts.map +1 -1
  68. package/dist/numberfield/createNumberField.d.ts +18 -0
  69. package/dist/numberfield/createNumberField.d.ts.map +1 -1
  70. package/dist/overlays/createModal.d.ts +16 -0
  71. package/dist/overlays/createModal.d.ts.map +1 -1
  72. package/dist/overlays/createOverlay.d.ts.map +1 -1
  73. package/dist/overlays/index.d.ts +1 -1
  74. package/dist/overlays/index.d.ts.map +1 -1
  75. package/dist/popover/createOverlayPosition.d.ts.map +1 -1
  76. package/dist/popover/createPopover.d.ts.map +1 -1
  77. package/dist/progress/createProgressBar.d.ts.map +1 -1
  78. package/dist/radio/createRadioGroup.d.ts +2 -2
  79. package/dist/radio/createRadioGroup.d.ts.map +1 -1
  80. package/dist/searchfield/createSearchField.d.ts.map +1 -1
  81. package/dist/select/createHiddenSelect.d.ts.map +1 -1
  82. package/dist/select/createSelect.d.ts.map +1 -1
  83. package/dist/slider/createSlider.d.ts.map +1 -1
  84. package/dist/table/createTable.d.ts.map +1 -1
  85. package/dist/tabs/createTabs.d.ts +1 -1
  86. package/dist/tabs/createTabs.d.ts.map +1 -1
  87. package/dist/tag/createTag.d.ts.map +1 -1
  88. package/dist/tag/createTagGroup.d.ts.map +1 -1
  89. package/dist/toast/createToast.d.ts +4 -0
  90. package/dist/toast/createToast.d.ts.map +1 -1
  91. package/dist/toast/createToastRegion.d.ts.map +1 -1
  92. package/dist/toolbar/createToolbar.d.ts.map +1 -1
  93. package/dist/tooltip/createTooltipTrigger.d.ts.map +1 -1
  94. package/dist/tree/createTree.d.ts.map +1 -1
  95. package/dist/tree/createTreeItem.d.ts.map +1 -1
  96. package/dist/tree/types.d.ts +4 -0
  97. package/dist/tree/types.d.ts.map +1 -1
  98. package/dist/utils/env.d.ts +1 -1
  99. package/dist/utils/env.d.ts.map +1 -1
  100. package/dist/utils/platform.d.ts.map +1 -1
  101. package/dist/visually-hidden/createVisuallyHidden.d.ts.map +1 -1
  102. package/package.json +8 -6
  103. package/src/actiongroup/createActionGroup.ts +324 -0
  104. package/src/actiongroup/index.ts +8 -0
  105. package/src/autocomplete/createAutocomplete.ts +32 -9
  106. package/src/breadcrumbs/createBreadcrumbs.ts +10 -15
  107. package/src/button/createButton.ts +1 -1
  108. package/src/button/createToggleButtonGroup.ts +128 -0
  109. package/src/button/index.ts +9 -0
  110. package/src/calendar/createCalendarCell.ts +6 -4
  111. package/src/calendar/createCalendarGrid.ts +27 -18
  112. package/src/calendar/createRangeCalendarCell.ts +26 -9
  113. package/src/checkbox/createCheckboxGroup.ts +21 -4
  114. package/src/collections/index.ts +242 -0
  115. package/src/color/createColorArea.ts +380 -314
  116. package/src/color/createColorField.ts +137 -137
  117. package/src/color/createColorSlider.ts +286 -197
  118. package/src/color/createColorSwatch.ts +40 -40
  119. package/src/color/createColorWheel.ts +218 -208
  120. package/src/color/index.ts +24 -24
  121. package/src/color/types.ts +116 -116
  122. package/src/combobox/createComboBox.ts +670 -647
  123. package/src/combobox/index.ts +6 -6
  124. package/src/datepicker/createDatePicker.ts +54 -16
  125. package/src/datepicker/createDateRangePicker.ts +246 -0
  126. package/src/datepicker/createDateSegment.ts +185 -31
  127. package/src/datepicker/createTimeSegment.ts +370 -0
  128. package/src/datepicker/index.ts +14 -0
  129. package/src/dialog/createDialog.ts +120 -120
  130. package/src/dialog/index.ts +2 -2
  131. package/src/dialog/types.ts +19 -19
  132. package/src/disclosure/createDisclosureGroup.ts +5 -2
  133. package/src/dnd/createDrag.ts +224 -209
  134. package/src/dnd/createDraggableCollection.ts +96 -63
  135. package/src/dnd/createDraggableItem.ts +259 -243
  136. package/src/dnd/createDrop.ts +322 -321
  137. package/src/dnd/createDroppableCollection.ts +682 -293
  138. package/src/dnd/createDroppableItem.ts +215 -213
  139. package/src/dnd/index.ts +55 -47
  140. package/src/dnd/types.ts +89 -89
  141. package/src/dnd/utils.ts +294 -294
  142. package/src/focus/createAutoFocus.ts +321 -321
  143. package/src/focus/createFocusRestore.ts +313 -313
  144. package/src/focus/createVirtualFocus.ts +396 -396
  145. package/src/form/createFormValidation.ts +224 -224
  146. package/src/form/index.ts +11 -11
  147. package/src/grid/createGrid.ts +3 -1
  148. package/src/gridlist/createGridList.ts +16 -0
  149. package/src/gridlist/createGridListItem.ts +1 -1
  150. package/src/i18n/NumberFormatter.ts +266 -266
  151. package/src/i18n/createCollator.ts +79 -79
  152. package/src/i18n/createDateFormatter.ts +83 -83
  153. package/src/i18n/createFilter.ts +131 -131
  154. package/src/i18n/createNumberFormatter.ts +52 -52
  155. package/src/i18n/index.ts +40 -40
  156. package/src/i18n/locale.tsx +188 -188
  157. package/src/i18n/utils.ts +99 -99
  158. package/src/index.ts +51 -0
  159. package/src/interactions/createFocus.ts +6 -5
  160. package/src/interactions/createFocusWithin.ts +6 -5
  161. package/src/interactions/createLongPress.ts +174 -174
  162. package/src/interactions/createMove.ts +289 -289
  163. package/src/interactions/createPress.ts +5 -5
  164. package/src/landmark/createLandmark.ts +377 -377
  165. package/src/landmark/index.ts +8 -8
  166. package/src/link/createLink.ts +23 -8
  167. package/src/listbox/createListBox.ts +308 -269
  168. package/src/listbox/createOption.ts +162 -151
  169. package/src/listbox/index.ts +12 -12
  170. package/src/live-announcer/announce.ts +322 -322
  171. package/src/live-announcer/index.ts +9 -9
  172. package/src/menu/createMenu.ts +405 -396
  173. package/src/menu/createMenuItem.ts +149 -149
  174. package/src/menu/createMenuTrigger.ts +88 -88
  175. package/src/menu/index.ts +18 -18
  176. package/src/meter/createMeter.ts +1 -6
  177. package/src/numberfield/createNumberField.ts +311 -268
  178. package/src/numberfield/index.ts +5 -5
  179. package/src/overlays/ariaHideOutside.ts +219 -219
  180. package/src/overlays/createInteractOutside.ts +149 -149
  181. package/src/overlays/createModal.tsx +238 -202
  182. package/src/overlays/createOverlay.ts +165 -155
  183. package/src/overlays/createOverlayTrigger.ts +85 -85
  184. package/src/overlays/createPreventScroll.ts +266 -266
  185. package/src/overlays/index.ts +48 -44
  186. package/src/popover/calculatePosition.ts +6 -6
  187. package/src/popover/createOverlayPosition.ts +7 -4
  188. package/src/popover/createPopover.ts +21 -7
  189. package/src/progress/createProgressBar.ts +6 -1
  190. package/src/radio/createRadioGroup.ts +88 -14
  191. package/src/searchfield/createSearchField.ts +241 -186
  192. package/src/searchfield/index.ts +2 -2
  193. package/src/select/createHiddenSelect.tsx +263 -236
  194. package/src/select/createSelect.ts +373 -395
  195. package/src/select/index.ts +14 -14
  196. package/src/slider/createSlider.ts +364 -349
  197. package/src/slider/index.ts +2 -2
  198. package/src/ssr/index.tsx +370 -370
  199. package/src/table/createTable.ts +3 -1
  200. package/src/table/createTableColumnHeader.ts +1 -1
  201. package/src/table/createTableRow.ts +1 -1
  202. package/src/tabs/createTabs.ts +80 -51
  203. package/src/tag/createTag.ts +135 -6
  204. package/src/tag/createTagGroup.ts +7 -2
  205. package/src/toast/createToast.ts +8 -2
  206. package/src/toast/createToastRegion.ts +0 -1
  207. package/src/toolbar/createToolbar.ts +75 -1
  208. package/src/tooltip/createTooltip.ts +79 -79
  209. package/src/tooltip/createTooltipTrigger.ts +226 -222
  210. package/src/tooltip/index.ts +6 -6
  211. package/src/tree/createTree.ts +261 -246
  212. package/src/tree/createTreeItem.ts +282 -233
  213. package/src/tree/createTreeSelectionCheckbox.ts +68 -68
  214. package/src/tree/index.ts +16 -16
  215. package/src/tree/types.ts +91 -87
  216. package/src/utils/env.ts +55 -54
  217. package/src/utils/platform.ts +16 -6
  218. package/src/visually-hidden/createVisuallyHidden.ts +139 -124
  219. package/src/visually-hidden/index.ts +6 -6
@@ -1,321 +1,322 @@
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
- }
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
+ hasDropButton: getProps().hasDropButton,
43
+ isDisabled: getProps().isDisabled,
44
+ }));
45
+
46
+ // Track internal state
47
+ let x = 0;
48
+ let y = 0;
49
+ let dragOverElements = new Set<Element>();
50
+ let dropEffect: DataTransfer['dropEffect'] = 'none';
51
+ let allowedOperations = DROP_OPERATION.all;
52
+ let dropActivateTimer: ReturnType<typeof setTimeout> | undefined;
53
+
54
+ const fireDropEnter = (e: DragEvent) => {
55
+ const rect = (e.currentTarget as HTMLElement).getBoundingClientRect();
56
+ state.enterTarget(e.clientX - rect.x, e.clientY - rect.y);
57
+ };
58
+
59
+ const fireDropExit = (e: DragEvent) => {
60
+ const rect = (e.currentTarget as HTMLElement).getBoundingClientRect();
61
+ state.exitTarget(e.clientX - rect.x, e.clientY - rect.y);
62
+ };
63
+
64
+ const getAllowedOperations = (e: DragEvent): number => {
65
+ let allowed = DROP_OPERATION_ALLOWED[e.dataTransfer?.effectAllowed ?? 'none'] ?? DROP_OPERATION.none;
66
+
67
+ // Use global allowed operations if set (for internal drags)
68
+ const globalAllowed = getGlobalAllowedDropOperations();
69
+ if (globalAllowed) {
70
+ allowed &= globalAllowed;
71
+ }
72
+
73
+ // Handle modifier keys for operation switching
74
+ let modifierAllowed = DROP_OPERATION.none;
75
+
76
+ // macOS: Alt=copy, Ctrl=link, Cmd=move
77
+ // Windows/Linux: Alt=link, Shift=move, Ctrl=copy
78
+ const isMac = typeof navigator !== 'undefined' && /mac/i.test(navigator.platform);
79
+
80
+ if (isMac) {
81
+ if (e.altKey) modifierAllowed |= DROP_OPERATION.copy;
82
+ if (e.ctrlKey) modifierAllowed |= DROP_OPERATION.link;
83
+ if (e.metaKey) modifierAllowed |= DROP_OPERATION.move;
84
+ } else {
85
+ if (e.altKey) modifierAllowed |= DROP_OPERATION.link;
86
+ if (e.shiftKey) modifierAllowed |= DROP_OPERATION.move;
87
+ if (e.ctrlKey) modifierAllowed |= DROP_OPERATION.copy;
88
+ }
89
+
90
+ if (modifierAllowed) {
91
+ return allowed & modifierAllowed;
92
+ }
93
+
94
+ return allowed;
95
+ };
96
+
97
+ const allowedOperationsToArray = (ops: number): DropOperation[] => {
98
+ const result: DropOperation[] = [];
99
+ if (ops & DROP_OPERATION.move) result.push('move');
100
+ if (ops & DROP_OPERATION.copy) result.push('copy');
101
+ if (ops & DROP_OPERATION.link) result.push('link');
102
+ return result;
103
+ };
104
+
105
+ const getDropOperationForAllowed = (allowed: number, operation: DropOperation): DropOperation => {
106
+ const op = DROP_OPERATION[operation];
107
+ return allowed & op ? operation : 'cancel';
108
+ };
109
+
110
+ const onDragEnter = (e: DragEvent) => {
111
+ e.preventDefault();
112
+ e.stopPropagation();
113
+
114
+ dragOverElements.add(e.target as Element);
115
+ if (dragOverElements.size > 1) {
116
+ return;
117
+ }
118
+
119
+ const p = getProps();
120
+ const allowedOpsBits = getAllowedOperations(e);
121
+ const allowedOps = allowedOperationsToArray(allowedOpsBits);
122
+ let dropOp: DropOperation = allowedOps[0] ?? 'cancel';
123
+
124
+ if (typeof p.getDropOperation === 'function' && e.dataTransfer) {
125
+ const types = new DragTypesImpl(e.dataTransfer);
126
+ dropOp = getDropOperationForAllowed(
127
+ allowedOpsBits,
128
+ p.getDropOperation(types, allowedOps)
129
+ );
130
+ }
131
+
132
+ if (typeof p.getDropOperationForPoint === 'function' && e.dataTransfer) {
133
+ const types = new DragTypesImpl(e.dataTransfer);
134
+ const rect = (e.currentTarget as HTMLElement).getBoundingClientRect();
135
+ dropOp = getDropOperationForAllowed(
136
+ allowedOpsBits,
137
+ p.getDropOperationForPoint(types, allowedOps, e.clientX - rect.x, e.clientY - rect.y)
138
+ );
139
+ }
140
+
141
+ x = e.clientX;
142
+ y = e.clientY;
143
+ allowedOperations = allowedOpsBits;
144
+ dropEffect = (DROP_OPERATION_TO_DROP_EFFECT[dropOp] || 'none') as DataTransfer['dropEffect'];
145
+
146
+ if (e.dataTransfer) {
147
+ e.dataTransfer.dropEffect = dropEffect;
148
+ }
149
+
150
+ if (dropOp !== 'cancel') {
151
+ fireDropEnter(e);
152
+ }
153
+ };
154
+
155
+ const onDragOver = (e: DragEvent) => {
156
+ e.preventDefault();
157
+ e.stopPropagation();
158
+
159
+ const allowedOpsBits = getAllowedOperations(e);
160
+
161
+ // Skip if position and operations haven't changed
162
+ if (e.clientX === x && e.clientY === y && allowedOpsBits === allowedOperations) {
163
+ if (e.dataTransfer) {
164
+ e.dataTransfer.dropEffect = dropEffect;
165
+ }
166
+ return;
167
+ }
168
+
169
+ x = e.clientX;
170
+ y = e.clientY;
171
+
172
+ const prevDropEffect = dropEffect;
173
+ const p = getProps();
174
+
175
+ // Update drop effect if allowed operations changed
176
+ if (allowedOpsBits !== allowedOperations) {
177
+ const allowedOps = allowedOperationsToArray(allowedOpsBits);
178
+ let dropOp: DropOperation = allowedOps[0] ?? 'cancel';
179
+
180
+ if (typeof p.getDropOperation === 'function' && e.dataTransfer) {
181
+ const types = new DragTypesImpl(e.dataTransfer);
182
+ dropOp = getDropOperationForAllowed(
183
+ allowedOpsBits,
184
+ p.getDropOperation(types, allowedOps)
185
+ );
186
+ }
187
+ dropEffect = (DROP_OPERATION_TO_DROP_EFFECT[dropOp] || 'none') as DataTransfer['dropEffect'];
188
+ }
189
+
190
+ // Check point-specific operation
191
+ if (typeof p.getDropOperationForPoint === 'function' && e.dataTransfer) {
192
+ const types = new DragTypesImpl(e.dataTransfer);
193
+ const rect = (e.currentTarget as HTMLElement).getBoundingClientRect();
194
+ const dropOp = getDropOperationForAllowed(
195
+ allowedOpsBits,
196
+ p.getDropOperationForPoint(types, allowedOperationsToArray(allowedOpsBits), x - rect.x, y - rect.y)
197
+ );
198
+ dropEffect = (DROP_OPERATION_TO_DROP_EFFECT[dropOp] || 'none') as DataTransfer['dropEffect'];
199
+ }
200
+
201
+ allowedOperations = allowedOpsBits;
202
+
203
+ if (e.dataTransfer) {
204
+ e.dataTransfer.dropEffect = dropEffect;
205
+ }
206
+
207
+ // Fire enter/exit events on drop effect change
208
+ if (dropEffect === 'none' && prevDropEffect !== 'none') {
209
+ fireDropExit(e);
210
+ } else if (dropEffect !== 'none' && prevDropEffect === 'none') {
211
+ fireDropEnter(e);
212
+ }
213
+
214
+ // Fire move event
215
+ if (dropEffect !== 'none') {
216
+ const rect = (e.currentTarget as HTMLElement).getBoundingClientRect();
217
+ state.moveInTarget(x - rect.x, y - rect.y);
218
+ }
219
+
220
+ // Handle drop activate timer
221
+ clearTimeout(dropActivateTimer);
222
+
223
+ if (typeof p.onDropActivate === 'function' && dropEffect !== 'none') {
224
+ const rect = (e.currentTarget as HTMLElement).getBoundingClientRect();
225
+ const activateX = x - rect.x;
226
+ const activateY = y - rect.y;
227
+ dropActivateTimer = setTimeout(() => {
228
+ state.activateTarget(activateX, activateY);
229
+ }, DROP_ACTIVATE_TIMEOUT);
230
+ }
231
+ };
232
+
233
+ const onDragLeave = (e: DragEvent) => {
234
+ e.preventDefault();
235
+ e.stopPropagation();
236
+
237
+ // Track drag over elements (WebKit workaround for relatedTarget being null)
238
+ dragOverElements.delete(e.target as Element);
239
+
240
+ // Remove elements no longer in DOM
241
+ for (const element of dragOverElements) {
242
+ if (!e.currentTarget || !(e.currentTarget as Element).contains(element)) {
243
+ dragOverElements.delete(element);
244
+ }
245
+ }
246
+
247
+ if (dragOverElements.size > 0) {
248
+ return;
249
+ }
250
+
251
+ if (dropEffect !== 'none') {
252
+ fireDropExit(e);
253
+ }
254
+
255
+ clearTimeout(dropActivateTimer);
256
+ };
257
+
258
+ const onDropHandler = (e: DragEvent) => {
259
+ e.preventDefault();
260
+ e.stopPropagation();
261
+
262
+ // Track drop effect globally for Chrome Android
263
+ setGlobalDropEffect(dropEffect);
264
+
265
+ const p = getProps();
266
+ if (typeof p.onDrop === 'function' && e.dataTransfer) {
267
+ const items = readFromDataTransfer(e.dataTransfer);
268
+ const dropOperation = DROP_EFFECT_TO_DROP_OPERATION[dropEffect];
269
+ const rect = (e.currentTarget as HTMLElement).getBoundingClientRect();
270
+
271
+ state.drop(e.clientX - rect.x, e.clientY - rect.y, items, dropOperation);
272
+ }
273
+
274
+ dragOverElements.clear();
275
+ fireDropExit(e);
276
+ clearTimeout(dropActivateTimer);
277
+ };
278
+
279
+ const dropProps = createMemo(() => {
280
+ const p = getProps();
281
+
282
+ if (p.isDisabled) {
283
+ return {};
284
+ }
285
+
286
+ const baseProps: Record<string, unknown> = {
287
+ onDragEnter,
288
+ onDragOver,
289
+ onDragLeave,
290
+ onDrop: onDropHandler,
291
+ };
292
+
293
+ return baseProps;
294
+ });
295
+
296
+ const dropButtonProps = createMemo(() => {
297
+ const p = getProps();
298
+
299
+ if (p.isDisabled) {
300
+ return {
301
+ disabled: true,
302
+ };
303
+ }
304
+
305
+ return {
306
+ type: 'button' as const,
307
+ 'aria-label': 'Drop',
308
+ };
309
+ });
310
+
311
+ return {
312
+ get dropProps() {
313
+ return dropProps() as DropAria['dropProps'];
314
+ },
315
+ get isDropTarget() {
316
+ return state.isDropTarget;
317
+ },
318
+ get dropButtonProps() {
319
+ return dropButtonProps() as DropAria['dropButtonProps'];
320
+ },
321
+ };
322
+ }