css-drawer 0.3.1 → 0.3.2

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/dist/index.cjs CHANGED
@@ -1,5 +1,4 @@
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=`
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});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
2
  ${i?`<div class="drawer-handle"></div>`:``}
4
3
  <div class="drawer-content">${n}</div>
5
4
  `,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;
package/dist/index.d.cts CHANGED
@@ -1,5 +1,4 @@
1
1
  //#region src/index.d.ts
2
-
3
2
  type DrawerElement = HTMLDialogElement;
4
3
  type DrawerRef = string | DrawerElement | null | undefined;
5
4
  type DrawerDirection = 'bottom' | 'top' | 'left' | 'right' | 'modal';
@@ -1 +1 @@
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
+ {"version":3,"file":"index.d.cts","names":[],"sources":["../src/index.ts"],"mappings":";KAOY,aAAA,GAAgB,iBAAA;AAAA,KAEhB,SAAA,YAAqB,aAAA;AAAA,KAErB,eAAA;AAAA,UAEK,mBAAA;EAJgB;EAM/B,EAAA;EAJU;EAMV,OAAA;;EAEA,SAAA,GAAY,eAAA;EARa;EAUzB,MAAA;EARkC;EAUlC,SAAA;EAJ2B;EAM3B,mBAAA;AAAA;AAAA,UAGe,mBAAA;EATH;EAWZ,MAAA;EAPA;EASA,OAAA;EAPmB;EASnB,QAAA;AAAA;;;;iBAiBc,IAAA,CAAK,MAAA,EAAQ,SAAA;;;;iBAQb,KAAA,CAAM,MAAA,EAAQ,SAAA;AAR9B;;;AAAA,iBAgBgB,QAAA,CAAA;;AARhB;;iBAgBgB,MAAA,CAAO,MAAA,EAAQ,SAAA;;;AAR/B;iBAgBgB,OAAA,CAAA,GAAW,aAAA;;;;iBAOX,MAAA,CAAA,GAAU,aAAA;;;;iBAQV,MAAA,CAAO,OAAA,GAAS,mBAAA,GAA2B,aAAA;AAf3D;;;AAAA,iBA4CgB,KAAA,CAAM,MAAA,EAAQ,aAAA,GAAgB,aAAA;;AArC9C;;iBA6CgB,OAAA,CAAQ,MAAA,EAAQ,SAAA;;;AArChC;;;iBA+CgB,IAAA,CAAA;;;;;iBAgBA,SAAA,CACd,MAAA,EAAQ,SAAA,EACR,QAAA,EAAU,mBAAA;AApCZ;;;;AAAA,iBAqEgB,KAAA,CAAM,EAAA,UAAY,OAAA;EAAY,mBAAA;AAAA;EAAA;;;wBAM7B,UAAA;AAAA"}
package/dist/index.d.mts CHANGED
@@ -1,5 +1,4 @@
1
1
  //#region src/index.d.ts
2
-
3
2
  type DrawerElement = HTMLDialogElement;
4
3
  type DrawerRef = string | DrawerElement | null | undefined;
5
4
  type DrawerDirection = 'bottom' | 'top' | 'left' | 'right' | 'modal';
@@ -1 +1 @@
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"}
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../src/index.ts"],"mappings":";KAOY,aAAA,GAAgB,iBAAA;AAAA,KAEhB,SAAA,YAAqB,aAAA;AAAA,KAErB,eAAA;AAAA,UAEK,mBAAA;EAJgB;EAM/B,EAAA;EAJU;EAMV,OAAA;;EAEA,SAAA,GAAY,eAAA;EARa;EAUzB,MAAA;EARkC;EAUlC,SAAA;EAJ2B;EAM3B,mBAAA;AAAA;AAAA,UAGe,mBAAA;EATH;EAWZ,MAAA;EAPA;EASA,OAAA;EAPmB;EASnB,QAAA;AAAA;;;;iBAiBc,IAAA,CAAK,MAAA,EAAQ,SAAA;;;;iBAQb,KAAA,CAAM,MAAA,EAAQ,SAAA;AAR9B;;;AAAA,iBAgBgB,QAAA,CAAA;;AARhB;;iBAgBgB,MAAA,CAAO,MAAA,EAAQ,SAAA;;;AAR/B;iBAgBgB,OAAA,CAAA,GAAW,aAAA;;;;iBAOX,MAAA,CAAA,GAAU,aAAA;;;;iBAQV,MAAA,CAAO,OAAA,GAAS,mBAAA,GAA2B,aAAA;AAf3D;;;AAAA,iBA4CgB,KAAA,CAAM,MAAA,EAAQ,aAAA,GAAgB,aAAA;;AArC9C;;iBA6CgB,OAAA,CAAQ,MAAA,EAAQ,SAAA;;;AArChC;;;iBA+CgB,IAAA,CAAA;;;;;iBAgBA,SAAA,CACd,MAAA,EAAQ,SAAA,EACR,QAAA,EAAU,mBAAA;AApCZ;;;;AAAA,iBAqEgB,KAAA,CAAM,EAAA,UAAY,OAAA;EAAY,mBAAA;AAAA;EAAA;;;wBAM7B,UAAA;AAAA"}
package/dist/index.mjs CHANGED
@@ -1,5 +1,4 @@
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=`
1
+ import{t as e}from"./observer-Cam7lP_h.mjs";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
2
  ${i?`<div class="drawer-handle"></div>`:``}
4
3
  <div class="drawer-content">${n}</div>
5
4
  `,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};
@@ -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'\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"}
1
+ {"version":3,"file":"index.mjs","names":[],"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,EACjB,EAAE,WAAW,CAMjB,SAAgB,EAAM,EAAyB,CAClC,EAAQ,EACjB,EAAE,OAAO,CAMb,SAAgB,GAAiB,CACf,MAAM,KAAK,SAAS,iBAAgC,sBAAsB,CACnF,CAAC,SAAS,CAAC,QAAS,GAAM,EAAE,OAAO,CAAC,CAM7C,SAAgB,EAAO,EAA4B,CAEjD,OADW,EAAQ,EACV,EAAE,MAAQ,GAMrB,SAAgB,GAA2B,CACzC,OAAO,MAAM,KAAK,SAAS,iBAAgC,sBAAsB,CAAC,CAMpF,SAAgB,GAA+B,CAC7C,IAAM,EAAO,GAAS,CACtB,OAAO,EAAK,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,EACjB,EAAE,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"}
@@ -1 +1,2 @@
1
+ import './style.css';
1
2
  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}});
@@ -1,2 +1,3 @@
1
+ import './style.css';
1
2
  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
3
+ //# sourceMappingURL=observer-Cam7lP_h.mjs.map
@@ -1 +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"}
1
+ {"version":3,"file":"observer-Cam7lP_h.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,EAmBJ,IAhBqB,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,QAKE,CAAC,QAAQ,SAAS,KAAM,CAC9B,QAAS,GACT,WAAY,GACZ,gBAAiB,CAAC,OAAO,CAC1B,CAAC,CAIJ,GAAoB"}
package/dist/react.cjs CHANGED
@@ -1,2 +1 @@
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;
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});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,5 +1,5 @@
1
- import * as react_jsx_runtime0 from "react/jsx-runtime";
2
- import * as react0 from "react";
1
+ import * as _$react_jsx_runtime0 from "react/jsx-runtime";
2
+ import * as _$react from "react";
3
3
  import { ComponentPropsWithoutRef, ReactNode, RefObject } from "react";
4
4
 
5
5
  //#region src/react.d.ts
@@ -21,7 +21,7 @@ interface RootProps {
21
21
  declare function Root({
22
22
  children,
23
23
  direction
24
- }: RootProps): react_jsx_runtime0.JSX.Element;
24
+ }: RootProps): _$react_jsx_runtime0.JSX.Element;
25
25
  interface ContentProps extends Omit<ComponentPropsWithoutRef<'dialog'>, 'open'> {
26
26
  /** Controlled open state */
27
27
  open?: boolean;
@@ -35,10 +35,10 @@ interface TitleProps extends ComponentPropsWithoutRef<'h2'> {}
35
35
  interface DescriptionProps extends ComponentPropsWithoutRef<'p'> {}
36
36
  declare const Drawer: {
37
37
  Root: typeof Root;
38
- Content: react0.ForwardRefExoticComponent<ContentProps & react0.RefAttributes<HTMLDialogElement>>;
39
- Handle: react0.ForwardRefExoticComponent<HandleProps & react0.RefAttributes<HTMLDivElement>>;
40
- Title: react0.ForwardRefExoticComponent<TitleProps & react0.RefAttributes<HTMLHeadingElement>>;
41
- Description: react0.ForwardRefExoticComponent<DescriptionProps & react0.RefAttributes<HTMLParagraphElement>>;
38
+ Content: _$react.ForwardRefExoticComponent<ContentProps & _$react.RefAttributes<HTMLDialogElement>>;
39
+ Handle: _$react.ForwardRefExoticComponent<HandleProps & _$react.RefAttributes<HTMLDivElement>>;
40
+ Title: _$react.ForwardRefExoticComponent<TitleProps & _$react.RefAttributes<HTMLHeadingElement>>;
41
+ Description: _$react.ForwardRefExoticComponent<DescriptionProps & _$react.RefAttributes<HTMLParagraphElement>>;
42
42
  };
43
43
  //#endregion
44
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 };
@@ -1 +1 @@
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"}
1
+ {"version":3,"file":"react.d.cts","names":[],"sources":["../src/react.tsx"],"mappings":";;;;;KAeK,SAAA;;;AAJgB;iBA8BL,YAAA,CAAA,GAAgB,iBAAA;;;;AAAhC;iBASgB,cAAA,CAAe,GAAA,EAAK,SAAA,CAAU,iBAAA;AAAA,UAqBpC,SAAA;EACR,QAAA,EAAU,SAAA;EA/BqC;EAiC/C,SAAA,GAAY,SAAA;AAAA;AAAA,iBAGL,IAAA,CAAA;EAAO,QAAA;EAAU;AAAA,GAAa,SAAA,GAAS,oBAAA,CAAA,GAAA,CAAA,OAAA;AAAA,UAStC,YAAA,SAAqB,IAAA,CAAK,wBAAA;EApCA;EAsClC,IAAA;EAtC6B;EAwC7B,YAAA,IAAgB,IAAA;EAxCqD;EA0CrE,mBAAA;AAAA;AAAA,UAmDQ,WAAA,SAAoB,wBAAA;AAAA,UAapB,UAAA,SAAmB,wBAAA;AAAA,UAQnB,gBAAA,SAAyB,wBAAA;AAAA,cAQtB,MAAA"}
package/dist/react.d.mts CHANGED
@@ -1,6 +1,6 @@
1
- import * as react0 from "react";
1
+ import * as _$react from "react";
2
2
  import { ComponentPropsWithoutRef, ReactNode, RefObject } from "react";
3
- import * as react_jsx_runtime0 from "react/jsx-runtime";
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';
@@ -21,7 +21,7 @@ interface RootProps {
21
21
  declare function Root({
22
22
  children,
23
23
  direction
24
- }: RootProps): react_jsx_runtime0.JSX.Element;
24
+ }: RootProps): _$react_jsx_runtime0.JSX.Element;
25
25
  interface ContentProps extends Omit<ComponentPropsWithoutRef<'dialog'>, 'open'> {
26
26
  /** Controlled open state */
27
27
  open?: boolean;
@@ -35,10 +35,10 @@ interface TitleProps extends ComponentPropsWithoutRef<'h2'> {}
35
35
  interface DescriptionProps extends ComponentPropsWithoutRef<'p'> {}
36
36
  declare const Drawer: {
37
37
  Root: typeof Root;
38
- Content: react0.ForwardRefExoticComponent<ContentProps & react0.RefAttributes<HTMLDialogElement>>;
39
- Handle: react0.ForwardRefExoticComponent<HandleProps & react0.RefAttributes<HTMLDivElement>>;
40
- Title: react0.ForwardRefExoticComponent<TitleProps & react0.RefAttributes<HTMLHeadingElement>>;
41
- Description: react0.ForwardRefExoticComponent<DescriptionProps & react0.RefAttributes<HTMLParagraphElement>>;
38
+ Content: _$react.ForwardRefExoticComponent<ContentProps & _$react.RefAttributes<HTMLDialogElement>>;
39
+ Handle: _$react.ForwardRefExoticComponent<HandleProps & _$react.RefAttributes<HTMLDivElement>>;
40
+ Title: _$react.ForwardRefExoticComponent<TitleProps & _$react.RefAttributes<HTMLHeadingElement>>;
41
+ Description: _$react.ForwardRefExoticComponent<DescriptionProps & _$react.RefAttributes<HTMLParagraphElement>>;
42
42
  };
43
43
  //#endregion
44
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 };
@@ -1 +1 @@
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"}
1
+ {"version":3,"file":"react.d.mts","names":[],"sources":["../src/react.tsx"],"mappings":";;;;;KAeK,SAAA;;;AAJgB;iBA8BL,YAAA,CAAA,GAAgB,iBAAA;;;;AAAhC;iBASgB,cAAA,CAAe,GAAA,EAAK,SAAA,CAAU,iBAAA;AAAA,UAqBpC,SAAA;EACR,QAAA,EAAU,SAAA;EA/BqC;EAiC/C,SAAA,GAAY,SAAA;AAAA;AAAA,iBAGL,IAAA,CAAA;EAAO,QAAA;EAAU;AAAA,GAAa,SAAA,GAAS,oBAAA,CAAA,GAAA,CAAA,OAAA;AAAA,UAStC,YAAA,SAAqB,IAAA,CAAK,wBAAA;EApCA;EAsClC,IAAA;EAtC6B;EAwC7B,YAAA,IAAgB,IAAA;EAxCqD;EA0CrE,mBAAA;AAAA;AAAA,UAmDQ,WAAA,SAAoB,wBAAA;AAAA,UAapB,UAAA,SAAmB,wBAAA;AAAA,UAQnB,gBAAA,SAAyB,wBAAA;AAAA,cAQtB,MAAA"}
package/dist/react.mjs CHANGED
@@ -1,3 +1,2 @@
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};
1
+ import{t as e}from"./observer-Cam7lP_h.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";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
2
  //# 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 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"}
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,SAAf,CAAwB,MAAO,CAAE,YAAW,CACzC,WACsB,CAAA,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,SAAD,CACE,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,WACM,CAAA,EAGd,CACD,EAAQ,YAAc,iBAKtB,MAAM,EAAS,GAAyC,CAAE,YAAW,GAAG,GAAS,IAC/E,EAAC,MAAD,CACO,MACL,UAAW,iBAAiB,GAAa,KAAK,MAAM,CACpD,cAAY,OACZ,GAAI,EACJ,CAAA,CACF,CACF,EAAO,YAAc,gBAKrB,MAAM,EAAQ,GAA4C,EAAO,IAC/D,EAAC,KAAD,CAAS,MAAK,GAAI,EAAS,CAAA,CAC3B,CACF,EAAM,YAAc,eAKpB,MAAM,EAAc,GAAoD,EAAO,IAC7E,EAAC,IAAD,CAAQ,MAAK,GAAI,EAAS,CAAA,CAC1B,CACF,EAAY,YAAc,qBAG1B,MAAa,EAAS,CACpB,OACA,UACA,SACA,QACA,cACD"}
@@ -1,94 +1,70 @@
1
- /* CSS Drawer - Vaul-quality drawer with auto-nesting and directions */
2
-
3
1
  :root {
4
- /* Visual */
5
2
  --drawer-bg: #fff;
6
3
  --drawer-radius: 24px;
7
- --drawer-backdrop: hsl(0 0% 0% / 0.4);
4
+ --drawer-backdrop: #0006;
8
5
  --drawer-backdrop-blur: 4px;
9
-
10
- /* Sizing */
11
6
  --drawer-max-height: 96dvh;
12
7
  --drawer-modal-width: fit-content;
13
8
  --drawer-modal-height: fit-content;
14
-
15
- /* Handle */
16
- --drawer-handle-bg: hsl(0 0% 80%);
17
- --drawer-handle-bg-hover: hsl(0 0% 60%);
9
+ --drawer-handle-bg: #ccc;
10
+ --drawer-handle-bg-hover: #999;
18
11
  --drawer-handle-width: 48px;
19
12
  --drawer-handle-width-hover: 56px;
20
13
  --drawer-handle-height: 5px;
21
- --drawer-handle-padding-block: 1rem 0.5rem;
14
+ --drawer-handle-padding-block: 1rem .5rem;
22
15
  --drawer-handle-padding-inline: 0;
23
-
24
- /* Shadows */
25
- --drawer-shadow-bottom: 0 -10px 60px hsl(0 0% 0% / 0.12), 0 -4px 20px hsl(0 0% 0% / 0.08);
26
- --drawer-shadow-top: 0 10px 60px hsl(0 0% 0% / 0.12), 0 4px 20px hsl(0 0% 0% / 0.08);
27
- --drawer-shadow-right: -10px 0 60px hsl(0 0% 0% / 0.12), -4px 0 20px hsl(0 0% 0% / 0.08);
28
- --drawer-shadow-left: 10px 0 60px hsl(0 0% 0% / 0.12), 4px 0 20px hsl(0 0% 0% / 0.08);
29
- --drawer-shadow-modal: 0 25px 50px -12px hsl(0 0% 0% / 0.25);
30
-
31
- /* Animation */
32
- --drawer-duration: 0.5s;
33
- --drawer-duration-close: 0.35s;
34
- --drawer-ease: cubic-bezier(0.32, 0.72, 0, 1);
35
-
36
- /* Nesting effects */
37
- --drawer-nested-scale: 0.94;
16
+ --drawer-shadow-bottom: 0 -10px 60px #0000001f, 0 -4px 20px #00000014;
17
+ --drawer-shadow-top: 0 10px 60px #0000001f, 0 4px 20px #00000014;
18
+ --drawer-shadow-right: -10px 0 60px #0000001f, -4px 0 20px #00000014;
19
+ --drawer-shadow-left: 10px 0 60px #0000001f, 4px 0 20px #00000014;
20
+ --drawer-shadow-modal: 0 25px 50px -12px #00000040;
21
+ --drawer-duration: .5s;
22
+ --drawer-duration-close: .35s;
23
+ --drawer-ease: cubic-bezier(.32, .72, 0, 1);
24
+ --drawer-nested-scale: .94;
38
25
  --drawer-nested-offset: 20px;
39
- --drawer-nested-brightness: 0.92;
40
- --drawer-nested-backdrop: hsl(0 0% 0% / 0.15);
26
+ --drawer-nested-brightness: .92;
27
+ --drawer-nested-backdrop: #00000026;
41
28
  }
42
29
 
43
30
  @media (prefers-color-scheme: dark) {
44
31
  :root {
45
- --drawer-bg: hsl(0 0% 12%);
46
- --drawer-handle-bg: hsl(0 0% 35%);
47
- --drawer-handle-bg-hover: hsl(0 0% 50%);
48
- --drawer-shadow-bottom: 0 -10px 60px hsl(0 0% 0% / 0.4), 0 -4px 20px hsl(0 0% 0% / 0.3);
49
- --drawer-shadow-top: 0 10px 60px hsl(0 0% 0% / 0.4), 0 4px 20px hsl(0 0% 0% / 0.3);
50
- --drawer-shadow-right: -10px 0 60px hsl(0 0% 0% / 0.4), -4px 0 20px hsl(0 0% 0% / 0.3);
51
- --drawer-shadow-left: 10px 0 60px hsl(0 0% 0% / 0.4), 4px 0 20px hsl(0 0% 0% / 0.3);
52
- --drawer-shadow-modal: 0 25px 50px -12px hsl(0 0% 0% / 0.5);
32
+ --drawer-bg: #1f1f1f;
33
+ --drawer-handle-bg: #595959;
34
+ --drawer-handle-bg-hover: gray;
35
+ --drawer-shadow-bottom: 0 -10px 60px #0006, 0 -4px 20px #0000004d;
36
+ --drawer-shadow-top: 0 10px 60px #0006, 0 4px 20px #0000004d;
37
+ --drawer-shadow-right: -10px 0 60px #0006, -4px 0 20px #0000004d;
38
+ --drawer-shadow-left: 10px 0 60px #0006, 4px 0 20px #0000004d;
39
+ --drawer-shadow-modal: 0 25px 50px -12px #00000080;
53
40
  }
54
41
  }
55
42
 
56
- /* Background scale effect */
57
43
  body {
58
44
  transition: scale var(--drawer-duration) var(--drawer-ease), border-radius var(--drawer-duration) var(--drawer-ease);
59
- transform-origin: center top;
45
+ transform-origin: top;
60
46
  }
61
47
 
62
48
  body:has(.drawer[open]) {
63
- overflow: hidden;
64
49
  scale: var(--drawer-nested-scale);
65
50
  border-radius: var(--drawer-radius);
51
+ overflow: hidden;
66
52
  }
67
53
 
68
- /* Base drawer */
69
54
  .drawer {
70
- border: none;
71
- padding: 0;
72
- margin: 0;
73
- max-width: 100%;
74
- max-height: 100%;
75
- position: fixed;
76
55
  background: var(--drawer-bg);
77
- overflow: hidden;
78
56
  opacity: 0;
79
- transition:
80
- display var(--drawer-duration-close) allow-discrete,
57
+ max-width: 100%;
58
+ max-height: 100%;
59
+ transition: display var(--drawer-duration-close) allow-discrete,
81
60
  overlay var(--drawer-duration-close) allow-discrete,
82
61
  translate var(--drawer-duration-close) var(--drawer-ease),
83
62
  scale var(--drawer-duration-close) var(--drawer-ease),
84
63
  filter var(--drawer-duration-close) ease,
85
64
  opacity var(--drawer-duration-close) ease;
86
-
87
- /* Default: bottom */
88
65
  --_translate-closed: 0 100%;
89
66
  --_border-radius: var(--drawer-radius) var(--drawer-radius) 0 0;
90
- inset: auto 0 0 0;
91
- margin-inline: auto;
67
+ margin: 0;
92
68
  width: var(--drawer-width, 100%);
93
69
  max-width: var(--drawer-max-width, none);
94
70
  height: var(--drawer-height, auto);
@@ -96,94 +72,97 @@ body:has(.drawer[open]) {
96
72
  border-radius: var(--drawer-border-radius, var(--_border-radius));
97
73
  box-shadow: var(--drawer-shadow-bottom);
98
74
  translate: var(--_translate-closed);
75
+ border: none;
76
+ margin-inline: auto;
77
+ padding: 0;
78
+ position: fixed;
79
+ inset: auto 0 0;
80
+ overflow: hidden;
99
81
  }
100
82
 
101
83
  .drawer::backdrop {
102
84
  background: var(--drawer-backdrop);
103
85
  opacity: 0;
104
- backdrop-filter: blur(var(--drawer-backdrop-blur));
105
86
  -webkit-backdrop-filter: blur(var(--drawer-backdrop-blur));
106
- transition:
107
- display var(--drawer-duration-close) allow-discrete,
87
+ transition: display var(--drawer-duration-close) allow-discrete,
108
88
  overlay var(--drawer-duration-close) allow-discrete,
109
89
  opacity var(--drawer-duration-close) ease;
110
90
  }
111
91
 
112
92
  .drawer[open] {
113
93
  opacity: 1;
114
- translate: 0 0;
115
- transition:
116
- display var(--drawer-duration) allow-discrete,
94
+ transition: display var(--drawer-duration) allow-discrete,
117
95
  overlay var(--drawer-duration) allow-discrete,
118
96
  translate var(--drawer-duration) var(--drawer-ease),
119
97
  scale var(--drawer-duration) var(--drawer-ease),
120
98
  filter var(--drawer-duration) ease,
121
- opacity 0.15s ease;
99
+ opacity .15s ease;
100
+ translate: 0;
122
101
  }
123
102
 
124
103
  .drawer[open]::backdrop {
125
104
  opacity: 1;
126
- transition: display var(--drawer-duration) allow-discrete, overlay var(--drawer-duration) allow-discrete, opacity 0.2s ease;
105
+ transition: display var(--drawer-duration) allow-discrete, overlay var(--drawer-duration) allow-discrete, opacity .2s ease;
127
106
  }
128
107
 
129
108
  @starting-style {
130
- .drawer[open] { opacity: 0; translate: var(--_translate-closed); }
131
- .drawer[open]::backdrop { opacity: 0; }
132
- }
109
+ .drawer[open] {
110
+ opacity: 0;
111
+ translate: var(--_translate-closed);
112
+ }
133
113
 
134
- /* ===== DIRECTIONS ===== */
114
+ .drawer[open]::backdrop {
115
+ opacity: 0;
116
+ }
117
+ }
135
118
 
136
- /* Right */
137
119
  .drawer[data-direction="right"] {
138
120
  --_translate-closed: 100% 0;
139
121
  --_border-radius: var(--drawer-radius) 0 0 var(--drawer-radius);
140
- inset: 0 0 0 auto;
141
- margin: 0;
142
122
  width: var(--drawer-width, 500px);
143
123
  max-width: var(--drawer-max-width, 90%);
144
124
  height: var(--drawer-height, 100dvh);
145
125
  max-height: 100dvh;
146
126
  box-shadow: var(--drawer-shadow-right);
127
+ margin: 0;
128
+ inset: 0 0 0 auto;
147
129
  }
148
130
 
149
- /* Left */
150
131
  .drawer[data-direction="left"] {
151
132
  --_translate-closed: -100% 0;
152
133
  --_border-radius: 0 var(--drawer-radius) var(--drawer-radius) 0;
153
- inset: 0 auto 0 0;
154
- margin: 0;
155
134
  width: var(--drawer-width, 500px);
156
135
  max-width: var(--drawer-max-width, 90%);
157
136
  height: var(--drawer-height, 100dvh);
158
137
  max-height: 100dvh;
159
138
  box-shadow: var(--drawer-shadow-left);
139
+ margin: 0;
140
+ inset: 0 auto 0 0;
160
141
  }
161
142
 
162
- /* Top */
163
143
  .drawer[data-direction="top"] {
164
144
  --_translate-closed: 0 -100%;
165
145
  --_border-radius: 0 0 var(--drawer-radius) var(--drawer-radius);
166
- inset: 0 0 auto 0;
167
- margin-inline: auto;
168
146
  width: var(--drawer-width, 100%);
169
147
  max-width: var(--drawer-max-width, none);
170
148
  height: var(--drawer-height, auto);
171
149
  max-height: var(--drawer-max-height, 96dvh);
172
150
  box-shadow: var(--drawer-shadow-top);
151
+ margin-inline: auto;
152
+ inset: 0 0 auto;
173
153
  }
174
154
 
175
- /* Modal (centered) */
176
155
  .drawer[data-direction="modal"] {
177
156
  --_translate-closed: 0 0;
178
157
  --_border-radius: var(--drawer-radius);
179
- inset: 0;
180
- margin: auto;
181
158
  width: var(--drawer-modal-width, fit-content);
182
159
  max-width: var(--drawer-max-width, 90%);
183
160
  height: var(--drawer-modal-height, fit-content);
184
161
  max-height: var(--drawer-max-height, 96dvh);
185
162
  box-shadow: var(--drawer-shadow-modal);
186
163
  scale: var(--drawer-nested-scale);
164
+ margin: auto;
165
+ inset: 0;
187
166
  }
188
167
 
189
168
  .drawer[data-direction="modal"][open] {
@@ -196,11 +175,7 @@ body:has(.drawer[open]) {
196
175
  }
197
176
  }
198
177
 
199
- /* ===== AUTO-NESTING (up to 5 levels) ===== */
200
- /* Uses sibling combinators to count open drawers */
201
-
202
- /* 1+ open drawers after */
203
- .drawer[open]:has(~ .drawer[open]) {
178
+ .drawer[open]:has( ~ .drawer[open]) {
204
179
  scale: var(--drawer-nested-scale);
205
180
  translate: 0 calc(-1 * var(--drawer-nested-offset));
206
181
  border-radius: var(--drawer-radius);
@@ -208,125 +183,110 @@ body:has(.drawer[open]) {
208
183
  pointer-events: none;
209
184
  }
210
185
 
211
- /* 2+ open drawers after */
212
- .drawer[open]:has(~ .drawer[open] ~ .drawer[open]) {
186
+ .drawer[open]:has( ~ .drawer[open] ~ .drawer[open]) {
213
187
  scale: calc(var(--drawer-nested-scale) * var(--drawer-nested-scale));
214
188
  translate: 0 calc(-2 * var(--drawer-nested-offset));
215
189
  filter: brightness(calc(var(--drawer-nested-brightness) * var(--drawer-nested-brightness)));
216
190
  }
217
191
 
218
- /* 3+ open drawers after */
219
- .drawer[open]:has(~ .drawer[open] ~ .drawer[open] ~ .drawer[open]) {
192
+ .drawer[open]:has( ~ .drawer[open] ~ .drawer[open] ~ .drawer[open]) {
220
193
  scale: calc(var(--drawer-nested-scale) * var(--drawer-nested-scale) * var(--drawer-nested-scale));
221
194
  translate: 0 calc(-3 * var(--drawer-nested-offset));
222
195
  filter: brightness(calc(var(--drawer-nested-brightness) * var(--drawer-nested-brightness) * var(--drawer-nested-brightness)));
223
196
  }
224
197
 
225
- /* 4+ open drawers after */
226
- .drawer[open]:has(~ .drawer[open] ~ .drawer[open] ~ .drawer[open] ~ .drawer[open]) {
198
+ .drawer[open]:has( ~ .drawer[open] ~ .drawer[open] ~ .drawer[open] ~ .drawer[open]) {
227
199
  scale: calc(var(--drawer-nested-scale) * var(--drawer-nested-scale) * var(--drawer-nested-scale) * var(--drawer-nested-scale));
228
200
  translate: 0 calc(-4 * var(--drawer-nested-offset));
229
201
  filter: brightness(calc(var(--drawer-nested-brightness) * var(--drawer-nested-brightness) * var(--drawer-nested-brightness) * var(--drawer-nested-brightness)));
230
202
  }
231
203
 
232
- /* 5+ open drawers after */
233
- .drawer[open]:has(~ .drawer[open] ~ .drawer[open] ~ .drawer[open] ~ .drawer[open] ~ .drawer[open]) {
204
+ .drawer[open]:has( ~ .drawer[open] ~ .drawer[open] ~ .drawer[open] ~ .drawer[open] ~ .drawer[open]) {
234
205
  scale: calc(var(--drawer-nested-scale) * var(--drawer-nested-scale) * var(--drawer-nested-scale) * var(--drawer-nested-scale) * var(--drawer-nested-scale));
235
206
  translate: 0 calc(-5 * var(--drawer-nested-offset));
236
207
  filter: brightness(calc(var(--drawer-nested-brightness) * var(--drawer-nested-brightness) * var(--drawer-nested-brightness) * var(--drawer-nested-brightness) * var(--drawer-nested-brightness)));
237
208
  }
238
209
 
239
- /* Lighter backdrop for stacked drawers */
240
210
  .drawer[open] ~ .drawer[open]::backdrop {
241
211
  background: var(--drawer-nested-backdrop);
242
212
  backdrop-filter: none;
243
213
  }
244
214
 
245
- /* Handle */
246
215
  .drawer-handle {
247
- display: flex;
248
- justify-content: center;
249
216
  padding-block: var(--drawer-handle-padding-block);
250
217
  padding-inline: var(--drawer-handle-padding-inline);
251
218
  cursor: grab;
219
+ justify-content: center;
220
+ display: flex;
252
221
  }
253
222
 
254
- .drawer-handle::before {
255
- content: '';
223
+ .drawer-handle:before {
224
+ content: "";
256
225
  width: var(--drawer-handle-width);
257
226
  height: var(--drawer-handle-height);
258
227
  background: var(--drawer-handle-bg);
228
+ transition: width .2s var(--drawer-ease), height .2s var(--drawer-ease), background .2s ease;
259
229
  border-radius: 100px;
260
- transition: width 0.2s var(--drawer-ease), height 0.2s var(--drawer-ease), background 0.2s ease;
261
230
  }
262
231
 
263
- .drawer-handle:hover::before {
232
+ .drawer-handle:hover:before {
264
233
  width: var(--drawer-handle-width-hover);
265
234
  background: var(--drawer-handle-bg-hover);
266
235
  }
267
236
 
268
- /* Vertical handle for left/right drawers */
269
- .drawer[data-direction="left"] .drawer-handle,
270
- .drawer[data-direction="right"] .drawer-handle {
271
- flex-direction: column;
272
- align-items: center;
273
- justify-content: center;
237
+ .drawer[data-direction="left"] .drawer-handle, .drawer[data-direction="right"] .drawer-handle {
274
238
  padding-block: var(--drawer-handle-padding-inline);
275
239
  padding-inline: var(--drawer-handle-padding-block);
240
+ writing-mode: vertical-lr;
241
+ flex-direction: column;
242
+ justify-content: center;
243
+ align-items: center;
276
244
  height: 100%;
277
245
  position: absolute;
278
246
  top: 0;
279
- writing-mode: vertical-lr;
280
247
  }
281
248
 
282
- .drawer[data-direction="left"] .drawer-handle { right: 0; }
283
- .drawer[data-direction="right"] .drawer-handle { left: 0; }
249
+ .drawer[data-direction="left"] .drawer-handle {
250
+ right: 0;
251
+ }
252
+
253
+ .drawer[data-direction="right"] .drawer-handle {
254
+ left: 0;
255
+ }
284
256
 
285
- .drawer[data-direction="left"] .drawer-handle::before,
286
- .drawer[data-direction="right"] .drawer-handle::before {
257
+ .drawer[data-direction="left"] .drawer-handle:before, .drawer[data-direction="right"] .drawer-handle:before {
287
258
  width: var(--drawer-handle-height);
288
259
  height: var(--drawer-handle-width);
289
260
  }
290
261
 
291
- .drawer[data-direction="left"] .drawer-handle:hover::before,
292
- .drawer[data-direction="right"] .drawer-handle:hover::before {
262
+ .drawer[data-direction="left"] .drawer-handle:hover:before, .drawer[data-direction="right"] .drawer-handle:hover:before {
293
263
  width: var(--drawer-handle-height);
294
264
  height: var(--drawer-handle-width-hover);
295
265
  }
296
266
 
297
- /* Content - structural only, no opinionated padding */
298
267
  .drawer-content {
299
- overflow-x: hidden;
300
- overflow-y: auto;
301
268
  overscroll-behavior: contain;
302
269
  flex: 1;
303
270
  min-height: 0;
271
+ overflow: hidden auto;
304
272
  }
305
273
 
306
- /* Content sizing for directions */
307
- .drawer[data-direction="left"] .drawer-content,
308
- .drawer[data-direction="right"] .drawer-content {
274
+ .drawer[data-direction="left"] .drawer-content, .drawer[data-direction="right"] .drawer-content {
309
275
  height: 100%;
310
276
  }
311
277
 
312
- /* Reduced motion */
313
278
  @media (prefers-reduced-motion: reduce) {
314
- *, *::before, *::after {
315
- transition-duration: 0.01ms !important;
279
+ *, :before, :after {
280
+ transition-duration: .01ms !important;
316
281
  }
317
282
 
318
283
  body:has(.drawer[open]) {
319
284
  scale: 1;
320
285
  }
321
286
 
322
- .drawer[open]:has(~ .drawer[open]),
323
- .drawer[open]:has(~ .drawer[open] ~ .drawer[open]),
324
- .drawer[open]:has(~ .drawer[open] ~ .drawer[open] ~ .drawer[open]),
325
- .drawer[open]:has(~ .drawer[open] ~ .drawer[open] ~ .drawer[open] ~ .drawer[open]),
326
- .drawer[open]:has(~ .drawer[open] ~ .drawer[open] ~ .drawer[open] ~ .drawer[open] ~ .drawer[open]) {
327
- scale: 1;
328
- translate: 0 0;
287
+ .drawer[open]:has( ~ .drawer[open]), .drawer[open]:has( ~ .drawer[open] ~ .drawer[open]), .drawer[open]:has( ~ .drawer[open] ~ .drawer[open] ~ .drawer[open]), .drawer[open]:has( ~ .drawer[open] ~ .drawer[open] ~ .drawer[open] ~ .drawer[open]), .drawer[open]:has( ~ .drawer[open] ~ .drawer[open] ~ .drawer[open] ~ .drawer[open] ~ .drawer[open]) {
329
288
  filter: none;
289
+ translate: 0;
290
+ scale: 1;
330
291
  }
331
292
  }
332
-
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "css-drawer",
3
- "version": "0.3.1",
3
+ "version": "0.3.2",
4
4
  "description": "Vaul-quality drawer component using native <dialog> and pure CSS animations",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
@@ -47,12 +47,12 @@
47
47
  }
48
48
  },
49
49
  "devDependencies": {
50
- "@bosh-code/tsdown-plugin-inject-css": "^2.0.0",
51
- "@types/react": "^19.2.9",
50
+ "@tsdown/css": "^0.21.10",
51
+ "@types/react": "^19.2.14",
52
52
  "@types/react-dom": "^19.2.3",
53
- "react": "^19.2.3",
54
- "tsdown": "^0.18.4",
55
- "typescript": "^5.9.3"
53
+ "react": "^19.2.5",
54
+ "tsdown": "^0.21.10",
55
+ "typescript": "^6.0.3"
56
56
  },
57
57
  "keywords": [
58
58
  "drawer",
@@ -1,334 +0,0 @@
1
- /* CSS Drawer - Vaul-quality drawer with auto-nesting and directions */
2
-
3
- :root {
4
- /* Visual */
5
- --drawer-bg: #fff;
6
- --drawer-radius: 24px;
7
- --drawer-backdrop: hsl(0 0% 0% / 0.4);
8
- --drawer-backdrop-blur: 4px;
9
-
10
- /* Sizing */
11
- --drawer-max-height: 96dvh;
12
- --drawer-modal-width: fit-content;
13
- --drawer-modal-height: fit-content;
14
-
15
- /* Handle */
16
- --drawer-handle-bg: hsl(0 0% 80%);
17
- --drawer-handle-bg-hover: hsl(0 0% 60%);
18
- --drawer-handle-width: 48px;
19
- --drawer-handle-width-hover: 56px;
20
- --drawer-handle-height: 5px;
21
- --drawer-handle-padding-block: 1rem 0.5rem;
22
- --drawer-handle-padding-inline: 0;
23
-
24
- /* Shadows */
25
- --drawer-shadow-bottom: 0 -10px 60px hsl(0 0% 0% / 0.12), 0 -4px 20px hsl(0 0% 0% / 0.08);
26
- --drawer-shadow-top: 0 10px 60px hsl(0 0% 0% / 0.12), 0 4px 20px hsl(0 0% 0% / 0.08);
27
- --drawer-shadow-right: -10px 0 60px hsl(0 0% 0% / 0.12), -4px 0 20px hsl(0 0% 0% / 0.08);
28
- --drawer-shadow-left: 10px 0 60px hsl(0 0% 0% / 0.12), 4px 0 20px hsl(0 0% 0% / 0.08);
29
- --drawer-shadow-modal: 0 25px 50px -12px hsl(0 0% 0% / 0.25);
30
-
31
- /* Animation */
32
- --drawer-duration: 0.5s;
33
- --drawer-duration-close: 0.35s;
34
- --drawer-ease: cubic-bezier(0.32, 0.72, 0, 1);
35
-
36
- /* Nesting effects */
37
- --drawer-nested-scale: 0.94;
38
- --drawer-nested-offset: 20px;
39
- --drawer-nested-brightness: 0.92;
40
- --drawer-nested-backdrop: hsl(0 0% 0% / 0.15);
41
- }
42
-
43
- @media (prefers-color-scheme: dark) {
44
- :root {
45
- --drawer-bg: hsl(0 0% 12%);
46
- --drawer-handle-bg: hsl(0 0% 35%);
47
- --drawer-handle-bg-hover: hsl(0 0% 50%);
48
- --drawer-shadow-bottom: 0 -10px 60px hsl(0 0% 0% / 0.4), 0 -4px 20px hsl(0 0% 0% / 0.3);
49
- --drawer-shadow-top: 0 10px 60px hsl(0 0% 0% / 0.4), 0 4px 20px hsl(0 0% 0% / 0.3);
50
- --drawer-shadow-right: -10px 0 60px hsl(0 0% 0% / 0.4), -4px 0 20px hsl(0 0% 0% / 0.3);
51
- --drawer-shadow-left: 10px 0 60px hsl(0 0% 0% / 0.4), 4px 0 20px hsl(0 0% 0% / 0.3);
52
- --drawer-shadow-modal: 0 25px 50px -12px hsl(0 0% 0% / 0.5);
53
- }
54
- }
55
-
56
- /* Background scale effect */
57
- body {
58
- transition: scale var(--drawer-duration) var(--drawer-ease), border-radius var(--drawer-duration) var(--drawer-ease);
59
- transform-origin: center top;
60
- }
61
-
62
- body:has(.drawer[open]) {
63
- overflow: hidden;
64
- scale: var(--drawer-nested-scale);
65
- border-radius: var(--drawer-radius);
66
- }
67
-
68
- /* Base drawer */
69
- .drawer {
70
- border: none;
71
- padding: 0;
72
- margin: 0;
73
- max-width: 100%;
74
- max-height: 100%;
75
- position: fixed;
76
- background: var(--drawer-bg);
77
- overflow: hidden;
78
- opacity: 0;
79
- transition:
80
- display var(--drawer-duration-close) allow-discrete,
81
- overlay var(--drawer-duration-close) allow-discrete,
82
- translate var(--drawer-duration-close) var(--drawer-ease),
83
- scale var(--drawer-duration-close) var(--drawer-ease),
84
- filter var(--drawer-duration-close) ease,
85
- opacity var(--drawer-duration-close) ease;
86
-
87
- /* Default: bottom */
88
- --_translate-closed: 0 100%;
89
- --_border-radius: var(--drawer-radius) var(--drawer-radius) 0 0;
90
- inset: auto 0 0 0;
91
- margin-inline: auto;
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);
96
- border-radius: var(--drawer-border-radius, var(--_border-radius));
97
- box-shadow: var(--drawer-shadow-bottom);
98
- translate: var(--_translate-closed);
99
- }
100
-
101
- .drawer::backdrop {
102
- background: var(--drawer-backdrop);
103
- opacity: 0;
104
- backdrop-filter: blur(var(--drawer-backdrop-blur));
105
- -webkit-backdrop-filter: blur(var(--drawer-backdrop-blur));
106
- transition:
107
- display var(--drawer-duration-close) allow-discrete,
108
- overlay var(--drawer-duration-close) allow-discrete,
109
- opacity var(--drawer-duration-close) ease;
110
- }
111
-
112
- .drawer[open] {
113
- opacity: 1;
114
- translate: 0 0;
115
- transition:
116
- display var(--drawer-duration) allow-discrete,
117
- overlay var(--drawer-duration) allow-discrete,
118
- translate var(--drawer-duration) var(--drawer-ease),
119
- scale var(--drawer-duration) var(--drawer-ease),
120
- filter var(--drawer-duration) ease,
121
- opacity 0.15s ease;
122
- }
123
-
124
- .drawer[open]::backdrop {
125
- opacity: 1;
126
- transition: display var(--drawer-duration) allow-discrete, overlay var(--drawer-duration) allow-discrete, opacity 0.2s ease;
127
- }
128
-
129
- @starting-style {
130
- .drawer[open] { opacity: 0; translate: var(--_translate-closed); }
131
- .drawer[open]::backdrop { opacity: 0; }
132
- }
133
-
134
- /* ===== DIRECTIONS ===== */
135
-
136
- /* Right */
137
- .drawer[data-direction="right"] {
138
- --_translate-closed: 100% 0;
139
- --_border-radius: var(--drawer-radius) 0 0 var(--drawer-radius);
140
- inset: 0 0 0 auto;
141
- margin: 0;
142
- width: var(--drawer-width, 500px);
143
- max-width: var(--drawer-max-width, 90%);
144
- height: var(--drawer-height, 100dvh);
145
- max-height: 100dvh;
146
- box-shadow: var(--drawer-shadow-right);
147
- }
148
-
149
- /* Left */
150
- .drawer[data-direction="left"] {
151
- --_translate-closed: -100% 0;
152
- --_border-radius: 0 var(--drawer-radius) var(--drawer-radius) 0;
153
- inset: 0 auto 0 0;
154
- margin: 0;
155
- width: var(--drawer-width, 500px);
156
- max-width: var(--drawer-max-width, 90%);
157
- height: var(--drawer-height, 100dvh);
158
- max-height: 100dvh;
159
- box-shadow: var(--drawer-shadow-left);
160
- }
161
-
162
- /* Top */
163
- .drawer[data-direction="top"] {
164
- --_translate-closed: 0 -100%;
165
- --_border-radius: 0 0 var(--drawer-radius) var(--drawer-radius);
166
- inset: 0 0 auto 0;
167
- margin-inline: auto;
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);
172
- box-shadow: var(--drawer-shadow-top);
173
- }
174
-
175
- /* Modal (centered) */
176
- .drawer[data-direction="modal"] {
177
- --_translate-closed: 0 0;
178
- --_border-radius: var(--drawer-radius);
179
- inset: 0;
180
- margin: auto;
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);
185
- box-shadow: var(--drawer-shadow-modal);
186
- scale: var(--drawer-nested-scale);
187
- }
188
-
189
- .drawer[data-direction="modal"][open] {
190
- scale: 1;
191
- }
192
-
193
- @starting-style {
194
- .drawer[data-direction="modal"][open] {
195
- scale: var(--drawer-nested-scale);
196
- }
197
- }
198
-
199
- /* ===== AUTO-NESTING (up to 5 levels) ===== */
200
- /* Uses sibling combinators to count open drawers */
201
-
202
- /* 1+ open drawers after */
203
- .drawer[open]:has(~ .drawer[open]) {
204
- scale: var(--drawer-nested-scale);
205
- translate: 0 calc(-1 * var(--drawer-nested-offset));
206
- border-radius: var(--drawer-radius);
207
- filter: brightness(var(--drawer-nested-brightness));
208
- pointer-events: none;
209
- }
210
-
211
- /* 2+ open drawers after */
212
- .drawer[open]:has(~ .drawer[open] ~ .drawer[open]) {
213
- scale: calc(var(--drawer-nested-scale) * var(--drawer-nested-scale));
214
- translate: 0 calc(-2 * var(--drawer-nested-offset));
215
- filter: brightness(calc(var(--drawer-nested-brightness) * var(--drawer-nested-brightness)));
216
- }
217
-
218
- /* 3+ open drawers after */
219
- .drawer[open]:has(~ .drawer[open] ~ .drawer[open] ~ .drawer[open]) {
220
- scale: calc(var(--drawer-nested-scale) * var(--drawer-nested-scale) * var(--drawer-nested-scale));
221
- translate: 0 calc(-3 * var(--drawer-nested-offset));
222
- filter: brightness(calc(var(--drawer-nested-brightness) * var(--drawer-nested-brightness) * var(--drawer-nested-brightness)));
223
- }
224
-
225
- /* 4+ open drawers after */
226
- .drawer[open]:has(~ .drawer[open] ~ .drawer[open] ~ .drawer[open] ~ .drawer[open]) {
227
- scale: calc(var(--drawer-nested-scale) * var(--drawer-nested-scale) * var(--drawer-nested-scale) * var(--drawer-nested-scale));
228
- translate: 0 calc(-4 * var(--drawer-nested-offset));
229
- filter: brightness(calc(var(--drawer-nested-brightness) * var(--drawer-nested-brightness) * var(--drawer-nested-brightness) * var(--drawer-nested-brightness)));
230
- }
231
-
232
- /* 5+ open drawers after */
233
- .drawer[open]:has(~ .drawer[open] ~ .drawer[open] ~ .drawer[open] ~ .drawer[open] ~ .drawer[open]) {
234
- scale: calc(var(--drawer-nested-scale) * var(--drawer-nested-scale) * var(--drawer-nested-scale) * var(--drawer-nested-scale) * var(--drawer-nested-scale));
235
- translate: 0 calc(-5 * var(--drawer-nested-offset));
236
- filter: brightness(calc(var(--drawer-nested-brightness) * var(--drawer-nested-brightness) * var(--drawer-nested-brightness) * var(--drawer-nested-brightness) * var(--drawer-nested-brightness)));
237
- }
238
-
239
- /* Lighter backdrop for stacked drawers */
240
- .drawer[open] ~ .drawer[open]::backdrop {
241
- background: var(--drawer-nested-backdrop);
242
- backdrop-filter: none;
243
- }
244
-
245
- /* Handle */
246
- .drawer-handle {
247
- display: flex;
248
- justify-content: center;
249
- padding-block: var(--drawer-handle-padding-block);
250
- padding-inline: var(--drawer-handle-padding-inline);
251
- cursor: grab;
252
- }
253
-
254
- .drawer-handle::before {
255
- content: '';
256
- width: var(--drawer-handle-width);
257
- height: var(--drawer-handle-height);
258
- background: var(--drawer-handle-bg);
259
- border-radius: 100px;
260
- transition: width 0.2s var(--drawer-ease), height 0.2s var(--drawer-ease), background 0.2s ease;
261
- }
262
-
263
- .drawer-handle:hover::before {
264
- width: var(--drawer-handle-width-hover);
265
- background: var(--drawer-handle-bg-hover);
266
- }
267
-
268
- /* Vertical handle for left/right drawers */
269
- .drawer[data-direction="left"] .drawer-handle,
270
- .drawer[data-direction="right"] .drawer-handle {
271
- flex-direction: column;
272
- align-items: center;
273
- justify-content: center;
274
- padding-block: var(--drawer-handle-padding-inline);
275
- padding-inline: var(--drawer-handle-padding-block);
276
- height: 100%;
277
- position: absolute;
278
- top: 0;
279
- writing-mode: vertical-lr;
280
- }
281
-
282
- .drawer[data-direction="left"] .drawer-handle { right: 0; }
283
- .drawer[data-direction="right"] .drawer-handle { left: 0; }
284
-
285
- .drawer[data-direction="left"] .drawer-handle::before,
286
- .drawer[data-direction="right"] .drawer-handle::before {
287
- width: var(--drawer-handle-height);
288
- height: var(--drawer-handle-width);
289
- }
290
-
291
- .drawer[data-direction="left"] .drawer-handle:hover::before,
292
- .drawer[data-direction="right"] .drawer-handle:hover::before {
293
- width: var(--drawer-handle-height);
294
- height: var(--drawer-handle-width-hover);
295
- }
296
-
297
- /* Content - structural only, no opinionated padding */
298
- .drawer-content {
299
- overflow-x: hidden;
300
- overflow-y: auto;
301
- overscroll-behavior: contain;
302
- flex: 1;
303
- min-height: 0;
304
- }
305
-
306
- /* Content sizing for directions */
307
- .drawer[data-direction="left"] .drawer-content,
308
- .drawer[data-direction="right"] .drawer-content {
309
- height: 100%;
310
- }
311
-
312
- /* Reduced motion */
313
- @media (prefers-reduced-motion: reduce) {
314
- *, *::before, *::after {
315
- transition-duration: 0.01ms !important;
316
- }
317
-
318
- body:has(.drawer[open]) {
319
- scale: 1;
320
- }
321
-
322
- .drawer[open]:has(~ .drawer[open]),
323
- .drawer[open]:has(~ .drawer[open] ~ .drawer[open]),
324
- .drawer[open]:has(~ .drawer[open] ~ .drawer[open] ~ .drawer[open]),
325
- .drawer[open]:has(~ .drawer[open] ~ .drawer[open] ~ .drawer[open] ~ .drawer[open]),
326
- .drawer[open]:has(~ .drawer[open] ~ .drawer[open] ~ .drawer[open] ~ .drawer[open] ~ .drawer[open]) {
327
- scale: 1;
328
- translate: 0 0;
329
- filter: none;
330
- }
331
- }
332
-
333
-
334
- /*# sourceMappingURL=observer-QD6bSdOu.css.map*/
@@ -1 +0,0 @@
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"}