@hkdigital/lib-sveltekit 0.2.20 → 0.2.21
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/README.md +135 -135
- package/dist/assets/autospuiten/car-paint-picker.js +41 -41
- package/dist/assets/autospuiten/labels.js +7 -7
- package/dist/classes/cache/IndexedDbCache.js +1407 -1407
- package/dist/classes/cache/MemoryResponseCache.js +138 -138
- package/dist/classes/cache/index.js +5 -5
- package/dist/classes/cache/typedef.js +41 -41
- package/dist/classes/data/IterableTree.js +243 -243
- package/dist/classes/data/Selector.js +190 -190
- package/dist/classes/data/index.js +2 -2
- package/dist/classes/events/EventEmitter.js +275 -275
- package/dist/classes/events/index.js +2 -2
- package/dist/classes/index.js +4 -4
- package/dist/classes/logging/Logger.js +210 -210
- package/dist/classes/logging/constants.js +16 -16
- package/dist/classes/logging/index.js +4 -4
- package/dist/classes/logging/typedef.js +17 -17
- package/dist/classes/promise/HkPromise.js +377 -377
- package/dist/classes/promise/index.js +1 -1
- package/dist/classes/services/ServiceBase.js +463 -463
- package/dist/classes/services/ServiceManager.js +614 -614
- package/dist/classes/services/index.js +5 -5
- package/dist/classes/services/service-states.js +205 -205
- package/dist/classes/services/typedef.js +179 -179
- package/dist/classes/stores/SubscribersCount.js +107 -107
- package/dist/classes/stores/index.js +1 -1
- package/dist/classes/streams/LogTransformStream.js +19 -19
- package/dist/classes/streams/ServerEventsStore.js +110 -110
- package/dist/classes/streams/TimeStampSource.js +26 -26
- package/dist/classes/streams/index.js +3 -3
- package/dist/classes/svelte/audio/AudioLoader.svelte.js +58 -58
- package/dist/classes/svelte/audio/AudioScene.svelte.js +324 -324
- package/dist/classes/svelte/audio/mocks.js +35 -35
- package/dist/classes/svelte/finite-state-machine/FiniteStateMachine.svelte.js +133 -133
- package/dist/classes/svelte/finite-state-machine/index.js +1 -1
- package/dist/classes/svelte/image/ImageLoader.svelte.js +45 -45
- package/dist/classes/svelte/image/ImageScene.svelte.js +249 -249
- package/dist/classes/svelte/image/ImageVariantsLoader.svelte.js +152 -152
- package/dist/classes/svelte/image/index.js +4 -4
- package/dist/classes/svelte/image/mocks.js +35 -35
- package/dist/classes/svelte/image/typedef.js +8 -8
- package/dist/classes/svelte/index.js +14 -14
- package/dist/classes/svelte/loading-state-machine/LoadingStateMachine.svelte.js +109 -109
- package/dist/classes/svelte/loading-state-machine/constants.js +16 -16
- package/dist/classes/svelte/loading-state-machine/index.js +3 -3
- package/dist/classes/svelte/network-loader/NetworkLoader.svelte.js +338 -338
- package/dist/classes/svelte/network-loader/constants.js +3 -3
- package/dist/classes/svelte/network-loader/index.js +3 -3
- package/dist/classes/svelte/network-loader/mocks.js +30 -30
- package/dist/classes/svelte/network-loader/typedef.js +8 -8
- package/dist/components/area/HkArea.svelte +49 -49
- package/dist/components/area/HkGridArea.svelte +77 -77
- package/dist/components/area/index.js +2 -2
- package/dist/components/buttons/button/Button.svelte +82 -82
- package/dist/components/buttons/button-icon-steeze/SteezeIconButton.svelte +30 -30
- package/dist/components/buttons/button-text/TextButton.svelte +21 -21
- package/dist/components/buttons/index.js +3 -3
- package/dist/components/debug/debug-panel-design-scaling/DebugPanelDesignScaling.svelte +146 -146
- package/dist/components/debug/index.js +1 -1
- package/dist/components/drag-drop/DragController.js +44 -44
- package/dist/components/drag-drop/DragDropContext.svelte +111 -111
- package/dist/components/drag-drop/Draggable.svelte +519 -519
- package/dist/components/drag-drop/DropZoneArea.svelte +119 -119
- package/dist/components/drag-drop/DropZoneList.svelte +125 -125
- package/dist/components/drag-drop/{DropZone.svelte → Dropzone.svelte} +258 -258
- package/dist/components/drag-drop/actions.js +26 -26
- package/dist/components/drag-drop/drag-state.svelte.js +322 -322
- package/dist/components/drag-drop/index.js +7 -7
- package/dist/components/drag-drop/util.js +85 -85
- package/dist/components/hkdev/blocks/TextBlock.svelte +46 -46
- package/dist/components/hkdev/buttons/CheckButton.svelte +62 -62
- package/dist/components/icons/HkIcon.svelte +86 -86
- package/dist/components/icons/HkTabIcon.svelte +116 -116
- package/dist/components/icons/SteezeIcon.svelte +97 -97
- package/dist/components/icons/index.js +6 -6
- package/dist/components/icons/typedef.js +16 -16
- package/dist/components/index.js +2 -2
- package/dist/components/inputs/index.js +1 -1
- package/dist/components/inputs/text-input/TestTextInput.svelte__ +102 -102
- package/dist/components/inputs/text-input/TextInput.svelte +223 -223
- package/dist/components/inputs/text-input/TextInput.svelte___ +83 -83
- package/dist/components/inputs/text-input/assets/IconInvalid.svelte +14 -14
- package/dist/components/inputs/text-input/assets/IconValid.svelte +12 -12
- package/dist/components/layout/grid-layers/GridLayers.svelte +63 -63
- package/dist/components/layout/grid-layers/util.js +74 -74
- package/dist/components/layout/index.js +1 -1
- package/dist/components/panels/index.js +1 -1
- package/dist/components/panels/panel/Panel.svelte +43 -43
- package/dist/components/rows/index.js +3 -3
- package/dist/components/rows/panel-grid-row/PanelGridRow.svelte +104 -104
- package/dist/components/rows/panel-row-2/PanelRow2.svelte +40 -40
- package/dist/components/tab-bar/HkTabBar.state.svelte.js +149 -149
- package/dist/components/tab-bar/HkTabBar.svelte +74 -74
- package/dist/components/tab-bar/HkTabBarSelector.state.svelte.js +93 -93
- package/dist/components/tab-bar/HkTabBarSelector.svelte +49 -49
- package/dist/components/tab-bar/index.js +17 -17
- package/dist/components/tab-bar/typedef.js +11 -11
- package/dist/config/imagetools-config.js +189 -189
- package/dist/config/imagetools.d.ts +72 -72
- package/dist/constants/bases.js +13 -13
- package/dist/constants/errors/api.js +9 -9
- package/dist/constants/errors/generic.js +5 -5
- package/dist/constants/errors/index.js +3 -3
- package/dist/constants/errors/jwt.js +5 -5
- package/dist/constants/http/headers.js +6 -6
- package/dist/constants/http/index.js +2 -2
- package/dist/constants/http/methods.js +14 -14
- package/dist/constants/index.js +3 -3
- package/dist/constants/mime/application.js +5 -5
- package/dist/constants/mime/audio.js +13 -13
- package/dist/constants/mime/image.js +3 -3
- package/dist/constants/mime/index.js +4 -4
- package/dist/constants/mime/text.js +2 -2
- package/dist/constants/regexp/index.js +31 -31
- package/dist/constants/regexp/inspiratie.js__ +95 -95
- package/dist/constants/regexp/text.js +49 -49
- package/dist/constants/regexp/user.js +32 -32
- package/dist/constants/regexp/web.js +3 -3
- package/dist/constants/state-labels/drag-states.js +6 -6
- package/dist/constants/state-labels/drop-states.js +6 -6
- package/dist/constants/state-labels/input-states.js +11 -11
- package/dist/constants/state-labels/submit-states.js +4 -4
- package/dist/constants/time.js +28 -28
- package/dist/css/utilities.css +43 -43
- package/dist/design/design-config.js +73 -73
- package/dist/design/tailwind-theme-extend.js +158 -158
- package/dist/features/button-group/ButtonGroup.svelte +82 -82
- package/dist/features/button-group/typedef.js +10 -10
- package/dist/features/compare-left-right/CompareLeftRight.svelte +179 -179
- package/dist/features/compare-left-right/index.js +1 -1
- package/dist/features/game-box/GameBox.svelte +577 -577
- package/dist/features/game-box/gamebox.util.js +83 -83
- package/dist/features/hk-app-layout/HkAppLayout.state.svelte.js +25 -25
- package/dist/features/hk-app-layout/HkAppLayout.svelte +251 -251
- package/dist/features/image-box/ImageBox.svelte +210 -210
- package/dist/features/image-box/index.js +5 -5
- package/dist/features/image-box/typedef.js +32 -32
- package/dist/features/index.js +23 -23
- package/dist/features/presenter/ImageSlide.svelte +64 -64
- package/dist/features/presenter/Presenter.state.svelte.js +638 -638
- package/dist/features/presenter/Presenter.svelte +142 -142
- package/dist/features/presenter/constants.js +7 -7
- package/dist/features/presenter/index.js +10 -10
- package/dist/features/presenter/typedef.js +106 -106
- package/dist/features/presenter/util.js +210 -210
- package/dist/features/virtual-viewport/VirtualViewport.svelte +196 -196
- package/dist/logging/adapters/console.js +114 -114
- package/dist/logging/adapters/pino.js +60 -60
- package/dist/logging/constants.js +1 -1
- package/dist/logging/factories/client.js +21 -21
- package/dist/logging/factories/server.js +22 -22
- package/dist/logging/factories/universal.js +23 -23
- package/dist/logging/index.js +8 -8
- package/dist/schemas/index.js +1 -1
- package/dist/schemas/validate-url.js +180 -180
- package/dist/server/index.js +1 -1
- package/dist/server/logger.js +94 -94
- package/dist/states/index.js +1 -1
- package/dist/states/navigation.svelte.js +55 -55
- package/dist/stores/index.js +1 -1
- package/dist/stores/theme.js +80 -80
- package/dist/themes/hkdev/components/blocks/text-block.css +34 -41
- package/dist/themes/hkdev/components/boxes/game-box.css +11 -12
- package/dist/themes/hkdev/components/buttons/button-icon-steeze.css +22 -22
- package/dist/themes/hkdev/components/buttons/button-text.css +32 -32
- package/dist/themes/hkdev/components/buttons/button.css +146 -146
- package/dist/themes/hkdev/components/buttons/skip-button.css +5 -6
- package/dist/themes/hkdev/components/drag-drop/draggable.css +73 -73
- package/dist/themes/hkdev/components/drag-drop/drop-zone.css +58 -48
- package/dist/themes/hkdev/components/icons/icon-steeze.css +16 -22
- package/dist/themes/hkdev/components/inputs/text-input.css +102 -104
- package/dist/themes/hkdev/components/panels/panel.css +25 -27
- package/dist/themes/hkdev/components/rows/panel-grid-row.css +4 -6
- package/dist/themes/hkdev/components/rows/panel-row-2.css +5 -7
- package/dist/themes/hkdev/components.css +29 -53
- package/dist/themes/hkdev/debug.css +1 -1
- package/dist/themes/hkdev/global/layout.css +32 -39
- package/dist/themes/hkdev/global/on-colors.css +32 -53
- package/dist/themes/hkdev/globals.css +4 -11
- package/dist/themes/hkdev/responsive.css +12 -12
- package/dist/themes/hkdev/theme-ext.js +12 -15
- package/dist/themes/hkdev/theme.css +219 -0
- package/dist/themes/index.d.ts +1 -1
- package/dist/themes/index.js +1 -1
- package/dist/typedef/context.js +6 -6
- package/dist/typedef/drag.js +25 -25
- package/dist/typedef/drop.js +12 -12
- package/dist/typedef/image.js +38 -38
- package/dist/typedef/index.js +4 -4
- package/dist/util/array/index.js +436 -436
- package/dist/util/bases/base58.js +262 -262
- package/dist/util/bases/index.js +1 -1
- package/dist/util/compare/index.js +247 -247
- package/dist/util/css/css-vars.js +83 -83
- package/dist/util/css/index.js +1 -1
- package/dist/util/design-system/components/states.js +22 -22
- package/dist/util/design-system/css/clamp.js +66 -66
- package/dist/util/design-system/css/root-design-vars.js +102 -102
- package/dist/util/design-system/index.js +5 -5
- package/dist/util/design-system/layout/scaling.js +228 -228
- package/dist/util/design-system/skeleton.js +208 -208
- package/dist/util/design-system/tailwind.js +288 -288
- package/dist/util/env/index.js +9 -9
- package/dist/util/expect/arrays.js +47 -47
- package/dist/util/expect/index.js +259 -259
- package/dist/util/expect/primitives.js +55 -55
- package/dist/util/expect/url.js +60 -60
- package/dist/util/function/index.js +218 -218
- package/dist/util/geo/index.js +26 -26
- package/dist/util/http/caching.js +263 -263
- package/dist/util/http/errors.js +97 -97
- package/dist/util/http/headers.js +75 -75
- package/dist/util/http/http-request.js +578 -578
- package/dist/util/http/index.js +22 -22
- package/dist/util/http/json-request.js +224 -224
- package/dist/util/http/mocks.js +65 -65
- package/dist/util/http/response.js +294 -294
- package/dist/util/http/typedef.js +93 -93
- package/dist/util/http/url.js +52 -52
- package/dist/util/image/index.js +86 -86
- package/dist/util/index.js +2 -2
- package/dist/util/is/index.js +140 -140
- package/dist/util/iterate/index.js +234 -234
- package/dist/util/object/index.js +1361 -1361
- package/dist/util/singleton/index.js +97 -97
- package/dist/util/string/array-path.js +75 -75
- package/dist/util/string/convert.js +54 -54
- package/dist/util/string/fs.js +226 -226
- package/dist/util/string/index.js +5 -5
- package/dist/util/string/interpolate.js +61 -61
- package/dist/util/string/pad.js +10 -10
- package/dist/util/svelte/index.js +4 -4
- package/dist/util/svelte/loading/loading-tracker.svelte.js +108 -108
- package/dist/util/svelte/observe/index.js +49 -49
- package/dist/util/svelte/state-context/index.js +117 -117
- package/dist/util/svelte/wait/index.js +38 -38
- package/dist/util/sveltekit/index.js +1 -1
- package/dist/util/sveltekit/route-folders/index.js +101 -101
- package/dist/util/time/index.js +323 -323
- package/dist/util/unique/index.js +249 -249
- package/dist/valibot/date.js__ +10 -10
- package/dist/valibot/index.js +9 -9
- package/dist/valibot/url.js +95 -95
- package/dist/valibot/user.js +23 -23
- package/dist/zod/all.js +33 -33
- package/dist/zod/generic.js +11 -11
- package/dist/zod/javascript.js +32 -32
- package/dist/zod/user.js +16 -16
- package/dist/zod/web.js +52 -52
- package/package.json +132 -129
- package/dist/components/layout/grid-layers/GridLayers.svelte__heightFrom__ +0 -372
- package/dist/themes/hkdev/theme.d.ts +0 -234
- package/dist/themes/hkdev/theme.js +0 -235
- package/dist/util/http/test-data__/content-length-test-hkdigital-small.V4HfZyBQ.avif +0 -0
@@ -1,519 +1,519 @@
|
|
1
|
-
<script>
|
2
|
-
import { browser } from '$app/environment';
|
3
|
-
|
4
|
-
import { toStateClasses } from '../../util/design-system/index.js';
|
5
|
-
import { createOrGetDragState } from './drag-state.svelte.js';
|
6
|
-
import { DragController } from './DragController.js';
|
7
|
-
import { onDestroy } from 'svelte';
|
8
|
-
import {
|
9
|
-
IDLE,
|
10
|
-
DRAGGING,
|
11
|
-
DRAG_PREVIEW,
|
12
|
-
DROPPING
|
13
|
-
} from '../../constants/state-labels/drag-states.js';
|
14
|
-
|
15
|
-
/** @typedef {import('../../typedef').SimulatedDragEvent} SimulatedDragEvent */
|
16
|
-
|
17
|
-
/**
|
18
|
-
* @type {{
|
19
|
-
* item: any,
|
20
|
-
* group?: string,
|
21
|
-
* source?: string,
|
22
|
-
* disabled?: boolean,
|
23
|
-
* dragDelay?: number,
|
24
|
-
* base?: string,
|
25
|
-
* classes?: string,
|
26
|
-
* children: import('svelte').Snippet<[{
|
27
|
-
* element: HTMLElement,
|
28
|
-
* rect: DOMRect,
|
29
|
-
* isDragging: boolean
|
30
|
-
* }]>,
|
31
|
-
* draggingSnippet?: import('svelte').Snippet<[{
|
32
|
-
* element: HTMLElement,
|
33
|
-
* rect: DOMRect
|
34
|
-
* }]>,
|
35
|
-
* contextKey?: import('../../typedef').ContextKey,
|
36
|
-
* isDragging?: boolean,
|
37
|
-
* isDropping?: boolean,
|
38
|
-
* isDragPreview?: boolean,
|
39
|
-
* onDragStart?: (detail: {
|
40
|
-
* event: DragEvent,
|
41
|
-
* item: any,
|
42
|
-
* source: string,
|
43
|
-
* group: string,
|
44
|
-
* getController: () => DragController
|
45
|
-
* }) => void,
|
46
|
-
* onDragging?: (detail: {
|
47
|
-
* event: DragEvent,
|
48
|
-
* item: any
|
49
|
-
* }) => void,
|
50
|
-
* onDragEnd?: (detail: {
|
51
|
-
* event: DragEvent,
|
52
|
-
* item: any,
|
53
|
-
* wasDropped: boolean
|
54
|
-
* }) => void,
|
55
|
-
* onDrop?: (detail: {
|
56
|
-
* event: DragEvent,
|
57
|
-
* item: any,
|
58
|
-
* wasDropped: boolean
|
59
|
-
* }) => void,
|
60
|
-
* canDrag?: (item: any) => boolean,
|
61
|
-
* [key: string]: any
|
62
|
-
* }}
|
63
|
-
*/
|
64
|
-
let {
|
65
|
-
item,
|
66
|
-
group = 'default',
|
67
|
-
source = 'default',
|
68
|
-
disabled = false,
|
69
|
-
dragDelay = 0,
|
70
|
-
base = '',
|
71
|
-
classes = '',
|
72
|
-
children,
|
73
|
-
draggingSnippet,
|
74
|
-
contextKey,
|
75
|
-
isDragging = $bindable(false),
|
76
|
-
isDropping = $bindable(false),
|
77
|
-
isDragPreview = $bindable(false),
|
78
|
-
onDragStart,
|
79
|
-
onDragging,
|
80
|
-
onDragEnd,
|
81
|
-
onDrop,
|
82
|
-
canDrag = () => true,
|
83
|
-
...attrs
|
84
|
-
} = $props();
|
85
|
-
|
86
|
-
const dragState = createOrGetDragState(contextKey);
|
87
|
-
|
88
|
-
const draggableId = dragState.newDraggableId();
|
89
|
-
|
90
|
-
// svelte-ignore non_reactive_update
|
91
|
-
let draggableElement;
|
92
|
-
|
93
|
-
let dragTimeout = null;
|
94
|
-
let currentState = $state(IDLE);
|
95
|
-
|
96
|
-
// Custom preview follower state
|
97
|
-
let showPreview = $state(false);
|
98
|
-
let previewX = $state(0);
|
99
|
-
let previewY = $state(0);
|
100
|
-
let dragOffsetX = $state(0);
|
101
|
-
let dragOffsetY = $state(0);
|
102
|
-
let customPreviewSet = $state(false);
|
103
|
-
let elementRect = $state(null);
|
104
|
-
|
105
|
-
// Track if current draggable can drop in the active zone
|
106
|
-
let canDropInActiveZone = $derived.by(() => {
|
107
|
-
if (currentState !== DRAGGING || !dragState.activeDropZone) return false;
|
108
|
-
|
109
|
-
const activeZone = dragState.dropZones.get(dragState.activeDropZone);
|
110
|
-
return activeZone?.canDrop || false;
|
111
|
-
});
|
112
|
-
|
113
|
-
// Computed state object for CSS classes
|
114
|
-
let stateObject = $derived({
|
115
|
-
idle: currentState === IDLE,
|
116
|
-
dragging: currentState === DRAGGING,
|
117
|
-
'drag-preview': currentState === DRAG_PREVIEW,
|
118
|
-
dropping: currentState === DROPPING,
|
119
|
-
'drag-disabled': disabled || !canDrag(item),
|
120
|
-
'can-drop': currentState === DRAGGING && canDropInActiveZone,
|
121
|
-
'cannot-drop':
|
122
|
-
currentState === DRAGGING &&
|
123
|
-
dragState.activeDropZone &&
|
124
|
-
!canDropInActiveZone
|
125
|
-
});
|
126
|
-
|
127
|
-
let stateClasses = $derived(toStateClasses(stateObject));
|
128
|
-
|
129
|
-
// Update bindable props
|
130
|
-
$effect(() => {
|
131
|
-
isDragging = currentState === DRAGGING;
|
132
|
-
isDropping = currentState === DROPPING;
|
133
|
-
isDragPreview = currentState === DRAG_PREVIEW;
|
134
|
-
});
|
135
|
-
|
136
|
-
// Clean up on component destroy
|
137
|
-
onDestroy(() => {
|
138
|
-
if (showPreview) {
|
139
|
-
document.removeEventListener('dragover', handleDocumentDragOver);
|
140
|
-
}
|
141
|
-
});
|
142
|
-
|
143
|
-
/**
|
144
|
-
* Handle document level dragover to ensure we get position updates
|
145
|
-
* @param {DragEvent} event
|
146
|
-
*/
|
147
|
-
function handleDocumentDragOver(event) {
|
148
|
-
if (showPreview && currentState === DRAGGING) {
|
149
|
-
// Update position for the custom preview
|
150
|
-
previewX = event.clientX - dragOffsetX;
|
151
|
-
previewY = event.clientY - dragOffsetY;
|
152
|
-
|
153
|
-
// Prevent default to allow drop
|
154
|
-
event.preventDefault();
|
155
|
-
}
|
156
|
-
}
|
157
|
-
|
158
|
-
/**
|
159
|
-
* Handle drag start
|
160
|
-
* @param {DragEvent} event
|
161
|
-
*/
|
162
|
-
function handleDragStart(event) {
|
163
|
-
if (disabled || !canDrag(item)) {
|
164
|
-
event.preventDefault();
|
165
|
-
return;
|
166
|
-
}
|
167
|
-
|
168
|
-
// Handle drag delay
|
169
|
-
if (dragDelay > 0) {
|
170
|
-
event.preventDefault();
|
171
|
-
currentState = DRAG_PREVIEW;
|
172
|
-
|
173
|
-
dragTimeout = setTimeout(() => {
|
174
|
-
currentState = DRAGGING;
|
175
|
-
startDrag(event);
|
176
|
-
}, dragDelay);
|
177
|
-
return;
|
178
|
-
}
|
179
|
-
|
180
|
-
currentState = DRAGGING;
|
181
|
-
startDrag(event);
|
182
|
-
}
|
183
|
-
|
184
|
-
let transparentPixel;
|
185
|
-
|
186
|
-
if (browser) {
|
187
|
-
transparentPixel = new Image();
|
188
|
-
transparentPixel.src =
|
189
|
-
'data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7';
|
190
|
-
}
|
191
|
-
|
192
|
-
/**
|
193
|
-
* Start the drag operation
|
194
|
-
* @param {DragEvent} event - The drag event
|
195
|
-
*/
|
196
|
-
function startDrag(event) {
|
197
|
-
// Set a transparent 1x1 pixel image to hide browser's default preview
|
198
|
-
event.dataTransfer.setDragImage(transparentPixel, 0, 0);
|
199
|
-
|
200
|
-
// Get the element's bounding rectangle
|
201
|
-
const rect = draggableElement.getBoundingClientRect();
|
202
|
-
|
203
|
-
// Calculate grab offsets - this is where the user grabbed the element
|
204
|
-
dragOffsetX = event.clientX - rect.left;
|
205
|
-
dragOffsetY = event.clientY - rect.top;
|
206
|
-
|
207
|
-
// Create drag data with draggableId included
|
208
|
-
const dragData = {
|
209
|
-
draggableId,
|
210
|
-
offsetX: dragOffsetX,
|
211
|
-
offsetY: dragOffsetY,
|
212
|
-
item,
|
213
|
-
source,
|
214
|
-
group
|
215
|
-
};
|
216
|
-
|
217
|
-
// console.debug('handleDragStart:', draggableId, dragData);
|
218
|
-
|
219
|
-
// Set shared drag state
|
220
|
-
dragState.start(draggableId, dragData);
|
221
|
-
|
222
|
-
// Set minimal data transfer for browser drag and drop API
|
223
|
-
event.dataTransfer.effectAllowed = 'move';
|
224
|
-
|
225
|
-
// Set draggableId as custom mime type, since that value is availabe
|
226
|
-
// during all event types (dragstart, dragenter, dragover, dragleave,
|
227
|
-
// drop and dragend)
|
228
|
-
event.dataTransfer.setData(`application/x-draggable-${draggableId}`, '1');
|
229
|
-
|
230
|
-
// Also keep text/plain for browser compatibility
|
231
|
-
event.dataTransfer.setData('text/plain', draggableId);
|
232
|
-
|
233
|
-
// Create the preview controller
|
234
|
-
const previewController = new DragController(event);
|
235
|
-
|
236
|
-
// Function to get the preview controller
|
237
|
-
const getController = () => previewController;
|
238
|
-
|
239
|
-
// Call onDragStart with the getController function
|
240
|
-
onDragStart?.({ event, item, source, group, getController });
|
241
|
-
|
242
|
-
// Store rectangle information for the snippet
|
243
|
-
elementRect = rect;
|
244
|
-
|
245
|
-
// These offsets represent where the user grabbed the element relative to its top-left corner
|
246
|
-
dragOffsetX = event.clientX - rect.left;
|
247
|
-
dragOffsetY = event.clientY - rect.top;
|
248
|
-
|
249
|
-
// Set initial position - this places the preview at the element's original position
|
250
|
-
previewX = rect.left;
|
251
|
-
previewY = rect.top;
|
252
|
-
|
253
|
-
// Add document level event listener to track mouse movement
|
254
|
-
document.addEventListener('dragover', handleDocumentDragOver);
|
255
|
-
|
256
|
-
// Show custom preview
|
257
|
-
showPreview = true;
|
258
|
-
customPreviewSet = true;
|
259
|
-
}
|
260
|
-
|
261
|
-
/**
|
262
|
-
* Handle during drag
|
263
|
-
* @param {DragEvent} event
|
264
|
-
*/
|
265
|
-
function handleDrag(event) {
|
266
|
-
if (currentState === DRAGGING) {
|
267
|
-
onDragging?.({ event, item });
|
268
|
-
}
|
269
|
-
}
|
270
|
-
|
271
|
-
/**
|
272
|
-
* Handle drag end
|
273
|
-
* @param {DragEvent} event
|
274
|
-
*/
|
275
|
-
function handleDragEnd(event) {
|
276
|
-
clearTimeout(dragTimeout);
|
277
|
-
|
278
|
-
// Clear global drag state
|
279
|
-
dragState.end(draggableId);
|
280
|
-
|
281
|
-
// Clean up document event listener
|
282
|
-
if (customPreviewSet) {
|
283
|
-
document.removeEventListener('dragover', handleDocumentDragOver);
|
284
|
-
showPreview = false;
|
285
|
-
customPreviewSet = false;
|
286
|
-
elementRect = null;
|
287
|
-
}
|
288
|
-
|
289
|
-
// Check if drop was successful
|
290
|
-
const wasDropped = event.dataTransfer.dropEffect !== 'none';
|
291
|
-
|
292
|
-
if (wasDropped) {
|
293
|
-
currentState = DROPPING;
|
294
|
-
onDrop?.({ event, item, wasDropped: true });
|
295
|
-
|
296
|
-
// Brief dropping state before returning to idle
|
297
|
-
setTimeout(() => {
|
298
|
-
currentState = IDLE;
|
299
|
-
}, 100);
|
300
|
-
} else {
|
301
|
-
currentState = IDLE;
|
302
|
-
}
|
303
|
-
|
304
|
-
onDragEnd?.({ event, item, wasDropped });
|
305
|
-
}
|
306
|
-
|
307
|
-
/**
|
308
|
-
* Handle mouse down for drag delay
|
309
|
-
* @param {MouseEvent} event
|
310
|
-
*/
|
311
|
-
function handleMouseDown(event) {
|
312
|
-
if (dragDelay > 0 && !disabled && canDrag(item)) {
|
313
|
-
// Could add visual feedback here
|
314
|
-
}
|
315
|
-
}
|
316
|
-
|
317
|
-
/**
|
318
|
-
* Handle mouse up to cancel drag delay
|
319
|
-
* @param {MouseEvent} event
|
320
|
-
*/
|
321
|
-
function handleMouseUp(event) {
|
322
|
-
if (dragTimeout) {
|
323
|
-
clearTimeout(dragTimeout);
|
324
|
-
currentState = IDLE;
|
325
|
-
}
|
326
|
-
}
|
327
|
-
|
328
|
-
// Add these variables for touch handling
|
329
|
-
let touchDragging = $state(false);
|
330
|
-
let touchStartX = 0;
|
331
|
-
let touchStartY = 0;
|
332
|
-
let touchPreviewElement = null;
|
333
|
-
|
334
|
-
/**
|
335
|
-
* Handle touch start
|
336
|
-
* @param {TouchEvent} event
|
337
|
-
*/
|
338
|
-
function handleTouchStart(event) {
|
339
|
-
if (disabled || !canDrag(item)) return;
|
340
|
-
|
341
|
-
const touch = event.touches[0];
|
342
|
-
touchStartX = touch.clientX;
|
343
|
-
touchStartY = touch.clientY;
|
344
|
-
|
345
|
-
// Start drag after a small delay to distinguish from scrolling
|
346
|
-
dragTimeout = setTimeout(() => {
|
347
|
-
touchDragging = true;
|
348
|
-
currentState = DRAGGING;
|
349
|
-
|
350
|
-
// Create drag data
|
351
|
-
const rect = draggableElement.getBoundingClientRect();
|
352
|
-
dragOffsetX = touch.clientX - rect.left;
|
353
|
-
dragOffsetY = touch.clientY - rect.top;
|
354
|
-
|
355
|
-
const dragData = {
|
356
|
-
draggableId,
|
357
|
-
offsetX: dragOffsetX,
|
358
|
-
offsetY: dragOffsetY,
|
359
|
-
item,
|
360
|
-
source,
|
361
|
-
group
|
362
|
-
};
|
363
|
-
|
364
|
-
dragState.start(draggableId, dragData);
|
365
|
-
|
366
|
-
// Show preview
|
367
|
-
// if (draggingSnippet) {
|
368
|
-
elementRect = rect;
|
369
|
-
previewX = rect.left;
|
370
|
-
previewY = rect.top;
|
371
|
-
showPreview = true;
|
372
|
-
// }
|
373
|
-
|
374
|
-
// Prevent scrolling while dragging
|
375
|
-
event.preventDefault();
|
376
|
-
|
377
|
-
// Add document-level touch handlers
|
378
|
-
document.addEventListener('touchmove', handleTouchMove, {
|
379
|
-
passive: false
|
380
|
-
});
|
381
|
-
|
382
|
-
document.addEventListener('touchend', handleTouchEnd);
|
383
|
-
}, 150); // 150ms delay to distinguish from scrolling
|
384
|
-
}
|
385
|
-
|
386
|
-
/**
|
387
|
-
* Handle touch move
|
388
|
-
* @param {TouchEvent} event
|
389
|
-
*/
|
390
|
-
function handleTouchMove(event) {
|
391
|
-
if (!touchDragging) return;
|
392
|
-
|
393
|
-
event.preventDefault();
|
394
|
-
const touch = event.touches[0];
|
395
|
-
|
396
|
-
// Update preview position
|
397
|
-
if (showPreview) {
|
398
|
-
previewX = touch.clientX - dragOffsetX;
|
399
|
-
previewY = touch.clientY - dragOffsetY;
|
400
|
-
}
|
401
|
-
|
402
|
-
/** @type {SimulatedDragEvent} */
|
403
|
-
const simulatedEvent = {
|
404
|
-
type: 'dragover',
|
405
|
-
clientX: touch.clientX,
|
406
|
-
clientY: touch.clientY,
|
407
|
-
dataTransfer: {
|
408
|
-
types: [`application/x-draggable-${draggableId}`, 'text/plain'],
|
409
|
-
getData: () => 1,
|
410
|
-
dropEffect: 'move',
|
411
|
-
effectAllowed: 'move',
|
412
|
-
files: []
|
413
|
-
},
|
414
|
-
|
415
|
-
preventDefault: () => {},
|
416
|
-
stopPropagation: () => {}
|
417
|
-
};
|
418
|
-
|
419
|
-
// Update active dropzone in drag state
|
420
|
-
dragState.updateActiveDropZone(
|
421
|
-
touch.clientX,
|
422
|
-
touch.clientY,
|
423
|
-
simulatedEvent
|
424
|
-
);
|
425
|
-
}
|
426
|
-
|
427
|
-
/**
|
428
|
-
* Handle touch end
|
429
|
-
* @param {TouchEvent} event
|
430
|
-
*/
|
431
|
-
function handleTouchEnd(event) {
|
432
|
-
clearTimeout(dragTimeout);
|
433
|
-
|
434
|
-
if (!touchDragging) return;
|
435
|
-
|
436
|
-
const touch = event.changedTouches[0];
|
437
|
-
|
438
|
-
/** @type {SimulatedDragEvent} */
|
439
|
-
const simulatedEvent = {
|
440
|
-
type: 'drop',
|
441
|
-
clientX: touch.clientX,
|
442
|
-
clientY: touch.clientY,
|
443
|
-
dataTransfer: {
|
444
|
-
types: [`application/x-draggable-${draggableId}`, 'text/plain'],
|
445
|
-
getData: () => 1,
|
446
|
-
dropEffect: 'move',
|
447
|
-
effectAllowed: 'move',
|
448
|
-
files: []
|
449
|
-
},
|
450
|
-
preventDefault: () => {}, // Add this!
|
451
|
-
stopPropagation: () => {} // And this!
|
452
|
-
};
|
453
|
-
|
454
|
-
// Trigger drop at final touch position
|
455
|
-
dragState.handleDropAtPoint(touch.clientX, touch.clientY, simulatedEvent);
|
456
|
-
|
457
|
-
// Clean up
|
458
|
-
touchDragging = false;
|
459
|
-
currentState = IDLE;
|
460
|
-
showPreview = false;
|
461
|
-
dragState.end(draggableId);
|
462
|
-
|
463
|
-
// Remove document handlers
|
464
|
-
document.removeEventListener('touchmove', handleTouchMove);
|
465
|
-
document.removeEventListener('touchend', handleTouchEnd);
|
466
|
-
}
|
467
|
-
</script>
|
468
|
-
|
469
|
-
<div
|
470
|
-
data-component="draggable"
|
471
|
-
bind:this={draggableElement}
|
472
|
-
draggable={!disabled && canDrag(item)}
|
473
|
-
ondragstart={handleDragStart}
|
474
|
-
ondrag={handleDrag}
|
475
|
-
ondragend={handleDragEnd}
|
476
|
-
onmousedown={handleMouseDown}
|
477
|
-
onmouseup={handleMouseUp}
|
478
|
-
ontouchstart={handleTouchStart}
|
479
|
-
class="{base} {classes} {stateClasses}"
|
480
|
-
style="touch-action: none;"
|
481
|
-
{...attrs}
|
482
|
-
>
|
483
|
-
{@render children({
|
484
|
-
element: draggableElement,
|
485
|
-
rect: elementRect,
|
486
|
-
isDragging: false
|
487
|
-
})}
|
488
|
-
</div>
|
489
|
-
|
490
|
-
{#if showPreview && elementRect}
|
491
|
-
<div
|
492
|
-
data-companion="drag-preview-follower"
|
493
|
-
class={stateClasses}
|
494
|
-
style="position: fixed; z-index: 9999; pointer-events: none;"
|
495
|
-
style:left="{previewX}px"
|
496
|
-
style:top="{previewY}px"
|
497
|
-
>
|
498
|
-
{#if draggingSnippet}
|
499
|
-
{@render draggingSnippet({
|
500
|
-
element: draggableElement,
|
501
|
-
rect: elementRect
|
502
|
-
})}
|
503
|
-
{:else}
|
504
|
-
{@render children({
|
505
|
-
element: draggableElement,
|
506
|
-
rect: elementRect,
|
507
|
-
isDragging: true
|
508
|
-
})}
|
509
|
-
{/if}
|
510
|
-
</div>
|
511
|
-
{/if}
|
512
|
-
|
513
|
-
<style>
|
514
|
-
[data-component='draggable'] {
|
515
|
-
-webkit-touch-callout: none;
|
516
|
-
-webkit-user-select: none;
|
517
|
-
user-select: none;
|
518
|
-
}
|
519
|
-
</style>
|
1
|
+
<script>
|
2
|
+
import { browser } from '$app/environment';
|
3
|
+
|
4
|
+
import { toStateClasses } from '../../util/design-system/index.js';
|
5
|
+
import { createOrGetDragState } from './drag-state.svelte.js';
|
6
|
+
import { DragController } from './DragController.js';
|
7
|
+
import { onDestroy } from 'svelte';
|
8
|
+
import {
|
9
|
+
IDLE,
|
10
|
+
DRAGGING,
|
11
|
+
DRAG_PREVIEW,
|
12
|
+
DROPPING
|
13
|
+
} from '../../constants/state-labels/drag-states.js';
|
14
|
+
|
15
|
+
/** @typedef {import('../../typedef').SimulatedDragEvent} SimulatedDragEvent */
|
16
|
+
|
17
|
+
/**
|
18
|
+
* @type {{
|
19
|
+
* item: any,
|
20
|
+
* group?: string,
|
21
|
+
* source?: string,
|
22
|
+
* disabled?: boolean,
|
23
|
+
* dragDelay?: number,
|
24
|
+
* base?: string,
|
25
|
+
* classes?: string,
|
26
|
+
* children: import('svelte').Snippet<[{
|
27
|
+
* element: HTMLElement,
|
28
|
+
* rect: DOMRect,
|
29
|
+
* isDragging: boolean
|
30
|
+
* }]>,
|
31
|
+
* draggingSnippet?: import('svelte').Snippet<[{
|
32
|
+
* element: HTMLElement,
|
33
|
+
* rect: DOMRect
|
34
|
+
* }]>,
|
35
|
+
* contextKey?: import('../../typedef').ContextKey,
|
36
|
+
* isDragging?: boolean,
|
37
|
+
* isDropping?: boolean,
|
38
|
+
* isDragPreview?: boolean,
|
39
|
+
* onDragStart?: (detail: {
|
40
|
+
* event: DragEvent,
|
41
|
+
* item: any,
|
42
|
+
* source: string,
|
43
|
+
* group: string,
|
44
|
+
* getController: () => DragController
|
45
|
+
* }) => void,
|
46
|
+
* onDragging?: (detail: {
|
47
|
+
* event: DragEvent,
|
48
|
+
* item: any
|
49
|
+
* }) => void,
|
50
|
+
* onDragEnd?: (detail: {
|
51
|
+
* event: DragEvent,
|
52
|
+
* item: any,
|
53
|
+
* wasDropped: boolean
|
54
|
+
* }) => void,
|
55
|
+
* onDrop?: (detail: {
|
56
|
+
* event: DragEvent,
|
57
|
+
* item: any,
|
58
|
+
* wasDropped: boolean
|
59
|
+
* }) => void,
|
60
|
+
* canDrag?: (item: any) => boolean,
|
61
|
+
* [key: string]: any
|
62
|
+
* }}
|
63
|
+
*/
|
64
|
+
let {
|
65
|
+
item,
|
66
|
+
group = 'default',
|
67
|
+
source = 'default',
|
68
|
+
disabled = false,
|
69
|
+
dragDelay = 0,
|
70
|
+
base = '',
|
71
|
+
classes = '',
|
72
|
+
children,
|
73
|
+
draggingSnippet,
|
74
|
+
contextKey,
|
75
|
+
isDragging = $bindable(false),
|
76
|
+
isDropping = $bindable(false),
|
77
|
+
isDragPreview = $bindable(false),
|
78
|
+
onDragStart,
|
79
|
+
onDragging,
|
80
|
+
onDragEnd,
|
81
|
+
onDrop,
|
82
|
+
canDrag = () => true,
|
83
|
+
...attrs
|
84
|
+
} = $props();
|
85
|
+
|
86
|
+
const dragState = createOrGetDragState(contextKey);
|
87
|
+
|
88
|
+
const draggableId = dragState.newDraggableId();
|
89
|
+
|
90
|
+
// svelte-ignore non_reactive_update
|
91
|
+
let draggableElement;
|
92
|
+
|
93
|
+
let dragTimeout = null;
|
94
|
+
let currentState = $state(IDLE);
|
95
|
+
|
96
|
+
// Custom preview follower state
|
97
|
+
let showPreview = $state(false);
|
98
|
+
let previewX = $state(0);
|
99
|
+
let previewY = $state(0);
|
100
|
+
let dragOffsetX = $state(0);
|
101
|
+
let dragOffsetY = $state(0);
|
102
|
+
let customPreviewSet = $state(false);
|
103
|
+
let elementRect = $state(null);
|
104
|
+
|
105
|
+
// Track if current draggable can drop in the active zone
|
106
|
+
let canDropInActiveZone = $derived.by(() => {
|
107
|
+
if (currentState !== DRAGGING || !dragState.activeDropZone) return false;
|
108
|
+
|
109
|
+
const activeZone = dragState.dropZones.get(dragState.activeDropZone);
|
110
|
+
return activeZone?.canDrop || false;
|
111
|
+
});
|
112
|
+
|
113
|
+
// Computed state object for CSS classes
|
114
|
+
let stateObject = $derived({
|
115
|
+
idle: currentState === IDLE,
|
116
|
+
dragging: currentState === DRAGGING,
|
117
|
+
'drag-preview': currentState === DRAG_PREVIEW,
|
118
|
+
dropping: currentState === DROPPING,
|
119
|
+
'drag-disabled': disabled || !canDrag(item),
|
120
|
+
'can-drop': currentState === DRAGGING && canDropInActiveZone,
|
121
|
+
'cannot-drop':
|
122
|
+
currentState === DRAGGING &&
|
123
|
+
dragState.activeDropZone &&
|
124
|
+
!canDropInActiveZone
|
125
|
+
});
|
126
|
+
|
127
|
+
let stateClasses = $derived(toStateClasses(stateObject));
|
128
|
+
|
129
|
+
// Update bindable props
|
130
|
+
$effect(() => {
|
131
|
+
isDragging = currentState === DRAGGING;
|
132
|
+
isDropping = currentState === DROPPING;
|
133
|
+
isDragPreview = currentState === DRAG_PREVIEW;
|
134
|
+
});
|
135
|
+
|
136
|
+
// Clean up on component destroy
|
137
|
+
onDestroy(() => {
|
138
|
+
if (showPreview) {
|
139
|
+
document.removeEventListener('dragover', handleDocumentDragOver);
|
140
|
+
}
|
141
|
+
});
|
142
|
+
|
143
|
+
/**
|
144
|
+
* Handle document level dragover to ensure we get position updates
|
145
|
+
* @param {DragEvent} event
|
146
|
+
*/
|
147
|
+
function handleDocumentDragOver(event) {
|
148
|
+
if (showPreview && currentState === DRAGGING) {
|
149
|
+
// Update position for the custom preview
|
150
|
+
previewX = event.clientX - dragOffsetX;
|
151
|
+
previewY = event.clientY - dragOffsetY;
|
152
|
+
|
153
|
+
// Prevent default to allow drop
|
154
|
+
event.preventDefault();
|
155
|
+
}
|
156
|
+
}
|
157
|
+
|
158
|
+
/**
|
159
|
+
* Handle drag start
|
160
|
+
* @param {DragEvent} event
|
161
|
+
*/
|
162
|
+
function handleDragStart(event) {
|
163
|
+
if (disabled || !canDrag(item)) {
|
164
|
+
event.preventDefault();
|
165
|
+
return;
|
166
|
+
}
|
167
|
+
|
168
|
+
// Handle drag delay
|
169
|
+
if (dragDelay > 0) {
|
170
|
+
event.preventDefault();
|
171
|
+
currentState = DRAG_PREVIEW;
|
172
|
+
|
173
|
+
dragTimeout = setTimeout(() => {
|
174
|
+
currentState = DRAGGING;
|
175
|
+
startDrag(event);
|
176
|
+
}, dragDelay);
|
177
|
+
return;
|
178
|
+
}
|
179
|
+
|
180
|
+
currentState = DRAGGING;
|
181
|
+
startDrag(event);
|
182
|
+
}
|
183
|
+
|
184
|
+
let transparentPixel;
|
185
|
+
|
186
|
+
if (browser) {
|
187
|
+
transparentPixel = new Image();
|
188
|
+
transparentPixel.src =
|
189
|
+
'data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7';
|
190
|
+
}
|
191
|
+
|
192
|
+
/**
|
193
|
+
* Start the drag operation
|
194
|
+
* @param {DragEvent} event - The drag event
|
195
|
+
*/
|
196
|
+
function startDrag(event) {
|
197
|
+
// Set a transparent 1x1 pixel image to hide browser's default preview
|
198
|
+
event.dataTransfer.setDragImage(transparentPixel, 0, 0);
|
199
|
+
|
200
|
+
// Get the element's bounding rectangle
|
201
|
+
const rect = draggableElement.getBoundingClientRect();
|
202
|
+
|
203
|
+
// Calculate grab offsets - this is where the user grabbed the element
|
204
|
+
dragOffsetX = event.clientX - rect.left;
|
205
|
+
dragOffsetY = event.clientY - rect.top;
|
206
|
+
|
207
|
+
// Create drag data with draggableId included
|
208
|
+
const dragData = {
|
209
|
+
draggableId,
|
210
|
+
offsetX: dragOffsetX,
|
211
|
+
offsetY: dragOffsetY,
|
212
|
+
item,
|
213
|
+
source,
|
214
|
+
group
|
215
|
+
};
|
216
|
+
|
217
|
+
// console.debug('handleDragStart:', draggableId, dragData);
|
218
|
+
|
219
|
+
// Set shared drag state
|
220
|
+
dragState.start(draggableId, dragData);
|
221
|
+
|
222
|
+
// Set minimal data transfer for browser drag and drop API
|
223
|
+
event.dataTransfer.effectAllowed = 'move';
|
224
|
+
|
225
|
+
// Set draggableId as custom mime type, since that value is availabe
|
226
|
+
// during all event types (dragstart, dragenter, dragover, dragleave,
|
227
|
+
// drop and dragend)
|
228
|
+
event.dataTransfer.setData(`application/x-draggable-${draggableId}`, '1');
|
229
|
+
|
230
|
+
// Also keep text/plain for browser compatibility
|
231
|
+
event.dataTransfer.setData('text/plain', draggableId);
|
232
|
+
|
233
|
+
// Create the preview controller
|
234
|
+
const previewController = new DragController(event);
|
235
|
+
|
236
|
+
// Function to get the preview controller
|
237
|
+
const getController = () => previewController;
|
238
|
+
|
239
|
+
// Call onDragStart with the getController function
|
240
|
+
onDragStart?.({ event, item, source, group, getController });
|
241
|
+
|
242
|
+
// Store rectangle information for the snippet
|
243
|
+
elementRect = rect;
|
244
|
+
|
245
|
+
// These offsets represent where the user grabbed the element relative to its top-left corner
|
246
|
+
dragOffsetX = event.clientX - rect.left;
|
247
|
+
dragOffsetY = event.clientY - rect.top;
|
248
|
+
|
249
|
+
// Set initial position - this places the preview at the element's original position
|
250
|
+
previewX = rect.left;
|
251
|
+
previewY = rect.top;
|
252
|
+
|
253
|
+
// Add document level event listener to track mouse movement
|
254
|
+
document.addEventListener('dragover', handleDocumentDragOver);
|
255
|
+
|
256
|
+
// Show custom preview
|
257
|
+
showPreview = true;
|
258
|
+
customPreviewSet = true;
|
259
|
+
}
|
260
|
+
|
261
|
+
/**
|
262
|
+
* Handle during drag
|
263
|
+
* @param {DragEvent} event
|
264
|
+
*/
|
265
|
+
function handleDrag(event) {
|
266
|
+
if (currentState === DRAGGING) {
|
267
|
+
onDragging?.({ event, item });
|
268
|
+
}
|
269
|
+
}
|
270
|
+
|
271
|
+
/**
|
272
|
+
* Handle drag end
|
273
|
+
* @param {DragEvent} event
|
274
|
+
*/
|
275
|
+
function handleDragEnd(event) {
|
276
|
+
clearTimeout(dragTimeout);
|
277
|
+
|
278
|
+
// Clear global drag state
|
279
|
+
dragState.end(draggableId);
|
280
|
+
|
281
|
+
// Clean up document event listener
|
282
|
+
if (customPreviewSet) {
|
283
|
+
document.removeEventListener('dragover', handleDocumentDragOver);
|
284
|
+
showPreview = false;
|
285
|
+
customPreviewSet = false;
|
286
|
+
elementRect = null;
|
287
|
+
}
|
288
|
+
|
289
|
+
// Check if drop was successful
|
290
|
+
const wasDropped = event.dataTransfer.dropEffect !== 'none';
|
291
|
+
|
292
|
+
if (wasDropped) {
|
293
|
+
currentState = DROPPING;
|
294
|
+
onDrop?.({ event, item, wasDropped: true });
|
295
|
+
|
296
|
+
// Brief dropping state before returning to idle
|
297
|
+
setTimeout(() => {
|
298
|
+
currentState = IDLE;
|
299
|
+
}, 100);
|
300
|
+
} else {
|
301
|
+
currentState = IDLE;
|
302
|
+
}
|
303
|
+
|
304
|
+
onDragEnd?.({ event, item, wasDropped });
|
305
|
+
}
|
306
|
+
|
307
|
+
/**
|
308
|
+
* Handle mouse down for drag delay
|
309
|
+
* @param {MouseEvent} event
|
310
|
+
*/
|
311
|
+
function handleMouseDown(event) {
|
312
|
+
if (dragDelay > 0 && !disabled && canDrag(item)) {
|
313
|
+
// Could add visual feedback here
|
314
|
+
}
|
315
|
+
}
|
316
|
+
|
317
|
+
/**
|
318
|
+
* Handle mouse up to cancel drag delay
|
319
|
+
* @param {MouseEvent} event
|
320
|
+
*/
|
321
|
+
function handleMouseUp(event) {
|
322
|
+
if (dragTimeout) {
|
323
|
+
clearTimeout(dragTimeout);
|
324
|
+
currentState = IDLE;
|
325
|
+
}
|
326
|
+
}
|
327
|
+
|
328
|
+
// Add these variables for touch handling
|
329
|
+
let touchDragging = $state(false);
|
330
|
+
let touchStartX = 0;
|
331
|
+
let touchStartY = 0;
|
332
|
+
let touchPreviewElement = null;
|
333
|
+
|
334
|
+
/**
|
335
|
+
* Handle touch start
|
336
|
+
* @param {TouchEvent} event
|
337
|
+
*/
|
338
|
+
function handleTouchStart(event) {
|
339
|
+
if (disabled || !canDrag(item)) return;
|
340
|
+
|
341
|
+
const touch = event.touches[0];
|
342
|
+
touchStartX = touch.clientX;
|
343
|
+
touchStartY = touch.clientY;
|
344
|
+
|
345
|
+
// Start drag after a small delay to distinguish from scrolling
|
346
|
+
dragTimeout = setTimeout(() => {
|
347
|
+
touchDragging = true;
|
348
|
+
currentState = DRAGGING;
|
349
|
+
|
350
|
+
// Create drag data
|
351
|
+
const rect = draggableElement.getBoundingClientRect();
|
352
|
+
dragOffsetX = touch.clientX - rect.left;
|
353
|
+
dragOffsetY = touch.clientY - rect.top;
|
354
|
+
|
355
|
+
const dragData = {
|
356
|
+
draggableId,
|
357
|
+
offsetX: dragOffsetX,
|
358
|
+
offsetY: dragOffsetY,
|
359
|
+
item,
|
360
|
+
source,
|
361
|
+
group
|
362
|
+
};
|
363
|
+
|
364
|
+
dragState.start(draggableId, dragData);
|
365
|
+
|
366
|
+
// Show preview
|
367
|
+
// if (draggingSnippet) {
|
368
|
+
elementRect = rect;
|
369
|
+
previewX = rect.left;
|
370
|
+
previewY = rect.top;
|
371
|
+
showPreview = true;
|
372
|
+
// }
|
373
|
+
|
374
|
+
// Prevent scrolling while dragging
|
375
|
+
event.preventDefault();
|
376
|
+
|
377
|
+
// Add document-level touch handlers
|
378
|
+
document.addEventListener('touchmove', handleTouchMove, {
|
379
|
+
passive: false
|
380
|
+
});
|
381
|
+
|
382
|
+
document.addEventListener('touchend', handleTouchEnd);
|
383
|
+
}, 150); // 150ms delay to distinguish from scrolling
|
384
|
+
}
|
385
|
+
|
386
|
+
/**
|
387
|
+
* Handle touch move
|
388
|
+
* @param {TouchEvent} event
|
389
|
+
*/
|
390
|
+
function handleTouchMove(event) {
|
391
|
+
if (!touchDragging) return;
|
392
|
+
|
393
|
+
event.preventDefault();
|
394
|
+
const touch = event.touches[0];
|
395
|
+
|
396
|
+
// Update preview position
|
397
|
+
if (showPreview) {
|
398
|
+
previewX = touch.clientX - dragOffsetX;
|
399
|
+
previewY = touch.clientY - dragOffsetY;
|
400
|
+
}
|
401
|
+
|
402
|
+
/** @type {SimulatedDragEvent} */
|
403
|
+
const simulatedEvent = {
|
404
|
+
type: 'dragover',
|
405
|
+
clientX: touch.clientX,
|
406
|
+
clientY: touch.clientY,
|
407
|
+
dataTransfer: {
|
408
|
+
types: [`application/x-draggable-${draggableId}`, 'text/plain'],
|
409
|
+
getData: () => 1,
|
410
|
+
dropEffect: 'move',
|
411
|
+
effectAllowed: 'move',
|
412
|
+
files: []
|
413
|
+
},
|
414
|
+
|
415
|
+
preventDefault: () => {},
|
416
|
+
stopPropagation: () => {}
|
417
|
+
};
|
418
|
+
|
419
|
+
// Update active dropzone in drag state
|
420
|
+
dragState.updateActiveDropZone(
|
421
|
+
touch.clientX,
|
422
|
+
touch.clientY,
|
423
|
+
simulatedEvent
|
424
|
+
);
|
425
|
+
}
|
426
|
+
|
427
|
+
/**
|
428
|
+
* Handle touch end
|
429
|
+
* @param {TouchEvent} event
|
430
|
+
*/
|
431
|
+
function handleTouchEnd(event) {
|
432
|
+
clearTimeout(dragTimeout);
|
433
|
+
|
434
|
+
if (!touchDragging) return;
|
435
|
+
|
436
|
+
const touch = event.changedTouches[0];
|
437
|
+
|
438
|
+
/** @type {SimulatedDragEvent} */
|
439
|
+
const simulatedEvent = {
|
440
|
+
type: 'drop',
|
441
|
+
clientX: touch.clientX,
|
442
|
+
clientY: touch.clientY,
|
443
|
+
dataTransfer: {
|
444
|
+
types: [`application/x-draggable-${draggableId}`, 'text/plain'],
|
445
|
+
getData: () => 1,
|
446
|
+
dropEffect: 'move',
|
447
|
+
effectAllowed: 'move',
|
448
|
+
files: []
|
449
|
+
},
|
450
|
+
preventDefault: () => {}, // Add this!
|
451
|
+
stopPropagation: () => {} // And this!
|
452
|
+
};
|
453
|
+
|
454
|
+
// Trigger drop at final touch position
|
455
|
+
dragState.handleDropAtPoint(touch.clientX, touch.clientY, simulatedEvent);
|
456
|
+
|
457
|
+
// Clean up
|
458
|
+
touchDragging = false;
|
459
|
+
currentState = IDLE;
|
460
|
+
showPreview = false;
|
461
|
+
dragState.end(draggableId);
|
462
|
+
|
463
|
+
// Remove document handlers
|
464
|
+
document.removeEventListener('touchmove', handleTouchMove);
|
465
|
+
document.removeEventListener('touchend', handleTouchEnd);
|
466
|
+
}
|
467
|
+
</script>
|
468
|
+
|
469
|
+
<div
|
470
|
+
data-component="draggable"
|
471
|
+
bind:this={draggableElement}
|
472
|
+
draggable={!disabled && canDrag(item)}
|
473
|
+
ondragstart={handleDragStart}
|
474
|
+
ondrag={handleDrag}
|
475
|
+
ondragend={handleDragEnd}
|
476
|
+
onmousedown={handleMouseDown}
|
477
|
+
onmouseup={handleMouseUp}
|
478
|
+
ontouchstart={handleTouchStart}
|
479
|
+
class="{base} {classes} {stateClasses}"
|
480
|
+
style="touch-action: none;"
|
481
|
+
{...attrs}
|
482
|
+
>
|
483
|
+
{@render children({
|
484
|
+
element: draggableElement,
|
485
|
+
rect: elementRect,
|
486
|
+
isDragging: false
|
487
|
+
})}
|
488
|
+
</div>
|
489
|
+
|
490
|
+
{#if showPreview && elementRect}
|
491
|
+
<div
|
492
|
+
data-companion="drag-preview-follower"
|
493
|
+
class={stateClasses}
|
494
|
+
style="position: fixed; z-index: 9999; pointer-events: none;"
|
495
|
+
style:left="{previewX}px"
|
496
|
+
style:top="{previewY}px"
|
497
|
+
>
|
498
|
+
{#if draggingSnippet}
|
499
|
+
{@render draggingSnippet({
|
500
|
+
element: draggableElement,
|
501
|
+
rect: elementRect
|
502
|
+
})}
|
503
|
+
{:else}
|
504
|
+
{@render children({
|
505
|
+
element: draggableElement,
|
506
|
+
rect: elementRect,
|
507
|
+
isDragging: true
|
508
|
+
})}
|
509
|
+
{/if}
|
510
|
+
</div>
|
511
|
+
{/if}
|
512
|
+
|
513
|
+
<style>
|
514
|
+
[data-component='draggable'] {
|
515
|
+
-webkit-touch-callout: none;
|
516
|
+
-webkit-user-select: none;
|
517
|
+
user-select: none;
|
518
|
+
}
|
519
|
+
</style>
|