@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 {
|
|
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, {
|
|
15
|
-
|
|
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
|
-
|
|
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
|
-
|
|
7
|
-
|
|
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 >
|
|
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(
|
|
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
|
|
127
|
-
if (
|
|
115
|
+
const focusable = getFocusableElements();
|
|
116
|
+
if (!focusable.length) return;
|
|
128
117
|
|
|
129
|
-
const
|
|
130
|
-
const
|
|
118
|
+
const first = focusable[0];
|
|
119
|
+
const last = focusable[focusable.length - 1];
|
|
131
120
|
|
|
132
|
-
if (e.shiftKey) {
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
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
|
|
149
|
-
if (
|
|
150
|
-
|
|
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;
|
|
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}
|