ui-svelte 0.2.12 → 0.2.14
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 +2 -2
- package/dist/charts/ArcChart.svelte +0 -1
- package/dist/control/Fab.svelte +103 -0
- package/dist/control/Fab.svelte.d.ts +25 -0
- package/dist/control/Record.svelte +0 -3
- package/dist/control/ToggleTheme.svelte +1 -1
- package/dist/control/Video.svelte +2 -0
- package/dist/control/css/btn.css +17 -17
- package/dist/control/css/fab.css +84 -0
- package/dist/control/css/media.css +7 -7
- package/dist/control/css/toggle-group.css +1 -17
- package/dist/css/decorations.css +348 -189
- package/dist/css/utilities.css +0 -4
- package/dist/display/Accordion.svelte +3 -3
- package/dist/display/Accordion.svelte.d.ts +1 -1
- package/dist/display/Card.svelte +3 -3
- package/dist/display/Card.svelte.d.ts +1 -1
- package/dist/display/Code.svelte +1 -1
- package/dist/display/Collapsible.svelte +3 -3
- package/dist/display/Collapsible.svelte.d.ts +1 -1
- package/dist/display/Countdown.svelte +169 -0
- package/dist/display/Countdown.svelte.d.ts +21 -0
- package/dist/display/Item.svelte +12 -0
- package/dist/display/Item.svelte.d.ts +2 -0
- package/dist/display/Marquee.svelte +0 -2
- package/dist/display/Section.svelte +3 -3
- package/dist/display/Section.svelte.d.ts +1 -1
- package/dist/display/css/accordion.css +14 -14
- package/dist/display/css/alert.css +42 -15
- package/dist/display/css/avatar.css +36 -36
- package/dist/display/css/card.css +108 -36
- package/dist/display/css/chip.css +16 -16
- package/dist/display/css/collapsible.css +32 -32
- package/dist/display/css/countdown.css +206 -0
- package/dist/display/css/item.css +24 -0
- package/dist/display/css/marquee.css +0 -3
- package/dist/display/css/section.css +88 -109
- package/dist/display/css/table.css +1 -16
- package/dist/form/ColorField.svelte +60 -2
- package/dist/form/DragDrop.svelte +317 -87
- package/dist/form/DragDrop.svelte.d.ts +1 -0
- package/dist/form/Dropzone.svelte +3 -3
- package/dist/form/Dropzone.svelte.d.ts +1 -1
- package/dist/form/ImageCropper.svelte +135 -4
- package/dist/form/RadioGroup.svelte +6 -2
- package/dist/form/RadioGroup.svelte.d.ts +1 -1
- package/dist/form/Slider.svelte +6 -2
- package/dist/form/Slider.svelte.d.ts +1 -1
- package/dist/form/TextField.svelte +13 -4
- package/dist/form/TextField.svelte.d.ts +3 -2
- package/dist/form/Toggle.svelte +6 -2
- package/dist/form/Toggle.svelte.d.ts +1 -1
- package/dist/form/css/control.css +14 -14
- package/dist/form/css/csv-field.css +8 -13
- package/dist/form/css/drag-drop.css +90 -127
- package/dist/form/css/dropzone.css +8 -8
- package/dist/form/css/editor.css +14 -14
- package/dist/form/css/image-cropper.css +28 -7
- package/dist/form/css/radio-group.css +25 -0
- package/dist/form/css/slider.css +36 -0
- package/dist/form/css/textarea.css +14 -14
- package/dist/form/css/toggle.css +12 -0
- package/dist/hooks/use-chat.svelte.js +1 -1
- package/dist/hooks/use-form.svelte.js +3 -3
- package/dist/hooks/use-search.svelte.js +0 -3
- package/dist/icons/index.d.ts +4 -0
- package/dist/icons/index.js +4 -0
- package/dist/index.css +28 -48
- package/dist/index.d.ts +4 -2
- package/dist/index.js +3 -1
- package/dist/layout/Provider.svelte +5 -5
- package/dist/layout/Sidebar.svelte +17 -8
- package/dist/layout/Sidebar.svelte.d.ts +2 -1
- package/dist/layout/css/app-bar.css +8 -7
- package/dist/layout/css/footer.css +7 -7
- package/dist/layout/css/sidebar.css +120 -59
- package/dist/navigation/BottomNav.svelte +23 -14
- package/dist/navigation/css/bottom-nav.css +74 -34
- package/dist/navigation/css/nav-menu.css +14 -15
- package/dist/navigation/css/side-nav.css +14 -15
- package/dist/overlay/AlertDialog.svelte +58 -0
- package/dist/overlay/AlertDialog.svelte.d.ts +14 -25
- package/dist/overlay/Command.svelte +177 -170
- package/dist/overlay/Command.svelte.d.ts +12 -3
- package/dist/overlay/Drawer.svelte +4 -4
- package/dist/overlay/Drawer.svelte.d.ts +1 -1
- package/dist/overlay/Modal.svelte +4 -4
- package/dist/overlay/Modal.svelte.d.ts +1 -1
- package/dist/overlay/Tooltip.svelte +0 -5
- package/dist/overlay/css/command.css +30 -42
- package/dist/overlay/css/drawer.css +10 -10
- package/dist/overlay/css/modal.css +70 -18
- package/dist/overlay/css/toast.css +42 -14
- package/dist/overlay/css/tooltip.css +49 -23
- package/dist/stores/theme.svelte.js +19 -12
- package/package.json +3 -2
- package/dist/utils/charts.d.ts +0 -27
- package/dist/utils/charts.js +0 -140
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
<script lang="ts">
|
|
16
16
|
import { cn } from '../utils/class-names.js';
|
|
17
17
|
import type { Snippet } from 'svelte';
|
|
18
|
+
import { onMount } from 'svelte';
|
|
18
19
|
|
|
19
20
|
type Props = {
|
|
20
21
|
groups?: DragDropGroup[];
|
|
@@ -33,6 +34,7 @@
|
|
|
33
34
|
itemSnippet?: Snippet<[DragDropItem, number]>;
|
|
34
35
|
groupHeaderSnippet?: Snippet<[DragDropGroup, number]>;
|
|
35
36
|
emptySnippet?: Snippet<[DragDropGroup?]>;
|
|
37
|
+
emptyText?: string;
|
|
36
38
|
};
|
|
37
39
|
|
|
38
40
|
let {
|
|
@@ -51,17 +53,31 @@
|
|
|
51
53
|
onGroupReorder,
|
|
52
54
|
itemSnippet,
|
|
53
55
|
groupHeaderSnippet,
|
|
54
|
-
emptySnippet
|
|
56
|
+
emptySnippet,
|
|
57
|
+
emptyText = 'Drop items here'
|
|
55
58
|
}: Props = $props();
|
|
56
59
|
|
|
57
60
|
let draggedItem = $state<DragDropItem | null>(null);
|
|
58
61
|
let draggedFromGroup = $state<string | null>(null);
|
|
59
|
-
let draggedFromIndex = $state
|
|
62
|
+
let draggedFromIndex = $state(-1);
|
|
60
63
|
let dragOverGroup = $state<string | null>(null);
|
|
61
|
-
let dragOverIndex = $state
|
|
64
|
+
let dragOverIndex = $state(-1);
|
|
62
65
|
let isDragging = $state(false);
|
|
63
66
|
|
|
64
|
-
|
|
67
|
+
let containerEl = $state<HTMLElement>();
|
|
68
|
+
let isTouchDragging = $state(false);
|
|
69
|
+
let isTouchPending = $state(false);
|
|
70
|
+
let touchDraggedElement = $state<HTMLElement | null>(null);
|
|
71
|
+
let ghostElement = $state<HTMLElement | null>(null);
|
|
72
|
+
let isTouchDevice = $state(false);
|
|
73
|
+
|
|
74
|
+
let touchStart = { x: 0, y: 0 };
|
|
75
|
+
let touchCurrent = { x: 0, y: 0 };
|
|
76
|
+
let longPressTimer: ReturnType<typeof setTimeout> | null = null;
|
|
77
|
+
const LONG_PRESS_DURATION = 400;
|
|
78
|
+
const SCROLL_THRESHOLD = 8;
|
|
79
|
+
|
|
80
|
+
const colorClass = {
|
|
65
81
|
primary: 'is-primary',
|
|
66
82
|
secondary: 'is-secondary',
|
|
67
83
|
muted: 'is-muted',
|
|
@@ -71,14 +87,14 @@
|
|
|
71
87
|
warning: 'is-warning'
|
|
72
88
|
};
|
|
73
89
|
|
|
74
|
-
const
|
|
90
|
+
const variantClass = {
|
|
75
91
|
soft: 'is-soft',
|
|
76
92
|
solid: 'is-solid',
|
|
77
93
|
outlined: 'is-outlined',
|
|
78
94
|
ghost: 'is-ghost'
|
|
79
95
|
};
|
|
80
96
|
|
|
81
|
-
const
|
|
97
|
+
const sizeClass = {
|
|
82
98
|
sm: 'is-sm',
|
|
83
99
|
md: 'is-md',
|
|
84
100
|
lg: 'is-lg'
|
|
@@ -86,8 +102,77 @@
|
|
|
86
102
|
|
|
87
103
|
const isSingleMode = $derived(groups.length === 0);
|
|
88
104
|
|
|
105
|
+
function performReorder(dropIndex: number, dropGroupId?: string) {
|
|
106
|
+
if (!draggedItem) return;
|
|
107
|
+
|
|
108
|
+
if (isSingleMode) {
|
|
109
|
+
const newItems = [...items];
|
|
110
|
+
newItems.splice(draggedFromIndex, 1);
|
|
111
|
+
const adjustedIndex = dropIndex > draggedFromIndex ? dropIndex - 1 : dropIndex;
|
|
112
|
+
newItems.splice(adjustedIndex, 0, draggedItem);
|
|
113
|
+
items = newItems;
|
|
114
|
+
onReorder?.(newItems);
|
|
115
|
+
} else {
|
|
116
|
+
const newGroups = groups.map((g) => ({ ...g, items: [...g.items] }));
|
|
117
|
+
|
|
118
|
+
if (draggedFromGroup === dropGroupId) {
|
|
119
|
+
const group = newGroups.find((g) => g.id === dropGroupId);
|
|
120
|
+
if (group) {
|
|
121
|
+
group.items.splice(draggedFromIndex, 1);
|
|
122
|
+
const adjustedIndex = dropIndex > draggedFromIndex ? dropIndex - 1 : dropIndex;
|
|
123
|
+
group.items.splice(adjustedIndex, 0, draggedItem);
|
|
124
|
+
}
|
|
125
|
+
} else {
|
|
126
|
+
const fromGroup = newGroups.find((g) => g.id === draggedFromGroup);
|
|
127
|
+
const toGroup = newGroups.find((g) => g.id === dropGroupId);
|
|
128
|
+
if (fromGroup && toGroup) {
|
|
129
|
+
fromGroup.items.splice(draggedFromIndex, 1);
|
|
130
|
+
toGroup.items.splice(dropIndex, 0, draggedItem);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
groups = newGroups;
|
|
135
|
+
onReorder?.(
|
|
136
|
+
newGroups.find((g) => g.id === dropGroupId)?.items || [],
|
|
137
|
+
draggedFromGroup ?? undefined,
|
|
138
|
+
dropGroupId
|
|
139
|
+
);
|
|
140
|
+
onGroupReorder?.(newGroups);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
function performDropOnEmpty(groupId: string) {
|
|
145
|
+
if (!draggedItem || isSingleMode) return;
|
|
146
|
+
|
|
147
|
+
const newGroups = groups.map((g) => ({ ...g, items: [...g.items] }));
|
|
148
|
+
const fromGroup = newGroups.find((g) => g.id === draggedFromGroup);
|
|
149
|
+
const toGroup = newGroups.find((g) => g.id === groupId);
|
|
150
|
+
|
|
151
|
+
if (fromGroup && toGroup) {
|
|
152
|
+
fromGroup.items.splice(draggedFromIndex, 1);
|
|
153
|
+
toGroup.items.push(draggedItem);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
groups = newGroups;
|
|
157
|
+
onReorder?.(toGroup?.items || [], draggedFromGroup ?? undefined, groupId);
|
|
158
|
+
onGroupReorder?.(newGroups);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
function resetDragState() {
|
|
162
|
+
draggedItem = null;
|
|
163
|
+
draggedFromGroup = null;
|
|
164
|
+
draggedFromIndex = -1;
|
|
165
|
+
dragOverGroup = null;
|
|
166
|
+
dragOverIndex = -1;
|
|
167
|
+
isDragging = false;
|
|
168
|
+
isTouchDragging = false;
|
|
169
|
+
}
|
|
170
|
+
|
|
89
171
|
function handleDragStart(e: DragEvent, item: DragDropItem, index: number, groupId?: string) {
|
|
90
|
-
if (isDisabled)
|
|
172
|
+
if (isDisabled || isTouchDevice || isTouchDragging) {
|
|
173
|
+
e.preventDefault();
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
91
176
|
|
|
92
177
|
draggedItem = item;
|
|
93
178
|
draggedFromIndex = index;
|
|
@@ -100,35 +185,23 @@
|
|
|
100
185
|
}
|
|
101
186
|
|
|
102
187
|
const target = e.target as HTMLElement;
|
|
103
|
-
requestAnimationFrame(() =>
|
|
104
|
-
target.classList.add('is-dragging');
|
|
105
|
-
});
|
|
188
|
+
requestAnimationFrame(() => target.classList.add('is-dragging'));
|
|
106
189
|
}
|
|
107
190
|
|
|
108
191
|
function handleDragEnd(e: DragEvent) {
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
draggedItem = null;
|
|
113
|
-
draggedFromGroup = null;
|
|
114
|
-
draggedFromIndex = -1;
|
|
115
|
-
dragOverGroup = null;
|
|
116
|
-
dragOverIndex = -1;
|
|
117
|
-
isDragging = false;
|
|
192
|
+
(e.target as HTMLElement).classList.remove('is-dragging');
|
|
193
|
+
resetDragState();
|
|
118
194
|
}
|
|
119
195
|
|
|
120
196
|
function handleDragOver(e: DragEvent, index: number, groupId?: string) {
|
|
121
197
|
e.preventDefault();
|
|
122
198
|
if (!draggedItem || isDisabled) return;
|
|
123
|
-
|
|
124
199
|
if (!allowGroupTransfer && groupId !== draggedFromGroup) return;
|
|
125
200
|
|
|
126
201
|
dragOverIndex = index;
|
|
127
202
|
dragOverGroup = groupId ?? null;
|
|
128
203
|
|
|
129
|
-
if (e.dataTransfer)
|
|
130
|
-
e.dataTransfer.dropEffect = 'move';
|
|
131
|
-
}
|
|
204
|
+
if (e.dataTransfer) e.dataTransfer.dropEffect = 'move';
|
|
132
205
|
}
|
|
133
206
|
|
|
134
207
|
function handleDragLeave() {
|
|
@@ -144,44 +217,7 @@
|
|
|
144
217
|
return;
|
|
145
218
|
}
|
|
146
219
|
|
|
147
|
-
|
|
148
|
-
const newItems = [...items];
|
|
149
|
-
newItems.splice(draggedFromIndex, 1);
|
|
150
|
-
newItems.splice(dropIndex > draggedFromIndex ? dropIndex - 1 : dropIndex, 0, draggedItem);
|
|
151
|
-
items = newItems;
|
|
152
|
-
onReorder?.(newItems);
|
|
153
|
-
} else {
|
|
154
|
-
const newGroups = groups.map((g) => ({ ...g, items: [...g.items] }));
|
|
155
|
-
|
|
156
|
-
if (draggedFromGroup === groupId) {
|
|
157
|
-
const group = newGroups.find((g) => g.id === groupId);
|
|
158
|
-
if (group) {
|
|
159
|
-
group.items.splice(draggedFromIndex, 1);
|
|
160
|
-
group.items.splice(
|
|
161
|
-
dropIndex > draggedFromIndex ? dropIndex - 1 : dropIndex,
|
|
162
|
-
0,
|
|
163
|
-
draggedItem
|
|
164
|
-
);
|
|
165
|
-
}
|
|
166
|
-
} else {
|
|
167
|
-
const fromGroup = newGroups.find((g) => g.id === draggedFromGroup);
|
|
168
|
-
const toGroup = newGroups.find((g) => g.id === groupId);
|
|
169
|
-
|
|
170
|
-
if (fromGroup && toGroup) {
|
|
171
|
-
fromGroup.items.splice(draggedFromIndex, 1);
|
|
172
|
-
toGroup.items.splice(dropIndex, 0, draggedItem);
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
groups = newGroups;
|
|
177
|
-
onReorder?.(
|
|
178
|
-
newGroups.find((g) => g.id === groupId)?.items || [],
|
|
179
|
-
draggedFromGroup ?? undefined,
|
|
180
|
-
groupId
|
|
181
|
-
);
|
|
182
|
-
onGroupReorder?.(newGroups);
|
|
183
|
-
}
|
|
184
|
-
|
|
220
|
+
performReorder(dropIndex, groupId);
|
|
185
221
|
handleDragEnd(e);
|
|
186
222
|
}
|
|
187
223
|
|
|
@@ -194,34 +230,229 @@
|
|
|
194
230
|
return;
|
|
195
231
|
}
|
|
196
232
|
|
|
197
|
-
if (
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
233
|
+
if (groupId) performDropOnEmpty(groupId);
|
|
234
|
+
handleDragEnd(e);
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
function createGhostElement(source: HTMLElement): HTMLElement {
|
|
238
|
+
const ghost = source.cloneNode(true) as HTMLElement;
|
|
239
|
+
Object.assign(ghost.style, {
|
|
240
|
+
position: 'fixed',
|
|
241
|
+
pointerEvents: 'none',
|
|
242
|
+
zIndex: '9999',
|
|
243
|
+
opacity: '0.9',
|
|
244
|
+
width: `${source.offsetWidth}px`,
|
|
245
|
+
transform: 'scale(1.02)',
|
|
246
|
+
boxShadow: '0 8px 24px rgba(0, 0, 0, 0.2)'
|
|
247
|
+
});
|
|
248
|
+
ghost.classList.add('drag-drop-ghost');
|
|
249
|
+
document.body.appendChild(ghost);
|
|
250
|
+
return ghost;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
function updateGhostPosition(x: number, y: number) {
|
|
254
|
+
if (ghostElement) {
|
|
255
|
+
ghostElement.style.left = `${x - ghostElement.offsetWidth / 2}px`;
|
|
256
|
+
ghostElement.style.top = `${y - ghostElement.offsetHeight / 2}px`;
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
function removeGhostElement() {
|
|
261
|
+
ghostElement?.remove();
|
|
262
|
+
ghostElement = null;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
function clearTimers() {
|
|
266
|
+
if (longPressTimer) {
|
|
267
|
+
clearTimeout(longPressTimer);
|
|
268
|
+
longPressTimer = null;
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
function cancelTouchDrag() {
|
|
273
|
+
clearTimers();
|
|
274
|
+
removeGhostElement();
|
|
275
|
+
touchDraggedElement?.classList.remove('is-dragging');
|
|
276
|
+
isTouchPending = false;
|
|
277
|
+
resetDragState();
|
|
278
|
+
}
|
|
201
279
|
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
280
|
+
function getItemAtPoint(
|
|
281
|
+
x: number,
|
|
282
|
+
y: number
|
|
283
|
+
): { element: HTMLElement; index: number; groupId?: string } | null {
|
|
284
|
+
if (!containerEl) return null;
|
|
285
|
+
|
|
286
|
+
const allItems = containerEl.querySelectorAll('.drag-drop-item');
|
|
287
|
+
for (const item of allItems) {
|
|
288
|
+
const rect = item.getBoundingClientRect();
|
|
289
|
+
if (x >= rect.left && x <= rect.right && y >= rect.top && y <= rect.bottom) {
|
|
290
|
+
const groupEl = item.closest('.drag-drop-group');
|
|
291
|
+
const groupId = groupEl?.getAttribute('data-group-id') || undefined;
|
|
292
|
+
const listEl = item.closest('.drag-drop-list');
|
|
293
|
+
const siblings = listEl?.querySelectorAll('.drag-drop-item') || [];
|
|
294
|
+
const index = Array.from(siblings).indexOf(item);
|
|
295
|
+
return { element: item as HTMLElement, index, groupId };
|
|
205
296
|
}
|
|
297
|
+
}
|
|
206
298
|
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
299
|
+
const emptyZones = containerEl.querySelectorAll('.drag-drop-empty');
|
|
300
|
+
for (const zone of emptyZones) {
|
|
301
|
+
const rect = zone.getBoundingClientRect();
|
|
302
|
+
if (x >= rect.left && x <= rect.right && y >= rect.top && y <= rect.bottom) {
|
|
303
|
+
const groupEl = zone.closest('.drag-drop-group');
|
|
304
|
+
const groupId = groupEl?.getAttribute('data-group-id') || undefined;
|
|
305
|
+
return { element: zone as HTMLElement, index: 0, groupId };
|
|
306
|
+
}
|
|
210
307
|
}
|
|
211
308
|
|
|
212
|
-
|
|
309
|
+
return null;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
function handleTouchStart(e: TouchEvent, item: DragDropItem, index: number, groupId?: string) {
|
|
313
|
+
if (isDisabled) return;
|
|
314
|
+
|
|
315
|
+
const touch = e.touches[0];
|
|
316
|
+
touchStart = { x: touch.clientX, y: touch.clientY };
|
|
317
|
+
touchCurrent = { ...touchStart };
|
|
318
|
+
|
|
319
|
+
touchDraggedElement = e.currentTarget as HTMLElement;
|
|
320
|
+
isTouchPending = true;
|
|
321
|
+
|
|
322
|
+
longPressTimer = setTimeout(() => {
|
|
323
|
+
if (!isTouchPending) return;
|
|
324
|
+
|
|
325
|
+
isTouchDragging = true;
|
|
326
|
+
isDragging = true;
|
|
327
|
+
isTouchPending = false;
|
|
328
|
+
draggedItem = item;
|
|
329
|
+
draggedFromIndex = index;
|
|
330
|
+
draggedFromGroup = groupId ?? null;
|
|
331
|
+
|
|
332
|
+
touchDraggedElement?.classList.add('is-dragging');
|
|
333
|
+
ghostElement = createGhostElement(touchDraggedElement!);
|
|
334
|
+
updateGhostPosition(touchCurrent.x, touchCurrent.y);
|
|
335
|
+
|
|
336
|
+
navigator.vibrate?.(50);
|
|
337
|
+
}, LONG_PRESS_DURATION);
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
function handleTouchMove(e: TouchEvent) {
|
|
341
|
+
const touch = e.touches[0];
|
|
342
|
+
touchCurrent = { x: touch.clientX, y: touch.clientY };
|
|
343
|
+
|
|
344
|
+
const deltaX = Math.abs(touchCurrent.x - touchStart.x);
|
|
345
|
+
const deltaY = Math.abs(touchCurrent.y - touchStart.y);
|
|
346
|
+
|
|
347
|
+
if (!isTouchDragging && (deltaX > SCROLL_THRESHOLD || deltaY > SCROLL_THRESHOLD)) {
|
|
348
|
+
cancelTouchDrag();
|
|
349
|
+
return;
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
if (!isTouchDragging || !draggedItem) return;
|
|
353
|
+
|
|
354
|
+
e.preventDefault();
|
|
355
|
+
updateGhostPosition(touchCurrent.x, touchCurrent.y);
|
|
356
|
+
|
|
357
|
+
containerEl
|
|
358
|
+
?.querySelectorAll('.is-touch-drag-over')
|
|
359
|
+
.forEach((el) => el.classList.remove('is-touch-drag-over'));
|
|
360
|
+
|
|
361
|
+
const itemAtPoint = getItemAtPoint(touchCurrent.x, touchCurrent.y);
|
|
362
|
+
if (itemAtPoint && itemAtPoint.element !== touchDraggedElement) {
|
|
363
|
+
if (!allowGroupTransfer && itemAtPoint.groupId !== draggedFromGroup) return;
|
|
364
|
+
|
|
365
|
+
dragOverIndex = itemAtPoint.index;
|
|
366
|
+
dragOverGroup = itemAtPoint.groupId ?? null;
|
|
367
|
+
itemAtPoint.element.classList.add('is-touch-drag-over');
|
|
368
|
+
} else {
|
|
369
|
+
dragOverIndex = -1;
|
|
370
|
+
dragOverGroup = null;
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
function handleTouchEnd() {
|
|
375
|
+
clearTimers();
|
|
376
|
+
isTouchPending = false;
|
|
377
|
+
|
|
378
|
+
if (!isTouchDragging || !draggedItem) {
|
|
379
|
+
isTouchDragging = false;
|
|
380
|
+
touchDraggedElement = null;
|
|
381
|
+
return;
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
containerEl
|
|
385
|
+
?.querySelectorAll('.is-touch-drag-over')
|
|
386
|
+
.forEach((el) => el.classList.remove('is-touch-drag-over'));
|
|
387
|
+
|
|
388
|
+
const itemAtPoint = getItemAtPoint(touchCurrent.x, touchCurrent.y);
|
|
389
|
+
if (itemAtPoint) {
|
|
390
|
+
if (allowGroupTransfer || itemAtPoint.groupId === draggedFromGroup) {
|
|
391
|
+
performReorder(itemAtPoint.index, itemAtPoint.groupId);
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
touchDraggedElement?.classList.remove('is-dragging');
|
|
396
|
+
removeGhostElement();
|
|
397
|
+
touchDraggedElement = null;
|
|
398
|
+
resetDragState();
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
onMount(() => {
|
|
402
|
+
isTouchDevice = 'ontouchstart' in window || navigator.maxTouchPoints > 0;
|
|
403
|
+
|
|
404
|
+
const onGlobalTouchMove = (e: TouchEvent) => {
|
|
405
|
+
if (isTouchPending || isTouchDragging) {
|
|
406
|
+
handleTouchMove(e);
|
|
407
|
+
}
|
|
408
|
+
};
|
|
409
|
+
|
|
410
|
+
const onGlobalTouchEnd = () => {
|
|
411
|
+
if (isTouchPending || isTouchDragging) {
|
|
412
|
+
handleTouchEnd();
|
|
413
|
+
}
|
|
414
|
+
};
|
|
415
|
+
|
|
416
|
+
document.addEventListener('touchmove', onGlobalTouchMove, { passive: false });
|
|
417
|
+
document.addEventListener('touchend', onGlobalTouchEnd);
|
|
418
|
+
document.addEventListener('touchcancel', onGlobalTouchEnd);
|
|
419
|
+
|
|
420
|
+
return () => {
|
|
421
|
+
document.removeEventListener('touchmove', onGlobalTouchMove);
|
|
422
|
+
document.removeEventListener('touchend', onGlobalTouchEnd);
|
|
423
|
+
document.removeEventListener('touchcancel', onGlobalTouchEnd);
|
|
424
|
+
clearTimers();
|
|
425
|
+
};
|
|
426
|
+
});
|
|
427
|
+
|
|
428
|
+
function isItemDragging(item: DragDropItem) {
|
|
429
|
+
return draggedItem?.id === item.id;
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
function isItemDragOver(index: number, groupId?: string): boolean {
|
|
433
|
+
if (dragOverIndex !== index) return false;
|
|
434
|
+
if (dragOverGroup !== (groupId ?? null)) return false;
|
|
435
|
+
if (!draggedItem) return false;
|
|
436
|
+
|
|
437
|
+
const targetId = isSingleMode
|
|
438
|
+
? items[index]?.id
|
|
439
|
+
: groups.find((g) => g.id === groupId)?.items[index]?.id;
|
|
440
|
+
|
|
441
|
+
return draggedItem.id !== targetId;
|
|
213
442
|
}
|
|
214
443
|
</script>
|
|
215
444
|
|
|
216
445
|
<div
|
|
446
|
+
bind:this={containerEl}
|
|
217
447
|
class={cn(
|
|
218
448
|
'drag-drop',
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
449
|
+
colorClass[color],
|
|
450
|
+
variantClass[variant],
|
|
451
|
+
sizeClass[size],
|
|
222
452
|
direction === 'horizontal' ? 'is-horizontal' : 'is-vertical',
|
|
223
453
|
isDisabled && 'is-disabled',
|
|
224
454
|
isDragging && 'is-dragging',
|
|
455
|
+
isTouchDragging && 'is-touch-dragging',
|
|
225
456
|
className
|
|
226
457
|
)}
|
|
227
458
|
>
|
|
@@ -231,16 +462,17 @@
|
|
|
231
462
|
<div
|
|
232
463
|
class={cn(
|
|
233
464
|
'drag-drop-item',
|
|
234
|
-
|
|
235
|
-
|
|
465
|
+
isItemDragging(item) && 'is-dragging',
|
|
466
|
+
isItemDragOver(index) && 'is-drag-over',
|
|
236
467
|
itemClass
|
|
237
468
|
)}
|
|
238
|
-
draggable={!isDisabled}
|
|
469
|
+
draggable={!isDisabled && !isTouchDevice}
|
|
239
470
|
ondragstart={(e) => handleDragStart(e, item, index)}
|
|
240
471
|
ondragend={handleDragEnd}
|
|
241
472
|
ondragover={(e) => handleDragOver(e, index)}
|
|
242
473
|
ondragleave={handleDragLeave}
|
|
243
474
|
ondrop={(e) => handleDrop(e, index)}
|
|
475
|
+
ontouchstart={(e) => handleTouchStart(e, item, index)}
|
|
244
476
|
role="listitem"
|
|
245
477
|
>
|
|
246
478
|
{#if itemSnippet}
|
|
@@ -271,14 +503,14 @@
|
|
|
271
503
|
{#if emptySnippet}
|
|
272
504
|
{@render emptySnippet()}
|
|
273
505
|
{:else}
|
|
274
|
-
<span>
|
|
506
|
+
<span>{emptyText}</span>
|
|
275
507
|
{/if}
|
|
276
508
|
</div>
|
|
277
509
|
{/if}
|
|
278
510
|
</div>
|
|
279
511
|
{:else}
|
|
280
512
|
{#each groups as group, groupIndex (group.id)}
|
|
281
|
-
<div class={cn('drag-drop-group', groupClass)}>
|
|
513
|
+
<div class={cn('drag-drop-group', groupClass)} data-group-id={group.id}>
|
|
282
514
|
{#if groupHeaderSnippet}
|
|
283
515
|
{@render groupHeaderSnippet(group, groupIndex)}
|
|
284
516
|
{:else if group.title}
|
|
@@ -293,19 +525,17 @@
|
|
|
293
525
|
<div
|
|
294
526
|
class={cn(
|
|
295
527
|
'drag-drop-item',
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
dragOverGroup === group.id &&
|
|
299
|
-
draggedItem?.id !== item.id &&
|
|
300
|
-
'is-drag-over',
|
|
528
|
+
isItemDragging(item) && 'is-dragging',
|
|
529
|
+
isItemDragOver(index, group.id) && 'is-drag-over',
|
|
301
530
|
itemClass
|
|
302
531
|
)}
|
|
303
|
-
draggable={!isDisabled}
|
|
532
|
+
draggable={!isDisabled && !isTouchDevice}
|
|
304
533
|
ondragstart={(e) => handleDragStart(e, item, index, group.id)}
|
|
305
534
|
ondragend={handleDragEnd}
|
|
306
535
|
ondragover={(e) => handleDragOver(e, index, group.id)}
|
|
307
536
|
ondragleave={handleDragLeave}
|
|
308
537
|
ondrop={(e) => handleDrop(e, index, group.id)}
|
|
538
|
+
ontouchstart={(e) => handleTouchStart(e, item, index, group.id)}
|
|
309
539
|
role="listitem"
|
|
310
540
|
>
|
|
311
541
|
{#if itemSnippet}
|
|
@@ -337,7 +567,7 @@
|
|
|
337
567
|
{#if emptySnippet}
|
|
338
568
|
{@render emptySnippet(group)}
|
|
339
569
|
{:else}
|
|
340
|
-
<span>
|
|
570
|
+
<span>{emptyText}</span>
|
|
341
571
|
{/if}
|
|
342
572
|
</div>
|
|
343
573
|
{/if}
|
|
@@ -26,6 +26,7 @@ type Props = {
|
|
|
26
26
|
itemSnippet?: Snippet<[DragDropItem, number]>;
|
|
27
27
|
groupHeaderSnippet?: Snippet<[DragDropGroup, number]>;
|
|
28
28
|
emptySnippet?: Snippet<[DragDropGroup?]>;
|
|
29
|
+
emptyText?: string;
|
|
29
30
|
};
|
|
30
31
|
declare const DragDrop: import("svelte").Component<Props, {}, "items" | "groups">;
|
|
31
32
|
type DragDrop = ReturnType<typeof DragDrop>;
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
| 'warning'
|
|
30
30
|
| 'danger'
|
|
31
31
|
| 'surface'
|
|
32
|
-
| '
|
|
32
|
+
| 'background';
|
|
33
33
|
variant?: 'solid' | 'soft' | 'outlined' | 'ghost';
|
|
34
34
|
size?: 'sm' | 'md' | 'lg';
|
|
35
35
|
name?: string;
|
|
@@ -49,7 +49,7 @@
|
|
|
49
49
|
class: className,
|
|
50
50
|
controlClass,
|
|
51
51
|
icon,
|
|
52
|
-
color = '
|
|
52
|
+
color = 'background',
|
|
53
53
|
variant = 'outlined',
|
|
54
54
|
size = 'md',
|
|
55
55
|
name,
|
|
@@ -75,7 +75,7 @@
|
|
|
75
75
|
danger: 'is-danger',
|
|
76
76
|
warning: 'is-warning',
|
|
77
77
|
surface: 'is-surface',
|
|
78
|
-
|
|
78
|
+
background: 'is-background'
|
|
79
79
|
};
|
|
80
80
|
|
|
81
81
|
const variantClasses = {
|
|
@@ -14,7 +14,7 @@ type Props = {
|
|
|
14
14
|
class?: string;
|
|
15
15
|
controlClass?: string;
|
|
16
16
|
icon?: IconData;
|
|
17
|
-
color?: 'primary' | 'secondary' | 'muted' | 'success' | 'info' | 'warning' | 'danger' | 'surface' | '
|
|
17
|
+
color?: 'primary' | 'secondary' | 'muted' | 'success' | 'info' | 'warning' | 'danger' | 'surface' | 'background';
|
|
18
18
|
variant?: 'solid' | 'soft' | 'outlined' | 'ghost';
|
|
19
19
|
size?: 'sm' | 'md' | 'lg';
|
|
20
20
|
name?: string;
|