@abhivarde/svelte-drawer 1.0.0 → 1.0.1
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
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
# Svelte Drawer
|
|
2
2
|
|
|
3
|
-
[](https://www.npmjs.com/package/@abhivarde/svelte-drawer)
|
|
4
|
-

|
|
5
|
-
|
|
6
3
|
A drawer component for Svelte 5, inspired by [Vaul](https://github.com/emilkowalski/vaul).
|
|
7
4
|
|
|
5
|
+
[](https://www.npmjs.com/package/@abhivarde/svelte-drawer) [](https://npmx.dev/package/@abhivarde/svelte-drawer)
|
|
6
|
+
|
|
8
7
|
## Features
|
|
9
8
|
|
|
10
9
|
- ✅ Smooth animations with **gesture-driven dragging** (mouse & touch)
|
|
@@ -22,6 +21,7 @@ A drawer component for Svelte 5, inspired by [Vaul](https://github.com/emilkowal
|
|
|
22
21
|
- ✅ Fully accessible with keyboard navigation
|
|
23
22
|
- ✅ Full **TypeScript** support
|
|
24
23
|
- ✅ Customizable styling with **Tailwind CSS**
|
|
24
|
+
- ✅ **Auto height** — resizes to match content (AI streaming, forms, dynamic lists)
|
|
25
25
|
|
|
26
26
|
## Installation
|
|
27
27
|
|
|
@@ -55,10 +55,10 @@ npm install @abhivarde/svelte-drawer
|
|
|
55
55
|
</Drawer>
|
|
56
56
|
```
|
|
57
57
|
|
|
58
|
-
|
|
59
58
|
### Backdrop Blur
|
|
60
59
|
|
|
61
60
|
Add a premium blur effect to the overlay background:
|
|
61
|
+
|
|
62
62
|
```svelte
|
|
63
63
|
<script>
|
|
64
64
|
import { Drawer, DrawerOverlay, DrawerContent, DrawerHandle } from '@abhivarde/svelte-drawer';
|
|
@@ -69,12 +69,12 @@ Add a premium blur effect to the overlay background:
|
|
|
69
69
|
<Drawer bind:open>
|
|
70
70
|
<!-- Default medium blur -->
|
|
71
71
|
<DrawerOverlay blur class="fixed inset-0 bg-black/40" />
|
|
72
|
-
|
|
72
|
+
|
|
73
73
|
<!-- Or specify blur intensity -->
|
|
74
74
|
<!-- <DrawerOverlay blur="sm" class="fixed inset-0 bg-black/40" /> -->
|
|
75
75
|
<!-- <DrawerOverlay blur="lg" class="fixed inset-0 bg-black/40" /> -->
|
|
76
76
|
<!-- <DrawerOverlay blur="xl" class="fixed inset-0 bg-black/40" /> -->
|
|
77
|
-
|
|
77
|
+
|
|
78
78
|
<DrawerContent class="fixed bottom-0 left-0 right-0 bg-white rounded-t-lg p-4">
|
|
79
79
|
<DrawerHandle class="mb-8" />
|
|
80
80
|
<h2>Blurred Backdrop</h2>
|
|
@@ -84,6 +84,7 @@ Add a premium blur effect to the overlay background:
|
|
|
84
84
|
```
|
|
85
85
|
|
|
86
86
|
**Available blur intensities:**
|
|
87
|
+
|
|
87
88
|
- `blur={true}` or `blur="md"` - Medium blur (default)
|
|
88
89
|
- `blur="sm"` - Small blur
|
|
89
90
|
- `blur="lg"` - Large blur
|
|
@@ -272,6 +273,7 @@ Snap points allow the drawer to rest at predefined heights, creating an iOS-like
|
|
|
272
273
|
### Portal Support
|
|
273
274
|
|
|
274
275
|
Render the drawer in a portal to avoid z-index conflicts in complex layouts.
|
|
276
|
+
|
|
275
277
|
```svelte
|
|
276
278
|
<script>
|
|
277
279
|
import { Drawer, DrawerOverlay, DrawerContent, DrawerHandle } from '@abhivarde/svelte-drawer';
|
|
@@ -301,6 +303,7 @@ Render the drawer in a portal to avoid z-index conflicts in complex layouts.
|
|
|
301
303
|
```
|
|
302
304
|
|
|
303
305
|
**When to use portals:**
|
|
306
|
+
|
|
304
307
|
- Complex layouts with nested z-index contexts
|
|
305
308
|
- Third-party component libraries with fixed positioning
|
|
306
309
|
- Modals inside scrollable containers
|
|
@@ -311,6 +314,7 @@ Render the drawer in a portal to avoid z-index conflicts in complex layouts.
|
|
|
311
314
|
Optional pre-styled header and footer components for quick setup.
|
|
312
315
|
|
|
313
316
|
#### DrawerHeader
|
|
317
|
+
|
|
314
318
|
```svelte
|
|
315
319
|
<script>
|
|
316
320
|
import { Drawer, DrawerOverlay, DrawerContent, DrawerHeader, DrawerHandle } from '@abhivarde/svelte-drawer';
|
|
@@ -322,8 +326,8 @@ Optional pre-styled header and footer components for quick setup.
|
|
|
322
326
|
<Drawer bind:open>
|
|
323
327
|
<DrawerOverlay class="fixed inset-0 bg-black/40" />
|
|
324
328
|
<DrawerContent class="fixed bottom-0 left-0 right-0 bg-white rounded-t-lg">
|
|
325
|
-
<DrawerHeader
|
|
326
|
-
title="Drawer Title"
|
|
329
|
+
<DrawerHeader
|
|
330
|
+
title="Drawer Title"
|
|
327
331
|
description="Optional description text"
|
|
328
332
|
showCloseButton={true}
|
|
329
333
|
/>
|
|
@@ -354,6 +358,7 @@ Optional pre-styled header and footer components for quick setup.
|
|
|
354
358
|
```
|
|
355
359
|
|
|
356
360
|
#### DrawerFooter
|
|
361
|
+
|
|
357
362
|
```svelte
|
|
358
363
|
<script>
|
|
359
364
|
import { Drawer, DrawerOverlay, DrawerContent, DrawerFooter } from '@abhivarde/svelte-drawer';
|
|
@@ -401,6 +406,7 @@ Optional pre-styled header and footer components for quick setup.
|
|
|
401
406
|
### Persistent State
|
|
402
407
|
|
|
403
408
|
Automatically save and restore drawer state across page reloads.
|
|
409
|
+
|
|
404
410
|
```svelte
|
|
405
411
|
<script>
|
|
406
412
|
import { Drawer, DrawerOverlay, DrawerContent } from '@abhivarde/svelte-drawer';
|
|
@@ -408,8 +414,8 @@ Automatically save and restore drawer state across page reloads.
|
|
|
408
414
|
let open = $state(false);
|
|
409
415
|
</script>
|
|
410
416
|
|
|
411
|
-
<Drawer
|
|
412
|
-
bind:open
|
|
417
|
+
<Drawer
|
|
418
|
+
bind:open
|
|
413
419
|
persistState={true}
|
|
414
420
|
persistKey="main-drawer"
|
|
415
421
|
>
|
|
@@ -422,9 +428,10 @@ Automatically save and restore drawer state across page reloads.
|
|
|
422
428
|
```
|
|
423
429
|
|
|
424
430
|
**With snap points:**
|
|
431
|
+
|
|
425
432
|
```svelte
|
|
426
|
-
<Drawer
|
|
427
|
-
bind:open
|
|
433
|
+
<Drawer
|
|
434
|
+
bind:open
|
|
428
435
|
snapPoints={[0.25, 0.5, 0.9]}
|
|
429
436
|
bind:activeSnapPoint
|
|
430
437
|
persistState={true}
|
|
@@ -440,6 +447,7 @@ Automatically save and restore drawer state across page reloads.
|
|
|
440
447
|
```
|
|
441
448
|
|
|
442
449
|
**Clear saved state programmatically:**
|
|
450
|
+
|
|
443
451
|
```svelte
|
|
444
452
|
<script>
|
|
445
453
|
import { clearDrawerState } from '@abhivarde/svelte-drawer';
|
|
@@ -453,6 +461,43 @@ Automatically save and restore drawer state across page reloads.
|
|
|
453
461
|
<button onclick={resetDrawer}>Reset Drawer State</button>
|
|
454
462
|
```
|
|
455
463
|
|
|
464
|
+
### Auto Height (AI & Dynamic Content)
|
|
465
|
+
|
|
466
|
+
Use `autoHeight` on `DrawerContent` when your drawer content changes height at runtime — AI streaming responses, multi-step forms, search results, or any dynamic list.
|
|
467
|
+
|
|
468
|
+
```svelte
|
|
469
|
+
<script>
|
|
470
|
+
import { Drawer, DrawerOverlay, DrawerContent, DrawerHandle } from '@abhivarde/svelte-drawer';
|
|
471
|
+
|
|
472
|
+
let open = $state(false);
|
|
473
|
+
let streamedText = $state('Thinking...');
|
|
474
|
+
|
|
475
|
+
// simulate streaming
|
|
476
|
+
function openAndStream() {
|
|
477
|
+
open = true;
|
|
478
|
+
setTimeout(() => streamedText = 'Here is the AI response. It grows as tokens arrive, and the drawer follows automatically.', 1000);
|
|
479
|
+
}
|
|
480
|
+
</script>
|
|
481
|
+
|
|
482
|
+
<button onclick={openAndStream}>Ask AI</button>
|
|
483
|
+
|
|
484
|
+
<Drawer bind:open>
|
|
485
|
+
<DrawerOverlay class="fixed inset-0 bg-black/40" />
|
|
486
|
+
<DrawerContent autoHeight class="fixed bottom-0 left-0 right-0 bg-white rounded-t-lg p-4">
|
|
487
|
+
<DrawerHandle class="mb-4" />
|
|
488
|
+
<p>{streamedText}</p>
|
|
489
|
+
</DrawerContent>
|
|
490
|
+
</Drawer>
|
|
491
|
+
```
|
|
492
|
+
|
|
493
|
+
**How it works:**
|
|
494
|
+
|
|
495
|
+
- A `ResizeObserver` watches the inner content element
|
|
496
|
+
- When content height changes, it drives the existing `drawerPosition` tween
|
|
497
|
+
- The drawer animates smoothly to the correct height — no magic numbers needed
|
|
498
|
+
- Fully compatible with snap points, portals, and all existing props
|
|
499
|
+
- Zero impact on drawers that don't use `autoHeight` (opt-in, default `false`)
|
|
500
|
+
|
|
456
501
|
## Variants
|
|
457
502
|
|
|
458
503
|
Available variants for `DrawerVariants` component:
|
|
@@ -507,6 +552,7 @@ Content container for the drawer.
|
|
|
507
552
|
|
|
508
553
|
- `class` (string, optional) - CSS classes for styling
|
|
509
554
|
- `trapFocus` (boolean, optional, default: true) - Whether to trap focus inside drawer
|
|
555
|
+
- `autoHeight` (boolean, optional, default: false) - Automatically resize the drawer height to match its content
|
|
510
556
|
|
|
511
557
|
### DrawerHandle
|
|
512
558
|
|
|
@@ -593,4 +639,4 @@ See the [LICENSE](./LICENSE) file for details.
|
|
|
593
639
|
|
|
594
640
|
## Credits
|
|
595
641
|
|
|
596
|
-
Inspired by [Vaul](https://github.com/emilkowalski/vaul) by Emil Kowalski.
|
|
642
|
+
Inspired by [Vaul](https://github.com/emilkowalski/vaul) by Emil Kowalski.
|
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
let {
|
|
17
17
|
class: className = "",
|
|
18
18
|
trapFocus = true,
|
|
19
|
+
autoHeight = false,
|
|
19
20
|
children,
|
|
20
21
|
...restProps
|
|
21
22
|
} = $props();
|
|
@@ -165,8 +166,8 @@
|
|
|
165
166
|
if (!contentElement) return [];
|
|
166
167
|
return Array.from(
|
|
167
168
|
contentElement.querySelectorAll(
|
|
168
|
-
'a[href], button:not([disabled]), textarea:not([disabled]), input:not([disabled]), select:not([disabled]), [tabindex]:not([tabindex="-1"])'
|
|
169
|
-
)
|
|
169
|
+
'a[href], button:not([disabled]), textarea:not([disabled]), input:not([disabled]), select:not([disabled]), [tabindex]:not([tabindex="-1"])',
|
|
170
|
+
),
|
|
170
171
|
) as HTMLElement[];
|
|
171
172
|
}
|
|
172
173
|
|
|
@@ -188,17 +189,22 @@
|
|
|
188
189
|
}
|
|
189
190
|
}
|
|
190
191
|
|
|
192
|
+
let contentInnerRef = $state<HTMLElement | null>(null);
|
|
193
|
+
|
|
191
194
|
$effect(() => {
|
|
192
|
-
if (
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
});
|
|
201
|
-
}
|
|
195
|
+
if (!autoHeight || !contentInnerRef) return;
|
|
196
|
+
|
|
197
|
+
const ro = new ResizeObserver((entries) => {
|
|
198
|
+
const entry = entries[0];
|
|
199
|
+
if (!entry) return;
|
|
200
|
+
const totalHeight = entry.contentRect.height;
|
|
201
|
+
const viewportH = window.innerHeight;
|
|
202
|
+
const pct = Math.max(0, ((viewportH - totalHeight) / viewportH) * 100);
|
|
203
|
+
drawer.drawerPosition.set(pct, { duration: 200 });
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
ro.observe(contentInnerRef);
|
|
207
|
+
return () => ro.disconnect();
|
|
202
208
|
});
|
|
203
209
|
|
|
204
210
|
onMount(() => {
|
|
@@ -221,6 +227,8 @@
|
|
|
221
227
|
ontouchstart={onPointerDown}
|
|
222
228
|
{...restProps}
|
|
223
229
|
>
|
|
224
|
-
{
|
|
230
|
+
<div bind:this={contentInnerRef}>
|
|
231
|
+
{@render children()}
|
|
232
|
+
</div>
|
|
225
233
|
</div>
|
|
226
234
|
{/if}
|
package/dist/types.d.ts
CHANGED