@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
- [![npm version](https://img.shields.io/npm/v/@abhivarde/svelte-drawer)](https://www.npmjs.com/package/@abhivarde/svelte-drawer)
4
- ![GitHub Repo Views](https://gitviews.com/repo/AbhiVarde/svelte-drawer.svg)
5
-
6
3
  A drawer component for Svelte 5, inspired by [Vaul](https://github.com/emilkowalski/vaul).
7
4
 
5
+ [![](https://img.shields.io/badge/npm-@abhivarde/svelte--drawer-000?style=flat&logo=npm&logoColor=white)](https://www.npmjs.com/package/@abhivarde/svelte-drawer) [![](https://img.shields.io/badge/npmx-@abhivarde/svelte--drawer-000?style=flat&logo=node.js&logoColor=white)](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 (drawer.open && trapFocus && contentElement) {
193
- tick().then(() => {
194
- const focusable = getFocusableElements();
195
- if (focusable[0]) {
196
- focusable[0].focus({ preventScroll: true });
197
- } else {
198
- contentElement?.focus({ preventScroll: true });
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
- {@render children()}
230
+ <div bind:this={contentInnerRef}>
231
+ {@render children()}
232
+ </div>
225
233
  </div>
226
234
  {/if}
@@ -1,6 +1,7 @@
1
1
  declare const DrawerContent: import("svelte").Component<{
2
2
  class?: string;
3
3
  trapFocus?: boolean;
4
+ autoHeight?: boolean;
4
5
  children: any;
5
6
  } & Record<string, any>, {}, "">;
6
7
  type DrawerContent = ReturnType<typeof DrawerContent>;
package/dist/types.d.ts CHANGED
@@ -15,6 +15,7 @@ export interface DrawerProps {
15
15
  export interface DrawerContentProps {
16
16
  class?: string;
17
17
  trapFocus?: boolean;
18
+ autoHeight?: boolean;
18
19
  }
19
20
  export interface DrawerOverlayProps {
20
21
  class?: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@abhivarde/svelte-drawer",
3
- "version": "1.0.0",
3
+ "version": "1.0.1",
4
4
  "description": "A drawer component for Svelte 5, inspired by Vaul",
5
5
  "author": "Abhi Varde",
6
6
  "license": "MIT",