@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.
- package/dist/autocomplete/createAutocomplete.d.ts +2 -2
- package/dist/autocomplete/createAutocomplete.d.ts.map +1 -1
- package/dist/index.js +233 -234
- package/dist/index.js.map +2 -2
- package/dist/index.ssr.js +233 -234
- package/dist/index.ssr.js.map +2 -2
- package/dist/interactions/PressEvent.d.ts +13 -10
- package/dist/interactions/PressEvent.d.ts.map +1 -1
- package/dist/interactions/createPress.d.ts.map +1 -1
- package/dist/interactions/index.d.ts +1 -1
- package/dist/interactions/index.d.ts.map +1 -1
- package/dist/select/createHiddenSelect.d.ts.map +1 -1
- package/dist/toolbar/createToolbar.d.ts.map +1 -1
- package/dist/tooltip/createTooltipTrigger.d.ts.map +1 -1
- package/package.json +9 -7
- package/src/autocomplete/createAutocomplete.ts +341 -0
- package/src/autocomplete/index.ts +9 -0
- package/src/breadcrumbs/createBreadcrumbs.ts +196 -0
- package/src/breadcrumbs/index.ts +8 -0
- package/src/button/createButton.ts +142 -0
- package/src/button/createToggleButton.ts +101 -0
- package/src/button/index.ts +4 -0
- package/src/button/types.ts +78 -0
- package/src/calendar/createCalendar.ts +138 -0
- package/src/calendar/createCalendarCell.ts +187 -0
- package/src/calendar/createCalendarGrid.ts +140 -0
- package/src/calendar/createRangeCalendar.ts +136 -0
- package/src/calendar/createRangeCalendarCell.ts +186 -0
- package/src/calendar/index.ts +34 -0
- package/src/checkbox/createCheckbox.ts +135 -0
- package/src/checkbox/createCheckboxGroup.ts +137 -0
- package/src/checkbox/createCheckboxGroupItem.ts +117 -0
- package/src/checkbox/createCheckboxGroupState.ts +193 -0
- package/src/checkbox/index.ts +13 -0
- package/src/color/createColorArea.ts +314 -0
- package/src/color/createColorField.ts +137 -0
- package/src/color/createColorSlider.ts +197 -0
- package/src/color/createColorSwatch.ts +40 -0
- package/src/color/createColorWheel.ts +208 -0
- package/src/color/index.ts +24 -0
- package/src/color/types.ts +116 -0
- package/src/combobox/createComboBox.ts +647 -0
- package/src/combobox/index.ts +6 -0
- package/src/combobox/intl/en-US.json +7 -0
- package/src/combobox/intl/es-ES.json +7 -0
- package/src/combobox/intl/index.ts +23 -0
- package/src/datepicker/createDateField.ts +154 -0
- package/src/datepicker/createDatePicker.ts +206 -0
- package/src/datepicker/createDateSegment.ts +229 -0
- package/src/datepicker/createTimeField.ts +154 -0
- package/src/datepicker/index.ts +28 -0
- package/src/dialog/createDialog.ts +120 -0
- package/src/dialog/index.ts +2 -0
- package/src/dialog/types.ts +19 -0
- package/src/disclosure/createDisclosure.ts +131 -0
- package/src/disclosure/createDisclosureGroup.ts +62 -0
- package/src/disclosure/index.ts +11 -0
- package/src/dnd/createDrag.ts +209 -0
- package/src/dnd/createDraggableCollection.ts +63 -0
- package/src/dnd/createDraggableItem.ts +243 -0
- package/src/dnd/createDrop.ts +321 -0
- package/src/dnd/createDroppableCollection.ts +293 -0
- package/src/dnd/createDroppableItem.ts +213 -0
- package/src/dnd/index.ts +47 -0
- package/src/dnd/types.ts +89 -0
- package/src/dnd/utils.ts +294 -0
- package/src/focus/FocusScope.tsx +408 -0
- package/src/focus/createAutoFocus.ts +321 -0
- package/src/focus/createFocusRestore.ts +313 -0
- package/src/focus/createVirtualFocus.ts +396 -0
- package/src/focus/index.ts +35 -0
- package/src/form/createFormReset.ts +51 -0
- package/src/form/createFormValidation.ts +224 -0
- package/src/form/index.ts +11 -0
- package/src/grid/GridKeyboardDelegate.ts +429 -0
- package/src/grid/createGrid.ts +261 -0
- package/src/grid/createGridCell.ts +182 -0
- package/src/grid/createGridRow.ts +153 -0
- package/src/grid/index.ts +18 -0
- package/src/grid/types.ts +133 -0
- package/src/gridlist/createGridList.ts +185 -0
- package/src/gridlist/createGridListItem.ts +180 -0
- package/src/gridlist/createGridListSelectionCheckbox.ts +59 -0
- package/src/gridlist/index.ts +16 -0
- package/src/gridlist/types.ts +81 -0
- package/src/i18n/NumberFormatter.ts +266 -0
- package/src/i18n/createCollator.ts +79 -0
- package/src/i18n/createDateFormatter.ts +83 -0
- package/src/i18n/createFilter.ts +131 -0
- package/src/i18n/createNumberFormatter.ts +52 -0
- package/src/i18n/createStringFormatter.ts +87 -0
- package/src/i18n/index.ts +40 -0
- package/src/i18n/locale.tsx +188 -0
- package/src/i18n/utils.ts +99 -0
- package/src/index.ts +670 -0
- package/src/interactions/FocusableProvider.tsx +44 -0
- package/src/interactions/PressEvent.ts +126 -0
- package/src/interactions/createFocus.ts +163 -0
- package/src/interactions/createFocusRing.ts +89 -0
- package/src/interactions/createFocusWithin.ts +206 -0
- package/src/interactions/createFocusable.ts +168 -0
- package/src/interactions/createHover.ts +254 -0
- package/src/interactions/createInteractionModality.ts +424 -0
- package/src/interactions/createKeyboard.ts +82 -0
- package/src/interactions/createLongPress.ts +174 -0
- package/src/interactions/createMove.ts +289 -0
- package/src/interactions/createPress.ts +834 -0
- package/src/interactions/index.ts +78 -0
- package/src/label/createField.ts +145 -0
- package/src/label/createLabel.ts +117 -0
- package/src/label/createLabels.ts +50 -0
- package/src/label/index.ts +19 -0
- package/src/landmark/createLandmark.ts +377 -0
- package/src/landmark/index.ts +8 -0
- package/src/link/createLink.ts +182 -0
- package/src/link/index.ts +1 -0
- package/src/listbox/createListBox.ts +269 -0
- package/src/listbox/createOption.ts +151 -0
- package/src/listbox/index.ts +12 -0
- package/src/live-announcer/announce.ts +322 -0
- package/src/live-announcer/index.ts +9 -0
- package/src/menu/createMenu.ts +396 -0
- package/src/menu/createMenuItem.ts +149 -0
- package/src/menu/createMenuTrigger.ts +88 -0
- package/src/menu/index.ts +18 -0
- package/src/meter/createMeter.ts +75 -0
- package/src/meter/index.ts +1 -0
- package/src/numberfield/createNumberField.ts +268 -0
- package/src/numberfield/index.ts +5 -0
- package/src/overlays/ariaHideOutside.ts +219 -0
- package/src/overlays/createInteractOutside.ts +149 -0
- package/src/overlays/createModal.tsx +202 -0
- package/src/overlays/createOverlay.ts +155 -0
- package/src/overlays/createOverlayTrigger.ts +85 -0
- package/src/overlays/createPreventScroll.ts +266 -0
- package/src/overlays/index.ts +44 -0
- package/src/popover/calculatePosition.ts +766 -0
- package/src/popover/createOverlayPosition.ts +356 -0
- package/src/popover/createPopover.ts +170 -0
- package/src/popover/index.ts +24 -0
- package/src/progress/createProgressBar.ts +128 -0
- package/src/progress/index.ts +5 -0
- package/src/radio/createRadio.ts +287 -0
- package/src/radio/createRadioGroup.ts +189 -0
- package/src/radio/createRadioGroupState.ts +201 -0
- package/src/radio/index.ts +23 -0
- package/src/searchfield/createSearchField.ts +186 -0
- package/src/searchfield/index.ts +2 -0
- package/src/select/createHiddenSelect.tsx +236 -0
- package/src/select/createSelect.ts +395 -0
- package/src/select/index.ts +14 -0
- package/src/selection/createTypeSelect.ts +201 -0
- package/src/selection/index.ts +6 -0
- package/src/separator/createSeparator.ts +82 -0
- package/src/separator/index.ts +6 -0
- package/src/slider/createSlider.ts +349 -0
- package/src/slider/index.ts +2 -0
- package/src/ssr/index.tsx +370 -0
- package/src/switch/createSwitch.ts +70 -0
- package/src/switch/index.ts +1 -0
- package/src/table/createTable.ts +526 -0
- package/src/table/createTableCell.ts +147 -0
- package/src/table/createTableColumnHeader.ts +115 -0
- package/src/table/createTableHeaderRow.ts +40 -0
- package/src/table/createTableRow.ts +155 -0
- package/src/table/createTableRowGroup.ts +32 -0
- package/src/table/createTableSelectAllCheckbox.ts +73 -0
- package/src/table/createTableSelectionCheckbox.ts +59 -0
- package/src/table/index.ts +30 -0
- package/src/table/types.ts +165 -0
- package/src/tabs/createTabs.ts +472 -0
- package/src/tabs/index.ts +14 -0
- package/src/tag/createTag.ts +194 -0
- package/src/tag/createTagGroup.ts +154 -0
- package/src/tag/index.ts +12 -0
- package/src/textfield/createTextField.ts +198 -0
- package/src/textfield/index.ts +5 -0
- package/src/toast/createToast.ts +118 -0
- package/src/toast/createToastRegion.ts +100 -0
- package/src/toast/index.ts +11 -0
- package/src/toggle/createToggle.ts +223 -0
- package/src/toggle/createToggleState.ts +94 -0
- package/src/toggle/index.ts +7 -0
- package/src/toolbar/createToolbar.ts +369 -0
- package/src/toolbar/index.ts +6 -0
- package/src/tooltip/createTooltip.ts +79 -0
- package/src/tooltip/createTooltipTrigger.ts +222 -0
- package/src/tooltip/index.ts +6 -0
- package/src/tree/createTree.ts +246 -0
- package/src/tree/createTreeItem.ts +233 -0
- package/src/tree/createTreeSelectionCheckbox.ts +68 -0
- package/src/tree/index.ts +16 -0
- package/src/tree/types.ts +87 -0
- package/src/utils/createDescription.ts +137 -0
- package/src/utils/dom.ts +327 -0
- package/src/utils/env.ts +54 -0
- package/src/utils/events.ts +106 -0
- package/src/utils/filterDOMProps.ts +116 -0
- package/src/utils/focus.ts +151 -0
- package/src/utils/geometry.ts +115 -0
- package/src/utils/globalListeners.ts +142 -0
- package/src/utils/index.ts +80 -0
- package/src/utils/mergeProps.ts +52 -0
- package/src/utils/platform.ts +52 -0
- package/src/utils/reactivity.ts +36 -0
- package/src/utils/textSelection.ts +114 -0
- package/src/visually-hidden/createVisuallyHidden.ts +124 -0
- package/src/visually-hidden/index.ts +6 -0
- package/dist/index.jsx +0 -15845
- package/dist/index.jsx.map +0 -7
|
@@ -0,0 +1,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
|
+
}
|
package/src/dnd/index.ts
ADDED
|
@@ -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';
|
package/src/dnd/types.ts
ADDED
|
@@ -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
|
+
}
|
package/src/dnd/utils.ts
ADDED
|
@@ -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
|
+
}
|