@hkdigital/lib-sveltekit 0.1.76 → 0.1.78
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/assets/autospuiten/car-paint-picker.d.ts +7 -7
- package/dist/components/drag-drop/DragDropContext.svelte +26 -0
- package/dist/components/drag-drop/DragDropContext.svelte.d.ts +18 -0
- package/dist/components/drag-drop/Draggable.svelte +227 -0
- package/dist/components/drag-drop/Draggable.svelte.d.ts +76 -0
- package/dist/components/drag-drop/DropZone.svelte +377 -0
- package/dist/components/drag-drop/DropZone.svelte.d.ts +114 -0
- package/dist/components/drag-drop/drag-state.svelte.d.ts +30 -0
- package/dist/components/drag-drop/drag-state.svelte.js +50 -0
- package/dist/components/drag-drop/index.d.ts +4 -0
- package/dist/components/drag-drop/index.js +5 -0
- package/dist/components/drag-drop/util.d.ts +32 -0
- package/dist/components/drag-drop/util.js +85 -0
- package/dist/components/tab-bar/HkTabBar.state.svelte.d.ts +4 -4
- package/dist/components/tab-bar/HkTabBar.svelte +3 -3
- package/dist/components/tab-bar/HkTabBar.svelte.d.ts +2 -2
- package/dist/components/tab-bar/HkTabBarSelector.state.svelte.d.ts +3 -3
- package/dist/components/tab-bar/HkTabBarSelector.svelte +3 -3
- package/dist/components/tab-bar/HkTabBarSelector.svelte.d.ts +2 -2
- package/dist/components/tab-bar/index.d.ts +1 -0
- package/dist/components/tab-bar/typedef.d.ts +3 -1
- package/dist/components/tab-bar/typedef.js +3 -0
- package/dist/constants/state-labels/drag-states.d.ts +5 -0
- package/dist/constants/state-labels/drag-states.js +6 -0
- package/dist/constants/state-labels/drop-states.d.ts +6 -0
- package/dist/constants/state-labels/drop-states.js +6 -0
- package/dist/themes/hkdev/components/drag-drop/draggable.css +51 -0
- package/dist/themes/hkdev/components/drag-drop/dropzone.css +73 -0
- package/dist/themes/hkdev/components.css +6 -0
- package/dist/typedef/context.d.ts +3 -0
- package/dist/typedef/context.js +6 -0
- package/dist/typedef/drag.d.ts +20 -0
- package/dist/typedef/drag.js +9 -0
- package/dist/typedef/drop.d.ts +20 -0
- package/dist/typedef/drop.js +9 -0
- package/dist/typedef/{image-meta.js → image.js} +0 -1
- package/dist/typedef/index.d.ts +4 -1
- package/dist/typedef/index.js +4 -1
- package/dist/util/geo/index.d.ts +10 -0
- package/dist/util/geo/index.js +26 -0
- package/dist/util/svelte/state-context/index.d.ts +2 -1
- package/dist/util/svelte/state-context/index.js +46 -12
- package/dist/widgets/game-box/GameBox.svelte +1 -0
- package/dist/widgets/hk-app-layout/HkAppLayout.state.svelte.d.ts +3 -3
- package/dist/widgets/image-box/ImageBox.svelte +2 -4
- package/package.json +3 -1
- /package/dist/typedef/{image-meta.d.ts → image.d.ts} +0 -0
@@ -0,0 +1,377 @@
|
|
1
|
+
<script>
|
2
|
+
import { onMount, onDestroy } from 'svelte';
|
3
|
+
import { toStateClasses } from '../../util/design-system/index.js';
|
4
|
+
|
5
|
+
import { createOrGetDragState } from './drag-state.svelte.js';
|
6
|
+
|
7
|
+
import {
|
8
|
+
findDraggableSource,
|
9
|
+
getDraggableIdFromEvent,
|
10
|
+
processDropWithData
|
11
|
+
} from './util.js';
|
12
|
+
|
13
|
+
import {
|
14
|
+
READY,
|
15
|
+
DRAG_OVER,
|
16
|
+
CAN_DROP,
|
17
|
+
CANNOT_DROP,
|
18
|
+
DROP_DISABLED,
|
19
|
+
ACTIVE_DROP
|
20
|
+
} from '../../constants/state-labels/drop-states.js';
|
21
|
+
|
22
|
+
/**
|
23
|
+
* @type {{
|
24
|
+
* zone?: string,
|
25
|
+
* group?: string,
|
26
|
+
* disabled?: boolean,
|
27
|
+
* accepts?: (item: any) => boolean,
|
28
|
+
* maxItems?: number,
|
29
|
+
* base?: string,
|
30
|
+
* classes?: string,
|
31
|
+
* children?: import('svelte').Snippet,
|
32
|
+
* contextKey?: import('../../typedef').ContextKey,
|
33
|
+
* empty?: import('svelte').Snippet,
|
34
|
+
* preview?: import('svelte').Snippet<[{
|
35
|
+
* item: any,
|
36
|
+
* source: string,
|
37
|
+
* group: string,
|
38
|
+
* metadata?: any
|
39
|
+
* }]>,
|
40
|
+
* isDragOver?: boolean,
|
41
|
+
* canDrop?: boolean,
|
42
|
+
* isDropping?: boolean,
|
43
|
+
* itemCount?: number,
|
44
|
+
* onDragEnter?: (detail: {
|
45
|
+
* event: DragEvent,
|
46
|
+
* zone: string,
|
47
|
+
* canDrop: boolean
|
48
|
+
* }) => void,
|
49
|
+
* onDragOver?: (detail: {
|
50
|
+
* event: DragEvent,
|
51
|
+
* zone: string
|
52
|
+
* }) => void,
|
53
|
+
* onDragLeave?: (detail: {
|
54
|
+
* event: DragEvent,
|
55
|
+
* zone: string
|
56
|
+
* }) => void,
|
57
|
+
* onDrop?: (detail: {
|
58
|
+
* event: DragEvent,
|
59
|
+
* zone: string,
|
60
|
+
* item: any,
|
61
|
+
* source: string,
|
62
|
+
* metadata?: any
|
63
|
+
* }) => any | Promise<any>,
|
64
|
+
* onDropStart?: (detail: {
|
65
|
+
* event: DragEvent,
|
66
|
+
* zone: string,
|
67
|
+
* data: any
|
68
|
+
* }) => void,
|
69
|
+
* onDropEnd?: (detail: {
|
70
|
+
* event: DragEvent,
|
71
|
+
* zone: string,
|
72
|
+
* data: any,
|
73
|
+
* success: boolean,
|
74
|
+
* error?: Error
|
75
|
+
* }) => void,
|
76
|
+
* [key: string]: any
|
77
|
+
* }}
|
78
|
+
*/
|
79
|
+
let {
|
80
|
+
zone = 'default',
|
81
|
+
group = 'default',
|
82
|
+
disabled = false,
|
83
|
+
accepts = () => true,
|
84
|
+
maxItems = Infinity,
|
85
|
+
base = '',
|
86
|
+
classes = '',
|
87
|
+
children,
|
88
|
+
contextKey,
|
89
|
+
empty,
|
90
|
+
preview,
|
91
|
+
isDragOver = $bindable(false),
|
92
|
+
canDrop = $bindable(false),
|
93
|
+
isDropping = $bindable(false),
|
94
|
+
itemCount = $bindable(0),
|
95
|
+
onDragEnter,
|
96
|
+
onDragOver,
|
97
|
+
onDragLeave,
|
98
|
+
onDrop,
|
99
|
+
onDropStart,
|
100
|
+
onDropEnd,
|
101
|
+
...attrs
|
102
|
+
} = $props();
|
103
|
+
|
104
|
+
|
105
|
+
const dragState = createOrGetDragState(contextKey);
|
106
|
+
|
107
|
+
// console.debug('DropZone contextKey:', contextKey);
|
108
|
+
|
109
|
+
let currentState = $state(READY);
|
110
|
+
let dropzoneElement; // Reference to the dropzone DOM element
|
111
|
+
|
112
|
+
// We'll use a flag to track if we're currently in the dropzone
|
113
|
+
// without relying on a counter approach
|
114
|
+
let isCurrentlyOver = $state(false);
|
115
|
+
|
116
|
+
// Cleanup function
|
117
|
+
let cleanup;
|
118
|
+
|
119
|
+
onMount(() => {
|
120
|
+
// Global dragend listener to ensure state cleanup
|
121
|
+
const handleGlobalDragEnd = () => {
|
122
|
+
isCurrentlyOver = false;
|
123
|
+
currentState = READY;
|
124
|
+
};
|
125
|
+
|
126
|
+
document.addEventListener('dragend', handleGlobalDragEnd);
|
127
|
+
|
128
|
+
cleanup = () => {
|
129
|
+
document.removeEventListener('dragend', handleGlobalDragEnd);
|
130
|
+
};
|
131
|
+
});
|
132
|
+
|
133
|
+
onDestroy(() => {
|
134
|
+
cleanup?.();
|
135
|
+
});
|
136
|
+
|
137
|
+
// Computed state object for CSS classes
|
138
|
+
let stateObject = $derived({
|
139
|
+
ready: currentState === READY,
|
140
|
+
'drag-over': currentState === DRAG_OVER,
|
141
|
+
'can-drop': currentState === CAN_DROP,
|
142
|
+
'cannot-drop': currentState === CANNOT_DROP,
|
143
|
+
'drop-disabled': disabled,
|
144
|
+
'active-drop': currentState === ACTIVE_DROP
|
145
|
+
});
|
146
|
+
|
147
|
+
let stateClasses = $derived(toStateClasses(stateObject));
|
148
|
+
|
149
|
+
// Update bindable props
|
150
|
+
$effect(() => {
|
151
|
+
isDragOver = [
|
152
|
+
DRAG_OVER,
|
153
|
+
CAN_DROP,
|
154
|
+
CANNOT_DROP
|
155
|
+
].includes(currentState);
|
156
|
+
|
157
|
+
canDrop = currentState === CAN_DROP;
|
158
|
+
isDropping = currentState === ACTIVE_DROP;
|
159
|
+
});
|
160
|
+
|
161
|
+
/**
|
162
|
+
* Check if we can accept the dragged item
|
163
|
+
*
|
164
|
+
* @param {Object} data
|
165
|
+
*
|
166
|
+
* @returns {boolean}
|
167
|
+
*/
|
168
|
+
function canAcceptDrop(data) {
|
169
|
+
// console.debug('canAcceptDrop', data, {group});
|
170
|
+
|
171
|
+
if (disabled) return false;
|
172
|
+
if (!data) return false;
|
173
|
+
if (data.group !== group) return false;
|
174
|
+
if (!accepts(data.item)) return false;
|
175
|
+
if (itemCount >= maxItems) return false;
|
176
|
+
return true;
|
177
|
+
}
|
178
|
+
|
179
|
+
/**
|
180
|
+
* Handle drag enter with improved DOM traversal check
|
181
|
+
* @param {DragEvent} event
|
182
|
+
*/
|
183
|
+
function handleDragEnter(event) {
|
184
|
+
// Prevent default to allow drop
|
185
|
+
event.preventDefault();
|
186
|
+
|
187
|
+
// If we're already in a drag-over state, don't reset anything
|
188
|
+
if (isCurrentlyOver) return;
|
189
|
+
|
190
|
+
// Now we're over this dropzone
|
191
|
+
isCurrentlyOver = true;
|
192
|
+
|
193
|
+
// Get the draggable ID from the event
|
194
|
+
const draggableId = getDraggableIdFromEvent(event);
|
195
|
+
|
196
|
+
if (draggableId) {
|
197
|
+
// Get the drag data for this specific draggable
|
198
|
+
const dragData = dragState.getDraggable(draggableId);
|
199
|
+
|
200
|
+
// Update state based on acceptance
|
201
|
+
if (dragData) {
|
202
|
+
currentState = canAcceptDrop(dragData)
|
203
|
+
? CAN_DROP
|
204
|
+
: CANNOT_DROP;
|
205
|
+
} else {
|
206
|
+
currentState = DRAG_OVER;
|
207
|
+
}
|
208
|
+
} else {
|
209
|
+
// Fallback to the current drag data (for compatibility)
|
210
|
+
const dragData = dragState.current;
|
211
|
+
|
212
|
+
if (dragData) {
|
213
|
+
currentState = canAcceptDrop(dragData)
|
214
|
+
? CAN_DROP
|
215
|
+
: CANNOT_DROP;
|
216
|
+
} else {
|
217
|
+
currentState = DRAG_OVER;
|
218
|
+
}
|
219
|
+
}
|
220
|
+
|
221
|
+
// Notify listeners
|
222
|
+
onDragEnter?.({ event, zone, canDrop: currentState === CAN_DROP });
|
223
|
+
}
|
224
|
+
|
225
|
+
/**
|
226
|
+
* Handle drag over
|
227
|
+
* @param {DragEvent} event
|
228
|
+
*/
|
229
|
+
function handleDragOver(event) {
|
230
|
+
// Prevent default to allow drop
|
231
|
+
event.preventDefault();
|
232
|
+
|
233
|
+
// If we're not currently over this dropzone (despite dragover firing),
|
234
|
+
// treat it as an enter event
|
235
|
+
if (!isCurrentlyOver) {
|
236
|
+
handleDragEnter(event);
|
237
|
+
return;
|
238
|
+
}
|
239
|
+
|
240
|
+
// Get the draggable ID from the event
|
241
|
+
const draggableId = getDraggableIdFromEvent(event);
|
242
|
+
let dragData;
|
243
|
+
|
244
|
+
if (draggableId) {
|
245
|
+
// Get the drag data for this specific draggable
|
246
|
+
dragData = dragState.getDraggable(draggableId);
|
247
|
+
} else {
|
248
|
+
// Fallback to the current drag data (for compatibility)
|
249
|
+
dragData = dragState.current;
|
250
|
+
}
|
251
|
+
|
252
|
+
// Re-evaluate acceptance
|
253
|
+
if (dragData && [DRAG_OVER, CAN_DROP, CANNOT_DROP].includes(currentState)) {
|
254
|
+
currentState = canAcceptDrop(dragData)
|
255
|
+
? CAN_DROP
|
256
|
+
: CANNOT_DROP;
|
257
|
+
}
|
258
|
+
|
259
|
+
// Set visual feedback based on drop acceptance
|
260
|
+
if (currentState === CAN_DROP) {
|
261
|
+
event.dataTransfer.dropEffect = 'move';
|
262
|
+
} else if (currentState === CANNOT_DROP) {
|
263
|
+
event.dataTransfer.dropEffect = 'none';
|
264
|
+
}
|
265
|
+
|
266
|
+
// Notify listeners
|
267
|
+
onDragOver?.({ event, zone });
|
268
|
+
}
|
269
|
+
|
270
|
+
/**
|
271
|
+
* Handle drag leave with improved DOM traversal check
|
272
|
+
* @param {DragEvent} event
|
273
|
+
*/
|
274
|
+
function handleDragLeave(event) {
|
275
|
+
// We need to check if we're actually leaving the dropzone or just
|
276
|
+
// entering a child element within the dropzone
|
277
|
+
|
278
|
+
// relatedTarget is the element we're moving to
|
279
|
+
const relatedTarget = event.relatedTarget;
|
280
|
+
|
281
|
+
// If relatedTarget is null or outside our dropzone, we're truly leaving
|
282
|
+
const isActuallyLeaving = !relatedTarget ||
|
283
|
+
!dropzoneElement.contains(relatedTarget);
|
284
|
+
|
285
|
+
if (isActuallyLeaving) {
|
286
|
+
isCurrentlyOver = false;
|
287
|
+
currentState = READY;
|
288
|
+
onDragLeave?.({ event, zone });
|
289
|
+
}
|
290
|
+
}
|
291
|
+
|
292
|
+
/**
|
293
|
+
* Handle drop
|
294
|
+
* @param {DragEvent} event
|
295
|
+
*/
|
296
|
+
function handleDrop(event) {
|
297
|
+
// Prevent default browser actions
|
298
|
+
event.preventDefault();
|
299
|
+
|
300
|
+
// Reset our tracking state
|
301
|
+
isCurrentlyOver = false;
|
302
|
+
|
303
|
+
try {
|
304
|
+
// First try to get the draggable ID from the event
|
305
|
+
const draggableId = getDraggableIdFromEvent(event);
|
306
|
+
let dragData;
|
307
|
+
|
308
|
+
if (draggableId) {
|
309
|
+
// Get the drag data from state using the draggable ID
|
310
|
+
dragData = dragState.getDraggable(draggableId);
|
311
|
+
}
|
312
|
+
|
313
|
+
// If we couldn't get it from the element attribute, try dataTransfer
|
314
|
+
if (!dragData) {
|
315
|
+
// Parse the JSON data from the dataTransfer object (only works during drop)
|
316
|
+
const jsonData = event.dataTransfer.getData('application/json');
|
317
|
+
if (jsonData) {
|
318
|
+
dragData = JSON.parse(jsonData);
|
319
|
+
}
|
320
|
+
}
|
321
|
+
|
322
|
+
// Check if we can accept this drop
|
323
|
+
if (dragData && canAcceptDrop(dragData)) {
|
324
|
+
// Update state and notify listeners
|
325
|
+
currentState = ACTIVE_DROP;
|
326
|
+
onDropStart?.({ event, zone, data: dragData });
|
327
|
+
|
328
|
+
// Call the onDrop handler and handle Promise resolution
|
329
|
+
const dropResult = onDrop?.({
|
330
|
+
event,
|
331
|
+
zone,
|
332
|
+
item: dragData.item,
|
333
|
+
source: dragData.source,
|
334
|
+
metadata: dragData.metadata
|
335
|
+
});
|
336
|
+
|
337
|
+
// Handle async or sync results
|
338
|
+
Promise.resolve(dropResult).then(() => {
|
339
|
+
currentState = READY;
|
340
|
+
onDropEnd?.({ event, zone, data: dragData, success: true });
|
341
|
+
}).catch((error) => {
|
342
|
+
currentState = READY;
|
343
|
+
onDropEnd?.({ event, zone, data: dragData, success: false, error });
|
344
|
+
});
|
345
|
+
} else {
|
346
|
+
// Not a valid drop, reset state
|
347
|
+
currentState = READY;
|
348
|
+
}
|
349
|
+
} catch (error) {
|
350
|
+
// Handle parsing errors
|
351
|
+
console.error('Drop error:', error);
|
352
|
+
currentState = READY;
|
353
|
+
}
|
354
|
+
}
|
355
|
+
</script>
|
356
|
+
|
357
|
+
<div
|
358
|
+
data-component="dropzone"
|
359
|
+
bind:this={dropzoneElement}
|
360
|
+
ondragenter={handleDragEnter}
|
361
|
+
ondragover={handleDragOver}
|
362
|
+
ondragleave={handleDragLeave}
|
363
|
+
ondrop={handleDrop}
|
364
|
+
class="{base} {classes} {stateClasses}"
|
365
|
+
data-zone={zone}
|
366
|
+
{...attrs}
|
367
|
+
>
|
368
|
+
{#if children}
|
369
|
+
{@render children()}
|
370
|
+
{:else if currentState === CAN_DROP && preview}
|
371
|
+
{@render preview(dragState.current)}
|
372
|
+
{:else if itemCount === 0 && empty}
|
373
|
+
{@render empty()}
|
374
|
+
{:else}
|
375
|
+
<div data-element="drop-zone-empty">Drop items here</div>
|
376
|
+
{/if}
|
377
|
+
</div>
|
@@ -0,0 +1,114 @@
|
|
1
|
+
export default DropZone;
|
2
|
+
type DropZone = {
|
3
|
+
$on?(type: string, callback: (e: any) => void): () => void;
|
4
|
+
$set?(props: Partial<{
|
5
|
+
[key: string]: any;
|
6
|
+
zone?: string;
|
7
|
+
group?: string;
|
8
|
+
disabled?: boolean;
|
9
|
+
accepts?: (item: any) => boolean;
|
10
|
+
maxItems?: number;
|
11
|
+
base?: string;
|
12
|
+
classes?: string;
|
13
|
+
children?: Snippet<[]>;
|
14
|
+
contextKey?: ContextKey;
|
15
|
+
empty?: Snippet<[]>;
|
16
|
+
preview?: Snippet<[{
|
17
|
+
item: any;
|
18
|
+
source: string;
|
19
|
+
group: string;
|
20
|
+
metadata?: any;
|
21
|
+
}]>;
|
22
|
+
isDragOver?: boolean;
|
23
|
+
canDrop?: boolean;
|
24
|
+
isDropping?: boolean;
|
25
|
+
itemCount?: number;
|
26
|
+
onDragEnter?: (detail: {
|
27
|
+
event: DragEvent;
|
28
|
+
zone: string;
|
29
|
+
canDrop: boolean;
|
30
|
+
}) => void;
|
31
|
+
onDragOver?: (detail: {
|
32
|
+
event: DragEvent;
|
33
|
+
zone: string;
|
34
|
+
}) => void;
|
35
|
+
onDragLeave?: (detail: {
|
36
|
+
event: DragEvent;
|
37
|
+
zone: string;
|
38
|
+
}) => void;
|
39
|
+
onDrop?: (detail: {
|
40
|
+
event: DragEvent;
|
41
|
+
zone: string;
|
42
|
+
item: any;
|
43
|
+
source: string;
|
44
|
+
metadata?: any;
|
45
|
+
}) => any;
|
46
|
+
onDropStart?: (detail: {
|
47
|
+
event: DragEvent;
|
48
|
+
zone: string;
|
49
|
+
data: any;
|
50
|
+
}) => void;
|
51
|
+
onDropEnd?: (detail: {
|
52
|
+
event: DragEvent;
|
53
|
+
zone: string;
|
54
|
+
data: any;
|
55
|
+
success: boolean;
|
56
|
+
error?: Error;
|
57
|
+
}) => void;
|
58
|
+
}>): void;
|
59
|
+
};
|
60
|
+
declare const DropZone: import("svelte").Component<{
|
61
|
+
[key: string]: any;
|
62
|
+
zone?: string;
|
63
|
+
group?: string;
|
64
|
+
disabled?: boolean;
|
65
|
+
accepts?: (item: any) => boolean;
|
66
|
+
maxItems?: number;
|
67
|
+
base?: string;
|
68
|
+
classes?: string;
|
69
|
+
children?: import("svelte").Snippet;
|
70
|
+
contextKey?: import("../../typedef").ContextKey;
|
71
|
+
empty?: import("svelte").Snippet;
|
72
|
+
preview?: import("svelte").Snippet<[{
|
73
|
+
item: any;
|
74
|
+
source: string;
|
75
|
+
group: string;
|
76
|
+
metadata?: any;
|
77
|
+
}]>;
|
78
|
+
isDragOver?: boolean;
|
79
|
+
canDrop?: boolean;
|
80
|
+
isDropping?: boolean;
|
81
|
+
itemCount?: number;
|
82
|
+
onDragEnter?: (detail: {
|
83
|
+
event: DragEvent;
|
84
|
+
zone: string;
|
85
|
+
canDrop: boolean;
|
86
|
+
}) => void;
|
87
|
+
onDragOver?: (detail: {
|
88
|
+
event: DragEvent;
|
89
|
+
zone: string;
|
90
|
+
}) => void;
|
91
|
+
onDragLeave?: (detail: {
|
92
|
+
event: DragEvent;
|
93
|
+
zone: string;
|
94
|
+
}) => void;
|
95
|
+
onDrop?: (detail: {
|
96
|
+
event: DragEvent;
|
97
|
+
zone: string;
|
98
|
+
item: any;
|
99
|
+
source: string;
|
100
|
+
metadata?: any;
|
101
|
+
}) => any | Promise<any>;
|
102
|
+
onDropStart?: (detail: {
|
103
|
+
event: DragEvent;
|
104
|
+
zone: string;
|
105
|
+
data: any;
|
106
|
+
}) => void;
|
107
|
+
onDropEnd?: (detail: {
|
108
|
+
event: DragEvent;
|
109
|
+
zone: string;
|
110
|
+
data: any;
|
111
|
+
success: boolean;
|
112
|
+
error?: Error;
|
113
|
+
}) => void;
|
114
|
+
}, {}, "isDropping" | "isDragOver" | "canDrop" | "itemCount">;
|
@@ -0,0 +1,30 @@
|
|
1
|
+
export const createOrGetDragState: (contextKey: import("../../typedef").ContextKey) => DragState;
|
2
|
+
export const createDragState: (contextKey: import("../../typedef").ContextKey) => DragState;
|
3
|
+
export const getDragState: (contextKey: import("../../typedef").ContextKey) => DragState;
|
4
|
+
declare class DragState {
|
5
|
+
draggables: Map<any, any>;
|
6
|
+
/**
|
7
|
+
* @param {string} draggableId
|
8
|
+
* @param {import('../../typedef/drag.js').DragData} dragData
|
9
|
+
*/
|
10
|
+
start(draggableId: string, dragData: import("../../typedef/drag.js").DragData): void;
|
11
|
+
/**
|
12
|
+
* @param {string} draggableId
|
13
|
+
*/
|
14
|
+
end(draggableId: string): void;
|
15
|
+
/**
|
16
|
+
* @param {string} draggableId
|
17
|
+
* @returns {import('../../typedef/drag.js').DragData|undefined}
|
18
|
+
*/
|
19
|
+
getDraggable(draggableId: string): import("../../typedef/drag.js").DragData | undefined;
|
20
|
+
/**
|
21
|
+
* Get the most recently started drag operation (convenience method)
|
22
|
+
* @returns {import('../../typedef/drag.js').DragData|undefined}
|
23
|
+
*/
|
24
|
+
get current(): import("../../typedef/drag.js").DragData | undefined;
|
25
|
+
/**
|
26
|
+
* @returns {boolean}
|
27
|
+
*/
|
28
|
+
isDragging(): boolean;
|
29
|
+
}
|
30
|
+
export {};
|
@@ -0,0 +1,50 @@
|
|
1
|
+
// drag-state.svelte.js
|
2
|
+
import { defineStateContext } from '../../util/svelte/state-context/index.js';
|
3
|
+
|
4
|
+
class DragState {
|
5
|
+
// Replace the single 'current' with a Map of draggable IDs
|
6
|
+
draggables = $state(new Map());
|
7
|
+
|
8
|
+
/**
|
9
|
+
* @param {string} draggableId
|
10
|
+
* @param {import('../../typedef/drag.js').DragData} dragData
|
11
|
+
*/
|
12
|
+
start(draggableId, dragData) {
|
13
|
+
this.draggables.set(draggableId, dragData);
|
14
|
+
}
|
15
|
+
|
16
|
+
/**
|
17
|
+
* @param {string} draggableId
|
18
|
+
*/
|
19
|
+
end(draggableId) {
|
20
|
+
this.draggables.delete(draggableId);
|
21
|
+
}
|
22
|
+
|
23
|
+
/**
|
24
|
+
* @param {string} draggableId
|
25
|
+
* @returns {import('../../typedef/drag.js').DragData|undefined}
|
26
|
+
*/
|
27
|
+
getDraggable(draggableId) {
|
28
|
+
return this.draggables.get(draggableId);
|
29
|
+
}
|
30
|
+
|
31
|
+
/**
|
32
|
+
* Get the most recently started drag operation (convenience method)
|
33
|
+
* @returns {import('../../typedef/drag.js').DragData|undefined}
|
34
|
+
*/
|
35
|
+
get current() {
|
36
|
+
// For backward compatibility with existing code
|
37
|
+
const entries = Array.from(this.draggables.entries());
|
38
|
+
return entries.length > 0 ? entries[entries.length - 1][1] : undefined;
|
39
|
+
}
|
40
|
+
|
41
|
+
/**
|
42
|
+
* @returns {boolean}
|
43
|
+
*/
|
44
|
+
isDragging() {
|
45
|
+
return this.draggables.size > 0;
|
46
|
+
}
|
47
|
+
}
|
48
|
+
|
49
|
+
export const [createOrGetDragState, createDragState, getDragState] =
|
50
|
+
defineStateContext(DragState);
|
@@ -0,0 +1,32 @@
|
|
1
|
+
/**
|
2
|
+
* Find the source draggable element from an event
|
3
|
+
*
|
4
|
+
* @param {DragEvent} event
|
5
|
+
* @returns {HTMLElement|null}
|
6
|
+
*/
|
7
|
+
export function findDraggableSource(event: DragEvent): HTMLElement | null;
|
8
|
+
/**
|
9
|
+
* Get draggable ID from an event, if available
|
10
|
+
* @param {DragEvent} event
|
11
|
+
* @returns {string|null}
|
12
|
+
*/
|
13
|
+
export function getDraggableIdFromEvent(event: DragEvent): string | null;
|
14
|
+
/**
|
15
|
+
* Process a drop event with the provided data and handlers
|
16
|
+
* @param {DragEvent} event
|
17
|
+
* @param {any} data The drag data
|
18
|
+
* @param {Object} options
|
19
|
+
* @param {Function} options.onDropStart Optional drop start handler
|
20
|
+
* @param {Function} options.onDrop Main drop handler
|
21
|
+
* @param {Function} options.onDropEnd Optional drop end handler
|
22
|
+
* @param {string} options.zone The drop zone identifier
|
23
|
+
* @param {Function} options.setState Function to update component state
|
24
|
+
* @returns {Promise<boolean>} Success status
|
25
|
+
*/
|
26
|
+
export function processDropWithData(event: DragEvent, data: any, { onDropStart, onDrop, onDropEnd, zone, setState }: {
|
27
|
+
onDropStart: Function;
|
28
|
+
onDrop: Function;
|
29
|
+
onDropEnd: Function;
|
30
|
+
zone: string;
|
31
|
+
setState: Function;
|
32
|
+
}): Promise<boolean>;
|
@@ -0,0 +1,85 @@
|
|
1
|
+
/**
|
2
|
+
* Find the source draggable element from an event
|
3
|
+
*
|
4
|
+
* @param {DragEvent} event
|
5
|
+
* @returns {HTMLElement|null}
|
6
|
+
*/
|
7
|
+
export function findDraggableSource(event) {
|
8
|
+
const target = /** @type {Element|EventTarget|null} */ (event.target);
|
9
|
+
|
10
|
+
if (!(target instanceof Element)) {
|
11
|
+
return null;
|
12
|
+
}
|
13
|
+
|
14
|
+
let element = /** @type {Element|null} */ (target);
|
15
|
+
|
16
|
+
// Walk up the DOM tree
|
17
|
+
while (element !== null && element !== document.body) {
|
18
|
+
if (element.hasAttribute('data-id')) {
|
19
|
+
// Return as HTMLElement if needed
|
20
|
+
return /** @type {HTMLElement} */ (element);
|
21
|
+
}
|
22
|
+
|
23
|
+
element = element.parentElement;
|
24
|
+
}
|
25
|
+
|
26
|
+
return null;
|
27
|
+
}
|
28
|
+
|
29
|
+
/**
|
30
|
+
* Get draggable ID from an event, if available
|
31
|
+
* @param {DragEvent} event
|
32
|
+
* @returns {string|null}
|
33
|
+
*/
|
34
|
+
export function getDraggableIdFromEvent(event) {
|
35
|
+
const element = findDraggableSource(event);
|
36
|
+
return element ? element.getAttribute('data-id') : null;
|
37
|
+
}
|
38
|
+
|
39
|
+
/**
|
40
|
+
* Process a drop event with the provided data and handlers
|
41
|
+
* @param {DragEvent} event
|
42
|
+
* @param {any} data The drag data
|
43
|
+
* @param {Object} options
|
44
|
+
* @param {Function} options.onDropStart Optional drop start handler
|
45
|
+
* @param {Function} options.onDrop Main drop handler
|
46
|
+
* @param {Function} options.onDropEnd Optional drop end handler
|
47
|
+
* @param {string} options.zone The drop zone identifier
|
48
|
+
* @param {Function} options.setState Function to update component state
|
49
|
+
* @returns {Promise<boolean>} Success status
|
50
|
+
*/
|
51
|
+
export async function processDropWithData(event, data, {
|
52
|
+
onDropStart,
|
53
|
+
onDrop,
|
54
|
+
onDropEnd,
|
55
|
+
zone,
|
56
|
+
setState
|
57
|
+
}) {
|
58
|
+
try {
|
59
|
+
// Update state and notify listeners
|
60
|
+
setState('ACTIVE_DROP');
|
61
|
+
onDropStart?.({ event, zone, data });
|
62
|
+
|
63
|
+
// Call the onDrop handler
|
64
|
+
const dropResult = onDrop?.({
|
65
|
+
event,
|
66
|
+
zone,
|
67
|
+
item: data.item,
|
68
|
+
source: data.source,
|
69
|
+
metadata: data.metadata
|
70
|
+
});
|
71
|
+
|
72
|
+
// Handle async or sync results
|
73
|
+
await Promise.resolve(dropResult);
|
74
|
+
|
75
|
+
// Success path
|
76
|
+
setState('READY');
|
77
|
+
onDropEnd?.({ event, zone, data, success: true });
|
78
|
+
return true;
|
79
|
+
} catch (error) {
|
80
|
+
// Error path
|
81
|
+
setState('READY');
|
82
|
+
onDropEnd?.({ event, zone, data, success: false, error });
|
83
|
+
return false;
|
84
|
+
}
|
85
|
+
}
|