css-drawer 0.2.1 → 0.3.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 CHANGED
@@ -8,7 +8,7 @@ A near drop-in replacement for [Vaul](https://vaul.emilkowal.ski) using native `
8
8
 
9
9
  | Feature | Vaul | CSS Drawer |
10
10
  |---------|------|------------|
11
- | Bundle size | ~12KB | **1.4KB** JS + 8KB CSS (gzip: ~2.5KB total) |
11
+ | Bundle size | ~12KB | **~2KB** JS + 10.8KB CSS (gzip: ~3KB total) |
12
12
  | Animation engine | JavaScript | Pure CSS |
13
13
  | Nesting | Manual setup | Automatic (CSS `:has()`) |
14
14
  | Accessibility | Built-in | Automatic (native `<dialog>` + `inert`) |
@@ -89,6 +89,7 @@ Then use the native dialog API in your component:
89
89
 
90
90
  ```typescript
91
91
  import { Component } from '@angular/core';
92
+ import { getTop, closeAll } from 'css-drawer';
92
93
 
93
94
  @Component({
94
95
  selector: 'app-example',
@@ -96,6 +97,9 @@ import { Component } from '@angular/core';
96
97
  <button (click)="openDrawer(drawer)">Open</button>
97
98
 
98
99
  <dialog #drawer class="drawer" data-direction="modal">
100
+ @if (isTopDrawer(drawer)) {
101
+ <div class="top-badge">Top Drawer</div>
102
+ }
99
103
  <div class="drawer-content">
100
104
  <h2>Title</h2>
101
105
  <button (click)="closeDrawer(drawer)">Close</button>
@@ -111,9 +115,19 @@ export class ExampleComponent {
111
115
  closeDrawer(dialog: HTMLDialogElement) {
112
116
  dialog.close();
113
117
  }
118
+
119
+ isTopDrawer(dialog: HTMLDialogElement): boolean {
120
+ return getTop() === dialog;
121
+ }
122
+
123
+ closeAllDrawers() {
124
+ closeAll();
125
+ }
114
126
  }
115
127
  ```
116
128
 
129
+ > **Note:** Angular's change detection runs after template-bound events like `(click)`, so `isTopDrawer()` re-evaluates automatically. For zoneless Angular or programmatic updates outside template events, use signals.
130
+
117
131
  ---
118
132
 
119
133
  ## React API
@@ -223,6 +237,50 @@ Semantic description for accessibility.
223
237
  |------|------|---------|-------------|
224
238
  | `...props` | `HTMLAttributes<HTMLParagraphElement>` | - | All native p props |
225
239
 
240
+ ### useIsTopDrawer(ref)
241
+
242
+ Hook to check if a drawer is the topmost open drawer. Useful for conditionally rendering content (like notifications) only in the top drawer.
243
+
244
+ ```tsx
245
+ import { useRef } from 'react'
246
+ import { Drawer, useIsTopDrawer } from 'css-drawer/react'
247
+
248
+ function MyDrawer() {
249
+ const ref = useRef<HTMLDialogElement>(null)
250
+ const isTop = useIsTopDrawer(ref)
251
+
252
+ return (
253
+ <Drawer.Root>
254
+ <Drawer.Content ref={ref}>
255
+ {isTop && <div className="notification">You have new messages</div>}
256
+ {/* drawer content */}
257
+ </Drawer.Content>
258
+ </Drawer.Root>
259
+ )
260
+ }
261
+ ```
262
+
263
+ | Param | Type | Description |
264
+ |-------|------|-------------|
265
+ | `ref` | `RefObject<HTMLDialogElement \| null>` | Ref to the drawer dialog element |
266
+
267
+ **Returns:** `boolean` - `true` if this drawer is currently the topmost open drawer
268
+
269
+ The hook automatically updates when any drawer opens or closes.
270
+
271
+ ### getTopDrawer()
272
+
273
+ Utility function to get the topmost open drawer element. Useful for imperative access.
274
+
275
+ ```tsx
276
+ import { getTopDrawer } from 'css-drawer/react'
277
+
278
+ const topDrawer = getTopDrawer()
279
+ topDrawer?.close()
280
+ ```
281
+
282
+ **Returns:** `HTMLDialogElement | null`
283
+
226
284
  ---
227
285
 
228
286
  ## Vanilla JS API
@@ -230,7 +288,7 @@ Semantic description for accessibility.
230
288
  ### Installation
231
289
 
232
290
  ```ts
233
- import { open, close, closeAll } from 'css-drawer'
291
+ import { open, close, closeAll, getTop, subscribe } from 'css-drawer'
234
292
  // Styles are auto-injected
235
293
  ```
236
294
 
@@ -613,8 +671,16 @@ Override any of these CSS custom properties to customize the drawer:
613
671
 
614
672
  | Variable | Default | Description |
615
673
  |----------|---------|-------------|
616
- | `--drawer-max-width` | `500px` | Maximum width |
617
- | `--drawer-max-height` | `96dvh` | Maximum height (uses dynamic viewport) |
674
+ | `--drawer-width` | direction-based | Drawer width (100% for bottom/top, 500px for left/right) |
675
+ | `--drawer-height` | direction-based | Drawer height (auto for bottom/top, 100dvh for left/right) |
676
+ | `--drawer-max-width` | direction-based | Maximum width (none for bottom/top, 90% for left/right/modal) |
677
+ | `--drawer-max-height` | `96dvh` | Maximum height (bottom/top/modal only) |
678
+ | `--drawer-modal-width` | `fit-content` | Modal width |
679
+ | `--drawer-modal-height` | `fit-content` | Modal height |
680
+
681
+ > **Note:** `--drawer-width`, `--drawer-height`, and `--drawer-max-width` are not defined globally—each direction uses sensible fallbacks. Set these per-instance to override.
682
+
683
+ > **Fullscreen modal:** Set `--drawer-modal-width`, `--drawer-modal-height`, `--drawer-max-width`, and `--drawer-max-height` to `100%`.
618
684
 
619
685
  #### Handle
620
686
 
@@ -810,6 +876,8 @@ Full TypeScript support included.
810
876
  // React
811
877
  import {
812
878
  Drawer,
879
+ useIsTopDrawer,
880
+ getTopDrawer,
813
881
  type DrawerRootProps,
814
882
  type DrawerContentProps,
815
883
  type DrawerDirection
@@ -819,6 +887,9 @@ import {
819
887
  import {
820
888
  open,
821
889
  close,
890
+ getTop,
891
+ closeAll,
892
+ subscribe,
822
893
  create,
823
894
  type DrawerElement,
824
895
  type DrawerRef,
package/dist/index.cjs CHANGED
@@ -1,5 +1,5 @@
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=`
1
+ require('./observer-CtfG4nrq.css');
2
+ const e=require(`./observer-B-WjvsFU.cjs`);function t(e){return e?typeof e==`string`?document.getElementById(e):e:null}function n(e){t(e)?.showModal()}function r(e){t(e)?.close()}function i(){Array.from(document.querySelectorAll(`dialog.drawer[open]`)).reverse().forEach(e=>e.close())}function a(e){return t(e)?.open??!1}function o(){return Array.from(document.querySelectorAll(`dialog.drawer[open]`))}function s(){let e=o();return e[e.length-1]??null}function c(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
3
  ${i?`<div class="drawer-handle"></div>`:``}
4
4
  <div class="drawer-content">${n}</div>
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;
5
+ `,s.addEventListener(`click`,e=>{e.target===s&&s.dataset.closeOnOutsideClick!==`false`&&s.close()}),s}function l(e){return document.body.appendChild(e),e}function u(e){t(e)?.remove()}function d(){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 f(n,r){let i=t(n);if(!i)return()=>{};let{onOpen:a,onClose:o,onCancel:s}=r,c=()=>o?.(),l=()=>s?.(),u=e=>{e.detail?.target===i&&i.open&&a?.()};return i.addEventListener(`close`,c),i.addEventListener(`cancel`,l),window.addEventListener(e.t,u),()=>{i.removeEventListener(`close`,c),i.removeEventListener(`cancel`,l),window.removeEventListener(e.t,u)}}function p(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=r,exports.closeAll=i,exports.create=c,exports.getOpen=o,exports.getTop=s,exports.init=d,exports.isOpen=a,exports.mount=l,exports.open=n,exports.props=p,exports.subscribe=f,exports.unmount=u;
@@ -1 +1 @@
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"}
1
+ {"version":3,"file":"index.d.cts","names":[],"sources":["../src/index.ts"],"sourcesContent":[],"mappings":";;AA4BiB,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;EAmCT;;;;;UAxKC,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;;;;;iBAiCI,KAAA;;;;;;wBAMC"}
@@ -1 +1 @@
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"}
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../src/index.ts"],"sourcesContent":[],"mappings":";;AA4BiB,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;EAmCT;;;;;UAxKC,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;;;;;iBAiCI,KAAA;;;;;;wBAMC"}
package/dist/index.mjs CHANGED
@@ -1,6 +1,6 @@
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=`
1
+ import{t as e}from"./observer-DIOx0VCj.mjs";import './observer-QD6bSdOu.css';
2
+ function t(e){return e?typeof e==`string`?document.getElementById(e):e:null}function n(e){t(e)?.showModal()}function r(e){t(e)?.close()}function i(){Array.from(document.querySelectorAll(`dialog.drawer[open]`)).reverse().forEach(e=>e.close())}function a(e){return t(e)?.open??!1}function o(){return Array.from(document.querySelectorAll(`dialog.drawer[open]`))}function s(){let e=o();return e[e.length-1]??null}function c(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
3
  ${i?`<div class="drawer-handle"></div>`:``}
4
4
  <div class="drawer-content">${n}</div>
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
+ `,s.addEventListener(`click`,e=>{e.target===s&&s.dataset.closeOnOutsideClick!==`false`&&s.close()}),s}function l(e){return document.body.appendChild(e),e}function u(e){t(e)?.remove()}function d(){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 f(n,r){let i=t(n);if(!i)return()=>{};let{onOpen:a,onClose:o,onCancel:s}=r,c=()=>o?.(),l=()=>s?.(),u=e=>{e.detail?.target===i&&i.open&&a?.()};return i.addEventListener(`close`,c),i.addEventListener(`cancel`,l),window.addEventListener(e,u),()=>{i.removeEventListener(`close`,c),i.removeEventListener(`cancel`,l),window.removeEventListener(e,u)}}function p(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{r as close,i as closeAll,c as create,o as getOpen,s as getTop,d as init,a as isOpen,l as mount,n as open,p as props,f as subscribe,u as unmount};
6
6
  //# sourceMappingURL=index.mjs.map
@@ -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 */\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"}
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'\nimport { DRAWER_STATE_CHANGE } from './observer'\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 // Listen to shared drawer state change event (no per-subscription observer)\n const handleStateChange = (e: Event) => {\n const detail = (e as CustomEvent).detail\n if (detail?.target === el && el.open) {\n onOpen?.()\n }\n }\n\n el.addEventListener('close', handleClose)\n el.addEventListener('cancel', handleCancel)\n window.addEventListener(DRAWER_STATE_CHANGE, handleStateChange)\n\n return () => {\n el.removeEventListener('close', handleClose)\n el.removeEventListener('cancel', handleCancel)\n window.removeEventListener(DRAWER_STATE_CHANGE, handleStateChange)\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":"4CAwCA,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,EAAqB,GAAa,CACtB,EAAkB,QACtB,SAAW,GAAM,EAAG,MAC9B,KAAU,EAQd,OAJA,EAAG,iBAAiB,QAAS,EAAY,CACzC,EAAG,iBAAiB,SAAU,EAAa,CAC3C,OAAO,iBAAiB,EAAqB,EAAkB,KAElD,CACX,EAAG,oBAAoB,QAAS,EAAY,CAC5C,EAAG,oBAAoB,SAAU,EAAa,CAC9C,OAAO,oBAAoB,EAAqB,EAAkB,EAQtE,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"}
@@ -0,0 +1 @@
1
+ const e=`drawer:statechange`;let t=!1;function n(){if(typeof window>`u`||t)return;t=!0;let n=()=>{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 r of t)if(r.type===`attributes`&&r.attributeName===`open`&&r.target.classList.contains(`drawer`)){n(),window.dispatchEvent(new CustomEvent(e,{detail:{target:r.target}}));break}}).observe(document.body,{subtree:!0,attributes:!0,attributeFilter:[`open`]})}n(),Object.defineProperty(exports,`t`,{enumerable:!0,get:function(){return e}});
@@ -8,8 +8,9 @@
8
8
  --drawer-backdrop-blur: 4px;
9
9
 
10
10
  /* Sizing */
11
- --drawer-max-width: 500px;
12
11
  --drawer-max-height: 96dvh;
12
+ --drawer-modal-width: fit-content;
13
+ --drawer-modal-height: fit-content;
13
14
 
14
15
  /* Handle */
15
16
  --drawer-handle-bg: hsl(0 0% 80%);
@@ -88,10 +89,10 @@ body:has(.drawer[open]) {
88
89
  --_border-radius: var(--drawer-radius) var(--drawer-radius) 0 0;
89
90
  inset: auto 0 0 0;
90
91
  margin-inline: auto;
91
- width: 100%;
92
- max-width: var(--drawer-max-width);
93
- height: auto;
94
- max-height: var(--drawer-max-height);
92
+ width: var(--drawer-width, 100%);
93
+ max-width: var(--drawer-max-width, none);
94
+ height: var(--drawer-height, auto);
95
+ max-height: var(--drawer-max-height, 96dvh);
95
96
  border-radius: var(--drawer-border-radius, var(--_border-radius));
96
97
  box-shadow: var(--drawer-shadow-bottom);
97
98
  translate: var(--_translate-closed);
@@ -138,9 +139,9 @@ body:has(.drawer[open]) {
138
139
  --_border-radius: var(--drawer-radius) 0 0 var(--drawer-radius);
139
140
  inset: 0 0 0 auto;
140
141
  margin: 0;
141
- width: 100%;
142
- max-width: var(--drawer-max-width);
143
- height: 100dvh;
142
+ width: var(--drawer-width, 500px);
143
+ max-width: var(--drawer-max-width, 90%);
144
+ height: var(--drawer-height, 100dvh);
144
145
  max-height: 100dvh;
145
146
  box-shadow: var(--drawer-shadow-right);
146
147
  }
@@ -151,9 +152,9 @@ body:has(.drawer[open]) {
151
152
  --_border-radius: 0 var(--drawer-radius) var(--drawer-radius) 0;
152
153
  inset: 0 auto 0 0;
153
154
  margin: 0;
154
- width: 100%;
155
- max-width: var(--drawer-max-width);
156
- height: 100dvh;
155
+ width: var(--drawer-width, 500px);
156
+ max-width: var(--drawer-max-width, 90%);
157
+ height: var(--drawer-height, 100dvh);
157
158
  max-height: 100dvh;
158
159
  box-shadow: var(--drawer-shadow-left);
159
160
  }
@@ -164,10 +165,10 @@ body:has(.drawer[open]) {
164
165
  --_border-radius: 0 0 var(--drawer-radius) var(--drawer-radius);
165
166
  inset: 0 0 auto 0;
166
167
  margin-inline: auto;
167
- width: 100%;
168
- max-width: var(--drawer-max-width);
169
- height: auto;
170
- max-height: var(--drawer-max-height);
168
+ width: var(--drawer-width, 100%);
169
+ max-width: var(--drawer-max-width, none);
170
+ height: var(--drawer-height, auto);
171
+ max-height: var(--drawer-max-height, 96dvh);
171
172
  box-shadow: var(--drawer-shadow-top);
172
173
  }
173
174
 
@@ -177,10 +178,10 @@ body:has(.drawer[open]) {
177
178
  --_border-radius: var(--drawer-radius);
178
179
  inset: 0;
179
180
  margin: auto;
180
- width: fit-content;
181
- max-width: var(--drawer-max-width);
182
- height: fit-content;
183
- max-height: var(--drawer-max-height);
181
+ width: var(--drawer-modal-width, fit-content);
182
+ max-width: var(--drawer-max-width, 90%);
183
+ height: var(--drawer-modal-height, fit-content);
184
+ max-height: var(--drawer-max-height, 96dvh);
184
185
  box-shadow: var(--drawer-shadow-modal);
185
186
  scale: var(--drawer-nested-scale);
186
187
  }
@@ -0,0 +1,2 @@
1
+ const e=`drawer:statechange`;let t=!1;function n(){if(typeof window>`u`||t)return;t=!0;let n=()=>{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 r of t)if(r.type===`attributes`&&r.attributeName===`open`&&r.target.classList.contains(`drawer`)){n(),window.dispatchEvent(new CustomEvent(e,{detail:{target:r.target}}));break}}).observe(document.body,{subtree:!0,attributes:!0,attributeFilter:[`open`]})}n();export{e as t};
2
+ //# sourceMappingURL=observer-DIOx0VCj.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"observer-DIOx0VCj.mjs","names":[],"sources":["../src/observer.ts"],"sourcesContent":["/**\n * Shared drawer state observer\n * Single MutationObserver that dispatches events for all drawer state changes\n */\n\nexport const DRAWER_STATE_CHANGE = 'drawer:statechange'\n\nlet initialized = false\n\nexport function initDrawerObserver(): void {\n if (typeof window === 'undefined' || initialized) return\n initialized = true\n\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 window.dispatchEvent(new CustomEvent(DRAWER_STATE_CHANGE, {\n detail: { target: mutation.target }\n }))\n break\n }\n }\n })\n\n observer.observe(document.body, {\n subtree: true,\n attributes: true,\n attributeFilter: ['open'],\n })\n}\n\n// Auto-initialize on import\ninitDrawerObserver()\n"],"mappings":"AAKA,MAAa,EAAsB,qBAEnC,IAAI,EAAc,GAElB,SAAgB,GAA2B,CACzC,GAAI,OAAO,OAAW,KAAe,EAAa,OAClD,EAAc,GAEd,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,OAAO,cAAc,IAAI,YAAY,EAAqB,CACxD,OAAQ,CAAE,OAAQ,EAAS,OAAQ,CACpC,CAAC,CAAC,CACH,QAGJ,CAEO,QAAQ,SAAS,KAAM,CAC9B,QAAS,GACT,WAAY,GACZ,gBAAiB,CAAC,OAAO,CAC1B,CAAC,CAIJ,GAAoB"}
@@ -8,8 +8,9 @@
8
8
  --drawer-backdrop-blur: 4px;
9
9
 
10
10
  /* Sizing */
11
- --drawer-max-width: 500px;
12
11
  --drawer-max-height: 96dvh;
12
+ --drawer-modal-width: fit-content;
13
+ --drawer-modal-height: fit-content;
13
14
 
14
15
  /* Handle */
15
16
  --drawer-handle-bg: hsl(0 0% 80%);
@@ -88,10 +89,10 @@ body:has(.drawer[open]) {
88
89
  --_border-radius: var(--drawer-radius) var(--drawer-radius) 0 0;
89
90
  inset: auto 0 0 0;
90
91
  margin-inline: auto;
91
- width: 100%;
92
- max-width: var(--drawer-max-width);
93
- height: auto;
94
- max-height: var(--drawer-max-height);
92
+ width: var(--drawer-width, 100%);
93
+ max-width: var(--drawer-max-width, none);
94
+ height: var(--drawer-height, auto);
95
+ max-height: var(--drawer-max-height, 96dvh);
95
96
  border-radius: var(--drawer-border-radius, var(--_border-radius));
96
97
  box-shadow: var(--drawer-shadow-bottom);
97
98
  translate: var(--_translate-closed);
@@ -138,9 +139,9 @@ body:has(.drawer[open]) {
138
139
  --_border-radius: var(--drawer-radius) 0 0 var(--drawer-radius);
139
140
  inset: 0 0 0 auto;
140
141
  margin: 0;
141
- width: 100%;
142
- max-width: var(--drawer-max-width);
143
- height: 100dvh;
142
+ width: var(--drawer-width, 500px);
143
+ max-width: var(--drawer-max-width, 90%);
144
+ height: var(--drawer-height, 100dvh);
144
145
  max-height: 100dvh;
145
146
  box-shadow: var(--drawer-shadow-right);
146
147
  }
@@ -151,9 +152,9 @@ body:has(.drawer[open]) {
151
152
  --_border-radius: 0 var(--drawer-radius) var(--drawer-radius) 0;
152
153
  inset: 0 auto 0 0;
153
154
  margin: 0;
154
- width: 100%;
155
- max-width: var(--drawer-max-width);
156
- height: 100dvh;
155
+ width: var(--drawer-width, 500px);
156
+ max-width: var(--drawer-max-width, 90%);
157
+ height: var(--drawer-height, 100dvh);
157
158
  max-height: 100dvh;
158
159
  box-shadow: var(--drawer-shadow-left);
159
160
  }
@@ -164,10 +165,10 @@ body:has(.drawer[open]) {
164
165
  --_border-radius: 0 0 var(--drawer-radius) var(--drawer-radius);
165
166
  inset: 0 0 auto 0;
166
167
  margin-inline: auto;
167
- width: 100%;
168
- max-width: var(--drawer-max-width);
169
- height: auto;
170
- max-height: var(--drawer-max-height);
168
+ width: var(--drawer-width, 100%);
169
+ max-width: var(--drawer-max-width, none);
170
+ height: var(--drawer-height, auto);
171
+ max-height: var(--drawer-max-height, 96dvh);
171
172
  box-shadow: var(--drawer-shadow-top);
172
173
  }
173
174
 
@@ -177,10 +178,10 @@ body:has(.drawer[open]) {
177
178
  --_border-radius: var(--drawer-radius);
178
179
  inset: 0;
179
180
  margin: auto;
180
- width: fit-content;
181
- max-width: var(--drawer-max-width);
182
- height: fit-content;
183
- max-height: var(--drawer-max-height);
181
+ width: var(--drawer-modal-width, fit-content);
182
+ max-width: var(--drawer-max-width, 90%);
183
+ height: var(--drawer-modal-height, fit-content);
184
+ max-height: var(--drawer-max-height, 96dvh);
184
185
  box-shadow: var(--drawer-shadow-modal);
185
186
  scale: var(--drawer-nested-scale);
186
187
  }
@@ -330,4 +331,4 @@ body:has(.drawer[open]) {
330
331
  }
331
332
 
332
333
 
333
- /*# sourceMappingURL=drawer-CXCJQa45.css.map*/
334
+ /*# sourceMappingURL=observer-QD6bSdOu.css.map*/
@@ -0,0 +1 @@
1
+ {"version":3,"file":"observer-QD6bSdOu.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-height: 96dvh;\n --drawer-modal-width: fit-content;\n --drawer-modal-height: fit-content;\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: var(--drawer-width, 100%);\n max-width: var(--drawer-max-width, none);\n height: var(--drawer-height, auto);\n max-height: var(--drawer-max-height, 96dvh);\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: var(--drawer-width, 500px);\n max-width: var(--drawer-max-width, 90%);\n height: var(--drawer-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: var(--drawer-width, 500px);\n max-width: var(--drawer-max-width, 90%);\n height: var(--drawer-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: var(--drawer-width, 100%);\n max-width: var(--drawer-max-width, none);\n height: var(--drawer-height, auto);\n max-height: var(--drawer-max-height, 96dvh);\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: var(--drawer-modal-width, fit-content);\n max-width: var(--drawer-max-width, 90%);\n height: var(--drawer-modal-height, fit-content);\n max-height: var(--drawer-max-height, 96dvh);\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;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"}
package/dist/react.cjs CHANGED
@@ -1,2 +1,2 @@
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;
1
+ require('./observer-CtfG4nrq.css');
2
+ const e=require(`./observer-B-WjvsFU.cjs`);let t=require(`react`),n=require(`react/jsx-runtime`);const r=(0,t.createContext)({direction:void 0});function i(){return(0,t.useContext)(r)}function a(){return typeof window>`u`?[]:Array.from(document.querySelectorAll(`dialog.drawer[open]`))}function o(){let e=a();return e[e.length-1]??null}function s(n){let[r,i]=(0,t.useState)(!1);return(0,t.useEffect)(()=>{let t=()=>{let e=o();i(n.current!==null&&n.current===e)};return t(),window.addEventListener(e.t,t),()=>window.removeEventListener(e.t,t)},[n]),r}function c({children:e,direction:t}){return(0,n.jsx)(r.Provider,{value:{direction:t},children:e})}const l=(0,t.forwardRef)(({children:e,className:r,open:a,onOpenChange:o,closeOnOutsideClick:s=!0,...c},l)=>{let{direction:u}=i(),d=(0,t.useRef)(null),f=l||d;return(0,t.useEffect)(()=>{let e=f.current;e&&(a&&!e.open?(e.showModal(),o?.(!0)):a===!1&&e.open&&e.close())},[a]),(0,n.jsx)(`dialog`,{ref:f,className:`drawer ${r??``}`.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:e})});l.displayName=`Drawer.Content`;const u=(0,t.forwardRef)(({className:e,...t},r)=>(0,n.jsx)(`div`,{ref:r,className:`drawer-handle ${e??``}`.trim(),"aria-hidden":`true`,...t}));u.displayName=`Drawer.Handle`;const d=(0,t.forwardRef)((e,t)=>(0,n.jsx)(`h2`,{ref:t,...e}));d.displayName=`Drawer.Title`;const f=(0,t.forwardRef)((e,t)=>(0,n.jsx)(`p`,{ref:t,...e}));f.displayName=`Drawer.Description`;const p={Root:c,Content:l,Handle:u,Title:d,Description:f};exports.Drawer=p,exports.getTopDrawer=o,exports.useIsTopDrawer=s;
package/dist/react.d.cts CHANGED
@@ -1,9 +1,18 @@
1
1
  import * as react_jsx_runtime0 from "react/jsx-runtime";
2
2
  import * as react0 from "react";
3
- import { ComponentPropsWithoutRef, ReactNode } from "react";
3
+ import { ComponentPropsWithoutRef, ReactNode, RefObject } from "react";
4
4
 
5
5
  //#region src/react.d.ts
6
6
  type Direction = 'bottom' | 'top' | 'left' | 'right' | 'modal';
7
+ /**
8
+ * Get the topmost open drawer
9
+ */
10
+ declare function getTopDrawer(): HTMLDialogElement | null;
11
+ /**
12
+ * Hook to check if a drawer is the topmost open drawer
13
+ * Useful for conditionally rendering content only in the top drawer
14
+ */
15
+ declare function useIsTopDrawer(ref: RefObject<HTMLDialogElement | null>): boolean;
7
16
  interface RootProps {
8
17
  children: ReactNode;
9
18
  /** Direction the drawer opens from */
@@ -32,5 +41,5 @@ declare const Drawer: {
32
41
  Description: react0.ForwardRefExoticComponent<DescriptionProps & react0.RefAttributes<HTMLParagraphElement>>;
33
42
  };
34
43
  //#endregion
35
- 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 };
44
+ 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, getTopDrawer, useIsTopDrawer };
36
45
  //# sourceMappingURL=react.d.cts.map
@@ -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;EAyD1B;EAaA,mBAAW,CAAA,EAAA,OAAQ;AAAwB;AAgBrD,UA7BU,WAAA,SAAoB,wBAmC7B,CAAA,KAAA,CAAA,CAAA;UAtBS,UAAA,SAAmB;UAQnB,gBAAA,SAAyB;cAQtB"}
1
+ {"version":3,"file":"react.d.cts","names":[],"sources":["../src/react.tsx"],"sourcesContent":[],"mappings":";;;;;KAeK,SAAA;;;AAJgB;AA8BL,iBAAA,YAAA,CAAA,CAAgB,EAAA,iBAAiB,GAAA,IAAA;AASjD;AAkBC;AAMsB;;AAGG,iBA3BV,cAAA,CA2BU,GAAA,EA3BU,SA2BV,CA3BoB,iBA2BpB,GAAA,IAAA,CAAA,CAAA,EAAA,OAAA;UANhB,SAAA,CAM6B;EAAS,QAAA,EALpC,SAKoC;EAAA;EAStC,SAAA,CAAA,EAZI,SAYS;AAAY;AAyDmB,iBAlE7C,IAAA,CA+EY;EAAA,QAAQ;EAAA;AAAwB,CAAxB,EA/EU,SA+Ec,CAAA,EA/EL,kBAAA,CAAA,GAAA,CAAA,OA+EK;AAAA,UAtE3C,YAAA,SAAqB,IA8EI,CA9EC,wBA8EuB,CAAA,QAAA,CAAA,EAAA,MAAA,CAAA,CAAA;EAQ9C;;;;;;;UA7BH,WAAA,SAAoB;UAapB,UAAA,SAAmB;UAQnB,gBAAA,SAAyB;cAQtB"}
package/dist/react.d.mts CHANGED
@@ -1,9 +1,18 @@
1
1
  import * as react0 from "react";
2
- import { ComponentPropsWithoutRef, ReactNode } from "react";
2
+ import { ComponentPropsWithoutRef, ReactNode, RefObject } from "react";
3
3
  import * as react_jsx_runtime0 from "react/jsx-runtime";
4
4
 
5
5
  //#region src/react.d.ts
6
6
  type Direction = 'bottom' | 'top' | 'left' | 'right' | 'modal';
7
+ /**
8
+ * Get the topmost open drawer
9
+ */
10
+ declare function getTopDrawer(): HTMLDialogElement | null;
11
+ /**
12
+ * Hook to check if a drawer is the topmost open drawer
13
+ * Useful for conditionally rendering content only in the top drawer
14
+ */
15
+ declare function useIsTopDrawer(ref: RefObject<HTMLDialogElement | null>): boolean;
7
16
  interface RootProps {
8
17
  children: ReactNode;
9
18
  /** Direction the drawer opens from */
@@ -32,5 +41,5 @@ declare const Drawer: {
32
41
  Description: react0.ForwardRefExoticComponent<DescriptionProps & react0.RefAttributes<HTMLParagraphElement>>;
33
42
  };
34
43
  //#endregion
35
- 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 };
44
+ 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, getTopDrawer, useIsTopDrawer };
36
45
  //# sourceMappingURL=react.d.mts.map
@@ -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;EAyD1B;EAaA,mBAAW,CAAA,EAAA,OAAQ;AAAwB;AAgBrD,UA7BU,WAAA,SAAoB,wBAmC7B,CAAA,KAAA,CAAA,CAAA;UAtBS,UAAA,SAAmB;UAQnB,gBAAA,SAAyB;cAQtB"}
1
+ {"version":3,"file":"react.d.mts","names":[],"sources":["../src/react.tsx"],"sourcesContent":[],"mappings":";;;;;KAeK,SAAA;;;AAJgB;AA8BL,iBAAA,YAAA,CAAA,CAAgB,EAAA,iBAAiB,GAAA,IAAA;AASjD;AAkBC;AAMsB;;AAGG,iBA3BV,cAAA,CA2BU,GAAA,EA3BU,SA2BV,CA3BoB,iBA2BpB,GAAA,IAAA,CAAA,CAAA,EAAA,OAAA;UANhB,SAAA,CAM6B;EAAS,QAAA,EALpC,SAKoC;EAAA;EAStC,SAAA,CAAA,EAZI,SAYS;AAAY;AAyDmB,iBAlE7C,IAAA,CA+EY;EAAA,QAAQ;EAAA;AAAwB,CAAxB,EA/EU,SA+Ec,CAAA,EA/EL,kBAAA,CAAA,GAAA,CAAA,OA+EK;AAAA,UAtE3C,YAAA,SAAqB,IA8EI,CA9EC,wBA8EuB,CAAA,QAAA,CAAA,EAAA,MAAA,CAAA,CAAA;EAQ9C;;;;;;;UA7BH,WAAA,SAAoB;UAapB,UAAA,SAAmB;UAQnB,gBAAA,SAAyB;cAQtB"}
package/dist/react.mjs CHANGED
@@ -1,3 +1,3 @@
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};
1
+ import{t as e}from"./observer-DIOx0VCj.mjs";import{createContext as t,forwardRef as n,useContext as r,useEffect as i,useRef as a,useState as o}from"react";import{jsx as s}from"react/jsx-runtime";import './observer-QD6bSdOu.css';
2
+ const c=t({direction:void 0});function l(){return r(c)}function u(){return typeof window>`u`?[]:Array.from(document.querySelectorAll(`dialog.drawer[open]`))}function d(){let e=u();return e[e.length-1]??null}function f(t){let[n,r]=o(!1);return i(()=>{let n=()=>{let e=d();r(t.current!==null&&t.current===e)};return n(),window.addEventListener(e,n),()=>window.removeEventListener(e,n)},[t]),n}function p({children:e,direction:t}){return s(c.Provider,{value:{direction:t},children:e})}const m=n(({children:e,className:t,open:n,onOpenChange:r,closeOnOutsideClick:o=!0,...c},u)=>{let{direction:d}=l(),f=a(null),p=u||f;return i(()=>{let e=p.current;e&&(n&&!e.open?(e.showModal(),r?.(!0)):n===!1&&e.open&&e.close())},[n]),s(`dialog`,{ref:p,className:`drawer ${t??``}`.trim(),"data-direction":d,onClose:e=>{e.target===e.currentTarget&&(c.onClose?.(e),r?.(!1))},onClick:e=>{c.onClick?.(e),o&&e.target===e.currentTarget&&e.currentTarget.close()},...c,children:e})});m.displayName=`Drawer.Content`;const h=n(({className:e,...t},n)=>s(`div`,{ref:n,className:`drawer-handle ${e??``}`.trim(),"aria-hidden":`true`,...t}));h.displayName=`Drawer.Handle`;const g=n((e,t)=>s(`h2`,{ref:t,...e}));g.displayName=`Drawer.Title`;const _=n((e,t)=>s(`p`,{ref:t,...e}));_.displayName=`Drawer.Description`;const v={Root:p,Content:m,Handle:h,Title:g,Description:_};export{v as Drawer,d as getTopDrawer,f as useIsTopDrawer};
3
3
  //# sourceMappingURL=react.mjs.map
@@ -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' | '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"}
1
+ {"version":3,"file":"react.mjs","names":[],"sources":["../src/react.tsx"],"sourcesContent":["import {\n createContext,\n useContext,\n forwardRef,\n useEffect,\n useRef,\n useState,\n type ReactNode,\n type ComponentPropsWithoutRef,\n type RefObject,\n} from 'react'\nimport './drawer.css'\nimport { DRAWER_STATE_CHANGE } from './observer'\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/* ===== Utilities ===== */\n\n/**\n * Get all open drawers\n */\nfunction getOpenDrawers(): HTMLDialogElement[] {\n if (typeof window === 'undefined') return []\n return Array.from(document.querySelectorAll<HTMLDialogElement>('dialog.drawer[open]'))\n}\n\n/**\n * Get the topmost open drawer\n */\nexport function getTopDrawer(): HTMLDialogElement | null {\n const open = getOpenDrawers()\n return open[open.length - 1] ?? null\n}\n\n/**\n * Hook to check if a drawer is the topmost open drawer\n * Useful for conditionally rendering content only in the top drawer\n */\nexport function useIsTopDrawer(ref: RefObject<HTMLDialogElement | null>): boolean {\n const [isTop, setIsTop] = useState(false)\n\n useEffect(() => {\n const checkIsTop = () => {\n const topDrawer = getTopDrawer()\n setIsTop(ref.current !== null && ref.current === topDrawer)\n }\n\n // Initial check\n checkIsTop()\n\n // Listen to shared drawer state change event (single observer, not N observers)\n window.addEventListener(DRAWER_STATE_CHANGE, checkIsTop)\n return () => window.removeEventListener(DRAWER_STATE_CHANGE, checkIsTop)\n }, [ref])\n\n return isTop\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":"mMAsBA,MAAM,EAAgB,EAAkC,CAAE,UAAW,IAAA,GAAW,CAAC,CAEjF,SAAS,GAAmB,CAC1B,OAAO,EAAW,EAAc,CAQlC,SAAS,GAAsC,CAE7C,OADI,OAAO,OAAW,IAAoB,EAAE,CACrC,MAAM,KAAK,SAAS,iBAAoC,sBAAsB,CAAC,CAMxF,SAAgB,GAAyC,CACvD,IAAM,EAAO,GAAgB,CAC7B,OAAO,EAAK,EAAK,OAAS,IAAM,KAOlC,SAAgB,EAAe,EAAmD,CAChF,GAAM,CAAC,EAAO,GAAY,EAAS,GAAM,CAgBzC,OAdA,MAAgB,CACd,IAAM,MAAmB,CACvB,IAAM,EAAY,GAAc,CAChC,EAAS,EAAI,UAAY,MAAQ,EAAI,UAAY,EAAU,EAQ7D,OAJA,GAAY,CAGZ,OAAO,iBAAiB,EAAqB,EAAW,KAC3C,OAAO,oBAAoB,EAAqB,EAAW,EACvE,CAAC,EAAI,CAAC,CAEF,EAUT,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.2.1",
3
+ "version": "0.3.0",
4
4
  "description": "Vaul-quality drawer component using native <dialog> and pure CSS animations",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
package/src/drawer.css CHANGED
@@ -8,8 +8,9 @@
8
8
  --drawer-backdrop-blur: 4px;
9
9
 
10
10
  /* Sizing */
11
- --drawer-max-width: 500px;
12
11
  --drawer-max-height: 96dvh;
12
+ --drawer-modal-width: fit-content;
13
+ --drawer-modal-height: fit-content;
13
14
 
14
15
  /* Handle */
15
16
  --drawer-handle-bg: hsl(0 0% 80%);
@@ -88,10 +89,10 @@ body:has(.drawer[open]) {
88
89
  --_border-radius: var(--drawer-radius) var(--drawer-radius) 0 0;
89
90
  inset: auto 0 0 0;
90
91
  margin-inline: auto;
91
- width: 100%;
92
- max-width: var(--drawer-max-width);
93
- height: auto;
94
- max-height: var(--drawer-max-height);
92
+ width: var(--drawer-width, 100%);
93
+ max-width: var(--drawer-max-width, none);
94
+ height: var(--drawer-height, auto);
95
+ max-height: var(--drawer-max-height, 96dvh);
95
96
  border-radius: var(--drawer-border-radius, var(--_border-radius));
96
97
  box-shadow: var(--drawer-shadow-bottom);
97
98
  translate: var(--_translate-closed);
@@ -138,9 +139,9 @@ body:has(.drawer[open]) {
138
139
  --_border-radius: var(--drawer-radius) 0 0 var(--drawer-radius);
139
140
  inset: 0 0 0 auto;
140
141
  margin: 0;
141
- width: 100%;
142
- max-width: var(--drawer-max-width);
143
- height: 100dvh;
142
+ width: var(--drawer-width, 500px);
143
+ max-width: var(--drawer-max-width, 90%);
144
+ height: var(--drawer-height, 100dvh);
144
145
  max-height: 100dvh;
145
146
  box-shadow: var(--drawer-shadow-right);
146
147
  }
@@ -151,9 +152,9 @@ body:has(.drawer[open]) {
151
152
  --_border-radius: 0 var(--drawer-radius) var(--drawer-radius) 0;
152
153
  inset: 0 auto 0 0;
153
154
  margin: 0;
154
- width: 100%;
155
- max-width: var(--drawer-max-width);
156
- height: 100dvh;
155
+ width: var(--drawer-width, 500px);
156
+ max-width: var(--drawer-max-width, 90%);
157
+ height: var(--drawer-height, 100dvh);
157
158
  max-height: 100dvh;
158
159
  box-shadow: var(--drawer-shadow-left);
159
160
  }
@@ -164,10 +165,10 @@ body:has(.drawer[open]) {
164
165
  --_border-radius: 0 0 var(--drawer-radius) var(--drawer-radius);
165
166
  inset: 0 0 auto 0;
166
167
  margin-inline: auto;
167
- width: 100%;
168
- max-width: var(--drawer-max-width);
169
- height: auto;
170
- max-height: var(--drawer-max-height);
168
+ width: var(--drawer-width, 100%);
169
+ max-width: var(--drawer-max-width, none);
170
+ height: var(--drawer-height, auto);
171
+ max-height: var(--drawer-max-height, 96dvh);
171
172
  box-shadow: var(--drawer-shadow-top);
172
173
  }
173
174
 
@@ -177,10 +178,10 @@ body:has(.drawer[open]) {
177
178
  --_border-radius: var(--drawer-radius);
178
179
  inset: 0;
179
180
  margin: auto;
180
- width: fit-content;
181
- max-width: var(--drawer-max-width);
182
- height: fit-content;
183
- max-height: var(--drawer-max-height);
181
+ width: var(--drawer-modal-width, fit-content);
182
+ max-width: var(--drawer-max-width, 90%);
183
+ height: var(--drawer-modal-height, fit-content);
184
+ max-height: var(--drawer-max-height, 96dvh);
184
185
  box-shadow: var(--drawer-shadow-modal);
185
186
  scale: var(--drawer-nested-scale);
186
187
  }
File without changes
@@ -1 +0,0 @@
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"}
@@ -1 +0,0 @@
1
- export{};