@abhivarde/svelte-drawer 0.0.20 → 0.0.22
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 +165 -2
- package/dist/components/Drawer.svelte +10 -1
- package/dist/components/Drawer.svelte.d.ts +2 -0
- package/dist/components/DrawerFooter.svelte +23 -0
- package/dist/components/DrawerFooter.svelte.d.ts +9 -0
- package/dist/components/DrawerHeader.svelte +71 -0
- package/dist/components/DrawerHeader.svelte.d.ts +12 -0
- package/dist/components/DrawerPortal.svelte +53 -0
- package/dist/components/DrawerPortal.svelte.d.ts +7 -0
- package/dist/index.d.ts +4 -1
- package/dist/index.js +3 -0
- package/dist/types.d.ts +15 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -231,6 +231,135 @@ Snap points allow the drawer to rest at predefined heights, creating an iOS-like
|
|
|
231
231
|
- Use `bind:activeSnapPoint` to programmatically control the current position
|
|
232
232
|
- Use `onSnapPointChange` callback to react to snap changes
|
|
233
233
|
|
|
234
|
+
### Portal Support
|
|
235
|
+
|
|
236
|
+
Render the drawer in a portal to avoid z-index conflicts in complex layouts.
|
|
237
|
+
```svelte
|
|
238
|
+
<script>
|
|
239
|
+
import { Drawer, DrawerOverlay, DrawerContent, DrawerHandle } from '@abhivarde/svelte-drawer';
|
|
240
|
+
|
|
241
|
+
let open = $state(false);
|
|
242
|
+
</script>
|
|
243
|
+
|
|
244
|
+
<!-- Enable portal (renders at end of body) -->
|
|
245
|
+
<Drawer bind:open portal={true}>
|
|
246
|
+
<DrawerOverlay class="fixed inset-0 bg-black/40" />
|
|
247
|
+
<DrawerContent class="fixed bottom-0 left-0 right-0 bg-white rounded-t-lg p-4">
|
|
248
|
+
<DrawerHandle class="mb-8" />
|
|
249
|
+
<h2>Portal Drawer</h2>
|
|
250
|
+
<p>This drawer is rendered in a portal, preventing z-index issues.</p>
|
|
251
|
+
</DrawerContent>
|
|
252
|
+
</Drawer>
|
|
253
|
+
|
|
254
|
+
<!-- Custom portal container -->
|
|
255
|
+
<Drawer bind:open portal={true} portalContainer="#custom-portal">
|
|
256
|
+
<DrawerOverlay />
|
|
257
|
+
<DrawerContent>
|
|
258
|
+
<h2>Custom Portal</h2>
|
|
259
|
+
</DrawerContent>
|
|
260
|
+
</Drawer>
|
|
261
|
+
|
|
262
|
+
<div id="custom-portal"></div>
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
**When to use portals:**
|
|
266
|
+
- Complex layouts with nested z-index contexts
|
|
267
|
+
- Third-party component libraries with fixed positioning
|
|
268
|
+
- Modals inside scrollable containers
|
|
269
|
+
- Preventing overflow: hidden conflicts
|
|
270
|
+
|
|
271
|
+
### Header & Footer Components
|
|
272
|
+
|
|
273
|
+
Optional pre-styled header and footer components for quick setup.
|
|
274
|
+
|
|
275
|
+
#### DrawerHeader
|
|
276
|
+
```svelte
|
|
277
|
+
<script>
|
|
278
|
+
import { Drawer, DrawerOverlay, DrawerContent, DrawerHeader, DrawerHandle } from '@abhivarde/svelte-drawer';
|
|
279
|
+
|
|
280
|
+
let open = $state(false);
|
|
281
|
+
</script>
|
|
282
|
+
|
|
283
|
+
<!-- With title and description -->
|
|
284
|
+
<Drawer bind:open>
|
|
285
|
+
<DrawerOverlay class="fixed inset-0 bg-black/40" />
|
|
286
|
+
<DrawerContent class="fixed bottom-0 left-0 right-0 bg-white rounded-t-lg">
|
|
287
|
+
<DrawerHeader
|
|
288
|
+
title="Drawer Title"
|
|
289
|
+
description="Optional description text"
|
|
290
|
+
showCloseButton={true}
|
|
291
|
+
/>
|
|
292
|
+
<div class="p-4">
|
|
293
|
+
<p>Drawer content here</p>
|
|
294
|
+
</div>
|
|
295
|
+
</DrawerContent>
|
|
296
|
+
</Drawer>
|
|
297
|
+
|
|
298
|
+
<!-- Custom header content -->
|
|
299
|
+
<Drawer bind:open>
|
|
300
|
+
<DrawerOverlay />
|
|
301
|
+
<DrawerContent class="fixed bottom-0 left-0 right-0 bg-white rounded-t-lg">
|
|
302
|
+
<DrawerHeader>
|
|
303
|
+
<div class="flex items-center gap-3">
|
|
304
|
+
<img src="/icon.png" alt="Icon" class="w-8 h-8" />
|
|
305
|
+
<div>
|
|
306
|
+
<h2 class="font-semibold">Custom Header</h2>
|
|
307
|
+
<p class="text-sm text-gray-600">Your custom content</p>
|
|
308
|
+
</div>
|
|
309
|
+
</div>
|
|
310
|
+
</DrawerHeader>
|
|
311
|
+
<div class="p-4">
|
|
312
|
+
<p>Drawer content</p>
|
|
313
|
+
</div>
|
|
314
|
+
</DrawerContent>
|
|
315
|
+
</Drawer>
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
#### DrawerFooter
|
|
319
|
+
```svelte
|
|
320
|
+
<script>
|
|
321
|
+
import { Drawer, DrawerOverlay, DrawerContent, DrawerFooter } from '@abhivarde/svelte-drawer';
|
|
322
|
+
|
|
323
|
+
let open = $state(false);
|
|
324
|
+
</script>
|
|
325
|
+
|
|
326
|
+
<!-- Simple footer with custom content -->
|
|
327
|
+
<Drawer bind:open>
|
|
328
|
+
<DrawerOverlay class="fixed inset-0 bg-black/40" />
|
|
329
|
+
<DrawerContent class="fixed bottom-0 left-0 right-0 bg-white rounded-t-lg flex flex-col">
|
|
330
|
+
<div class="p-4 flex-1">
|
|
331
|
+
<h2>Drawer Content</h2>
|
|
332
|
+
</div>
|
|
333
|
+
<DrawerFooter>
|
|
334
|
+
<button onclick={() => open = false} class="px-4 py-2 bg-gray-200 rounded">
|
|
335
|
+
Cancel
|
|
336
|
+
</button>
|
|
337
|
+
<button class="px-4 py-2 bg-black text-white rounded">
|
|
338
|
+
Confirm
|
|
339
|
+
</button>
|
|
340
|
+
</DrawerFooter>
|
|
341
|
+
</DrawerContent>
|
|
342
|
+
</Drawer>
|
|
343
|
+
|
|
344
|
+
<!-- Custom footer with links -->
|
|
345
|
+
<Drawer bind:open>
|
|
346
|
+
<DrawerOverlay />
|
|
347
|
+
<DrawerContent class="fixed bottom-0 left-0 right-0 bg-white rounded-t-lg flex flex-col">
|
|
348
|
+
<div class="p-4 flex-1">
|
|
349
|
+
<h2>Content</h2>
|
|
350
|
+
</div>
|
|
351
|
+
<DrawerFooter class="bg-gray-50">
|
|
352
|
+
<div class="flex justify-between w-full">
|
|
353
|
+
<a href="/privacy" class="text-sm text-gray-600 hover:text-gray-900">Privacy</a>
|
|
354
|
+
<a href="/terms" class="text-sm text-gray-600 hover:text-gray-900">Terms</a>
|
|
355
|
+
</div>
|
|
356
|
+
</DrawerFooter>
|
|
357
|
+
</DrawerContent>
|
|
358
|
+
</Drawer>
|
|
359
|
+
```
|
|
360
|
+
|
|
361
|
+
**Note:** These components are **optional**. You can still build custom headers and footers using plain HTML/Svelte markup without importing these components.
|
|
362
|
+
|
|
234
363
|
## Variants
|
|
235
364
|
|
|
236
365
|
Available variants for `DrawerVariants` component:
|
|
@@ -259,9 +388,11 @@ Main wrapper component that manages drawer state and animations.
|
|
|
259
388
|
- `onOpenChange` (function, optional) - Callback when open state changes
|
|
260
389
|
- `direction` ('bottom' | 'top' | 'left' | 'right', default: 'bottom') - Direction from which drawer slides
|
|
261
390
|
- `closeOnEscape` (boolean, optional, default: true) - Whether Escape key closes the drawer
|
|
262
|
-
- `snapPoints` (number[], optional) - Array of snap positions between 0-1
|
|
391
|
+
- `snapPoints` (number[], optional) - Array of snap positions between 0-1
|
|
263
392
|
- `activeSnapPoint` (number, bindable, optional) - Current active snap point value
|
|
264
|
-
- `onSnapPointChange` (function, optional) - Callback fired when
|
|
393
|
+
- `onSnapPointChange` (function, optional) - Callback fired when snap changes
|
|
394
|
+
- `portal` (boolean, optional, default: false) - Render drawer in a portal
|
|
395
|
+
- `portalContainer` (HTMLElement | string, optional) - Custom portal container element or selector
|
|
265
396
|
|
|
266
397
|
### DrawerOverlay
|
|
267
398
|
|
|
@@ -318,6 +449,38 @@ Pre-styled drawer content with built-in variants.
|
|
|
318
449
|
- `class` (string, optional) - Additional CSS classes for styling
|
|
319
450
|
- `trapFocus` (boolean, optional, default: true) - Whether to trap focus inside drawer
|
|
320
451
|
|
|
452
|
+
### DrawerHeader
|
|
453
|
+
|
|
454
|
+
Optional pre-styled header component.
|
|
455
|
+
|
|
456
|
+
**Props:**
|
|
457
|
+
|
|
458
|
+
- `title` (string, optional) - Header title text
|
|
459
|
+
- `description` (string, optional) - Description text below title
|
|
460
|
+
- `showCloseButton` (boolean, optional, default: true) - Show close button
|
|
461
|
+
- `onClose` (function, optional) - Custom close handler
|
|
462
|
+
- `class` (string, optional) - CSS classes for styling
|
|
463
|
+
|
|
464
|
+
**Features:**
|
|
465
|
+
|
|
466
|
+
- Automatic close button with drawer context
|
|
467
|
+
- Customizable via children render
|
|
468
|
+
- Border and padding included
|
|
469
|
+
|
|
470
|
+
### DrawerFooter
|
|
471
|
+
|
|
472
|
+
Optional pre-styled footer component.
|
|
473
|
+
|
|
474
|
+
**Props:**
|
|
475
|
+
|
|
476
|
+
- `class` (string, optional) - CSS classes for styling
|
|
477
|
+
|
|
478
|
+
**Features:**
|
|
479
|
+
|
|
480
|
+
- Automatic border and spacing
|
|
481
|
+
- Flexible content via children
|
|
482
|
+
- Works with mt-auto for bottom positioning
|
|
483
|
+
|
|
321
484
|
## Demo
|
|
322
485
|
|
|
323
486
|
Visit [drawer.abhivarde.in](https://drawer.abhivarde.in) to see live examples.
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import { Tween } from "svelte/motion";
|
|
3
3
|
import { expoOut } from "svelte/easing";
|
|
4
4
|
import { setContext, onMount } from "svelte";
|
|
5
|
+
import DrawerPortal from "./DrawerPortal.svelte";
|
|
5
6
|
|
|
6
7
|
let {
|
|
7
8
|
open = $bindable(false),
|
|
@@ -11,6 +12,8 @@
|
|
|
11
12
|
snapPoints = undefined,
|
|
12
13
|
activeSnapPoint = $bindable(undefined),
|
|
13
14
|
onSnapPointChange = undefined,
|
|
15
|
+
portal = false,
|
|
16
|
+
portalContainer = undefined,
|
|
14
17
|
children,
|
|
15
18
|
} = $props();
|
|
16
19
|
|
|
@@ -143,4 +146,10 @@
|
|
|
143
146
|
});
|
|
144
147
|
</script>
|
|
145
148
|
|
|
146
|
-
{
|
|
149
|
+
{#if portal}
|
|
150
|
+
<DrawerPortal container={portalContainer}>
|
|
151
|
+
{@render children()}
|
|
152
|
+
</DrawerPortal>
|
|
153
|
+
{:else}
|
|
154
|
+
{@render children()}
|
|
155
|
+
{/if}
|
|
@@ -6,6 +6,8 @@ declare const Drawer: import("svelte").Component<{
|
|
|
6
6
|
snapPoints?: any;
|
|
7
7
|
activeSnapPoint?: any;
|
|
8
8
|
onSnapPointChange?: any;
|
|
9
|
+
portal?: boolean;
|
|
10
|
+
portalContainer?: any;
|
|
9
11
|
children: any;
|
|
10
12
|
}, {}, "open" | "activeSnapPoint">;
|
|
11
13
|
type Drawer = ReturnType<typeof Drawer>;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
let {
|
|
3
|
+
class: className = "",
|
|
4
|
+
children,
|
|
5
|
+
actions,
|
|
6
|
+
...restProps
|
|
7
|
+
}: {
|
|
8
|
+
class?: string;
|
|
9
|
+
children?: any;
|
|
10
|
+
actions?: any;
|
|
11
|
+
[key: string]: any;
|
|
12
|
+
} = $props();
|
|
13
|
+
</script>
|
|
14
|
+
|
|
15
|
+
<div class="p-4 border-t border-gray-200 mt-auto {className}" {...restProps}>
|
|
16
|
+
{#if children}
|
|
17
|
+
{@render children()}
|
|
18
|
+
{:else if actions}
|
|
19
|
+
<div class="flex gap-3 justify-end">
|
|
20
|
+
{@render actions()}
|
|
21
|
+
</div>
|
|
22
|
+
{/if}
|
|
23
|
+
</div>
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
type $$ComponentProps = {
|
|
2
|
+
class?: string;
|
|
3
|
+
children?: any;
|
|
4
|
+
actions?: any;
|
|
5
|
+
[key: string]: any;
|
|
6
|
+
};
|
|
7
|
+
declare const DrawerFooter: import("svelte").Component<$$ComponentProps, {}, "">;
|
|
8
|
+
type DrawerFooter = ReturnType<typeof DrawerFooter>;
|
|
9
|
+
export default DrawerFooter;
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
let {
|
|
3
|
+
title,
|
|
4
|
+
description,
|
|
5
|
+
showCloseButton = true,
|
|
6
|
+
onClose,
|
|
7
|
+
class: className = "",
|
|
8
|
+
children,
|
|
9
|
+
...restProps
|
|
10
|
+
}: {
|
|
11
|
+
title?: string;
|
|
12
|
+
description?: string;
|
|
13
|
+
showCloseButton?: boolean;
|
|
14
|
+
onClose?: () => void;
|
|
15
|
+
class?: string;
|
|
16
|
+
children?: any;
|
|
17
|
+
[key: string]: any;
|
|
18
|
+
} = $props();
|
|
19
|
+
|
|
20
|
+
import { getContext } from "svelte";
|
|
21
|
+
|
|
22
|
+
const drawer = getContext<any>("drawer");
|
|
23
|
+
|
|
24
|
+
function handleClose() {
|
|
25
|
+
if (onClose) {
|
|
26
|
+
onClose();
|
|
27
|
+
} else {
|
|
28
|
+
drawer?.closeDrawer();
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
</script>
|
|
32
|
+
|
|
33
|
+
<div
|
|
34
|
+
class="flex items-center justify-between p-4 border-b border-gray-200 {className}"
|
|
35
|
+
{...restProps}
|
|
36
|
+
>
|
|
37
|
+
{#if children}
|
|
38
|
+
{@render children()}
|
|
39
|
+
{:else}
|
|
40
|
+
<div class="flex-1">
|
|
41
|
+
{#if title}
|
|
42
|
+
<h2 class="text-lg font-semibold text-gray-900">{title}</h2>
|
|
43
|
+
{/if}
|
|
44
|
+
{#if description}
|
|
45
|
+
<p class="text-sm text-gray-600 mt-1">{description}</p>
|
|
46
|
+
{/if}
|
|
47
|
+
</div>
|
|
48
|
+
|
|
49
|
+
{#if showCloseButton}
|
|
50
|
+
<button
|
|
51
|
+
onclick={handleClose}
|
|
52
|
+
class="p-2 rounded-md hover:bg-gray-100 transition-colors"
|
|
53
|
+
aria-label="Close drawer"
|
|
54
|
+
>
|
|
55
|
+
<svg
|
|
56
|
+
class="w-5 h-5 text-gray-500"
|
|
57
|
+
fill="none"
|
|
58
|
+
stroke="currentColor"
|
|
59
|
+
viewBox="0 0 24 24"
|
|
60
|
+
>
|
|
61
|
+
<path
|
|
62
|
+
stroke-linecap="round"
|
|
63
|
+
stroke-linejoin="round"
|
|
64
|
+
stroke-width="2"
|
|
65
|
+
d="M6 18L18 6M6 6l12 12"
|
|
66
|
+
/>
|
|
67
|
+
</svg>
|
|
68
|
+
</button>
|
|
69
|
+
{/if}
|
|
70
|
+
{/if}
|
|
71
|
+
</div>
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
type $$ComponentProps = {
|
|
2
|
+
title?: string;
|
|
3
|
+
description?: string;
|
|
4
|
+
showCloseButton?: boolean;
|
|
5
|
+
onClose?: () => void;
|
|
6
|
+
class?: string;
|
|
7
|
+
children?: any;
|
|
8
|
+
[key: string]: any;
|
|
9
|
+
};
|
|
10
|
+
declare const DrawerHeader: import("svelte").Component<$$ComponentProps, {}, "">;
|
|
11
|
+
type DrawerHeader = ReturnType<typeof DrawerHeader>;
|
|
12
|
+
export default DrawerHeader;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { onMount } from "svelte";
|
|
3
|
+
|
|
4
|
+
let {
|
|
5
|
+
children,
|
|
6
|
+
container = undefined,
|
|
7
|
+
}: {
|
|
8
|
+
children?: any;
|
|
9
|
+
container?: HTMLElement | string;
|
|
10
|
+
} = $props();
|
|
11
|
+
|
|
12
|
+
let portalContainer = $state<HTMLElement | null>(null);
|
|
13
|
+
let mounted = $state(false);
|
|
14
|
+
|
|
15
|
+
onMount(() => {
|
|
16
|
+
if (container) {
|
|
17
|
+
if (typeof container === "string") {
|
|
18
|
+
portalContainer = document.querySelector(container);
|
|
19
|
+
} else {
|
|
20
|
+
portalContainer = container;
|
|
21
|
+
}
|
|
22
|
+
} else {
|
|
23
|
+
portalContainer = document.createElement("div");
|
|
24
|
+
portalContainer.setAttribute("data-drawer-portal", "");
|
|
25
|
+
document.body.appendChild(portalContainer);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
mounted = true;
|
|
29
|
+
|
|
30
|
+
return () => {
|
|
31
|
+
if (
|
|
32
|
+
!container &&
|
|
33
|
+
portalContainer &&
|
|
34
|
+
document.body.contains(portalContainer)
|
|
35
|
+
) {
|
|
36
|
+
document.body.removeChild(portalContainer);
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
});
|
|
40
|
+
</script>
|
|
41
|
+
|
|
42
|
+
{#if mounted && portalContainer}
|
|
43
|
+
{#each [children] as child}
|
|
44
|
+
{@render child?.()}
|
|
45
|
+
{/each}
|
|
46
|
+
{/if}
|
|
47
|
+
|
|
48
|
+
<style>
|
|
49
|
+
:global([data-drawer-portal]) {
|
|
50
|
+
position: relative;
|
|
51
|
+
z-index: 9999;
|
|
52
|
+
}
|
|
53
|
+
</style>
|
package/dist/index.d.ts
CHANGED
|
@@ -3,4 +3,7 @@ export { default as DrawerContent } from "./components/DrawerContent.svelte";
|
|
|
3
3
|
export { default as DrawerOverlay } from "./components/DrawerOverlay.svelte";
|
|
4
4
|
export { default as DrawerVariants } from "./components/DrawerVariants.svelte";
|
|
5
5
|
export { default as DrawerHandle } from "./components/DrawerHandle.svelte";
|
|
6
|
-
export
|
|
6
|
+
export { default as DrawerPortal } from "./components/DrawerPortal.svelte";
|
|
7
|
+
export { default as DrawerHeader } from "./components/DrawerHeader.svelte";
|
|
8
|
+
export { default as DrawerFooter } from "./components/DrawerFooter.svelte";
|
|
9
|
+
export type { DrawerProps, DrawerContentProps, DrawerOverlayProps, DrawerHandleProps, DrawerVariant, DrawerVariantsProps, DrawerPortalProps, DrawerHeaderProps, DrawerFooterProps, } from "./types";
|
package/dist/index.js
CHANGED
|
@@ -3,3 +3,6 @@ export { default as DrawerContent } from "./components/DrawerContent.svelte";
|
|
|
3
3
|
export { default as DrawerOverlay } from "./components/DrawerOverlay.svelte";
|
|
4
4
|
export { default as DrawerVariants } from "./components/DrawerVariants.svelte";
|
|
5
5
|
export { default as DrawerHandle } from "./components/DrawerHandle.svelte";
|
|
6
|
+
export { default as DrawerPortal } from "./components/DrawerPortal.svelte";
|
|
7
|
+
export { default as DrawerHeader } from "./components/DrawerHeader.svelte";
|
|
8
|
+
export { default as DrawerFooter } from "./components/DrawerFooter.svelte";
|
package/dist/types.d.ts
CHANGED
|
@@ -6,6 +6,8 @@ export interface DrawerProps {
|
|
|
6
6
|
snapPoints?: number[];
|
|
7
7
|
activeSnapPoint?: number;
|
|
8
8
|
onSnapPointChange?: (snapPoint: number) => void;
|
|
9
|
+
portal?: boolean;
|
|
10
|
+
portalContainer?: HTMLElement | string;
|
|
9
11
|
}
|
|
10
12
|
export interface DrawerContentProps {
|
|
11
13
|
class?: string;
|
|
@@ -17,6 +19,19 @@ export interface DrawerOverlayProps {
|
|
|
17
19
|
export interface DrawerHandleProps {
|
|
18
20
|
class?: string;
|
|
19
21
|
}
|
|
22
|
+
export interface DrawerPortalProps {
|
|
23
|
+
container?: HTMLElement | string;
|
|
24
|
+
}
|
|
25
|
+
export interface DrawerHeaderProps {
|
|
26
|
+
title?: string;
|
|
27
|
+
description?: string;
|
|
28
|
+
showCloseButton?: boolean;
|
|
29
|
+
onClose?: () => void;
|
|
30
|
+
class?: string;
|
|
31
|
+
}
|
|
32
|
+
export interface DrawerFooterProps {
|
|
33
|
+
class?: string;
|
|
34
|
+
}
|
|
20
35
|
export type DrawerVariant = "default" | "sheet" | "dialog" | "minimal" | "sidebar";
|
|
21
36
|
export interface DrawerVariantsProps {
|
|
22
37
|
variant?: DrawerVariant;
|