@abhivarde/svelte-drawer 0.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 ADDED
@@ -0,0 +1,124 @@
1
+ # Svelte Drawer
2
+
3
+ A drawer component for Svelte 5, inspired by [Vaul](https://github.com/emilkowalski/vaul).
4
+
5
+ ## Features
6
+
7
+ - ✅ Smooth animations using Svelte 5's Spring motion
8
+ - ✅ Multiple directions (bottom, top, left, right)
9
+ - ✅ Nested drawers support
10
+ - ✅ Scrollable content
11
+ - ✅ Fully accessible with keyboard navigation
12
+ - ✅ TypeScript support
13
+ - ✅ Customizable styling with Tailwind CSS
14
+
15
+ ## Installation
16
+
17
+ ```bash
18
+ npm install @abhivarde/svelte-drawer
19
+ ```
20
+
21
+ ## Usage
22
+
23
+ ### Basic Example
24
+
25
+ ```svelte
26
+ <script>
27
+ import { Drawer, DrawerOverlay, DrawerContent } from '@abhivarde/svelte-drawer';
28
+
29
+ let open = $state(false);
30
+ </script>
31
+
32
+ <button onclick={() => open = true}>
33
+ Open Drawer
34
+ </button>
35
+
36
+ <Drawer bind:open>
37
+ <DrawerOverlay class="fixed inset-0 bg-black/40" />
38
+ <DrawerContent class="fixed bottom-0 left-0 right-0 bg-white rounded-t-lg p-4">
39
+ <h2>Drawer Content</h2>
40
+ <p>This is a drawer component.</p>
41
+ <button onclick={() => open = false}>Close</button>
42
+ </DrawerContent>
43
+ </Drawer>
44
+ ```
45
+
46
+ ### Side Drawer
47
+
48
+ ```svelte
49
+ <script>
50
+ import { Drawer, DrawerOverlay, DrawerContent } from '@abhivarde/svelte-drawer';
51
+
52
+ let open = $state(false);
53
+ </script>
54
+
55
+ <Drawer bind:open direction="right">
56
+ <DrawerOverlay class="fixed inset-0 bg-black/40" />
57
+ <DrawerContent class="fixed right-0 top-0 bottom-0 w-80 bg-white p-4">
58
+ <h2>Side Drawer</h2>
59
+ <button onclick={() => open = false}>Close</button>
60
+ </DrawerContent>
61
+ </Drawer>
62
+ ```
63
+
64
+ ### Controlled Drawer
65
+
66
+ ```svelte
67
+ <script>
68
+ import { Drawer, DrawerOverlay, DrawerContent } from '@abhivarde/svelte-drawer';
69
+
70
+ let open = $state(false);
71
+
72
+ function handleOpenChange(isOpen) {
73
+ console.log('Drawer is now:', isOpen ? 'open' : 'closed');
74
+ open = isOpen;
75
+ }
76
+ </script>
77
+
78
+ <Drawer bind:open onOpenChange={handleOpenChange}>
79
+ <DrawerOverlay class="fixed inset-0 bg-black/40" />
80
+ <DrawerContent class="fixed bottom-0 left-0 right-0 bg-white rounded-t-lg p-4">
81
+ <h2>Controlled Drawer</h2>
82
+ </DrawerContent>
83
+ </Drawer>
84
+ ```
85
+
86
+ ## API Reference
87
+
88
+ ### Drawer
89
+
90
+ Main wrapper component that manages drawer state and animations.
91
+
92
+ **Props:**
93
+
94
+ - `open` (boolean, bindable) - Controls the open/closed state
95
+ - `onOpenChange` (function, optional) - Callback when open state changes
96
+ - `direction` ('bottom' | 'top' | 'left' | 'right', default: 'bottom') - Direction from which drawer slides
97
+
98
+ ### DrawerOverlay
99
+
100
+ Overlay component that appears behind the drawer.
101
+
102
+ **Props:**
103
+
104
+ - `class` (string, optional) - CSS classes for styling
105
+
106
+ ### DrawerContent
107
+
108
+ Content container for the drawer.
109
+
110
+ **Props:**
111
+
112
+ - `class` (string, optional) - CSS classes for styling
113
+
114
+ ## Demo
115
+
116
+ Visit [drawer.abhivarde.in](https://drawer.abhivarde.in) to see live examples.
117
+
118
+ ## License
119
+
120
+ MIT
121
+
122
+ ## Credits
123
+
124
+ Inspired by [Vaul](https://github.com/emilkowalski/vaul) by Emil Kowalski.
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="107" height="128" viewBox="0 0 107 128"><title>svelte-logo</title><path d="M94.157 22.819c-10.4-14.885-30.94-19.297-45.792-9.835L22.282 29.608A29.92 29.92 0 0 0 8.764 49.65a31.5 31.5 0 0 0 3.108 20.231 30 30 0 0 0-4.477 11.183 31.9 31.9 0 0 0 5.448 24.116c10.402 14.887 30.942 19.297 45.791 9.835l26.083-16.624A29.92 29.92 0 0 0 98.235 78.35a31.53 31.53 0 0 0-3.105-20.232 30 30 0 0 0 4.474-11.182 31.88 31.88 0 0 0-5.447-24.116" style="fill:#ff3e00"/><path d="M45.817 106.582a20.72 20.72 0 0 1-22.237-8.243 19.17 19.17 0 0 1-3.277-14.503 18 18 0 0 1 .624-2.435l.49-1.498 1.337.981a33.6 33.6 0 0 0 10.203 5.098l.97.294-.09.968a5.85 5.85 0 0 0 1.052 3.878 6.24 6.24 0 0 0 6.695 2.485 5.8 5.8 0 0 0 1.603-.704L69.27 76.28a5.43 5.43 0 0 0 2.45-3.631 5.8 5.8 0 0 0-.987-4.371 6.24 6.24 0 0 0-6.698-2.487 5.7 5.7 0 0 0-1.6.704l-9.953 6.345a19 19 0 0 1-5.296 2.326 20.72 20.72 0 0 1-22.237-8.243 19.17 19.17 0 0 1-3.277-14.502 17.99 17.99 0 0 1 8.13-12.052l26.081-16.623a19 19 0 0 1 5.3-2.329 20.72 20.72 0 0 1 22.237 8.243 19.17 19.17 0 0 1 3.277 14.503 18 18 0 0 1-.624 2.435l-.49 1.498-1.337-.98a33.6 33.6 0 0 0-10.203-5.1l-.97-.294.09-.968a5.86 5.86 0 0 0-1.052-3.878 6.24 6.24 0 0 0-6.696-2.485 5.8 5.8 0 0 0-1.602.704L37.73 51.72a5.42 5.42 0 0 0-2.449 3.63 5.79 5.79 0 0 0 .986 4.372 6.24 6.24 0 0 0 6.698 2.486 5.8 5.8 0 0 0 1.602-.704l9.952-6.342a19 19 0 0 1 5.295-2.328 20.72 20.72 0 0 1 22.237 8.242 19.17 19.17 0 0 1 3.277 14.503 18 18 0 0 1-8.13 12.053l-26.081 16.622a19 19 0 0 1-5.3 2.328" style="fill:#fff"/></svg>
@@ -0,0 +1,42 @@
1
+ <script>
2
+ import { Spring } from 'svelte/motion';
3
+ import { setContext } from 'svelte';
4
+
5
+ let {
6
+ open = $bindable(false),
7
+ onOpenChange = undefined,
8
+ direction = 'bottom',
9
+ children
10
+ } = $props();
11
+
12
+ let overlayOpacity = new Spring(0, { stiffness: 0.32, damping: 0.72 });
13
+ let drawerPosition = new Spring(100, { stiffness: 0.32, damping: 0.72 });
14
+
15
+ $effect(() => {
16
+ if (open) {
17
+ overlayOpacity.target = 1;
18
+ drawerPosition.target = 0;
19
+ } else {
20
+ overlayOpacity.target = 0;
21
+ drawerPosition.target = 100;
22
+ }
23
+ });
24
+
25
+ function closeDrawer() {
26
+ open = false;
27
+ if (onOpenChange) {
28
+ onOpenChange(false);
29
+ }
30
+ }
31
+
32
+ // Provide context to child components
33
+ setContext('drawer', {
34
+ get open() { return open; },
35
+ get overlayOpacity() { return overlayOpacity; },
36
+ get drawerPosition() { return drawerPosition; },
37
+ get direction() { return direction; },
38
+ closeDrawer
39
+ });
40
+ </script>
41
+
42
+ {@render children()}
@@ -0,0 +1,17 @@
1
+ export default Drawer;
2
+ type Drawer = {
3
+ $on?(type: string, callback: (e: any) => void): () => void;
4
+ $set?(props: Partial<$$ComponentProps>): void;
5
+ };
6
+ declare const Drawer: import("svelte").Component<{
7
+ open?: boolean;
8
+ onOpenChange?: any;
9
+ direction?: string;
10
+ children: any;
11
+ }, {}, "open">;
12
+ type $$ComponentProps = {
13
+ open?: boolean;
14
+ onOpenChange?: any;
15
+ direction?: string;
16
+ children: any;
17
+ };
@@ -0,0 +1,37 @@
1
+ <script>
2
+ import { getContext } from 'svelte';
3
+
4
+ let {
5
+ class: className = '',
6
+ children,
7
+ ...restProps
8
+ } = $props();
9
+
10
+ const drawer = getContext('drawer');
11
+
12
+ function getTransform() {
13
+ const pos = drawer.drawerPosition.current;
14
+ switch (drawer.direction) {
15
+ case 'bottom':
16
+ return `translateY(${pos}%)`;
17
+ case 'top':
18
+ return `translateY(-${pos}%)`;
19
+ case 'left':
20
+ return `translateX(-${pos}%)`;
21
+ case 'right':
22
+ return `translateX(${pos}%)`;
23
+ default:
24
+ return `translateY(${pos}%)`;
25
+ }
26
+ }
27
+ </script>
28
+
29
+ {#if drawer.open}
30
+ <div
31
+ class={className}
32
+ style="transform: {getTransform()}; z-index: 50;"
33
+ {...restProps}
34
+ >
35
+ {@render children()}
36
+ </div>
37
+ {/if}
@@ -0,0 +1,13 @@
1
+ export default DrawerContent;
2
+ type DrawerContent = {
3
+ $on?(type: string, callback: (e: any) => void): () => void;
4
+ $set?(props: Partial<$$ComponentProps>): void;
5
+ };
6
+ declare const DrawerContent: import("svelte").Component<{
7
+ class?: string;
8
+ children: any;
9
+ } & Record<string, any>, {}, "">;
10
+ type $$ComponentProps = {
11
+ class?: string;
12
+ children: any;
13
+ } & Record<string, any>;
@@ -0,0 +1,33 @@
1
+ <script>
2
+ import { getContext } from 'svelte';
3
+
4
+ let {
5
+ class: className = '',
6
+ ...restProps
7
+ } = $props();
8
+
9
+ const drawer = getContext('drawer');
10
+
11
+ /**
12
+ * @param {KeyboardEvent} e
13
+ */
14
+ function handleKeydown(e) {
15
+ if (e.key === 'Enter' || e.key === ' ') {
16
+ e.preventDefault();
17
+ drawer.closeDrawer();
18
+ }
19
+ }
20
+ </script>
21
+
22
+ {#if drawer.open}
23
+ <div
24
+ class="fixed inset-0 bg-black/40 cursor-pointer {className}"
25
+ style="opacity: {drawer.overlayOpacity.current}; z-index: 40;"
26
+ onclick={drawer.closeDrawer}
27
+ onkeydown={handleKeydown}
28
+ role="button"
29
+ tabindex="0"
30
+ aria-label="Close drawer"
31
+ {...restProps}
32
+ ></div>
33
+ {/if}
@@ -0,0 +1,11 @@
1
+ export default DrawerOverlay;
2
+ type DrawerOverlay = {
3
+ $on?(type: string, callback: (e: any) => void): () => void;
4
+ $set?(props: Partial<$$ComponentProps>): void;
5
+ };
6
+ declare const DrawerOverlay: import("svelte").Component<{
7
+ class?: string;
8
+ } & Record<string, any>, {}, "">;
9
+ type $$ComponentProps = {
10
+ class?: string;
11
+ } & Record<string, any>;
@@ -0,0 +1,4 @@
1
+ export { default as Drawer } from "./components/Drawer.svelte";
2
+ export { default as DrawerOverlay } from "./components/DrawerOverlay.svelte";
3
+ export { default as DrawerContent } from "./components/DrawerContent.svelte";
4
+ export * from "./types";
package/dist/index.js ADDED
@@ -0,0 +1,4 @@
1
+ export { default as Drawer } from "./components/Drawer.svelte";
2
+ export { default as DrawerOverlay } from "./components/DrawerOverlay.svelte";
3
+ export { default as DrawerContent } from "./components/DrawerContent.svelte";
4
+ export * from "./types";
@@ -0,0 +1,11 @@
1
+ export interface DrawerProps {
2
+ open?: boolean;
3
+ onOpenChange?: (open: boolean) => void;
4
+ direction?: "bottom" | "top" | "left" | "right";
5
+ }
6
+ export interface DrawerContentProps {
7
+ class?: string;
8
+ }
9
+ export interface DrawerOverlayProps {
10
+ class?: string;
11
+ }
package/dist/types.js ADDED
@@ -0,0 +1 @@
1
+ export {};
package/package.json ADDED
@@ -0,0 +1,65 @@
1
+ {
2
+ "name": "@abhivarde/svelte-drawer",
3
+ "version": "0.0.1",
4
+ "description": "A drawer component for Svelte 5, inspired by Vaul",
5
+ "author": "Abhi Varde",
6
+ "license": "MIT",
7
+ "type": "module",
8
+ "scripts": {
9
+ "dev": "vite dev",
10
+ "build": "vite build && npm run package",
11
+ "preview": "vite preview",
12
+ "package": "svelte-kit sync && svelte-package && publint",
13
+ "prepublishOnly": "npm run package",
14
+ "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
15
+ "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
16
+ "test": "npm run test:integration && npm run test:unit",
17
+ "lint": "prettier --check . && eslint .",
18
+ "format": "prettier --write ."
19
+ },
20
+ "exports": {
21
+ ".": {
22
+ "types": "./dist/index.d.ts",
23
+ "svelte": "./dist/index.js"
24
+ }
25
+ },
26
+ "files": [
27
+ "dist",
28
+ "!dist/**/*.test.*",
29
+ "!dist/**/*.spec.*"
30
+ ],
31
+ "peerDependencies": {
32
+ "svelte": "^5.0.0"
33
+ },
34
+ "devDependencies": {
35
+ "@sveltejs/adapter-auto": "^7.0.0",
36
+ "@sveltejs/kit": "^2.49.1",
37
+ "@sveltejs/package": "^2.5.7",
38
+ "@sveltejs/vite-plugin-svelte": "^6.2.1",
39
+ "publint": "^0.2.12",
40
+ "svelte": "^5.45.6",
41
+ "svelte-check": "^4.3.4",
42
+ "typescript": "^5.9.3",
43
+ "vite": "^7.2.6"
44
+ },
45
+ "svelte": "./dist/index.js",
46
+ "types": "./dist/index.d.ts",
47
+ "keywords": [
48
+ "svelte",
49
+ "svelte5",
50
+ "drawer",
51
+ "dialog",
52
+ "modal",
53
+ "sheet",
54
+ "component",
55
+ "ui"
56
+ ],
57
+ "repository": {
58
+ "type": "git",
59
+ "url": "https://github.com/AbhiVarde/svelte-drawer"
60
+ },
61
+ "bugs": {
62
+ "url": "https://github.com/AbhiVarde/svelte-drawer/issues"
63
+ },
64
+ "homepage": "https://drawer.abhivarde.in"
65
+ }