css-drawer 0.1.0
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 +470 -0
- package/dist/index.cjs +4 -0
- package/dist/index.d.cts +83 -0
- package/dist/index.d.cts.map +1 -0
- package/dist/index.d.mts +83 -0
- package/dist/index.d.mts.map +1 -0
- package/dist/index.mjs +5 -0
- package/dist/index.mjs.map +1 -0
- package/dist/react.cjs +2 -0
- package/dist/react.css +290 -0
- package/dist/react.css.map +1 -0
- package/dist/react.d.cts +29 -0
- package/dist/react.d.cts.map +1 -0
- package/dist/react.d.mts +29 -0
- package/dist/react.d.mts.map +1 -0
- package/dist/react.mjs +3 -0
- package/dist/react.mjs.map +1 -0
- package/package.json +90 -0
package/README.md
ADDED
|
@@ -0,0 +1,470 @@
|
|
|
1
|
+
# CSS Drawer
|
|
2
|
+
|
|
3
|
+
A near drop-in replacement for [Vaul](https://vaul.emilkowal.ski) using native `<dialog>` and pure CSS animations.
|
|
4
|
+
|
|
5
|
+
**Zero JavaScript animations.** The only JS: `dialog.showModal()` and `dialog.close()`.
|
|
6
|
+
|
|
7
|
+
## Why?
|
|
8
|
+
|
|
9
|
+
| Feature | Vaul | CSS Drawer |
|
|
10
|
+
|---------|------|------------|
|
|
11
|
+
| Bundle size | ~12KB | **1.4KB** JS + 8KB CSS (gzip: ~2.5KB total) |
|
|
12
|
+
| Animation engine | JavaScript | Pure CSS |
|
|
13
|
+
| Nesting | Manual setup | Automatic (CSS `:has()`) |
|
|
14
|
+
| Accessibility | Built-in | Automatic (native `<dialog>` + `inert`) |
|
|
15
|
+
| API | Controlled state | Native refs |
|
|
16
|
+
|
|
17
|
+
## Installation
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
npm install css-drawer
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## Quick Start
|
|
26
|
+
|
|
27
|
+
### React (Recommended)
|
|
28
|
+
|
|
29
|
+
```tsx
|
|
30
|
+
import { useRef } from 'react'
|
|
31
|
+
import { Drawer } from 'css-drawer/react'
|
|
32
|
+
|
|
33
|
+
function App() {
|
|
34
|
+
const ref = useRef<HTMLDialogElement>(null)
|
|
35
|
+
|
|
36
|
+
return (
|
|
37
|
+
<>
|
|
38
|
+
<button onClick={() => ref.current?.showModal()}>
|
|
39
|
+
Open
|
|
40
|
+
</button>
|
|
41
|
+
|
|
42
|
+
<Drawer.Root>
|
|
43
|
+
<Drawer.Content ref={ref}>
|
|
44
|
+
<Drawer.Handle />
|
|
45
|
+
<div className="drawer-content">
|
|
46
|
+
<Drawer.Title>Title</Drawer.Title>
|
|
47
|
+
<Drawer.Description>Description</Drawer.Description>
|
|
48
|
+
<button onClick={() => ref.current?.close()}>Close</button>
|
|
49
|
+
</div>
|
|
50
|
+
</Drawer.Content>
|
|
51
|
+
</Drawer.Root>
|
|
52
|
+
</>
|
|
53
|
+
)
|
|
54
|
+
}
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### Vanilla JS
|
|
58
|
+
|
|
59
|
+
```ts
|
|
60
|
+
import { open, close } from 'css-drawer'
|
|
61
|
+
import 'css-drawer/styles'
|
|
62
|
+
|
|
63
|
+
document.querySelector('#open-btn').onclick = () => open('my-drawer')
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
```html
|
|
67
|
+
<button id="open-btn">Open</button>
|
|
68
|
+
|
|
69
|
+
<dialog class="drawer" id="my-drawer">
|
|
70
|
+
<div class="drawer-handle"></div>
|
|
71
|
+
<div className="drawer-content">
|
|
72
|
+
<h2>Title</h2>
|
|
73
|
+
<p>Description</p>
|
|
74
|
+
<button onclick="this.closest('dialog').close()">Close</button>
|
|
75
|
+
</div>
|
|
76
|
+
</dialog>
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
---
|
|
80
|
+
|
|
81
|
+
## React API
|
|
82
|
+
|
|
83
|
+
### Installation
|
|
84
|
+
|
|
85
|
+
```tsx
|
|
86
|
+
import { Drawer } from 'css-drawer/react'
|
|
87
|
+
// Styles are auto-injected
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### Drawer.Root
|
|
91
|
+
|
|
92
|
+
Provides context for direction. Wrap your drawer content.
|
|
93
|
+
|
|
94
|
+
```tsx
|
|
95
|
+
<Drawer.Root direction="right">
|
|
96
|
+
<Drawer.Content ref={ref}>...</Drawer.Content>
|
|
97
|
+
</Drawer.Root>
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
| Prop | Type | Default | Description |
|
|
101
|
+
|------|------|---------|-------------|
|
|
102
|
+
| `direction` | `'bottom' \| 'top' \| 'left' \| 'right'` | `'bottom'` | Direction the drawer opens from |
|
|
103
|
+
| `children` | `ReactNode` | - | Drawer content |
|
|
104
|
+
|
|
105
|
+
### Drawer.Content
|
|
106
|
+
|
|
107
|
+
The dialog element. Pass a ref to control open/close.
|
|
108
|
+
|
|
109
|
+
```tsx
|
|
110
|
+
const ref = useRef<HTMLDialogElement>(null)
|
|
111
|
+
|
|
112
|
+
// Open
|
|
113
|
+
ref.current?.showModal()
|
|
114
|
+
|
|
115
|
+
// Close
|
|
116
|
+
ref.current?.close()
|
|
117
|
+
|
|
118
|
+
<Drawer.Content ref={ref}>...</Drawer.Content>
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
| Prop | Type | Default | Description |
|
|
122
|
+
|------|------|---------|-------------|
|
|
123
|
+
| `ref` | `Ref<HTMLDialogElement>` | - | Ref to control the dialog |
|
|
124
|
+
| `className` | `string` | - | Additional CSS classes |
|
|
125
|
+
| `...props` | `DialogHTMLAttributes` | - | All native dialog props |
|
|
126
|
+
|
|
127
|
+
### Drawer.Handle
|
|
128
|
+
|
|
129
|
+
Visual drag handle indicator.
|
|
130
|
+
|
|
131
|
+
```tsx
|
|
132
|
+
<Drawer.Handle />
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
| Prop | Type | Default | Description |
|
|
136
|
+
|------|------|---------|-------------|
|
|
137
|
+
| `className` | `string` | - | Additional CSS classes |
|
|
138
|
+
|
|
139
|
+
### Drawer.Title
|
|
140
|
+
|
|
141
|
+
Semantic heading for accessibility.
|
|
142
|
+
|
|
143
|
+
```tsx
|
|
144
|
+
<Drawer.Title>Create Issue</Drawer.Title>
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
| Prop | Type | Default | Description |
|
|
148
|
+
|------|------|---------|-------------|
|
|
149
|
+
| `...props` | `HTMLAttributes<HTMLHeadingElement>` | - | All native h2 props |
|
|
150
|
+
|
|
151
|
+
### Drawer.Description
|
|
152
|
+
|
|
153
|
+
Semantic description for accessibility.
|
|
154
|
+
|
|
155
|
+
```tsx
|
|
156
|
+
<Drawer.Description>Fill out the form below.</Drawer.Description>
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
| Prop | Type | Default | Description |
|
|
160
|
+
|------|------|---------|-------------|
|
|
161
|
+
| `...props` | `HTMLAttributes<HTMLParagraphElement>` | - | All native p props |
|
|
162
|
+
|
|
163
|
+
---
|
|
164
|
+
|
|
165
|
+
## Vanilla JS API
|
|
166
|
+
|
|
167
|
+
### Installation
|
|
168
|
+
|
|
169
|
+
```ts
|
|
170
|
+
import { open, close, closeAll } from 'css-drawer'
|
|
171
|
+
import 'css-drawer/styles'
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
### open(drawer)
|
|
175
|
+
|
|
176
|
+
Opens a drawer by ID or element reference.
|
|
177
|
+
|
|
178
|
+
```ts
|
|
179
|
+
open('my-drawer')
|
|
180
|
+
open(document.getElementById('my-drawer'))
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
| Param | Type | Description |
|
|
184
|
+
|-------|------|-------------|
|
|
185
|
+
| `drawer` | `string \| HTMLDialogElement` | Drawer ID or element |
|
|
186
|
+
|
|
187
|
+
### close(drawer)
|
|
188
|
+
|
|
189
|
+
Closes a drawer by ID or element reference.
|
|
190
|
+
|
|
191
|
+
```ts
|
|
192
|
+
close('my-drawer')
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
| Param | Type | Description |
|
|
196
|
+
|-------|------|-------------|
|
|
197
|
+
| `drawer` | `string \| HTMLDialogElement` | Drawer ID or element |
|
|
198
|
+
|
|
199
|
+
### closeAll()
|
|
200
|
+
|
|
201
|
+
Closes all open drawers in reverse order (top to bottom).
|
|
202
|
+
|
|
203
|
+
```ts
|
|
204
|
+
closeAll()
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
### isOpen(drawer)
|
|
208
|
+
|
|
209
|
+
Returns whether a drawer is open.
|
|
210
|
+
|
|
211
|
+
```ts
|
|
212
|
+
if (isOpen('my-drawer')) {
|
|
213
|
+
// ...
|
|
214
|
+
}
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
| Param | Type | Description |
|
|
218
|
+
|-------|------|-------------|
|
|
219
|
+
| `drawer` | `string \| HTMLDialogElement` | Drawer ID or element |
|
|
220
|
+
|
|
221
|
+
**Returns:** `boolean`
|
|
222
|
+
|
|
223
|
+
### getOpen()
|
|
224
|
+
|
|
225
|
+
Returns all currently open drawers.
|
|
226
|
+
|
|
227
|
+
```ts
|
|
228
|
+
const openDrawers = getOpen()
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
**Returns:** `HTMLDialogElement[]`
|
|
232
|
+
|
|
233
|
+
### getTop()
|
|
234
|
+
|
|
235
|
+
Returns the topmost open drawer.
|
|
236
|
+
|
|
237
|
+
```ts
|
|
238
|
+
const topDrawer = getTop()
|
|
239
|
+
topDrawer?.close()
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
**Returns:** `HTMLDialogElement | null`
|
|
243
|
+
|
|
244
|
+
### create(options)
|
|
245
|
+
|
|
246
|
+
Creates a drawer element programmatically.
|
|
247
|
+
|
|
248
|
+
```ts
|
|
249
|
+
const drawer = create({
|
|
250
|
+
id: 'my-drawer',
|
|
251
|
+
content: '<h2>Hello</h2>',
|
|
252
|
+
handle: true,
|
|
253
|
+
className: 'custom-class'
|
|
254
|
+
})
|
|
255
|
+
|
|
256
|
+
mount(drawer)
|
|
257
|
+
open(drawer)
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
| Option | Type | Default | Description |
|
|
261
|
+
|--------|------|---------|-------------|
|
|
262
|
+
| `id` | `string` | - | Drawer ID |
|
|
263
|
+
| `content` | `string` | `''` | HTML content |
|
|
264
|
+
| `handle` | `boolean` | `true` | Include drag handle |
|
|
265
|
+
| `className` | `string` | `''` | Additional CSS classes |
|
|
266
|
+
|
|
267
|
+
**Returns:** `HTMLDialogElement`
|
|
268
|
+
|
|
269
|
+
### mount(drawer)
|
|
270
|
+
|
|
271
|
+
Appends a drawer to the document body.
|
|
272
|
+
|
|
273
|
+
```ts
|
|
274
|
+
const drawer = create({ id: 'my-drawer' })
|
|
275
|
+
mount(drawer)
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
| Param | Type | Description |
|
|
279
|
+
|-------|------|-------------|
|
|
280
|
+
| `drawer` | `HTMLDialogElement` | Drawer element |
|
|
281
|
+
|
|
282
|
+
**Returns:** `HTMLDialogElement`
|
|
283
|
+
|
|
284
|
+
### unmount(drawer)
|
|
285
|
+
|
|
286
|
+
Removes a drawer from the DOM.
|
|
287
|
+
|
|
288
|
+
```ts
|
|
289
|
+
unmount('my-drawer')
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
| Param | Type | Description |
|
|
293
|
+
|-------|------|-------------|
|
|
294
|
+
| `drawer` | `string \| HTMLDialogElement` | Drawer ID or element |
|
|
295
|
+
|
|
296
|
+
### subscribe(drawer, handlers)
|
|
297
|
+
|
|
298
|
+
Subscribe to drawer events.
|
|
299
|
+
|
|
300
|
+
```ts
|
|
301
|
+
const unsubscribe = subscribe('my-drawer', {
|
|
302
|
+
onOpen: () => console.log('Opened'),
|
|
303
|
+
onClose: () => console.log('Closed'),
|
|
304
|
+
onCancel: () => console.log('Cancelled (Escape/backdrop)')
|
|
305
|
+
})
|
|
306
|
+
|
|
307
|
+
// Later
|
|
308
|
+
unsubscribe()
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
| Param | Type | Description |
|
|
312
|
+
|-------|------|-------------|
|
|
313
|
+
| `drawer` | `string \| HTMLDialogElement` | Drawer ID or element |
|
|
314
|
+
| `handlers.onOpen` | `() => void` | Called when drawer opens |
|
|
315
|
+
| `handlers.onClose` | `() => void` | Called when drawer closes |
|
|
316
|
+
| `handlers.onCancel` | `() => void` | Called on Escape or backdrop click |
|
|
317
|
+
|
|
318
|
+
**Returns:** `() => void` (cleanup function)
|
|
319
|
+
|
|
320
|
+
---
|
|
321
|
+
|
|
322
|
+
## Directions
|
|
323
|
+
|
|
324
|
+
### React
|
|
325
|
+
|
|
326
|
+
```tsx
|
|
327
|
+
<Drawer.Root direction="right">
|
|
328
|
+
<Drawer.Content ref={ref}>...</Drawer.Content>
|
|
329
|
+
</Drawer.Root>
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
### Vanilla
|
|
333
|
+
|
|
334
|
+
```html
|
|
335
|
+
<dialog class="drawer" data-direction="right">...</dialog>
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
### Responsive Direction
|
|
339
|
+
|
|
340
|
+
```tsx
|
|
341
|
+
const isMobile = useMediaQuery('(max-width: 768px)')
|
|
342
|
+
|
|
343
|
+
<Drawer.Root direction={isMobile ? 'bottom' : 'right'}>
|
|
344
|
+
...
|
|
345
|
+
</Drawer.Root>
|
|
346
|
+
```
|
|
347
|
+
|
|
348
|
+
| Direction | Description |
|
|
349
|
+
|-----------|-------------|
|
|
350
|
+
| `bottom` | Opens from bottom (default) |
|
|
351
|
+
| `top` | Opens from top |
|
|
352
|
+
| `left` | Opens from left |
|
|
353
|
+
| `right` | Opens from right |
|
|
354
|
+
|
|
355
|
+
---
|
|
356
|
+
|
|
357
|
+
## Auto-Nesting
|
|
358
|
+
|
|
359
|
+
Drawers automatically stack when opened. No configuration needed.
|
|
360
|
+
|
|
361
|
+
```tsx
|
|
362
|
+
const drawer1 = useRef<HTMLDialogElement>(null)
|
|
363
|
+
const drawer2 = useRef<HTMLDialogElement>(null)
|
|
364
|
+
|
|
365
|
+
// Open drawer1
|
|
366
|
+
drawer1.current?.showModal()
|
|
367
|
+
|
|
368
|
+
// Open drawer2 on top
|
|
369
|
+
drawer2.current?.showModal()
|
|
370
|
+
// drawer1 automatically scales down and dims
|
|
371
|
+
```
|
|
372
|
+
|
|
373
|
+
Works up to 5 levels. CSS `:has()` selectors handle the visual stacking.
|
|
374
|
+
|
|
375
|
+
---
|
|
376
|
+
|
|
377
|
+
## Accessibility
|
|
378
|
+
|
|
379
|
+
Accessibility is automatic:
|
|
380
|
+
|
|
381
|
+
- **Focus trapping**: Native `<dialog>` traps focus
|
|
382
|
+
- **Escape to close**: Native `<dialog>` behavior
|
|
383
|
+
- **Stacked drawers**: Underlying drawers get `inert` attribute automatically
|
|
384
|
+
- **Screen readers**: Only the top drawer is accessible
|
|
385
|
+
|
|
386
|
+
No setup required.
|
|
387
|
+
|
|
388
|
+
---
|
|
389
|
+
|
|
390
|
+
## CSS Customization
|
|
391
|
+
|
|
392
|
+
Override CSS custom properties:
|
|
393
|
+
|
|
394
|
+
```css
|
|
395
|
+
:root {
|
|
396
|
+
--drawer-bg: #fff;
|
|
397
|
+
--drawer-radius: 24px;
|
|
398
|
+
--drawer-max-width: 500px;
|
|
399
|
+
--drawer-max-height: 96dvh;
|
|
400
|
+
--drawer-backdrop: hsl(0 0% 0% / 0.4);
|
|
401
|
+
--drawer-handle: hsl(0 0% 80%);
|
|
402
|
+
--drawer-duration: 0.5s;
|
|
403
|
+
--drawer-duration-close: 0.35s;
|
|
404
|
+
--drawer-ease: cubic-bezier(0.32, 0.72, 0, 1);
|
|
405
|
+
}
|
|
406
|
+
```
|
|
407
|
+
|
|
408
|
+
| Variable | Default | Description |
|
|
409
|
+
|----------|---------|-------------|
|
|
410
|
+
| `--drawer-bg` | `#fff` | Background color |
|
|
411
|
+
| `--drawer-radius` | `24px` | Border radius |
|
|
412
|
+
| `--drawer-max-width` | `500px` | Maximum width |
|
|
413
|
+
| `--drawer-max-height` | `96dvh` | Maximum height |
|
|
414
|
+
| `--drawer-backdrop` | `hsl(0 0% 0% / 0.4)` | Backdrop color |
|
|
415
|
+
| `--drawer-handle` | `hsl(0 0% 80%)` | Handle color |
|
|
416
|
+
| `--drawer-duration` | `0.5s` | Open animation duration |
|
|
417
|
+
| `--drawer-duration-close` | `0.35s` | Close animation duration |
|
|
418
|
+
| `--drawer-ease` | `cubic-bezier(0.32, 0.72, 0, 1)` | Animation easing |
|
|
419
|
+
|
|
420
|
+
Dark mode is automatic via `prefers-color-scheme`.
|
|
421
|
+
|
|
422
|
+
---
|
|
423
|
+
|
|
424
|
+
## CSS Classes
|
|
425
|
+
|
|
426
|
+
| Class | Description |
|
|
427
|
+
|-------|-------------|
|
|
428
|
+
| `.drawer` | Required on the dialog element |
|
|
429
|
+
| `.drawer-handle` | Visual drag handle |
|
|
430
|
+
| `.drawer-content` | Scrollable content area |
|
|
431
|
+
|
|
432
|
+
---
|
|
433
|
+
|
|
434
|
+
## Browser Support
|
|
435
|
+
|
|
436
|
+
| Browser | Version |
|
|
437
|
+
|---------|---------|
|
|
438
|
+
| Chrome | 117+ |
|
|
439
|
+
| Safari | 17.5+ |
|
|
440
|
+
| Firefox | 129+ |
|
|
441
|
+
|
|
442
|
+
Uses `@starting-style`, `:has()`, `allow-discrete`, and `dvh` units.
|
|
443
|
+
|
|
444
|
+
---
|
|
445
|
+
|
|
446
|
+
## TypeScript
|
|
447
|
+
|
|
448
|
+
Full TypeScript support included.
|
|
449
|
+
|
|
450
|
+
```tsx
|
|
451
|
+
import {
|
|
452
|
+
Drawer,
|
|
453
|
+
type DrawerRootProps,
|
|
454
|
+
type DrawerContentProps,
|
|
455
|
+
type DrawerDirection
|
|
456
|
+
} from 'css-drawer/react'
|
|
457
|
+
|
|
458
|
+
import {
|
|
459
|
+
open,
|
|
460
|
+
close,
|
|
461
|
+
type DrawerElement,
|
|
462
|
+
type DrawerRef
|
|
463
|
+
} from 'css-drawer'
|
|
464
|
+
```
|
|
465
|
+
|
|
466
|
+
---
|
|
467
|
+
|
|
468
|
+
## License
|
|
469
|
+
|
|
470
|
+
MIT
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
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=``,handle:r=!0,className:i=``}=e,a=document.createElement(`dialog`);return a.className=`drawer ${i}`.trim(),t&&(a.id=t),a.innerHTML=`
|
|
2
|
+
${r?`<div class="drawer-handle"></div>`:``}
|
|
3
|
+
<div class="drawer-content">${n}</div>
|
|
4
|
+
`,a.addEventListener(`click`,e=>{e.target===a&&a.close()}),a}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.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){return{id:e,className:`drawer`,onClick:e=>{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
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
//#region src/index.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* CSS Drawer - Headless drawer component
|
|
4
|
+
* Works with any framework: React, Vue, Svelte, vanilla JS
|
|
5
|
+
*/
|
|
6
|
+
type DrawerElement = HTMLDialogElement;
|
|
7
|
+
type DrawerRef = string | DrawerElement | null | undefined;
|
|
8
|
+
interface CreateDrawerOptions {
|
|
9
|
+
/** Drawer ID */
|
|
10
|
+
id?: string;
|
|
11
|
+
/** HTML content for the drawer */
|
|
12
|
+
content?: string;
|
|
13
|
+
/** Include drag handle (default: true) */
|
|
14
|
+
handle?: boolean;
|
|
15
|
+
/** Additional CSS classes */
|
|
16
|
+
className?: string;
|
|
17
|
+
}
|
|
18
|
+
interface DrawerEventHandlers {
|
|
19
|
+
/** Called when drawer opens */
|
|
20
|
+
onOpen?: () => void;
|
|
21
|
+
/** Called when drawer closes */
|
|
22
|
+
onClose?: () => void;
|
|
23
|
+
/** Called when drawer is cancelled (backdrop click or Escape) */
|
|
24
|
+
onCancel?: () => void;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Open a drawer by ID or element reference
|
|
28
|
+
*/
|
|
29
|
+
declare function open(drawer: DrawerRef): void;
|
|
30
|
+
/**
|
|
31
|
+
* Close a drawer by ID or element reference
|
|
32
|
+
*/
|
|
33
|
+
declare function close(drawer: DrawerRef): void;
|
|
34
|
+
/**
|
|
35
|
+
* Close all open drawers (in reverse DOM order for proper animation)
|
|
36
|
+
*/
|
|
37
|
+
declare function closeAll(): void;
|
|
38
|
+
/**
|
|
39
|
+
* Check if a drawer is open
|
|
40
|
+
*/
|
|
41
|
+
declare function isOpen(drawer: DrawerRef): boolean;
|
|
42
|
+
/**
|
|
43
|
+
* Get all open drawers
|
|
44
|
+
*/
|
|
45
|
+
declare function getOpen(): DrawerElement[];
|
|
46
|
+
/**
|
|
47
|
+
* Get the topmost open drawer
|
|
48
|
+
*/
|
|
49
|
+
declare function getTop(): DrawerElement | null;
|
|
50
|
+
/**
|
|
51
|
+
* Create a drawer element programmatically
|
|
52
|
+
*/
|
|
53
|
+
declare function create(options?: CreateDrawerOptions): DrawerElement;
|
|
54
|
+
/**
|
|
55
|
+
* Mount a drawer to the DOM (appends to body)
|
|
56
|
+
*/
|
|
57
|
+
declare function mount(drawer: DrawerElement): DrawerElement;
|
|
58
|
+
/**
|
|
59
|
+
* Unmount a drawer from the DOM
|
|
60
|
+
*/
|
|
61
|
+
declare function unmount(drawer: DrawerRef): void;
|
|
62
|
+
/**
|
|
63
|
+
* Initialize global backdrop-click-to-close behavior
|
|
64
|
+
* Alternative to adding onclick to each drawer
|
|
65
|
+
*/
|
|
66
|
+
declare function init(): () => void;
|
|
67
|
+
/**
|
|
68
|
+
* Subscribe to drawer events
|
|
69
|
+
* @returns Cleanup function
|
|
70
|
+
*/
|
|
71
|
+
declare function subscribe(drawer: DrawerRef, handlers: DrawerEventHandlers): () => void;
|
|
72
|
+
/**
|
|
73
|
+
* React-friendly hook helper - returns props to spread on dialog
|
|
74
|
+
* Usage: <dialog {...drawer.props('my-drawer')} />
|
|
75
|
+
*/
|
|
76
|
+
declare function props(id: string): {
|
|
77
|
+
readonly id: string;
|
|
78
|
+
readonly className: "drawer";
|
|
79
|
+
readonly onClick: (e: MouseEvent) => void;
|
|
80
|
+
};
|
|
81
|
+
//#endregion
|
|
82
|
+
export { CreateDrawerOptions, DrawerElement, DrawerEventHandlers, DrawerRef, close, closeAll, create, getOpen, getTop, init, isOpen, mount, open, props, subscribe, unmount };
|
|
83
|
+
//# sourceMappingURL=index.d.cts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.cts","names":[],"sources":["../src/index.ts"],"sourcesContent":[],"mappings":";;AAwCA;AAEA;AAEA;AAWiB,KAfL,aAAA,GAAgB,iBAeQ;AAuBpB,KApCJ,SAAA,GAoCiB,MAAA,GApCI,aAoCK,GAAA,IAAA,GAAA,SAAA;AAQtB,UA1CC,mBAAA,CA0CsB;EAQvB;EAQA,EAAA,CAAA,EAAA,MAAM;EAQN;EAOA,OAAA,CAAA,EAAM,MAAA;EAQN;EAuBA,MAAA,CAAA,EAAK,OAAA;EAQL;EASA,SAAI,CAAA,EAAA,MAAA;AAgBpB;AAoCgB,UAlKC,mBAAA,CAsKU;;;;;;;;;;;iBA/IX,IAAA,SAAa;;;;iBAQb,KAAA,SAAc;;;;iBAQd,QAAA,CAAA;;;;iBAQA,MAAA,SAAe;;;;iBAQf,OAAA,CAAA,GAAW;;;;iBAOX,MAAA,CAAA,GAAU;;;;iBAQV,MAAA,WAAgB,sBAA2B;;;;iBAuB3C,KAAA,SAAc,gBAAgB;;;;iBAQ9B,OAAA,SAAgB;;;;;iBAShB,IAAA,CAAA;;;;;iBAgBA,SAAA,SACN,qBACE;;;;;iBAkCI,KAAA;;;wBAIC"}
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
//#region src/index.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* CSS Drawer - Headless drawer component
|
|
4
|
+
* Works with any framework: React, Vue, Svelte, vanilla JS
|
|
5
|
+
*/
|
|
6
|
+
type DrawerElement = HTMLDialogElement;
|
|
7
|
+
type DrawerRef = string | DrawerElement | null | undefined;
|
|
8
|
+
interface CreateDrawerOptions {
|
|
9
|
+
/** Drawer ID */
|
|
10
|
+
id?: string;
|
|
11
|
+
/** HTML content for the drawer */
|
|
12
|
+
content?: string;
|
|
13
|
+
/** Include drag handle (default: true) */
|
|
14
|
+
handle?: boolean;
|
|
15
|
+
/** Additional CSS classes */
|
|
16
|
+
className?: string;
|
|
17
|
+
}
|
|
18
|
+
interface DrawerEventHandlers {
|
|
19
|
+
/** Called when drawer opens */
|
|
20
|
+
onOpen?: () => void;
|
|
21
|
+
/** Called when drawer closes */
|
|
22
|
+
onClose?: () => void;
|
|
23
|
+
/** Called when drawer is cancelled (backdrop click or Escape) */
|
|
24
|
+
onCancel?: () => void;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Open a drawer by ID or element reference
|
|
28
|
+
*/
|
|
29
|
+
declare function open(drawer: DrawerRef): void;
|
|
30
|
+
/**
|
|
31
|
+
* Close a drawer by ID or element reference
|
|
32
|
+
*/
|
|
33
|
+
declare function close(drawer: DrawerRef): void;
|
|
34
|
+
/**
|
|
35
|
+
* Close all open drawers (in reverse DOM order for proper animation)
|
|
36
|
+
*/
|
|
37
|
+
declare function closeAll(): void;
|
|
38
|
+
/**
|
|
39
|
+
* Check if a drawer is open
|
|
40
|
+
*/
|
|
41
|
+
declare function isOpen(drawer: DrawerRef): boolean;
|
|
42
|
+
/**
|
|
43
|
+
* Get all open drawers
|
|
44
|
+
*/
|
|
45
|
+
declare function getOpen(): DrawerElement[];
|
|
46
|
+
/**
|
|
47
|
+
* Get the topmost open drawer
|
|
48
|
+
*/
|
|
49
|
+
declare function getTop(): DrawerElement | null;
|
|
50
|
+
/**
|
|
51
|
+
* Create a drawer element programmatically
|
|
52
|
+
*/
|
|
53
|
+
declare function create(options?: CreateDrawerOptions): DrawerElement;
|
|
54
|
+
/**
|
|
55
|
+
* Mount a drawer to the DOM (appends to body)
|
|
56
|
+
*/
|
|
57
|
+
declare function mount(drawer: DrawerElement): DrawerElement;
|
|
58
|
+
/**
|
|
59
|
+
* Unmount a drawer from the DOM
|
|
60
|
+
*/
|
|
61
|
+
declare function unmount(drawer: DrawerRef): void;
|
|
62
|
+
/**
|
|
63
|
+
* Initialize global backdrop-click-to-close behavior
|
|
64
|
+
* Alternative to adding onclick to each drawer
|
|
65
|
+
*/
|
|
66
|
+
declare function init(): () => void;
|
|
67
|
+
/**
|
|
68
|
+
* Subscribe to drawer events
|
|
69
|
+
* @returns Cleanup function
|
|
70
|
+
*/
|
|
71
|
+
declare function subscribe(drawer: DrawerRef, handlers: DrawerEventHandlers): () => void;
|
|
72
|
+
/**
|
|
73
|
+
* React-friendly hook helper - returns props to spread on dialog
|
|
74
|
+
* Usage: <dialog {...drawer.props('my-drawer')} />
|
|
75
|
+
*/
|
|
76
|
+
declare function props(id: string): {
|
|
77
|
+
readonly id: string;
|
|
78
|
+
readonly className: "drawer";
|
|
79
|
+
readonly onClick: (e: MouseEvent) => void;
|
|
80
|
+
};
|
|
81
|
+
//#endregion
|
|
82
|
+
export { CreateDrawerOptions, DrawerElement, DrawerEventHandlers, DrawerRef, close, closeAll, create, getOpen, getTop, init, isOpen, mount, open, props, subscribe, unmount };
|
|
83
|
+
//# sourceMappingURL=index.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/index.ts"],"sourcesContent":[],"mappings":";;AAwCA;AAEA;AAEA;AAWiB,KAfL,aAAA,GAAgB,iBAeQ;AAuBpB,KApCJ,SAAA,GAoCiB,MAAA,GApCI,aAoCK,GAAA,IAAA,GAAA,SAAA;AAQtB,UA1CC,mBAAA,CA0CsB;EAQvB;EAQA,EAAA,CAAA,EAAA,MAAM;EAQN;EAOA,OAAA,CAAA,EAAM,MAAA;EAQN;EAuBA,MAAA,CAAA,EAAK,OAAA;EAQL;EASA,SAAI,CAAA,EAAA,MAAA;AAgBpB;AAoCgB,UAlKC,mBAAA,CAsKU;;;;;;;;;;;iBA/IX,IAAA,SAAa;;;;iBAQb,KAAA,SAAc;;;;iBAQd,QAAA,CAAA;;;;iBAQA,MAAA,SAAe;;;;iBAQf,OAAA,CAAA,GAAW;;;;iBAOX,MAAA,CAAA,GAAU;;;;iBAQV,MAAA,WAAgB,sBAA2B;;;;iBAuB3C,KAAA,SAAc,gBAAgB;;;;iBAQ9B,OAAA,SAAgB;;;;;iBAShB,IAAA,CAAA;;;;;iBAgBA,SAAA,SACN,qBACE;;;;;iBAkCI,KAAA;;;wBAIC"}
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
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=``,handle:r=!0,className:i=``}=e,a=document.createElement(`dialog`);return a.className=`drawer ${i}`.trim(),t&&(a.id=t),a.innerHTML=`
|
|
2
|
+
${r?`<div class="drawer-handle"></div>`:``}
|
|
3
|
+
<div class="drawer-content">${n}</div>
|
|
4
|
+
`,a.addEventListener(`click`,e=>{e.target===a&&a.close()}),a}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.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){return{id:e,className:`drawer`,onClick:e=>{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
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +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}\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 = '' } = options\n\n const dialog = document.createElement('dialog') as DrawerElement\n dialog.className = `drawer ${className}`.trim()\n if (id) dialog.id = id\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\n dialog.addEventListener('click', (e) => {\n if (e.target === dialog) dialog.close()\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 */\nexport function init(): () => void {\n const handler = (e: MouseEvent) => {\n const target = e.target as HTMLElement\n if (target.matches('dialog.drawer')) {\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) {\n return {\n id,\n className: 'drawer',\n onClick: (e: MouseEvent) => {\n if (e.target === e.currentTarget) {\n ;(e.currentTarget as DrawerElement).close()\n }\n },\n } as const\n}\n\n"],"mappings":"AAMA,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,CA8BJ,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,SAAS,GAAM,YAAY,IAAO,EAEtD,EAAS,SAAS,cAAc,SAAS,CAc/C,MAbA,GAAO,UAAY,UAAU,IAAY,MAAM,CAC3C,IAAI,EAAO,GAAK,GAEpB,EAAO,UAAY;MACf,EAAS,oCAAsC,GAAG;kCACtB,EAAQ;IAIxC,EAAO,iBAAiB,QAAU,GAAM,CAClC,EAAE,SAAW,GAAQ,EAAO,OAAO,EACvC,CAEK,EAMT,SAAgB,EAAM,EAAsC,CAE1D,OADA,SAAS,KAAK,YAAY,EAAO,CAC1B,EAMT,SAAgB,EAAQ,EAAyB,CACpC,EAAQ,EAAO,EACtB,QAAQ,CAOd,SAAgB,GAAmB,CACjC,IAAM,EAAW,GAAkB,CACjC,IAAM,EAAS,EAAE,OACb,EAAO,QAAQ,gBAAgB,EAC/B,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,CAChC,MAAO,CACL,KACA,UAAW,SACX,QAAU,GAAkB,CACtB,EAAE,SAAW,EAAE,eACf,EAAE,cAAgC,OAAO,EAGhD"}
|
package/dist/react.cjs
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
require('./react.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:e,className:n,...i},a)=>{let{direction:o}=r();return(0,t.jsx)(`dialog`,{ref:a,className:`drawer ${n??``}`.trim(),"data-direction":o,onClick:e=>{i.onClick?.(e),e.target===e.currentTarget&&e.currentTarget.close()},...i,children:e})});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.css
ADDED
|
@@ -0,0 +1,290 @@
|
|
|
1
|
+
/* CSS Drawer - Vaul-quality drawer with auto-nesting and directions */
|
|
2
|
+
|
|
3
|
+
:root {
|
|
4
|
+
--drawer-bg: #fff;
|
|
5
|
+
--drawer-radius: 24px;
|
|
6
|
+
--drawer-max-width: 500px;
|
|
7
|
+
--drawer-max-height: 96dvh;
|
|
8
|
+
--drawer-backdrop: hsl(0 0% 0% / 0.4);
|
|
9
|
+
--drawer-handle: hsl(0 0% 80%);
|
|
10
|
+
--drawer-shadow-bottom: 0 -10px 60px hsl(0 0% 0% / 0.12), 0 -4px 20px hsl(0 0% 0% / 0.08);
|
|
11
|
+
--drawer-shadow-top: 0 10px 60px hsl(0 0% 0% / 0.12), 0 4px 20px hsl(0 0% 0% / 0.08);
|
|
12
|
+
--drawer-shadow-right: -10px 0 60px hsl(0 0% 0% / 0.12), -4px 0 20px hsl(0 0% 0% / 0.08);
|
|
13
|
+
--drawer-shadow-left: 10px 0 60px hsl(0 0% 0% / 0.12), 4px 0 20px hsl(0 0% 0% / 0.08);
|
|
14
|
+
--drawer-duration: 0.5s;
|
|
15
|
+
--drawer-duration-close: 0.35s;
|
|
16
|
+
--drawer-ease: cubic-bezier(0.32, 0.72, 0, 1);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
@media (prefers-color-scheme: dark) {
|
|
20
|
+
:root {
|
|
21
|
+
--drawer-bg: hsl(0 0% 12%);
|
|
22
|
+
--drawer-handle: hsl(0 0% 35%);
|
|
23
|
+
--drawer-shadow-bottom: 0 -10px 60px hsl(0 0% 0% / 0.4), 0 -4px 20px hsl(0 0% 0% / 0.3);
|
|
24
|
+
--drawer-shadow-top: 0 10px 60px hsl(0 0% 0% / 0.4), 0 4px 20px hsl(0 0% 0% / 0.3);
|
|
25
|
+
--drawer-shadow-right: -10px 0 60px hsl(0 0% 0% / 0.4), -4px 0 20px hsl(0 0% 0% / 0.3);
|
|
26
|
+
--drawer-shadow-left: 10px 0 60px hsl(0 0% 0% / 0.4), 4px 0 20px hsl(0 0% 0% / 0.3);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/* Background scale effect */
|
|
31
|
+
body {
|
|
32
|
+
transition: scale var(--drawer-duration) var(--drawer-ease), border-radius var(--drawer-duration) var(--drawer-ease);
|
|
33
|
+
transform-origin: center top;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
body:has(.drawer[open]) {
|
|
37
|
+
overflow: hidden;
|
|
38
|
+
scale: 0.94;
|
|
39
|
+
border-radius: var(--drawer-radius);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/* Base drawer */
|
|
43
|
+
.drawer {
|
|
44
|
+
border: none;
|
|
45
|
+
padding: 0;
|
|
46
|
+
margin: 0;
|
|
47
|
+
max-width: 100%;
|
|
48
|
+
max-height: 100%;
|
|
49
|
+
position: fixed;
|
|
50
|
+
background: var(--drawer-bg);
|
|
51
|
+
overflow: hidden;
|
|
52
|
+
opacity: 0;
|
|
53
|
+
transition:
|
|
54
|
+
display var(--drawer-duration-close) allow-discrete,
|
|
55
|
+
overlay var(--drawer-duration-close) allow-discrete,
|
|
56
|
+
translate var(--drawer-duration-close) var(--drawer-ease),
|
|
57
|
+
scale var(--drawer-duration-close) var(--drawer-ease),
|
|
58
|
+
filter var(--drawer-duration-close) ease,
|
|
59
|
+
opacity var(--drawer-duration-close) ease;
|
|
60
|
+
|
|
61
|
+
/* Default: bottom */
|
|
62
|
+
--_translate-closed: 0 100%;
|
|
63
|
+
inset: auto 0 0 0;
|
|
64
|
+
margin-inline: auto;
|
|
65
|
+
width: 100%;
|
|
66
|
+
max-width: var(--drawer-max-width);
|
|
67
|
+
height: auto;
|
|
68
|
+
max-height: var(--drawer-max-height);
|
|
69
|
+
border-radius: var(--drawer-radius) var(--drawer-radius) 0 0;
|
|
70
|
+
box-shadow: var(--drawer-shadow-bottom);
|
|
71
|
+
translate: var(--_translate-closed);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
.drawer::backdrop {
|
|
75
|
+
background: var(--drawer-backdrop);
|
|
76
|
+
opacity: 0;
|
|
77
|
+
backdrop-filter: blur(4px);
|
|
78
|
+
-webkit-backdrop-filter: blur(4px);
|
|
79
|
+
transition:
|
|
80
|
+
display var(--drawer-duration-close) allow-discrete,
|
|
81
|
+
overlay var(--drawer-duration-close) allow-discrete,
|
|
82
|
+
opacity var(--drawer-duration-close) ease;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
.drawer[open] {
|
|
86
|
+
opacity: 1;
|
|
87
|
+
translate: 0 0;
|
|
88
|
+
transition:
|
|
89
|
+
display var(--drawer-duration) allow-discrete,
|
|
90
|
+
overlay var(--drawer-duration) allow-discrete,
|
|
91
|
+
translate var(--drawer-duration) var(--drawer-ease),
|
|
92
|
+
scale var(--drawer-duration) var(--drawer-ease),
|
|
93
|
+
filter var(--drawer-duration) ease,
|
|
94
|
+
opacity 0.15s ease;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
.drawer[open]::backdrop {
|
|
98
|
+
opacity: 1;
|
|
99
|
+
transition: display var(--drawer-duration) allow-discrete, overlay var(--drawer-duration) allow-discrete, opacity 0.2s ease;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
@starting-style {
|
|
103
|
+
.drawer[open] { opacity: 0; translate: var(--_translate-closed); }
|
|
104
|
+
.drawer[open]::backdrop { opacity: 0; }
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/* ===== DIRECTIONS ===== */
|
|
108
|
+
|
|
109
|
+
/* Right */
|
|
110
|
+
.drawer[data-direction="right"] {
|
|
111
|
+
--_translate-closed: 100% 0;
|
|
112
|
+
inset: 0 0 0 auto;
|
|
113
|
+
margin: 0;
|
|
114
|
+
width: 100%;
|
|
115
|
+
max-width: var(--drawer-max-width);
|
|
116
|
+
height: 100dvh;
|
|
117
|
+
max-height: 100dvh;
|
|
118
|
+
border-radius: var(--drawer-radius) 0 0 var(--drawer-radius);
|
|
119
|
+
box-shadow: var(--drawer-shadow-right);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/* Left */
|
|
123
|
+
.drawer[data-direction="left"] {
|
|
124
|
+
--_translate-closed: -100% 0;
|
|
125
|
+
inset: 0 auto 0 0;
|
|
126
|
+
margin: 0;
|
|
127
|
+
width: 100%;
|
|
128
|
+
max-width: var(--drawer-max-width);
|
|
129
|
+
height: 100dvh;
|
|
130
|
+
max-height: 100dvh;
|
|
131
|
+
border-radius: 0 var(--drawer-radius) var(--drawer-radius) 0;
|
|
132
|
+
box-shadow: var(--drawer-shadow-left);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/* Top */
|
|
136
|
+
.drawer[data-direction="top"] {
|
|
137
|
+
--_translate-closed: 0 -100%;
|
|
138
|
+
inset: 0 0 auto 0;
|
|
139
|
+
margin-inline: auto;
|
|
140
|
+
width: 100%;
|
|
141
|
+
max-width: var(--drawer-max-width);
|
|
142
|
+
height: auto;
|
|
143
|
+
max-height: var(--drawer-max-height);
|
|
144
|
+
border-radius: 0 0 var(--drawer-radius) var(--drawer-radius);
|
|
145
|
+
box-shadow: var(--drawer-shadow-top);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/* ===== AUTO-NESTING (up to 5 levels) ===== */
|
|
149
|
+
/* Uses sibling combinators to count open drawers */
|
|
150
|
+
|
|
151
|
+
/* 1+ open drawers after */
|
|
152
|
+
.drawer[open]:has(~ .drawer[open]) {
|
|
153
|
+
scale: 0.94;
|
|
154
|
+
translate: 0 -20px;
|
|
155
|
+
border-radius: var(--drawer-radius);
|
|
156
|
+
filter: brightness(0.92);
|
|
157
|
+
pointer-events: none;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/* 2+ open drawers after */
|
|
161
|
+
.drawer[open]:has(~ .drawer[open] ~ .drawer[open]) {
|
|
162
|
+
scale: 0.88;
|
|
163
|
+
translate: 0 -40px;
|
|
164
|
+
filter: brightness(0.84);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/* 3+ open drawers after */
|
|
168
|
+
.drawer[open]:has(~ .drawer[open] ~ .drawer[open] ~ .drawer[open]) {
|
|
169
|
+
scale: 0.82;
|
|
170
|
+
translate: 0 -60px;
|
|
171
|
+
filter: brightness(0.76);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/* 4+ open drawers after */
|
|
175
|
+
.drawer[open]:has(~ .drawer[open] ~ .drawer[open] ~ .drawer[open] ~ .drawer[open]) {
|
|
176
|
+
scale: 0.76;
|
|
177
|
+
translate: 0 -80px;
|
|
178
|
+
filter: brightness(0.68);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/* 5+ open drawers after */
|
|
182
|
+
.drawer[open]:has(~ .drawer[open] ~ .drawer[open] ~ .drawer[open] ~ .drawer[open] ~ .drawer[open]) {
|
|
183
|
+
scale: 0.70;
|
|
184
|
+
translate: 0 -100px;
|
|
185
|
+
filter: brightness(0.60);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/* Lighter backdrop for stacked drawers */
|
|
189
|
+
.drawer[open] ~ .drawer[open]::backdrop {
|
|
190
|
+
background: hsl(0 0% 0% / 0.15);
|
|
191
|
+
backdrop-filter: none;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/* Handle */
|
|
195
|
+
.drawer-handle {
|
|
196
|
+
display: flex;
|
|
197
|
+
justify-content: center;
|
|
198
|
+
padding: 1rem 0 0.5rem;
|
|
199
|
+
cursor: grab;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
.drawer-handle::before {
|
|
203
|
+
content: '';
|
|
204
|
+
width: 48px;
|
|
205
|
+
height: 5px;
|
|
206
|
+
background: var(--drawer-handle);
|
|
207
|
+
border-radius: 100px;
|
|
208
|
+
transition: width 0.2s var(--drawer-ease), height 0.2s var(--drawer-ease), background 0.2s ease;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
.drawer-handle:hover::before {
|
|
212
|
+
width: 56px;
|
|
213
|
+
background: hsl(0 0% 60%);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/* Vertical handle for left/right drawers */
|
|
217
|
+
.drawer[data-direction="left"] .drawer-handle,
|
|
218
|
+
.drawer[data-direction="right"] .drawer-handle {
|
|
219
|
+
flex-direction: column;
|
|
220
|
+
align-items: center;
|
|
221
|
+
justify-content: center;
|
|
222
|
+
padding: 0.5rem 1rem 0.5rem 0.5rem;
|
|
223
|
+
height: 100%;
|
|
224
|
+
position: absolute;
|
|
225
|
+
top: 0;
|
|
226
|
+
writing-mode: vertical-lr;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
.drawer[data-direction="left"] .drawer-handle { right: 0; }
|
|
230
|
+
.drawer[data-direction="right"] .drawer-handle { left: 0; }
|
|
231
|
+
|
|
232
|
+
.drawer[data-direction="left"] .drawer-handle::before,
|
|
233
|
+
.drawer[data-direction="right"] .drawer-handle::before {
|
|
234
|
+
width: 5px;
|
|
235
|
+
height: 48px;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
.drawer[data-direction="left"] .drawer-handle:hover::before,
|
|
239
|
+
.drawer[data-direction="right"] .drawer-handle:hover::before {
|
|
240
|
+
width: 5px;
|
|
241
|
+
height: 56px;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
/* Content */
|
|
245
|
+
.drawer-content {
|
|
246
|
+
padding: 0.5rem 1.5rem 2rem;
|
|
247
|
+
padding-bottom: calc(2rem + env(safe-area-inset-bottom, 0px));
|
|
248
|
+
overflow-x: hidden;
|
|
249
|
+
overflow-y: auto;
|
|
250
|
+
overscroll-behavior: contain;
|
|
251
|
+
max-height: calc(var(--drawer-max-height) - 60px);
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
/* Content adjustments for directions */
|
|
255
|
+
.drawer[data-direction="left"] .drawer-content,
|
|
256
|
+
.drawer[data-direction="right"] .drawer-content {
|
|
257
|
+
padding: 1.5rem;
|
|
258
|
+
padding-left: 2.5rem;
|
|
259
|
+
max-height: 100%;
|
|
260
|
+
height: 100%;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
.drawer[data-direction="top"] .drawer-content {
|
|
264
|
+
padding-bottom: 1.5rem;
|
|
265
|
+
padding-top: 0.5rem;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
/* Reduced motion */
|
|
269
|
+
@media (prefers-reduced-motion: reduce) {
|
|
270
|
+
*, *::before, *::after {
|
|
271
|
+
transition-duration: 0.01ms !important;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
body:has(.drawer[open]) {
|
|
275
|
+
scale: 1;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
.drawer[open]:has(~ .drawer[open]),
|
|
279
|
+
.drawer[open]:has(~ .drawer[open] ~ .drawer[open]),
|
|
280
|
+
.drawer[open]:has(~ .drawer[open] ~ .drawer[open] ~ .drawer[open]),
|
|
281
|
+
.drawer[open]:has(~ .drawer[open] ~ .drawer[open] ~ .drawer[open] ~ .drawer[open]),
|
|
282
|
+
.drawer[open]:has(~ .drawer[open] ~ .drawer[open] ~ .drawer[open] ~ .drawer[open] ~ .drawer[open]) {
|
|
283
|
+
scale: 1;
|
|
284
|
+
translate: 0 0;
|
|
285
|
+
filter: none;
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
|
|
290
|
+
/*# sourceMappingURL=react.css.map*/
|
|
@@ -0,0 +1 @@
|
|
|
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 --drawer-bg: #fff;\n --drawer-radius: 24px;\n --drawer-max-width: 500px;\n --drawer-max-height: 96dvh;\n --drawer-backdrop: hsl(0 0% 0% / 0.4);\n --drawer-handle: hsl(0 0% 80%);\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-duration: 0.5s;\n --drawer-duration-close: 0.35s;\n --drawer-ease: cubic-bezier(0.32, 0.72, 0, 1);\n}\n\n@media (prefers-color-scheme: dark) {\n :root {\n --drawer-bg: hsl(0 0% 12%);\n --drawer-handle: hsl(0 0% 35%);\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: 0.94;\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 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-radius) var(--drawer-radius) 0 0;\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(4px);\n -webkit-backdrop-filter: blur(4px);\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 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 border-radius: var(--drawer-radius) 0 0 var(--drawer-radius);\n box-shadow: var(--drawer-shadow-right);\n}\n\n/* Left */\n.drawer[data-direction=\"left\"] {\n --_translate-closed: -100% 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 border-radius: 0 var(--drawer-radius) var(--drawer-radius) 0;\n box-shadow: var(--drawer-shadow-left);\n}\n\n/* Top */\n.drawer[data-direction=\"top\"] {\n --_translate-closed: 0 -100%;\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 border-radius: 0 0 var(--drawer-radius) var(--drawer-radius);\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: 0.94;\n translate: 0 -20px;\n border-radius: var(--drawer-radius);\n filter: brightness(0.92);\n pointer-events: none;\n}\n\n/* 2+ open drawers after */\n.drawer[open]:has(~ .drawer[open] ~ .drawer[open]) {\n scale: 0.88;\n translate: 0 -40px;\n filter: brightness(0.84);\n}\n\n/* 3+ open drawers after */\n.drawer[open]:has(~ .drawer[open] ~ .drawer[open] ~ .drawer[open]) {\n scale: 0.82;\n translate: 0 -60px;\n filter: brightness(0.76);\n}\n\n/* 4+ open drawers after */\n.drawer[open]:has(~ .drawer[open] ~ .drawer[open] ~ .drawer[open] ~ .drawer[open]) {\n scale: 0.76;\n translate: 0 -80px;\n filter: brightness(0.68);\n}\n\n/* 5+ open drawers after */\n.drawer[open]:has(~ .drawer[open] ~ .drawer[open] ~ .drawer[open] ~ .drawer[open] ~ .drawer[open]) {\n scale: 0.70;\n translate: 0 -100px;\n filter: brightness(0.60);\n}\n\n/* Lighter backdrop for stacked drawers */\n.drawer[open] ~ .drawer[open]::backdrop {\n background: hsl(0 0% 0% / 0.15);\n backdrop-filter: none;\n}\n\n/* Handle */\n.drawer-handle {\n display: flex;\n justify-content: center;\n padding: 1rem 0 0.5rem;\n cursor: grab;\n}\n\n.drawer-handle::before {\n content: '';\n width: 48px;\n height: 5px;\n background: var(--drawer-handle);\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: 56px;\n background: hsl(0 0% 60%);\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: 0.5rem 1rem 0.5rem 0.5rem;\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: 5px;\n height: 48px;\n}\n\n.drawer[data-direction=\"left\"] .drawer-handle:hover::before,\n.drawer[data-direction=\"right\"] .drawer-handle:hover::before {\n width: 5px;\n height: 56px;\n}\n\n/* Content */\n.drawer-content {\n padding: 0.5rem 1.5rem 2rem;\n padding-bottom: calc(2rem + env(safe-area-inset-bottom, 0px));\n overflow-x: hidden;\n overflow-y: auto;\n overscroll-behavior: contain;\n max-height: calc(var(--drawer-max-height) - 60px);\n}\n\n/* Content adjustments for directions */\n.drawer[data-direction=\"left\"] .drawer-content,\n.drawer[data-direction=\"right\"] .drawer-content {\n padding: 1.5rem;\n padding-left: 2.5rem;\n max-height: 100%;\n height: 100%;\n}\n\n.drawer[data-direction=\"top\"] .drawer-content {\n padding-bottom: 1.5rem;\n padding-top: 0.5rem;\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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;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;;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;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;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;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA"}
|
package/dist/react.d.cts
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import * as react_jsx_runtime0 from "react/jsx-runtime";
|
|
2
|
+
import * as react0 from "react";
|
|
3
|
+
import { ComponentPropsWithoutRef, ReactNode } from "react";
|
|
4
|
+
|
|
5
|
+
//#region src/react.d.ts
|
|
6
|
+
type Direction = 'bottom' | 'top' | 'left' | 'right';
|
|
7
|
+
interface RootProps {
|
|
8
|
+
children: ReactNode;
|
|
9
|
+
/** Direction the drawer opens from */
|
|
10
|
+
direction?: Direction;
|
|
11
|
+
}
|
|
12
|
+
declare function Root({
|
|
13
|
+
children,
|
|
14
|
+
direction
|
|
15
|
+
}: RootProps): react_jsx_runtime0.JSX.Element;
|
|
16
|
+
interface ContentProps extends Omit<ComponentPropsWithoutRef<'dialog'>, 'open'> {}
|
|
17
|
+
interface HandleProps extends ComponentPropsWithoutRef<'div'> {}
|
|
18
|
+
interface TitleProps extends ComponentPropsWithoutRef<'h2'> {}
|
|
19
|
+
interface DescriptionProps extends ComponentPropsWithoutRef<'p'> {}
|
|
20
|
+
declare const Drawer: {
|
|
21
|
+
Root: typeof Root;
|
|
22
|
+
Content: react0.ForwardRefExoticComponent<ContentProps & react0.RefAttributes<HTMLDialogElement>>;
|
|
23
|
+
Handle: react0.ForwardRefExoticComponent<HandleProps & react0.RefAttributes<HTMLDivElement>>;
|
|
24
|
+
Title: react0.ForwardRefExoticComponent<TitleProps & react0.RefAttributes<HTMLHeadingElement>>;
|
|
25
|
+
Description: react0.ForwardRefExoticComponent<DescriptionProps & react0.RefAttributes<HTMLParagraphElement>>;
|
|
26
|
+
};
|
|
27
|
+
//#endregion
|
|
28
|
+
export { Drawer, type ContentProps as DrawerContentProps, type DescriptionProps as DrawerDescriptionProps, type Direction as DrawerDirection, type HandleProps as DrawerHandleProps, type RootProps as DrawerRootProps, type TitleProps as DrawerTitleProps };
|
|
29
|
+
//# sourceMappingURL=react.d.cts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"react.d.cts","names":[],"sources":["../src/react.tsx"],"sourcesContent":[],"mappings":";;;;;KA8CK,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;UAqChB,WAAA,SAAoB,wBArCkB,CAAA,KAAA,CAAA,CAAA;AAAA,UAkDtC,UAAA,SAAmB,wBAzCO,CAAA,IAAL,CAAA,CAAA,CAAI;AA4BmB,UAqB5C,gBAAA,SAAyB,wBARkB,CAAA,GAAA,CAAA,CAAA,CAAA;AAgBxC,cAAA,MAMZ,EAAA"}
|
package/dist/react.d.mts
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import * as react0 from "react";
|
|
2
|
+
import { ComponentPropsWithoutRef, ReactNode } from "react";
|
|
3
|
+
import * as react_jsx_runtime0 from "react/jsx-runtime";
|
|
4
|
+
|
|
5
|
+
//#region src/react.d.ts
|
|
6
|
+
type Direction = 'bottom' | 'top' | 'left' | 'right';
|
|
7
|
+
interface RootProps {
|
|
8
|
+
children: ReactNode;
|
|
9
|
+
/** Direction the drawer opens from */
|
|
10
|
+
direction?: Direction;
|
|
11
|
+
}
|
|
12
|
+
declare function Root({
|
|
13
|
+
children,
|
|
14
|
+
direction
|
|
15
|
+
}: RootProps): react_jsx_runtime0.JSX.Element;
|
|
16
|
+
interface ContentProps extends Omit<ComponentPropsWithoutRef<'dialog'>, 'open'> {}
|
|
17
|
+
interface HandleProps extends ComponentPropsWithoutRef<'div'> {}
|
|
18
|
+
interface TitleProps extends ComponentPropsWithoutRef<'h2'> {}
|
|
19
|
+
interface DescriptionProps extends ComponentPropsWithoutRef<'p'> {}
|
|
20
|
+
declare const Drawer: {
|
|
21
|
+
Root: typeof Root;
|
|
22
|
+
Content: react0.ForwardRefExoticComponent<ContentProps & react0.RefAttributes<HTMLDialogElement>>;
|
|
23
|
+
Handle: react0.ForwardRefExoticComponent<HandleProps & react0.RefAttributes<HTMLDivElement>>;
|
|
24
|
+
Title: react0.ForwardRefExoticComponent<TitleProps & react0.RefAttributes<HTMLHeadingElement>>;
|
|
25
|
+
Description: react0.ForwardRefExoticComponent<DescriptionProps & react0.RefAttributes<HTMLParagraphElement>>;
|
|
26
|
+
};
|
|
27
|
+
//#endregion
|
|
28
|
+
export { Drawer, type ContentProps as DrawerContentProps, type DescriptionProps as DrawerDescriptionProps, type Direction as DrawerDirection, type HandleProps as DrawerHandleProps, type RootProps as DrawerRootProps, type TitleProps as DrawerTitleProps };
|
|
29
|
+
//# sourceMappingURL=react.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"react.d.mts","names":[],"sources":["../src/react.tsx"],"sourcesContent":[],"mappings":";;;;;KA8CK,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;UAqChB,WAAA,SAAoB,wBArCkB,CAAA,KAAA,CAAA,CAAA;AAAA,UAkDtC,UAAA,SAAmB,wBAzCO,CAAA,IAAL,CAAA,CAAA,CAAI;AA4BmB,UAqB5C,gBAAA,SAAyB,wBARkB,CAAA,GAAA,CAAA,CAAA,CAAA;AAgBxC,cAAA,MAMZ,EAAA"}
|
package/dist/react.mjs
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import{createContext as e,forwardRef as t,useContext as n}from"react";import{jsx as r}from"react/jsx-runtime";import './react.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 i=e({direction:void 0});function a(){return n(i)}function o({children:e,direction:t}){return r(i.Provider,{value:{direction:t},children:e})}const s=t(({children:e,className:t,...n},i)=>{let{direction:o}=a();return r(`dialog`,{ref:i,className:`drawer ${t??``}`.trim(),"data-direction":o,onClick:e=>{n.onClick?.(e),e.target===e.currentTarget&&e.currentTarget.close()},...n,children:e})});s.displayName=`Drawer.Content`;const c=t(({className:e,...t},n)=>r(`div`,{ref:n,className:`drawer-handle ${e??``}`.trim(),"aria-hidden":`true`,...t}));c.displayName=`Drawer.Handle`;const l=t((e,t)=>r(`h2`,{ref:t,...e}));l.displayName=`Drawer.Title`;const u=t((e,t)=>r(`p`,{ref:t,...e}));u.displayName=`Drawer.Description`;const d={Root:o,Content:s,Handle:c,Title:l,Description:u};export{d as Drawer};
|
|
3
|
+
//# sourceMappingURL=react.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"react.mjs","names":[],"sources":["../src/react.tsx"],"sourcesContent":["import {\n createContext,\n useContext,\n forwardRef,\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\nconst Content = forwardRef<HTMLDialogElement, ContentProps>(\n ({ children, className, ...props }, ref) => {\n const { direction } = useDrawerContext()\n\n return (\n <dialog\n ref={ref}\n className={`drawer ${className ?? ''}`.trim()}\n data-direction={direction}\n onClick={(e) => {\n props.onClick?.(e)\n // Backdrop click - only if clicking the dialog element itself\n if (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":"8GAUA,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,CAO7B,MAAM,EAAU,GACb,CAAE,WAAU,YAAW,GAAG,GAAS,IAAQ,CAC1C,GAAM,CAAE,aAAc,GAAkB,CAExC,OACE,EAAC,SAAA,CACM,MACL,UAAW,UAAU,GAAa,KAAK,MAAM,CAC7C,iBAAgB,EAChB,QAAU,GAAM,CACd,EAAM,UAAU,EAAE,CAEd,EAAE,SAAW,EAAE,eACjB,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
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "css-drawer",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Vaul-quality drawer component using native <dialog> and pure CSS animations",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.cjs",
|
|
7
|
+
"module": "./dist/index.mjs",
|
|
8
|
+
"types": "./dist/index.d.mts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"import": {
|
|
12
|
+
"types": "./dist/index.d.mts",
|
|
13
|
+
"default": "./dist/index.mjs"
|
|
14
|
+
},
|
|
15
|
+
"require": {
|
|
16
|
+
"types": "./dist/index.d.cts",
|
|
17
|
+
"default": "./dist/index.cjs"
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
"./react": {
|
|
21
|
+
"import": {
|
|
22
|
+
"types": "./dist/react.d.mts",
|
|
23
|
+
"default": "./dist/react.mjs"
|
|
24
|
+
},
|
|
25
|
+
"require": {
|
|
26
|
+
"types": "./dist/react.d.cts",
|
|
27
|
+
"default": "./dist/react.cjs"
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
"./styles": "./dist/react.css",
|
|
31
|
+
"./drawer.css": "./dist/react.css"
|
|
32
|
+
},
|
|
33
|
+
"files": [
|
|
34
|
+
"dist"
|
|
35
|
+
],
|
|
36
|
+
"peerDependencies": {
|
|
37
|
+
"react": ">=18.0.0",
|
|
38
|
+
"react-dom": ">=18.0.0"
|
|
39
|
+
},
|
|
40
|
+
"peerDependenciesMeta": {
|
|
41
|
+
"react": {
|
|
42
|
+
"optional": true
|
|
43
|
+
},
|
|
44
|
+
"react-dom": {
|
|
45
|
+
"optional": true
|
|
46
|
+
}
|
|
47
|
+
},
|
|
48
|
+
"devDependencies": {
|
|
49
|
+
"@bosh-code/tsdown-plugin-inject-css": "^2.0.0",
|
|
50
|
+
"@types/react": "^19.1.6",
|
|
51
|
+
"@types/react-dom": "^19.1.5",
|
|
52
|
+
"react": "^19.1.0",
|
|
53
|
+
"tsdown": "^0.18.2",
|
|
54
|
+
"typescript": "^5.7.2"
|
|
55
|
+
},
|
|
56
|
+
"keywords": [
|
|
57
|
+
"drawer",
|
|
58
|
+
"bottom-sheet",
|
|
59
|
+
"dialog",
|
|
60
|
+
"modal",
|
|
61
|
+
"css",
|
|
62
|
+
"headless",
|
|
63
|
+
"vaul",
|
|
64
|
+
"react"
|
|
65
|
+
],
|
|
66
|
+
"license": "MIT",
|
|
67
|
+
"author": "Saransh Chaudhary",
|
|
68
|
+
"repository": {
|
|
69
|
+
"type": "git",
|
|
70
|
+
"url": "git+https://github.com/tark-ai/css-drawer.git"
|
|
71
|
+
},
|
|
72
|
+
"homepage": "https://github.com/tark-ai/css-drawer#readme",
|
|
73
|
+
"bugs": {
|
|
74
|
+
"url": "https://github.com/tark-ai/css-drawer/issues"
|
|
75
|
+
},
|
|
76
|
+
"publishConfig": {
|
|
77
|
+
"access": "public"
|
|
78
|
+
},
|
|
79
|
+
"sideEffects": [
|
|
80
|
+
"*.css",
|
|
81
|
+
"./dist/index.mjs",
|
|
82
|
+
"./dist/index.cjs",
|
|
83
|
+
"./dist/react.mjs",
|
|
84
|
+
"./dist/react.cjs"
|
|
85
|
+
],
|
|
86
|
+
"scripts": {
|
|
87
|
+
"build": "tsdown",
|
|
88
|
+
"dev": "tsdown --watch"
|
|
89
|
+
}
|
|
90
|
+
}
|