@abhivarde/svelte-drawer 1.0.4 → 1.0.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.
package/README.md CHANGED
@@ -2,6 +2,7 @@
2
2
 
3
3
  A drawer component for Svelte 5, inspired by [Vaul](https://github.com/emilkowalski/vaul).
4
4
 
5
+ [![skills.sh](https://skills.sh/b/AbhiVarde/svelte-drawer)](https://skills.sh/AbhiVarde/svelte-drawer)
5
6
  [![](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
7
 
7
8
  ## Features
@@ -160,6 +161,24 @@ Add a premium blur effect to the overlay background:
160
161
  </Drawer>
161
162
  ```
162
163
 
164
+ ### Close Threshold
165
+
166
+ Control how far the user needs to drag before the drawer dismisses.
167
+
168
+ ```svelte
169
+ <!-- easier to dismiss (short drag) -->
170
+ <Drawer bind:open closeThreshold={0.15}>
171
+ ...
172
+ </Drawer>
173
+
174
+ <!-- harder to dismiss (long drag required) -->
175
+ <Drawer bind:open closeThreshold={0.5}>
176
+ ...
177
+ </Drawer>
178
+ ```
179
+
180
+ Values range from `0` to `1`. Default is `0.3` (30% of the viewport).
181
+
163
182
  ### Using Variants
164
183
 
165
184
  ```svelte
@@ -463,31 +482,34 @@ Automatically save and restore drawer state across page reloads.
463
482
 
464
483
  ### Auto Height (AI & Dynamic Content)
465
484
 
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.
485
+ Use `autoHeight` on `DrawerContent` when your drawer content changes height at runtime like AI streaming responses, multi-step forms, search results, or dynamic lists.
467
486
 
468
487
  ```svelte
469
488
  <script>
470
- import { Drawer, DrawerOverlay, DrawerContent, DrawerHandle } from '@abhivarde/svelte-drawer';
489
+ import {
490
+ Drawer,
491
+ DrawerOverlay,
492
+ DrawerContent,
493
+ DrawerHandle
494
+ } from '@abhivarde/svelte-drawer';
471
495
 
472
496
  let open = $state(false);
473
497
  let streaming = $state(false);
474
498
  let text = $state('');
475
499
 
476
- const lines = [
477
- 'Sure! Here is what autoHeight does.',
478
- '\n\nIt watches your content as it changes.',
479
- '\n\nWhen content grows, the drawer follows automatically.',
480
- '\n\nNo magic numbers. No hardcoded heights. Just works.',
481
- ];
482
-
483
500
  async function simulate() {
484
501
  open = true;
485
502
  streaming = true;
486
503
  text = '';
487
- for (const line of lines) {
488
- await new Promise(r => setTimeout(r, 500));
489
- text += line;
504
+
505
+ const content =
506
+ 'Sure! Here is what autoHeight does. It watches your content with a ResizeObserver. When content grows, the drawer follows automatically. No magic numbers, no hardcoded heights, it just works smoothly with streaming UI like AI chat apps.';
507
+
508
+ for (let i = 0; i < content.length; i++) {
509
+ text += content[i];
510
+ await new Promise(r => setTimeout(r, 18));
490
511
  }
512
+
491
513
  streaming = false;
492
514
  }
493
515
  </script>
@@ -496,12 +518,22 @@ Use `autoHeight` on `DrawerContent` when your drawer content changes height at r
496
518
 
497
519
  <Drawer bind:open>
498
520
  <DrawerOverlay />
499
- <DrawerContent autoHeight class="bg-gray-100 flex flex-col rounded-t-[10px] fixed bottom-0 left-0 right-0 outline-none">
521
+
522
+ <DrawerContent
523
+ autoHeight
524
+ class="bg-gray-100 flex flex-col rounded-t-[10px] fixed bottom-0 left-0 right-0 outline-none"
525
+ >
500
526
  <div class="p-4 bg-white rounded-t-[10px]">
501
527
  <DrawerHandle class="mb-8" />
502
- <p class="font-medium mb-2 text-gray-900">AI Response</p>
528
+
529
+ <p class="font-medium mb-2 text-gray-900">
530
+ AI Response
531
+ </p>
532
+
503
533
  {#if text}
504
- <p class="text-sm text-gray-600 whitespace-pre-wrap leading-relaxed">{text}{streaming ? '▌' : ''}</p>
534
+ <p class="text-sm text-gray-600 leading-relaxed">
535
+ {text}{streaming ? '▌' : ''}
536
+ </p>
505
537
  {/if}
506
538
  </div>
507
539
  </DrawerContent>
@@ -512,8 +544,9 @@ Use `autoHeight` on `DrawerContent` when your drawer content changes height at r
512
544
 
513
545
  - `height: auto` is applied to the drawer when `autoHeight` is true
514
546
  - The drawer grows and shrinks naturally as content changes
515
- - The slide-in animation is unaffected since it runs via CSS transform separately
516
- - Fully compatible with snap points, portals, and all existing props
547
+ - Perfect for AI streaming, dynamic lists, forms, and async content
548
+ - The slide animation remains smooth since it uses CSS transforms separately
549
+ - Fully compatible with snap points, portals, and existing props
517
550
  - Zero impact on drawers that do not use `autoHeight` (opt-in, default `false`)
518
551
 
519
552
  ## Variants
@@ -544,6 +577,7 @@ Main wrapper component that manages drawer state and animations.
544
577
  - `onOpenChange` (function, optional) - Callback when open state changes
545
578
  - `direction` ('bottom' | 'top' | 'left' | 'right', default: 'bottom') - Direction from which drawer slides
546
579
  - `closeOnEscape` (boolean, optional, default: true) - Whether Escape key closes the drawer
580
+ - `closeThreshold` (number, optional, default: 0.3) - How far the user must drag to dismiss (0–1). Lower = easier to close, higher = requires a longer drag.
547
581
  - `snapPoints` (number[], optional) - Array of snap positions between 0-1
548
582
  - `activeSnapPoint` (number, bindable, optional) - Current active snap point value
549
583
  - `onSnapPointChange` (function, optional) - Callback fired when snap changes
@@ -15,9 +15,10 @@
15
15
  onSnapPointChange = undefined,
16
16
  portal = false,
17
17
  portalContainer = undefined,
18
- persistState = false,
19
- persistKey = "default",
20
- persistSnapPoint = false,
18
+ persistState = false,
19
+ persistKey = "default",
20
+ persistSnapPoint = false,
21
+ closeThreshold = 0.3,
21
22
  children,
22
23
  } = $props();
23
24
 
@@ -34,7 +35,7 @@
34
35
  let previouslyFocusedElement: HTMLElement | null = null;
35
36
  let visible = false;
36
37
  let previousSnapPoint: number | undefined = undefined;
37
- let stateLoaded = false;
38
+ let stateLoaded = false;
38
39
 
39
40
  onMount(() => {
40
41
  if (persistState && !stateLoaded) {
@@ -67,7 +68,7 @@
67
68
  saveDrawerState(
68
69
  persistKey,
69
70
  open,
70
- persistSnapPoint ? activeSnapPoint : undefined
71
+ persistSnapPoint ? activeSnapPoint : undefined,
71
72
  );
72
73
  }
73
74
  });
@@ -171,6 +172,9 @@
171
172
  get activeSnapPoint() {
172
173
  return activeSnapPoint;
173
174
  },
175
+ get closeThreshold() {
176
+ return closeThreshold;
177
+ },
174
178
  setActiveSnapPoint(point: number) {
175
179
  activeSnapPoint = point;
176
180
  onSnapPointChange?.(point);
@@ -11,6 +11,7 @@ declare const Drawer: import("svelte").Component<{
11
11
  persistState?: boolean;
12
12
  persistKey?: string;
13
13
  persistSnapPoint?: boolean;
14
+ closeThreshold?: number;
14
15
  children: any;
15
16
  }, {}, "open" | "activeSnapPoint">;
16
17
  type Drawer = ReturnType<typeof Drawer>;
@@ -11,6 +11,7 @@
11
11
  snapPoints?: number[];
12
12
  activeSnapPoint?: number;
13
13
  setActiveSnapPoint?: (point: number) => void;
14
+ closeThreshold: number;
14
15
  };
15
16
 
16
17
  let {
@@ -134,6 +135,7 @@
134
135
  dragging = false;
135
136
 
136
137
  const pos = drawer.drawerPosition.current;
138
+ const threshold = drawer.closeThreshold * 100;
137
139
 
138
140
  if (drawer.snapPoints && drawer.snapPoints.length > 0) {
139
141
  const nearestSnapPoint = findNearestSnapPoint(pos);
@@ -142,14 +144,14 @@
142
144
  const lowestSnapPoint = Math.min(...drawer.snapPoints);
143
145
  const lowestSnapPos = snapPointToPosition(lowestSnapPoint);
144
146
 
145
- if (pos > lowestSnapPos + 30) {
147
+ if (pos > lowestSnapPos + threshold) {
146
148
  drawer.closeDrawer();
147
149
  } else {
148
150
  drawer.drawerPosition.set(snapPos);
149
151
  drawer.setActiveSnapPoint?.(nearestSnapPoint);
150
152
  }
151
153
  } else {
152
- if (pos > 30) {
154
+ if (pos > threshold) {
153
155
  drawer.closeDrawer();
154
156
  } else {
155
157
  drawer.drawerPosition.set(0);
package/dist/types.d.ts CHANGED
@@ -11,6 +11,7 @@ export interface DrawerProps {
11
11
  persistState?: boolean;
12
12
  persistKey?: string;
13
13
  persistSnapPoint?: boolean;
14
+ closeThreshold?: number;
14
15
  }
15
16
  export interface DrawerContentProps {
16
17
  class?: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@abhivarde/svelte-drawer",
3
- "version": "1.0.4",
3
+ "version": "1.0.6",
4
4
  "description": "A drawer component for Svelte 5, inspired by Vaul",
5
5
  "author": "Abhi Varde",
6
6
  "license": "MIT",