@hkdigital/lib-sveltekit 0.1.76 → 0.1.77

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.
@@ -0,0 +1,112 @@
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
+ empty?: Snippet<[]>;
15
+ preview?: Snippet<[{
16
+ item: any;
17
+ source: string;
18
+ group: string;
19
+ metadata?: any;
20
+ }]>;
21
+ isDragOver?: boolean;
22
+ canDrop?: boolean;
23
+ isDropping?: boolean;
24
+ itemCount?: number;
25
+ onDragEnter?: (detail: {
26
+ event: DragEvent;
27
+ zone: string;
28
+ canDrop: boolean;
29
+ }) => void;
30
+ onDragOver?: (detail: {
31
+ event: DragEvent;
32
+ zone: string;
33
+ }) => void;
34
+ onDragLeave?: (detail: {
35
+ event: DragEvent;
36
+ zone: string;
37
+ }) => void;
38
+ onDrop?: (detail: {
39
+ event: DragEvent;
40
+ zone: string;
41
+ item: any;
42
+ source: string;
43
+ metadata?: any;
44
+ }) => any;
45
+ onDropStart?: (detail: {
46
+ event: DragEvent;
47
+ zone: string;
48
+ data: any;
49
+ }) => void;
50
+ onDropEnd?: (detail: {
51
+ event: DragEvent;
52
+ zone: string;
53
+ data: any;
54
+ success: boolean;
55
+ error?: Error;
56
+ }) => void;
57
+ }>): void;
58
+ };
59
+ declare const Dropzone: import("svelte").Component<{
60
+ [key: string]: any;
61
+ zone?: string;
62
+ group?: string;
63
+ disabled?: boolean;
64
+ accepts?: (item: any) => boolean;
65
+ maxItems?: number;
66
+ base?: string;
67
+ classes?: string;
68
+ children?: import("svelte").Snippet;
69
+ empty?: import("svelte").Snippet;
70
+ preview?: import("svelte").Snippet<[{
71
+ item: any;
72
+ source: string;
73
+ group: string;
74
+ metadata?: any;
75
+ }]>;
76
+ isDragOver?: boolean;
77
+ canDrop?: boolean;
78
+ isDropping?: boolean;
79
+ itemCount?: number;
80
+ onDragEnter?: (detail: {
81
+ event: DragEvent;
82
+ zone: string;
83
+ canDrop: boolean;
84
+ }) => void;
85
+ onDragOver?: (detail: {
86
+ event: DragEvent;
87
+ zone: string;
88
+ }) => void;
89
+ onDragLeave?: (detail: {
90
+ event: DragEvent;
91
+ zone: string;
92
+ }) => void;
93
+ onDrop?: (detail: {
94
+ event: DragEvent;
95
+ zone: string;
96
+ item: any;
97
+ source: string;
98
+ metadata?: any;
99
+ }) => any | Promise<any>;
100
+ onDropStart?: (detail: {
101
+ event: DragEvent;
102
+ zone: string;
103
+ data: any;
104
+ }) => void;
105
+ onDropEnd?: (detail: {
106
+ event: DragEvent;
107
+ zone: string;
108
+ data: any;
109
+ success: boolean;
110
+ error?: Error;
111
+ }) => void;
112
+ }, {}, "isDropping" | "isDragOver" | "canDrop" | "itemCount">;
@@ -0,0 +1,282 @@
1
+ <script>
2
+ import { onMount, onDestroy } from 'svelte';
3
+ import { toStateClasses } from '$lib/util/design-system/index.js';
4
+ import { useDragState } from './drag-state.svelte.js';
5
+
6
+ import {
7
+ READY,
8
+ DRAG_OVER,
9
+ CAN_DROP,
10
+ CANNOT_DROP,
11
+ DROP_DISABLED,
12
+ ACTIVE_DROP
13
+ } from '$lib/constants/state-labels/drop-states.js';
14
+
15
+ const dragState = useDragState();
16
+
17
+ /**
18
+ * @type {{
19
+ * zone?: string,
20
+ * group?: string,
21
+ * disabled?: boolean,
22
+ * accepts?: (item: any) => boolean,
23
+ * maxItems?: number,
24
+ * base?: string,
25
+ * classes?: string,
26
+ * children?: import('svelte').Snippet,
27
+ * empty?: import('svelte').Snippet,
28
+ * preview?: import('svelte').Snippet<[{
29
+ * item: any,
30
+ * source: string,
31
+ * group: string,
32
+ * metadata?: any
33
+ * }]>,
34
+ * isDragOver?: boolean,
35
+ * canDrop?: boolean,
36
+ * isDropping?: boolean,
37
+ * itemCount?: number,
38
+ * onDragEnter?: (detail: {
39
+ * event: DragEvent,
40
+ * zone: string,
41
+ * canDrop: boolean
42
+ * }) => void,
43
+ * onDragOver?: (detail: {
44
+ * event: DragEvent,
45
+ * zone: string
46
+ * }) => void,
47
+ * onDragLeave?: (detail: {
48
+ * event: DragEvent,
49
+ * zone: string
50
+ * }) => void,
51
+ * onDrop?: (detail: {
52
+ * event: DragEvent,
53
+ * zone: string,
54
+ * item: any,
55
+ * source: string,
56
+ * metadata?: any
57
+ * }) => any | Promise<any>,
58
+ * onDropStart?: (detail: {
59
+ * event: DragEvent,
60
+ * zone: string,
61
+ * data: any
62
+ * }) => void,
63
+ * onDropEnd?: (detail: {
64
+ * event: DragEvent,
65
+ * zone: string,
66
+ * data: any,
67
+ * success: boolean,
68
+ * error?: Error
69
+ * }) => void,
70
+ * [key: string]: any
71
+ * }}
72
+ */
73
+ let {
74
+ zone = 'default',
75
+ group = 'default',
76
+ disabled = false,
77
+ accepts = () => true,
78
+ maxItems = Infinity,
79
+ base = '',
80
+ classes = '',
81
+ children,
82
+ empty,
83
+ preview,
84
+ isDragOver = $bindable(false),
85
+ canDrop = $bindable(false),
86
+ isDropping = $bindable(false),
87
+ itemCount = $bindable(0),
88
+ onDragEnter,
89
+ onDragOver,
90
+ onDragLeave,
91
+ onDrop,
92
+ onDropStart,
93
+ onDropEnd,
94
+ ...attrs
95
+ } = $props();
96
+
97
+ let currentState = $state(READY);
98
+ let dragCounter = 0;
99
+
100
+ // Cleanup function
101
+ let cleanup;
102
+
103
+ onMount(() => {
104
+ // Global dragend listener to ensure state cleanup
105
+ const handleGlobalDragEnd = () => {
106
+ dragCounter = 0;
107
+ currentState = READY;
108
+ };
109
+
110
+ document.addEventListener('dragend', handleGlobalDragEnd);
111
+
112
+ cleanup = () => {
113
+ document.removeEventListener('dragend', handleGlobalDragEnd);
114
+ };
115
+ });
116
+
117
+ onDestroy(() => {
118
+ cleanup?.();
119
+ });
120
+
121
+ // Computed state object for CSS classes
122
+ let stateObject = $derived({
123
+ ready: currentState === READY,
124
+ 'drag-over': currentState === DRAG_OVER,
125
+ 'can-drop': currentState === CAN_DROP,
126
+ 'cannot-drop': currentState === CANNOT_DROP,
127
+ 'drop-disabled': disabled,
128
+ 'active-drop': currentState === ACTIVE_DROP
129
+ });
130
+
131
+ let stateClasses = $derived(toStateClasses(stateObject));
132
+
133
+ // Update bindable props
134
+ $effect(() => {
135
+ isDragOver = [
136
+ DRAG_OVER,
137
+ CAN_DROP,
138
+ CANNOT_DROP
139
+ ].includes(currentState);
140
+
141
+ canDrop = currentState === CAN_DROP;
142
+ isDropping = currentState === ACTIVE_DROP;
143
+ });
144
+
145
+ /**
146
+ * Check if we can accept the dragged item
147
+ * @param {Object} data
148
+ * @returns {boolean}
149
+ */
150
+ function canAcceptDrop(data) {
151
+ if (disabled) return false;
152
+ if (!data) return false;
153
+ if (data.group !== group) return false;
154
+ if (!accepts(data.item)) return false;
155
+ if (itemCount >= maxItems) return false;
156
+ return true;
157
+ }
158
+
159
+ /**
160
+ * Handle drag enter
161
+ * @param {DragEvent} event
162
+ */
163
+ function handleDragEnter(event) {
164
+ event.preventDefault();
165
+ dragCounter++;
166
+
167
+ if (dragCounter === 1) {
168
+ const dragData = dragState.current;
169
+
170
+ if (dragData) {
171
+ currentState = canAcceptDrop(dragData)
172
+ ? CAN_DROP
173
+ : CANNOT_DROP;
174
+ } else {
175
+ currentState = DRAG_OVER;
176
+ }
177
+
178
+ onDragEnter?.({ event, zone, canDrop: currentState === CAN_DROP });
179
+ }
180
+ }
181
+
182
+ /**
183
+ * Handle drag over
184
+ * @param {DragEvent} event
185
+ */
186
+ function handleDragOver(event) {
187
+ event.preventDefault();
188
+
189
+ // Re-evaluate on each dragover in case state changed
190
+ const dragData = dragState.current;
191
+
192
+ if (dragData && currentState === DRAG_OVER) {
193
+ currentState = canAcceptDrop(dragData)
194
+ ? CAN_DROP
195
+ : CANNOT_DROP;
196
+ }
197
+
198
+ if (currentState === CAN_DROP) {
199
+ event.dataTransfer.dropEffect = 'move';
200
+ } else if (currentState === CANNOT_DROP) {
201
+ event.dataTransfer.dropEffect = 'none';
202
+ }
203
+
204
+ onDragOver?.({ event, zone });
205
+ }
206
+
207
+ /**
208
+ * Handle drag leave
209
+ * @param {DragEvent} event
210
+ */
211
+ function handleDragLeave(event) {
212
+ if (!event.currentTarget.contains(event.relatedTarget)) {
213
+ dragCounter--;
214
+
215
+ if (dragCounter <= 0) {
216
+ dragCounter = 0;
217
+ currentState = READY;
218
+ onDragLeave?.({ event, zone });
219
+ }
220
+ }
221
+ }
222
+
223
+ /**
224
+ * Handle drop
225
+ * @param {DragEvent} event
226
+ */
227
+ function handleDrop(event) {
228
+ event.preventDefault();
229
+ dragCounter = 0;
230
+
231
+ try {
232
+ const data = JSON.parse(event.dataTransfer.getData('application/json'));
233
+
234
+ if (canAcceptDrop(data)) {
235
+ currentState = ACTIVE_DROP;
236
+ onDropStart?.({ event, zone, data });
237
+
238
+ const dropResult = onDrop?.({
239
+ event,
240
+ zone,
241
+ item: data.item,
242
+ source: data.source,
243
+ metadata: data.metadata
244
+ });
245
+
246
+ Promise.resolve(dropResult).then(() => {
247
+ currentState = READY;
248
+ onDropEnd?.({ event, zone, data, success: true });
249
+ }).catch((error) => {
250
+ currentState = READY;
251
+ onDropEnd?.({ event, zone, data, success: false, error });
252
+ });
253
+ } else {
254
+ currentState = READY;
255
+ }
256
+ } catch (error) {
257
+ console.error('Drop error:', error);
258
+ currentState = READY;
259
+ }
260
+ }
261
+ </script>
262
+
263
+ <div
264
+ data-component="dropzone"
265
+ ondragenter={handleDragEnter}
266
+ ondragover={handleDragOver}
267
+ ondragleave={handleDragLeave}
268
+ ondrop={handleDrop}
269
+ class="{base} {classes} {stateClasses}"
270
+ data-zone={zone}
271
+ {...attrs}
272
+ >
273
+ {#if children}
274
+ {@render children()}
275
+ {:else if currentState === CAN_DROP && preview}
276
+ {@render preview(dragState.current)}
277
+ {:else if itemCount === 0 && empty}
278
+ {@render empty()}
279
+ {:else}
280
+ <div data-element="drop-zone-empty">Drop items here</div>
281
+ {/if}
282
+ </div>
@@ -0,0 +1,6 @@
1
+ export function useDragState(): {
2
+ readonly current: any;
3
+ start(item: any, source: any, group: any): void;
4
+ end(): void;
5
+ isDragging(): boolean;
6
+ };
@@ -0,0 +1,19 @@
1
+ let currentDrag = $state(null);
2
+
3
+ export function useDragState() {
4
+ return {
5
+ get current() { return currentDrag; },
6
+
7
+ start(item, source, group) {
8
+ currentDrag = { item, source, group };
9
+ },
10
+
11
+ end() {
12
+ currentDrag = null;
13
+ },
14
+
15
+ isDragging() {
16
+ return currentDrag !== null;
17
+ }
18
+ };
19
+ }
@@ -0,0 +1,2 @@
1
+ export { default as Draggable } from "./Draggable.svelte";
2
+ export { default as DropZone } from "./DropZone.svelte";
@@ -0,0 +1,2 @@
1
+ export { default as Draggable } from './Draggable.svelte';
2
+ export { default as DropZone } from './DropZone.svelte';
@@ -0,0 +1,5 @@
1
+ export const IDLE: "idle";
2
+ export const DRAGGING: "dragging";
3
+ export const DRAG_PREVIEW: "drag-preview";
4
+ export const DROPPING: "dropping";
5
+ export const DRAG_DISABLED: "drag-disabled";
@@ -0,0 +1,6 @@
1
+ // Draggable states
2
+ export const IDLE = 'idle'; // Not being dragged
3
+ export const DRAGGING = 'dragging'; // Currently being dragged
4
+ export const DRAG_PREVIEW = 'drag-preview'; // Mouse down, before drag
5
+ export const DROPPING = 'dropping'; // Just dropped, animating
6
+ export const DRAG_DISABLED = 'drag-disabled'; // Dragging not allowed
@@ -0,0 +1,6 @@
1
+ export const READY: "ready";
2
+ export const DRAG_OVER: "drag-over";
3
+ export const CAN_DROP: "can-drop";
4
+ export const CANNOT_DROP: "cannot-drop";
5
+ export const DROP_DISABLED: "drop-disabled";
6
+ export const ACTIVE_DROP: "active-drop";
@@ -0,0 +1,6 @@
1
+ export const READY = 'ready'; // Waiting for drag
2
+ export const DRAG_OVER = 'drag-over'; // Item dragged over zone
3
+ export const CAN_DROP = 'can-drop'; // Valid drop target
4
+ export const CANNOT_DROP = 'cannot-drop'; // Invalid drop target
5
+ export const DROP_DISABLED = 'drop-disabled'; // Dropping not allowed
6
+ export const ACTIVE_DROP = 'active-drop'; // Currently processing drop
@@ -0,0 +1,51 @@
1
+ @define-mixin component-draggable {
2
+ [data-component='draggable'] {
3
+ cursor: move;
4
+ user-select: none;
5
+ transition:
6
+ opacity 0.2s ease,
7
+ transform 0.2s ease;
8
+ }
9
+
10
+ /* State-based styling using state classes */
11
+ [data-component='draggable'].state-idle {
12
+ opacity: 1;
13
+ transform: scale(1);
14
+ }
15
+
16
+ [data-component='draggable'].state-dragging {
17
+ opacity: 0.5;
18
+ transform: scale(0.95);
19
+ }
20
+
21
+ [data-component='draggable'].state-drag-preview {
22
+ transform: scale(0.98);
23
+ filter: brightness(0.95);
24
+ }
25
+
26
+ [data-component='draggable'].state-dropping {
27
+ animation: drop-finish 0.2s ease-out;
28
+ }
29
+
30
+ [data-component='draggable'].state-drag-disabled {
31
+ cursor: not-allowed;
32
+ opacity: 0.6;
33
+ filter: grayscale(0.5);
34
+ }
35
+
36
+ /* Animations */
37
+ @keyframes drop-finish {
38
+ 0% {
39
+ transform: scale(0.95);
40
+ opacity: 0.8;
41
+ }
42
+ 50% {
43
+ transform: scale(1.05);
44
+ opacity: 1;
45
+ }
46
+ 100% {
47
+ transform: scale(1);
48
+ opacity: 1;
49
+ }
50
+ }
51
+ }
@@ -0,0 +1,73 @@
1
+ @define-mixin component-dropzone {
2
+ [data-component='dropzone'] {
3
+ min-height: 100px;
4
+ border: 2px dashed rgb(var(--color-surface-400));
5
+ border-radius: var(--theme-rounded-container);
6
+ padding: 1rem;
7
+ transition: all 0.2s ease;
8
+ position: relative;
9
+ background-color: rgb(var(--color-surface-50) / 0.5);
10
+ }
11
+
12
+ /* State-based styling using state classes */
13
+ [data-component='dropzone'].state-ready {
14
+ border-color: rgb(var(--color-surface-400));
15
+ background-color: transparent;
16
+ }
17
+
18
+ [data-component='dropzone'].state-drag-over {
19
+ border-color: rgb(var(--color-primary-400));
20
+ background-color: rgb(var(--color-primary-500) / 0.05);
21
+ }
22
+
23
+ [data-component='dropzone'].state-can-drop {
24
+ border-color: rgb(var(--color-success-500));
25
+ background-color: rgb(var(--color-success-500) / 0.08);
26
+ transform: scale(1.01);
27
+ box-shadow: 0 0 0 3px rgb(var(--color-success-500) / 0.2);
28
+ }
29
+
30
+ [data-component='dropzone'].state-cannot-drop {
31
+ border-color: rgb(var(--color-error-500));
32
+ background-color: rgb(var(--color-error-500) / 0.08);
33
+ cursor: not-allowed;
34
+ }
35
+
36
+ [data-component='dropzone'].state-active-drop {
37
+ animation: drop-pulse 0.3s ease-out;
38
+ border-color: rgb(var(--color-success-500));
39
+ background-color: rgb(var(--color-success-500) / 0.1);
40
+ }
41
+
42
+ [data-component='dropzone'].state-drop-disabled {
43
+ opacity: 0.5;
44
+ cursor: not-allowed;
45
+ background-color: rgb(var(--color-surface-100));
46
+ }
47
+
48
+ /* Empty state */
49
+ [data-element='drop-zone-empty'] {
50
+ display: flex;
51
+ align-items: center;
52
+ justify-content: center;
53
+ height: 100%;
54
+ color: rgb(var(--color-surface-500));
55
+ font-style: italic;
56
+ }
57
+
58
+ /* Animations */
59
+ @keyframes drop-pulse {
60
+ 0% {
61
+ transform: scale(1);
62
+ box-shadow: 0 0 0 0 rgb(var(--color-success-500) / 0.4);
63
+ }
64
+ 50% {
65
+ transform: scale(1.02);
66
+ box-shadow: 0 0 0 8px rgb(var(--color-success-500) / 0);
67
+ }
68
+ 100% {
69
+ transform: scale(1);
70
+ box-shadow: 0 0 0 0 rgb(var(--color-success-500) / 0);
71
+ }
72
+ }
73
+ }
@@ -11,6 +11,9 @@
11
11
 
12
12
  /*@import "./components/buttons/skip-button.css";*/
13
13
 
14
+ @import './components/drag-drop/draggable.css';
15
+ @import './components/drag-drop/dropzone.css';
16
+
14
17
  /* Icons */
15
18
  @import './components/icons/icon-steeze.css';
16
19
 
@@ -36,6 +39,9 @@
36
39
  @mixin component-button-icon-steeze;
37
40
  /* @mixin buttons-skip-button;*/
38
41
 
42
+ @mixin component-draggable;
43
+ @mixin component-dropzone;
44
+
39
45
  @mixin component-icon-steeze;
40
46
 
41
47
  @mixin component-panel;
@@ -0,0 +1,20 @@
1
+ declare const _default: {};
2
+ export default _default;
3
+ export type DragData = {
4
+ /**
5
+ * - The item being dragged
6
+ */
7
+ item: any;
8
+ /**
9
+ * - Source identifier
10
+ */
11
+ source?: string;
12
+ /**
13
+ * - Drag group
14
+ */
15
+ group?: string;
16
+ /**
17
+ * - Additional metadata
18
+ */
19
+ metadata?: any;
20
+ };
@@ -0,0 +1,9 @@
1
+ /**
2
+ * @typedef {Object} DragData
3
+ * @property {any} item - The item being dragged
4
+ * @property {string} [source] - Source identifier
5
+ * @property {string} [group] - Drag group
6
+ * @property {any} [metadata] - Additional metadata
7
+ */
8
+
9
+ export default {}
@@ -0,0 +1,20 @@
1
+ declare const _default: {};
2
+ export default _default;
3
+ export type DropData = {
4
+ /**
5
+ * - The dropped item
6
+ */
7
+ item: any;
8
+ /**
9
+ * - Source identifier
10
+ */
11
+ source: string;
12
+ /**
13
+ * - Drag group
14
+ */
15
+ group: string;
16
+ /**
17
+ * - Additional metadata
18
+ */
19
+ metadata?: any;
20
+ };
@@ -0,0 +1,9 @@
1
+ /**
2
+ * @typedef {Object} DropData
3
+ * @property {any} item - The dropped item
4
+ * @property {string} source - Source identifier
5
+ * @property {string} group - Drag group
6
+ * @property {any} [metadata] - Additional metadata
7
+ */
8
+
9
+ export default {}
@@ -10,5 +10,4 @@
10
10
  * Single ImageMeta object or array of ImageMeta objects
11
11
  */
12
12
 
13
-
14
13
  export default {};
@@ -1 +1,3 @@
1
- export * from "./image-meta.js";
1
+ export * from "./drag.js";
2
+ export * from "./drop.js";
3
+ export * from "./image.js";
@@ -1 +1,3 @@
1
- export * from './image-meta.js';
1
+ export * from './drag.js';
2
+ export * from './drop.js';
3
+ export * from './image.js';
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Check if a point lies within a polygon
3
+ *
4
+ * @param {number} xPct - X-coordinate as percentage
5
+ * @param {number} yPct - Y-coordinate as percentage
6
+ * @param {Array} polygon - Array of [x, y] points
7
+ *
8
+ * @returns {boolean} True if the point lies within the polygon
9
+ */
10
+ export function isPointInPolygon(xPct: number, yPct: number, polygon: any[]): boolean;