@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,213 @@
1
+ /**
2
+ * createDroppableItem - ARIA hook for droppable items within a collection.
3
+ *
4
+ * Provides accessibility props for items that can receive drops.
5
+ */
6
+
7
+ import { createMemo, type Accessor } from 'solid-js';
8
+ import type { JSX } from 'solid-js';
9
+ import type {
10
+ DroppableCollectionState,
11
+ DropTarget,
12
+ DropOperation,
13
+ } from '@proyecto-viviana/solid-stately';
14
+ import {
15
+ DragTypesImpl,
16
+ DROP_OPERATION,
17
+ DROP_OPERATION_ALLOWED,
18
+ DROP_OPERATION_TO_DROP_EFFECT,
19
+ getGlobalAllowedDropOperations,
20
+ } from './utils';
21
+
22
+ export interface DroppableItemOptions {
23
+ /** The unique key of the item. */
24
+ key: string | number;
25
+ /** Reference to the item element. */
26
+ ref: Accessor<HTMLElement | null>;
27
+ /** Whether this item is disabled for dropping. */
28
+ isDisabled?: boolean;
29
+ }
30
+
31
+ export interface DroppableItemAria {
32
+ /** Props for the droppable item element. */
33
+ dropProps: JSX.HTMLAttributes<HTMLElement>;
34
+ /** Whether the item is currently a drop target. */
35
+ isDropTarget: boolean;
36
+ }
37
+
38
+ /**
39
+ * Creates ARIA props for a droppable item within a collection.
40
+ *
41
+ * @param options - Accessor returning item options
42
+ * @param state - Droppable collection state
43
+ * @returns Droppable item ARIA props
44
+ */
45
+ export function createDroppableItem(
46
+ options: Accessor<DroppableItemOptions>,
47
+ state: DroppableCollectionState
48
+ ): DroppableItemAria {
49
+ const getOptions = createMemo(() => options());
50
+
51
+ const isDropTarget = createMemo(() => {
52
+ const { key } = getOptions();
53
+ const target = state.target;
54
+ return target?.type === 'item' && target.key === key;
55
+ });
56
+
57
+ const getTarget = (dropPosition: 'before' | 'on' | 'after'): DropTarget => {
58
+ const { key } = getOptions();
59
+ return {
60
+ type: 'item',
61
+ key,
62
+ dropPosition,
63
+ };
64
+ };
65
+
66
+ const getDropOperation = (
67
+ e: DragEvent,
68
+ target: DropTarget
69
+ ): DropOperation => {
70
+ if (!e.dataTransfer) return 'cancel';
71
+
72
+ const types = new DragTypesImpl(e.dataTransfer);
73
+ let allowedBits =
74
+ DROP_OPERATION_ALLOWED[e.dataTransfer.effectAllowed] || DROP_OPERATION.all;
75
+
76
+ // Use global allowed operations for internal drags
77
+ const globalAllowed = getGlobalAllowedDropOperations();
78
+ if (globalAllowed) {
79
+ allowedBits &= globalAllowed;
80
+ }
81
+
82
+ const allowedOperations: DropOperation[] = [];
83
+ if (allowedBits & DROP_OPERATION.move) allowedOperations.push('move');
84
+ if (allowedBits & DROP_OPERATION.copy) allowedOperations.push('copy');
85
+ if (allowedBits & DROP_OPERATION.link) allowedOperations.push('link');
86
+
87
+ return state.getDropOperation(target, types, allowedOperations);
88
+ };
89
+
90
+ let dropActivateTimer: ReturnType<typeof setTimeout> | undefined;
91
+ const DROP_ACTIVATE_TIMEOUT = 800;
92
+
93
+ const onDragEnter = (e: DragEvent) => {
94
+ e.preventDefault();
95
+ e.stopPropagation();
96
+
97
+ const opts = getOptions();
98
+ if (opts.isDisabled) return;
99
+
100
+ // Determine drop position based on cursor position
101
+ const rect = (e.currentTarget as HTMLElement).getBoundingClientRect();
102
+ const y = e.clientY - rect.y;
103
+ const height = rect.height;
104
+
105
+ let dropPosition: 'before' | 'on' | 'after';
106
+ if (y < height * 0.25) {
107
+ dropPosition = 'before';
108
+ } else if (y > height * 0.75) {
109
+ dropPosition = 'after';
110
+ } else {
111
+ dropPosition = 'on';
112
+ }
113
+
114
+ const target = getTarget(dropPosition);
115
+ const operation = getDropOperation(e, target);
116
+
117
+ if (operation !== 'cancel') {
118
+ state.setTarget(target);
119
+ e.dataTransfer!.dropEffect = DROP_OPERATION_TO_DROP_EFFECT[operation] as DataTransfer['dropEffect'];
120
+ }
121
+ };
122
+
123
+ const onDragOver = (e: DragEvent) => {
124
+ e.preventDefault();
125
+ e.stopPropagation();
126
+
127
+ const opts = getOptions();
128
+ if (opts.isDisabled) return;
129
+
130
+ // Update drop position based on cursor
131
+ const rect = (e.currentTarget as HTMLElement).getBoundingClientRect();
132
+ const y = e.clientY - rect.y;
133
+ const height = rect.height;
134
+
135
+ let dropPosition: 'before' | 'on' | 'after';
136
+ if (y < height * 0.25) {
137
+ dropPosition = 'before';
138
+ } else if (y > height * 0.75) {
139
+ dropPosition = 'after';
140
+ } else {
141
+ dropPosition = 'on';
142
+ }
143
+
144
+ const target = getTarget(dropPosition);
145
+ const operation = getDropOperation(e, target);
146
+
147
+ if (operation !== 'cancel') {
148
+ state.setTarget(target);
149
+ e.dataTransfer!.dropEffect = DROP_OPERATION_TO_DROP_EFFECT[operation] as DataTransfer['dropEffect'];
150
+
151
+ // Handle drop activate for 'on' position
152
+ clearTimeout(dropActivateTimer);
153
+ if (dropPosition === 'on') {
154
+ dropActivateTimer = setTimeout(() => {
155
+ // Would trigger onDropActivate
156
+ }, DROP_ACTIVATE_TIMEOUT);
157
+ }
158
+ } else {
159
+ e.dataTransfer!.dropEffect = 'none';
160
+ }
161
+ };
162
+
163
+ const onDragLeave = (e: DragEvent) => {
164
+ e.preventDefault();
165
+ e.stopPropagation();
166
+
167
+ clearTimeout(dropActivateTimer);
168
+
169
+ // Only clear target if leaving this item
170
+ const relatedTarget = e.relatedTarget as HTMLElement | null;
171
+ const currentTarget = e.currentTarget as HTMLElement;
172
+ if (!relatedTarget || !currentTarget.contains(relatedTarget)) {
173
+ // Clear if no longer over this item
174
+ const { key } = getOptions();
175
+ if (state.target?.type === 'item' && state.target.key === key) {
176
+ // State clearing handled by parent collection
177
+ }
178
+ }
179
+ };
180
+
181
+ const onDrop = (e: DragEvent) => {
182
+ e.preventDefault();
183
+ e.stopPropagation();
184
+
185
+ clearTimeout(dropActivateTimer);
186
+
187
+ // Drop handling is done by the parent collection
188
+ };
189
+
190
+ const dropProps = createMemo(() => {
191
+ const opts = getOptions();
192
+
193
+ if (opts.isDisabled) {
194
+ return {};
195
+ }
196
+
197
+ return {
198
+ onDragEnter,
199
+ onDragOver,
200
+ onDragLeave,
201
+ onDrop,
202
+ };
203
+ });
204
+
205
+ return {
206
+ get dropProps() {
207
+ return dropProps() as DroppableItemAria['dropProps'];
208
+ },
209
+ get isDropTarget() {
210
+ return isDropTarget();
211
+ },
212
+ };
213
+ }
@@ -0,0 +1,47 @@
1
+ /**
2
+ * Drag and Drop module for solidaria.
3
+ *
4
+ * Provides ARIA hooks for drag and drop interactions.
5
+ */
6
+
7
+ // Basic drag/drop hooks
8
+ export { createDrag } from './createDrag';
9
+ export { createDrop } from './createDrop';
10
+
11
+ // Collection hooks
12
+ export { createDraggableCollection, setGlobalDraggingCollectionRef, getGlobalDraggingCollectionRef } from './createDraggableCollection';
13
+ export { createDroppableCollection, setGlobalDropCollectionRef, getGlobalDropCollectionRef } from './createDroppableCollection';
14
+
15
+ // Item hooks
16
+ export { createDraggableItem } from './createDraggableItem';
17
+ export { createDroppableItem } from './createDroppableItem';
18
+
19
+ // Types
20
+ export type { AriaDragOptions, DragAria, AriaDropOptions, DropAria } from './types';
21
+ export type { DraggableCollectionOptions, DraggableCollectionAria } from './createDraggableCollection';
22
+ export type { DroppableCollectionOptions, DroppableCollectionAria, DropTargetDelegate } from './createDroppableCollection';
23
+ export type { DraggableItemOptions, DraggableItemAria } from './createDraggableItem';
24
+ export type { DroppableItemOptions, DroppableItemAria } from './createDroppableItem';
25
+
26
+ // Utilities
27
+ export {
28
+ CUSTOM_DRAG_TYPE,
29
+ NATIVE_DRAG_TYPES,
30
+ GENERIC_TYPE,
31
+ DROP_OPERATION,
32
+ DROP_OPERATION_ALLOWED,
33
+ EFFECT_ALLOWED,
34
+ DROP_EFFECT_TO_DROP_OPERATION,
35
+ DROP_OPERATION_TO_DROP_EFFECT,
36
+ getTypes,
37
+ writeToDataTransfer,
38
+ readFromDataTransfer,
39
+ DragTypesImpl,
40
+ isTextDropItem,
41
+ isFileDropItem,
42
+ isDirectoryDropItem,
43
+ setGlobalDropEffect,
44
+ getGlobalDropEffect,
45
+ setGlobalAllowedDropOperations,
46
+ getGlobalAllowedDropOperations,
47
+ } from './utils';
@@ -0,0 +1,89 @@
1
+ /**
2
+ * Drag and Drop ARIA types for solidaria.
3
+ */
4
+
5
+ import type { JSX } from 'solid-js';
6
+ import type {
7
+ DragItem,
8
+ DragStartEvent,
9
+ DragMoveEvent,
10
+ DragEndEvent,
11
+ DropEnterEvent,
12
+ DropMoveEvent,
13
+ DropActivateEvent,
14
+ DropExitEvent,
15
+ DropEvent,
16
+ DropOperation,
17
+ DragTypes,
18
+ DragPreviewRenderer,
19
+ } from '@proyecto-viviana/solid-stately';
20
+
21
+ export interface AriaDragOptions {
22
+ /** A function that returns the items being dragged. */
23
+ getItems: () => DragItem[];
24
+ /** Function that returns the allowed drop operations. */
25
+ getAllowedDropOperations?: () => DropOperation[];
26
+ /** Handler that is called when a drag operation is started. */
27
+ onDragStart?: (e: DragStartEvent) => void;
28
+ /** Handler that is called when the drag is moved. */
29
+ onDragMove?: (e: DragMoveEvent) => void;
30
+ /** Handler that is called when the drag operation ends. */
31
+ onDragEnd?: (e: DragEndEvent) => void;
32
+ /** Whether there is a separate drag button affordance. */
33
+ hasDragButton?: boolean;
34
+ /** Whether the drag operation is disabled. */
35
+ isDisabled?: boolean;
36
+ /** Preview renderer function ref. */
37
+ preview?: { current: DragPreviewRenderer | null };
38
+ }
39
+
40
+ export interface DragAria {
41
+ /** Props for the draggable element. */
42
+ dragProps: JSX.HTMLAttributes<HTMLElement>;
43
+ /** Props for the explicit drag button affordance, if any. */
44
+ dragButtonProps: JSX.ButtonHTMLAttributes<HTMLButtonElement>;
45
+ /** Whether the element is currently being dragged. */
46
+ isDragging: boolean;
47
+ }
48
+
49
+ export interface AriaDropOptions {
50
+ /**
51
+ * A function returning the drop operation to be performed.
52
+ */
53
+ getDropOperation?: (
54
+ types: DragTypes,
55
+ allowedOperations: DropOperation[]
56
+ ) => DropOperation;
57
+ /**
58
+ * A function returning the drop operation for a specific point.
59
+ */
60
+ getDropOperationForPoint?: (
61
+ types: DragTypes,
62
+ allowedOperations: DropOperation[],
63
+ x: number,
64
+ y: number
65
+ ) => DropOperation;
66
+ /** Handler that is called when a valid drag enters the drop target. */
67
+ onDropEnter?: (e: DropEnterEvent) => void;
68
+ /** Handler that is called when a valid drag is moved within the drop target. */
69
+ onDropMove?: (e: DropMoveEvent) => void;
70
+ /** Handler that is called after a valid drag is held over the drop target. */
71
+ onDropActivate?: (e: DropActivateEvent) => void;
72
+ /** Handler that is called when a valid drag exits the drop target. */
73
+ onDropExit?: (e: DropExitEvent) => void;
74
+ /** Handler that is called when a valid drag is dropped on the drop target. */
75
+ onDrop?: (e: DropEvent) => void;
76
+ /** Whether there is a separate drop button affordance. */
77
+ hasDropButton?: boolean;
78
+ /** Whether the drop target is disabled. */
79
+ isDisabled?: boolean;
80
+ }
81
+
82
+ export interface DropAria {
83
+ /** Props for the droppable element. */
84
+ dropProps: JSX.HTMLAttributes<HTMLElement>;
85
+ /** Whether the drop target is currently being hovered or focused. */
86
+ isDropTarget: boolean;
87
+ /** Props for the explicit drop button affordance, if any. */
88
+ dropButtonProps: JSX.ButtonHTMLAttributes<HTMLButtonElement>;
89
+ }
@@ -0,0 +1,294 @@
1
+ /**
2
+ * Drag and Drop utilities for solidaria.
3
+ */
4
+
5
+ import type {
6
+ DragItem,
7
+ DropItem,
8
+ TextDropItem,
9
+ FileDropItem,
10
+ DirectoryDropItem,
11
+ DropOperation,
12
+ } from '@proyecto-viviana/solid-stately';
13
+
14
+ // Native drag types that can be transferred between applications
15
+ export const NATIVE_DRAG_TYPES: Set<string> = new Set([
16
+ 'text/plain',
17
+ 'text/uri-list',
18
+ 'text/html',
19
+ ]);
20
+
21
+ // Custom drag type for serializing multiple items
22
+ export const CUSTOM_DRAG_TYPE = 'application/vnd.solidaria.items+json';
23
+
24
+ // Generic type for unknown file types
25
+ export const GENERIC_TYPE = 'application/octet-stream';
26
+
27
+ // Drop operation bit flags
28
+ export enum DROP_OPERATION {
29
+ none = 0,
30
+ cancel = 0,
31
+ move = 1 << 0,
32
+ copy = 1 << 1,
33
+ link = 1 << 2,
34
+ all = (1 << 0) | (1 << 1) | (1 << 2),
35
+ }
36
+
37
+ // Map from effectAllowed to DROP_OPERATION
38
+ export const DROP_OPERATION_ALLOWED: Record<string, number> = {
39
+ none: DROP_OPERATION.none,
40
+ copy: DROP_OPERATION.copy,
41
+ copyLink: DROP_OPERATION.copy | DROP_OPERATION.link,
42
+ copyMove: DROP_OPERATION.copy | DROP_OPERATION.move,
43
+ link: DROP_OPERATION.link,
44
+ linkMove: DROP_OPERATION.link | DROP_OPERATION.move,
45
+ move: DROP_OPERATION.move,
46
+ all: DROP_OPERATION.all,
47
+ uninitialized: DROP_OPERATION.all,
48
+ };
49
+
50
+ // Map from DROP_OPERATION to effectAllowed
51
+ export const EFFECT_ALLOWED: Record<number, string> = {
52
+ [DROP_OPERATION.none]: 'none',
53
+ [DROP_OPERATION.move]: 'move',
54
+ [DROP_OPERATION.copy]: 'copy',
55
+ [DROP_OPERATION.copy | DROP_OPERATION.move]: 'copyMove',
56
+ [DROP_OPERATION.link]: 'link',
57
+ [DROP_OPERATION.link | DROP_OPERATION.move]: 'linkMove',
58
+ [DROP_OPERATION.copy | DROP_OPERATION.link]: 'copyLink',
59
+ [DROP_OPERATION.all]: 'all',
60
+ };
61
+
62
+ // Map from dropEffect to DropOperation
63
+ export const DROP_EFFECT_TO_DROP_OPERATION: Record<string, DropOperation> = {
64
+ none: 'cancel',
65
+ link: 'link',
66
+ copy: 'copy',
67
+ move: 'move',
68
+ };
69
+
70
+ // Map from DropOperation to dropEffect
71
+ export const DROP_OPERATION_TO_DROP_EFFECT: Record<DropOperation, string> = {
72
+ cancel: 'none',
73
+ link: 'link',
74
+ copy: 'copy',
75
+ move: 'move',
76
+ };
77
+
78
+ /**
79
+ * Get the types present in drag items.
80
+ */
81
+ export function getTypes(items: DragItem[]): Set<string> {
82
+ const types = new Set<string>();
83
+ for (const item of items) {
84
+ for (const type of Object.keys(item)) {
85
+ types.add(type);
86
+ }
87
+ }
88
+ return types;
89
+ }
90
+
91
+ /**
92
+ * Write drag items to a DataTransfer object.
93
+ */
94
+ export function writeToDataTransfer(
95
+ dataTransfer: DataTransfer,
96
+ items: DragItem[]
97
+ ): void {
98
+ const groupedByType = new Map<string, string[]>();
99
+ let needsCustomData = false;
100
+ const customData: object[] = [];
101
+
102
+ for (const item of items) {
103
+ const types = Object.keys(item);
104
+ if (types.length > 1) {
105
+ needsCustomData = true;
106
+ }
107
+
108
+ const dataByType: Record<string, string> = {};
109
+ for (const type of types) {
110
+ let typeItems = groupedByType.get(type);
111
+ if (!typeItems) {
112
+ typeItems = [];
113
+ groupedByType.set(type, typeItems);
114
+ } else {
115
+ needsCustomData = true;
116
+ }
117
+
118
+ const data = item[type];
119
+ dataByType[type] = data;
120
+ typeItems.push(data);
121
+ }
122
+
123
+ customData.push(dataByType);
124
+ }
125
+
126
+ for (const [type, typeItems] of groupedByType) {
127
+ if (NATIVE_DRAG_TYPES.has(type)) {
128
+ // Join all items of this type with newlines
129
+ const data = typeItems.join('\n');
130
+ dataTransfer.items.add(data, type);
131
+ } else {
132
+ // Set first item for non-native types
133
+ dataTransfer.items.add(typeItems[0], type);
134
+ }
135
+ }
136
+
137
+ if (needsCustomData) {
138
+ const data = JSON.stringify(customData);
139
+ dataTransfer.items.add(data, CUSTOM_DRAG_TYPE);
140
+ }
141
+ }
142
+
143
+ /**
144
+ * Read drop items from a DataTransfer object.
145
+ */
146
+ export function readFromDataTransfer(dataTransfer: DataTransfer): DropItem[] {
147
+ const items: DropItem[] = [];
148
+
149
+ if (!dataTransfer) {
150
+ return items;
151
+ }
152
+
153
+ // Check for custom drag type first
154
+ let hasCustomType = false;
155
+ if (dataTransfer.types.includes(CUSTOM_DRAG_TYPE)) {
156
+ try {
157
+ const data = dataTransfer.getData(CUSTOM_DRAG_TYPE);
158
+ const parsed = JSON.parse(data);
159
+ for (const item of parsed) {
160
+ items.push({
161
+ kind: 'text',
162
+ types: new Set(Object.keys(item)),
163
+ getText: (type) => Promise.resolve(item[type]),
164
+ });
165
+ }
166
+ hasCustomType = true;
167
+ } catch {
168
+ // ignore parsing errors
169
+ }
170
+ }
171
+
172
+ // Fall back to native items
173
+ if (!hasCustomType) {
174
+ const stringItems = new Map<string, string>();
175
+
176
+ for (const item of dataTransfer.items) {
177
+ if (item.kind === 'string') {
178
+ const type = item.type || GENERIC_TYPE;
179
+ stringItems.set(type, dataTransfer.getData(item.type));
180
+ } else if (item.kind === 'file') {
181
+ const file = item.getAsFile();
182
+ if (file) {
183
+ items.push(createFileItem(file));
184
+ }
185
+ }
186
+ }
187
+
188
+ if (stringItems.size > 0) {
189
+ items.push({
190
+ kind: 'text',
191
+ types: new Set(stringItems.keys()),
192
+ getText: (type) => Promise.resolve(stringItems.get(type) ?? ''),
193
+ });
194
+ }
195
+ }
196
+
197
+ return items;
198
+ }
199
+
200
+ /**
201
+ * Create a FileDropItem from a File object.
202
+ */
203
+ function createFileItem(file: File): FileDropItem {
204
+ return {
205
+ kind: 'file',
206
+ type: file.type || GENERIC_TYPE,
207
+ name: file.name,
208
+ getText: () => file.text(),
209
+ getFile: () => Promise.resolve(file),
210
+ };
211
+ }
212
+
213
+ /**
214
+ * DragTypes implementation for checking drag types.
215
+ */
216
+ export class DragTypesImpl {
217
+ private types: Set<string>;
218
+ private includesUnknownTypes: boolean;
219
+
220
+ constructor(dataTransfer: DataTransfer) {
221
+ this.types = new Set<string>();
222
+ let hasFiles = false;
223
+
224
+ for (const item of dataTransfer.items) {
225
+ if (item.type !== CUSTOM_DRAG_TYPE) {
226
+ if (item.kind === 'file') {
227
+ hasFiles = true;
228
+ }
229
+ if (item.type) {
230
+ this.types.add(item.type);
231
+ } else {
232
+ this.types.add(GENERIC_TYPE);
233
+ }
234
+ }
235
+ }
236
+
237
+ // Safari doesn't expose file types until drop
238
+ this.includesUnknownTypes =
239
+ !hasFiles && dataTransfer.types.includes('Files');
240
+ }
241
+
242
+ has(type: string | symbol): boolean {
243
+ if (
244
+ this.includesUnknownTypes ||
245
+ (typeof type === 'symbol' && this.types.has(GENERIC_TYPE))
246
+ ) {
247
+ return true;
248
+ }
249
+ return typeof type === 'string' && this.types.has(type);
250
+ }
251
+ }
252
+
253
+ /**
254
+ * Check if a drop item is a text item.
255
+ */
256
+ export function isTextDropItem(dropItem: DropItem): dropItem is TextDropItem {
257
+ return dropItem.kind === 'text';
258
+ }
259
+
260
+ /**
261
+ * Check if a drop item is a file item.
262
+ */
263
+ export function isFileDropItem(dropItem: DropItem): dropItem is FileDropItem {
264
+ return dropItem.kind === 'file';
265
+ }
266
+
267
+ /**
268
+ * Check if a drop item is a directory item.
269
+ */
270
+ export function isDirectoryDropItem(
271
+ dropItem: DropItem
272
+ ): dropItem is DirectoryDropItem {
273
+ return dropItem.kind === 'directory';
274
+ }
275
+
276
+ // Global state for tracking drag operations
277
+ let globalDropEffect: string | undefined;
278
+ let globalAllowedDropOperations: number = DROP_OPERATION.none;
279
+
280
+ export function setGlobalDropEffect(effect: string | undefined): void {
281
+ globalDropEffect = effect;
282
+ }
283
+
284
+ export function getGlobalDropEffect(): string | undefined {
285
+ return globalDropEffect;
286
+ }
287
+
288
+ export function setGlobalAllowedDropOperations(ops: number): void {
289
+ globalAllowedDropOperations = ops;
290
+ }
291
+
292
+ export function getGlobalAllowedDropOperations(): number {
293
+ return globalAllowedDropOperations;
294
+ }