css-drawer 0.1.4 → 0.2.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 +86 -6
- package/dist/drawer-BWZh2Fyp.cjs +0 -0
- package/dist/drawer-C21nwiwE.css +331 -0
- package/dist/drawer-CXCJQa45.css +333 -0
- package/dist/drawer-CXCJQa45.css.map +1 -0
- package/dist/drawer-CiHZcyXE.mjs +1 -0
- package/dist/index.cjs +4 -3
- package/dist/index.d.cts +5 -5
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +5 -5
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +4 -3
- package/dist/index.mjs.map +1 -1
- package/dist/react.cjs +2 -2
- package/dist/react.d.cts +1 -1
- package/dist/react.d.cts.map +1 -1
- package/dist/react.d.mts +1 -1
- package/dist/react.d.mts.map +1 -1
- package/dist/react.mjs +2 -2
- package/dist/react.mjs.map +1 -1
- package/package.json +5 -4
- package/{dist/react.css → src/drawer.css} +26 -3
- package/dist/react.css.map +0 -1
package/README.md
CHANGED
|
@@ -58,7 +58,7 @@ function App() {
|
|
|
58
58
|
|
|
59
59
|
```ts
|
|
60
60
|
import { open, close } from 'css-drawer'
|
|
61
|
-
|
|
61
|
+
// Styles are auto-injected
|
|
62
62
|
|
|
63
63
|
document.querySelector('#open-btn').onclick = () => open('my-drawer')
|
|
64
64
|
```
|
|
@@ -68,7 +68,7 @@ document.querySelector('#open-btn').onclick = () => open('my-drawer')
|
|
|
68
68
|
|
|
69
69
|
<dialog class="drawer" id="my-drawer">
|
|
70
70
|
<div class="drawer-handle"></div>
|
|
71
|
-
<div
|
|
71
|
+
<div class="drawer-content">
|
|
72
72
|
<h2>Title</h2>
|
|
73
73
|
<p>Description</p>
|
|
74
74
|
<button onclick="this.closest('dialog').close()">Close</button>
|
|
@@ -76,6 +76,44 @@ document.querySelector('#open-btn').onclick = () => open('my-drawer')
|
|
|
76
76
|
</dialog>
|
|
77
77
|
```
|
|
78
78
|
|
|
79
|
+
### Angular
|
|
80
|
+
|
|
81
|
+
Angular's build system doesn't process CSS imports from JS modules. Import styles in your global `styles.css`:
|
|
82
|
+
|
|
83
|
+
```css
|
|
84
|
+
/* src/styles.css */
|
|
85
|
+
@import 'css-drawer/styles';
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
Then use the native dialog API in your component:
|
|
89
|
+
|
|
90
|
+
```typescript
|
|
91
|
+
import { Component } from '@angular/core';
|
|
92
|
+
|
|
93
|
+
@Component({
|
|
94
|
+
selector: 'app-example',
|
|
95
|
+
template: `
|
|
96
|
+
<button (click)="openDrawer(drawer)">Open</button>
|
|
97
|
+
|
|
98
|
+
<dialog #drawer class="drawer" data-direction="modal">
|
|
99
|
+
<div class="drawer-content">
|
|
100
|
+
<h2>Title</h2>
|
|
101
|
+
<button (click)="closeDrawer(drawer)">Close</button>
|
|
102
|
+
</div>
|
|
103
|
+
</dialog>
|
|
104
|
+
`
|
|
105
|
+
})
|
|
106
|
+
export class ExampleComponent {
|
|
107
|
+
openDrawer(dialog: HTMLDialogElement) {
|
|
108
|
+
dialog.showModal();
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
closeDrawer(dialog: HTMLDialogElement) {
|
|
112
|
+
dialog.close();
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
```
|
|
116
|
+
|
|
79
117
|
---
|
|
80
118
|
|
|
81
119
|
## React API
|
|
@@ -99,7 +137,7 @@ Provides context for direction. Wrap your drawer content.
|
|
|
99
137
|
|
|
100
138
|
| Prop | Type | Default | Description |
|
|
101
139
|
|------|------|---------|-------------|
|
|
102
|
-
| `direction` | `'bottom' \| 'top' \| 'left' \| 'right'` | `'bottom'` | Direction the drawer opens from |
|
|
140
|
+
| `direction` | `'bottom' \| 'top' \| 'left' \| 'right' \| 'modal'` | `'bottom'` | Direction the drawer opens from |
|
|
103
141
|
| `children` | `ReactNode` | - | Drawer content |
|
|
104
142
|
|
|
105
143
|
### Drawer.Content
|
|
@@ -193,7 +231,7 @@ Semantic description for accessibility.
|
|
|
193
231
|
|
|
194
232
|
```ts
|
|
195
233
|
import { open, close, closeAll } from 'css-drawer'
|
|
196
|
-
|
|
234
|
+
// Styles are auto-injected
|
|
197
235
|
```
|
|
198
236
|
|
|
199
237
|
### open(drawer)
|
|
@@ -286,6 +324,7 @@ open(drawer)
|
|
|
286
324
|
|--------|------|---------|-------------|
|
|
287
325
|
| `id` | `string` | - | Drawer ID |
|
|
288
326
|
| `content` | `string` | `''` | HTML content |
|
|
327
|
+
| `direction` | `DrawerDirection` | `'bottom'` | Direction the drawer opens from |
|
|
289
328
|
| `handle` | `boolean` | `true` | Include drag handle |
|
|
290
329
|
| `className` | `string` | `''` | Additional CSS classes |
|
|
291
330
|
| `closeOnOutsideClick` | `boolean` | `true` | Close when clicking outside |
|
|
@@ -377,6 +416,7 @@ const isMobile = useMediaQuery('(max-width: 768px)')
|
|
|
377
416
|
| `top` | Opens from top |
|
|
378
417
|
| `left` | Opens from left |
|
|
379
418
|
| `right` | Opens from right |
|
|
419
|
+
| `modal` | Centered modal with scale animation |
|
|
380
420
|
|
|
381
421
|
---
|
|
382
422
|
|
|
@@ -384,7 +424,9 @@ const isMobile = useMediaQuery('(max-width: 768px)')
|
|
|
384
424
|
|
|
385
425
|
Drawers automatically stack when opened. No configuration needed.
|
|
386
426
|
|
|
387
|
-
###
|
|
427
|
+
### Recommended: Sibling Pattern
|
|
428
|
+
|
|
429
|
+
For the best visual stacking effect (scale + dim), place drawers as **siblings** in the DOM:
|
|
388
430
|
|
|
389
431
|
```tsx
|
|
390
432
|
const drawer1 = useRef<HTMLDialogElement>(null)
|
|
@@ -396,6 +438,16 @@ drawer1.current?.showModal()
|
|
|
396
438
|
// Open drawer2 on top
|
|
397
439
|
drawer2.current?.showModal()
|
|
398
440
|
// drawer1 automatically scales down and dims
|
|
441
|
+
|
|
442
|
+
<>
|
|
443
|
+
<Drawer.Root>
|
|
444
|
+
<Drawer.Content ref={drawer1}>First drawer</Drawer.Content>
|
|
445
|
+
</Drawer.Root>
|
|
446
|
+
|
|
447
|
+
<Drawer.Root>
|
|
448
|
+
<Drawer.Content ref={drawer2}>Second drawer (sibling)</Drawer.Content>
|
|
449
|
+
</Drawer.Root>
|
|
450
|
+
</>
|
|
399
451
|
```
|
|
400
452
|
|
|
401
453
|
### With Controlled State
|
|
@@ -428,6 +480,28 @@ const [confirmOpen, setConfirmOpen] = useState(false)
|
|
|
428
480
|
|
|
429
481
|
Works up to 5 levels. CSS `:has()` selectors handle the visual stacking.
|
|
430
482
|
|
|
483
|
+
### DOM-Nested Dialogs
|
|
484
|
+
|
|
485
|
+
You can also nest a drawer inside another drawer's content (DOM nesting). This pattern works functionally—close events, buttons, and accessibility all work correctly—but the **automatic CSS scaling effect is not applied** to DOM-nested dialogs.
|
|
486
|
+
|
|
487
|
+
```tsx
|
|
488
|
+
// DOM-nested: works but no auto-scaling
|
|
489
|
+
<Drawer.Root>
|
|
490
|
+
<Drawer.Content open={parentOpen} onOpenChange={setParentOpen}>
|
|
491
|
+
<p>Parent content</p>
|
|
492
|
+
|
|
493
|
+
{/* Child nested inside parent */}
|
|
494
|
+
<Drawer.Root>
|
|
495
|
+
<Drawer.Content open={childOpen} onOpenChange={setChildOpen}>
|
|
496
|
+
<p>Child content</p>
|
|
497
|
+
</Drawer.Content>
|
|
498
|
+
</Drawer.Root>
|
|
499
|
+
</Drawer.Content>
|
|
500
|
+
</Drawer.Root>
|
|
501
|
+
```
|
|
502
|
+
|
|
503
|
+
Use the **sibling pattern** if you want the visual stacking effect.
|
|
504
|
+
|
|
431
505
|
---
|
|
432
506
|
|
|
433
507
|
## Accessibility
|
|
@@ -508,6 +582,7 @@ Override any of these CSS custom properties to customize the drawer:
|
|
|
508
582
|
--drawer-shadow-top: 0 10px 60px hsl(0 0% 0% / 0.12), 0 4px 20px hsl(0 0% 0% / 0.08);
|
|
509
583
|
--drawer-shadow-right: -10px 0 60px hsl(0 0% 0% / 0.12), -4px 0 20px hsl(0 0% 0% / 0.08);
|
|
510
584
|
--drawer-shadow-left: 10px 0 60px hsl(0 0% 0% / 0.12), 4px 0 20px hsl(0 0% 0% / 0.08);
|
|
585
|
+
--drawer-shadow-modal: 0 25px 50px -12px hsl(0 0% 0% / 0.25);
|
|
511
586
|
|
|
512
587
|
/* Animation */
|
|
513
588
|
--drawer-duration: 0.5s;
|
|
@@ -561,6 +636,7 @@ Override any of these CSS custom properties to customize the drawer:
|
|
|
561
636
|
| `--drawer-shadow-top` | `0 10px 60px hsl(0 0% 0% / 0.12), ...` | Darker |
|
|
562
637
|
| `--drawer-shadow-left` | `10px 0 60px hsl(0 0% 0% / 0.12), ...` | Darker |
|
|
563
638
|
| `--drawer-shadow-right` | `-10px 0 60px hsl(0 0% 0% / 0.12), ...` | Darker |
|
|
639
|
+
| `--drawer-shadow-modal` | `0 25px 50px -12px hsl(0 0% 0% / 0.25)` | Darker |
|
|
564
640
|
|
|
565
641
|
#### Animation
|
|
566
642
|
|
|
@@ -731,6 +807,7 @@ Uses `@starting-style`, `:has()`, `allow-discrete`, and `dvh` units.
|
|
|
731
807
|
Full TypeScript support included.
|
|
732
808
|
|
|
733
809
|
```tsx
|
|
810
|
+
// React
|
|
734
811
|
import {
|
|
735
812
|
Drawer,
|
|
736
813
|
type DrawerRootProps,
|
|
@@ -738,11 +815,14 @@ import {
|
|
|
738
815
|
type DrawerDirection
|
|
739
816
|
} from 'css-drawer/react'
|
|
740
817
|
|
|
818
|
+
// Vanilla JS
|
|
741
819
|
import {
|
|
742
820
|
open,
|
|
743
821
|
close,
|
|
822
|
+
create,
|
|
744
823
|
type DrawerElement,
|
|
745
|
-
type DrawerRef
|
|
824
|
+
type DrawerRef,
|
|
825
|
+
type DrawerDirection
|
|
746
826
|
} from 'css-drawer'
|
|
747
827
|
```
|
|
748
828
|
|
|
File without changes
|
|
@@ -0,0 +1,331 @@
|
|
|
1
|
+
/* CSS Drawer - Vaul-quality drawer with auto-nesting and directions */
|
|
2
|
+
|
|
3
|
+
:root {
|
|
4
|
+
/* Visual */
|
|
5
|
+
--drawer-bg: #fff;
|
|
6
|
+
--drawer-radius: 24px;
|
|
7
|
+
--drawer-backdrop: hsl(0 0% 0% / 0.4);
|
|
8
|
+
--drawer-backdrop-blur: 4px;
|
|
9
|
+
|
|
10
|
+
/* Sizing */
|
|
11
|
+
--drawer-max-width: 500px;
|
|
12
|
+
--drawer-max-height: 96dvh;
|
|
13
|
+
|
|
14
|
+
/* Handle */
|
|
15
|
+
--drawer-handle-bg: hsl(0 0% 80%);
|
|
16
|
+
--drawer-handle-bg-hover: hsl(0 0% 60%);
|
|
17
|
+
--drawer-handle-width: 48px;
|
|
18
|
+
--drawer-handle-width-hover: 56px;
|
|
19
|
+
--drawer-handle-height: 5px;
|
|
20
|
+
--drawer-handle-padding-block: 1rem 0.5rem;
|
|
21
|
+
--drawer-handle-padding-inline: 0;
|
|
22
|
+
|
|
23
|
+
/* Shadows */
|
|
24
|
+
--drawer-shadow-bottom: 0 -10px 60px hsl(0 0% 0% / 0.12), 0 -4px 20px hsl(0 0% 0% / 0.08);
|
|
25
|
+
--drawer-shadow-top: 0 10px 60px hsl(0 0% 0% / 0.12), 0 4px 20px hsl(0 0% 0% / 0.08);
|
|
26
|
+
--drawer-shadow-right: -10px 0 60px hsl(0 0% 0% / 0.12), -4px 0 20px hsl(0 0% 0% / 0.08);
|
|
27
|
+
--drawer-shadow-left: 10px 0 60px hsl(0 0% 0% / 0.12), 4px 0 20px hsl(0 0% 0% / 0.08);
|
|
28
|
+
--drawer-shadow-modal: 0 25px 50px -12px hsl(0 0% 0% / 0.25);
|
|
29
|
+
|
|
30
|
+
/* Animation */
|
|
31
|
+
--drawer-duration: 0.5s;
|
|
32
|
+
--drawer-duration-close: 0.35s;
|
|
33
|
+
--drawer-ease: cubic-bezier(0.32, 0.72, 0, 1);
|
|
34
|
+
|
|
35
|
+
/* Nesting effects */
|
|
36
|
+
--drawer-nested-scale: 0.94;
|
|
37
|
+
--drawer-nested-offset: 20px;
|
|
38
|
+
--drawer-nested-brightness: 0.92;
|
|
39
|
+
--drawer-nested-backdrop: hsl(0 0% 0% / 0.15);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
@media (prefers-color-scheme: dark) {
|
|
43
|
+
:root {
|
|
44
|
+
--drawer-bg: hsl(0 0% 12%);
|
|
45
|
+
--drawer-handle-bg: hsl(0 0% 35%);
|
|
46
|
+
--drawer-handle-bg-hover: hsl(0 0% 50%);
|
|
47
|
+
--drawer-shadow-bottom: 0 -10px 60px hsl(0 0% 0% / 0.4), 0 -4px 20px hsl(0 0% 0% / 0.3);
|
|
48
|
+
--drawer-shadow-top: 0 10px 60px hsl(0 0% 0% / 0.4), 0 4px 20px hsl(0 0% 0% / 0.3);
|
|
49
|
+
--drawer-shadow-right: -10px 0 60px hsl(0 0% 0% / 0.4), -4px 0 20px hsl(0 0% 0% / 0.3);
|
|
50
|
+
--drawer-shadow-left: 10px 0 60px hsl(0 0% 0% / 0.4), 4px 0 20px hsl(0 0% 0% / 0.3);
|
|
51
|
+
--drawer-shadow-modal: 0 25px 50px -12px hsl(0 0% 0% / 0.5);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/* Background scale effect */
|
|
56
|
+
body {
|
|
57
|
+
transition: scale var(--drawer-duration) var(--drawer-ease), border-radius var(--drawer-duration) var(--drawer-ease);
|
|
58
|
+
transform-origin: center top;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
body:has(.drawer[open]) {
|
|
62
|
+
overflow: hidden;
|
|
63
|
+
scale: var(--drawer-nested-scale);
|
|
64
|
+
border-radius: var(--drawer-radius);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/* Base drawer */
|
|
68
|
+
.drawer {
|
|
69
|
+
border: none;
|
|
70
|
+
padding: 0;
|
|
71
|
+
margin: 0;
|
|
72
|
+
max-width: 100%;
|
|
73
|
+
max-height: 100%;
|
|
74
|
+
position: fixed;
|
|
75
|
+
background: var(--drawer-bg);
|
|
76
|
+
overflow: hidden;
|
|
77
|
+
opacity: 0;
|
|
78
|
+
transition:
|
|
79
|
+
display var(--drawer-duration-close) allow-discrete,
|
|
80
|
+
overlay var(--drawer-duration-close) allow-discrete,
|
|
81
|
+
translate var(--drawer-duration-close) var(--drawer-ease),
|
|
82
|
+
scale var(--drawer-duration-close) var(--drawer-ease),
|
|
83
|
+
filter var(--drawer-duration-close) ease,
|
|
84
|
+
opacity var(--drawer-duration-close) ease;
|
|
85
|
+
|
|
86
|
+
/* Default: bottom */
|
|
87
|
+
--_translate-closed: 0 100%;
|
|
88
|
+
--_border-radius: var(--drawer-radius) var(--drawer-radius) 0 0;
|
|
89
|
+
inset: auto 0 0 0;
|
|
90
|
+
margin-inline: auto;
|
|
91
|
+
width: 100%;
|
|
92
|
+
max-width: var(--drawer-max-width);
|
|
93
|
+
height: auto;
|
|
94
|
+
max-height: var(--drawer-max-height);
|
|
95
|
+
border-radius: var(--drawer-border-radius, var(--_border-radius));
|
|
96
|
+
box-shadow: var(--drawer-shadow-bottom);
|
|
97
|
+
translate: var(--_translate-closed);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
.drawer::backdrop {
|
|
101
|
+
background: var(--drawer-backdrop);
|
|
102
|
+
opacity: 0;
|
|
103
|
+
backdrop-filter: blur(var(--drawer-backdrop-blur));
|
|
104
|
+
-webkit-backdrop-filter: blur(var(--drawer-backdrop-blur));
|
|
105
|
+
transition:
|
|
106
|
+
display var(--drawer-duration-close) allow-discrete,
|
|
107
|
+
overlay var(--drawer-duration-close) allow-discrete,
|
|
108
|
+
opacity var(--drawer-duration-close) ease;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
.drawer[open] {
|
|
112
|
+
opacity: 1;
|
|
113
|
+
translate: 0 0;
|
|
114
|
+
transition:
|
|
115
|
+
display var(--drawer-duration) allow-discrete,
|
|
116
|
+
overlay var(--drawer-duration) allow-discrete,
|
|
117
|
+
translate var(--drawer-duration) var(--drawer-ease),
|
|
118
|
+
scale var(--drawer-duration) var(--drawer-ease),
|
|
119
|
+
filter var(--drawer-duration) ease,
|
|
120
|
+
opacity 0.15s ease;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
.drawer[open]::backdrop {
|
|
124
|
+
opacity: 1;
|
|
125
|
+
transition: display var(--drawer-duration) allow-discrete, overlay var(--drawer-duration) allow-discrete, opacity 0.2s ease;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
@starting-style {
|
|
129
|
+
.drawer[open] { opacity: 0; translate: var(--_translate-closed); }
|
|
130
|
+
.drawer[open]::backdrop { opacity: 0; }
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/* ===== DIRECTIONS ===== */
|
|
134
|
+
|
|
135
|
+
/* Right */
|
|
136
|
+
.drawer[data-direction="right"] {
|
|
137
|
+
--_translate-closed: 100% 0;
|
|
138
|
+
--_border-radius: var(--drawer-radius) 0 0 var(--drawer-radius);
|
|
139
|
+
inset: 0 0 0 auto;
|
|
140
|
+
margin: 0;
|
|
141
|
+
width: 100%;
|
|
142
|
+
max-width: var(--drawer-max-width);
|
|
143
|
+
height: 100dvh;
|
|
144
|
+
max-height: 100dvh;
|
|
145
|
+
box-shadow: var(--drawer-shadow-right);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/* Left */
|
|
149
|
+
.drawer[data-direction="left"] {
|
|
150
|
+
--_translate-closed: -100% 0;
|
|
151
|
+
--_border-radius: 0 var(--drawer-radius) var(--drawer-radius) 0;
|
|
152
|
+
inset: 0 auto 0 0;
|
|
153
|
+
margin: 0;
|
|
154
|
+
width: 100%;
|
|
155
|
+
max-width: var(--drawer-max-width);
|
|
156
|
+
height: 100dvh;
|
|
157
|
+
max-height: 100dvh;
|
|
158
|
+
box-shadow: var(--drawer-shadow-left);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/* Top */
|
|
162
|
+
.drawer[data-direction="top"] {
|
|
163
|
+
--_translate-closed: 0 -100%;
|
|
164
|
+
--_border-radius: 0 0 var(--drawer-radius) var(--drawer-radius);
|
|
165
|
+
inset: 0 0 auto 0;
|
|
166
|
+
margin-inline: auto;
|
|
167
|
+
width: 100%;
|
|
168
|
+
max-width: var(--drawer-max-width);
|
|
169
|
+
height: auto;
|
|
170
|
+
max-height: var(--drawer-max-height);
|
|
171
|
+
box-shadow: var(--drawer-shadow-top);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/* Modal (centered) */
|
|
175
|
+
.drawer[data-direction="modal"] {
|
|
176
|
+
--_translate-closed: 0 0;
|
|
177
|
+
--_border-radius: var(--drawer-radius);
|
|
178
|
+
inset: 0;
|
|
179
|
+
margin: auto;
|
|
180
|
+
width: fit-content;
|
|
181
|
+
max-width: var(--drawer-max-width);
|
|
182
|
+
height: fit-content;
|
|
183
|
+
max-height: var(--drawer-max-height);
|
|
184
|
+
box-shadow: var(--drawer-shadow-modal);
|
|
185
|
+
scale: var(--drawer-nested-scale);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
.drawer[data-direction="modal"][open] {
|
|
189
|
+
scale: 1;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
@starting-style {
|
|
193
|
+
.drawer[data-direction="modal"][open] {
|
|
194
|
+
scale: var(--drawer-nested-scale);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/* ===== AUTO-NESTING (up to 5 levels) ===== */
|
|
199
|
+
/* Uses sibling combinators to count open drawers */
|
|
200
|
+
|
|
201
|
+
/* 1+ open drawers after */
|
|
202
|
+
.drawer[open]:has(~ .drawer[open]) {
|
|
203
|
+
scale: var(--drawer-nested-scale);
|
|
204
|
+
translate: 0 calc(-1 * var(--drawer-nested-offset));
|
|
205
|
+
border-radius: var(--drawer-radius);
|
|
206
|
+
filter: brightness(var(--drawer-nested-brightness));
|
|
207
|
+
pointer-events: none;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/* 2+ open drawers after */
|
|
211
|
+
.drawer[open]:has(~ .drawer[open] ~ .drawer[open]) {
|
|
212
|
+
scale: calc(var(--drawer-nested-scale) * var(--drawer-nested-scale));
|
|
213
|
+
translate: 0 calc(-2 * var(--drawer-nested-offset));
|
|
214
|
+
filter: brightness(calc(var(--drawer-nested-brightness) * var(--drawer-nested-brightness)));
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/* 3+ open drawers after */
|
|
218
|
+
.drawer[open]:has(~ .drawer[open] ~ .drawer[open] ~ .drawer[open]) {
|
|
219
|
+
scale: calc(var(--drawer-nested-scale) * var(--drawer-nested-scale) * var(--drawer-nested-scale));
|
|
220
|
+
translate: 0 calc(-3 * var(--drawer-nested-offset));
|
|
221
|
+
filter: brightness(calc(var(--drawer-nested-brightness) * var(--drawer-nested-brightness) * var(--drawer-nested-brightness)));
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/* 4+ open drawers after */
|
|
225
|
+
.drawer[open]:has(~ .drawer[open] ~ .drawer[open] ~ .drawer[open] ~ .drawer[open]) {
|
|
226
|
+
scale: calc(var(--drawer-nested-scale) * var(--drawer-nested-scale) * var(--drawer-nested-scale) * var(--drawer-nested-scale));
|
|
227
|
+
translate: 0 calc(-4 * var(--drawer-nested-offset));
|
|
228
|
+
filter: brightness(calc(var(--drawer-nested-brightness) * var(--drawer-nested-brightness) * var(--drawer-nested-brightness) * var(--drawer-nested-brightness)));
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/* 5+ open drawers after */
|
|
232
|
+
.drawer[open]:has(~ .drawer[open] ~ .drawer[open] ~ .drawer[open] ~ .drawer[open] ~ .drawer[open]) {
|
|
233
|
+
scale: calc(var(--drawer-nested-scale) * var(--drawer-nested-scale) * var(--drawer-nested-scale) * var(--drawer-nested-scale) * var(--drawer-nested-scale));
|
|
234
|
+
translate: 0 calc(-5 * var(--drawer-nested-offset));
|
|
235
|
+
filter: brightness(calc(var(--drawer-nested-brightness) * var(--drawer-nested-brightness) * var(--drawer-nested-brightness) * var(--drawer-nested-brightness) * var(--drawer-nested-brightness)));
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/* Lighter backdrop for stacked drawers */
|
|
239
|
+
.drawer[open] ~ .drawer[open]::backdrop {
|
|
240
|
+
background: var(--drawer-nested-backdrop);
|
|
241
|
+
backdrop-filter: none;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
/* Handle */
|
|
245
|
+
.drawer-handle {
|
|
246
|
+
display: flex;
|
|
247
|
+
justify-content: center;
|
|
248
|
+
padding-block: var(--drawer-handle-padding-block);
|
|
249
|
+
padding-inline: var(--drawer-handle-padding-inline);
|
|
250
|
+
cursor: grab;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
.drawer-handle::before {
|
|
254
|
+
content: '';
|
|
255
|
+
width: var(--drawer-handle-width);
|
|
256
|
+
height: var(--drawer-handle-height);
|
|
257
|
+
background: var(--drawer-handle-bg);
|
|
258
|
+
border-radius: 100px;
|
|
259
|
+
transition: width 0.2s var(--drawer-ease), height 0.2s var(--drawer-ease), background 0.2s ease;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
.drawer-handle:hover::before {
|
|
263
|
+
width: var(--drawer-handle-width-hover);
|
|
264
|
+
background: var(--drawer-handle-bg-hover);
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
/* Vertical handle for left/right drawers */
|
|
268
|
+
.drawer[data-direction="left"] .drawer-handle,
|
|
269
|
+
.drawer[data-direction="right"] .drawer-handle {
|
|
270
|
+
flex-direction: column;
|
|
271
|
+
align-items: center;
|
|
272
|
+
justify-content: center;
|
|
273
|
+
padding-block: var(--drawer-handle-padding-inline);
|
|
274
|
+
padding-inline: var(--drawer-handle-padding-block);
|
|
275
|
+
height: 100%;
|
|
276
|
+
position: absolute;
|
|
277
|
+
top: 0;
|
|
278
|
+
writing-mode: vertical-lr;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
.drawer[data-direction="left"] .drawer-handle { right: 0; }
|
|
282
|
+
.drawer[data-direction="right"] .drawer-handle { left: 0; }
|
|
283
|
+
|
|
284
|
+
.drawer[data-direction="left"] .drawer-handle::before,
|
|
285
|
+
.drawer[data-direction="right"] .drawer-handle::before {
|
|
286
|
+
width: var(--drawer-handle-height);
|
|
287
|
+
height: var(--drawer-handle-width);
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
.drawer[data-direction="left"] .drawer-handle:hover::before,
|
|
291
|
+
.drawer[data-direction="right"] .drawer-handle:hover::before {
|
|
292
|
+
width: var(--drawer-handle-height);
|
|
293
|
+
height: var(--drawer-handle-width-hover);
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
/* Content - structural only, no opinionated padding */
|
|
297
|
+
.drawer-content {
|
|
298
|
+
overflow-x: hidden;
|
|
299
|
+
overflow-y: auto;
|
|
300
|
+
overscroll-behavior: contain;
|
|
301
|
+
flex: 1;
|
|
302
|
+
min-height: 0;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
/* Content sizing for directions */
|
|
306
|
+
.drawer[data-direction="left"] .drawer-content,
|
|
307
|
+
.drawer[data-direction="right"] .drawer-content {
|
|
308
|
+
height: 100%;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
/* Reduced motion */
|
|
312
|
+
@media (prefers-reduced-motion: reduce) {
|
|
313
|
+
*, *::before, *::after {
|
|
314
|
+
transition-duration: 0.01ms !important;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
body:has(.drawer[open]) {
|
|
318
|
+
scale: 1;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
.drawer[open]:has(~ .drawer[open]),
|
|
322
|
+
.drawer[open]:has(~ .drawer[open] ~ .drawer[open]),
|
|
323
|
+
.drawer[open]:has(~ .drawer[open] ~ .drawer[open] ~ .drawer[open]),
|
|
324
|
+
.drawer[open]:has(~ .drawer[open] ~ .drawer[open] ~ .drawer[open] ~ .drawer[open]),
|
|
325
|
+
.drawer[open]:has(~ .drawer[open] ~ .drawer[open] ~ .drawer[open] ~ .drawer[open] ~ .drawer[open]) {
|
|
326
|
+
scale: 1;
|
|
327
|
+
translate: 0 0;
|
|
328
|
+
filter: none;
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
|
|
@@ -0,0 +1,333 @@
|
|
|
1
|
+
/* CSS Drawer - Vaul-quality drawer with auto-nesting and directions */
|
|
2
|
+
|
|
3
|
+
:root {
|
|
4
|
+
/* Visual */
|
|
5
|
+
--drawer-bg: #fff;
|
|
6
|
+
--drawer-radius: 24px;
|
|
7
|
+
--drawer-backdrop: hsl(0 0% 0% / 0.4);
|
|
8
|
+
--drawer-backdrop-blur: 4px;
|
|
9
|
+
|
|
10
|
+
/* Sizing */
|
|
11
|
+
--drawer-max-width: 500px;
|
|
12
|
+
--drawer-max-height: 96dvh;
|
|
13
|
+
|
|
14
|
+
/* Handle */
|
|
15
|
+
--drawer-handle-bg: hsl(0 0% 80%);
|
|
16
|
+
--drawer-handle-bg-hover: hsl(0 0% 60%);
|
|
17
|
+
--drawer-handle-width: 48px;
|
|
18
|
+
--drawer-handle-width-hover: 56px;
|
|
19
|
+
--drawer-handle-height: 5px;
|
|
20
|
+
--drawer-handle-padding-block: 1rem 0.5rem;
|
|
21
|
+
--drawer-handle-padding-inline: 0;
|
|
22
|
+
|
|
23
|
+
/* Shadows */
|
|
24
|
+
--drawer-shadow-bottom: 0 -10px 60px hsl(0 0% 0% / 0.12), 0 -4px 20px hsl(0 0% 0% / 0.08);
|
|
25
|
+
--drawer-shadow-top: 0 10px 60px hsl(0 0% 0% / 0.12), 0 4px 20px hsl(0 0% 0% / 0.08);
|
|
26
|
+
--drawer-shadow-right: -10px 0 60px hsl(0 0% 0% / 0.12), -4px 0 20px hsl(0 0% 0% / 0.08);
|
|
27
|
+
--drawer-shadow-left: 10px 0 60px hsl(0 0% 0% / 0.12), 4px 0 20px hsl(0 0% 0% / 0.08);
|
|
28
|
+
--drawer-shadow-modal: 0 25px 50px -12px hsl(0 0% 0% / 0.25);
|
|
29
|
+
|
|
30
|
+
/* Animation */
|
|
31
|
+
--drawer-duration: 0.5s;
|
|
32
|
+
--drawer-duration-close: 0.35s;
|
|
33
|
+
--drawer-ease: cubic-bezier(0.32, 0.72, 0, 1);
|
|
34
|
+
|
|
35
|
+
/* Nesting effects */
|
|
36
|
+
--drawer-nested-scale: 0.94;
|
|
37
|
+
--drawer-nested-offset: 20px;
|
|
38
|
+
--drawer-nested-brightness: 0.92;
|
|
39
|
+
--drawer-nested-backdrop: hsl(0 0% 0% / 0.15);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
@media (prefers-color-scheme: dark) {
|
|
43
|
+
:root {
|
|
44
|
+
--drawer-bg: hsl(0 0% 12%);
|
|
45
|
+
--drawer-handle-bg: hsl(0 0% 35%);
|
|
46
|
+
--drawer-handle-bg-hover: hsl(0 0% 50%);
|
|
47
|
+
--drawer-shadow-bottom: 0 -10px 60px hsl(0 0% 0% / 0.4), 0 -4px 20px hsl(0 0% 0% / 0.3);
|
|
48
|
+
--drawer-shadow-top: 0 10px 60px hsl(0 0% 0% / 0.4), 0 4px 20px hsl(0 0% 0% / 0.3);
|
|
49
|
+
--drawer-shadow-right: -10px 0 60px hsl(0 0% 0% / 0.4), -4px 0 20px hsl(0 0% 0% / 0.3);
|
|
50
|
+
--drawer-shadow-left: 10px 0 60px hsl(0 0% 0% / 0.4), 4px 0 20px hsl(0 0% 0% / 0.3);
|
|
51
|
+
--drawer-shadow-modal: 0 25px 50px -12px hsl(0 0% 0% / 0.5);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/* Background scale effect */
|
|
56
|
+
body {
|
|
57
|
+
transition: scale var(--drawer-duration) var(--drawer-ease), border-radius var(--drawer-duration) var(--drawer-ease);
|
|
58
|
+
transform-origin: center top;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
body:has(.drawer[open]) {
|
|
62
|
+
overflow: hidden;
|
|
63
|
+
scale: var(--drawer-nested-scale);
|
|
64
|
+
border-radius: var(--drawer-radius);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/* Base drawer */
|
|
68
|
+
.drawer {
|
|
69
|
+
border: none;
|
|
70
|
+
padding: 0;
|
|
71
|
+
margin: 0;
|
|
72
|
+
max-width: 100%;
|
|
73
|
+
max-height: 100%;
|
|
74
|
+
position: fixed;
|
|
75
|
+
background: var(--drawer-bg);
|
|
76
|
+
overflow: hidden;
|
|
77
|
+
opacity: 0;
|
|
78
|
+
transition:
|
|
79
|
+
display var(--drawer-duration-close) allow-discrete,
|
|
80
|
+
overlay var(--drawer-duration-close) allow-discrete,
|
|
81
|
+
translate var(--drawer-duration-close) var(--drawer-ease),
|
|
82
|
+
scale var(--drawer-duration-close) var(--drawer-ease),
|
|
83
|
+
filter var(--drawer-duration-close) ease,
|
|
84
|
+
opacity var(--drawer-duration-close) ease;
|
|
85
|
+
|
|
86
|
+
/* Default: bottom */
|
|
87
|
+
--_translate-closed: 0 100%;
|
|
88
|
+
--_border-radius: var(--drawer-radius) var(--drawer-radius) 0 0;
|
|
89
|
+
inset: auto 0 0 0;
|
|
90
|
+
margin-inline: auto;
|
|
91
|
+
width: 100%;
|
|
92
|
+
max-width: var(--drawer-max-width);
|
|
93
|
+
height: auto;
|
|
94
|
+
max-height: var(--drawer-max-height);
|
|
95
|
+
border-radius: var(--drawer-border-radius, var(--_border-radius));
|
|
96
|
+
box-shadow: var(--drawer-shadow-bottom);
|
|
97
|
+
translate: var(--_translate-closed);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
.drawer::backdrop {
|
|
101
|
+
background: var(--drawer-backdrop);
|
|
102
|
+
opacity: 0;
|
|
103
|
+
backdrop-filter: blur(var(--drawer-backdrop-blur));
|
|
104
|
+
-webkit-backdrop-filter: blur(var(--drawer-backdrop-blur));
|
|
105
|
+
transition:
|
|
106
|
+
display var(--drawer-duration-close) allow-discrete,
|
|
107
|
+
overlay var(--drawer-duration-close) allow-discrete,
|
|
108
|
+
opacity var(--drawer-duration-close) ease;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
.drawer[open] {
|
|
112
|
+
opacity: 1;
|
|
113
|
+
translate: 0 0;
|
|
114
|
+
transition:
|
|
115
|
+
display var(--drawer-duration) allow-discrete,
|
|
116
|
+
overlay var(--drawer-duration) allow-discrete,
|
|
117
|
+
translate var(--drawer-duration) var(--drawer-ease),
|
|
118
|
+
scale var(--drawer-duration) var(--drawer-ease),
|
|
119
|
+
filter var(--drawer-duration) ease,
|
|
120
|
+
opacity 0.15s ease;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
.drawer[open]::backdrop {
|
|
124
|
+
opacity: 1;
|
|
125
|
+
transition: display var(--drawer-duration) allow-discrete, overlay var(--drawer-duration) allow-discrete, opacity 0.2s ease;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
@starting-style {
|
|
129
|
+
.drawer[open] { opacity: 0; translate: var(--_translate-closed); }
|
|
130
|
+
.drawer[open]::backdrop { opacity: 0; }
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/* ===== DIRECTIONS ===== */
|
|
134
|
+
|
|
135
|
+
/* Right */
|
|
136
|
+
.drawer[data-direction="right"] {
|
|
137
|
+
--_translate-closed: 100% 0;
|
|
138
|
+
--_border-radius: var(--drawer-radius) 0 0 var(--drawer-radius);
|
|
139
|
+
inset: 0 0 0 auto;
|
|
140
|
+
margin: 0;
|
|
141
|
+
width: 100%;
|
|
142
|
+
max-width: var(--drawer-max-width);
|
|
143
|
+
height: 100dvh;
|
|
144
|
+
max-height: 100dvh;
|
|
145
|
+
box-shadow: var(--drawer-shadow-right);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/* Left */
|
|
149
|
+
.drawer[data-direction="left"] {
|
|
150
|
+
--_translate-closed: -100% 0;
|
|
151
|
+
--_border-radius: 0 var(--drawer-radius) var(--drawer-radius) 0;
|
|
152
|
+
inset: 0 auto 0 0;
|
|
153
|
+
margin: 0;
|
|
154
|
+
width: 100%;
|
|
155
|
+
max-width: var(--drawer-max-width);
|
|
156
|
+
height: 100dvh;
|
|
157
|
+
max-height: 100dvh;
|
|
158
|
+
box-shadow: var(--drawer-shadow-left);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/* Top */
|
|
162
|
+
.drawer[data-direction="top"] {
|
|
163
|
+
--_translate-closed: 0 -100%;
|
|
164
|
+
--_border-radius: 0 0 var(--drawer-radius) var(--drawer-radius);
|
|
165
|
+
inset: 0 0 auto 0;
|
|
166
|
+
margin-inline: auto;
|
|
167
|
+
width: 100%;
|
|
168
|
+
max-width: var(--drawer-max-width);
|
|
169
|
+
height: auto;
|
|
170
|
+
max-height: var(--drawer-max-height);
|
|
171
|
+
box-shadow: var(--drawer-shadow-top);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/* Modal (centered) */
|
|
175
|
+
.drawer[data-direction="modal"] {
|
|
176
|
+
--_translate-closed: 0 0;
|
|
177
|
+
--_border-radius: var(--drawer-radius);
|
|
178
|
+
inset: 0;
|
|
179
|
+
margin: auto;
|
|
180
|
+
width: fit-content;
|
|
181
|
+
max-width: var(--drawer-max-width);
|
|
182
|
+
height: fit-content;
|
|
183
|
+
max-height: var(--drawer-max-height);
|
|
184
|
+
box-shadow: var(--drawer-shadow-modal);
|
|
185
|
+
scale: var(--drawer-nested-scale);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
.drawer[data-direction="modal"][open] {
|
|
189
|
+
scale: 1;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
@starting-style {
|
|
193
|
+
.drawer[data-direction="modal"][open] {
|
|
194
|
+
scale: var(--drawer-nested-scale);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/* ===== AUTO-NESTING (up to 5 levels) ===== */
|
|
199
|
+
/* Uses sibling combinators to count open drawers */
|
|
200
|
+
|
|
201
|
+
/* 1+ open drawers after */
|
|
202
|
+
.drawer[open]:has(~ .drawer[open]) {
|
|
203
|
+
scale: var(--drawer-nested-scale);
|
|
204
|
+
translate: 0 calc(-1 * var(--drawer-nested-offset));
|
|
205
|
+
border-radius: var(--drawer-radius);
|
|
206
|
+
filter: brightness(var(--drawer-nested-brightness));
|
|
207
|
+
pointer-events: none;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/* 2+ open drawers after */
|
|
211
|
+
.drawer[open]:has(~ .drawer[open] ~ .drawer[open]) {
|
|
212
|
+
scale: calc(var(--drawer-nested-scale) * var(--drawer-nested-scale));
|
|
213
|
+
translate: 0 calc(-2 * var(--drawer-nested-offset));
|
|
214
|
+
filter: brightness(calc(var(--drawer-nested-brightness) * var(--drawer-nested-brightness)));
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/* 3+ open drawers after */
|
|
218
|
+
.drawer[open]:has(~ .drawer[open] ~ .drawer[open] ~ .drawer[open]) {
|
|
219
|
+
scale: calc(var(--drawer-nested-scale) * var(--drawer-nested-scale) * var(--drawer-nested-scale));
|
|
220
|
+
translate: 0 calc(-3 * var(--drawer-nested-offset));
|
|
221
|
+
filter: brightness(calc(var(--drawer-nested-brightness) * var(--drawer-nested-brightness) * var(--drawer-nested-brightness)));
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/* 4+ open drawers after */
|
|
225
|
+
.drawer[open]:has(~ .drawer[open] ~ .drawer[open] ~ .drawer[open] ~ .drawer[open]) {
|
|
226
|
+
scale: calc(var(--drawer-nested-scale) * var(--drawer-nested-scale) * var(--drawer-nested-scale) * var(--drawer-nested-scale));
|
|
227
|
+
translate: 0 calc(-4 * var(--drawer-nested-offset));
|
|
228
|
+
filter: brightness(calc(var(--drawer-nested-brightness) * var(--drawer-nested-brightness) * var(--drawer-nested-brightness) * var(--drawer-nested-brightness)));
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/* 5+ open drawers after */
|
|
232
|
+
.drawer[open]:has(~ .drawer[open] ~ .drawer[open] ~ .drawer[open] ~ .drawer[open] ~ .drawer[open]) {
|
|
233
|
+
scale: calc(var(--drawer-nested-scale) * var(--drawer-nested-scale) * var(--drawer-nested-scale) * var(--drawer-nested-scale) * var(--drawer-nested-scale));
|
|
234
|
+
translate: 0 calc(-5 * var(--drawer-nested-offset));
|
|
235
|
+
filter: brightness(calc(var(--drawer-nested-brightness) * var(--drawer-nested-brightness) * var(--drawer-nested-brightness) * var(--drawer-nested-brightness) * var(--drawer-nested-brightness)));
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/* Lighter backdrop for stacked drawers */
|
|
239
|
+
.drawer[open] ~ .drawer[open]::backdrop {
|
|
240
|
+
background: var(--drawer-nested-backdrop);
|
|
241
|
+
backdrop-filter: none;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
/* Handle */
|
|
245
|
+
.drawer-handle {
|
|
246
|
+
display: flex;
|
|
247
|
+
justify-content: center;
|
|
248
|
+
padding-block: var(--drawer-handle-padding-block);
|
|
249
|
+
padding-inline: var(--drawer-handle-padding-inline);
|
|
250
|
+
cursor: grab;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
.drawer-handle::before {
|
|
254
|
+
content: '';
|
|
255
|
+
width: var(--drawer-handle-width);
|
|
256
|
+
height: var(--drawer-handle-height);
|
|
257
|
+
background: var(--drawer-handle-bg);
|
|
258
|
+
border-radius: 100px;
|
|
259
|
+
transition: width 0.2s var(--drawer-ease), height 0.2s var(--drawer-ease), background 0.2s ease;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
.drawer-handle:hover::before {
|
|
263
|
+
width: var(--drawer-handle-width-hover);
|
|
264
|
+
background: var(--drawer-handle-bg-hover);
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
/* Vertical handle for left/right drawers */
|
|
268
|
+
.drawer[data-direction="left"] .drawer-handle,
|
|
269
|
+
.drawer[data-direction="right"] .drawer-handle {
|
|
270
|
+
flex-direction: column;
|
|
271
|
+
align-items: center;
|
|
272
|
+
justify-content: center;
|
|
273
|
+
padding-block: var(--drawer-handle-padding-inline);
|
|
274
|
+
padding-inline: var(--drawer-handle-padding-block);
|
|
275
|
+
height: 100%;
|
|
276
|
+
position: absolute;
|
|
277
|
+
top: 0;
|
|
278
|
+
writing-mode: vertical-lr;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
.drawer[data-direction="left"] .drawer-handle { right: 0; }
|
|
282
|
+
.drawer[data-direction="right"] .drawer-handle { left: 0; }
|
|
283
|
+
|
|
284
|
+
.drawer[data-direction="left"] .drawer-handle::before,
|
|
285
|
+
.drawer[data-direction="right"] .drawer-handle::before {
|
|
286
|
+
width: var(--drawer-handle-height);
|
|
287
|
+
height: var(--drawer-handle-width);
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
.drawer[data-direction="left"] .drawer-handle:hover::before,
|
|
291
|
+
.drawer[data-direction="right"] .drawer-handle:hover::before {
|
|
292
|
+
width: var(--drawer-handle-height);
|
|
293
|
+
height: var(--drawer-handle-width-hover);
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
/* Content - structural only, no opinionated padding */
|
|
297
|
+
.drawer-content {
|
|
298
|
+
overflow-x: hidden;
|
|
299
|
+
overflow-y: auto;
|
|
300
|
+
overscroll-behavior: contain;
|
|
301
|
+
flex: 1;
|
|
302
|
+
min-height: 0;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
/* Content sizing for directions */
|
|
306
|
+
.drawer[data-direction="left"] .drawer-content,
|
|
307
|
+
.drawer[data-direction="right"] .drawer-content {
|
|
308
|
+
height: 100%;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
/* Reduced motion */
|
|
312
|
+
@media (prefers-reduced-motion: reduce) {
|
|
313
|
+
*, *::before, *::after {
|
|
314
|
+
transition-duration: 0.01ms !important;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
body:has(.drawer[open]) {
|
|
318
|
+
scale: 1;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
.drawer[open]:has(~ .drawer[open]),
|
|
322
|
+
.drawer[open]:has(~ .drawer[open] ~ .drawer[open]),
|
|
323
|
+
.drawer[open]:has(~ .drawer[open] ~ .drawer[open] ~ .drawer[open]),
|
|
324
|
+
.drawer[open]:has(~ .drawer[open] ~ .drawer[open] ~ .drawer[open] ~ .drawer[open]),
|
|
325
|
+
.drawer[open]:has(~ .drawer[open] ~ .drawer[open] ~ .drawer[open] ~ .drawer[open] ~ .drawer[open]) {
|
|
326
|
+
scale: 1;
|
|
327
|
+
translate: 0 0;
|
|
328
|
+
filter: none;
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
|
|
333
|
+
/*# sourceMappingURL=drawer-CXCJQa45.css.map*/
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"drawer-CXCJQa45.css","names":[],"sources":["../src/drawer.css"],"sourcesContent":["/* CSS Drawer - Vaul-quality drawer with auto-nesting and directions */\n\n:root {\n /* Visual */\n --drawer-bg: #fff;\n --drawer-radius: 24px;\n --drawer-backdrop: hsl(0 0% 0% / 0.4);\n --drawer-backdrop-blur: 4px;\n\n /* Sizing */\n --drawer-max-width: 500px;\n --drawer-max-height: 96dvh;\n\n /* Handle */\n --drawer-handle-bg: hsl(0 0% 80%);\n --drawer-handle-bg-hover: hsl(0 0% 60%);\n --drawer-handle-width: 48px;\n --drawer-handle-width-hover: 56px;\n --drawer-handle-height: 5px;\n --drawer-handle-padding-block: 1rem 0.5rem;\n --drawer-handle-padding-inline: 0;\n\n /* Shadows */\n --drawer-shadow-bottom: 0 -10px 60px hsl(0 0% 0% / 0.12), 0 -4px 20px hsl(0 0% 0% / 0.08);\n --drawer-shadow-top: 0 10px 60px hsl(0 0% 0% / 0.12), 0 4px 20px hsl(0 0% 0% / 0.08);\n --drawer-shadow-right: -10px 0 60px hsl(0 0% 0% / 0.12), -4px 0 20px hsl(0 0% 0% / 0.08);\n --drawer-shadow-left: 10px 0 60px hsl(0 0% 0% / 0.12), 4px 0 20px hsl(0 0% 0% / 0.08);\n --drawer-shadow-modal: 0 25px 50px -12px hsl(0 0% 0% / 0.25);\n\n /* Animation */\n --drawer-duration: 0.5s;\n --drawer-duration-close: 0.35s;\n --drawer-ease: cubic-bezier(0.32, 0.72, 0, 1);\n\n /* Nesting effects */\n --drawer-nested-scale: 0.94;\n --drawer-nested-offset: 20px;\n --drawer-nested-brightness: 0.92;\n --drawer-nested-backdrop: hsl(0 0% 0% / 0.15);\n}\n\n@media (prefers-color-scheme: dark) {\n :root {\n --drawer-bg: hsl(0 0% 12%);\n --drawer-handle-bg: hsl(0 0% 35%);\n --drawer-handle-bg-hover: hsl(0 0% 50%);\n --drawer-shadow-bottom: 0 -10px 60px hsl(0 0% 0% / 0.4), 0 -4px 20px hsl(0 0% 0% / 0.3);\n --drawer-shadow-top: 0 10px 60px hsl(0 0% 0% / 0.4), 0 4px 20px hsl(0 0% 0% / 0.3);\n --drawer-shadow-right: -10px 0 60px hsl(0 0% 0% / 0.4), -4px 0 20px hsl(0 0% 0% / 0.3);\n --drawer-shadow-left: 10px 0 60px hsl(0 0% 0% / 0.4), 4px 0 20px hsl(0 0% 0% / 0.3);\n --drawer-shadow-modal: 0 25px 50px -12px hsl(0 0% 0% / 0.5);\n }\n}\n\n/* Background scale effect */\nbody {\n transition: scale var(--drawer-duration) var(--drawer-ease), border-radius var(--drawer-duration) var(--drawer-ease);\n transform-origin: center top;\n}\n\nbody:has(.drawer[open]) {\n overflow: hidden;\n scale: var(--drawer-nested-scale);\n border-radius: var(--drawer-radius);\n}\n\n/* Base drawer */\n.drawer {\n border: none;\n padding: 0;\n margin: 0;\n max-width: 100%;\n max-height: 100%;\n position: fixed;\n background: var(--drawer-bg);\n overflow: hidden;\n opacity: 0;\n transition:\n display var(--drawer-duration-close) allow-discrete,\n overlay var(--drawer-duration-close) allow-discrete,\n translate var(--drawer-duration-close) var(--drawer-ease),\n scale var(--drawer-duration-close) var(--drawer-ease),\n filter var(--drawer-duration-close) ease,\n opacity var(--drawer-duration-close) ease;\n\n /* Default: bottom */\n --_translate-closed: 0 100%;\n --_border-radius: var(--drawer-radius) var(--drawer-radius) 0 0;\n inset: auto 0 0 0;\n margin-inline: auto;\n width: 100%;\n max-width: var(--drawer-max-width);\n height: auto;\n max-height: var(--drawer-max-height);\n border-radius: var(--drawer-border-radius, var(--_border-radius));\n box-shadow: var(--drawer-shadow-bottom);\n translate: var(--_translate-closed);\n}\n\n.drawer::backdrop {\n background: var(--drawer-backdrop);\n opacity: 0;\n backdrop-filter: blur(var(--drawer-backdrop-blur));\n -webkit-backdrop-filter: blur(var(--drawer-backdrop-blur));\n transition:\n display var(--drawer-duration-close) allow-discrete,\n overlay var(--drawer-duration-close) allow-discrete,\n opacity var(--drawer-duration-close) ease;\n}\n\n.drawer[open] {\n opacity: 1;\n translate: 0 0;\n transition:\n display var(--drawer-duration) allow-discrete,\n overlay var(--drawer-duration) allow-discrete,\n translate var(--drawer-duration) var(--drawer-ease),\n scale var(--drawer-duration) var(--drawer-ease),\n filter var(--drawer-duration) ease,\n opacity 0.15s ease;\n}\n\n.drawer[open]::backdrop {\n opacity: 1;\n transition: display var(--drawer-duration) allow-discrete, overlay var(--drawer-duration) allow-discrete, opacity 0.2s ease;\n}\n\n@starting-style {\n .drawer[open] { opacity: 0; translate: var(--_translate-closed); }\n .drawer[open]::backdrop { opacity: 0; }\n}\n\n/* ===== DIRECTIONS ===== */\n\n/* Right */\n.drawer[data-direction=\"right\"] {\n --_translate-closed: 100% 0;\n --_border-radius: var(--drawer-radius) 0 0 var(--drawer-radius);\n inset: 0 0 0 auto;\n margin: 0;\n width: 100%;\n max-width: var(--drawer-max-width);\n height: 100dvh;\n max-height: 100dvh;\n box-shadow: var(--drawer-shadow-right);\n}\n\n/* Left */\n.drawer[data-direction=\"left\"] {\n --_translate-closed: -100% 0;\n --_border-radius: 0 var(--drawer-radius) var(--drawer-radius) 0;\n inset: 0 auto 0 0;\n margin: 0;\n width: 100%;\n max-width: var(--drawer-max-width);\n height: 100dvh;\n max-height: 100dvh;\n box-shadow: var(--drawer-shadow-left);\n}\n\n/* Top */\n.drawer[data-direction=\"top\"] {\n --_translate-closed: 0 -100%;\n --_border-radius: 0 0 var(--drawer-radius) var(--drawer-radius);\n inset: 0 0 auto 0;\n margin-inline: auto;\n width: 100%;\n max-width: var(--drawer-max-width);\n height: auto;\n max-height: var(--drawer-max-height);\n box-shadow: var(--drawer-shadow-top);\n}\n\n/* Modal (centered) */\n.drawer[data-direction=\"modal\"] {\n --_translate-closed: 0 0;\n --_border-radius: var(--drawer-radius);\n inset: 0;\n margin: auto;\n width: fit-content;\n max-width: var(--drawer-max-width);\n height: fit-content;\n max-height: var(--drawer-max-height);\n box-shadow: var(--drawer-shadow-modal);\n scale: var(--drawer-nested-scale);\n}\n\n.drawer[data-direction=\"modal\"][open] {\n scale: 1;\n}\n\n@starting-style {\n .drawer[data-direction=\"modal\"][open] {\n scale: var(--drawer-nested-scale);\n }\n}\n\n/* ===== AUTO-NESTING (up to 5 levels) ===== */\n/* Uses sibling combinators to count open drawers */\n\n/* 1+ open drawers after */\n.drawer[open]:has(~ .drawer[open]) {\n scale: var(--drawer-nested-scale);\n translate: 0 calc(-1 * var(--drawer-nested-offset));\n border-radius: var(--drawer-radius);\n filter: brightness(var(--drawer-nested-brightness));\n pointer-events: none;\n}\n\n/* 2+ open drawers after */\n.drawer[open]:has(~ .drawer[open] ~ .drawer[open]) {\n scale: calc(var(--drawer-nested-scale) * var(--drawer-nested-scale));\n translate: 0 calc(-2 * var(--drawer-nested-offset));\n filter: brightness(calc(var(--drawer-nested-brightness) * var(--drawer-nested-brightness)));\n}\n\n/* 3+ open drawers after */\n.drawer[open]:has(~ .drawer[open] ~ .drawer[open] ~ .drawer[open]) {\n scale: calc(var(--drawer-nested-scale) * var(--drawer-nested-scale) * var(--drawer-nested-scale));\n translate: 0 calc(-3 * var(--drawer-nested-offset));\n filter: brightness(calc(var(--drawer-nested-brightness) * var(--drawer-nested-brightness) * var(--drawer-nested-brightness)));\n}\n\n/* 4+ open drawers after */\n.drawer[open]:has(~ .drawer[open] ~ .drawer[open] ~ .drawer[open] ~ .drawer[open]) {\n scale: calc(var(--drawer-nested-scale) * var(--drawer-nested-scale) * var(--drawer-nested-scale) * var(--drawer-nested-scale));\n translate: 0 calc(-4 * var(--drawer-nested-offset));\n filter: brightness(calc(var(--drawer-nested-brightness) * var(--drawer-nested-brightness) * var(--drawer-nested-brightness) * var(--drawer-nested-brightness)));\n}\n\n/* 5+ open drawers after */\n.drawer[open]:has(~ .drawer[open] ~ .drawer[open] ~ .drawer[open] ~ .drawer[open] ~ .drawer[open]) {\n scale: calc(var(--drawer-nested-scale) * var(--drawer-nested-scale) * var(--drawer-nested-scale) * var(--drawer-nested-scale) * var(--drawer-nested-scale));\n translate: 0 calc(-5 * var(--drawer-nested-offset));\n filter: brightness(calc(var(--drawer-nested-brightness) * var(--drawer-nested-brightness) * var(--drawer-nested-brightness) * var(--drawer-nested-brightness) * var(--drawer-nested-brightness)));\n}\n\n/* Lighter backdrop for stacked drawers */\n.drawer[open] ~ .drawer[open]::backdrop {\n background: var(--drawer-nested-backdrop);\n backdrop-filter: none;\n}\n\n/* Handle */\n.drawer-handle {\n display: flex;\n justify-content: center;\n padding-block: var(--drawer-handle-padding-block);\n padding-inline: var(--drawer-handle-padding-inline);\n cursor: grab;\n}\n\n.drawer-handle::before {\n content: '';\n width: var(--drawer-handle-width);\n height: var(--drawer-handle-height);\n background: var(--drawer-handle-bg);\n border-radius: 100px;\n transition: width 0.2s var(--drawer-ease), height 0.2s var(--drawer-ease), background 0.2s ease;\n}\n\n.drawer-handle:hover::before {\n width: var(--drawer-handle-width-hover);\n background: var(--drawer-handle-bg-hover);\n}\n\n/* Vertical handle for left/right drawers */\n.drawer[data-direction=\"left\"] .drawer-handle,\n.drawer[data-direction=\"right\"] .drawer-handle {\n flex-direction: column;\n align-items: center;\n justify-content: center;\n padding-block: var(--drawer-handle-padding-inline);\n padding-inline: var(--drawer-handle-padding-block);\n height: 100%;\n position: absolute;\n top: 0;\n writing-mode: vertical-lr;\n}\n\n.drawer[data-direction=\"left\"] .drawer-handle { right: 0; }\n.drawer[data-direction=\"right\"] .drawer-handle { left: 0; }\n\n.drawer[data-direction=\"left\"] .drawer-handle::before,\n.drawer[data-direction=\"right\"] .drawer-handle::before {\n width: var(--drawer-handle-height);\n height: var(--drawer-handle-width);\n}\n\n.drawer[data-direction=\"left\"] .drawer-handle:hover::before,\n.drawer[data-direction=\"right\"] .drawer-handle:hover::before {\n width: var(--drawer-handle-height);\n height: var(--drawer-handle-width-hover);\n}\n\n/* Content - structural only, no opinionated padding */\n.drawer-content {\n overflow-x: hidden;\n overflow-y: auto;\n overscroll-behavior: contain;\n flex: 1;\n min-height: 0;\n}\n\n/* Content sizing for directions */\n.drawer[data-direction=\"left\"] .drawer-content,\n.drawer[data-direction=\"right\"] .drawer-content {\n height: 100%;\n}\n\n/* Reduced motion */\n@media (prefers-reduced-motion: reduce) {\n *, *::before, *::after {\n transition-duration: 0.01ms !important;\n }\n\n body:has(.drawer[open]) {\n scale: 1;\n }\n\n .drawer[open]:has(~ .drawer[open]),\n .drawer[open]:has(~ .drawer[open] ~ .drawer[open]),\n .drawer[open]:has(~ .drawer[open] ~ .drawer[open] ~ .drawer[open]),\n .drawer[open]:has(~ .drawer[open] ~ .drawer[open] ~ .drawer[open] ~ .drawer[open]),\n .drawer[open]:has(~ .drawer[open] ~ .drawer[open] ~ .drawer[open] ~ .drawer[open] ~ .drawer[open]) {\n scale: 1;\n translate: 0 0;\n filter: none;\n }\n}\n"],"mappings":"AAAA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export{};
|
package/dist/index.cjs
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
require('./drawer-C21nwiwE.css');
|
|
2
|
+
if(require(`./drawer-BWZh2Fyp.cjs`),typeof window<`u`){let e=()=>{let e=Array.from(document.querySelectorAll(`dialog.drawer[open]`));e.forEach((t,n)=>{n===e.length-1?t.removeAttribute(`inert`):t.setAttribute(`inert`,``)})};new MutationObserver(t=>{for(let n of t)if(n.type===`attributes`&&n.attributeName===`open`&&n.target.classList.contains(`drawer`)){e();break}}).observe(document.body,{subtree:!0,attributes:!0,attributeFilter:[`open`]})}function e(e){return e?typeof e==`string`?document.getElementById(e):e:null}function t(t){e(t)?.showModal()}function n(t){e(t)?.close()}function r(){Array.from(document.querySelectorAll(`dialog.drawer[open]`)).reverse().forEach(e=>e.close())}function i(t){return e(t)?.open??!1}function a(){return Array.from(document.querySelectorAll(`dialog.drawer[open]`))}function o(){let e=a();return e[e.length-1]??null}function s(e={}){let{id:t,content:n=``,direction:r,handle:i=!0,className:a=``,closeOnOutsideClick:o=!0}=e,s=document.createElement(`dialog`);return s.className=`drawer ${a}`.trim(),t&&(s.id=t),r&&(s.dataset.direction=r),o||(s.dataset.closeOnOutsideClick=`false`),s.innerHTML=`
|
|
3
|
+
${i?`<div class="drawer-handle"></div>`:``}
|
|
3
4
|
<div class="drawer-content">${n}</div>
|
|
4
|
-
`,
|
|
5
|
+
`,s.addEventListener(`click`,e=>{e.target===s&&s.dataset.closeOnOutsideClick!==`false`&&s.close()}),s}function c(e){return document.body.appendChild(e),e}function l(t){e(t)?.remove()}function u(){let e=e=>{let t=e.target;t.matches(`dialog.drawer`)&&t.dataset.closeOnOutsideClick!==`false`&&t.close()};return document.addEventListener(`click`,e),()=>document.removeEventListener(`click`,e)}function d(t,n){let r=e(t);if(!r)return()=>{};let{onOpen:i,onClose:a,onCancel:o}=n,s=()=>a?.(),c=()=>o?.(),l=new MutationObserver(e=>{for(let t of e)t.attributeName===`open`&&r.open&&i?.()});return r.addEventListener(`close`,s),r.addEventListener(`cancel`,c),l.observe(r,{attributes:!0}),()=>{r.removeEventListener(`close`,s),r.removeEventListener(`cancel`,c),l.disconnect()}}function f(e,t){let n=t?.closeOnOutsideClick??!0;return{id:e,className:`drawer`,"data-close-on-outside-click":n?void 0:`false`,onClick:e=>{n&&e.target===e.currentTarget&&e.currentTarget.close()}}}exports.close=n,exports.closeAll=r,exports.create=s,exports.getOpen=a,exports.getTop=o,exports.init=u,exports.isOpen=i,exports.mount=c,exports.open=t,exports.props=f,exports.subscribe=d,exports.unmount=l;
|
package/dist/index.d.cts
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
//#region src/index.d.ts
|
|
2
|
-
|
|
3
|
-
* CSS Drawer - Headless drawer component
|
|
4
|
-
* Works with any framework: React, Vue, Svelte, vanilla JS
|
|
5
|
-
*/
|
|
2
|
+
|
|
6
3
|
type DrawerElement = HTMLDialogElement;
|
|
7
4
|
type DrawerRef = string | DrawerElement | null | undefined;
|
|
5
|
+
type DrawerDirection = 'bottom' | 'top' | 'left' | 'right' | 'modal';
|
|
8
6
|
interface CreateDrawerOptions {
|
|
9
7
|
/** Drawer ID */
|
|
10
8
|
id?: string;
|
|
11
9
|
/** HTML content for the drawer */
|
|
12
10
|
content?: string;
|
|
11
|
+
/** Direction the drawer opens from (default: 'bottom') */
|
|
12
|
+
direction?: DrawerDirection;
|
|
13
13
|
/** Include drag handle (default: true) */
|
|
14
14
|
handle?: boolean;
|
|
15
15
|
/** Additional CSS classes */
|
|
@@ -85,5 +85,5 @@ declare function props(id: string, options?: {
|
|
|
85
85
|
readonly onClick: (e: MouseEvent) => void;
|
|
86
86
|
};
|
|
87
87
|
//#endregion
|
|
88
|
-
export { CreateDrawerOptions, DrawerElement, DrawerEventHandlers, DrawerRef, close, closeAll, create, getOpen, getTop, init, isOpen, mount, open, props, subscribe, unmount };
|
|
88
|
+
export { CreateDrawerOptions, DrawerDirection, DrawerElement, DrawerEventHandlers, DrawerRef, close, closeAll, create, getOpen, getTop, init, isOpen, mount, open, props, subscribe, unmount };
|
|
89
89
|
//# sourceMappingURL=index.d.cts.map
|
package/dist/index.d.cts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.cts","names":[],"sources":["../src/index.ts"],"sourcesContent":[],"mappings":";;
|
|
1
|
+
{"version":3,"file":"index.d.cts","names":[],"sources":["../src/index.ts"],"sourcesContent":[],"mappings":";;AA8DiB,KArBL,aAAA,GAAgB,iBAqBQ;AAuBpB,KA1CJ,SAAA,GA0CiB,MAAA,GA1CI,aA0CK,GAAA,IAAA,GAAA,SAAA;AAQtB,KAhDJ,eAAA,GAgDkB,QAAS,GAAA,KAAA,GAAA,MAAA,GAAA,OAAA,GAAA,OAAA;AAQvB,UAtDC,mBAAA,CAsDO;EAQR;EAQA,EAAA,CAAA,EAAA,MAAO;EAOP;EAQA,OAAA,CAAA,EAAM,MAAA;EA6BN;EAQA,SAAA,CAAO,EApHT,eAoHkB;EAUhB;EAgBA,MAAA,CAAA,EAAA,OAAS;EAoCT;;;;;UAzKC,mBAAA;;;;;;;;;;;iBAuBD,IAAA,SAAa;;;;iBAQb,KAAA,SAAc;;;;iBAQd,QAAA,CAAA;;;;iBAQA,MAAA,SAAe;;;;iBAQf,OAAA,CAAA,GAAW;;;;iBAOX,MAAA,CAAA,GAAU;;;;iBAQV,MAAA,WAAgB,sBAA2B;;;;iBA6B3C,KAAA,SAAc,gBAAgB;;;;iBAQ9B,OAAA,SAAgB;;;;;;iBAUhB,IAAA,CAAA;;;;;iBAgBA,SAAA,SACN,qBACE;;;;;iBAkCI,KAAA;;;;;;wBAMC"}
|
package/dist/index.d.mts
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
//#region src/index.d.ts
|
|
2
|
-
|
|
3
|
-
* CSS Drawer - Headless drawer component
|
|
4
|
-
* Works with any framework: React, Vue, Svelte, vanilla JS
|
|
5
|
-
*/
|
|
2
|
+
|
|
6
3
|
type DrawerElement = HTMLDialogElement;
|
|
7
4
|
type DrawerRef = string | DrawerElement | null | undefined;
|
|
5
|
+
type DrawerDirection = 'bottom' | 'top' | 'left' | 'right' | 'modal';
|
|
8
6
|
interface CreateDrawerOptions {
|
|
9
7
|
/** Drawer ID */
|
|
10
8
|
id?: string;
|
|
11
9
|
/** HTML content for the drawer */
|
|
12
10
|
content?: string;
|
|
11
|
+
/** Direction the drawer opens from (default: 'bottom') */
|
|
12
|
+
direction?: DrawerDirection;
|
|
13
13
|
/** Include drag handle (default: true) */
|
|
14
14
|
handle?: boolean;
|
|
15
15
|
/** Additional CSS classes */
|
|
@@ -85,5 +85,5 @@ declare function props(id: string, options?: {
|
|
|
85
85
|
readonly onClick: (e: MouseEvent) => void;
|
|
86
86
|
};
|
|
87
87
|
//#endregion
|
|
88
|
-
export { CreateDrawerOptions, DrawerElement, DrawerEventHandlers, DrawerRef, close, closeAll, create, getOpen, getTop, init, isOpen, mount, open, props, subscribe, unmount };
|
|
88
|
+
export { CreateDrawerOptions, DrawerDirection, DrawerElement, DrawerEventHandlers, DrawerRef, close, closeAll, create, getOpen, getTop, init, isOpen, mount, open, props, subscribe, unmount };
|
|
89
89
|
//# sourceMappingURL=index.d.mts.map
|
package/dist/index.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/index.ts"],"sourcesContent":[],"mappings":";;
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/index.ts"],"sourcesContent":[],"mappings":";;AA8DiB,KArBL,aAAA,GAAgB,iBAqBQ;AAuBpB,KA1CJ,SAAA,GA0CiB,MAAA,GA1CI,aA0CK,GAAA,IAAA,GAAA,SAAA;AAQtB,KAhDJ,eAAA,GAgDkB,QAAS,GAAA,KAAA,GAAA,MAAA,GAAA,OAAA,GAAA,OAAA;AAQvB,UAtDC,mBAAA,CAsDO;EAQR;EAQA,EAAA,CAAA,EAAA,MAAO;EAOP;EAQA,OAAA,CAAA,EAAM,MAAA;EA6BN;EAQA,SAAA,CAAO,EApHT,eAoHkB;EAUhB;EAgBA,MAAA,CAAA,EAAA,OAAS;EAoCT;;;;;UAzKC,mBAAA;;;;;;;;;;;iBAuBD,IAAA,SAAa;;;;iBAQb,KAAA,SAAc;;;;iBAQd,QAAA,CAAA;;;;iBAQA,MAAA,SAAe;;;;iBAQf,OAAA,CAAA,GAAW;;;;iBAOX,MAAA,CAAA,GAAU;;;;iBAQV,MAAA,WAAgB,sBAA2B;;;;iBA6B3C,KAAA,SAAc,gBAAgB;;;;iBAQ9B,OAAA,SAAgB;;;;;;iBAUhB,IAAA,CAAA;;;;;iBAgBA,SAAA,SACN,qBACE;;;;;iBAkCI,KAAA;;;;;;wBAMC"}
|
package/dist/index.mjs
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
import"./drawer-CiHZcyXE.mjs";import './drawer-CXCJQa45.css';
|
|
2
|
+
if(typeof window<`u`){let e=()=>{let e=Array.from(document.querySelectorAll(`dialog.drawer[open]`));e.forEach((t,n)=>{n===e.length-1?t.removeAttribute(`inert`):t.setAttribute(`inert`,``)})};new MutationObserver(t=>{for(let n of t)if(n.type===`attributes`&&n.attributeName===`open`&&n.target.classList.contains(`drawer`)){e();break}}).observe(document.body,{subtree:!0,attributes:!0,attributeFilter:[`open`]})}function e(e){return e?typeof e==`string`?document.getElementById(e):e:null}function t(t){e(t)?.showModal()}function n(t){e(t)?.close()}function r(){Array.from(document.querySelectorAll(`dialog.drawer[open]`)).reverse().forEach(e=>e.close())}function i(t){return e(t)?.open??!1}function a(){return Array.from(document.querySelectorAll(`dialog.drawer[open]`))}function o(){let e=a();return e[e.length-1]??null}function s(e={}){let{id:t,content:n=``,direction:r,handle:i=!0,className:a=``,closeOnOutsideClick:o=!0}=e,s=document.createElement(`dialog`);return s.className=`drawer ${a}`.trim(),t&&(s.id=t),r&&(s.dataset.direction=r),o||(s.dataset.closeOnOutsideClick=`false`),s.innerHTML=`
|
|
3
|
+
${i?`<div class="drawer-handle"></div>`:``}
|
|
3
4
|
<div class="drawer-content">${n}</div>
|
|
4
|
-
`,
|
|
5
|
+
`,s.addEventListener(`click`,e=>{e.target===s&&s.dataset.closeOnOutsideClick!==`false`&&s.close()}),s}function c(e){return document.body.appendChild(e),e}function l(t){e(t)?.remove()}function u(){let e=e=>{let t=e.target;t.matches(`dialog.drawer`)&&t.dataset.closeOnOutsideClick!==`false`&&t.close()};return document.addEventListener(`click`,e),()=>document.removeEventListener(`click`,e)}function d(t,n){let r=e(t);if(!r)return()=>{};let{onOpen:i,onClose:a,onCancel:o}=n,s=()=>a?.(),c=()=>o?.(),l=new MutationObserver(e=>{for(let t of e)t.attributeName===`open`&&r.open&&i?.()});return r.addEventListener(`close`,s),r.addEventListener(`cancel`,c),l.observe(r,{attributes:!0}),()=>{r.removeEventListener(`close`,s),r.removeEventListener(`cancel`,c),l.disconnect()}}function f(e,t){let n=t?.closeOnOutsideClick??!0;return{id:e,className:`drawer`,"data-close-on-outside-click":n?void 0:`false`,onClick:e=>{n&&e.target===e.currentTarget&&e.currentTarget.close()}}}export{n as close,r as closeAll,s as create,a as getOpen,o as getTop,u as init,i as isOpen,c as mount,t as open,f as props,d as subscribe,l as unmount};
|
|
5
6
|
//# sourceMappingURL=index.mjs.map
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":["open"],"sources":["../src/index.ts"],"sourcesContent":["/**\n * CSS Drawer - Headless drawer component\n * Works with any framework: React, Vue, Svelte, vanilla JS\n */\n\n/* ===== Auto-enable accessibility for stacked drawers ===== */\nif (typeof window !== 'undefined') {\n const updateInertState = () => {\n const openDrawers = Array.from(\n document.querySelectorAll<HTMLDialogElement>('dialog.drawer[open]')\n )\n openDrawers.forEach((drawer, index) => {\n if (index === openDrawers.length - 1) {\n drawer.removeAttribute('inert')\n } else {\n drawer.setAttribute('inert', '')\n }\n })\n }\n\n const observer = new MutationObserver((mutations) => {\n for (const mutation of mutations) {\n if (\n mutation.type === 'attributes' &&\n mutation.attributeName === 'open' &&\n (mutation.target as HTMLElement).classList.contains('drawer')\n ) {\n updateInertState()\n break\n }\n }\n })\n\n observer.observe(document.body, {\n subtree: true,\n attributes: true,\n attributeFilter: ['open'],\n })\n}\n\nexport type DrawerElement = HTMLDialogElement\n\nexport type DrawerRef = string | DrawerElement | null | undefined\n\nexport interface CreateDrawerOptions {\n /** Drawer ID */\n id?: string\n /** HTML content for the drawer */\n content?: string\n /** Include drag handle (default: true) */\n handle?: boolean\n /** Additional CSS classes */\n className?: string\n /** Close when clicking outside (default: true) */\n closeOnOutsideClick?: boolean\n}\n\nexport interface DrawerEventHandlers {\n /** Called when drawer opens */\n onOpen?: () => void\n /** Called when drawer closes */\n onClose?: () => void\n /** Called when drawer is cancelled (backdrop click or Escape) */\n onCancel?: () => void\n}\n\n/**\n * Resolve a drawer reference to an element\n */\nfunction resolve(drawer: DrawerRef): DrawerElement | null {\n if (!drawer) return null\n if (typeof drawer === 'string') {\n return document.getElementById(drawer) as DrawerElement | null\n }\n return drawer\n}\n\n/**\n * Open a drawer by ID or element reference\n */\nexport function open(drawer: DrawerRef): void {\n const el = resolve(drawer)\n el?.showModal()\n}\n\n/**\n * Close a drawer by ID or element reference\n */\nexport function close(drawer: DrawerRef): void {\n const el = resolve(drawer)\n el?.close()\n}\n\n/**\n * Close all open drawers (in reverse DOM order for proper animation)\n */\nexport function closeAll(): void {\n const drawers = Array.from(document.querySelectorAll<DrawerElement>('dialog.drawer[open]'))\n drawers.reverse().forEach((d) => d.close())\n}\n\n/**\n * Check if a drawer is open\n */\nexport function isOpen(drawer: DrawerRef): boolean {\n const el = resolve(drawer)\n return el?.open ?? false\n}\n\n/**\n * Get all open drawers\n */\nexport function getOpen(): DrawerElement[] {\n return Array.from(document.querySelectorAll<DrawerElement>('dialog.drawer[open]'))\n}\n\n/**\n * Get the topmost open drawer\n */\nexport function getTop(): DrawerElement | null {\n const open = getOpen()\n return open[open.length - 1] ?? null\n}\n\n/**\n * Create a drawer element programmatically\n */\nexport function create(options: CreateDrawerOptions = {}): DrawerElement {\n const { id, content = '', handle = true, className = '', closeOnOutsideClick = true } = options\n\n const dialog = document.createElement('dialog') as DrawerElement\n dialog.className = `drawer ${className}`.trim()\n if (id) dialog.id = id\n if (!closeOnOutsideClick) {\n dialog.dataset.closeOnOutsideClick = 'false'\n }\n\n dialog.innerHTML = `\n ${handle ? '<div class=\"drawer-handle\"></div>' : ''}\n <div class=\"drawer-content\">${content}</div>\n `\n\n // Backdrop click to close (respects data attribute)\n dialog.addEventListener('click', (e) => {\n if (e.target === dialog && dialog.dataset.closeOnOutsideClick !== 'false') {\n dialog.close()\n }\n })\n\n return dialog\n}\n\n/**\n * Mount a drawer to the DOM (appends to body)\n */\nexport function mount(drawer: DrawerElement): DrawerElement {\n document.body.appendChild(drawer)\n return drawer\n}\n\n/**\n * Unmount a drawer from the DOM\n */\nexport function unmount(drawer: DrawerRef): void {\n const el = resolve(drawer)\n el?.remove()\n}\n\n/**\n * Initialize global backdrop-click-to-close behavior\n * Alternative to adding onclick to each drawer\n * Respects data-close-on-outside-click=\"false\" attribute\n */\nexport function init(): () => void {\n const handler = (e: MouseEvent) => {\n const target = e.target as HTMLElement\n if (target.matches('dialog.drawer') && (target as DrawerElement).dataset.closeOnOutsideClick !== 'false') {\n ;(target as DrawerElement).close()\n }\n }\n\n document.addEventListener('click', handler)\n return () => document.removeEventListener('click', handler)\n}\n\n/**\n * Subscribe to drawer events\n * @returns Cleanup function\n */\nexport function subscribe(\n drawer: DrawerRef,\n handlers: DrawerEventHandlers\n): () => void {\n const el = resolve(drawer)\n if (!el) return () => {}\n\n const { onOpen, onClose, onCancel } = handlers\n\n const handleClose = () => onClose?.()\n const handleCancel = () => onCancel?.()\n\n // Use MutationObserver to detect open attribute\n const observer = new MutationObserver((mutations) => {\n for (const mutation of mutations) {\n if (mutation.attributeName === 'open' && el.open) {\n onOpen?.()\n }\n }\n })\n\n el.addEventListener('close', handleClose)\n el.addEventListener('cancel', handleCancel)\n observer.observe(el, { attributes: true })\n\n return () => {\n el.removeEventListener('close', handleClose)\n el.removeEventListener('cancel', handleCancel)\n observer.disconnect()\n }\n}\n\n/**\n * React-friendly hook helper - returns props to spread on dialog\n * Usage: <dialog {...drawer.props('my-drawer')} />\n */\nexport function props(id: string, options?: { closeOnOutsideClick?: boolean }) {\n const closeOnOutsideClick = options?.closeOnOutsideClick ?? true\n return {\n id,\n className: 'drawer',\n 'data-close-on-outside-click': closeOnOutsideClick ? undefined : 'false',\n onClick: (e: MouseEvent) => {\n if (closeOnOutsideClick && e.target === e.currentTarget) {\n ;(e.currentTarget as DrawerElement).close()\n }\n },\n } as const\n}\n\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.mjs","names":["open"],"sources":["../src/index.ts"],"sourcesContent":["/**\n * CSS Drawer - Headless drawer component\n * Works with any framework: React, Vue, Svelte, vanilla JS\n */\nimport './drawer.css'\n\n/* ===== Auto-enable accessibility for stacked drawers ===== */\nif (typeof window !== 'undefined') {\n const updateInertState = () => {\n const openDrawers = Array.from(\n document.querySelectorAll<HTMLDialogElement>('dialog.drawer[open]')\n )\n openDrawers.forEach((drawer, index) => {\n if (index === openDrawers.length - 1) {\n drawer.removeAttribute('inert')\n } else {\n drawer.setAttribute('inert', '')\n }\n })\n }\n\n const observer = new MutationObserver((mutations) => {\n for (const mutation of mutations) {\n if (\n mutation.type === 'attributes' &&\n mutation.attributeName === 'open' &&\n (mutation.target as HTMLElement).classList.contains('drawer')\n ) {\n updateInertState()\n break\n }\n }\n })\n\n observer.observe(document.body, {\n subtree: true,\n attributes: true,\n attributeFilter: ['open'],\n })\n}\n\nexport type DrawerElement = HTMLDialogElement\n\nexport type DrawerRef = string | DrawerElement | null | undefined\n\nexport type DrawerDirection = 'bottom' | 'top' | 'left' | 'right' | 'modal'\n\nexport interface CreateDrawerOptions {\n /** Drawer ID */\n id?: string\n /** HTML content for the drawer */\n content?: string\n /** Direction the drawer opens from (default: 'bottom') */\n direction?: DrawerDirection\n /** Include drag handle (default: true) */\n handle?: boolean\n /** Additional CSS classes */\n className?: string\n /** Close when clicking outside (default: true) */\n closeOnOutsideClick?: boolean\n}\n\nexport interface DrawerEventHandlers {\n /** Called when drawer opens */\n onOpen?: () => void\n /** Called when drawer closes */\n onClose?: () => void\n /** Called when drawer is cancelled (backdrop click or Escape) */\n onCancel?: () => void\n}\n\n/**\n * Resolve a drawer reference to an element\n */\nfunction resolve(drawer: DrawerRef): DrawerElement | null {\n if (!drawer) return null\n if (typeof drawer === 'string') {\n return document.getElementById(drawer) as DrawerElement | null\n }\n return drawer\n}\n\n/**\n * Open a drawer by ID or element reference\n */\nexport function open(drawer: DrawerRef): void {\n const el = resolve(drawer)\n el?.showModal()\n}\n\n/**\n * Close a drawer by ID or element reference\n */\nexport function close(drawer: DrawerRef): void {\n const el = resolve(drawer)\n el?.close()\n}\n\n/**\n * Close all open drawers (in reverse DOM order for proper animation)\n */\nexport function closeAll(): void {\n const drawers = Array.from(document.querySelectorAll<DrawerElement>('dialog.drawer[open]'))\n drawers.reverse().forEach((d) => d.close())\n}\n\n/**\n * Check if a drawer is open\n */\nexport function isOpen(drawer: DrawerRef): boolean {\n const el = resolve(drawer)\n return el?.open ?? false\n}\n\n/**\n * Get all open drawers\n */\nexport function getOpen(): DrawerElement[] {\n return Array.from(document.querySelectorAll<DrawerElement>('dialog.drawer[open]'))\n}\n\n/**\n * Get the topmost open drawer\n */\nexport function getTop(): DrawerElement | null {\n const open = getOpen()\n return open[open.length - 1] ?? null\n}\n\n/**\n * Create a drawer element programmatically\n */\nexport function create(options: CreateDrawerOptions = {}): DrawerElement {\n const { id, content = '', direction, handle = true, className = '', closeOnOutsideClick = true } = options\n\n const dialog = document.createElement('dialog') as DrawerElement\n dialog.className = `drawer ${className}`.trim()\n if (id) dialog.id = id\n if (direction) dialog.dataset.direction = direction\n if (!closeOnOutsideClick) {\n dialog.dataset.closeOnOutsideClick = 'false'\n }\n\n dialog.innerHTML = `\n ${handle ? '<div class=\"drawer-handle\"></div>' : ''}\n <div class=\"drawer-content\">${content}</div>\n `\n\n // Backdrop click to close (respects data attribute)\n dialog.addEventListener('click', (e) => {\n if (e.target === dialog && dialog.dataset.closeOnOutsideClick !== 'false') {\n dialog.close()\n }\n })\n\n return dialog\n}\n\n/**\n * Mount a drawer to the DOM (appends to body)\n */\nexport function mount(drawer: DrawerElement): DrawerElement {\n document.body.appendChild(drawer)\n return drawer\n}\n\n/**\n * Unmount a drawer from the DOM\n */\nexport function unmount(drawer: DrawerRef): void {\n const el = resolve(drawer)\n el?.remove()\n}\n\n/**\n * Initialize global backdrop-click-to-close behavior\n * Alternative to adding onclick to each drawer\n * Respects data-close-on-outside-click=\"false\" attribute\n */\nexport function init(): () => void {\n const handler = (e: MouseEvent) => {\n const target = e.target as HTMLElement\n if (target.matches('dialog.drawer') && (target as DrawerElement).dataset.closeOnOutsideClick !== 'false') {\n ;(target as DrawerElement).close()\n }\n }\n\n document.addEventListener('click', handler)\n return () => document.removeEventListener('click', handler)\n}\n\n/**\n * Subscribe to drawer events\n * @returns Cleanup function\n */\nexport function subscribe(\n drawer: DrawerRef,\n handlers: DrawerEventHandlers\n): () => void {\n const el = resolve(drawer)\n if (!el) return () => {}\n\n const { onOpen, onClose, onCancel } = handlers\n\n const handleClose = () => onClose?.()\n const handleCancel = () => onCancel?.()\n\n // Use MutationObserver to detect open attribute\n const observer = new MutationObserver((mutations) => {\n for (const mutation of mutations) {\n if (mutation.attributeName === 'open' && el.open) {\n onOpen?.()\n }\n }\n })\n\n el.addEventListener('close', handleClose)\n el.addEventListener('cancel', handleCancel)\n observer.observe(el, { attributes: true })\n\n return () => {\n el.removeEventListener('close', handleClose)\n el.removeEventListener('cancel', handleCancel)\n observer.disconnect()\n }\n}\n\n/**\n * React-friendly hook helper - returns props to spread on dialog\n * Usage: <dialog {...drawer.props('my-drawer')} />\n */\nexport function props(id: string, options?: { closeOnOutsideClick?: boolean }) {\n const closeOnOutsideClick = options?.closeOnOutsideClick ?? true\n return {\n id,\n className: 'drawer',\n 'data-close-on-outside-click': closeOnOutsideClick ? undefined : 'false',\n onClick: (e: MouseEvent) => {\n if (closeOnOutsideClick && e.target === e.currentTarget) {\n ;(e.currentTarget as DrawerElement).close()\n }\n },\n } as const\n}\n\n"],"mappings":"8BAOA,GAAI,OAAO,OAAW,IAAa,CACjC,IAAM,MAAyB,CAC7B,IAAM,EAAc,MAAM,KACxB,SAAS,iBAAoC,sBAAsB,CACpE,CACD,EAAY,SAAS,EAAQ,IAAU,CACjC,IAAU,EAAY,OAAS,EACjC,EAAO,gBAAgB,QAAQ,CAE/B,EAAO,aAAa,QAAS,GAAG,EAElC,EAGa,IAAI,iBAAkB,GAAc,CACnD,IAAK,IAAM,KAAY,EACrB,GACE,EAAS,OAAS,cAClB,EAAS,gBAAkB,QAC1B,EAAS,OAAuB,UAAU,SAAS,SAAS,CAC7D,CACA,GAAkB,CAClB,QAGJ,CAEO,QAAQ,SAAS,KAAM,CAC9B,QAAS,GACT,WAAY,GACZ,gBAAiB,CAAC,OAAO,CAC1B,CAAC,CAoCJ,SAAS,EAAQ,EAAyC,CAKxD,OAJK,EACD,OAAO,GAAW,SACb,SAAS,eAAe,EAAO,CAEjC,EAJa,KAUtB,SAAgB,EAAK,EAAyB,CACjC,EAAQ,EAAO,EACtB,WAAW,CAMjB,SAAgB,EAAM,EAAyB,CAClC,EAAQ,EAAO,EACtB,OAAO,CAMb,SAAgB,GAAiB,CACf,MAAM,KAAK,SAAS,iBAAgC,sBAAsB,CAAC,CACnF,SAAS,CAAC,QAAS,GAAM,EAAE,OAAO,CAAC,CAM7C,SAAgB,EAAO,EAA4B,CAEjD,OADW,EAAQ,EAAO,EACf,MAAQ,GAMrB,SAAgB,GAA2B,CACzC,OAAO,MAAM,KAAK,SAAS,iBAAgC,sBAAsB,CAAC,CAMpF,SAAgB,GAA+B,CAC7C,IAAMA,EAAO,GAAS,CACtB,OAAOA,EAAKA,EAAK,OAAS,IAAM,KAMlC,SAAgB,EAAO,EAA+B,EAAE,CAAiB,CACvE,GAAM,CAAE,KAAI,UAAU,GAAI,YAAW,SAAS,GAAM,YAAY,GAAI,sBAAsB,IAAS,EAE7F,EAAS,SAAS,cAAc,SAAS,CAoB/C,MAnBA,GAAO,UAAY,UAAU,IAAY,MAAM,CAC3C,IAAI,EAAO,GAAK,GAChB,IAAW,EAAO,QAAQ,UAAY,GACrC,IACH,EAAO,QAAQ,oBAAsB,SAGvC,EAAO,UAAY;MACf,EAAS,oCAAsC,GAAG;kCACtB,EAAQ;IAIxC,EAAO,iBAAiB,QAAU,GAAM,CAClC,EAAE,SAAW,GAAU,EAAO,QAAQ,sBAAwB,SAChE,EAAO,OAAO,EAEhB,CAEK,EAMT,SAAgB,EAAM,EAAsC,CAE1D,OADA,SAAS,KAAK,YAAY,EAAO,CAC1B,EAMT,SAAgB,EAAQ,EAAyB,CACpC,EAAQ,EAAO,EACtB,QAAQ,CAQd,SAAgB,GAAmB,CACjC,IAAM,EAAW,GAAkB,CACjC,IAAM,EAAS,EAAE,OACb,EAAO,QAAQ,gBAAgB,EAAK,EAAyB,QAAQ,sBAAwB,SAC7F,EAAyB,OAAO,EAKtC,OADA,SAAS,iBAAiB,QAAS,EAAQ,KAC9B,SAAS,oBAAoB,QAAS,EAAQ,CAO7D,SAAgB,EACd,EACA,EACY,CACZ,IAAM,EAAK,EAAQ,EAAO,CAC1B,GAAI,CAAC,EAAI,UAAa,GAEtB,GAAM,CAAE,SAAQ,UAAS,YAAa,EAEhC,MAAoB,KAAW,CAC/B,MAAqB,KAAY,CAGjC,EAAW,IAAI,iBAAkB,GAAc,CACnD,IAAK,IAAM,KAAY,EACjB,EAAS,gBAAkB,QAAU,EAAG,MAC1C,KAAU,EAGd,CAMF,OAJA,EAAG,iBAAiB,QAAS,EAAY,CACzC,EAAG,iBAAiB,SAAU,EAAa,CAC3C,EAAS,QAAQ,EAAI,CAAE,WAAY,GAAM,CAAC,KAE7B,CACX,EAAG,oBAAoB,QAAS,EAAY,CAC5C,EAAG,oBAAoB,SAAU,EAAa,CAC9C,EAAS,YAAY,EAQzB,SAAgB,EAAM,EAAY,EAA6C,CAC7E,IAAM,EAAsB,GAAS,qBAAuB,GAC5D,MAAO,CACL,KACA,UAAW,SACX,8BAA+B,EAAsB,IAAA,GAAY,QACjE,QAAU,GAAkB,CACtB,GAAuB,EAAE,SAAW,EAAE,eACtC,EAAE,cAAgC,OAAO,EAGhD"}
|
package/dist/react.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
require('./
|
|
2
|
-
let e=require(`react`),t=require(`react/jsx-runtime`);if(typeof window<`u`){let e=()=>{let e=Array.from(document.querySelectorAll(`dialog.drawer[open]`));e.forEach((t,n)=>{n===e.length-1?t.removeAttribute(`inert`):t.setAttribute(`inert`,``)})};new MutationObserver(t=>{for(let n of t)if(n.type===`attributes`&&n.attributeName===`open`&&n.target.classList.contains(`drawer`)){e();break}}).observe(document.body,{subtree:!0,attributes:!0,attributeFilter:[`open`]})}const n=(0,e.createContext)({direction:void 0});function r(){return(0,e.useContext)(n)}function i({children:e,direction:r}){return(0,t.jsx)(n.Provider,{value:{direction:r},children:e})}const a=(0,e.forwardRef)(({children:n,className:i,open:a,onOpenChange:o,closeOnOutsideClick:s=!0,...c},l)=>{let{direction:u}=r(),d=(0,e.useRef)(null),f=l||d;return(0,e.useEffect)(()=>{let e=f.current;e&&(a&&!e.open?(e.showModal(),o?.(!0)):a===!1&&e.open&&e.close())},[a]),(0,t.jsx)(`dialog`,{ref:f,className:`drawer ${i??``}`.trim(),"data-direction":u,onClose:e=>{c.onClose?.(e),o?.(!1)},onClick:e=>{c.onClick?.(e),s&&e.target===e.currentTarget&&e.currentTarget.close()},...c,children:n})});a.displayName=`Drawer.Content`;const o=(0,e.forwardRef)(({className:e,...n},r)=>(0,t.jsx)(`div`,{ref:r,className:`drawer-handle ${e??``}`.trim(),"aria-hidden":`true`,...n}));o.displayName=`Drawer.Handle`;const s=(0,e.forwardRef)((e,n)=>(0,t.jsx)(`h2`,{ref:n,...e}));s.displayName=`Drawer.Title`;const c=(0,e.forwardRef)((e,n)=>(0,t.jsx)(`p`,{ref:n,...e}));c.displayName=`Drawer.Description`;const l={Root:i,Content:a,Handle:o,Title:s,Description:c};exports.Drawer=l;
|
|
1
|
+
require(`./drawer-BWZh2Fyp.cjs`);require('./drawer-C21nwiwE.css');
|
|
2
|
+
let e=require(`react`),t=require(`react/jsx-runtime`);if(typeof window<`u`){let e=()=>{let e=Array.from(document.querySelectorAll(`dialog.drawer[open]`));e.forEach((t,n)=>{n===e.length-1?t.removeAttribute(`inert`):t.setAttribute(`inert`,``)})};new MutationObserver(t=>{for(let n of t)if(n.type===`attributes`&&n.attributeName===`open`&&n.target.classList.contains(`drawer`)){e();break}}).observe(document.body,{subtree:!0,attributes:!0,attributeFilter:[`open`]})}const n=(0,e.createContext)({direction:void 0});function r(){return(0,e.useContext)(n)}function i({children:e,direction:r}){return(0,t.jsx)(n.Provider,{value:{direction:r},children:e})}const a=(0,e.forwardRef)(({children:n,className:i,open:a,onOpenChange:o,closeOnOutsideClick:s=!0,...c},l)=>{let{direction:u}=r(),d=(0,e.useRef)(null),f=l||d;return(0,e.useEffect)(()=>{let e=f.current;e&&(a&&!e.open?(e.showModal(),o?.(!0)):a===!1&&e.open&&e.close())},[a]),(0,t.jsx)(`dialog`,{ref:f,className:`drawer ${i??``}`.trim(),"data-direction":u,onClose:e=>{e.target===e.currentTarget&&(c.onClose?.(e),o?.(!1))},onClick:e=>{c.onClick?.(e),s&&e.target===e.currentTarget&&e.currentTarget.close()},...c,children:n})});a.displayName=`Drawer.Content`;const o=(0,e.forwardRef)(({className:e,...n},r)=>(0,t.jsx)(`div`,{ref:r,className:`drawer-handle ${e??``}`.trim(),"aria-hidden":`true`,...n}));o.displayName=`Drawer.Handle`;const s=(0,e.forwardRef)((e,n)=>(0,t.jsx)(`h2`,{ref:n,...e}));s.displayName=`Drawer.Title`;const c=(0,e.forwardRef)((e,n)=>(0,t.jsx)(`p`,{ref:n,...e}));c.displayName=`Drawer.Description`;const l={Root:i,Content:a,Handle:o,Title:s,Description:c};exports.Drawer=l;
|
package/dist/react.d.cts
CHANGED
|
@@ -3,7 +3,7 @@ import * as react0 from "react";
|
|
|
3
3
|
import { ComponentPropsWithoutRef, ReactNode } from "react";
|
|
4
4
|
|
|
5
5
|
//#region src/react.d.ts
|
|
6
|
-
type Direction = 'bottom' | 'top' | 'left' | 'right';
|
|
6
|
+
type Direction = 'bottom' | 'top' | 'left' | 'right' | 'modal';
|
|
7
7
|
interface RootProps {
|
|
8
8
|
children: ReactNode;
|
|
9
9
|
/** Direction the drawer opens from */
|
package/dist/react.d.cts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"react.d.cts","names":[],"sources":["../src/react.tsx"],"sourcesContent":[],"mappings":";;;;;KAgDK,SAAA;UAcK,SAAA;YACE;EAfP;EAcK,SAAA,CAAA,EAGI,SAHK;AAGI;iBAGd,IAAA,CAAO;EAAA,QAAA;EAAA;AAAA,CAAA,EAAuB,SAAvB,CAAA,EAAgC,kBAAA,CAAA,GAAA,CAAA,OAAhC;UASN,YAAA,SAAqB,IATL,CASU,wBATV,CAAA,QAAA,CAAA,EAAA,MAAA,CAAA,CAAA;EAAa;EAAS,IAAA,CAAA,EAAA,OAAA;EAAA;EAStC,YAAA,CAAA,EAAa,CAAA,IAAA,EAAA,OAAa,EAAA,GAAA,IAAA;
|
|
1
|
+
{"version":3,"file":"react.d.cts","names":[],"sources":["../src/react.tsx"],"sourcesContent":[],"mappings":";;;;;KAgDK,SAAA;UAcK,SAAA;YACE;EAfP;EAcK,SAAA,CAAA,EAGI,SAHK;AAGI;iBAGd,IAAA,CAAO;EAAA,QAAA;EAAA;AAAA,CAAA,EAAuB,SAAvB,CAAA,EAAgC,kBAAA,CAAA,GAAA,CAAA,OAAhC;UASN,YAAA,SAAqB,IATL,CASU,wBATV,CAAA,QAAA,CAAA,EAAA,MAAA,CAAA,CAAA;EAAa;EAAS,IAAA,CAAA,EAAA,OAAA;EAAA;EAStC,YAAA,CAAA,EAAa,CAAA,IAAA,EAAA,OAAa,EAAA,GAAA,IAAA;EAyD1B;EAaA,mBAAW,CAAA,EAAA,OAAQ;AAAwB;AAgBrD,UA7BU,WAAA,SAAoB,wBAmC7B,CAAA,KAAA,CAAA,CAAA;UAtBS,UAAA,SAAmB;UAQnB,gBAAA,SAAyB;cAQtB"}
|
package/dist/react.d.mts
CHANGED
|
@@ -3,7 +3,7 @@ import { ComponentPropsWithoutRef, ReactNode } from "react";
|
|
|
3
3
|
import * as react_jsx_runtime0 from "react/jsx-runtime";
|
|
4
4
|
|
|
5
5
|
//#region src/react.d.ts
|
|
6
|
-
type Direction = 'bottom' | 'top' | 'left' | 'right';
|
|
6
|
+
type Direction = 'bottom' | 'top' | 'left' | 'right' | 'modal';
|
|
7
7
|
interface RootProps {
|
|
8
8
|
children: ReactNode;
|
|
9
9
|
/** Direction the drawer opens from */
|
package/dist/react.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"react.d.mts","names":[],"sources":["../src/react.tsx"],"sourcesContent":[],"mappings":";;;;;KAgDK,SAAA;UAcK,SAAA;YACE;EAfP;EAcK,SAAA,CAAA,EAGI,SAHK;AAGI;iBAGd,IAAA,CAAO;EAAA,QAAA;EAAA;AAAA,CAAA,EAAuB,SAAvB,CAAA,EAAgC,kBAAA,CAAA,GAAA,CAAA,OAAhC;UASN,YAAA,SAAqB,IATL,CASU,wBATV,CAAA,QAAA,CAAA,EAAA,MAAA,CAAA,CAAA;EAAa;EAAS,IAAA,CAAA,EAAA,OAAA;EAAA;EAStC,YAAA,CAAA,EAAa,CAAA,IAAA,EAAA,OAAa,EAAA,GAAA,IAAA;
|
|
1
|
+
{"version":3,"file":"react.d.mts","names":[],"sources":["../src/react.tsx"],"sourcesContent":[],"mappings":";;;;;KAgDK,SAAA;UAcK,SAAA;YACE;EAfP;EAcK,SAAA,CAAA,EAGI,SAHK;AAGI;iBAGd,IAAA,CAAO;EAAA,QAAA;EAAA;AAAA,CAAA,EAAuB,SAAvB,CAAA,EAAgC,kBAAA,CAAA,GAAA,CAAA,OAAhC;UASN,YAAA,SAAqB,IATL,CASU,wBATV,CAAA,QAAA,CAAA,EAAA,MAAA,CAAA,CAAA;EAAa;EAAS,IAAA,CAAA,EAAA,OAAA;EAAA;EAStC,YAAA,CAAA,EAAa,CAAA,IAAA,EAAA,OAAa,EAAA,GAAA,IAAA;EAyD1B;EAaA,mBAAW,CAAA,EAAA,OAAQ;AAAwB;AAgBrD,UA7BU,WAAA,SAAoB,wBAmC7B,CAAA,KAAA,CAAA,CAAA;UAtBS,UAAA,SAAmB;UAQnB,gBAAA,SAAyB;cAQtB"}
|
package/dist/react.mjs
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import{createContext as e,forwardRef as t,useContext as n,useEffect as r,useRef as i}from"react";import{jsx as a}from"react/jsx-runtime";import './
|
|
2
|
-
if(typeof window<`u`){let e=()=>{let e=Array.from(document.querySelectorAll(`dialog.drawer[open]`));e.forEach((t,n)=>{n===e.length-1?t.removeAttribute(`inert`):t.setAttribute(`inert`,``)})};new MutationObserver(t=>{for(let n of t)if(n.type===`attributes`&&n.attributeName===`open`&&n.target.classList.contains(`drawer`)){e();break}}).observe(document.body,{subtree:!0,attributes:!0,attributeFilter:[`open`]})}const o=e({direction:void 0});function s(){return n(o)}function c({children:e,direction:t}){return a(o.Provider,{value:{direction:t},children:e})}const l=t(({children:e,className:t,open:n,onOpenChange:o,closeOnOutsideClick:c=!0,...l},u)=>{let{direction:d}=s(),f=i(null),p=u||f;return r(()=>{let e=p.current;e&&(n&&!e.open?(e.showModal(),o?.(!0)):n===!1&&e.open&&e.close())},[n]),a(`dialog`,{ref:p,className:`drawer ${t??``}`.trim(),"data-direction":d,onClose:e=>{l.onClose?.(e),o?.(!1)},onClick:e=>{l.onClick?.(e),c&&e.target===e.currentTarget&&e.currentTarget.close()},...l,children:e})});l.displayName=`Drawer.Content`;const u=t(({className:e,...t},n)=>a(`div`,{ref:n,className:`drawer-handle ${e??``}`.trim(),"aria-hidden":`true`,...t}));u.displayName=`Drawer.Handle`;const d=t((e,t)=>a(`h2`,{ref:t,...e}));d.displayName=`Drawer.Title`;const f=t((e,t)=>a(`p`,{ref:t,...e}));f.displayName=`Drawer.Description`;const p={Root:c,Content:l,Handle:u,Title:d,Description:f};export{p as Drawer};
|
|
1
|
+
import"./drawer-CiHZcyXE.mjs";import{createContext as e,forwardRef as t,useContext as n,useEffect as r,useRef as i}from"react";import{jsx as a}from"react/jsx-runtime";import './drawer-CXCJQa45.css';
|
|
2
|
+
if(typeof window<`u`){let e=()=>{let e=Array.from(document.querySelectorAll(`dialog.drawer[open]`));e.forEach((t,n)=>{n===e.length-1?t.removeAttribute(`inert`):t.setAttribute(`inert`,``)})};new MutationObserver(t=>{for(let n of t)if(n.type===`attributes`&&n.attributeName===`open`&&n.target.classList.contains(`drawer`)){e();break}}).observe(document.body,{subtree:!0,attributes:!0,attributeFilter:[`open`]})}const o=e({direction:void 0});function s(){return n(o)}function c({children:e,direction:t}){return a(o.Provider,{value:{direction:t},children:e})}const l=t(({children:e,className:t,open:n,onOpenChange:o,closeOnOutsideClick:c=!0,...l},u)=>{let{direction:d}=s(),f=i(null),p=u||f;return r(()=>{let e=p.current;e&&(n&&!e.open?(e.showModal(),o?.(!0)):n===!1&&e.open&&e.close())},[n]),a(`dialog`,{ref:p,className:`drawer ${t??``}`.trim(),"data-direction":d,onClose:e=>{e.target===e.currentTarget&&(l.onClose?.(e),o?.(!1))},onClick:e=>{l.onClick?.(e),c&&e.target===e.currentTarget&&e.currentTarget.close()},...l,children:e})});l.displayName=`Drawer.Content`;const u=t(({className:e,...t},n)=>a(`div`,{ref:n,className:`drawer-handle ${e??``}`.trim(),"aria-hidden":`true`,...t}));u.displayName=`Drawer.Handle`;const d=t((e,t)=>a(`h2`,{ref:t,...e}));d.displayName=`Drawer.Title`;const f=t((e,t)=>a(`p`,{ref:t,...e}));f.displayName=`Drawer.Description`;const p={Root:c,Content:l,Handle:u,Title:d,Description:f};export{p as Drawer};
|
|
3
3
|
//# sourceMappingURL=react.mjs.map
|
package/dist/react.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"react.mjs","names":[],"sources":["../src/react.tsx"],"sourcesContent":["import {\n createContext,\n useContext,\n forwardRef,\n useEffect,\n useRef,\n type ReactNode,\n type ComponentPropsWithoutRef,\n} from 'react'\nimport './drawer.css'\n\n/* ===== Auto-enable accessibility for stacked drawers ===== */\nif (typeof window !== 'undefined') {\n const updateInertState = () => {\n const openDrawers = Array.from(\n document.querySelectorAll<HTMLDialogElement>('dialog.drawer[open]')\n )\n openDrawers.forEach((drawer, index) => {\n const isTopmost = index === openDrawers.length - 1\n if (isTopmost) {\n drawer.removeAttribute('inert')\n } else {\n drawer.setAttribute('inert', '')\n }\n })\n }\n\n const observer = new MutationObserver((mutations) => {\n for (const mutation of mutations) {\n if (\n mutation.type === 'attributes' &&\n mutation.attributeName === 'open' &&\n (mutation.target as HTMLElement).classList.contains('drawer')\n ) {\n updateInertState()\n break\n }\n }\n })\n\n observer.observe(document.body, {\n subtree: true,\n attributes: true,\n attributeFilter: ['open'],\n })\n}\n\n/* ===== Types ===== */\ntype Direction = 'bottom' | 'top' | 'left' | 'right'\n\ninterface DrawerContextValue {\n direction?: Direction\n}\n\n/* ===== Context ===== */\nconst DrawerContext = createContext<DrawerContextValue>({ direction: undefined })\n\nfunction useDrawerContext() {\n return useContext(DrawerContext)\n}\n\n/* ===== Root ===== */\ninterface RootProps {\n children: ReactNode\n /** Direction the drawer opens from */\n direction?: Direction\n}\n\nfunction Root({ children, direction }: RootProps) {\n return (\n <DrawerContext.Provider value={{ direction }}>\n {children}\n </DrawerContext.Provider>\n )\n}\n\n/* ===== Content ===== */\ninterface ContentProps extends Omit<ComponentPropsWithoutRef<'dialog'>, 'open'> {\n /** Controlled open state */\n open?: boolean\n /** Called when open state changes */\n onOpenChange?: (open: boolean) => void\n /** Close when clicking outside the drawer (default: true) */\n closeOnOutsideClick?: boolean\n}\n\nconst Content = forwardRef<HTMLDialogElement, ContentProps>(\n ({ children, className, open, onOpenChange, closeOnOutsideClick = true, ...props }, ref) => {\n const { direction } = useDrawerContext()\n const internalRef = useRef<HTMLDialogElement>(null)\n const dialogRef = (ref as React.RefObject<HTMLDialogElement>) || internalRef\n\n useEffect(() => {\n const dialog = dialogRef.current\n if (!dialog) return\n\n if (open && !dialog.open) {\n dialog.showModal()\n onOpenChange?.(true)\n } else if (open === false && dialog.open) {\n dialog.close()\n }\n }, [open])\n\n return (\n <dialog\n ref={dialogRef}\n className={`drawer ${className ?? ''}`.trim()}\n data-direction={direction}\n onClose={(e) => {\n props.onClose?.(e)\n onOpenChange?.(false)\n }}\n onClick={(e) => {\n props.onClick?.(e)\n // Backdrop click - only if clicking the dialog element itself\n if (closeOnOutsideClick && e.target === e.currentTarget) {\n e.currentTarget.close()\n }\n }}\n {...props}\n >\n {children}\n </dialog>\n )\n }\n)\nContent.displayName = 'Drawer.Content'\n\n/* ===== Handle ===== */\ninterface HandleProps extends ComponentPropsWithoutRef<'div'> {}\n\nconst Handle = forwardRef<HTMLDivElement, HandleProps>(({ className, ...props }, ref) => (\n <div\n ref={ref}\n className={`drawer-handle ${className ?? ''}`.trim()}\n aria-hidden=\"true\"\n {...props}\n />\n))\nHandle.displayName = 'Drawer.Handle'\n\n/* ===== Title ===== */\ninterface TitleProps extends ComponentPropsWithoutRef<'h2'> {}\n\nconst Title = forwardRef<HTMLHeadingElement, TitleProps>((props, ref) => (\n <h2 ref={ref} {...props} />\n))\nTitle.displayName = 'Drawer.Title'\n\n/* ===== Description ===== */\ninterface DescriptionProps extends ComponentPropsWithoutRef<'p'> {}\n\nconst Description = forwardRef<HTMLParagraphElement, DescriptionProps>((props, ref) => (\n <p ref={ref} {...props} />\n))\nDescription.displayName = 'Drawer.Description'\n\n/* ===== Namespace Export ===== */\nexport const Drawer = {\n Root,\n Content,\n Handle,\n Title,\n Description,\n}\n\nexport type {\n RootProps as DrawerRootProps,\n ContentProps as DrawerContentProps,\n HandleProps as DrawerHandleProps,\n TitleProps as DrawerTitleProps,\n DescriptionProps as DrawerDescriptionProps,\n Direction as DrawerDirection,\n}\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"react.mjs","names":[],"sources":["../src/react.tsx"],"sourcesContent":["import {\n createContext,\n useContext,\n forwardRef,\n useEffect,\n useRef,\n type ReactNode,\n type ComponentPropsWithoutRef,\n} from 'react'\nimport './drawer.css'\n\n/* ===== Auto-enable accessibility for stacked drawers ===== */\nif (typeof window !== 'undefined') {\n const updateInertState = () => {\n const openDrawers = Array.from(\n document.querySelectorAll<HTMLDialogElement>('dialog.drawer[open]')\n )\n openDrawers.forEach((drawer, index) => {\n const isTopmost = index === openDrawers.length - 1\n if (isTopmost) {\n drawer.removeAttribute('inert')\n } else {\n drawer.setAttribute('inert', '')\n }\n })\n }\n\n const observer = new MutationObserver((mutations) => {\n for (const mutation of mutations) {\n if (\n mutation.type === 'attributes' &&\n mutation.attributeName === 'open' &&\n (mutation.target as HTMLElement).classList.contains('drawer')\n ) {\n updateInertState()\n break\n }\n }\n })\n\n observer.observe(document.body, {\n subtree: true,\n attributes: true,\n attributeFilter: ['open'],\n })\n}\n\n/* ===== Types ===== */\ntype Direction = 'bottom' | 'top' | 'left' | 'right' | 'modal'\n\ninterface DrawerContextValue {\n direction?: Direction\n}\n\n/* ===== Context ===== */\nconst DrawerContext = createContext<DrawerContextValue>({ direction: undefined })\n\nfunction useDrawerContext() {\n return useContext(DrawerContext)\n}\n\n/* ===== Root ===== */\ninterface RootProps {\n children: ReactNode\n /** Direction the drawer opens from */\n direction?: Direction\n}\n\nfunction Root({ children, direction }: RootProps) {\n return (\n <DrawerContext.Provider value={{ direction }}>\n {children}\n </DrawerContext.Provider>\n )\n}\n\n/* ===== Content ===== */\ninterface ContentProps extends Omit<ComponentPropsWithoutRef<'dialog'>, 'open'> {\n /** Controlled open state */\n open?: boolean\n /** Called when open state changes */\n onOpenChange?: (open: boolean) => void\n /** Close when clicking outside the drawer (default: true) */\n closeOnOutsideClick?: boolean\n}\n\nconst Content = forwardRef<HTMLDialogElement, ContentProps>(\n ({ children, className, open, onOpenChange, closeOnOutsideClick = true, ...props }, ref) => {\n const { direction } = useDrawerContext()\n const internalRef = useRef<HTMLDialogElement>(null)\n const dialogRef = (ref as React.RefObject<HTMLDialogElement>) || internalRef\n\n useEffect(() => {\n const dialog = dialogRef.current\n if (!dialog) return\n\n if (open && !dialog.open) {\n dialog.showModal()\n onOpenChange?.(true)\n } else if (open === false && dialog.open) {\n dialog.close()\n }\n }, [open])\n\n return (\n <dialog\n ref={dialogRef}\n className={`drawer ${className ?? ''}`.trim()}\n data-direction={direction}\n onClose={(e) => {\n // Only handle close event if it originated from THIS dialog\n // This prevents nested dialogs from triggering parent dialog closes\n if (e.target !== e.currentTarget) return\n\n props.onClose?.(e)\n onOpenChange?.(false)\n }}\n onClick={(e) => {\n props.onClick?.(e)\n // Backdrop click - only if clicking the dialog element itself\n if (closeOnOutsideClick && e.target === e.currentTarget) {\n e.currentTarget.close()\n }\n }}\n {...props}\n >\n {children}\n </dialog>\n )\n }\n)\nContent.displayName = 'Drawer.Content'\n\n/* ===== Handle ===== */\ninterface HandleProps extends ComponentPropsWithoutRef<'div'> {}\n\nconst Handle = forwardRef<HTMLDivElement, HandleProps>(({ className, ...props }, ref) => (\n <div\n ref={ref}\n className={`drawer-handle ${className ?? ''}`.trim()}\n aria-hidden=\"true\"\n {...props}\n />\n))\nHandle.displayName = 'Drawer.Handle'\n\n/* ===== Title ===== */\ninterface TitleProps extends ComponentPropsWithoutRef<'h2'> {}\n\nconst Title = forwardRef<HTMLHeadingElement, TitleProps>((props, ref) => (\n <h2 ref={ref} {...props} />\n))\nTitle.displayName = 'Drawer.Title'\n\n/* ===== Description ===== */\ninterface DescriptionProps extends ComponentPropsWithoutRef<'p'> {}\n\nconst Description = forwardRef<HTMLParagraphElement, DescriptionProps>((props, ref) => (\n <p ref={ref} {...props} />\n))\nDescription.displayName = 'Drawer.Description'\n\n/* ===== Namespace Export ===== */\nexport const Drawer = {\n Root,\n Content,\n Handle,\n Title,\n Description,\n}\n\nexport type {\n RootProps as DrawerRootProps,\n ContentProps as DrawerContentProps,\n HandleProps as DrawerHandleProps,\n TitleProps as DrawerTitleProps,\n DescriptionProps as DrawerDescriptionProps,\n Direction as DrawerDirection,\n}\n"],"mappings":"uKAYA,GAAI,OAAO,OAAW,IAAa,CACjC,IAAM,MAAyB,CAC7B,IAAM,EAAc,MAAM,KACxB,SAAS,iBAAoC,sBAAsB,CACpE,CACD,EAAY,SAAS,EAAQ,IAAU,CACnB,IAAU,EAAY,OAAS,EAE/C,EAAO,gBAAgB,QAAQ,CAE/B,EAAO,aAAa,QAAS,GAAG,EAElC,EAGa,IAAI,iBAAkB,GAAc,CACnD,IAAK,IAAM,KAAY,EACrB,GACE,EAAS,OAAS,cAClB,EAAS,gBAAkB,QAC1B,EAAS,OAAuB,UAAU,SAAS,SAAS,CAC7D,CACA,GAAkB,CAClB,QAGJ,CAEO,QAAQ,SAAS,KAAM,CAC9B,QAAS,GACT,WAAY,GACZ,gBAAiB,CAAC,OAAO,CAC1B,CAAC,CAWJ,MAAM,EAAgB,EAAkC,CAAE,UAAW,IAAA,GAAW,CAAC,CAEjF,SAAS,GAAmB,CAC1B,OAAO,EAAW,EAAc,CAUlC,SAAS,EAAK,CAAE,WAAU,aAAwB,CAChD,OACE,EAAC,EAAc,SAAA,CAAS,MAAO,CAAE,YAAW,CACzC,YACsB,CAc7B,MAAM,EAAU,GACb,CAAE,WAAU,YAAW,OAAM,eAAc,sBAAsB,GAAM,GAAG,GAAS,IAAQ,CAC1F,GAAM,CAAE,aAAc,GAAkB,CAClC,EAAc,EAA0B,KAAK,CAC7C,EAAa,GAA8C,EAcjE,OAZA,MAAgB,CACd,IAAM,EAAS,EAAU,QACpB,IAED,GAAQ,CAAC,EAAO,MAClB,EAAO,WAAW,CAClB,IAAe,GAAK,EACX,IAAS,IAAS,EAAO,MAClC,EAAO,OAAO,GAEf,CAAC,EAAK,CAAC,CAGR,EAAC,SAAA,CACC,IAAK,EACL,UAAW,UAAU,GAAa,KAAK,MAAM,CAC7C,iBAAgB,EAChB,QAAU,GAAM,CAGV,EAAE,SAAW,EAAE,gBAEnB,EAAM,UAAU,EAAE,CAClB,IAAe,GAAM,GAEvB,QAAU,GAAM,CACd,EAAM,UAAU,EAAE,CAEd,GAAuB,EAAE,SAAW,EAAE,eACxC,EAAE,cAAc,OAAO,EAG3B,GAAI,EAEH,YACM,EAGd,CACD,EAAQ,YAAc,iBAKtB,MAAM,EAAS,GAAyC,CAAE,YAAW,GAAG,GAAS,IAC/E,EAAC,MAAA,CACM,MACL,UAAW,iBAAiB,GAAa,KAAK,MAAM,CACpD,cAAY,OACZ,GAAI,GACJ,CACF,CACF,EAAO,YAAc,gBAKrB,MAAM,EAAQ,GAA4C,EAAO,IAC/D,EAAC,KAAA,CAAQ,MAAK,GAAI,GAAS,CAC3B,CACF,EAAM,YAAc,eAKpB,MAAM,EAAc,GAAoD,EAAO,IAC7E,EAAC,IAAA,CAAO,MAAK,GAAI,GAAS,CAC1B,CACF,EAAY,YAAc,qBAG1B,MAAa,EAAS,CACpB,OACA,UACA,SACA,QACA,cACD"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "css-drawer",
|
|
3
|
-
"version": "0.1
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"description": "Vaul-quality drawer component using native <dialog> and pure CSS animations",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.cjs",
|
|
@@ -27,11 +27,12 @@
|
|
|
27
27
|
"default": "./dist/react.cjs"
|
|
28
28
|
}
|
|
29
29
|
},
|
|
30
|
-
"./styles": "./
|
|
31
|
-
"./drawer.css": "./
|
|
30
|
+
"./styles": "./src/drawer.css",
|
|
31
|
+
"./drawer.css": "./src/drawer.css"
|
|
32
32
|
},
|
|
33
33
|
"files": [
|
|
34
|
-
"dist"
|
|
34
|
+
"dist",
|
|
35
|
+
"src/drawer.css"
|
|
35
36
|
],
|
|
36
37
|
"peerDependencies": {
|
|
37
38
|
"react": ">=18.0.0",
|
|
@@ -25,6 +25,7 @@
|
|
|
25
25
|
--drawer-shadow-top: 0 10px 60px hsl(0 0% 0% / 0.12), 0 4px 20px hsl(0 0% 0% / 0.08);
|
|
26
26
|
--drawer-shadow-right: -10px 0 60px hsl(0 0% 0% / 0.12), -4px 0 20px hsl(0 0% 0% / 0.08);
|
|
27
27
|
--drawer-shadow-left: 10px 0 60px hsl(0 0% 0% / 0.12), 4px 0 20px hsl(0 0% 0% / 0.08);
|
|
28
|
+
--drawer-shadow-modal: 0 25px 50px -12px hsl(0 0% 0% / 0.25);
|
|
28
29
|
|
|
29
30
|
/* Animation */
|
|
30
31
|
--drawer-duration: 0.5s;
|
|
@@ -47,6 +48,7 @@
|
|
|
47
48
|
--drawer-shadow-top: 0 10px 60px hsl(0 0% 0% / 0.4), 0 4px 20px hsl(0 0% 0% / 0.3);
|
|
48
49
|
--drawer-shadow-right: -10px 0 60px hsl(0 0% 0% / 0.4), -4px 0 20px hsl(0 0% 0% / 0.3);
|
|
49
50
|
--drawer-shadow-left: 10px 0 60px hsl(0 0% 0% / 0.4), 4px 0 20px hsl(0 0% 0% / 0.3);
|
|
51
|
+
--drawer-shadow-modal: 0 25px 50px -12px hsl(0 0% 0% / 0.5);
|
|
50
52
|
}
|
|
51
53
|
}
|
|
52
54
|
|
|
@@ -169,6 +171,30 @@ body:has(.drawer[open]) {
|
|
|
169
171
|
box-shadow: var(--drawer-shadow-top);
|
|
170
172
|
}
|
|
171
173
|
|
|
174
|
+
/* Modal (centered) */
|
|
175
|
+
.drawer[data-direction="modal"] {
|
|
176
|
+
--_translate-closed: 0 0;
|
|
177
|
+
--_border-radius: var(--drawer-radius);
|
|
178
|
+
inset: 0;
|
|
179
|
+
margin: auto;
|
|
180
|
+
width: fit-content;
|
|
181
|
+
max-width: var(--drawer-max-width);
|
|
182
|
+
height: fit-content;
|
|
183
|
+
max-height: var(--drawer-max-height);
|
|
184
|
+
box-shadow: var(--drawer-shadow-modal);
|
|
185
|
+
scale: var(--drawer-nested-scale);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
.drawer[data-direction="modal"][open] {
|
|
189
|
+
scale: 1;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
@starting-style {
|
|
193
|
+
.drawer[data-direction="modal"][open] {
|
|
194
|
+
scale: var(--drawer-nested-scale);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
172
198
|
/* ===== AUTO-NESTING (up to 5 levels) ===== */
|
|
173
199
|
/* Uses sibling combinators to count open drawers */
|
|
174
200
|
|
|
@@ -302,6 +328,3 @@ body:has(.drawer[open]) {
|
|
|
302
328
|
filter: none;
|
|
303
329
|
}
|
|
304
330
|
}
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
/*# sourceMappingURL=react.css.map*/
|
package/dist/react.css.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"react.css","names":[],"sources":["../src/drawer.css"],"sourcesContent":["/* CSS Drawer - Vaul-quality drawer with auto-nesting and directions */\n\n:root {\n /* Visual */\n --drawer-bg: #fff;\n --drawer-radius: 24px;\n --drawer-backdrop: hsl(0 0% 0% / 0.4);\n --drawer-backdrop-blur: 4px;\n\n /* Sizing */\n --drawer-max-width: 500px;\n --drawer-max-height: 96dvh;\n\n /* Handle */\n --drawer-handle-bg: hsl(0 0% 80%);\n --drawer-handle-bg-hover: hsl(0 0% 60%);\n --drawer-handle-width: 48px;\n --drawer-handle-width-hover: 56px;\n --drawer-handle-height: 5px;\n --drawer-handle-padding-block: 1rem 0.5rem;\n --drawer-handle-padding-inline: 0;\n\n /* Shadows */\n --drawer-shadow-bottom: 0 -10px 60px hsl(0 0% 0% / 0.12), 0 -4px 20px hsl(0 0% 0% / 0.08);\n --drawer-shadow-top: 0 10px 60px hsl(0 0% 0% / 0.12), 0 4px 20px hsl(0 0% 0% / 0.08);\n --drawer-shadow-right: -10px 0 60px hsl(0 0% 0% / 0.12), -4px 0 20px hsl(0 0% 0% / 0.08);\n --drawer-shadow-left: 10px 0 60px hsl(0 0% 0% / 0.12), 4px 0 20px hsl(0 0% 0% / 0.08);\n\n /* Animation */\n --drawer-duration: 0.5s;\n --drawer-duration-close: 0.35s;\n --drawer-ease: cubic-bezier(0.32, 0.72, 0, 1);\n\n /* Nesting effects */\n --drawer-nested-scale: 0.94;\n --drawer-nested-offset: 20px;\n --drawer-nested-brightness: 0.92;\n --drawer-nested-backdrop: hsl(0 0% 0% / 0.15);\n}\n\n@media (prefers-color-scheme: dark) {\n :root {\n --drawer-bg: hsl(0 0% 12%);\n --drawer-handle-bg: hsl(0 0% 35%);\n --drawer-handle-bg-hover: hsl(0 0% 50%);\n --drawer-shadow-bottom: 0 -10px 60px hsl(0 0% 0% / 0.4), 0 -4px 20px hsl(0 0% 0% / 0.3);\n --drawer-shadow-top: 0 10px 60px hsl(0 0% 0% / 0.4), 0 4px 20px hsl(0 0% 0% / 0.3);\n --drawer-shadow-right: -10px 0 60px hsl(0 0% 0% / 0.4), -4px 0 20px hsl(0 0% 0% / 0.3);\n --drawer-shadow-left: 10px 0 60px hsl(0 0% 0% / 0.4), 4px 0 20px hsl(0 0% 0% / 0.3);\n }\n}\n\n/* Background scale effect */\nbody {\n transition: scale var(--drawer-duration) var(--drawer-ease), border-radius var(--drawer-duration) var(--drawer-ease);\n transform-origin: center top;\n}\n\nbody:has(.drawer[open]) {\n overflow: hidden;\n scale: var(--drawer-nested-scale);\n border-radius: var(--drawer-radius);\n}\n\n/* Base drawer */\n.drawer {\n border: none;\n padding: 0;\n margin: 0;\n max-width: 100%;\n max-height: 100%;\n position: fixed;\n background: var(--drawer-bg);\n overflow: hidden;\n opacity: 0;\n transition:\n display var(--drawer-duration-close) allow-discrete,\n overlay var(--drawer-duration-close) allow-discrete,\n translate var(--drawer-duration-close) var(--drawer-ease),\n scale var(--drawer-duration-close) var(--drawer-ease),\n filter var(--drawer-duration-close) ease,\n opacity var(--drawer-duration-close) ease;\n\n /* Default: bottom */\n --_translate-closed: 0 100%;\n --_border-radius: var(--drawer-radius) var(--drawer-radius) 0 0;\n inset: auto 0 0 0;\n margin-inline: auto;\n width: 100%;\n max-width: var(--drawer-max-width);\n height: auto;\n max-height: var(--drawer-max-height);\n border-radius: var(--drawer-border-radius, var(--_border-radius));\n box-shadow: var(--drawer-shadow-bottom);\n translate: var(--_translate-closed);\n}\n\n.drawer::backdrop {\n background: var(--drawer-backdrop);\n opacity: 0;\n backdrop-filter: blur(var(--drawer-backdrop-blur));\n -webkit-backdrop-filter: blur(var(--drawer-backdrop-blur));\n transition:\n display var(--drawer-duration-close) allow-discrete,\n overlay var(--drawer-duration-close) allow-discrete,\n opacity var(--drawer-duration-close) ease;\n}\n\n.drawer[open] {\n opacity: 1;\n translate: 0 0;\n transition:\n display var(--drawer-duration) allow-discrete,\n overlay var(--drawer-duration) allow-discrete,\n translate var(--drawer-duration) var(--drawer-ease),\n scale var(--drawer-duration) var(--drawer-ease),\n filter var(--drawer-duration) ease,\n opacity 0.15s ease;\n}\n\n.drawer[open]::backdrop {\n opacity: 1;\n transition: display var(--drawer-duration) allow-discrete, overlay var(--drawer-duration) allow-discrete, opacity 0.2s ease;\n}\n\n@starting-style {\n .drawer[open] { opacity: 0; translate: var(--_translate-closed); }\n .drawer[open]::backdrop { opacity: 0; }\n}\n\n/* ===== DIRECTIONS ===== */\n\n/* Right */\n.drawer[data-direction=\"right\"] {\n --_translate-closed: 100% 0;\n --_border-radius: var(--drawer-radius) 0 0 var(--drawer-radius);\n inset: 0 0 0 auto;\n margin: 0;\n width: 100%;\n max-width: var(--drawer-max-width);\n height: 100dvh;\n max-height: 100dvh;\n box-shadow: var(--drawer-shadow-right);\n}\n\n/* Left */\n.drawer[data-direction=\"left\"] {\n --_translate-closed: -100% 0;\n --_border-radius: 0 var(--drawer-radius) var(--drawer-radius) 0;\n inset: 0 auto 0 0;\n margin: 0;\n width: 100%;\n max-width: var(--drawer-max-width);\n height: 100dvh;\n max-height: 100dvh;\n box-shadow: var(--drawer-shadow-left);\n}\n\n/* Top */\n.drawer[data-direction=\"top\"] {\n --_translate-closed: 0 -100%;\n --_border-radius: 0 0 var(--drawer-radius) var(--drawer-radius);\n inset: 0 0 auto 0;\n margin-inline: auto;\n width: 100%;\n max-width: var(--drawer-max-width);\n height: auto;\n max-height: var(--drawer-max-height);\n box-shadow: var(--drawer-shadow-top);\n}\n\n/* ===== AUTO-NESTING (up to 5 levels) ===== */\n/* Uses sibling combinators to count open drawers */\n\n/* 1+ open drawers after */\n.drawer[open]:has(~ .drawer[open]) {\n scale: var(--drawer-nested-scale);\n translate: 0 calc(-1 * var(--drawer-nested-offset));\n border-radius: var(--drawer-radius);\n filter: brightness(var(--drawer-nested-brightness));\n pointer-events: none;\n}\n\n/* 2+ open drawers after */\n.drawer[open]:has(~ .drawer[open] ~ .drawer[open]) {\n scale: calc(var(--drawer-nested-scale) * var(--drawer-nested-scale));\n translate: 0 calc(-2 * var(--drawer-nested-offset));\n filter: brightness(calc(var(--drawer-nested-brightness) * var(--drawer-nested-brightness)));\n}\n\n/* 3+ open drawers after */\n.drawer[open]:has(~ .drawer[open] ~ .drawer[open] ~ .drawer[open]) {\n scale: calc(var(--drawer-nested-scale) * var(--drawer-nested-scale) * var(--drawer-nested-scale));\n translate: 0 calc(-3 * var(--drawer-nested-offset));\n filter: brightness(calc(var(--drawer-nested-brightness) * var(--drawer-nested-brightness) * var(--drawer-nested-brightness)));\n}\n\n/* 4+ open drawers after */\n.drawer[open]:has(~ .drawer[open] ~ .drawer[open] ~ .drawer[open] ~ .drawer[open]) {\n scale: calc(var(--drawer-nested-scale) * var(--drawer-nested-scale) * var(--drawer-nested-scale) * var(--drawer-nested-scale));\n translate: 0 calc(-4 * var(--drawer-nested-offset));\n filter: brightness(calc(var(--drawer-nested-brightness) * var(--drawer-nested-brightness) * var(--drawer-nested-brightness) * var(--drawer-nested-brightness)));\n}\n\n/* 5+ open drawers after */\n.drawer[open]:has(~ .drawer[open] ~ .drawer[open] ~ .drawer[open] ~ .drawer[open] ~ .drawer[open]) {\n scale: calc(var(--drawer-nested-scale) * var(--drawer-nested-scale) * var(--drawer-nested-scale) * var(--drawer-nested-scale) * var(--drawer-nested-scale));\n translate: 0 calc(-5 * var(--drawer-nested-offset));\n filter: brightness(calc(var(--drawer-nested-brightness) * var(--drawer-nested-brightness) * var(--drawer-nested-brightness) * var(--drawer-nested-brightness) * var(--drawer-nested-brightness)));\n}\n\n/* Lighter backdrop for stacked drawers */\n.drawer[open] ~ .drawer[open]::backdrop {\n background: var(--drawer-nested-backdrop);\n backdrop-filter: none;\n}\n\n/* Handle */\n.drawer-handle {\n display: flex;\n justify-content: center;\n padding-block: var(--drawer-handle-padding-block);\n padding-inline: var(--drawer-handle-padding-inline);\n cursor: grab;\n}\n\n.drawer-handle::before {\n content: '';\n width: var(--drawer-handle-width);\n height: var(--drawer-handle-height);\n background: var(--drawer-handle-bg);\n border-radius: 100px;\n transition: width 0.2s var(--drawer-ease), height 0.2s var(--drawer-ease), background 0.2s ease;\n}\n\n.drawer-handle:hover::before {\n width: var(--drawer-handle-width-hover);\n background: var(--drawer-handle-bg-hover);\n}\n\n/* Vertical handle for left/right drawers */\n.drawer[data-direction=\"left\"] .drawer-handle,\n.drawer[data-direction=\"right\"] .drawer-handle {\n flex-direction: column;\n align-items: center;\n justify-content: center;\n padding-block: var(--drawer-handle-padding-inline);\n padding-inline: var(--drawer-handle-padding-block);\n height: 100%;\n position: absolute;\n top: 0;\n writing-mode: vertical-lr;\n}\n\n.drawer[data-direction=\"left\"] .drawer-handle { right: 0; }\n.drawer[data-direction=\"right\"] .drawer-handle { left: 0; }\n\n.drawer[data-direction=\"left\"] .drawer-handle::before,\n.drawer[data-direction=\"right\"] .drawer-handle::before {\n width: var(--drawer-handle-height);\n height: var(--drawer-handle-width);\n}\n\n.drawer[data-direction=\"left\"] .drawer-handle:hover::before,\n.drawer[data-direction=\"right\"] .drawer-handle:hover::before {\n width: var(--drawer-handle-height);\n height: var(--drawer-handle-width-hover);\n}\n\n/* Content - structural only, no opinionated padding */\n.drawer-content {\n overflow-x: hidden;\n overflow-y: auto;\n overscroll-behavior: contain;\n flex: 1;\n min-height: 0;\n}\n\n/* Content sizing for directions */\n.drawer[data-direction=\"left\"] .drawer-content,\n.drawer[data-direction=\"right\"] .drawer-content {\n height: 100%;\n}\n\n/* Reduced motion */\n@media (prefers-reduced-motion: reduce) {\n *, *::before, *::after {\n transition-duration: 0.01ms !important;\n }\n\n body:has(.drawer[open]) {\n scale: 1;\n }\n\n .drawer[open]:has(~ .drawer[open]),\n .drawer[open]:has(~ .drawer[open] ~ .drawer[open]),\n .drawer[open]:has(~ .drawer[open] ~ .drawer[open] ~ .drawer[open]),\n .drawer[open]:has(~ .drawer[open] ~ .drawer[open] ~ .drawer[open] ~ .drawer[open]),\n .drawer[open]:has(~ .drawer[open] ~ .drawer[open] ~ .drawer[open] ~ .drawer[open] ~ .drawer[open]) {\n scale: 1;\n translate: 0 0;\n filter: none;\n }\n}\n"],"mappings":"AAAA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA"}
|