@hkdigital/lib-sveltekit 0.2.4 → 0.2.6

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.
@@ -34,7 +34,7 @@
34
34
 
35
35
  import { EventEmitter } from '../events';
36
36
 
37
- import { DEBUG, INFO, WARN, ERROR, FATAL, NONE, LEVELS } from './constants.js';
37
+ import { DEBUG, INFO, WARN, ERROR, FATAL, LEVELS } from './constants.js';
38
38
 
39
39
  /**
40
40
  * Logger class for consistent logging across services
@@ -8,8 +8,7 @@
8
8
  IDLE,
9
9
  DRAGGING,
10
10
  DRAG_PREVIEW,
11
- DROPPING,
12
- DRAG_DISABLED
11
+ DROPPING
13
12
  } from '../../constants/state-labels/drag-states.js';
14
13
 
15
14
 
@@ -99,14 +98,24 @@
99
98
  let customPreviewSet = $state(false);
100
99
  let elementRect = $state(null);
101
100
 
102
- // Computed state object for CSS classes
103
- let stateObject = $derived({
104
- idle: currentState === IDLE,
105
- dragging: currentState === DRAGGING,
106
- 'drag-preview': currentState === DRAG_PREVIEW,
107
- dropping: currentState === DROPPING,
108
- 'drag-disabled': disabled || !canDrag(item)
109
- });
101
+ // Track if current draggable can drop in the active zone
102
+ let canDropInActiveZone = $derived.by(() => {
103
+ if (currentState !== DRAGGING || !dragState.activeDropZone) return false;
104
+
105
+ const activeZone = dragState.dropZones.get(dragState.activeDropZone);
106
+ return activeZone?.canDrop || false;
107
+ });
108
+
109
+ // Computed state object for CSS classes
110
+ let stateObject = $derived({
111
+ idle: currentState === IDLE,
112
+ dragging: currentState === DRAGGING,
113
+ 'drag-preview': currentState === DRAG_PREVIEW,
114
+ dropping: currentState === DROPPING,
115
+ 'drag-disabled': disabled || !canDrag(item),
116
+ 'can-drop': currentState === DRAGGING && canDropInActiveZone,
117
+ 'cannot-drop': currentState === DRAGGING && dragState.activeDropZone && !canDropInActiveZone
118
+ });
110
119
 
111
120
  let stateClasses = $derived(toStateClasses(stateObject));
112
121
 
@@ -465,6 +474,7 @@ function handleTouchMove(event) {
465
474
  {#if draggingSnippet && showPreview && elementRect}
466
475
  <div
467
476
  data-companion="drag-preview-follower"
477
+ class={stateClasses}
468
478
  style="position: fixed; z-index: 9999; pointer-events: none;"
469
479
  style:left="{previewX}px"
470
480
  style:top="{previewY}px"
@@ -1,187 +1,63 @@
1
1
  <script>
2
- import { onMount, onDestroy } from 'svelte';
3
- import { setupLayerObserver, measureTargetLayer } from './util.js';
4
-
5
2
  /**
3
+ * GridLayers Component
4
+ *
5
+ * Creates a single-cell grid where all children occupy the same space,
6
+ * enabling layered layouts with natural height behavior.
7
+ *
6
8
  * @type {{
7
9
  * base?: string,
8
10
  * bg?: string,
9
11
  * padding?: string,
10
12
  * margin?: string,
11
- * height?: string,
12
13
  * classes?: string,
13
14
  * style?: string,
14
- * cellBase?: string,
15
- * cellBg?: string,
16
- * cellPadding?: string,
17
- * cellMargin?: string,
18
- * cellClasses?: string,
19
- * cellStyle?: string,
20
- * heightFrom?: string|null,
15
+ * overflow?: string,
21
16
  * children: import('svelte').Snippet,
22
- * cellAttrs?: { [attr: string]: any },
23
17
  * [attr: string]: any
24
18
  * }}
25
19
  */
26
20
  const {
27
- // Style
21
+ // Container styles
28
22
  base = '',
29
23
  bg = '',
30
24
  padding = '',
31
25
  margin = '',
32
- height = 'h-full',
33
26
  classes = '',
34
27
  style = '',
35
- cellBase = '',
36
- cellBg = '',
37
- cellPadding = '',
38
- cellMargin = '',
39
- cellClasses = '',
40
- cellStyle = '',
41
-
42
- // Behavior
43
- heightFrom = null,
28
+ overflow = '',
44
29
 
45
- // Props
46
- cellAttrs = {},
30
+ // Content
47
31
  children,
48
32
 
49
33
  // Attributes
50
34
  ...attrs
51
35
  } = $props();
52
36
 
53
- // Component state
54
- let gridContainer = $state(null);
55
- let gridContent = $state(null);
56
- let calculatedHeight = $state(0);
57
- let observer = $state(null);
58
- let targetLayer = $state(null);
59
- let isFirstRender = $state(heightFrom !== null); // Start with true if heightFrom is provided
60
- let preCalculatedHeight = $state(0);
61
-
62
- // Derived container style that updates reactively when dependencies change
37
+ // Build the inline style
63
38
  let containerStyle = $derived.by(() => {
64
- const styles = [];
39
+ const styles = ['grid-template: 1fr / 1fr;'];
65
40
 
66
41
  if (style) {
67
42
  styles.push(style);
68
43
  }
69
44
 
70
- if (heightFrom && calculatedHeight > 0) {
71
- styles.push(`height: ${calculatedHeight}px;`);
72
- }
73
-
74
45
  return styles.join(' ');
75
46
  });
76
-
77
- /**
78
- * Handler for height changes detected by the observer
79
- * @param {number} newHeight - The new calculated height
80
- */
81
- function handleHeightChange(newHeight) {
82
- calculatedHeight = newHeight;
83
- }
84
-
85
- /**
86
- * Initialize height measurement and observation
87
- */
88
- function initializeHeightTracking() {
89
- if (!heightFrom || !gridContent) return;
90
-
91
- // Measure the layer initially
92
- const { element, height } = measureTargetLayer(gridContent, heightFrom);
93
-
94
- if (element) {
95
- targetLayer = element;
96
- calculatedHeight = height;
97
-
98
- // Setup observer for future changes
99
- observer = setupLayerObserver(element, handleHeightChange);
100
- }
101
- }
102
-
103
- // Initialize on mount with the two-pass rendering approach
104
- onMount(() => {
105
- if (heightFrom) {
106
- // First render: measure invisibly
107
- requestAnimationFrame(() => {
108
- if (gridContent) {
109
- const { element, height } = measureTargetLayer(gridContent, heightFrom);
110
-
111
- if (element) {
112
- targetLayer = element;
113
- preCalculatedHeight = height;
114
-
115
- // Second render: show with correct height
116
- requestAnimationFrame(() => {
117
- calculatedHeight = preCalculatedHeight;
118
- isFirstRender = false;
119
-
120
- // Setup observer for future changes
121
- observer = setupLayerObserver(element, handleHeightChange);
122
- });
123
- } else {
124
- // No target layer found, just show the component
125
- isFirstRender = false;
126
- }
127
- } else {
128
- // No grid content, just show the component
129
- isFirstRender = false;
130
- }
131
- });
132
- } else {
133
- // No heightFrom, no need for measurement
134
- isFirstRender = false;
135
- }
136
- });
137
-
138
- // Effect to re-setup observer when either the target layer or heightFrom changes
139
- $effect(() => {
140
- // Only handle changes after initial setup
141
- if (!isFirstRender && heightFrom && gridContent && !observer) {
142
- initializeHeightTracking();
143
- }
144
- });
145
-
146
- // Clean up on destroy
147
- onDestroy(() => {
148
- if (observer) {
149
- observer.disconnect();
150
- observer = null;
151
- }
152
- });
153
-
154
47
  </script>
155
48
 
156
49
  <div
157
50
  data-component="grid-layers"
158
- bind:this={gridContainer}
159
- class="relative {isFirstRender ? 'invisible' : ''} {base} {bg} {heightFrom ? '' : height} {classes} {margin} {padding}"
51
+ class="grid {base} {bg} {classes} {margin} {padding} {overflow}"
160
52
  style={containerStyle}
161
53
  {...attrs}
162
54
  >
163
- <div
164
- data-section="grid"
165
- bind:this={gridContent}
166
- class="absolute inset-0 grid {cellBase} {cellBg} {cellPadding} {cellMargin} {cellClasses}"
167
- style={cellStyle}
168
- {...cellAttrs}
169
- >
170
- {@render children()}
171
- </div>
55
+ {@render children()}
172
56
  </div>
173
57
 
174
58
  <style>
175
- /* All children of the layer share the same grid area
176
- but aren't absolutely positioned */
177
- [data-section='grid'] {
178
- grid-template-columns: 1fr;
179
- grid-template-rows: 1fr;
180
- }
181
-
182
- [data-section='grid'] > :global(*) {
183
- grid-column: 1;
184
- grid-row: 1;
185
- z-index: 0; /* Base z-index to allow explicit stacking order */
59
+ /* All direct children occupy the same grid area */
60
+ div > :global(*) {
61
+ grid-area: 1 / 1;
186
62
  }
187
63
  </style>
@@ -7,20 +7,10 @@ type GridLayers = {
7
7
  bg?: string;
8
8
  padding?: string;
9
9
  margin?: string;
10
- height?: string;
11
10
  classes?: string;
12
11
  style?: string;
13
- cellBase?: string;
14
- cellBg?: string;
15
- cellPadding?: string;
16
- cellMargin?: string;
17
- cellClasses?: string;
18
- cellStyle?: string;
19
- heightFrom?: string;
12
+ overflow?: string;
20
13
  children: Snippet<[]>;
21
- cellAttrs?: {
22
- [attr: string]: any;
23
- };
24
14
  }>): void;
25
15
  };
26
16
  declare const GridLayers: import("svelte").Component<{
@@ -29,18 +19,8 @@ declare const GridLayers: import("svelte").Component<{
29
19
  bg?: string;
30
20
  padding?: string;
31
21
  margin?: string;
32
- height?: string;
33
22
  classes?: string;
34
23
  style?: string;
35
- cellBase?: string;
36
- cellBg?: string;
37
- cellPadding?: string;
38
- cellMargin?: string;
39
- cellClasses?: string;
40
- cellStyle?: string;
41
- heightFrom?: string | null;
24
+ overflow?: string;
42
25
  children: import("svelte").Snippet;
43
- cellAttrs?: {
44
- [attr: string]: any;
45
- };
46
26
  }, {}, "">;
@@ -0,0 +1,372 @@
1
+ <script>
2
+ import { onMount, onDestroy } from 'svelte';
3
+ import { setupLayerObserver, measureTargetLayer } from './util.js';
4
+
5
+ /**
6
+ * # GridLayers Component
7
+ *
8
+ * A Svelte 5 component that creates a layered grid layout where all
9
+ * children occupy the same grid area, allowing for overlapping content with
10
+ * precise positioning control.
11
+ *
12
+ * ## Overview
13
+ *
14
+ * GridLayers uses CSS Grid to stack multiple elements in the same grid cell
15
+ * (1x1 grid), enabling layered layouts without absolute positioning on the
16
+ * children. This approach maintains the natural flow and sizing behavior of
17
+ * grid items while allowing them to overlap.
18
+ *
19
+ * ## Height Control
20
+ *
21
+ * The component offers two methods for controlling height:
22
+ *
23
+ * ### 1. Fixed Height (default)
24
+ *
25
+ * Use the `height` prop with Tailwind classes:
26
+ * ```svelte
27
+ * <GridLayers height="h-[500px]">
28
+ * <!-- content -->
29
+ * </GridLayers>
30
+ * ```
31
+ *
32
+ * ### 2. Dynamic Height
33
+ * Use the `heightFrom` prop to make the container's height match a specific
34
+ * child layer:
35
+ *
36
+ * ```svelte
37
+ * <GridLayers heightFrom="content">
38
+ * <div data-layer="content">
39
+ * <!-- This layer's height determines the container height -->
40
+ * </div>
41
+ * <div data-layer="overlay">
42
+ * <!-- Other layers adapt to the container -->
43
+ * </div>
44
+ * </GridLayers>
45
+ * ```
46
+ *
47
+ * The `heightFrom` value should match a child's `data-layer` attribute.
48
+ * The component will:
49
+ * - Initially render invisibly to measure the target layer
50
+ * - Apply the measured height to the container
51
+ * - Watch for changes and update automatically
52
+ *
53
+ * ## Positioning Layers
54
+ *
55
+ * Each child element can be positioned within the grid cell using Tailwind's
56
+ * alignment utilities:
57
+ *
58
+ * ### justify-self (Horizontal Alignment)
59
+ * - `justify-self-start` - Align to the left
60
+ * - `justify-self-center` - Center horizontally
61
+ * - `justify-self-end` - Align to the right
62
+ * - `justify-self-stretch` - Stretch to full width (default)
63
+ *
64
+ * ### self (Vertical Alignment)
65
+ * - `self-start` - Align to the top
66
+ * - `self-center` - Center vertically
67
+ * - `self-end` - Align to the bottom
68
+ * - `self-stretch` - Stretch to full height (default)
69
+ *
70
+ * ### Combining Positions
71
+ * ```svelte
72
+ * <GridLayers height="h-[400px]">
73
+ * <div class="justify-self-start self-start">Top Left</div>
74
+ * <div class="justify-self-center self-center">Centered</div>
75
+ * <div class="justify-self-end self-end">Bottom Right</div>
76
+ * </GridLayers>
77
+ * ```
78
+ *
79
+ * ### Fine-tuning with Margins
80
+ * For precise positioning adjustments, use margins:
81
+ * ```svelte
82
+ * <div class="justify-self-end self-end mr-4 mb-4">
83
+ * <!-- Positioned at bottom-right with 1rem spacing -->
84
+ * </div>
85
+ * ```
86
+ *
87
+ * ## Technical Implementation
88
+ *
89
+ * ### The Grid Container
90
+ * The inner grid container uses `absolute inset-0` which:
91
+ * - Positions it absolutely within the relative parent
92
+ * - `inset-0` is shorthand for `top: 0, right: 0, bottom: 0, left: 0`
93
+ * - Makes the grid fill the entire parent container
94
+ * - Ensures the grid respects the parent's dimensions (fixed or dynamic)
95
+ *
96
+ * This approach creates a stable positioning context while maintaining the
97
+ * parent's flow in the document.
98
+ *
99
+ * ### Grid Structure
100
+ * All children are assigned to the same grid cell:
101
+ * ```css
102
+ * grid-template-columns: 1fr;
103
+ * grid-template-rows: 1fr;
104
+ * grid-column: 1;
105
+ * grid-row: 1;
106
+ * ```
107
+ *
108
+ * ## Overflow Behavior
109
+ *
110
+ * When a layer's content exceeds the container bounds:
111
+ *
112
+ * ### Default Behavior
113
+ * - Content will overflow and be visible outside the container
114
+ * - This can break layouts or create unwanted scrollbars
115
+ *
116
+ * ### Controlling Overflow
117
+ * Add overflow utilities to the container:
118
+ * ```svelte
119
+ * <!-- Hide overflow -->
120
+ * <GridLayers classes="overflow-hidden">
121
+ *
122
+ * <!-- Scroll if needed -->
123
+ * <GridLayers classes="overflow-auto">
124
+ *
125
+ * <!-- Scroll specific layer -->
126
+ * <GridLayers>
127
+ * <div class="overflow-auto">
128
+ * <!-- Scrollable content -->
129
+ * </div>
130
+ * </GridLayers>
131
+ * ```
132
+ *
133
+ * ### Best Practices for Overflow
134
+ * 1. Use `overflow-hidden` on the container when layers should be clipped
135
+ * 2. Apply `overflow-auto` to specific layers that need scrolling
136
+ * 3. Consider using `max-h-*` classes on content layers
137
+ * 4. Test with different content sizes to ensure proper behavior
138
+ *
139
+ * ## Z-Index Stacking
140
+ *
141
+ * Layers have a base `z-index: 0` and stack in DOM order. Control stacking with:
142
+ * ```svelte
143
+ * <div class="z-10">Top layer</div>
144
+ * <div class="z-0">Base layer</div>
145
+ * <div class="z-20">Topmost layer</div>
146
+ * ```
147
+ *
148
+ * ## Common Patterns
149
+ *
150
+ * ### Header/Content/Footer
151
+ * ```svelte
152
+ * <GridLayers heightFrom="content">
153
+ * <div data-layer="header" class="self-start">
154
+ * <header>Fixed header</header>
155
+ * </div>
156
+ * <div data-layer="content" class="self-center">
157
+ * <main>Dynamic content</main>
158
+ * </div>
159
+ * <div data-layer="footer" class="self-end">
160
+ * <footer>Fixed footer</footer>
161
+ * </div>
162
+ * </GridLayers>
163
+ * ```
164
+ *
165
+ * ### Centered Overlay
166
+ * ```svelte
167
+ * <GridLayers height="h-screen">
168
+ * <div class="z-0">
169
+ * <img src="background.jpg" class="w-full h-full object-cover" />
170
+ * </div>
171
+ * <div class="z-10 justify-self-center self-center">
172
+ * <div class="bg-white p-8 rounded shadow-lg">
173
+ * Centered content over background
174
+ * </div>
175
+ * </div>
176
+ * </GridLayers>
177
+ * ```
178
+ *
179
+ * ### Corner Badges
180
+ * ```svelte
181
+ * <GridLayers height="h-64">
182
+ * <div class="justify-self-end self-start m-4 z-10">
183
+ * <span class="badge">New</span>
184
+ * </div>
185
+ * <div>
186
+ * Main content
187
+ * </div>
188
+ * </GridLayers>
189
+ * ```
190
+ */
191
+
192
+ /**
193
+ * @type {{
194
+ * base?: string,
195
+ * bg?: string,
196
+ * padding?: string,
197
+ * margin?: string,
198
+ * height?: string,
199
+ * classes?: string,
200
+ * style?: string,
201
+ * cellBase?: string,
202
+ * cellBg?: string,
203
+ * cellPadding?: string,
204
+ * cellMargin?: string,
205
+ * cellClasses?: string,
206
+ * cellStyle?: string,
207
+ * heightFrom?: string|null,
208
+ * children: import('svelte').Snippet,
209
+ * cellAttrs?: { [attr: string]: any },
210
+ * [attr: string]: any
211
+ * }}
212
+ */
213
+ const {
214
+ // Style
215
+ base = '',
216
+ bg = '',
217
+ padding = '',
218
+ margin = '',
219
+ height = 'h-full',
220
+ classes = '',
221
+ style = '',
222
+ cellBase = '',
223
+ cellBg = '',
224
+ cellPadding = '',
225
+ cellMargin = '',
226
+ cellClasses = '',
227
+ cellStyle = '',
228
+
229
+ // Behavior
230
+ heightFrom = null,
231
+
232
+ // Props
233
+ cellAttrs = {},
234
+ children,
235
+
236
+ // Attributes
237
+ ...attrs
238
+ } = $props();
239
+
240
+ // Component state
241
+ let gridContent = $state(null);
242
+ let calculatedHeight = $state(0);
243
+ let observer = $state(null);
244
+
245
+ // Start with true if heightFrom is provided
246
+ let isFirstRender = $state(heightFrom !== null);
247
+
248
+ let preCalculatedHeight = $state(0);
249
+
250
+ // Derived container style that updates reactively when dependencies change
251
+ let containerStyle = $derived.by(() => {
252
+ const styles = [];
253
+
254
+ if (style) {
255
+ styles.push(style);
256
+ }
257
+
258
+ if (heightFrom && calculatedHeight > 0) {
259
+ styles.push(`height: ${calculatedHeight}px;`);
260
+ }
261
+
262
+ return styles.join(' ');
263
+ });
264
+
265
+ /**
266
+ * Handler for height changes detected by the observer
267
+ * @param {number} newHeight - The new calculated height
268
+ */
269
+ function handleHeightChange(newHeight) {
270
+ calculatedHeight = newHeight;
271
+ }
272
+
273
+ /**
274
+ * Initialize height measurement and observation
275
+ */
276
+ function initializeHeightTracking() {
277
+ if (!heightFrom || !gridContent) return;
278
+
279
+ // Measure the layer initially
280
+ const { element, height } = measureTargetLayer(gridContent, heightFrom);
281
+
282
+ if (element) {
283
+ calculatedHeight = height;
284
+
285
+ // Setup observer for future changes
286
+ observer = setupLayerObserver(element, handleHeightChange);
287
+ }
288
+ }
289
+
290
+ // Initialize on mount with the two-pass rendering approach
291
+ onMount(() => {
292
+ if (heightFrom) {
293
+ // First render: measure invisibly
294
+ requestAnimationFrame(() => {
295
+ if (gridContent) {
296
+ const { element, height } = measureTargetLayer(gridContent, heightFrom);
297
+
298
+ if (element) {
299
+ preCalculatedHeight = height;
300
+
301
+ // Second render: show with correct height
302
+ requestAnimationFrame(() => {
303
+ calculatedHeight = preCalculatedHeight;
304
+ isFirstRender = false;
305
+
306
+ // Setup observer for future changes
307
+ observer = setupLayerObserver(element, handleHeightChange);
308
+ });
309
+ } else {
310
+ // No target layer found, just show the component
311
+ isFirstRender = false;
312
+ }
313
+ } else {
314
+ // No grid content, just show the component
315
+ isFirstRender = false;
316
+ }
317
+ });
318
+ } else {
319
+ // No heightFrom, no need for measurement
320
+ isFirstRender = false;
321
+ }
322
+ });
323
+
324
+ // Effect to re-setup observer when either the target layer or heightFrom changes
325
+ $effect(() => {
326
+ // Only handle changes after initial setup
327
+ if (!isFirstRender && heightFrom && gridContent && !observer) {
328
+ initializeHeightTracking();
329
+ }
330
+ });
331
+
332
+ // Clean up on destroy
333
+ onDestroy(() => {
334
+ if (observer) {
335
+ observer.disconnect();
336
+ observer = null;
337
+ }
338
+ });
339
+
340
+ </script>
341
+
342
+ <div
343
+ data-component="grid-layers"
344
+ class="relative {isFirstRender ? 'invisible' : ''} {base} {bg} {heightFrom ? '' : height} {classes} {margin} {padding}"
345
+ style={containerStyle}
346
+ {...attrs}
347
+ >
348
+ <div
349
+ data-section="grid"
350
+ bind:this={gridContent}
351
+ class="absolute inset-0 grid {cellBase} {cellBg} {cellPadding} {cellMargin} {cellClasses}"
352
+ style={cellStyle}
353
+ {...cellAttrs}
354
+ >
355
+ {@render children()}
356
+ </div>
357
+ </div>
358
+
359
+ <style>
360
+ /* All children of the layer share the same grid area
361
+ but aren't absolutely positioned */
362
+ [data-section='grid'] {
363
+ grid-template-columns: 1fr;
364
+ grid-template-rows: 1fr;
365
+ }
366
+
367
+ [data-section='grid'] > :global(*) {
368
+ grid-column: 1;
369
+ grid-row: 1;
370
+ z-index: 0; /* Base z-index to allow explicit stacking order */
371
+ }
372
+ </style>
@@ -3,7 +3,7 @@
3
3
 
4
4
  /**
5
5
  * @type {{
6
- * imageMeta?: import('@hkdigital/lib-sveltekit/config/typedef.js').ImageMeta | import('@hkdigital/lib-sveltekit/config/typedef.js').ImageMeta[],
6
+ * imageMeta?: import('../../typedef').ImageSource,
7
7
  * slideDuration?: number,
8
8
  * nextSlideLabel?: string,
9
9
  * presenter?: { gotoSlide: (name: string) => void, getCurrentSlideName: () => string },
@@ -53,7 +53,7 @@
53
53
  });
54
54
  </script>
55
55
 
56
- <div class="absolute inset-0" class:invisible={!show}>
56
+ <div class="justify-self-stretch self-stretch grid" class:invisible={!show}>
57
57
  <ImageBox
58
58
  {imageMeta}
59
59
  {fit}
@@ -3,7 +3,7 @@ type ImageSlide = {
3
3
  $on?(type: string, callback: (e: any) => void): () => void;
4
4
  $set?(props: Partial<{
5
5
  [attr: string]: any;
6
- imageMeta?: any;
6
+ imageMeta?: ImageSource;
7
7
  slideDuration?: number;
8
8
  nextSlideLabel?: string;
9
9
  presenter?: {
@@ -20,7 +20,7 @@ type ImageSlide = {
20
20
  };
21
21
  declare const ImageSlide: import("svelte").Component<{
22
22
  [attr: string]: any;
23
- imageMeta?: any | any[];
23
+ imageMeta?: import("../../typedef").ImageSource;
24
24
  slideDuration?: number;
25
25
  nextSlideLabel?: string;
26
26
  presenter?: {
@@ -109,8 +109,9 @@
109
109
  });
110
110
  </script>
111
111
 
112
- <GridLayers data-component="presenter" {classes}>
112
+ <GridLayers data-feature="presenter" {classes}>
113
113
  <div
114
+ data-layer="layer1"
114
115
  style:z-index={presenter.layerA.z}
115
116
  style:visibility={presenter.layerA.visible ? 'visible' : 'hidden'}
116
117
  inert={presenter.busy}
@@ -122,6 +123,7 @@
122
123
  </div>
123
124
 
124
125
  <div
126
+ data-layer="layer2"
125
127
  style:z-index={presenter.layerB.z}
126
128
  style:visibility={presenter.layerB.visible ? 'visible' : 'hidden'}
127
129
  inert={presenter.busy}
@@ -133,7 +135,7 @@
133
135
  </div>
134
136
 
135
137
  {#if loadingSnippet && presenter.loadingSpinner}
136
- <div class="h-full w-full" style="z-index:20;">
138
+ <div class="justify-self-stretch self-stretch overflow-hidden grid z-[20]">
137
139
  {@render loadingSnippet()}
138
140
  </div>
139
141
  {/if}
@@ -7,10 +7,9 @@
7
7
  }
8
8
 
9
9
  [data-component='draggable']:not(.state-dragging):not(.state-drag-disabled) {
10
- cursor: grab; /* Open hand when hovering draggable items */
10
+ cursor: grab; /* Open hand when hovering draggable items */
11
11
  }
12
12
 
13
-
14
13
  /*[data-component='draggable']:active {
15
14
  cursor: grabbing;
16
15
  }*/
@@ -42,6 +41,20 @@
42
41
  filter: grayscale(0.5);
43
42
  }
44
43
 
44
+ /* Preview follower */
45
+
46
+ [data-companion='drag-preview-follower'] {
47
+ /*box-shadow: 0 4px 10px rgba(0, 0, 0, 0.1);*/
48
+ }
49
+
50
+ [data-companion='drag-preview-follower'].state-can-drop {
51
+ box-shadow: 0 0 12px rgba(74, 222, 128, 0.5);
52
+ }
53
+
54
+ [data-companion='drag-preview-follower'].state-cannot-drop {
55
+ box-shadow: 0 0 12px rgba(239, 68, 68, 0.5);
56
+ }
57
+
45
58
  /* Animations */
46
59
  @keyframes drop-finish {
47
60
  0% {
@@ -57,8 +70,4 @@
57
70
  opacity: 1;
58
71
  }
59
72
  }
60
-
61
- & [data-companion="drag-preview-follower"] {
62
- box-shadow: 0 4px 10px rgba(0,0,0,0.1);
63
- }
64
73
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hkdigital/lib-sveltekit",
3
- "version": "0.2.4",
3
+ "version": "0.2.6",
4
4
  "author": {
5
5
  "name": "HKdigital",
6
6
  "url": "https://hkdigital.nl"
@@ -1,167 +0,0 @@
1
- <script>
2
- /**
3
- * Grid Layers Component
4
- *
5
- * A component that creates a single-cell grid where all children exist
6
- * in the same grid cell, allowing them to be positioned independently
7
- * and stacked on top of each other. Perfect for complex layouts like
8
- * overlaying text on images, card stacks, positioning UI elements, etc.
9
- *
10
- * Each child can use grid positioning properties (justify-self-*, self-*)
11
- * for precise placement. Children can control stacking order with z-index.
12
- *
13
- * @example Basic usage with 9-position grid
14
- * ```html
15
- * <GridLayers classes="border w-[500px] h-[500px]">
16
- * <!-- Top Row -->
17
- * <div class="justify-self-start self-start">
18
- * <div class="bg-blue-500 w-[100px] h-[100px]">
19
- * Top Left
20
- * </div>
21
- * </div>
22
- * <div class="justify-self-center self-start">
23
- * <div class="bg-blue-300 w-[100px] h-[100px]">
24
- * Top Center
25
- * </div>
26
- * </div>
27
- * <div class="justify-self-end self-start">
28
- * <div class="bg-blue-500 w-[100px] h-[100px]">
29
- * Top Right
30
- * </div>
31
- * </div>
32
- *
33
- * <!-- Middle Row -->
34
- * <div class="justify-self-start self-center">
35
- * <div class="bg-green-500 w-[100px] h-[100px]">
36
- * Middle Left
37
- * </div>
38
- * </div>
39
- * <div class="justify-self-center self-center">
40
- * <div class="bg-green-300 w-[100px] h-[100px]">
41
- * Middle Center
42
- * </div>
43
- * </div>
44
- * <div class="justify-self-end self-center">
45
- * <div class="bg-green-500 w-[100px] h-[100px]">
46
- * Middle Right
47
- * </div>
48
- * </div>
49
- *
50
- * <!-- Bottom Row -->
51
- * <div class="justify-self-start self-end">
52
- * <div class="bg-red-500 w-[100px] h-[100px]">
53
- * Bottom Left
54
- * </div>
55
- * </div>
56
- * <div class="justify-self-center self-end">
57
- * <div class="bg-red-300 w-[100px] h-[100px]">
58
- * Bottom Center
59
- * </div>
60
- * </div>
61
- * <div class="justify-self-end self-end">
62
- * <div class="bg-red-500 w-[100px] h-[100px]">
63
- * Bottom Right
64
- * </div>
65
- * </div>
66
- * </GridLayers>
67
- * ```
68
- *
69
- * @example Text over image
70
- * ```html
71
- * <GridLayers classes="w-full h-[300px]">
72
- * <!-- Background image layer -->
73
- * <div class="justify-self-stretch self-stretch z-0">
74
- * <img
75
- * src="/images/landscape.jpg"
76
- * alt="Landscape"
77
- * class="w-full h-full object-cover"
78
- * />
79
- * </div>
80
- *
81
- * <!-- Text overlay layer -->
82
- * <div class="justify-self-center self-center z-10">
83
- * <div class="bg-black/50 p-16up text-white
84
- * font-ui rounded-md">
85
- * <h2 class="text-2xl">Explore Nature</h2>
86
- * <p>Discover the beauty of the outdoors</p>
87
- * </div>
88
- * </div>
89
- * </GridLayers>
90
- * ```
91
- */
92
-
93
- /**
94
- * @type {{
95
- * base?: string,
96
- * bg?: string,
97
- * padding?: string,
98
- * margin?: string,
99
- * height?: string,
100
- * classes?: string,
101
- * style?: string,
102
- * cellBase?: string,
103
- * cellBg?: string,
104
- * cellPadding?: string,
105
- * cellMargin?: string,
106
- * cellClasses?: string,
107
- * cellStyle?: string,
108
- * children: import('svelte').Snippet,
109
- * cellAttrs?: { [attr: string]: * },
110
- * [attr: string]: any
111
- * }}
112
- */
113
- const {
114
- // Style
115
- base,
116
- bg,
117
- padding,
118
- margin,
119
- height,
120
- classes,
121
- style,
122
- cellBase,
123
- cellBg,
124
- cellPadding,
125
- cellMargin,
126
- cellClasses,
127
- cellStyle,
128
-
129
- cellAttrs,
130
-
131
- // Snippets
132
- children,
133
-
134
- // Attributes
135
- ...attrs
136
- } = $props();
137
- </script>
138
-
139
- <div
140
- data-component="grid-layers"
141
- class="relative {base} {bg} {height} {classes} {margin} {padding}"
142
- {style}
143
- {...attrs}
144
- >
145
- <div
146
- data-section="grid"
147
- class="absolute inset-0 grid {cellBase} {cellBg} {cellPadding} {cellMargin} {cellClasses}"
148
- style={cellStyle}
149
- >
150
- {@render children()}
151
- </div>
152
- </div>
153
-
154
- <style>
155
- /* All children of the layer share the same grid area
156
- but aren't absolutely positioned */
157
- [data-section='grid'] {
158
- grid-template-columns: 1fr;
159
- grid-template-rows: 1fr;
160
- }
161
-
162
- [data-section='grid'] > :global(*) {
163
- grid-column: 1;
164
- grid-row: 1;
165
- z-index: 0; /* Base z-index to allow explicit stacking order */
166
- }
167
- </style>