@abhivarde/svelte-drawer 0.0.7 → 0.0.9

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.
@@ -1,6 +1,6 @@
1
1
  <script lang="ts">
2
2
  import { Tween } from "svelte/motion";
3
- import { cubicOut } from "svelte/easing";
3
+ import { expoOut } from "svelte/easing";
4
4
  import { setContext, onMount } from "svelte";
5
5
 
6
6
  let {
@@ -11,21 +11,31 @@
11
11
  children,
12
12
  } = $props();
13
13
 
14
- let overlayOpacity = new Tween(0, { duration: 300, easing: cubicOut });
15
- let drawerPosition = new Tween(100, { duration: 300, easing: cubicOut });
14
+ let overlayOpacity = new Tween(0, {
15
+ duration: 150,
16
+ easing: expoOut,
17
+ });
18
+
19
+ let drawerPosition = new Tween(100, {
20
+ duration: 220,
21
+ easing: expoOut,
22
+ });
23
+
16
24
  let previouslyFocusedElement: HTMLElement | null = null;
17
25
 
26
+ let visible = false;
27
+
18
28
  $effect(() => {
19
29
  if (open) {
30
+ visible = true;
20
31
  previouslyFocusedElement = document.activeElement as HTMLElement;
21
-
22
32
  document.body.style.overflow = "hidden";
23
33
 
24
34
  overlayOpacity.set(1);
25
35
  drawerPosition.set(0);
26
- } else {
27
- overlayOpacity.set(0);
28
- drawerPosition.set(100);
36
+ } else if (visible) {
37
+ overlayOpacity.set(0, { duration: 120 });
38
+ drawerPosition.set(100, { duration: 180 });
29
39
 
30
40
  document.body.style.overflow = "";
31
41
 
@@ -33,12 +43,16 @@
33
43
  previouslyFocusedElement.focus();
34
44
  previouslyFocusedElement = null;
35
45
  }
46
+
47
+ setTimeout(() => {
48
+ visible = false;
49
+ }, 180);
36
50
  }
37
51
  });
38
52
 
39
53
  function closeDrawer() {
40
54
  open = false;
41
- if (onOpenChange) onOpenChange(false);
55
+ onOpenChange?.(false);
42
56
  }
43
57
 
44
58
  function handleKeydown(e: KeyboardEvent) {
@@ -60,6 +74,9 @@
60
74
  get open() {
61
75
  return open;
62
76
  },
77
+ get visible() {
78
+ return visible;
79
+ },
63
80
  get overlayOpacity() {
64
81
  return overlayOpacity;
65
82
  },
@@ -3,8 +3,9 @@
3
3
 
4
4
  type DrawerContext = {
5
5
  open: boolean;
6
- overlayOpacity: { current: number; set: (v: number) => void };
7
- drawerPosition: { current: number; set: (v: number) => void };
6
+ visible: boolean;
7
+ overlayOpacity: { current: number; set: (v: number, opts?: any) => void };
8
+ drawerPosition: { current: number; set: (v: number, opts?: any) => void };
8
9
  direction: "bottom" | "top" | "left" | "right";
9
10
  closeDrawer: () => void;
10
11
  };
@@ -33,8 +34,6 @@
33
34
  return `translateX(-${pos}%)`;
34
35
  case "right":
35
36
  return `translateX(${pos}%)`;
36
- default:
37
- return `translateY(${pos}%)`;
38
37
  }
39
38
  }
40
39
 
@@ -90,9 +89,8 @@
90
89
  document.body.style.cursor = "default";
91
90
 
92
91
  const pos = drawer.drawerPosition.current;
93
- const deltaThreshold = 30;
94
92
 
95
- if (pos > deltaThreshold) {
93
+ if (pos > 30) {
96
94
  drawer.closeDrawer();
97
95
  } else {
98
96
  drawer.drawerPosition.set(0);
@@ -104,52 +102,39 @@
104
102
 
105
103
  function getFocusableElements(): HTMLElement[] {
106
104
  if (!contentElement) return [];
107
-
108
- const focusableSelectors = [
109
- "a[href]",
110
- "button:not([disabled])",
111
- "textarea:not([disabled])",
112
- "input:not([disabled])",
113
- "select:not([disabled])",
114
- '[tabindex]:not([tabindex="-1"])',
115
- ];
116
-
117
105
  return Array.from(
118
- contentElement.querySelectorAll(focusableSelectors.join(","))
106
+ contentElement.querySelectorAll(
107
+ 'a[href], button:not([disabled]), textarea:not([disabled]), input:not([disabled]), select:not([disabled]), [tabindex]:not([tabindex="-1"])'
108
+ )
119
109
  ) as HTMLElement[];
120
110
  }
121
111
 
122
112
  function handleFocusTrap(e: KeyboardEvent) {
123
- if (!trapFocus || !drawer.open) return;
124
- if (e.key !== "Tab") return;
113
+ if (!trapFocus || !drawer.open || e.key !== "Tab") return;
125
114
 
126
- const focusableElements = getFocusableElements();
127
- if (focusableElements.length === 0) return;
115
+ const focusable = getFocusableElements();
116
+ if (!focusable.length) return;
128
117
 
129
- const firstElement = focusableElements[0];
130
- const lastElement = focusableElements[focusableElements.length - 1];
118
+ const first = focusable[0];
119
+ const last = focusable[focusable.length - 1];
131
120
 
132
- if (e.shiftKey) {
133
- if (document.activeElement === firstElement) {
134
- e.preventDefault();
135
- lastElement.focus();
136
- }
137
- } else {
138
- if (document.activeElement === lastElement) {
139
- e.preventDefault();
140
- firstElement.focus();
141
- }
121
+ if (e.shiftKey && document.activeElement === first) {
122
+ e.preventDefault();
123
+ last.focus();
124
+ } else if (!e.shiftKey && document.activeElement === last) {
125
+ e.preventDefault();
126
+ first.focus();
142
127
  }
143
128
  }
144
129
 
145
130
  $effect(() => {
146
131
  if (drawer.open && trapFocus && contentElement) {
147
132
  tick().then(() => {
148
- const focusableElements = getFocusableElements();
149
- if (focusableElements.length > 0) {
150
- focusableElements[0].focus();
133
+ const focusable = getFocusableElements();
134
+ if (focusable[0]) {
135
+ focusable[0].focus({ preventScroll: true });
151
136
  } else {
152
- contentElement?.focus();
137
+ contentElement?.focus({ preventScroll: true });
153
138
  }
154
139
  });
155
140
  }
@@ -163,11 +148,11 @@
163
148
  });
164
149
  </script>
165
150
 
166
- {#if drawer.open}
151
+ {#if drawer.open || drawer.visible}
167
152
  <div
168
153
  bind:this={contentElement}
169
154
  class={className}
170
- style="transform: {getTransform()}; z-index: 50; cursor: grab; transition: transform 300ms cubic-bezier(0.33, 1, 0.68, 1);"
155
+ style="transform: {getTransform()}; z-index: 50; cursor: grab;"
171
156
  onpointerdown={onPointerDown}
172
157
  tabindex="-1"
173
158
  role="dialog"
@@ -176,4 +161,4 @@
176
161
  >
177
162
  {@render children()}
178
163
  </div>
179
- {/if}
164
+ {/if}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@abhivarde/svelte-drawer",
3
- "version": "0.0.7",
3
+ "version": "0.0.9",
4
4
  "description": "A drawer component for Svelte 5, inspired by Vaul",
5
5
  "author": "Abhi Varde",
6
6
  "license": "MIT",