@nordhealth/components 4.25.2 → 4.26.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (62) hide show
  1. package/custom-elements.json +15700 -13775
  2. package/lib/Aside.js +8 -0
  3. package/lib/Aside.js.map +1 -0
  4. package/lib/AsideDrawer.js +14 -0
  5. package/lib/AsideDrawer.js.map +1 -0
  6. package/lib/AsideTrigger.js +5 -0
  7. package/lib/AsideTrigger.js.map +1 -0
  8. package/lib/Button.js +1 -1
  9. package/lib/Button.js.map +1 -1
  10. package/lib/{Calendar-B5X2WKNb.js → Calendar-ChNZdVRO.js} +2 -2
  11. package/lib/{Calendar-B5X2WKNb.js.map → Calendar-ChNZdVRO.js.map} +1 -1
  12. package/lib/Calendar.js +1 -1
  13. package/lib/Checkbox.js +1 -1
  14. package/lib/CommandMenu.js +1 -1
  15. package/lib/CommandMenu.js.map +1 -1
  16. package/lib/DatePicker.js +1 -1
  17. package/lib/Drawer.js +1 -1
  18. package/lib/Drawer.js.map +1 -1
  19. package/lib/DropdownItem.js +1 -1
  20. package/lib/DropdownSubmenu.js.map +1 -1
  21. package/lib/Footer.js +1 -1
  22. package/lib/Footer.js.map +1 -1
  23. package/lib/Header.js +1 -1
  24. package/lib/Header.js.map +1 -1
  25. package/lib/Icon.js +1 -1
  26. package/lib/Input.js +1 -1
  27. package/lib/Layout.js +3 -1
  28. package/lib/Layout.js.map +1 -1
  29. package/lib/Modal.js +1 -1
  30. package/lib/NavToggle-DzKbd-El.js +2 -0
  31. package/lib/NavToggle-DzKbd-El.js.map +1 -0
  32. package/lib/NavToggle.js +1 -1
  33. package/lib/Navigation.js +1 -1
  34. package/lib/Navigation.js.map +1 -1
  35. package/lib/Select.js +1 -1
  36. package/lib/TagGroup.js +1 -1
  37. package/lib/Toggle.js +1 -1
  38. package/lib/Truncate.js.map +1 -1
  39. package/lib/bundle.js +56 -38
  40. package/lib/bundle.js.map +1 -1
  41. package/lib/focus-D8oSvIcN.js +2 -0
  42. package/lib/focus-D8oSvIcN.js.map +1 -0
  43. package/lib/index.js +1 -1
  44. package/lib/react.d.ts +45 -20
  45. package/lib/src/aside/Aside.d.ts +388 -0
  46. package/lib/src/aside-drawer/AsideDrawer.d.ts +153 -0
  47. package/lib/src/aside-trigger/AsideTrigger.d.ts +84 -0
  48. package/lib/src/button/Button.d.ts +6 -0
  49. package/lib/src/common/body-scroll-lock.d.ts +20 -0
  50. package/lib/src/drawer/Drawer.d.ts +2 -1
  51. package/lib/src/dropdown-submenu/DropdownSubmenu.d.ts +0 -6
  52. package/lib/src/header/Header.d.ts +1 -1
  53. package/lib/src/index.d.ts +3 -0
  54. package/lib/src/layout/Layout.d.ts +15 -1
  55. package/lib/src/nav-toggle/NavToggle.d.ts +1 -16
  56. package/lib/src/truncate/Truncate.d.ts +1 -10
  57. package/lib/storage-CGZ-YX4-.js +2 -0
  58. package/lib/storage-CGZ-YX4-.js.map +1 -0
  59. package/lib/vue.d.ts +40 -20
  60. package/package.json +7 -7
  61. package/lib/NavToggle-BQxuLW2X.js +0 -2
  62. package/lib/NavToggle-BQxuLW2X.js.map +0 -1
package/lib/Aside.js ADDED
@@ -0,0 +1,8 @@
1
+ import{_ as e}from"./tslib.es6-CmLYFWVC.js";import{css as t,LitElement as i,html as r,nothing as a}from"lit";import{property as n,state as s,query as o,customElement as l}from"lit/decorators.js";import{classMap as d}from"lit/directives/class-map.js";import{S as c}from"./SlotController-Z6eG7LSZ.js";import{o as h}from"./observe-D0n0zOfU.js";import{N as u}from"./events-Bv6wNHwJ.js";import{g as p}from"./focus-D8oSvIcN.js";import{s as v}from"./Component-DSU3Qp0O.js";import"./Tooltip.js";import"./EventController-BBOmvfLa.js";import"./positioning-D-K8Mueq.js";import"./fsm-Bq5jMQrK.js";let w=0,g=0,b=null;function f(){if("undefined"==typeof document)return;if(0===w)return;if(w--,w>0)return;const e=document.body;b&&(e.style.position=b.position,e.style.top=b.top,e.style.insetInlineStart=b.insetInlineStart,e.style.insetInlineEnd=b.insetInlineEnd,e.style.inlineSize=b.inlineSize,e.style.overflow=b.overflow,b=null),window.scrollTo(0,g)}const y=t`:host{--_n-aside-rail-width:var(--n-aside-rail-width, 48px);--_n-aside-rail-gap:var(--n-aside-rail-gap, 0);--_n-aside-rail-padding-inline:var(--n-aside-rail-padding-inline, 8px);--_n-aside-rail-padding-block:var(--n-aside-rail-padding-block, 8px);--_n-aside-rail-border-color:var(--n-aside-rail-border-color, var(--n-color-border));--_n-aside-rail-border-width:var(--n-aside-rail-border-width, 1px);--_n-aside-rail-box-shadow:var(--n-aside-rail-box-shadow, none);--_n-aside-rail-background-color:var(--n-aside-rail-background-color, var(--n-color-surface));--_n-aside-background-color:var(--n-aside-background-color, var(--n-color-background));--_n-aside-drawers-margin-inline-start:var(--n-aside-drawers-margin-inline-start, 0px);--_n-aside-resize-block-size:var(--n-aside-resize-block-size, 100%);--_n-aside-resize-color:var(--n-aside-resize-color, var(--n-layout-resize-color, var(--n-color-accent)));--_n-aside-z-index:var(--n-aside-z-index, var(--n-index-nav));--_n-aside-z-index-fullscreen:var(--n-aside-z-index-fullscreen, var(--n-index-top-bar, 1000));--_n-aside-rail-narrow-block-size:var(--n-aside-rail-narrow-block-size, 56px);--_n-aside-rail-narrow-padding-inline:var(--n-aside-rail-narrow-padding-inline, var(--n-space-m, 16px));--_n-aside-rail-narrow-padding-block:var(--n-aside-rail-narrow-padding-block, var(--n-space-s, 8px));--_n-aside-rail-narrow-border-block-start-width:var(--n-aside-rail-narrow-border-block-start-width, 1px);--_n-aside-rail-narrow-border-color:var(--n-aside-rail-narrow-border-color, var(--_n-aside-rail-border-color));--_n-aside-drawer-floating-inset-block-start:var(
2
+ --n-aside-drawer-floating-inset-block-start,
3
+ var(--n-layout-header-block-size, 60px)
4
+ );--_n-aside-drawer-floating-inset-inline:var(--n-aside-drawer-floating-inset-inline, 0px);--_n-aside-drawer-floating-bottom-offset:var(--n-aside-drawer-floating-bottom-offset, 0px);--_n-aside-drawer-floating-inset-block-end:var(
5
+ --n-aside-drawer-floating-inset-block-end,
6
+ calc(var(--_n-aside-rail-narrow-block-size) - var(--_n-aside-drawer-floating-bottom-offset))
7
+ );--_n-aside-backdrop-background:var(--n-aside-backdrop-background, var(--n-color-overlay));--_n-aside-drawer-slide-duration:var(--n-aside-drawer-slide-duration, 300ms);--_n-aside-drawer-slide-easing:var(--n-aside-drawer-slide-easing, cubic-bezier(0.4, 0, 0.2, 1));--_n-aside-z-backdrop:var(--n-aside-z-backdrop, calc(var(--n-index-top-bar, 1000) + 2));--_n-aside-z-drawer-floating:var(--n-aside-z-drawer-floating, calc(var(--n-index-top-bar, 1000) + 3));--_n-aside-z-rail-narrow:var(--n-aside-z-rail-narrow, calc(var(--n-index-top-bar, 1000) + 4));display:flex;position:relative;background:var(--_n-aside-background-color);block-size:100%;min-block-size:100%;align-self:stretch}.n-aside{display:flex;flex:1;flex-direction:row;align-items:stretch;gap:var(--_n-aside-rail-gap);position:relative;block-size:100%}.n-aside-rail{order:99;flex:0 0 auto;inline-size:var(--_n-aside-rail-width);block-size:100%;display:flex;flex-direction:column;align-items:center;padding-inline-start:var(--_n-aside-rail-padding-inline);padding-inline-end:var(--_n-aside-rail-padding-inline);padding-block-start:var(--_n-aside-rail-padding-block);padding-block-end:var(--_n-aside-rail-padding-block);box-sizing:border-box;background:var(--_n-aside-rail-background-color);border-inline-start:var(--_n-aside-rail-border-width) solid var(--_n-aside-rail-border-color);box-shadow:var(--_n-aside-rail-box-shadow);margin-inline-start:auto;position:relative;z-index:calc(var(--_n-aside-z-index) + 1)}:host([data-fullscreen]) .n-aside-rail{z-index:calc(var(--_n-aside-z-index-fullscreen) + 1)}.n-aside-rail ::slotted(*){display:block}.n-aside-drawers{display:flex;flex:0 0 auto;margin-inline-start:var(--_n-aside-drawers-margin-inline-start)}:host(:is([active-drawer=''],:not([active-drawer]))) .n-aside-drawers{display:contents}.n-resize{touch-action:none;user-select:none;position:absolute;block-size:var(--_n-aside-resize-block-size);inset-block:0;margin-block:auto;inset-inline-start:calc(var(--_n-aside-drawers-margin-inline-start) - 4px);inline-size:10px;background:0 0;border:0;padding:0;z-index:calc(var(--n-index-nav) + 1)}.n-resize[data-handle-mode=col-resize]{cursor:col-resize}.n-resize[data-handle-mode=pointer]{cursor:pointer}.n-resize[data-handle-mode=default]{cursor:default}.n-resize::after{content:'';position:absolute;inset-block:0;inset-inline:0;margin-inline:auto;inline-size:3px;background:var(--_n-aside-resize-color);transition:opacity var(--n-transition-slowly);opacity:0}.n-resize:not([data-handle-mode=default]):focus-visible::after,.n-resize:not([data-handle-mode=default]):hover::after{opacity:1;transition-delay:.15s}.n-aside.n-dragging .n-resize::after{opacity:1}.n-resize:focus{outline:0}.n-resize[aria-disabled=true]::after{display:none}@media (max-width:767px){.n-resize{display:none}}.n-aside.n-dragging{cursor:col-resize;user-select:none}.n-resize-tooltip{transform:translateY(var(--n-resize-tooltip-y,0))}.n-aside.n-dragging .n-resize-tooltip{visibility:hidden!important;opacity:0!important}.n-aside-backdrop{display:none}:host([data-narrow-floating-drawer]) .n-aside-backdrop{display:block;position:fixed;inset-block-start:0;inset-inline-start:0;inset-inline-end:0;inset-block-end:var(--_n-aside-rail-narrow-block-size);background:var(--_n-aside-backdrop-background);pointer-events:auto;z-index:var(--_n-aside-z-backdrop)}:host([data-narrow-floating-drawer]:not([data-narrow-rail-bottom])) .n-aside-backdrop{inset-block-end:0}@media (max-width:767px){:host([data-narrow-floating-drawer]) .n-aside,:host([data-narrow-rail-bottom]) .n-aside{display:contents}:host([data-narrow-rail-bottom]) .n-aside-rail{position:fixed;inset-inline-start:0;inset-inline-end:0;inset-block-end:var(--n-aside-rail-narrow-inset-block-end,0);block-size:var(--_n-aside-rail-narrow-block-size);inline-size:auto;flex-direction:row;justify-content:flex-start;align-items:center;padding-inline-start:var(--_n-aside-rail-narrow-padding-inline);padding-inline-end:var(--_n-aside-rail-narrow-padding-inline);padding-block-start:var(--_n-aside-rail-narrow-padding-block);padding-block-end:var(--_n-aside-rail-narrow-padding-block);margin-inline-start:0;border-inline-start:0;border-block-start:var(--_n-aside-rail-narrow-border-block-start-width) solid var(--_n-aside-rail-narrow-border-color);overflow-x:auto;overflow-y:hidden;scroll-snap-type:x mandatory;scrollbar-width:none;pointer-events:auto;z-index:var(--_n-aside-z-rail-narrow)}:host([data-narrow-rail-bottom]) .n-aside-rail::-webkit-scrollbar{display:none}:host([data-narrow-rail-bottom]) .n-aside-rail ::slotted(*){inline-size:auto;flex:0 0 auto;scroll-snap-align:start}:host([data-narrow-floating-drawer]) .n-aside-drawers{position:fixed;inset-block-start:var(--_n-aside-drawer-floating-inset-block-start);inset-inline-start:var(--_n-aside-drawer-floating-inset-inline);inline-size:calc(100vw - (var(--_n-aside-drawer-floating-inset-inline) * 2));block-size:calc(100vh - var(--_n-aside-drawer-floating-inset-block-start) - var(--_n-aside-drawer-floating-inset-block-end));margin-inline-start:0;overscroll-behavior:contain;pointer-events:auto;z-index:var(--_n-aside-z-drawer-floating);animation:n-aside-drawer-slide-up var(--_n-aside-drawer-slide-duration) var(--_n-aside-drawer-slide-easing)}@keyframes n-aside-drawer-slide-up{from{transform:translateY(100%)}to{transform:translateY(0)}}:host([data-narrow-floating-drawer]) ::slotted(nord-aside-drawer){inline-size:100%!important;max-inline-size:100%!important;min-inline-size:0!important;block-size:100%;margin-block:0;margin-inline:0}:host([data-narrow-floating-drawer]) ::slotted(nord-aside-drawer nord-drawer){box-shadow:var(--n-box-shadow-popout);border-radius:var(--n-border-radius)}:host(:not([data-narrow-floating-drawer])) .n-aside-drawers{pointer-events:none}}`;let m=0;class k extends u{constructor(e,t){super("active-drawer-change"),Object.defineProperties(this,{activeDrawer:{value:e,enumerable:!0,writable:!1},previousDrawer:{value:t,enumerable:!0,writable:!1}})}}class D extends u{constructor(e){super("drawer-open"),Object.defineProperty(this,"activeDrawer",{value:e,enumerable:!0,writable:!1})}}class z extends u{constructor(e){super("drawer-close"),Object.defineProperty(this,"previousDrawer",{value:e,enumerable:!0,writable:!1})}}class x extends u{constructor(e,t){super("drawer-resize"),Object.defineProperties(this,{drawerId:{value:e,enumerable:!0,writable:!1},width:{value:t,enumerable:!0,writable:!1}})}}class S extends u{constructor(e,t){super("handle-mode-change"),Object.defineProperties(this,{mode:{value:e,enumerable:!0,writable:!1},previous:{value:t,enumerable:!0,writable:!1}})}}const C=["a[href]","button","input","select","textarea","[tabindex]","audio[controls]","video[controls]",'[contenteditable]:not([contenteditable="false"])',"details > summary:first-of-type"].join(","),_="undefined"!=typeof matchMedia?matchMedia("(min-width: 768px)"):{matches:!1,addEventListener(){},removeEventListener(){}};new WeakSet;let L=class extends i{constructor(){super(...arguments),this.activeDrawer="",this.railWidth=48,this.railGap=0,this.handleClick="toggle",this.lastDrawer="",this.isDragging=!1,this.wideScreen=_.matches,this.hasRailItems=!1,this.tooltipSlot=new c(this,"tooltip-divider"),this.resizeTooltipId="n-aside-resize-tip-"+ ++m,this.dragStartX=0,this.hasDragged=!1,this.handleRailSlotChange=e=>{const t=e.target.assignedElements({flatten:!0}).length>0;t!==this.hasRailItems&&(this.hasRailItems=t,this.syncNarrowState())},this.handleBackdropClick=()=>{this.activeDrawer&&(this.lastDrawer=this.activeDrawer,this.activeDrawer="")},this.handleMediaChange=e=>{this.wideScreen=e.matches,this.syncNarrowState(),this.syncLayoutDrawerSize()},this.handleHostKeyDown=e=>{"Escape"===e.key&&(this.wideScreen||this.activeDrawer&&(e.defaultPrevented||(this.lastDrawer=this.activeDrawer,this.activeDrawer="",e.stopPropagation())))},this.holdsBodyScrollLock=!1,this.floatingFocusActive=!1,this.floatingFocusReturnElement=null,this.trappedDrawer=null,this.handleFloatingFocusKeydown=e=>{if("Tab"!==e.key)return;const t=this.trappedDrawer;if(!t)return;const i=this.focusableElementsInDrawer(t);if(0===i.length)return e.preventDefault(),t.tabIndex=-1,void t.focus();const r=i[0],a=i[i.length-1],n=p(document);e.shiftKey?n!==r&&this.drawerContainsFocus(t,n)||(e.preventDefault(),a.focus()):n!==a&&this.drawerContainsFocus(t,n)||(e.preventDefault(),r.focus())},this.handleDrawerWidthChange=e=>{const t=e,i=e.target;i instanceof HTMLElement&&"NORD-ASIDE-DRAWER"===i.tagName&&(this.dispatchEvent(new x(i.id,t.width)),this.syncLayoutDrawerSize())},this.startDragging=e=>{if(0!==e.button||!this.wideScreen)return;const t=e.currentTarget;try{t.setPointerCapture(e.pointerId)}catch(e){}this.isDragging=!0,this.dragStartX=e.clientX,this.hasDragged=!1,this.dragGeometry={rect:this.getBoundingClientRect(),styles:getComputedStyle(this)}},this.stopDragging=e=>{if(!this.isDragging)return;const t=!this.hasDragged;if(this.isDragging=!1,this.hasDragged=!1,this.dragGeometry=void 0,e){const t=e.currentTarget;try{t.hasPointerCapture(e.pointerId)&&t.releasePointerCapture(e.pointerId)}catch(e){}}t&&this.handleHandleClick()},this.handlePointerLeave=e=>{var t;this.isDragging&&this.stopDragging(e),null===(t=this.resizeTooltipEl)||void 0===t||t.style.removeProperty("--n-resize-tooltip-y")},this.handleResizeHoverMove=e=>{const t=e.currentTarget;if(!t||!this.resizeTooltipEl)return;const i=t.getBoundingClientRect(),r=e.clientY-(i.top+i.height/2);this.resizeTooltipEl.style.setProperty("--n-resize-tooltip-y",`${r}px`)},this.handleDrag=e=>{var t;if(!this.activeDrawerEl)return void(Math.abs(e.clientX-this.dragStartX)>=4&&(this.hasDragged=!0));if(!this.hasDragged&&Math.abs(e.clientX-this.dragStartX)<4)return;this.hasDragged=!0;const i=this.activeDrawerEl;if(i.isPinned)return;const r=null!==(t=this.dragGeometry)&&void 0!==t?t:{rect:this.getBoundingClientRect(),styles:getComputedStyle(this)},a=r.rect.right-e.clientX-this.railWidth-this.railGap,n=this.drawerWidthAtCapWithMainFloor(r),s=void 0!==n?Math.min(a,n):a;i.setWidth(s,"drag")},this.scheduleLeftCapReconcile=()=>{this.leftCapReconcileScheduled||(this.leftCapReconcileScheduled=!0,requestAnimationFrame((()=>{this.leftCapReconcileScheduled=!1;const e={rect:this.getBoundingClientRect(),styles:getComputedStyle(this)};this.flushFullscreenAvailable(e),this.flushDragCap(e)})))},this.leftCapReconcileScheduled=!1,this.handleLayoutNavChange=()=>{this.scheduleLeftCapReconcile()},this.handleViewportResize=()=>{this.scheduleLeftCapReconcile()},this.handleKeyboardResize=e=>{const t=this.activeDrawerEl;if(!t||!this.wideScreen)return;const i=this.drawerWidthAtCapWithMainFloor(),r=e=>void 0!==i?Math.min(e,i):e;switch(e.key){case"ArrowLeft":t.setWidth(r(t.width+30),"keyboard");break;case"ArrowRight":t.setWidth(t.width-30,"keyboard");break;case"Home":t.setWidth(t.minWidth,"keyboard");break;case"End":t.setWidth(r(t.maxWidth),"keyboard");break;default:return}e.preventDefault()}}render(){const e=this.handleInteractive,t=e.tooltip&&!this.isDragging;if(this.dataset.handleMode!==e.cursor){const t=this.dataset.handleMode;this.dataset.handleMode=e.cursor,void 0!==t&&this.dispatchEvent(new S(e.cursor,t))}return r`<div class="${d({"n-aside":!0,"n-dragging":this.isDragging})}" part="aside"><button type="button" class="n-resize" part="resize" role="separator" aria-orientation="vertical" aria-label="Resize drawer" aria-disabled="${e.enabled?"false":"true"}" aria-describedby="${t?this.resizeTooltipId:a}" data-handle-mode="${e.cursor}" tabindex="${e.dragOk?"0":"-1"}" ?hidden="${!this.wideScreen||!this.activeDrawer}" @pointerdown="${this.startDragging}" @pointermove="${this.isDragging?this.handleDrag:this.handleResizeHoverMove}" @pointerleave="${this.handlePointerLeave}" @pointerup="${this.stopDragging}" @keydown="${this.handleKeyboardResize}"></button> ${this.tooltipSlot.hasContent?r`<nord-tooltip id="${this.resizeTooltipId}" position="inline-start" class="n-resize-tooltip"><slot name="tooltip-divider"></slot></nord-tooltip>`:a}<div class="n-aside-rail" part="rail"><slot name="rail" @slotchange="${this.handleRailSlotChange}"></slot></div><div class="n-aside-drawers" part="drawers"><slot></slot></div><div class="n-aside-backdrop" part="backdrop" aria-hidden="true" @click="${this.handleBackdropClick}"></div></div>`}connectedCallback(){super.connectedCallback(),this.applyRailVars(),this.observeChildren(),_.addEventListener("change",this.handleMediaChange),this.addEventListener("width-change",this.handleDrawerWidthChange),this.addEventListener("keydown",this.handleHostKeyDown),window.addEventListener("resize",this.handleViewportResize,{passive:!0}),queueMicrotask((()=>{this.attachLayoutSync(),this.syncNarrowState()}))}disconnectedCallback(){var e;super.disconnectedCallback(),null===(e=this.childObserver)||void 0===e||e.disconnect(),this.childObserver=void 0,_.removeEventListener("change",this.handleMediaChange),this.removeEventListener("width-change",this.handleDrawerWidthChange),this.removeEventListener("keydown",this.handleHostKeyDown),window.removeEventListener("resize",this.handleViewportResize),this.clearNarrowState(),this.detachLayoutSync()}syncNarrowState(){const e=this.hostLayout,t=!this.wideScreen,i=t&&this.hasRailItems,r=t&&!!this.activeDrawer;this.toggleAttribute("data-narrow-rail-bottom",i),this.toggleAttribute("data-narrow-floating-drawer",r),null==e||e.toggleAttribute("data-narrow-rail-bottom",i),null==e||e.toggleAttribute("data-narrow-floating-drawer",r),this.style.removeProperty("--n-aside-rail-narrow-inset-block-end"),r&&!this.holdsBodyScrollLock?(!function(){if("undefined"==typeof document)return;if(w++,w>1)return;g=window.scrollY||window.pageYOffset||0;const e=document.body;b={position:e.style.position,top:e.style.top,insetInlineStart:e.style.insetInlineStart,insetInlineEnd:e.style.insetInlineEnd,inlineSize:e.style.inlineSize,overflow:e.style.overflow},e.style.position="fixed",e.style.top=`-${g}px`,e.style.insetInlineStart="0",e.style.insetInlineEnd="0",e.style.inlineSize="100%",e.style.overflow="hidden"}(),this.holdsBodyScrollLock=!0):!r&&this.holdsBodyScrollLock&&(f(),this.holdsBodyScrollLock=!1),r&&!this.floatingFocusActive?this.enterFloatingFocus():!r&&this.floatingFocusActive&&this.exitFloatingFocus()}enterFloatingFocus(){if("undefined"==typeof document)return;const e=this.activeDrawerEl;if(!e)return;this.floatingFocusActive=!0,this.trappedDrawer=e,e.floating=!0;const t=p(document);this.floatingFocusReturnElement=t instanceof HTMLElement?t:null,document.addEventListener("keydown",this.handleFloatingFocusKeydown,!0),requestAnimationFrame((()=>{const t=this.firstFocusableInDrawer(e);t?t.focus():(e.tabIndex=-1,e.focus())}))}exitFloatingFocus(){this.floatingFocusActive=!1,"undefined"!=typeof document&&document.removeEventListener("keydown",this.handleFloatingFocusKeydown,!0);const e=this.trappedDrawer;e&&(e.floating=!1,"-1"===e.getAttribute("tabindex")&&e.removeAttribute("tabindex")),this.trappedDrawer=null;const t=this.floatingFocusReturnElement;this.floatingFocusReturnElement=null,t&&t.isConnected&&t.focus()}drawerContainsFocus(e,t){return null!=t&&(e===t||e.contains(t))}focusableElementsInDrawer(e){const t=e.querySelectorAll(C);return Array.from(t).filter((e=>this.isFocusable(e)))}firstFocusableInDrawer(e){var t;return null!==(t=this.focusableElementsInDrawer(e)[0])&&void 0!==t?t:null}isFocusable(e){return!e.hasAttribute("disabled")&&("true"!==e.getAttribute("aria-hidden")&&(!(e.tabIndex<0)&&(null!==e.offsetParent||e.getClientRects().length>0)))}clearNarrowState(){const e=this.hostLayout;this.removeAttribute("data-narrow-rail-bottom"),this.removeAttribute("data-narrow-floating-drawer"),null==e||e.removeAttribute("data-narrow-rail-bottom"),null==e||e.removeAttribute("data-narrow-floating-drawer"),this.style.removeProperty("--n-aside-rail-narrow-inset-block-end"),this.holdsBodyScrollLock&&(f(),this.holdsBodyScrollLock=!1),this.floatingFocusActive&&this.exitFloatingFocus()}get hostLayout(){return"drawer"!==this.getAttribute("slot")?null:this.closest("nord-layout")}attachLayoutSync(){const e=this.hostLayout;if(!e)return;this.syncedLayout=e,this.syncLayoutDrawerSize(),e.addEventListener("nav-open-change",this.handleLayoutNavChange),e.addEventListener("nav-resize",this.handleLayoutNavChange);const t=e.querySelector("nord-navigation");t&&"undefined"!=typeof ResizeObserver&&(this.navResizeObserver=new ResizeObserver((()=>this.scheduleLeftCapReconcile())),this.navResizeObserver.observe(t))}detachLayoutSync(){var e;this.syncedLayout&&(this.syncedLayout.removeEventListener("nav-open-change",this.handleLayoutNavChange),this.syncedLayout.removeEventListener("nav-resize",this.handleLayoutNavChange),null===(e=this.navResizeObserver)||void 0===e||e.disconnect(),this.navResizeObserver=void 0,this.syncedLayout=void 0)}syncLayoutDrawerSize(){var e,t;const i=this.hostLayout;if(!i)return;if(!this.wideScreen)return i.style.setProperty("--n-layout-drawer-inline-size","0px"),void i.style.setProperty("--n-layout-drawer-min-inline-size","0px");let r=this.railWidth;if(this.activeDrawer){const i=this.findDrawer(this.activeDrawer),a=null!==(t=null!==(e=null==i?void 0:i.width)&&void 0!==e?e:null==i?void 0:i.defaultWidth)&&void 0!==t?t:0;if(a){const e=i?getComputedStyle(i):null,t=e&&Number.parseFloat(e.marginInlineStart)||0,n=e&&Number.parseFloat(e.marginInlineEnd)||0,s=this.renderRoot.querySelector(".n-aside-drawers");r+=a+t+n+(s&&Number.parseFloat(getComputedStyle(s).marginInlineStart)||0)+this.railGap}}i.style.setProperty("--n-layout-drawer-inline-size",`${r}px`),i.style.setProperty("--n-layout-drawer-min-inline-size",`${this.railWidth}px`)}applyRailVars(){this.style.setProperty("--_n-aside-rail-width",`${this.railWidth}px`),this.style.setProperty("--_n-aside-rail-gap",`${this.railGap}px`),this.syncLayoutDrawerSize()}handleActiveDrawerChange(e,t){if(e!==t){if(t){const i=this.findDrawer(t);if(!i||i.disabled)return void(this.activeDrawer=e);this.lastDrawer=t}this.updateDrawerVisibility(),this.syncLayoutDrawerSize(),this.scheduleLeftCapReconcile(),this.syncNarrowState(),this.dispatchEvent(new k(t,e)),t&&!e?this.dispatchEvent(new D(t)):!t&&e&&this.dispatchEvent(new z(e))}}findDrawer(e){return e?this.querySelector(`:scope > nord-aside-drawer#${CSS.escape(e)}`):null}get activeDrawerEl(){return this.activeDrawer?this.findDrawer(this.activeDrawer):null}updateDrawerVisibility(){this.querySelectorAll(":scope > nord-aside-drawer").forEach((e=>{const t=!!this.activeDrawer&&e.id===this.activeDrawer;e.toggleAttribute("hidden",!t)}))}observeChildren(){this.childObserver=new MutationObserver((e=>{let t=!1,i=!1;for(const r of e){if("childList"===r.type){t=!0,i=!0;continue}if("attributes"!==r.type)continue;const e=r.target;"NORD-ASIDE-DRAWER"===e.tagName&&e.parentElement===this&&("id"===r.attributeName&&r.oldValue&&this.activeDrawer===r.oldValue&&(this.activeDrawer=e.id,this.lastDrawer===r.oldValue&&(this.lastDrawer=e.id)),"fullscreen"!==r.attributeName&&"data-animating-fullscreen"!==r.attributeName||(i=!0),t=!0)}t&&(this.activeDrawer&&!this.findDrawer(this.activeDrawer)?this.activeDrawer="":this.updateDrawerVisibility()),i&&this.syncFullscreenAttr()})),this.childObserver.observe(this,{childList:!0,subtree:!0,attributes:!0,attributeFilter:["id","disabled","fullscreen","data-animating-fullscreen"],attributeOldValue:!0}),this.updateDrawerVisibility(),this.syncFullscreenAttr()}syncFullscreenAttr(){const e=this.querySelectorAll(":scope > nord-aside-drawer");let t=!1;e.forEach((e=>{(e.hasAttribute("fullscreen")||e.hasAttribute("data-animating-fullscreen"))&&(t=!0)})),this.toggleAttribute("data-fullscreen",t);const i=this.activeDrawerEl;(null==i?void 0:i.hasAttribute("fullscreen"))?this.scheduleLeftCapReconcile():this.clearFullscreenSizeOverride()}getLeftCapViewportX(){const e=this.hostLayout;if(!e)return;const t=e.querySelector("nord-navigation"),i=t?t.getBoundingClientRect().right:0;return"rail"===e.getAttribute("collapse-mode")?Math.max(0,i):Math.max(i,54)}drawerWidthAtCap(e){return this.computeDrawerWidthAtCap(!1,e)}drawerWidthAtCapWithMainFloor(e){return this.computeDrawerWidthAtCap(!0,e)}computeDrawerWidthAtCap(e,t){var i,r;const a=this.getLeftCapViewportX();if(void 0===a)return;const n=null!==(i=null==t?void 0:t.rect)&&void 0!==i?i:this.getBoundingClientRect(),s=null!==(r=null==t?void 0:t.styles)&&void 0!==r?r:getComputedStyle(this),o=e?this.parseLengthVar(s,"--n-layout-main-min-inline-size"):0;return Math.max(0,n.right-this.railWidth-this.railGap-a-o)}parseLengthVar(e,t){return Number.parseFloat(e.getPropertyValue(t))||0}flushFullscreenAvailable(e){const t=this.activeDrawerEl;if(!t)return;const i=this.drawerWidthAtCap(e);if(void 0===i)return void t.style.removeProperty("--_n-aside-drawer-fullscreen-available");const r=e.styles,a=this.parseLengthVar(r,"--n-aside-drawer-border-width");t.style.setProperty("--_n-aside-drawer-fullscreen-available",`${Math.max(0,i-a)}px`)}flushDragCap(e){const t=this.activeDrawerEl;if(!t||t.hasAttribute("fullscreen")||this.isDragging)return;const i=this.drawerWidthAtCapWithMainFloor(e);void 0!==i&&t.width>i&&t.setWidth(i,"attribute")}clearFullscreenSizeOverride(){var e;null===(e=this.activeDrawerEl)||void 0===e||e.style.removeProperty("--_n-aside-drawer-fullscreen-available")}handleHandleClick(){if("none"===this.handleClick)return;if(!!this.activeDrawer)return this.lastDrawer=this.activeDrawer,void(this.activeDrawer="");if("toggle"!==this.handleClick)return;if(this.lastDrawer){const e=this.findDrawer(this.lastDrawer);if(e&&!e.disabled)return void(this.activeDrawer=this.lastDrawer)}const e=this.findFirstTriggerDrawer();e&&(this.activeDrawer=e)}findFirstTriggerDrawer(){const e=this.querySelectorAll("nord-aside-trigger[drawer]");for(const t of e){const e=t.getAttribute("drawer");if(!e)continue;const i=this.findDrawer(e);if(i&&!i.disabled)return e;0}return null}get handleInteractive(){if(!this.wideScreen)return{enabled:!1,dragOk:!1,clickOk:!1,cursor:"default",tooltip:!1};const e=this.activeDrawerEl,t=!!e,i=t&&!e.isPinned&&!e.fullscreen;let r;switch(this.handleClick){case"toggle":r=!0;break;case"close":r=t;break;default:r=!1}const a=i||r;let n;n=i?"col-resize":r?"pointer":"default";return{enabled:a,dragOk:i,clickOk:r,cursor:n,tooltip:a&&this.tooltipSlot.hasContent}}};L.styles=[v,y],e([n({reflect:!0,attribute:"active-drawer"})],L.prototype,"activeDrawer",void 0),e([n({reflect:!0,type:Number,attribute:"rail-width"})],L.prototype,"railWidth",void 0),e([n({reflect:!0,type:Number,attribute:"rail-gap"})],L.prototype,"railGap",void 0),e([n({reflect:!0,attribute:"handle-click"})],L.prototype,"handleClick",void 0),e([s()],L.prototype,"lastDrawer",void 0),e([s()],L.prototype,"isDragging",void 0),e([s()],L.prototype,"wideScreen",void 0),e([s()],L.prototype,"hasRailItems",void 0),e([o(".n-resize")],L.prototype,"handleEl",void 0),e([o(".n-resize-tooltip")],L.prototype,"resizeTooltipEl",void 0),e([h("railWidth"),h("railGap")],L.prototype,"applyRailVars",null),e([h("activeDrawer")],L.prototype,"handleActiveDrawerChange",null),L=e([l("nord-aside")],L);var E=L;export{k as AsideActiveDrawerChangeEvent,z as AsideDrawerCloseEvent,D as AsideDrawerOpenEvent,x as AsideDrawerResizeEvent,S as AsideHandleModeChangeEvent,E as default};
8
+ //# sourceMappingURL=Aside.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Aside.js","sources":["../src/common/body-scroll-lock.ts","../src/aside/Aside.ts"],"sourcesContent":["/**\n * iOS-safe body-scroll lock with reference counting.\n *\n * `overflow: hidden` on `<body>` is unreliable on iOS Safari — touch\n * scroll still bleeds through. The canonical fix is to apply\n * `position: fixed; top: -<savedScrollY>px` so the viewport's scroll\n * container becomes the (now-unscrollable) viewport itself. On unlock\n * we restore the original styles AND scroll position so the page\n * looks unchanged.\n *\n * Ref-counted so multiple overlays (e.g. a floating drawer and a modal\n * opened on top of it) can request the lock independently — the body\n * stays locked until the last caller releases.\n *\n * Callers should pair every `lockBodyScroll()` with one\n * `unlockBodyScroll()`. Each component should track its own lock state\n * (a boolean) so disconnect / repeated calls don't desync the count.\n */\n\ninterface SavedBodyStyles {\n position: string\n top: string\n insetInlineStart: string\n insetInlineEnd: string\n inlineSize: string\n overflow: string\n}\n\nlet lockCount = 0\nlet savedScrollY = 0\nlet savedBodyStyles: SavedBodyStyles | null = null\n\nexport function lockBodyScroll() {\n if (typeof document === 'undefined')\n return\n lockCount++\n if (lockCount > 1)\n return // already locked by an earlier caller\n\n savedScrollY = window.scrollY || window.pageYOffset || 0\n const body = document.body\n savedBodyStyles = {\n position: body.style.position,\n top: body.style.top,\n insetInlineStart: body.style.insetInlineStart,\n insetInlineEnd: body.style.insetInlineEnd,\n inlineSize: body.style.inlineSize,\n overflow: body.style.overflow,\n }\n // `position: fixed` + the inverse of the saved scroll keeps the\n // visible page in place. `inline-size: 100%` prevents the body from\n // collapsing to content width once it's out of flow. `overflow:\n // hidden` is the desktop / Android safety net.\n body.style.position = 'fixed'\n body.style.top = `-${savedScrollY}px`\n body.style.insetInlineStart = '0'\n body.style.insetInlineEnd = '0'\n body.style.inlineSize = '100%'\n body.style.overflow = 'hidden'\n}\n\nexport function unlockBodyScroll() {\n if (typeof document === 'undefined')\n return\n if (lockCount === 0)\n return // nothing to unlock\n lockCount--\n if (lockCount > 0)\n return // still locked by another caller\n\n const body = document.body\n if (savedBodyStyles) {\n body.style.position = savedBodyStyles.position\n body.style.top = savedBodyStyles.top\n body.style.insetInlineStart = savedBodyStyles.insetInlineStart\n body.style.insetInlineEnd = savedBodyStyles.insetInlineEnd\n body.style.inlineSize = savedBodyStyles.inlineSize\n body.style.overflow = savedBodyStyles.overflow\n savedBodyStyles = null\n }\n // Restore scroll without smooth-scroll — instant snap so the user\n // doesn't see the page animate back to where they were.\n window.scrollTo(0, savedScrollY)\n}\n","import type AsideDrawer from '../aside-drawer/AsideDrawer.js'\nimport type { AsideDrawerWidthChangeEvent } from '../aside-drawer/AsideDrawer.js'\nimport { html, LitElement, nothing } from 'lit'\nimport { customElement, property, query, state } from 'lit/decorators.js'\nimport { classMap } from 'lit/directives/class-map.js'\nimport { lockBodyScroll, unlockBodyScroll } from '../common/body-scroll-lock.js'\nimport { SlotController } from '../common/controllers/SlotController.js'\nimport { observe } from '../common/decorators/observe.js'\nimport { NordEvent } from '../common/events.js'\nimport { getFocusedElement } from '../common/focus.js'\n\nimport componentStyle from '../common/styles/Component.css'\nimport style from './Aside.css'\nimport '../tooltip/Tooltip.js'\n\nlet resizeTooltipIdCounter = 0\n\nexport type AsideHandleClick = 'toggle' | 'close' | 'none'\nexport type AsideHandleMode = 'col-resize' | 'pointer' | 'default'\n\n/**\n * Precomputed aside geometry — the host's bounding rect and computed\n * style. Hoisted once per coalesced frame and threaded into the cap\n * computations so a single frame doesn't re-read layout multiple times.\n * @internal\n */\ninterface AsideGeometry {\n rect: DOMRect\n styles: CSSStyleDeclaration\n}\n\n/**\n * Event dispatched when the aside's active drawer changes — on open,\n * switch, and close. `activeDrawer` is the new active drawer id (empty\n * string when all drawers closed); `previousDrawer` is the prior one.\n */\nexport class AsideActiveDrawerChangeEvent extends NordEvent {\n declare activeDrawer: string\n declare previousDrawer: string\n\n constructor(activeDrawer: string, previousDrawer: string) {\n super('active-drawer-change')\n Object.defineProperties(this, {\n activeDrawer: { value: activeDrawer, enumerable: true, writable: false },\n previousDrawer: { value: previousDrawer, enumerable: true, writable: false },\n })\n }\n}\n\n/**\n * Event dispatched when a drawer opens from the fully-closed state.\n * `activeDrawer` is the id of the drawer that opened.\n */\nexport class AsideDrawerOpenEvent extends NordEvent {\n declare activeDrawer: string\n\n constructor(activeDrawer: string) {\n super('drawer-open')\n Object.defineProperty(this, 'activeDrawer', {\n value: activeDrawer,\n enumerable: true,\n writable: false,\n })\n }\n}\n\n/**\n * Event dispatched when all drawers close. `previousDrawer` is the id of\n * the drawer that was open before closing.\n */\nexport class AsideDrawerCloseEvent extends NordEvent {\n declare previousDrawer: string\n\n constructor(previousDrawer: string) {\n super('drawer-close')\n Object.defineProperty(this, 'previousDrawer', {\n value: previousDrawer,\n enumerable: true,\n writable: false,\n })\n }\n}\n\n/**\n * Event dispatched when the active drawer's width changes. `drawerId`\n * names the specific drawer whose width changed (unlike the\n * active-drawer-change event, which describes a transition between\n * drawers); `width` is the new width in px.\n */\nexport class AsideDrawerResizeEvent extends NordEvent {\n declare drawerId: string\n declare width: number\n\n constructor(drawerId: string, width: number) {\n super('drawer-resize')\n Object.defineProperties(this, {\n drawerId: { value: drawerId, enumerable: true, writable: false },\n width: { value: width, enumerable: true, writable: false },\n })\n }\n}\n\n/**\n * Event dispatched when the resize handle's interaction mode changes.\n * `mode` is the new mode; `previous` is the prior mode.\n */\nexport class AsideHandleModeChangeEvent extends NordEvent {\n declare mode: AsideHandleMode\n declare previous: AsideHandleMode\n\n constructor(mode: AsideHandleMode, previous: AsideHandleMode) {\n super('handle-mode-change')\n Object.defineProperties(this, {\n mode: { value: mode, enumerable: true, writable: false },\n previous: { value: previous, enumerable: true, writable: false },\n })\n }\n}\n\nconst NAV_RESIZE_STEP = 30\nconst RESIZE_DRAG_THRESHOLD = 4\n// Minimum strip of viewport (in px) that must remain visible on the\n// inline-start side when the drawer is being resized. Only applies when\n// the layout is NOT in rail collapse mode — in that case the nav's\n// right edge does the gating instead. See `getLeftCapViewportX`.\nconst DRAWER_VIEWPORT_LEFT_OFFSET = 54\n\n// Selector matching natively-focusable / explicitly-tabbable elements,\n// used to find focusable descendants inside the floating drawer for the\n// narrow-viewport focus trap.\nconst FOCUSABLE_SELECTOR = [\n 'a[href]',\n 'button',\n 'input',\n 'select',\n 'textarea',\n '[tabindex]',\n 'audio[controls]',\n 'video[controls]',\n '[contenteditable]:not([contenteditable=\"false\"])',\n 'details > summary:first-of-type',\n].join(',')\n\nconst mediaQuery\n = typeof matchMedia !== 'undefined'\n ? matchMedia('(min-width: 768px)')\n : ({\n matches: false,\n addEventListener() {\n /* noop */\n },\n removeEventListener() {\n /* noop */\n },\n } as unknown as MediaQueryList)\n\nconst warnedTriggers = new WeakSet<HTMLElement>()\n\n/**\n * Container for the right-side rail + drawer pattern in the app shell.\n *\n * @status new\n * @category structure\n * @slot rail - Free-form content placed in the rail column.\n * @slot tooltip-divider - Tooltip anchored to the resize handle on hover.\n * @slot - Default slot for `<nord-aside-drawer>` panels.\n *\n * @fires {AsideActiveDrawerChangeEvent} active-drawer-change - Dispatched\n * when `active-drawer` changes, with `activeDrawer` / `previousDrawer`\n * properties. Fires on every transition including open, switch, and close.\n * @fires {AsideDrawerOpenEvent} drawer-open - Dispatched when transitioning\n * from no active drawer to an active one (i.e. a drawer opened from the\n * closed state). Has an `activeDrawer` property.\n * @fires {AsideDrawerCloseEvent} drawer-close - Dispatched when all drawers\n * close (active-drawer went from a non-empty value to empty). Has a\n * `previousDrawer` property.\n * @fires {AsideDrawerResizeEvent} drawer-resize - Dispatched when the active\n * drawer's width changes, with `drawerId` (the specific drawer that changed)\n * and `width` properties. Unlike active-drawer-change, this names a single\n * drawer by id rather than describing a transition between drawers.\n * @fires {AsideHandleModeChangeEvent} handle-mode-change - Dispatched when\n * the resize handle's mode changes (`col-resize` / `pointer` / `default`).\n * Mirrors the current value to the host's `data-handle-mode` attribute so\n * consumers can also read it synchronously. Has `mode` / `previous` properties.\n *\n * @cssprop [--n-aside-rail-width=48px] - Controls the inline-size of the rail column.\n * @cssprop [--n-aside-rail-gap=var(--n-space-s)] - Controls the gap between the rail and the active drawer.\n * @cssprop [--n-aside-rail-background-color=var(--n-color-surface)] - Controls the background color of the rail column.\n * @cssprop [--n-aside-background-color=var(--n-color-background)] - Controls the background color of the aside container (matches the layout's main area by default).\n * @cssprop [--n-aside-drawers-margin-inline-start=0] - Inline-start margin of the drawers wrapper — creates a gap between the layout's main area and the active drawer card. The resize handle follows this margin so it stays at the drawer's edge.\n * @cssprop [--n-aside-rail-narrow-block-size=56px] - Block-size of the rail when it renders as a bottom bar at <768px.\n * @cssprop [--n-aside-rail-narrow-padding-inline=var(--n-space-m)] - Inline padding inside the bottom rail at <768px.\n * @cssprop [--n-aside-rail-narrow-padding-block=var(--n-space-s)] - Block padding inside the bottom rail at <768px.\n * @cssprop [--n-aside-rail-narrow-border-block-start-width=1px] - Top border width of the bottom rail at <768px. Set to 0 to remove.\n * @cssprop [--n-aside-rail-narrow-border-color=var(--n-aside-rail-border-color)] - Top border color of the bottom rail at <768px.\n * @cssprop [--n-aside-drawer-floating-inset-block-start=var(--n-layout-header-size, 60px)] - Distance from the viewport top to the floating drawer at <768px (clears the layout header).\n * @cssprop [--n-aside-drawer-floating-inset-inline=0px] - Inline gap between the floating drawer and each viewport edge at <768px. Default 0 = full viewport width.\n * @cssprop [--n-aside-drawer-slide-duration=300ms] - Duration of the slide-up animation when a drawer opens at <768px.\n * @cssprop [--n-aside-drawer-slide-easing=cubic-bezier(0.4, 0, 0.2, 1)] - Easing of the slide-up animation when a drawer opens at <768px.\n * @cssprop [--n-aside-drawer-floating-inset-block-end=calc(rail-size - bottom-offset)] - Distance from the viewport bottom to the floating drawer at <768px. By default the drawer's bottom edge meets the rail's top edge.\n * @cssprop [--n-aside-drawer-floating-bottom-offset=0] - Subtracts from the floating drawer's `inset-block-end` to let the drawer extend past the rail's top edge (visual overlap). Default 0.\n * @cssprop [--n-aside-backdrop-background=rgb(0 0 0 / 0.4)] - Background color of the backdrop behind the floating drawer at <768px.\n * @cssprop [--n-aside-z-backdrop=calc(var(--n-index-top-bar) + 2)] - z-index of the backdrop at <768px (above the sticky footer so it dims the footer too).\n * @cssprop [--n-aside-z-drawer-floating=calc(var(--n-index-top-bar) + 3)] - z-index of the floating drawer overlay at <768px (above the backdrop).\n * @cssprop [--n-aside-z-rail-narrow=calc(var(--n-index-top-bar) + 4)] - z-index of the bottom rail at <768px (above the drawer so it remains tappable).\n */\n@customElement('nord-aside')\nexport default class Aside extends LitElement {\n static styles = [componentStyle, style]\n\n /**\n * The id of the currently-active `<nord-aside-drawer>`. Empty string\n * means no drawer is open. Setting to an unknown / disabled id is a\n * silent no-op.\n */\n @property({ reflect: true, attribute: 'active-drawer' }) activeDrawer: string = ''\n\n /**\n * Width of the rail column in px.\n */\n @property({ reflect: true, type: Number, attribute: 'rail-width' }) railWidth: number = 48\n\n /**\n * Gap between rail and the active drawer panel in px.\n */\n @property({ reflect: true, type: Number, attribute: 'rail-gap' }) railGap: number = 0\n\n /**\n * Controls what clicking (no-drag) the resize handle does.\n * Drag-for-resize is independent of this attribute.\n */\n @property({ reflect: true, attribute: 'handle-click' }) handleClick: AsideHandleClick = 'toggle'\n\n /**\n * Session-only memory of the most-recent non-empty `active-drawer`.\n * Does not persist across page reload. Used by the resize-handle\n * click in `toggle` mode to reopen the last drawer.\n * @internal\n */\n @state() private lastDrawer: string = ''\n\n @state() private isDragging: boolean = false\n\n @state() private wideScreen: boolean = mediaQuery.matches\n\n @state() private hasRailItems: boolean = false\n\n @query('.n-resize') private handleEl?: HTMLButtonElement\n @query('.n-resize-tooltip') private resizeTooltipEl?: HTMLElement\n\n private tooltipSlot = new SlotController(this, 'tooltip-divider')\n private readonly resizeTooltipId = `n-aside-resize-tip-${++resizeTooltipIdCounter}`\n\n private childObserver?: MutationObserver\n\n private dragStartX = 0\n private hasDragged = false\n\n render() {\n const handleState = this.handleInteractive\n const showTooltip = handleState.tooltip && !this.isDragging\n // Reflect the resize handle's current mode on the host so consumers\n // (story / app code) can react — e.g. hide a \"Drag to resize\" line\n // when the active drawer is pinned or fullscreen.\n if (this.dataset.handleMode !== handleState.cursor) {\n const previous = this.dataset.handleMode as AsideHandleMode | undefined\n this.dataset.handleMode = handleState.cursor\n if (previous !== undefined) {\n this.dispatchEvent(new AsideHandleModeChangeEvent(handleState.cursor, previous))\n }\n }\n return html`\n <div class=${classMap({ 'n-aside': true, 'n-dragging': this.isDragging })} part=\"aside\">\n <button\n type=\"button\"\n class=\"n-resize\"\n part=\"resize\"\n role=\"separator\"\n aria-orientation=\"vertical\"\n aria-label=\"Resize drawer\"\n aria-disabled=${handleState.enabled ? 'false' : 'true'}\n aria-describedby=${showTooltip ? this.resizeTooltipId : nothing}\n data-handle-mode=${handleState.cursor}\n tabindex=${handleState.dragOk ? '0' : '-1'}\n ?hidden=${!this.wideScreen || !this.activeDrawer}\n @pointerdown=${this.startDragging}\n @pointermove=${this.isDragging ? this.handleDrag : this.handleResizeHoverMove}\n @pointerleave=${this.handlePointerLeave}\n @pointerup=${this.stopDragging}\n @keydown=${this.handleKeyboardResize}\n ></button>\n ${this.tooltipSlot.hasContent\n ? html`\n <nord-tooltip\n id=${this.resizeTooltipId}\n position=\"inline-start\"\n class=\"n-resize-tooltip\"\n >\n <slot name=\"tooltip-divider\"></slot>\n </nord-tooltip>\n `\n : nothing}\n <div class=\"n-aside-rail\" part=\"rail\">\n <slot name=\"rail\" @slotchange=${this.handleRailSlotChange}></slot>\n </div>\n <div class=\"n-aside-drawers\" part=\"drawers\">\n <slot></slot>\n </div>\n <div\n class=\"n-aside-backdrop\"\n part=\"backdrop\"\n aria-hidden=\"true\"\n @click=${this.handleBackdropClick}\n ></div>\n </div>\n `\n }\n\n private handleRailSlotChange = (e: Event) => {\n const slot = e.target as HTMLSlotElement\n const next = slot.assignedElements({ flatten: true }).length > 0\n if (next !== this.hasRailItems) {\n this.hasRailItems = next\n this.syncNarrowState()\n }\n }\n\n private handleBackdropClick = () => {\n if (this.activeDrawer) {\n this.lastDrawer = this.activeDrawer\n this.activeDrawer = ''\n }\n }\n\n connectedCallback() {\n super.connectedCallback()\n this.applyRailVars()\n this.observeChildren()\n mediaQuery.addEventListener('change', this.handleMediaChange)\n this.addEventListener('width-change', this.handleDrawerWidthChange)\n this.addEventListener('keydown', this.handleHostKeyDown)\n window.addEventListener('resize', this.handleViewportResize, { passive: true })\n // When slotted into <nord-layout slot=\"drawer\">, push the aside's\n // outer width into `--n-layout-drawer-inline-size` so the layout's\n // drawer slot grows/shrinks with the active drawer.\n queueMicrotask(() => {\n this.attachLayoutSync()\n this.syncNarrowState()\n })\n }\n\n disconnectedCallback() {\n super.disconnectedCallback()\n this.childObserver?.disconnect()\n this.childObserver = undefined\n mediaQuery.removeEventListener('change', this.handleMediaChange)\n this.removeEventListener('width-change', this.handleDrawerWidthChange)\n this.removeEventListener('keydown', this.handleHostKeyDown)\n window.removeEventListener('resize', this.handleViewportResize)\n this.clearNarrowState()\n this.detachLayoutSync()\n }\n\n private handleMediaChange = (e: MediaQueryListEvent) => {\n this.wideScreen = e.matches\n this.syncNarrowState()\n // Recompute the layout's drawer inline-size — at narrow it's pinned\n // to 0 so the aside overlay doesn't reserve space; going wide must\n // restore the rail + drawer outer width so the layout reveals the\n // drawer slot column.\n this.syncLayoutDrawerSize()\n }\n\n /**\n * Esc closes the active drawer when it's floating (narrow + open).\n * Wide-screen handling is unchanged — Esc has no effect at desktop.\n */\n private handleHostKeyDown = (e: KeyboardEvent) => {\n if (e.key !== 'Escape')\n return\n if (this.wideScreen)\n return\n if (!this.activeDrawer)\n return\n // A nested overlay (dropdown / modal) inside the drawer may have\n // already handled this Esc and called preventDefault — don't steal it\n // to close the drawer underneath.\n if (e.defaultPrevented)\n return\n this.lastDrawer = this.activeDrawer\n this.activeDrawer = ''\n // Only swallow the event once we've actually closed a drawer, so an\n // Esc that does nothing here still propagates to ancestor handlers.\n e.stopPropagation()\n }\n\n /**\n * Compute narrow-mode state from viewport + slot + layout context, and\n * reflect two attributes on the aside host AND its containing layout:\n *\n * - `data-narrow-rail-bottom`: rail is a bottom-fixed bar.\n * Active when narrow + rail has slotted items.\n * - `data-narrow-floating-drawer`: drawer is a floating overlay.\n * Active when narrow + a drawer is active (regardless of collapse-mode\n * or fullscreen — fullscreen is neutralized at narrow).\n *\n * Also writes `--n-aside-rail-narrow-inset-block-end` on the host so\n * the rail lifts above the layout's sticky-footer when one is present.\n * Suspends `--n-layout-drawer-inline-size` (sets to 0) at narrow so\n * the layout's main area doesn't reserve drawer space.\n */\n /**\n * Tracks whether THIS aside currently holds a body-scroll lock so we\n * don't double-lock or leak a lock across disconnect. The lock helper\n * itself is ref-counted globally; this flag is per-instance bookkeeping.\n */\n private holdsBodyScrollLock = false\n\n private syncNarrowState() {\n const layout = this.hostLayout\n const isNarrow = !this.wideScreen\n const isRailBottom = isNarrow && this.hasRailItems\n const isFloatingDrawer = isNarrow && !!this.activeDrawer\n\n this.toggleAttribute('data-narrow-rail-bottom', isRailBottom)\n this.toggleAttribute('data-narrow-floating-drawer', isFloatingDrawer)\n layout?.toggleAttribute('data-narrow-rail-bottom', isRailBottom)\n layout?.toggleAttribute('data-narrow-floating-drawer', isFloatingDrawer)\n\n // Rail is always pinned to the viewport bottom at narrow — including\n // when there's a sticky footer (rail sits on top of the footer, its\n // higher z-index keeps it tappable). Clear any prior lift value.\n this.style.removeProperty('--n-aside-rail-narrow-inset-block-end')\n\n // Lock body scroll while a drawer is floating at narrow. iOS Safari\n // doesn't honor `overflow: hidden` on `<body>` for touch scroll, so\n // the helper uses `position: fixed; top: -<scrollY>px` — the\n // canonical fix. Per-instance flag prevents double-lock; the helper\n // itself ref-counts globally so multiple overlay components coexist.\n if (isFloatingDrawer && !this.holdsBodyScrollLock) {\n lockBodyScroll()\n this.holdsBodyScrollLock = true\n }\n else if (!isFloatingDrawer && this.holdsBodyScrollLock) {\n unlockBodyScroll()\n this.holdsBodyScrollLock = false\n }\n\n // Focus management for the narrow floating overlay. The drawer becomes\n // a modal-like dialog: capture the activating trigger, move focus in,\n // trap Tab, and restore focus on close. Only at narrow + floating —\n // the wide-screen docked drawer is part of the page flow and never\n // traps focus.\n if (isFloatingDrawer && !this.floatingFocusActive) {\n this.enterFloatingFocus()\n }\n else if (!isFloatingDrawer && this.floatingFocusActive) {\n this.exitFloatingFocus()\n }\n }\n\n /**\n * Tracks whether the narrow floating-drawer focus trap is currently\n * engaged, so `syncNarrowState` only enters / exits on a real\n * transition (mirrors `holdsBodyScrollLock`).\n */\n private floatingFocusActive = false\n\n /**\n * The element that was focused when the floating drawer opened (the\n * activating trigger). Focus is returned here on close if it's still\n * connected.\n */\n private floatingFocusReturnElement: HTMLElement | null = null\n\n /**\n * The drawer currently held in the floating focus trap. Cached so\n * `exitFloatingFocus` can clear the contract `floating` flag and the\n * fallback `tabindex` even after `activeDrawer` has already changed.\n */\n private trappedDrawer: AsideDrawer | null = null\n\n /**\n * Engage the floating-drawer focus trap. SSR-safe: only runs when a\n * document is available (it's only ever called from `syncNarrowState`,\n * which runs in client lifecycle, but guard anyway).\n */\n private enterFloatingFocus() {\n if (typeof document === 'undefined')\n return\n const drawer = this.activeDrawerEl\n if (!drawer)\n return\n\n this.floatingFocusActive = true\n this.trappedDrawer = drawer\n // Flip the contract property so the drawer host advertises itself as\n // a modal dialog (role=dialog + aria-modal) while floating.\n drawer.floating = true\n\n // Capture the activating trigger (shadow-aware) so we can return\n // focus to it on close.\n const focused = getFocusedElement(document)\n this.floatingFocusReturnElement = focused instanceof HTMLElement ? focused : null\n\n document.addEventListener('keydown', this.handleFloatingFocusKeydown, true)\n\n // Defer focus to rAF: syncNarrowState runs synchronously during a Lit\n // property set, before updateDrawerVisibility has removed `hidden` in\n // cases where the aside connects with activeDrawer already set in HTML.\n // focus() on a hidden element is a browser no-op — screen readers never\n // receive the dialog entry announcement. The rAF ensures the DOM is\n // visible before we move focus.\n requestAnimationFrame(() => {\n const focusTarget = this.firstFocusableInDrawer(drawer)\n if (focusTarget) {\n focusTarget.focus()\n }\n else {\n drawer.tabIndex = -1\n drawer.focus()\n }\n })\n }\n\n /**\n * Release the floating-drawer focus trap and restore focus to the\n * captured trigger if it's still connected.\n */\n private exitFloatingFocus() {\n this.floatingFocusActive = false\n if (typeof document !== 'undefined') {\n document.removeEventListener('keydown', this.handleFloatingFocusKeydown, true)\n }\n\n const drawer = this.trappedDrawer\n if (drawer) {\n drawer.floating = false\n // Remove the fallback tabindex we may have added so the drawer\n // doesn't linger as a tab stop once it's docked again.\n if (drawer.getAttribute('tabindex') === '-1') {\n drawer.removeAttribute('tabindex')\n }\n }\n this.trappedDrawer = null\n\n const returnElement = this.floatingFocusReturnElement\n this.floatingFocusReturnElement = null\n if (returnElement && returnElement.isConnected) {\n returnElement.focus()\n }\n }\n\n /**\n * Trap Tab / Shift+Tab within the floating drawer. Focusable elements\n * are queried live each keystroke because the drawer's content lives in\n * slotted light DOM and can change. Wraps from last to first and back.\n */\n private handleFloatingFocusKeydown = (e: KeyboardEvent) => {\n if (e.key !== 'Tab')\n return\n const drawer = this.trappedDrawer\n if (!drawer)\n return\n const focusable = this.focusableElementsInDrawer(drawer)\n if (focusable.length === 0) {\n // Nothing tabbable inside — keep focus pinned on the drawer host.\n e.preventDefault()\n drawer.tabIndex = -1\n drawer.focus()\n return\n }\n const first = focusable[0]\n const last = focusable[focusable.length - 1]\n const active = getFocusedElement(document)\n if (e.shiftKey) {\n if (active === first || !this.drawerContainsFocus(drawer, active)) {\n e.preventDefault()\n last.focus()\n }\n }\n else if (active === last || !this.drawerContainsFocus(drawer, active)) {\n e.preventDefault()\n first.focus()\n }\n }\n\n /**\n * Whether the currently-focused element lives inside the drawer's\n * subtree (light DOM or nested shadow roots). Used to redirect focus\n * back into the trap when Tab would otherwise escape it.\n */\n private drawerContainsFocus(drawer: AsideDrawer, active: Element | undefined): boolean {\n return active != null && (drawer === active || drawer.contains(active))\n }\n\n /**\n * Query the drawer's currently-focusable descendants in DOM order.\n * Slotted light DOM, so a live `querySelectorAll` reflects the latest\n * content. Filters out disabled / hidden / non-tabbable elements.\n */\n private focusableElementsInDrawer(drawer: AsideDrawer): HTMLElement[] {\n const candidates = drawer.querySelectorAll<HTMLElement>(FOCUSABLE_SELECTOR)\n return Array.from(candidates).filter(element => this.isFocusable(element))\n }\n\n private firstFocusableInDrawer(drawer: AsideDrawer): HTMLElement | null {\n return this.focusableElementsInDrawer(drawer)[0] ?? null\n }\n\n private isFocusable(element: HTMLElement): boolean {\n if (element.hasAttribute('disabled'))\n return false\n if (element.getAttribute('aria-hidden') === 'true')\n return false\n if (element.tabIndex < 0)\n return false\n // `offsetParent` is null for `display: none` (and fixed-position\n // edge cases handled well enough here) — skip non-rendered elements.\n return element.offsetParent !== null || element.getClientRects().length > 0\n }\n\n private clearNarrowState() {\n const layout = this.hostLayout\n this.removeAttribute('data-narrow-rail-bottom')\n this.removeAttribute('data-narrow-floating-drawer')\n layout?.removeAttribute('data-narrow-rail-bottom')\n layout?.removeAttribute('data-narrow-floating-drawer')\n this.style.removeProperty('--n-aside-rail-narrow-inset-block-end')\n // Release any body-scroll lock this instance held so disconnects\n // don't leak the lock count.\n if (this.holdsBodyScrollLock) {\n unlockBodyScroll()\n this.holdsBodyScrollLock = false\n }\n // Tear down the floating focus trap so disconnect doesn't leak the\n // document-level keydown listener.\n if (this.floatingFocusActive) {\n this.exitFloatingFocus()\n }\n }\n\n /**\n * Re-broadcast a drawer's local `width-change` as the aside's\n * `drawer-resize` so consumers only need one listener.\n */\n private handleDrawerWidthChange = (e: Event) => {\n const widthChangeEvent = e as AsideDrawerWidthChangeEvent\n const target = e.target\n if (!(target instanceof HTMLElement) || target.tagName !== 'NORD-ASIDE-DRAWER') {\n return\n }\n this.dispatchEvent(new AsideDrawerResizeEvent(target.id, widthChangeEvent.width))\n this.syncLayoutDrawerSize()\n }\n\n /**\n * Cached references to the listened-to layout. Cleared on disconnect.\n */\n private syncedLayout?: HTMLElement\n\n private get hostLayout(): HTMLElement | null {\n // Use the static `slot` HTML attribute rather than `assignedSlot.name`.\n // `assignedSlot` is set by the browser after the slot-assignment\n // algorithm runs, which can race with the layout's async first render\n // (Lit) or Vue's mount cycle — leaving it null when our handlers fire.\n // The `slot` attribute is set declaratively in HTML and is reliable.\n if (this.getAttribute('slot') !== 'drawer')\n return null\n return this.closest('nord-layout') as HTMLElement | null\n }\n\n /**\n * When slotted into `<nord-layout slot=\"drawer\">`, push the aside's\n * outer width into `--n-layout-drawer-inline-size` so the layout's\n * drawer slot grows/shrinks to fit. No-op when the aside isn't slotted\n * into a layout.\n */\n private attachLayoutSync() {\n const layout = this.hostLayout\n if (!layout)\n return\n this.syncedLayout = layout\n this.syncLayoutDrawerSize()\n // React to the layout's nav state — both the discrete events fired\n // at the start of an open/close, and the through-transition frames\n // caught via a ResizeObserver on the nav element itself. Both feed\n // `scheduleLeftCapReconcile` so a docked / fullscreen drawer follows\n // the freed / consumed left-side space in lockstep.\n layout.addEventListener('nav-open-change', this.handleLayoutNavChange)\n layout.addEventListener('nav-resize', this.handleLayoutNavChange)\n const nav = layout.querySelector('nord-navigation')\n if (nav && typeof ResizeObserver !== 'undefined') {\n this.navResizeObserver = new ResizeObserver(() => this.scheduleLeftCapReconcile())\n this.navResizeObserver.observe(nav)\n }\n }\n\n private detachLayoutSync() {\n if (!this.syncedLayout)\n return\n this.syncedLayout.removeEventListener('nav-open-change', this.handleLayoutNavChange)\n this.syncedLayout.removeEventListener('nav-resize', this.handleLayoutNavChange)\n this.navResizeObserver?.disconnect()\n this.navResizeObserver = undefined\n this.syncedLayout = undefined\n }\n\n /**\n * Push the aside's intended outer width (rail + active drawer + margins\n * + gap) into `--n-layout-drawer-inline-size` so the layout's drawer\n * slot wrapper grows/shrinks to fit. Also pins\n * `--n-layout-drawer-min-inline-size` to the rail's outer width so\n * narrow viewports (where the layout would otherwise drop the content\n * margin and overlay the rail) still reserve room for the rail.\n */\n private syncLayoutDrawerSize() {\n const layout = this.hostLayout\n if (!layout)\n return\n // At narrow viewports the aside renders as an overlay (bottom rail,\n // floating drawer) — it must not reserve inline space in the\n // layout's main area.\n if (!this.wideScreen) {\n layout.style.setProperty('--n-layout-drawer-inline-size', '0px')\n layout.style.setProperty('--n-layout-drawer-min-inline-size', '0px')\n return\n }\n let total = this.railWidth\n if (this.activeDrawer) {\n const active = this.findDrawer(this.activeDrawer)\n const drawerWidth = active?.width ?? active?.defaultWidth ?? 0\n if (drawerWidth) {\n const style = active ? getComputedStyle(active) : null\n const marginStart = style ? Number.parseFloat(style.marginInlineStart) || 0 : 0\n const marginEnd = style ? Number.parseFloat(style.marginInlineEnd) || 0 : 0\n // `.n-aside-drawers` carries the inline-start margin that creates\n // the gap between the layout's main area and the drawer card.\n // Counted into the slot width so the main content's margin-inline-end\n // grows to reveal that gap.\n const drawersWrapper = this.renderRoot.querySelector('.n-aside-drawers')\n const drawersMarginStart = drawersWrapper\n ? Number.parseFloat(getComputedStyle(drawersWrapper).marginInlineStart) || 0\n : 0\n total += drawerWidth + marginStart + marginEnd + drawersMarginStart + this.railGap\n }\n }\n layout.style.setProperty('--n-layout-drawer-inline-size', `${total}px`)\n layout.style.setProperty('--n-layout-drawer-min-inline-size', `${this.railWidth}px`)\n }\n\n /**\n * Surface `rail-width` / `rail-gap` as custom properties so the slotted\n * rail content can react without the host needing a re-render.\n */\n @observe('railWidth')\n @observe('railGap')\n protected applyRailVars() {\n this.style.setProperty('--_n-aside-rail-width', `${this.railWidth}px`)\n this.style.setProperty('--_n-aside-rail-gap', `${this.railGap}px`)\n this.syncLayoutDrawerSize()\n }\n\n @observe('activeDrawer')\n protected handleActiveDrawerChange(previousDrawer: string, activeDrawer: string) {\n if (previousDrawer === activeDrawer) {\n return\n }\n\n if (activeDrawer) {\n const drawer = this.findDrawer(activeDrawer)\n if (!drawer || drawer.disabled) {\n this.activeDrawer = previousDrawer\n return\n }\n this.lastDrawer = activeDrawer\n }\n\n this.updateDrawerVisibility()\n this.syncLayoutDrawerSize()\n this.scheduleLeftCapReconcile()\n this.syncNarrowState()\n\n this.dispatchEvent(new AsideActiveDrawerChangeEvent(activeDrawer, previousDrawer))\n\n // Specialized lifecycle events for consumers that only care about\n // \"did any drawer just open?\" / \"did all drawers just close?\".\n if (activeDrawer && !previousDrawer) {\n this.dispatchEvent(new AsideDrawerOpenEvent(activeDrawer))\n }\n else if (!activeDrawer && previousDrawer) {\n this.dispatchEvent(new AsideDrawerCloseEvent(previousDrawer))\n }\n }\n\n private findDrawer(id: string): AsideDrawer | null {\n return id\n ? this.querySelector<AsideDrawer>(\n `:scope > nord-aside-drawer#${CSS.escape(id)}`,\n )\n : null\n }\n\n private get activeDrawerEl(): AsideDrawer | null {\n return this.activeDrawer ? this.findDrawer(this.activeDrawer) : null\n }\n\n private updateDrawerVisibility() {\n const drawers = this.querySelectorAll<AsideDrawer>(':scope > nord-aside-drawer')\n drawers.forEach((drawer) => {\n const isActive = !!this.activeDrawer && drawer.id === this.activeDrawer\n drawer.toggleAttribute('hidden', !isActive)\n })\n }\n\n private observeChildren() {\n this.childObserver = new MutationObserver((mutations) => {\n let needsVisibilityRefresh = false\n let needsFullscreenSync = false\n\n for (const mutation of mutations) {\n if (mutation.type === 'childList') {\n needsVisibilityRefresh = true\n needsFullscreenSync = true\n continue\n }\n if (mutation.type !== 'attributes') {\n continue\n }\n const target = mutation.target as HTMLElement\n // subtree:true means we also see attribute mutations on deep\n // descendants — filter to direct nord-aside-drawer children only.\n if (target.tagName !== 'NORD-ASIDE-DRAWER' || target.parentElement !== this) {\n continue\n }\n if (mutation.attributeName === 'id' && mutation.oldValue && this.activeDrawer === mutation.oldValue) {\n this.activeDrawer = target.id\n if (this.lastDrawer === mutation.oldValue) {\n this.lastDrawer = target.id\n }\n }\n if (mutation.attributeName === 'fullscreen' || mutation.attributeName === 'data-animating-fullscreen') {\n needsFullscreenSync = true\n }\n needsVisibilityRefresh = true\n }\n\n if (needsVisibilityRefresh) {\n if (this.activeDrawer && !this.findDrawer(this.activeDrawer)) {\n this.activeDrawer = ''\n }\n else {\n this.updateDrawerVisibility()\n }\n }\n if (needsFullscreenSync) {\n this.syncFullscreenAttr()\n }\n })\n\n // subtree:true so attribute changes on direct children (id, disabled,\n // fullscreen, data-animating-fullscreen) fire too. The callback\n // filters to direct `nord-aside-drawer` children, so descendants are\n // ignored. `fullscreen` + `data-animating-fullscreen` drive\n // `syncFullscreenAttr`, which reflects a `[data-fullscreen]` attr on\n // the host so CSS can react reliably (more robust than `:has()`\n // reactivity for attribute changes across some engines).\n this.childObserver.observe(this, {\n childList: true,\n subtree: true,\n attributes: true,\n attributeFilter: ['id', 'disabled', 'fullscreen', 'data-animating-fullscreen'],\n attributeOldValue: true,\n })\n\n this.updateDrawerVisibility()\n this.syncFullscreenAttr()\n }\n\n /**\n * Reflect a `data-fullscreen` attribute on the host whenever any\n * direct `<nord-aside-drawer>` child is currently fullscreen OR\n * transitioning into/out of fullscreen. CSS selectors target this\n * attribute to bump the rail above the fullscreen drawer's z-index.\n */\n private syncFullscreenAttr() {\n const drawers = this.querySelectorAll<AsideDrawer>(':scope > nord-aside-drawer')\n let hasFullscreen = false\n drawers.forEach((d) => {\n if (d.hasAttribute('fullscreen') || d.hasAttribute('data-animating-fullscreen')) {\n hasFullscreen = true\n }\n })\n this.toggleAttribute('data-fullscreen', hasFullscreen)\n // Drive the active drawer's fullscreen inline-size from the layout\n // context: when entering fullscreen (or its size requirements change),\n // override AsideDrawer's max-width-based default with the left-cap\n // derived value. When leaving fullscreen, clear the override.\n const active = this.activeDrawerEl\n if (active?.hasAttribute('fullscreen')) {\n this.scheduleLeftCapReconcile()\n }\n else {\n this.clearFullscreenSizeOverride()\n }\n }\n\n /* ---------------------------------------------\n / DRAG / CLICK ON THE RESIZE HANDLE\n / --------------------------------------------- */\n\n private startDragging = (e: PointerEvent) => {\n if (e.button !== 0 || !this.wideScreen) {\n return\n }\n const target = e.currentTarget as HTMLElement\n // setPointerCapture throws on synthetic PointerEvents (tests) — the\n // capture is a nice-to-have for real pointers, not load-bearing.\n try {\n target.setPointerCapture(e.pointerId)\n }\n catch {\n /* synthetic event, no real pointer to capture */\n }\n this.isDragging = true\n this.dragStartX = e.clientX\n this.hasDragged = false\n // Cache the aside's geometry for the whole drag: only `clientX`\n // changes between pointermove frames, so the bounding rect (right\n // edge) and computed style (rail/gap inputs, layout floor vars) are\n // stable. Reading them once at drag-start avoids two forced layout\n // reflows per pointermove.\n this.dragGeometry = {\n rect: this.getBoundingClientRect(),\n styles: getComputedStyle(this),\n }\n }\n\n /**\n * Aside geometry captured at `startDragging` and reused for every\n * pointermove until `stopDragging`. Cleared on drag end.\n */\n private dragGeometry?: AsideGeometry\n\n private stopDragging = (e?: PointerEvent) => {\n if (!this.isDragging) {\n return\n }\n const wasClick = !this.hasDragged\n this.isDragging = false\n this.hasDragged = false\n this.dragGeometry = undefined\n if (e) {\n const target = e.currentTarget as HTMLElement\n try {\n if (target.hasPointerCapture(e.pointerId)) {\n target.releasePointerCapture(e.pointerId)\n }\n }\n catch {\n /* synthetic event */\n }\n }\n if (wasClick) {\n this.handleHandleClick()\n }\n }\n\n private handlePointerLeave = (e: PointerEvent) => {\n // If we're mid-drag and the pointer escapes without capture, treat as up.\n if (this.isDragging) {\n this.stopDragging(e)\n }\n this.resizeTooltipEl?.style.removeProperty('--n-resize-tooltip-y')\n }\n\n /**\n * Follow the pointer vertically so the tooltip floats next to the cursor\n * along the handle's full height instead of staying anchored to its\n * midpoint. Mirrors `<nord-layout>`'s handleResizeHoverMove.\n */\n private handleResizeHoverMove = (e: PointerEvent) => {\n const target = e.currentTarget as HTMLElement | null\n if (!target || !this.resizeTooltipEl) {\n return\n }\n const rect = target.getBoundingClientRect()\n const offset = e.clientY - (rect.top + rect.height / 2)\n this.resizeTooltipEl.style.setProperty('--n-resize-tooltip-y', `${offset}px`)\n }\n\n private handleDrag = (e: PointerEvent) => {\n if (!this.activeDrawerEl) {\n // Drag with no active drawer: just track that the pointer moved\n // past the threshold so stopDragging treats this as a drag, not click.\n if (Math.abs(e.clientX - this.dragStartX) >= RESIZE_DRAG_THRESHOLD) {\n this.hasDragged = true\n }\n return\n }\n if (!this.hasDragged && Math.abs(e.clientX - this.dragStartX) < RESIZE_DRAG_THRESHOLD) {\n return\n }\n this.hasDragged = true\n\n const drawer = this.activeDrawerEl\n if (drawer.isPinned) {\n return\n }\n // Geometry was cached at drag-start (only clientX changes mid-drag).\n // Fall back to a live read for the unusual case of a drag without a\n // start (e.g. a synthetic pointermove in a test).\n const geometry = this.dragGeometry ?? {\n rect: this.getBoundingClientRect(),\n styles: getComputedStyle(this),\n }\n // Aside is on the right side: width grows as the pointer moves left.\n const desired = geometry.rect.right - e.clientX - this.railWidth - this.railGap\n // Clamp at the left-cap (nav's right edge OR the viewport-left\n // offset, per collapse-mode rules) so dragging can't push the\n // drawer over the layout's <nord-navigation> sidebar. ALSO reserves\n // `--n-layout-main-min-inline-size` (default 600px at >=1240px)\n // so the layout's main content body never collapses below the floor.\n // Skipped silently when there's no host layout.\n const leftCapWidth = this.drawerWidthAtCapWithMainFloor(geometry)\n const width = leftCapWidth !== undefined ? Math.min(desired, leftCapWidth) : desired\n drawer.setWidth(width, 'drag')\n }\n\n /**\n * Viewport-x where the drawer's inline-start edge must stop. Unified\n * rule that drives drag, keyboard, and fullscreen sizing:\n *\n * - `collapse-mode=\"rail\"` → `max(0, nav.right)`. Nav stays visible\n * as a rail, so the cap is the nav's right edge regardless of\n * open/closed state (rail width vs expanded width).\n * - otherwise → `max(nav.right, DRAWER_VIEWPORT_LEFT_OFFSET)`. The\n * nav can translate fully off-screen; the 54px viewport floor only\n * kicks in when the nav is actually off-screen — when the nav is\n * on-screen and wider than 54, its right edge wins.\n *\n * Returns `undefined` when no host layout is present.\n */\n private getLeftCapViewportX(): number | undefined {\n const layout = this.hostLayout\n if (!layout)\n return undefined\n const nav = layout.querySelector('nord-navigation') as HTMLElement | null\n const navRight = nav ? nav.getBoundingClientRect().right : 0\n return layout.getAttribute('collapse-mode') === 'rail'\n ? Math.max(0, navRight)\n : Math.max(navRight, DRAWER_VIEWPORT_LEFT_OFFSET)\n }\n\n /**\n * Derive the drawer's maximum inline-size from {@link getLeftCapViewportX}\n * WITHOUT reserving the layout's main-panel floor — the drawer grows\n * all the way to the layout's main-panel edge. Used by fullscreen sizing.\n *\n * Returns `undefined` when no host layout is present.\n */\n private drawerWidthAtCap(geometry?: AsideGeometry): number | undefined {\n return this.computeDrawerWidthAtCap(false, geometry)\n }\n\n /**\n * Derive the drawer's maximum inline-size from {@link getLeftCapViewportX}\n * WITH the layout's main-panel floor reserved — drag / keyboard resize\n * can never push the main body below `--n-layout-main-min-inline-size`\n * (default `0` below 1240px, `600px` above). Used by drag, keyboard, and\n * the drag-cap reconcile.\n *\n * Returns `undefined` when no host layout is present.\n */\n private drawerWidthAtCapWithMainFloor(geometry?: AsideGeometry): number | undefined {\n return this.computeDrawerWidthAtCap(true, geometry)\n }\n\n /**\n * Shared core for {@link drawerWidthAtCap} /\n * {@link drawerWidthAtCapWithMainFloor}. Subtracts:\n * - `--n-layout-main-min-inline-size` when `reserveMainMin === true`.\n *\n * Reads the aside's geometry (bounding rect + computed style) once;\n * callers in the same coalesced frame may pass a precomputed\n * {@link AsideGeometry} to avoid redundant layout reads.\n *\n * Returns `undefined` when no host layout is present.\n */\n private computeDrawerWidthAtCap(\n reserveMainMin: boolean,\n geometry?: AsideGeometry,\n ): number | undefined {\n const cap = this.getLeftCapViewportX()\n if (cap === undefined)\n return undefined\n const rect = geometry?.rect ?? this.getBoundingClientRect()\n const styles = geometry?.styles ?? getComputedStyle(this)\n const mainMin = reserveMainMin\n ? this.parseLengthVar(styles, '--n-layout-main-min-inline-size')\n : 0\n return Math.max(0, rect.right - this.railWidth - this.railGap - cap - mainMin)\n }\n\n private parseLengthVar(styles: CSSStyleDeclaration, name: string): number {\n return Number.parseFloat(styles.getPropertyValue(name)) || 0\n }\n\n /**\n * Publishes the layout's left-cap (the maximum the fullscreen drawer\n * can grow to without overlapping the nav) as\n * `--_n-aside-drawer-fullscreen-available` on the active drawer.\n *\n * The drawer's `:host([fullscreen])` rule uses CSS `min()` to clamp\n * its own ceiling (`--_n-aside-drawer-fullscreen-max`) by this\n * available value — so a consumer who sets `max-fullscreen-width`\n * higher than the layout cap can never overflow past the main-panel\n * edge. Pure-CSS resolution; this method just publishes the input.\n *\n * Coalesced via rAF so a flurry of nav/viewport/slot events collapses\n * to one write per frame. The host's geometry (bounding rect + computed\n * style) is read once per frame and threaded into both flushes so a\n * single frame never re-reads layout.\n */\n private scheduleLeftCapReconcile = () => {\n if (this.leftCapReconcileScheduled)\n return\n this.leftCapReconcileScheduled = true\n requestAnimationFrame(() => {\n this.leftCapReconcileScheduled = false\n // Read the aside's geometry once for the whole frame — both flushes\n // derive their caps from the same rect + computed style.\n const geometry: AsideGeometry = {\n rect: this.getBoundingClientRect(),\n styles: getComputedStyle(this),\n }\n this.flushFullscreenAvailable(geometry)\n this.flushDragCap(geometry)\n })\n }\n\n private leftCapReconcileScheduled = false\n\n private flushFullscreenAvailable(geometry: AsideGeometry) {\n const drawer = this.activeDrawerEl\n if (!drawer)\n return\n const available = this.drawerWidthAtCap(geometry)\n if (available === undefined) {\n drawer.style.removeProperty('--_n-aside-drawer-fullscreen-available')\n return\n }\n // `drawerWidthAtCap` already subtracts the inline-end offset\n // ONCE — which matches the `inset-inline-end` reservation. The\n // fullscreen drawer also needs the same clearance on its INLINE-START\n // (left edge) so its border doesn't paint flush against the nav.\n // Subtract one more `inlineEndOffset` here so the left edge lands at\n // `nav.right + offset` instead of `nav.right`. Additionally subtract\n // the drawer's own `border-inline-start-width` because the slotted\n // <nord-drawer>'s border is drawn outside its inline-size box and\n // would otherwise overflow past the cap by that width.\n const asideStyles = geometry.styles\n // The drawer's own border-inline-start (from `--n-aside-drawer-border-width`)\n // sits outside the slotted nord-drawer's inline-size box, so the\n // available width needs to account for it too. Read from the aside's\n // public token — which inherits down to the drawer.\n const drawerBorder = this.parseLengthVar(asideStyles, '--n-aside-drawer-border-width')\n drawer.style.setProperty(\n '--_n-aside-drawer-fullscreen-available',\n `${Math.max(0, available - drawerBorder)}px`,\n )\n }\n\n /**\n * If the active drawer's docked width now exceeds the drag cap (e.g.\n * the nav just expanded and the cap tightened), shrink the drawer\n * down to the new cap. Skipped while a drag is in flight so the\n * user's interaction stays unbroken; skipped for fullscreen drawers\n * since fullscreen has its own cap path. Reconciles the same events\n * `flushFullscreenAvailable` listens to.\n */\n private flushDragCap(geometry: AsideGeometry) {\n const drawer = this.activeDrawerEl\n if (!drawer || drawer.hasAttribute('fullscreen') || this.isDragging)\n return\n const cap = this.drawerWidthAtCapWithMainFloor(geometry)\n if (cap === undefined)\n return\n if (drawer.width > cap)\n drawer.setWidth(cap, 'attribute')\n }\n\n /**\n * Remove the published `--_n-aside-drawer-fullscreen-available` var\n * so the drawer's CSS `min()` rule falls back to `100vw` and the\n * drawer's own `--_n-aside-drawer-fullscreen-max` becomes the\n * effective cap. Called when the drawer leaves fullscreen.\n */\n private clearFullscreenSizeOverride() {\n this.activeDrawerEl?.style.removeProperty('--_n-aside-drawer-fullscreen-available')\n }\n\n private handleLayoutNavChange = () => {\n this.scheduleLeftCapReconcile()\n }\n\n private handleViewportResize = () => {\n this.scheduleLeftCapReconcile()\n }\n\n private navResizeObserver?: ResizeObserver\n\n /**\n * Apply the `handle-click` matrix. Called by `stopDragging` when the\n * pointer hasn't moved past the drag threshold.\n */\n private handleHandleClick() {\n if (this.handleClick === 'none') {\n return\n }\n const open = !!this.activeDrawer\n\n if (open) {\n // toggle + close: close (and stash) the active drawer\n this.lastDrawer = this.activeDrawer\n this.activeDrawer = ''\n return\n }\n\n if (this.handleClick !== 'toggle') {\n return\n }\n\n // toggle mode, no active drawer → reopen lastDrawer if valid, else\n // open the first DOM-order trigger whose drawer resolves + isn't disabled.\n if (this.lastDrawer) {\n const drawer = this.findDrawer(this.lastDrawer)\n if (drawer && !drawer.disabled) {\n this.activeDrawer = this.lastDrawer\n return\n }\n }\n const fallback = this.findFirstTriggerDrawer()\n if (fallback) {\n this.activeDrawer = fallback\n }\n }\n\n private findFirstTriggerDrawer(): string | null {\n const triggers = this.querySelectorAll<HTMLElement>('nord-aside-trigger[drawer]')\n for (const trigger of triggers) {\n const id = trigger.getAttribute('drawer')\n if (!id) {\n continue\n }\n const drawer = this.findDrawer(id)\n if (drawer && !drawer.disabled) {\n return id\n }\n if (!drawer && process.env.NODE_ENV !== 'production') {\n if (!warnedTriggers.has(trigger)) {\n warnedTriggers.add(trigger)\n\n console.warn(\n `<nord-aside-trigger drawer=\"${id}\"> points at a non-existent <nord-aside-drawer> — click is a no-op.`,\n )\n }\n }\n }\n return null\n }\n\n /* ---------------------------------------------\n / KEYBOARD RESIZE\n / --------------------------------------------- */\n\n private handleKeyboardResize = (e: KeyboardEvent) => {\n const drawer = this.activeDrawerEl\n if (!drawer || !this.wideScreen) {\n return\n }\n const leftCapWidth = this.drawerWidthAtCapWithMainFloor()\n const cap = (width: number) => leftCapWidth !== undefined ? Math.min(width, leftCapWidth) : width\n // Aside is on the right: ArrowLeft = wider, ArrowRight = narrower.\n switch (e.key) {\n case 'ArrowLeft':\n drawer.setWidth(cap(drawer.width + NAV_RESIZE_STEP), 'keyboard')\n break\n case 'ArrowRight':\n drawer.setWidth(drawer.width - NAV_RESIZE_STEP, 'keyboard')\n break\n case 'Home':\n drawer.setWidth(drawer.minWidth, 'keyboard')\n break\n case 'End':\n drawer.setWidth(cap(drawer.maxWidth), 'keyboard')\n break\n default:\n return\n }\n e.preventDefault()\n }\n\n /* ---------------------------------------------\n / UNIFIED AFFORDANCE RULE\n / --------------------------------------------- */\n\n /**\n * \"Is the handle interactive right now?\" — single computation that\n * drives cursor, tooltip-divider visibility, and `aria-disabled`.\n * @internal\n */\n get handleInteractive(): {\n enabled: boolean\n dragOk: boolean\n clickOk: boolean\n cursor: 'col-resize' | 'pointer' | 'default'\n tooltip: boolean\n } {\n if (!this.wideScreen) {\n return { enabled: false, dragOk: false, clickOk: false, cursor: 'default', tooltip: false }\n }\n const drawer = this.activeDrawerEl\n const open = !!drawer\n const dragOk = open && !drawer!.isPinned && !drawer!.fullscreen\n\n let clickOk: boolean\n switch (this.handleClick) {\n case 'toggle':\n clickOk = true // toggle works in both states (reopen lastDrawer when closed)\n break\n case 'close':\n clickOk = open\n break\n case 'none':\n default:\n clickOk = false\n break\n }\n\n const enabled = dragOk || clickOk\n let cursor: 'col-resize' | 'pointer' | 'default'\n if (dragOk) {\n cursor = 'col-resize'\n }\n else if (clickOk) {\n cursor = 'pointer'\n }\n else {\n cursor = 'default'\n }\n const tooltip = enabled && this.tooltipSlot.hasContent\n return { enabled, dragOk, clickOk, cursor, tooltip }\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'nord-aside': Aside\n }\n}\n"],"names":["lockCount","savedScrollY","savedBodyStyles","unlockBodyScroll","document","body","style","position","top","insetInlineStart","insetInlineEnd","inlineSize","overflow","window","scrollTo","resizeTooltipIdCounter","AsideActiveDrawerChangeEvent","NordEvent","constructor","activeDrawer","previousDrawer","super","Object","defineProperties","this","value","enumerable","writable","AsideDrawerOpenEvent","defineProperty","AsideDrawerCloseEvent","AsideDrawerResizeEvent","drawerId","width","AsideHandleModeChangeEvent","mode","previous","FOCUSABLE_SELECTOR","join","mediaQuery","matchMedia","matches","addEventListener","removeEventListener","WeakSet","Aside","LitElement","railWidth","railGap","handleClick","lastDrawer","isDragging","wideScreen","hasRailItems","tooltipSlot","SlotController","resizeTooltipId","dragStartX","hasDragged","handleRailSlotChange","e","next","target","assignedElements","flatten","length","syncNarrowState","handleBackdropClick","handleMediaChange","syncLayoutDrawerSize","handleHostKeyDown","key","defaultPrevented","stopPropagation","holdsBodyScrollLock","floatingFocusActive","floatingFocusReturnElement","trappedDrawer","handleFloatingFocusKeydown","drawer","focusable","focusableElementsInDrawer","preventDefault","tabIndex","focus","first","last","active","getFocusedElement","shiftKey","drawerContainsFocus","handleDrawerWidthChange","widthChangeEvent","HTMLElement","tagName","dispatchEvent","id","startDragging","button","currentTarget","setPointerCapture","pointerId","_a","clientX","dragGeometry","rect","getBoundingClientRect","styles","getComputedStyle","stopDragging","wasClick","undefined","hasPointerCapture","releasePointerCapture","handleHandleClick","handlePointerLeave","resizeTooltipEl","removeProperty","handleResizeHoverMove","offset","clientY","height","setProperty","handleDrag","activeDrawerEl","Math","abs","isPinned","geometry","desired","right","leftCapWidth","drawerWidthAtCapWithMainFloor","min","setWidth","scheduleLeftCapReconcile","leftCapReconcileScheduled","requestAnimationFrame","flushFullscreenAvailable","flushDragCap","handleLayoutNavChange","handleViewportResize","handleKeyboardResize","cap","minWidth","maxWidth","render","handleState","handleInteractive","showTooltip","tooltip","dataset","handleMode","cursor","html","classMap","enabled","nothing","dragOk","hasContent","connectedCallback","applyRailVars","observeChildren","passive","queueMicrotask","attachLayoutSync","disconnectedCallback","childObserver","disconnect","clearNarrowState","detachLayoutSync","layout","hostLayout","isNarrow","isRailBottom","isFloatingDrawer","toggleAttribute","scrollY","pageYOffset","lockBodyScroll","enterFloatingFocus","exitFloatingFocus","floating","focused","focusTarget","firstFocusableInDrawer","getAttribute","removeAttribute","returnElement","isConnected","contains","candidates","querySelectorAll","Array","from","filter","element","isFocusable","hasAttribute","offsetParent","getClientRects","closest","syncedLayout","nav","querySelector","ResizeObserver","navResizeObserver","observe","total","findDrawer","drawerWidth","_b","defaultWidth","marginStart","Number","parseFloat","marginInlineStart","marginEnd","marginInlineEnd","drawersWrapper","renderRoot","handleActiveDrawerChange","disabled","updateDrawerVisibility","CSS","escape","forEach","isActive","MutationObserver","mutations","needsVisibilityRefresh","needsFullscreenSync","mutation","type","parentElement","attributeName","oldValue","syncFullscreenAttr","childList","subtree","attributes","attributeFilter","attributeOldValue","drawers","hasFullscreen","d","clearFullscreenSizeOverride","getLeftCapViewportX","navRight","max","drawerWidthAtCap","computeDrawerWidthAtCap","reserveMainMin","mainMin","parseLengthVar","name","getPropertyValue","available","asideStyles","drawerBorder","fallback","findFirstTriggerDrawer","triggers","trigger","clickOk","open","fullscreen","componentStyle","__decorate","property","reflect","attribute","prototype","state","query","customElement"],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ import{_ as e}from"./tslib.es6-CmLYFWVC.js";import{css as i,LitElement as r,html as t,isServer as n}from"lit";import{property as a,state as d,customElement as s}from"lit/decorators.js";import{o as l}from"./observe-D0n0zOfU.js";import{N as o}from"./events-Bv6wNHwJ.js";import{c as h}from"./number-Dg2vCfGd.js";import{s as c}from"./storage-CGZ-YX4-.js";import{s as u}from"./Component-DSU3Qp0O.js";const w=i`:host{--_n-aside-drawer-inline-size:var(--n-aside-drawer-inline-size, 420px);--_n-aside-drawer-min-inline-size:var(--n-aside-drawer-min-inline-size, 280px);--_n-aside-drawer-max-inline-size:var(--n-aside-drawer-max-inline-size, none);--_n-aside-drawer-background-color:var(--n-aside-drawer-background-color, var(--n-color-surface));--_n-aside-drawer-border-color:var(--n-aside-drawer-border-color, var(--n-color-border));--_n-aside-drawer-border-width:var(--n-aside-drawer-border-width, 0);--_n-aside-drawer-border-inline-end-width:var(
2
+ --n-aside-drawer-border-inline-end-width,
3
+ var(--_n-aside-drawer-border-width)
4
+ );--_n-aside-drawer-border-block-start-width:var(
5
+ --n-aside-drawer-border-block-start-width,
6
+ var(--_n-aside-drawer-border-width)
7
+ );--_n-aside-drawer-border-block-end-width:var(
8
+ --n-aside-drawer-border-block-end-width,
9
+ var(--_n-aside-drawer-border-width)
10
+ );--_n-aside-drawer-border-radius:var(--n-aside-drawer-border-radius, 0);--_n-aside-drawer-box-shadow:var(--n-aside-drawer-box-shadow, var(--n-box-shadow-nav));--_n-aside-drawer-margin-block:var(--n-aside-drawer-margin-block, 0);--_n-aside-drawer-margin-inline-start:var(--n-aside-drawer-margin-inline-start, 0);--_n-aside-drawer-margin-inline-end:var(--n-aside-drawer-margin-inline-end, 0);--_n-aside-drawer-fullscreen-inset-block-start:var(
11
+ --n-aside-drawer-fullscreen-inset-block-start,
12
+ var(--n-layout-header-size, 0)
13
+ );--_n-aside-drawer-z-index:var(--n-aside-z-index, var(--n-index-nav));--_n-aside-drawer-fullscreen-z-index:var(--n-aside-z-index-fullscreen, var(--n-index-top-bar, 1000));display:block;position:relative;inline-size:var(--_n-aside-drawer-inline-size);min-inline-size:var(--_n-aside-drawer-min-inline-size);max-inline-size:var(--_n-aside-drawer-max-inline-size);margin-block-start:var(--_n-aside-drawer-margin-block);margin-block-end:var(--_n-aside-drawer-margin-block);margin-inline-start:var(--_n-aside-drawer-margin-inline-start);margin-inline-end:var(--_n-aside-drawer-margin-inline-end);background:var(--_n-aside-drawer-background-color);box-shadow:var(--_n-aside-drawer-box-shadow);box-sizing:border-box;z-index:var(--_n-aside-drawer-z-index)}.n-aside-drawer{block-size:100%;inline-size:100%}::slotted(*){block-size:100%}::slotted(nord-drawer){--n-drawer-border-color:var(--_n-aside-drawer-border-color);--n-drawer-border-radius:var(--_n-aside-drawer-border-radius);--n-drawer-border-inline-start-width:var(--_n-aside-drawer-border-width);--n-drawer-border-inline-end-width:var(--_n-aside-drawer-border-inline-end-width);--n-drawer-border-block-start-width:var(--_n-aside-drawer-border-block-start-width);--n-drawer-border-block-end-width:var(--_n-aside-drawer-border-block-end-width)}@media (min-width:768px){:host([data-animating-fullscreen]),:host([fullscreen]){position:fixed;inset-block-start:var(--_n-aside-drawer-fullscreen-inset-block-start);inset-block-end:var(--n-layout-footer-block-size,0);inset-inline-end:calc(var(--n-aside-rail-width,48px) + var(--_n-aside-rail-gap,0px));margin-inline:0;z-index:var(--_n-aside-drawer-fullscreen-z-index)}:host([fullscreen]){inline-size:min(var(--_n-aside-drawer-fullscreen-max,100vw),var(--_n-aside-drawer-fullscreen-available,100vw));max-inline-size:min(var(--_n-aside-drawer-fullscreen-max,100vw),var(--_n-aside-drawer-fullscreen-available,100vw));min-inline-size:0}}`;class b extends o{constructor(e,i){super("width-change"),Object.defineProperty(this,"width",{value:e,enumerable:!0,writable:!1}),Object.defineProperty(this,"source",{value:i,enumerable:!0,writable:!1})}}const m="nord-aside.";function v(e){return`${m}${e}.width`}let p=class extends r{constructor(){super(...arguments),this.defaultWidth=420,this.minWidth=280,this.maxWidth=Number.MAX_SAFE_INTEGER,this.maxFullscreenWidth=Number.MAX_SAFE_INTEGER,this.disabled=!1,this.fullscreen=!1,this.floating=!1,this.width=420,this.widthSeeded=!1,this.fullscreenSeeded=!1,this.observedWidth=0}render(){return t`<div class="n-aside-drawer" part="drawer"><slot></slot></div>`}connectedCallback(){super.connectedCallback(),this.applyFloatingRole(),this.applyAriaLabelledby(),n||(this.observedWidth=this.getBoundingClientRect().width,"undefined"!=typeof ResizeObserver&&(this.fullscreenResizeObserver=new ResizeObserver((e=>{var i,r,t;if(this.hasAttribute("data-animating-fullscreen"))return;const n=e[0];if(!n)return;const a=null!==(t=null===(r=null===(i=n.borderBoxSize)||void 0===i?void 0:i[0])||void 0===r?void 0:r.inlineSize)&&void 0!==t?t:n.contentRect.width;this.observedWidth=a})),this.fullscreenResizeObserver.observe(this)))}disconnectedCallback(){var e,i;super.disconnectedCallback(),null===(e=this.fullscreenResizeObserver)||void 0===e||e.disconnect(),this.fullscreenResizeObserver=void 0,null===(i=this.fullscreenAnimation)||void 0===i||i.cancel(),this.fullscreenAnimation=void 0,this.fullscreenAnimationCleanup=void 0}willUpdate(){this.widthSeeded||(this.seedWidth(),this.widthSeeded=!0)}handleFullscreenChange(e,i){if(n)return;if(!this.fullscreenSeeded)return this.fullscreenSeeded=!0,void(this.observedWidth=this.getBoundingClientRect().width);if("undefined"!=typeof matchMedia&&!matchMedia("(min-width: 768px)").matches)return;this.fullscreenAnimation&&(this.observedWidth=this.getBoundingClientRect().width,this.fullscreenAnimation.cancel());const r=this.observedWidth;this.setAttribute("data-animating-fullscreen",""),i||this.setWidth(this.defaultWidth,"attribute"),requestAnimationFrame((()=>requestAnimationFrame((()=>{const e=this.getBoundingClientRect().width;if(Math.abs(r-e)<1)return this.removeAttribute("data-animating-fullscreen"),this.observedWidth=e,void(this.fullscreenAnimationCleanup=void 0);const i=this.animate([{inlineSize:`${r}px`,maxInlineSize:`${r}px`,minInlineSize:`${r}px`},{inlineSize:`${e}px`,maxInlineSize:`${e}px`,minInlineSize:`${e}px`}],{duration:300,easing:"cubic-bezier(0.4, 0, 0.2, 1)",fill:"none"});this.fullscreenAnimation=i;const t=()=>{this.fullscreenAnimation===i&&(this.fullscreenAnimation=void 0,this.fullscreenAnimationCleanup=void 0,this.removeAttribute("data-animating-fullscreen"),this.observedWidth=this.getBoundingClientRect().width)};this.fullscreenAnimationCleanup=t,i.onfinish=t,i.oncancel=t}))))}handleBoundsChange(){if(!this.widthSeeded)return;const e=h(this.width,this.minWidth,this.maxWidth);e!==this.width?this.setWidth(e,"attribute"):this.applyWidthVars()}handleFloatingChange(){this.applyFloatingRole()}applyFloatingRole(){this.floating?(this.setAttribute("role","dialog"),this.setAttribute("aria-modal","true")):(this.setAttribute("role","region"),this.removeAttribute("aria-modal"))}applyWidthVars(){this.style.setProperty("--_n-aside-drawer-inline-size",`${this.width}px`),this.style.setProperty("--_n-aside-drawer-min-inline-size",`${this.minWidth}px`);const e=this.maxWidth>=Number.MAX_SAFE_INTEGER;this.style.setProperty("--_n-aside-drawer-max-inline-size",e?"none":`${this.maxWidth}px`);const i=this.maxFullscreenWidth>=Number.MAX_SAFE_INTEGER;this.style.setProperty("--_n-aside-drawer-fullscreen-max",i?"100vw":`max(${this.minWidth}px, ${this.maxFullscreenWidth}px)`)}setWidth(e,i){const r=h(Math.round(e),this.minWidth,this.maxWidth);r!==this.width&&(this.width=r,!n&&this.id&&(c(v(this.id),this.defaultWidth).value=this.width),this.dispatchEvent(new b(this.width,i)))}get isPinned(){return this.minWidth===this.maxWidth}seedWidth(){if(n||!this.id)return this.width=h(this.defaultWidth,this.minWidth,this.maxWidth),void this.applyWidthVars();const e=c(v(this.id),this.defaultWidth),i=c((r=this.id,`${m}${r}.defaultWidth`),this.defaultWidth);var r;let t,a;i.value!==this.defaultWidth?(t=this.defaultWidth,e.value=t,i.value=this.defaultWidth,a="attribute"):(t=e.value,a="storage"),this.width=h(Math.round(t),this.minWidth,this.maxWidth),this.applyWidthVars(),this.dispatchEvent(new b(this.width,a))}applyAriaLabelledby(){if(!this.id)return void this.removeAttribute("aria-labelledby");const e=this.closest("nord-aside");if(!e)return;const i=e.querySelector(`nord-aside-trigger[drawer="${this.id}"]`);i&&(i.id||(i.id=`${this.id}-trigger`),this.setAttribute("aria-labelledby",i.id))}};p.styles=[u,w],e([a({reflect:!0,type:Number,attribute:"default-width"})],p.prototype,"defaultWidth",void 0),e([a({reflect:!0,type:Number,attribute:"min-width"})],p.prototype,"minWidth",void 0),e([a({reflect:!1,type:Number,attribute:"max-width"})],p.prototype,"maxWidth",void 0),e([a({reflect:!1,type:Number,attribute:"max-fullscreen-width"})],p.prototype,"maxFullscreenWidth",void 0),e([a({type:Boolean,reflect:!0})],p.prototype,"disabled",void 0),e([a({type:Boolean,reflect:!0})],p.prototype,"fullscreen",void 0),e([a({type:Boolean,reflect:!1})],p.prototype,"floating",void 0),e([d()],p.prototype,"width",void 0),e([l("fullscreen")],p.prototype,"handleFullscreenChange",null),e([l("defaultWidth"),l("minWidth"),l("maxWidth"),l("maxFullscreenWidth")],p.prototype,"handleBoundsChange",null),e([l("floating")],p.prototype,"handleFloatingChange",null),e([l("width")],p.prototype,"applyWidthVars",null),p=e([s("nord-aside-drawer")],p);var f=p;export{b as AsideDrawerWidthChangeEvent,f as default};
14
+ //# sourceMappingURL=AsideDrawer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AsideDrawer.js","sources":["../src/aside-drawer/AsideDrawer.ts"],"sourcesContent":["import { html, isServer, LitElement } from 'lit'\nimport { customElement, property, state } from 'lit/decorators.js'\nimport { observe } from '../common/decorators/observe.js'\nimport { NordEvent } from '../common/events.js'\nimport { clamp } from '../common/number.js'\nimport { storage } from '../common/storage.js'\n\nimport componentStyle from '../common/styles/Component.css'\nimport style from './AsideDrawer.css'\n\nexport type AsideDrawerWidthSource = 'drag' | 'keyboard' | 'attribute' | 'storage'\n\n/**\n * Event dispatched when the drawer's width changes. The `width` property is\n * the new applied inline-size in px, and `source` is the cause of the change.\n */\nexport class AsideDrawerWidthChangeEvent extends NordEvent {\n declare width: number\n declare source: AsideDrawerWidthSource\n\n constructor(width: number, source: AsideDrawerWidthSource) {\n super('width-change')\n Object.defineProperty(this, 'width', {\n value: width,\n enumerable: true,\n writable: false,\n })\n Object.defineProperty(this, 'source', {\n value: source,\n enumerable: true,\n writable: false,\n })\n }\n}\n\nconst STORAGE_KEY_PREFIX = 'nord-aside.'\n\nfunction widthKey(id: string) {\n return `${STORAGE_KEY_PREFIX}${id}.width`\n}\n\nfunction defaultWidthKey(id: string) {\n return `${STORAGE_KEY_PREFIX}${id}.defaultWidth`\n}\n\n/**\n * A drawer panel inside `<nord-aside>`.\n *\n * @status new\n * @category structure\n * @slot - Default slot. Place arbitrary drawer content here.\n *\n * @fires {AsideDrawerWidthChangeEvent} width-change - Dispatched when the\n * drawer's width changes. The event exposes `width` (the new applied\n * inline-size in px) and `source` (`'drag' | 'keyboard' | 'attribute' | 'storage'`).\n */\n@customElement('nord-aside-drawer')\nexport default class AsideDrawer extends LitElement {\n static styles = [componentStyle, style]\n\n /**\n * Initial width in px. The persisted value overrides this on load; if\n * `default-width` changes between sessions, the persisted value is\n * re-seeded to the new default.\n */\n @property({ reflect: true, type: Number, attribute: 'default-width' }) defaultWidth: number = 420\n\n /**\n * Resize clamp lower bound in px.\n */\n @property({ reflect: true, type: Number, attribute: 'min-width' }) minWidth: number = 280\n\n /**\n * Resize clamp upper bound in px. Defaults to `Number.MAX_SAFE_INTEGER`\n * so an unset `max-width` doesn't constrain the drawer — the consumer's\n * layout (or the rail / nav anchoring) determines the practical ceiling.\n * Set an explicit value to clamp.\n *\n * Not reflected: an unset cap is the `Number.MAX_SAFE_INTEGER` sentinel,\n * which must never render into the DOM as `max-width=\"9007199254740991\"`.\n */\n @property({ reflect: false, type: Number, attribute: 'max-width' }) maxWidth: number = Number.MAX_SAFE_INTEGER\n\n /**\n * Maximum inline-size in px the drawer can reach when `[fullscreen]`\n * is active. Independent of `max-width` (which only caps the docked\n * drag / keyboard resize) — fullscreen typically wants to grow\n * larger than the docked maximum. Defaults to `Number.MAX_SAFE_INTEGER`\n * so an unset value lets fullscreen fill all available room (bounded\n * by the layout's left-cap rule in `<nord-aside>`).\n *\n * Not reflected: an unset cap is the `Number.MAX_SAFE_INTEGER` sentinel,\n * which must never render into the DOM.\n */\n @property({ reflect: false, type: Number, attribute: 'max-fullscreen-width' }) maxFullscreenWidth: number = Number.MAX_SAFE_INTEGER\n\n /**\n * When set, the drawer cannot be opened. Programmatic opens are silently\n * ignored; if the drawer is already open and this flips on, the drawer\n * stays open until closed normally.\n */\n @property({ type: Boolean, reflect: true }) disabled: boolean = false\n\n /**\n * Reflected. Phase 3 wires the overlay sizing internally.\n */\n @property({ type: Boolean, reflect: true }) fullscreen: boolean = false\n\n /**\n * Set by the parent `<nord-aside>` when the drawer renders as a narrow\n * floating overlay (rather than a docked region). When `true` the host\n * becomes an accessible modal dialog (`role=\"dialog\"` + `aria-modal`);\n * when `false` the host is a `role=\"region\"` landmark. Drive this only\n * through the parent — it owns the floating-vs-docked decision.\n */\n @property({ type: Boolean, reflect: false }) floating: boolean = false\n\n /**\n * Currently-applied width in px. Public read-only-ish; use `setWidth()`\n * (or drive via `default-width` + min/max attrs) rather than poking\n * this directly.\n * @internal\n */\n @state() width = 420\n\n private widthSeeded = false\n private fullscreenSeeded = false\n private fullscreenAnimationCleanup?: () => void\n private fullscreenAnimation?: Animation\n // Last painted inline-size, kept up to date by ResizeObserver. Used as\n // the animation's `fromWidth` on toggle — read from this cache instead\n // of `getBoundingClientRect()` (which would force sync layout against\n // the already-mutated `fullscreen` attribute).\n private observedWidth = 0\n private fullscreenResizeObserver?: ResizeObserver\n\n render() {\n return html`\n <div class=\"n-aside-drawer\" part=\"drawer\">\n <slot></slot>\n </div>\n `\n }\n\n connectedCallback() {\n super.connectedCallback()\n this.applyFloatingRole()\n this.applyAriaLabelledby()\n if (isServer) {\n return\n }\n // Seed observedWidth synchronously so the first toggle has a valid\n // fromWidth even before ResizeObserver has fired.\n this.observedWidth = this.getBoundingClientRect().width\n if (typeof ResizeObserver !== 'undefined') {\n this.fullscreenResizeObserver = new ResizeObserver((entries) => {\n // Don't overwrite while an animation is running — intermediate\n // frames aren't valid `fromWidth` candidates for the NEXT toggle.\n if (this.hasAttribute('data-animating-fullscreen'))\n return\n const entry = entries[0]\n if (!entry)\n return\n const size = entry.borderBoxSize?.[0]?.inlineSize ?? entry.contentRect.width\n this.observedWidth = size\n })\n this.fullscreenResizeObserver.observe(this)\n }\n }\n\n disconnectedCallback() {\n super.disconnectedCallback()\n this.fullscreenResizeObserver?.disconnect()\n this.fullscreenResizeObserver = undefined\n this.fullscreenAnimation?.cancel()\n this.fullscreenAnimation = undefined\n this.fullscreenAnimationCleanup = undefined\n }\n\n willUpdate() {\n if (!this.widthSeeded) {\n this.seedWidth()\n this.widthSeeded = true\n }\n }\n\n /**\n * Animate the fullscreen toggle ONLY when the user flips the attribute.\n * Not on initial mount, not on drag-resize, not on show/hide, not on\n * nav-state-driven size changes.\n *\n * Uses Web Animations API with explicit `from`/`to` keyframes because\n * the drawer's `position` flips between `relative` (docked, in flex\n * flow) and `fixed` (fullscreen, overlay). CSS transitions don't\n * reliably interpolate `inline-size` across a position-type change.\n *\n * `fromWidth` comes from `observedWidth` — the last size painted by\n * the browser, captured by ResizeObserver BEFORE the consumer flipped\n * the attribute. Reading `getBoundingClientRect()` here would return\n * the post-toggle size (Lit has already reflected the attribute), so\n * the cached value is the only reliable pre-toggle width.\n *\n * `toWidth` is measured inside rAF, after Lit has committed the new\n * attribute and Aside has updated `--_n-aside-drawer-fullscreen-size`.\n *\n * Exiting also resets the drawer's docked width to its declared\n * `default-width` so the user lands at a predictable size after\n * collapsing.\n */\n @observe('fullscreen')\n protected handleFullscreenChange(_oldValue: boolean | undefined, newValue: boolean) {\n if (isServer) {\n return\n }\n if (!this.fullscreenSeeded) {\n this.fullscreenSeeded = true\n this.observedWidth = this.getBoundingClientRect().width\n return\n }\n // At narrow viewports the `fullscreen` attribute has no visual effect\n // (the parent <nord-aside> renders the drawer as a floating overlay\n // regardless). The CSS rules for [fullscreen] are gated behind\n // `@media (min-width: 768px)` — skip the animation here too so we\n // don't run a no-op WAAPI animation against zero size delta.\n if (typeof matchMedia !== 'undefined' && !matchMedia('(min-width: 768px)').matches) {\n return\n }\n\n // Reverse-mid-flight: snapshot the current visible width BEFORE\n // cancelling, because `animation.cancel()` synchronously reverts\n // the element to its CSS layout — measuring after cancel would\n // give us the underlying CSS width, not what the user is seeing.\n // oncancel fires synchronously on cancel(), so cleanup runs from there —\n // do NOT call cleanupFn manually here or it will run twice.\n if (this.fullscreenAnimation) {\n this.observedWidth = this.getBoundingClientRect().width\n this.fullscreenAnimation.cancel()\n }\n\n const fromWidth = this.observedWidth\n\n // Pin `position: fixed` for the entire animation in both directions\n // via `:host([data-animating-fullscreen])`. Setting this here also\n // tells the ResizeObserver to stop updating `observedWidth`.\n this.setAttribute('data-animating-fullscreen', '')\n\n if (!newValue) {\n this.setWidth(this.defaultWidth, 'attribute')\n }\n\n // Double-rAF: the first frame lets Aside's scheduleLeftCapReconcile rAF\n // (queued synchronously above via syncFullscreenAttr) write\n // `--_n-aside-drawer-fullscreen-available`. The second frame then reads\n // the post-reconcile toWidth so the animation end-state is correct.\n requestAnimationFrame(() => requestAnimationFrame(() => {\n const toWidth = this.getBoundingClientRect().width\n\n if (Math.abs(fromWidth - toWidth) < 1) {\n this.removeAttribute('data-animating-fullscreen')\n this.observedWidth = toWidth\n this.fullscreenAnimationCleanup = undefined\n return\n }\n\n const animation = this.animate(\n [\n {\n inlineSize: `${fromWidth}px`,\n maxInlineSize: `${fromWidth}px`,\n minInlineSize: `${fromWidth}px`,\n },\n {\n inlineSize: `${toWidth}px`,\n maxInlineSize: `${toWidth}px`,\n minInlineSize: `${toWidth}px`,\n },\n ],\n {\n duration: 300,\n easing: 'cubic-bezier(0.4, 0, 0.2, 1)',\n fill: 'none',\n },\n )\n\n this.fullscreenAnimation = animation\n\n const cleanup = () => {\n if (this.fullscreenAnimation !== animation)\n return\n this.fullscreenAnimation = undefined\n this.fullscreenAnimationCleanup = undefined\n this.removeAttribute('data-animating-fullscreen')\n this.observedWidth = this.getBoundingClientRect().width\n }\n this.fullscreenAnimationCleanup = cleanup\n animation.onfinish = cleanup\n animation.oncancel = cleanup\n }))\n }\n\n @observe('defaultWidth')\n @observe('minWidth')\n @observe('maxWidth')\n @observe('maxFullscreenWidth')\n protected handleBoundsChange() {\n // Re-seeding happens once on willUpdate; subsequent bound changes\n // just re-clamp the current width.\n if (!this.widthSeeded) {\n return\n }\n const clamped = clamp(this.width, this.minWidth, this.maxWidth)\n if (clamped !== this.width) {\n this.setWidth(clamped, 'attribute')\n }\n else {\n this.applyWidthVars()\n }\n }\n\n @observe('floating')\n protected handleFloatingChange() {\n this.applyFloatingRole()\n }\n\n /**\n * Single writer for the host's landmark/dialog role. A floating overlay\n * is an accessible modal (`role=\"dialog\"` + `aria-modal=\"true\"`); a docked\n * drawer is a `role=\"region\"` landmark with no aria-modal.\n */\n private applyFloatingRole() {\n if (this.floating) {\n this.setAttribute('role', 'dialog')\n this.setAttribute('aria-modal', 'true')\n }\n else {\n this.setAttribute('role', 'region')\n this.removeAttribute('aria-modal')\n }\n }\n\n @observe('width')\n protected applyWidthVars() {\n this.style.setProperty('--_n-aside-drawer-inline-size', `${this.width}px`)\n this.style.setProperty('--_n-aside-drawer-min-inline-size', `${this.minWidth}px`)\n // An unset `max-width` defaults to MAX_SAFE_INTEGER — too large for\n // CSS to handle reliably. Translate to `none` so the drawer has no\n // CSS-side cap; the clamp in `setWidth` still runs against the\n // numeric value so drag/keyboard don't escape JS-side bounds.\n const isMaxUnbounded = this.maxWidth >= Number.MAX_SAFE_INTEGER\n this.style.setProperty('--_n-aside-drawer-max-inline-size', isMaxUnbounded ? 'none' : `${this.maxWidth}px`)\n // Fullscreen rendered inline-size is the CSS-time `min()` of two vars:\n // - `--_n-aside-drawer-fullscreen-max` (this var, drawer-owned)\n // - `--_n-aside-drawer-fullscreen-available` (written by <nord-aside>\n // to the layout's left-cap; absent when there's no aside parent)\n // The drawer's own var floors at `minWidth` so a tiny consumer value\n // can never drop the drawer below its declared minimum. Unset\n // `max-fullscreen-width` resolves to `100vw` — a sensible standalone\n // fallback when there's no aside to publish the available width.\n const isFullscreenUnbounded = this.maxFullscreenWidth >= Number.MAX_SAFE_INTEGER\n this.style.setProperty(\n '--_n-aside-drawer-fullscreen-max',\n isFullscreenUnbounded\n ? '100vw'\n : `max(${this.minWidth}px, ${this.maxFullscreenWidth}px)`,\n )\n }\n\n /**\n * Update the drawer width, clamped to `[min-width, max-width]`. Persists\n * to localStorage and dispatches `width-change` with the given source.\n */\n setWidth(width: number, source: AsideDrawerWidthSource) {\n const next = clamp(Math.round(width), this.minWidth, this.maxWidth)\n if (next === this.width) {\n return\n }\n this.width = next\n if (!isServer && this.id) {\n storage(widthKey(this.id), this.defaultWidth).value = this.width\n }\n this.dispatchEvent(new AsideDrawerWidthChangeEvent(this.width, source))\n }\n\n /**\n * Whether the drawer is pinned — `min === max === default`. A pinned\n * drawer's resize handle is a drag no-op.\n */\n get isPinned(): boolean {\n return this.minWidth === this.maxWidth\n }\n\n /**\n * Lift Layout.ts's re-seed pattern: if the consumer-provided\n * `default-width` differs from the last default we wrote for this id,\n * the persisted width is reset to the new default.\n */\n private seedWidth() {\n // On the server there's no localStorage and no layout to paint; clamp\n // the declared default and skip both the storage read/write and the\n // `width-change` dispatch.\n if (isServer || !this.id) {\n this.width = clamp(this.defaultWidth, this.minWidth, this.maxWidth)\n this.applyWidthVars()\n return\n }\n const widthStore = storage(widthKey(this.id), this.defaultWidth)\n const defaultStore = storage(defaultWidthKey(this.id), this.defaultWidth)\n let next: number\n let source: AsideDrawerWidthSource\n if (defaultStore.value !== this.defaultWidth) {\n next = this.defaultWidth\n widthStore.value = next\n defaultStore.value = this.defaultWidth\n source = 'attribute'\n }\n else {\n next = widthStore.value\n source = 'storage'\n }\n this.width = clamp(Math.round(next), this.minWidth, this.maxWidth)\n this.applyWidthVars()\n this.dispatchEvent(new AsideDrawerWidthChangeEvent(this.width, source))\n }\n\n /**\n * Resolve the matching `<nord-aside-trigger drawer=\"<this.id>\">` inside\n * the ancestor `<nord-aside>` and use it as the drawer's aria-labelledby\n * target. Falls back to no label when no matching trigger exists.\n */\n private applyAriaLabelledby() {\n if (!this.id) {\n this.removeAttribute('aria-labelledby')\n return\n }\n const aside = this.closest('nord-aside')\n if (!aside) {\n return\n }\n const trigger = aside.querySelector<HTMLElement>(\n `nord-aside-trigger[drawer=\"${this.id}\"]`,\n )\n if (!trigger) {\n return\n }\n if (!trigger.id) {\n trigger.id = `${this.id}-trigger`\n }\n this.setAttribute('aria-labelledby', trigger.id)\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'nord-aside-drawer': AsideDrawer\n }\n}\n"],"names":["AsideDrawerWidthChangeEvent","NordEvent","constructor","width","source","super","Object","defineProperty","this","value","enumerable","writable","STORAGE_KEY_PREFIX","widthKey","id","AsideDrawer","LitElement","defaultWidth","minWidth","maxWidth","Number","MAX_SAFE_INTEGER","maxFullscreenWidth","disabled","fullscreen","floating","widthSeeded","fullscreenSeeded","observedWidth","render","html","connectedCallback","applyFloatingRole","applyAriaLabelledby","isServer","getBoundingClientRect","ResizeObserver","fullscreenResizeObserver","entries","hasAttribute","entry","size","_c","_a","borderBoxSize","_b","inlineSize","contentRect","observe","disconnectedCallback","disconnect","undefined","fullscreenAnimation","cancel","fullscreenAnimationCleanup","willUpdate","seedWidth","handleFullscreenChange","_oldValue","newValue","matchMedia","matches","fromWidth","setAttribute","setWidth","requestAnimationFrame","toWidth","Math","abs","removeAttribute","animation","animate","maxInlineSize","minInlineSize","duration","easing","fill","cleanup","onfinish","oncancel","handleBoundsChange","clamped","clamp","applyWidthVars","handleFloatingChange","style","setProperty","isMaxUnbounded","isFullscreenUnbounded","next","round","storage","dispatchEvent","isPinned","widthStore","defaultStore","aside","closest","trigger","querySelector","styles","componentStyle","__decorate","property","reflect","type","attribute","prototype","Boolean","state","customElement"],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ import{_ as t}from"./tslib.es6-CmLYFWVC.js";import{css as i,LitElement as e,html as r,nothing as s}from"lit";import{property as o,state as a,customElement as n}from"lit/decorators.js";import{S as d}from"./SlotController-Z6eG7LSZ.js";import{o as l}from"./observe-D0n0zOfU.js";import{s as c}from"./Component-DSU3Qp0O.js";import"./Button.js";import"./Icon.js";import"./Tooltip.js";import"./EventController-BBOmvfLa.js";import"lit/directives/ref.js";import"./LightDomController-DIwtVelV.js";import"./cond-CI1KbneT.js";import"./FocusableMixin-BlQLNPdJ.js";import"./InputMixin-LetXsCyv.js";import"./Spinner.js";import"lit/directives/if-defined.js";import"lit/directives/unsafe-html.js";import"./IconManager.js";import"./positioning-D-K8Mueq.js";import"./fsm-Bq5jMQrK.js";const h=i`:host{--_n-aside-trigger-size:var(--n-aside-trigger-size, 32px);--_n-aside-trigger-background-color:var(--n-aside-trigger-background-color, transparent);--_n-aside-trigger-background-color-active:var(
2
+ --n-aside-trigger-background-color-active,
3
+ var(--n-color-button-hover)
4
+ );display:block;inline-size:100%}.n-aside-trigger{display:block;position:relative;inline-size:100%;text-align:center}.n-aside-trigger nord-button{--n-button-min-block-size:var(--_n-aside-trigger-size);--n-button-min-inline-size:var(--_n-aside-trigger-size);--n-button-background-color:var(--_n-aside-trigger-background-color)}:host([aria-expanded=true]){--_n-aside-trigger-background-color:var(--_n-aside-trigger-background-color-active)}.n-aside-trigger-dot{position:absolute;inset-block-start:3px;inset-inline-end:2px;inline-size:5px;block-size:5px;border-radius:var(--n-border-radius-circle);background:var(--n-color-status-notification);pointer-events:none}`;let p=0,b=class extends e{constructor(){super(...arguments),this.defaultSlot=new d(this),this.tooltipSlot=new d(this,"tooltip"),this.drawer="",this.label="",this.disabled=!1,this.tooltip=!0,this.active=!1,this.targetDisabled=!1,this.tooltipId="n-aside-trigger-tip-"+ ++p,this.handleActiveDrawerChange=()=>{this.syncFromAside()},this.handleClick=t=>{var i;if(this.disabled||this.targetDisabled)return t.stopPropagation(),void t.preventDefault();const e=this.aside;if(!e||!this.drawer)return;const r=null!==(i=e.getAttribute("active-drawer"))&&void 0!==i?i:"";e.activeDrawer=r===this.drawer?"":this.drawer}}render(){const t=!this.defaultSlot.isEmpty,i=!t&&!!this.icon,e=void 0!==this.badge&&null!==this.badge&&""!==this.badge,o=!this.tooltipSlot.isEmpty,a=this.tooltip&&(!!this.label||o);return r`<div class="n-aside-trigger"><nord-button square size="s" variant="plain" type="button" aria-label="${this.label||s}" aria-describedby="${a?this.tooltipId:s}" ?disabled="${this.disabled}" @click="${this.handleClick}">${t?r`<slot></slot>`:s} ${i?r`<nord-icon name="${this.icon}" size="m"></nord-icon>`:s}</nord-button>${e?r`<span class="n-aside-trigger-dot" aria-hidden="true"></span>`:s} ${a?r`<nord-tooltip id="${this.tooltipId}" position="inline-start"><slot name="tooltip">${this.label}</slot></nord-tooltip>`:s}</div>`}connectedCallback(){super.connectedCallback(),this.startObservingAside()}disconnectedCallback(){super.disconnectedCallback(),this.stopObservingAside()}handleDrawerChange(){this.syncFromAside()}handleDisabledChange(){this.syncAria()}get aside(){return this.closest("nord-aside")}startObservingAside(){var t;this.observedAside=null!==(t=this.aside)&&void 0!==t?t:void 0,this.observedAside&&(this.observedAside.addEventListener("active-drawer-change",this.handleActiveDrawerChange),this.syncFromAside())}stopObservingAside(){var t;null===(t=this.observedAside)||void 0===t||t.removeEventListener("active-drawer-change",this.handleActiveDrawerChange),this.observedAside=void 0}syncFromAside(){var t,i,e;const r=null!==(t=this.observedAside)&&void 0!==t?t:this.aside;if(!r)return this.active=!1,this.targetDisabled=!1,void this.syncAria();const s=null!==(e=null!==(i=r.activeDrawer)&&void 0!==i?i:r.getAttribute("active-drawer"))&&void 0!==e?e:"";this.active=!!this.drawer&&s===this.drawer;const o=this.drawer?r.querySelector(`:scope > nord-aside-drawer#${CSS.escape(this.drawer)}`):null;this.targetDisabled=!o||o.hasAttribute("disabled"),this.syncAria()}syncAria(){this.drawer?this.setAttribute("aria-controls",this.drawer):this.removeAttribute("aria-controls"),this.setAttribute("aria-expanded",this.active?"true":"false"),this.disabled||this.targetDisabled?this.setAttribute("aria-disabled","true"):this.removeAttribute("aria-disabled")}};b.styles=[c,h],t([o({reflect:!0})],b.prototype,"drawer",void 0),t([o({reflect:!0})],b.prototype,"icon",void 0),t([o({reflect:!0})],b.prototype,"label",void 0),t([o({reflect:!0})],b.prototype,"badge",void 0),t([o({type:Boolean,reflect:!0})],b.prototype,"disabled",void 0),t([o({type:Boolean,reflect:!0})],b.prototype,"tooltip",void 0),t([a()],b.prototype,"active",void 0),t([a()],b.prototype,"targetDisabled",void 0),t([l("drawer")],b.prototype,"handleDrawerChange",null),t([l("disabled")],b.prototype,"handleDisabledChange",null),b=t([n("nord-aside-trigger")],b);var v=b;export{v as default};
5
+ //# sourceMappingURL=AsideTrigger.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AsideTrigger.js","sources":["../src/aside-trigger/AsideTrigger.ts"],"sourcesContent":["import { html, LitElement, nothing } from 'lit'\nimport { customElement, property, state } from 'lit/decorators.js'\nimport { SlotController } from '../common/controllers/SlotController.js'\nimport { observe } from '../common/decorators/observe.js'\n\nimport componentStyle from '../common/styles/Component.css'\nimport style from './AsideTrigger.css'\nimport '../button/Button.js'\nimport '../icon/Icon.js'\nimport '../tooltip/Tooltip.js'\n\nlet asideTriggerTooltipIdCounter = 0\n\n/**\n * Rail button that toggles a `<nord-aside-drawer>` inside `<nord-aside>`.\n *\n * @status new\n * @category action\n * @slot - Overrides the `icon` attribute for custom leading content.\n * @slot tooltip - Overrides the tooltip body (falls back to `label`).\n * Lets consumers add icons, shortcut hints, etc. — anything beyond the\n * plain string label.\n */\n@customElement('nord-aside-trigger')\nexport default class AsideTrigger extends LitElement {\n static styles = [componentStyle, style]\n\n private defaultSlot = new SlotController(this)\n private tooltipSlot = new SlotController(this, 'tooltip')\n\n /**\n * The id of the `<nord-aside-drawer>` this trigger toggles.\n * Required.\n */\n @property({ reflect: true }) drawer: string = ''\n\n /**\n * Nord icon name. Shortcut for the common case; equivalent to placing\n * `<nord-icon name=\"…\">` in the default slot.\n */\n @property({ reflect: true }) icon?: string\n\n /**\n * Accessible label (used for `aria-label` on the host).\n */\n @property({ reflect: true }) label: string = ''\n\n /**\n * Optional badge content (forwarded to inner `<nord-badge>`).\n */\n @property({ reflect: true }) badge?: string | number\n\n /**\n * Makes the trigger disabled. Takes precedence over the target\n * drawer's `disabled` state.\n */\n @property({ type: Boolean, reflect: true }) disabled: boolean = false\n\n /**\n * Whether to render a `<nord-tooltip>` showing `label` on hover/focus.\n * Defaults to `true` — set `tooltip=\"false\"` (or omit `label`) to opt out.\n */\n @property({ type: Boolean, reflect: true }) tooltip: boolean = true\n\n /**\n * Whether the ancestor `<nord-aside>`'s `active-drawer` currently\n * matches this trigger's `drawer` id.\n * @internal\n */\n @state() private active: boolean = false\n\n /**\n * Whether the target drawer exists and is not disabled. Drives\n * `aria-disabled` on the host.\n * @internal\n */\n @state() private targetDisabled: boolean = false\n\n private observedAside?: HTMLElement\n private readonly tooltipId = `n-aside-trigger-tip-${++asideTriggerTooltipIdCounter}`\n\n render() {\n const showCustomLeading = !this.defaultSlot.isEmpty\n const showIcon = !showCustomLeading && !!this.icon\n const showBadge = this.badge !== undefined && this.badge !== null && this.badge !== ''\n\n // Tooltip is rendered when explicitly enabled AND there's something to\n // show — either the `label` attr (default body) or a custom `tooltip`\n // slot.\n const hasTooltipSlot = !this.tooltipSlot.isEmpty\n const showTooltip = this.tooltip && (!!this.label || hasTooltipSlot)\n return html`\n <div class=\"n-aside-trigger\">\n <nord-button\n square\n size=\"s\"\n variant=\"plain\"\n type=\"button\"\n aria-label=${this.label || nothing}\n aria-describedby=${showTooltip ? this.tooltipId : nothing}\n ?disabled=${this.disabled}\n @click=${this.handleClick}\n >\n ${showCustomLeading ? html`<slot></slot>` : nothing}\n ${showIcon ? html`<nord-icon name=${this.icon!} size=\"m\"></nord-icon>` : nothing}\n </nord-button>\n ${showBadge ? html`<span class=\"n-aside-trigger-dot\" aria-hidden=\"true\"></span>` : nothing}\n ${showTooltip\n ? html`\n <nord-tooltip id=${this.tooltipId} position=\"inline-start\">\n <slot name=\"tooltip\">${this.label}</slot>\n </nord-tooltip>\n `\n : nothing}\n </div>\n `\n }\n\n connectedCallback() {\n super.connectedCallback()\n this.startObservingAside()\n }\n\n disconnectedCallback() {\n super.disconnectedCallback()\n this.stopObservingAside()\n }\n\n @observe('drawer')\n protected handleDrawerChange() {\n this.syncFromAside()\n }\n\n @observe('disabled')\n protected handleDisabledChange() {\n this.syncAria()\n }\n\n private get aside(): HTMLElement | null {\n return this.closest('nord-aside')\n }\n\n private readonly handleActiveDrawerChange = () => {\n this.syncFromAside()\n }\n\n /**\n * Listen to the aside's own `active-drawer-change` event instead of each\n * trigger installing its own subtree MutationObserver on the whole aside.\n * One shared event covers open / switch / close, so N triggers no longer\n * mean N observers waking on every aside DOM mutation.\n */\n private startObservingAside() {\n this.observedAside = this.aside ?? undefined\n if (!this.observedAside) {\n return\n }\n this.observedAside.addEventListener('active-drawer-change', this.handleActiveDrawerChange)\n this.syncFromAside()\n }\n\n private stopObservingAside() {\n this.observedAside?.removeEventListener('active-drawer-change', this.handleActiveDrawerChange)\n this.observedAside = undefined\n }\n\n private syncFromAside() {\n const aside = this.observedAside ?? this.aside\n if (!aside) {\n this.active = false\n this.targetDisabled = false\n this.syncAria()\n return\n }\n\n // Prefer the live `activeDrawer` property over the reflected attribute:\n // the `active-drawer-change` event fires during the aside's update cycle,\n // before Lit reflects the property to the `active-drawer` attribute, so\n // reading the attribute here would lag one tick behind. Fall back to the\n // attribute only while the aside element has not upgraded yet.\n const activeDrawer\n = (aside as HTMLElement & { activeDrawer?: string }).activeDrawer\n ?? aside.getAttribute('active-drawer')\n ?? ''\n this.active = !!this.drawer && activeDrawer === this.drawer\n\n const target = this.drawer\n ? aside.querySelector(`:scope > nord-aside-drawer#${CSS.escape(this.drawer)}`)\n : null\n this.targetDisabled = !target || target.hasAttribute('disabled')\n\n this.syncAria()\n }\n\n private syncAria() {\n if (this.drawer) {\n this.setAttribute('aria-controls', this.drawer)\n }\n else {\n this.removeAttribute('aria-controls')\n }\n this.setAttribute('aria-expanded', this.active ? 'true' : 'false')\n if (this.disabled || this.targetDisabled) {\n this.setAttribute('aria-disabled', 'true')\n }\n else {\n this.removeAttribute('aria-disabled')\n }\n }\n\n private handleClick = (event: Event) => {\n if (this.disabled || this.targetDisabled) {\n event.stopPropagation()\n event.preventDefault()\n return\n }\n const aside = this.aside\n if (!aside || !this.drawer) {\n return\n }\n const current = aside.getAttribute('active-drawer') ?? ''\n ;(aside as HTMLElement & { activeDrawer?: string }).activeDrawer\n = current === this.drawer ? '' : this.drawer\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'nord-aside-trigger': AsideTrigger\n }\n}\n"],"names":["asideTriggerTooltipIdCounter","AsideTrigger","LitElement","constructor","this","defaultSlot","SlotController","tooltipSlot","drawer","label","disabled","tooltip","active","targetDisabled","tooltipId","handleActiveDrawerChange","syncFromAside","handleClick","event","stopPropagation","preventDefault","aside","current","_a","getAttribute","activeDrawer","render","showCustomLeading","isEmpty","showIcon","icon","showBadge","undefined","badge","hasTooltipSlot","showTooltip","html","nothing","connectedCallback","super","startObservingAside","disconnectedCallback","stopObservingAside","handleDrawerChange","handleDisabledChange","syncAria","closest","observedAside","addEventListener","removeEventListener","_c","_b","target","querySelector","CSS","escape","hasAttribute","setAttribute","removeAttribute","styles","componentStyle","style","__decorate","property","reflect","prototype","type","Boolean","state","observe","customElement"],"mappings":"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"}
package/lib/Button.js CHANGED
@@ -9,5 +9,5 @@ import{_ as t}from"./tslib.es6-CmLYFWVC.js";import{css as o,html as n,nothing as
9
9
  --n-button-box-shadow,
10
10
  0 0 0 1px var(--n-color-surface),
11
11
  0 0 0 3px var(--n-color-status-danger)
12
- )}:host([disabled]){--_n-button-border-color:var(--n-button-border-color, var(--_n-button-background-color));--_n-button-background-color:var(--n-button-background-color, var(--n-color-border));--_n-button-box-shadow:var(--n-button-box-shadow, none);--_n-button-color:var(--n-button-color, var(--n-color-text-weak));--_n-button-opacity:0.5;pointer-events:none}:host([disabled]) .n-button::after{display:none}.n-button-spinner{position:absolute;transform:translateX(-50%) translateY(-50%);inset-block-start:50%;inset-inline-start:50%}:host([loading]:not([href])){pointer-events:none}:host([loading]:not([href])) .n-content,:host([loading]:not([href])) ::slotted([slot=end]),:host([loading]:not([href])) ::slotted([slot=start]){opacity:0}:host([size='s']){--_n-button-gap:var(--n-button-gap, var(--n-space-xs));--_n-button-padding-inline:var(--n-button-padding-inline, calc(var(--n-space-s) + 1px));--_n-button-font-size:var(--n-button-font-size, var(--n-font-size-s));--_n-button-min-block-size:var(--n-button-min-block-size, calc(var(--n-space-l) + var(--n-space-xs)));--_n-button-padding-block:calc(var(--n-space-xs) - 1px);--_n-button-icon-size:var(--n-size-icon-xs)}:host([size='l']){--_n-button-border-radius:var(--n-button-border-radius, var(--n-border-radius));--_n-button-padding-inline:var(--n-button-padding-inline, calc(var(--n-space-l) / 1.3));--_n-button-font-size:var(--n-button-font-size, var(--n-font-size-l));--_n-button-font-weight:var(--n-button-font-weight, var(--n-font-weight-active));--_n-button-min-block-size:var(--n-button-min-block-size, calc(var(--n-space-xxl) - var(--n-space-l)));--_n-button-icon-size:var(--n-size-icon-m)}:host([square]){--_n-button-inline-size:var(--_n-button-min-block-size);--_n-button-padding-block:0;--_n-button-padding-inline:0}:host([square]) .n-content{display:flex;justify-content:center;align-items:center}::slotted(*){color:inherit;pointer-events:none}::slotted(svg){color:var(--n-color-icon)}::slotted(button[slot=proxy]){display:none}:host(:not([variant=primary],[variant=danger])) ::slotted(nord-icon){color:var(--n-color-icon)}:host(:not([variant=primary],[variant=danger])) .n-toggle-icon{color:var(--_n-button-toggle-icon-color)}:host(:not([square])) slot:not([name])::slotted(nord-icon){transform:translateY(1px)}::slotted(nord-icon:not([size])),nord-icon{--_n-icon-size:var(--_n-button-icon-size)}`;let f=class extends(h(v(e))){constructor(){super(...arguments),this.defaultSlot=new c(this),this.buttonRef=s(),this.events=new b(this),this.lightDom=new d(this,{render:()=>this.renderLightDom()}),this.variant="default",this.type="submit",this.size="m",this.download=!1,this.target="_self",this.expand=!1,this.square=!1,this.loading=!1,this.hideDropdownIcon=!1,this.handleOuterClick=t=>{t.composedPath().some((t=>t===this.focusableRef.value||t===this.buttonRef.value))||t.stopPropagation()}}connectedCallback(){super.connectedCallback(),this.events.listen(this,"click",this.handleOuterClick,!0)}render(){var t;const o="toggle"===(null===(t=this.assignedSlot)||void 0===t?void 0:t.name),e=this.defaultSlot.assigned.some((t=>"nord-icon"===t.localName)),a=o&&!this.hideDropdownIcon&&!e&&!this.href,i=n`<slot name="start"></slot><div class="n-content"><slot></slot></div><nord-spinner class="n-button-spinner" color="currentColor" ?hidden="${!this.loading||Boolean(this.href)}"></nord-spinner><slot name="end">${a?n`<nord-icon name="interface-dropdown-small" class="n-toggle-icon"></nord-icon>`:r}</slot>`;return this.href?this.renderLink(i):this.renderButton(i)}renderLink(t){return n`<a ${l(this.focusableRef)} class="n-button" target="${this.target}" ?download="${this.download}" href="${u(this.disabled,r,this.href)}" tabindex="${u(this.disabled,"-1")}" aria-disabled="${u(this.disabled,"true")}" role="${u(this.disabled,"link")}">${t}</a>`}renderButton(t){return n`<slot name="proxy" @slotchange="${this.handleProxyChange}"></slot><button ${l(this.focusableRef)} class="n-button" ?disabled="${this.disabled}" name="${u(this.name)}" value="${u(this.value)}" @click="${this.handleClick}" aria-disabled="${u(this.loading,"true")}" aria-expanded="${u(this.accessibleExpanded)}" aria-haspopup="${u(this.accessibleHasPopup)}">${t}</button>`}renderLightDom(){return this.href||!this.form?r:n`<button ${l(this.buttonRef)} slot="proxy" name="${u(this.name)}" value="${u(this.value)}" ?disabled="${this.disabled}" form="${u(this._formId)}" type="${this.type}"></button>`}handleClick(t){this.buttonRef.value&&(t.stopPropagation(),this.buttonRef.value.click())}handleProxyChange(t){const o=t.target,n=this.buttonRef.value;n&&n.assignedSlot!==o&&this.appendChild(n)}};f.styles=[p,g],t([a({reflect:!0})],f.prototype,"variant",void 0),t([a({reflect:!0})],f.prototype,"type",void 0),t([a({reflect:!0})],f.prototype,"size",void 0),t([a({attribute:"aria-expanded"})],f.prototype,"accessibleExpanded",void 0),t([a({attribute:"aria-haspopup"})],f.prototype,"accessibleHasPopup",void 0),t([a({reflect:!0})],f.prototype,"href",void 0),t([a({reflect:!0,type:Boolean})],f.prototype,"download",void 0),t([a()],f.prototype,"target",void 0),t([a({reflect:!0,type:Boolean})],f.prototype,"expand",void 0),t([a({reflect:!0,type:Boolean})],f.prototype,"square",void 0),t([a({reflect:!0,type:Boolean})],f.prototype,"loading",void 0),t([a({reflect:!0,type:Boolean,attribute:"hide-dropdown-icon"})],f.prototype,"hideDropdownIcon",void 0),f=t([i("nord-button")],f);var _=f;export{_ as default};
12
+ )}:host([disabled]){--_n-button-border-color:var(--n-button-border-color, var(--_n-button-background-color));--_n-button-background-color:var(--n-button-background-color, var(--n-color-border));--_n-button-box-shadow:var(--n-button-box-shadow, none);--_n-button-color:var(--n-button-color, var(--n-color-text-weak));--_n-button-opacity:0.5;pointer-events:none}:host([disabled]) .n-button::after{display:none}.n-button-spinner{position:absolute;transform:translateX(-50%) translateY(-50%);inset-block-start:50%;inset-inline-start:50%}:host([loading]:not([href])){pointer-events:none}:host([loading]:not([href])) .n-content,:host([loading]:not([href])) ::slotted([slot=end]),:host([loading]:not([href])) ::slotted([slot=start]){opacity:0}:host([size='s']){--_n-button-gap:var(--n-button-gap, var(--n-space-xs));--_n-button-padding-inline:var(--n-button-padding-inline, calc(var(--n-space-s) + 1px));--_n-button-font-size:var(--n-button-font-size, var(--n-font-size-s));--_n-button-min-block-size:var(--n-button-min-block-size, calc(var(--n-space-l) + var(--n-space-xs)));--_n-button-padding-block:calc(var(--n-space-xs) - 1px);--_n-button-icon-size:var(--n-size-icon-xs)}:host([size='l']){--_n-button-border-radius:var(--n-button-border-radius, var(--n-border-radius));--_n-button-padding-inline:var(--n-button-padding-inline, calc(var(--n-space-l) / 1.3));--_n-button-font-size:var(--n-button-font-size, var(--n-font-size-l));--_n-button-font-weight:var(--n-button-font-weight, var(--n-font-weight-active));--_n-button-min-block-size:var(--n-button-min-block-size, calc(var(--n-space-xxl) - var(--n-space-l)));--_n-button-icon-size:var(--n-size-icon-m)}:host([square]){--_n-button-inline-size:var(--_n-button-min-block-size);--_n-button-padding-block:0;--_n-button-padding-inline:0}:host([square]) .n-content{display:flex;justify-content:center;align-items:center}::slotted(*){color:inherit;pointer-events:none}::slotted(svg){color:var(--n-color-icon)}::slotted(button[slot=proxy]){display:none}:host(:not([variant=primary],[variant=danger])) ::slotted(nord-icon){color:var(--n-color-icon)}:host(:not([variant=primary],[variant=danger])) .n-toggle-icon{color:var(--_n-button-toggle-icon-color)}:host(:not([square])) slot:not([name])::slotted(nord-icon){transform:translateY(1px)}::slotted(nord-icon:not([size])),nord-icon{--_n-icon-size:var(--_n-button-icon-size)}`;let f=class extends(h(v(e))){constructor(){super(...arguments),this.defaultSlot=new c(this),this.buttonRef=s(),this.events=new b(this),this.lightDom=new d(this,{render:()=>this.renderLightDom()}),this.variant="default",this.type="submit",this.size="m",this.download=!1,this.target="_self",this.expand=!1,this.square=!1,this.loading=!1,this.hideDropdownIcon=!1,this.handleOuterClick=t=>{t.composedPath().some((t=>t===this.focusableRef.value||t===this.buttonRef.value))||t.stopPropagation()}}connectedCallback(){super.connectedCallback(),this.events.listen(this,"click",this.handleOuterClick,!0)}render(){var t;const o="toggle"===(null===(t=this.assignedSlot)||void 0===t?void 0:t.name),e=this.defaultSlot.assigned.some((t=>"nord-icon"===t.localName)),a=o&&!this.hideDropdownIcon&&!e&&!this.href,i=n`<slot name="start"></slot><div class="n-content"><slot></slot></div><nord-spinner class="n-button-spinner" color="currentColor" ?hidden="${!this.loading||Boolean(this.href)}"></nord-spinner><slot name="end">${a?n`<nord-icon name="interface-dropdown-small" class="n-toggle-icon"></nord-icon>`:r}</slot>`;return this.href?this.renderLink(i):this.renderButton(i)}renderLink(t){return n`<a ${l(this.focusableRef)} class="n-button" target="${this.target}" ?download="${this.download}" href="${u(this.disabled,r,this.href)}" tabindex="${u(this.disabled,"-1")}" aria-disabled="${u(this.disabled,"true")}" role="${u(this.disabled,"link")}">${t}</a>`}renderButton(t){return n`<slot name="proxy" @slotchange="${this.handleProxyChange}"></slot><button ${l(this.focusableRef)} class="n-button" ?disabled="${this.disabled}" name="${u(this.name)}" value="${u(this.value)}" @click="${this.handleClick}" aria-disabled="${u(this.loading,"true")}" aria-expanded="${u(this.accessibleExpanded)}" aria-haspopup="${u(this.accessibleHasPopup)}">${t}</button>`}renderLightDom(){return this.href||!this.form?r:n`<button ${l(this.buttonRef)} slot="proxy" name="${u(this.name)}" value="${u(this.value)}" ?disabled="${this.disabled}" form="${u(this._formId)}" type="${this.type}"></button>`}handleClick(t){this.buttonRef.value&&(t.stopPropagation(),this.buttonRef.value.click())}handleProxyChange(t){const o=t.target,n=this.buttonRef.value;n&&n.assignedSlot!==o&&this.appendChild(n)}};f.shadowRootOptions={...e.shadowRootOptions,delegatesFocus:!0},f.styles=[p,g],t([a({reflect:!0})],f.prototype,"variant",void 0),t([a({reflect:!0})],f.prototype,"type",void 0),t([a({reflect:!0})],f.prototype,"size",void 0),t([a({attribute:"aria-expanded"})],f.prototype,"accessibleExpanded",void 0),t([a({attribute:"aria-haspopup"})],f.prototype,"accessibleHasPopup",void 0),t([a({reflect:!0})],f.prototype,"href",void 0),t([a({reflect:!0,type:Boolean})],f.prototype,"download",void 0),t([a()],f.prototype,"target",void 0),t([a({reflect:!0,type:Boolean})],f.prototype,"expand",void 0),t([a({reflect:!0,type:Boolean})],f.prototype,"square",void 0),t([a({reflect:!0,type:Boolean})],f.prototype,"loading",void 0),t([a({reflect:!0,type:Boolean,attribute:"hide-dropdown-icon"})],f.prototype,"hideDropdownIcon",void 0),f=t([i("nord-button")],f);var _=f;export{_ as default};
13
13
  //# sourceMappingURL=Button.js.map
package/lib/Button.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"Button.js","sources":["../src/button/Button.ts"],"sourcesContent":["/* eslint-disable lit-a11y/accessible-name */\nimport type { TemplateResult } from 'lit'\nimport { html, LitElement, nothing } from 'lit'\nimport { customElement, property } from 'lit/decorators.js'\nimport { createRef, ref } from 'lit/directives/ref.js'\nimport { EventController } from '../common/controllers/EventController.js'\nimport { LightDomController } from '../common/controllers/LightDomController.js'\nimport { SlotController } from '../common/controllers/SlotController.js'\n\nimport { cond } from '../common/directives/cond.js'\n\nimport { FocusableMixin } from '../common/mixins/FocusableMixin.js'\nimport { InputMixin } from '../common/mixins/InputMixin.js'\nimport componentStyle from '../common/styles/Component.css'\nimport style from './Button.css'\nimport '../spinner/Spinner.js'\n\n/**\n * Buttons are used for interface actions. Primary style should be\n * used only once per section for main call-to-action, while other\n * styles can appear more frequently.\n *\n * @status ready\n * @category action\n * @slot - The button content\n * @slot start - Used to place content at the start of button text. Typically used for icons.\n * @slot end - Used to place content at the end of button text. Typically used for icons.\n *\n * @cssprop [--n-button-border-radius=var(--n-border-radius-s)] - Controls the rounded corners of the button, using [border radius tokens](/tokens/#border-radius).\n * @cssprop [--n-button-gap=var(--n-space-s)] - Controls the spacing between items within the button, using our [spacing tokens](/tokens/#space).\n * @cssprop [--n-button-gradient=linear-gradient(to bottom, rgba(0, 0, 0, 0) 50%, rgba(0, 0, 0, 0.013) 100%))] - Controls the overlayed gradient background on the button.\n * @cssprop [--n-button-background-color=var(--n-color-button)] - Controls the background color of the button, using our [color tokens](/tokens/#color).\n * @cssprop [--n-button-border-color=var(--n-color-border-strong)] - Controls the border color of the button, using our [color tokens](/tokens/#color).\n * @cssprop [--n-button-text-align=center] - Controls the text alignment for the text in the button.\n * @cssprop [--n-button-box-shadow=var(--n-box-shadow)] - Controls the surrounding shadow, using our [box shadow tokens](/tokens/#box-shadow).\n * @cssprop [--n-button-color=var(--n-color-text)] - Controls the color of the text within the button, using our [color tokens](/tokens/#color).\n * @cssprop [--n-button-padding-inline=calc(var(--n-space-m) / 1.2)] - Controls the inline, or left and right, padding of the button.\n * @cssprop [--n-button-font-size=var(--n-font-size-m)] - Controls the size of the text within the button, using our [font tokens](/tokens/#font).\n * @cssprop [--n-button-font-weight=var(--n-font-weight)] - Controls the weight of the text within the button, using our [font tokens](/tokens/#font).\n * @cssprop [--n-button-min-block-size=var(--n-space-xl)] - Controls the minimum block size, or height, of the button using our [spacing tokens](/tokens/#space).\n * @cssprop [--n-button-toggle-icon-color=var(--n-color-icon)] - Controls the color of toggle icon that gets rendered when the button is used as a [Dropdown](/components/dropdown/) toggle.\n * @cssprop [--n-button-user-select=none] - Controls the text selection behavior of the button text.\n */\n@customElement('nord-button')\nexport default class Button extends InputMixin(FocusableMixin(LitElement)) {\n static styles = [componentStyle, style]\n\n private defaultSlot = new SlotController(this)\n private buttonRef = createRef<HTMLButtonElement>()\n private events = new EventController(this)\n private lightDom = new LightDomController(this, {\n render: () => this.renderLightDom(),\n })\n\n /**\n * The style variant of the button.\n */\n @property({ reflect: true }) variant: 'default' | 'primary' | 'dashed' | 'plain' | 'danger' = 'default'\n\n /**\n * The type of the button.\n */\n @property({ reflect: true }) type: 'button' | 'submit' | 'reset' = 'submit'\n\n /**\n * The size of the button.\n * This affects font-size and padding.\n */\n @property({ reflect: true }) size: 's' | 'm' | 'l' = 'm'\n\n /**\n * This does not need to be documented,\n * since it is only for forwarding the aria-expanded attribute\n * to the internal button element.\n * @private\n */\n @property({ attribute: 'aria-expanded' }) accessibleExpanded?: 'true' | 'false'\n\n /**\n * This does not need to be documented,\n * since it is only for forwarding the aria-haspopup attribute\n * to the internal button element.\n * @private\n */\n @property({ attribute: 'aria-haspopup' }) accessibleHasPopup?:\n | 'false'\n | 'true'\n | 'menu'\n | 'listbox'\n | 'tree'\n | 'grid'\n | 'dialog'\n\n /**\n * When provided, renders the button as a link,\n * with its href attribute set to the given value.\n */\n @property({ reflect: true }) href?: string\n\n /**\n * When provided together with a href property, the button will\n * trigger a file download instead of a page visit.\n */\n @property({ reflect: true, type: Boolean }) download = false\n\n /**\n * When provided together with a href property, determines where\n * to open the linked URL. The keywords have special meanings for\n * where to load the URL: “_self” means the current browsing context,\n * “_blank” usually a new tab but users can configure browsers this to\n * open a new window instead, “_parent” means the parent browsing\n * context of the current one, but if no parent exists, behaves as\n * _self, and finally “top” means the topmost browsing context.\n */\n @property() target: '_self' | '_blank' | '_parent' | '_top' = '_self'\n\n /**\n * Controls whether the button expands to fill the width of its container.\n */\n @property({ reflect: true, type: Boolean }) expand = false\n\n /**\n * When provided, the button is rendered as a square button. Use this for icon only buttons.\n */\n @property({ reflect: true, type: Boolean }) square = false\n\n /**\n * Controls whether the button is in loading state. Please note that the spinner\n * is hidden from assistive technologies, so you need to make sure to announce\n * the loading state to e.g. screen reader users. We also recommend disabling\n * all user interactions on the button itself while in loading state.\n */\n @property({ reflect: true, type: Boolean }) loading = false\n\n /**\n * Controls whether the `interface-dropdown-small` icon from Nordicons is hidden\n * in the `end` slot when used as a dropdown toggle.\n */\n @property({ reflect: true, type: Boolean, attribute: 'hide-dropdown-icon' }) hideDropdownIcon = false\n\n connectedCallback() {\n super.connectedCallback()\n this.events.listen(this, 'click', this.handleOuterClick, true)\n }\n\n render() {\n const isDropdownToggle = this.assignedSlot?.name === 'toggle'\n const isIconButton = this.defaultSlot.assigned.some(node => node.localName === 'nord-icon')\n const shouldShowDropdownIcon = isDropdownToggle && !this.hideDropdownIcon && !isIconButton && !this.href\n\n const innards = html`\n <slot name=\"start\"></slot>\n <div class=\"n-content\">\n <slot></slot>\n </div>\n <nord-spinner\n class=\"n-button-spinner\"\n color=\"currentColor\"\n ?hidden=${!this.loading || Boolean(this.href)}\n ></nord-spinner>\n <slot name=\"end\">\n ${shouldShowDropdownIcon\n ? html`<nord-icon name=\"interface-dropdown-small\" class=\"n-toggle-icon\"></nord-icon>`\n : nothing}\n </slot>\n `\n\n return this.href ? this.renderLink(innards) : this.renderButton(innards)\n }\n\n /**\n * We jump through some hoops here to ensure the link is treated correctly when \"disabled\".\n * Links cannot be disabled natively, so we need to rely on some aria magic to get the correct semantics.\n * Along with the advice in the article below, we also set tabindex to \"-1\", so it is taken out of tab order.\n *\n * @see https://www.scottohara.me/blog/2021/05/28/disabled-links.html\n */\n private renderLink(innards: TemplateResult) {\n return html`\n <a\n ${ref(this.focusableRef)}\n class=\"n-button\"\n target=${this.target}\n ?download=${this.download}\n href=${cond(this.disabled, nothing, this.href)}\n tabindex=${cond(this.disabled, '-1')}\n aria-disabled=${cond(this.disabled, 'true')}\n role=${cond(this.disabled, 'link')}\n >${innards}\n </a>\n `\n }\n\n private renderButton(innards: TemplateResult) {\n return html`\n <slot name=\"proxy\" @slotchange=${this.handleProxyChange}></slot>\n <button\n ${ref(this.focusableRef)}\n class=\"n-button\"\n ?disabled=${this.disabled}\n name=${cond(this.name)}\n value=${cond(this.value)}\n @click=${this.handleClick}\n aria-disabled=${cond(this.loading, 'true')}\n aria-expanded=${cond(this.accessibleExpanded)}\n aria-haspopup=${cond(this.accessibleHasPopup)}\n >\n ${innards}\n </button>\n `\n }\n\n private renderLightDom() {\n if (this.href || !this.form) {\n return nothing\n }\n\n return html`\n <button\n ${ref(this.buttonRef)}\n slot=\"proxy\"\n name=${cond(this.name)}\n value=${cond(this.value)}\n ?disabled=${this.disabled}\n form=${cond(this._formId)}\n type=${this.type}\n ></button>\n `\n }\n\n private handleOuterClick = (e: MouseEvent) => {\n // we want to avoid emitting click events when a click\n // happens in blank space in the host, but not on the button\n // so we stop propagation of any events if click didn't happen on the internal or proxy button\n const isInternalButton = e\n .composedPath()\n .some(node => node === this.focusableRef.value || node === this.buttonRef.value)\n\n if (!isInternalButton) {\n e.stopPropagation()\n }\n }\n\n private handleClick(e: Event) {\n if (this.buttonRef.value) {\n // prevents two events: one from internal button, one from proxy button\n e.stopPropagation()\n this.buttonRef.value.click()\n }\n }\n\n /**\n * React/Vue etc may remove our proxy button when updating button text, since they are unaware of its existence.\n * So we listen for a slotchange event, and if the element is no longer connected to the DOM we add it back in.\n */\n private handleProxyChange(e: Event) {\n const slot = e.target as HTMLSlotElement\n const proxyButton = this.buttonRef.value\n\n if (proxyButton && proxyButton.assignedSlot !== slot) {\n this.appendChild(proxyButton)\n }\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'nord-button': Button\n }\n}\n"],"names":["Button","InputMixin","FocusableMixin","LitElement","constructor","this","defaultSlot","SlotController","buttonRef","createRef","events","EventController","lightDom","LightDomController","render","renderLightDom","variant","type","size","download","target","expand","square","loading","hideDropdownIcon","handleOuterClick","e","composedPath","some","node","focusableRef","value","stopPropagation","connectedCallback","super","listen","isDropdownToggle","_a","assignedSlot","name","isIconButton","assigned","localName","shouldShowDropdownIcon","href","innards","html","Boolean","nothing","renderLink","renderButton","ref","cond","disabled","handleProxyChange","handleClick","accessibleExpanded","accessibleHasPopup","form","_formId","click","slot","proxyButton","appendChild","styles","componentStyle","style","__decorate","property","reflect","prototype","attribute","customElement"],"mappings":"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"}
1
+ {"version":3,"file":"Button.js","sources":["../src/button/Button.ts"],"sourcesContent":["/* eslint-disable lit-a11y/accessible-name */\nimport type { TemplateResult } from 'lit'\nimport { html, LitElement, nothing } from 'lit'\nimport { customElement, property } from 'lit/decorators.js'\nimport { createRef, ref } from 'lit/directives/ref.js'\nimport { EventController } from '../common/controllers/EventController.js'\nimport { LightDomController } from '../common/controllers/LightDomController.js'\nimport { SlotController } from '../common/controllers/SlotController.js'\n\nimport { cond } from '../common/directives/cond.js'\n\nimport { FocusableMixin } from '../common/mixins/FocusableMixin.js'\nimport { InputMixin } from '../common/mixins/InputMixin.js'\nimport componentStyle from '../common/styles/Component.css'\nimport style from './Button.css'\nimport '../spinner/Spinner.js'\n\n/**\n * Buttons are used for interface actions. Primary style should be\n * used only once per section for main call-to-action, while other\n * styles can appear more frequently.\n *\n * @status ready\n * @category action\n * @slot - The button content\n * @slot start - Used to place content at the start of button text. Typically used for icons.\n * @slot end - Used to place content at the end of button text. Typically used for icons.\n *\n * @cssprop [--n-button-border-radius=var(--n-border-radius-s)] - Controls the rounded corners of the button, using [border radius tokens](/tokens/#border-radius).\n * @cssprop [--n-button-gap=var(--n-space-s)] - Controls the spacing between items within the button, using our [spacing tokens](/tokens/#space).\n * @cssprop [--n-button-gradient=linear-gradient(to bottom, rgba(0, 0, 0, 0) 50%, rgba(0, 0, 0, 0.013) 100%))] - Controls the overlayed gradient background on the button.\n * @cssprop [--n-button-background-color=var(--n-color-button)] - Controls the background color of the button, using our [color tokens](/tokens/#color).\n * @cssprop [--n-button-border-color=var(--n-color-border-strong)] - Controls the border color of the button, using our [color tokens](/tokens/#color).\n * @cssprop [--n-button-text-align=center] - Controls the text alignment for the text in the button.\n * @cssprop [--n-button-box-shadow=var(--n-box-shadow)] - Controls the surrounding shadow, using our [box shadow tokens](/tokens/#box-shadow).\n * @cssprop [--n-button-color=var(--n-color-text)] - Controls the color of the text within the button, using our [color tokens](/tokens/#color).\n * @cssprop [--n-button-padding-inline=calc(var(--n-space-m) / 1.2)] - Controls the inline, or left and right, padding of the button.\n * @cssprop [--n-button-font-size=var(--n-font-size-m)] - Controls the size of the text within the button, using our [font tokens](/tokens/#font).\n * @cssprop [--n-button-font-weight=var(--n-font-weight)] - Controls the weight of the text within the button, using our [font tokens](/tokens/#font).\n * @cssprop [--n-button-min-block-size=var(--n-space-xl)] - Controls the minimum block size, or height, of the button using our [spacing tokens](/tokens/#space).\n * @cssprop [--n-button-toggle-icon-color=var(--n-color-icon)] - Controls the color of toggle icon that gets rendered when the button is used as a [Dropdown](/components/dropdown/) toggle.\n * @cssprop [--n-button-user-select=none] - Controls the text selection behavior of the button text.\n */\n@customElement('nord-button')\nexport default class Button extends InputMixin(FocusableMixin(LitElement)) {\n static shadowRootOptions = { ...LitElement.shadowRootOptions, delegatesFocus: true }\n\n static styles = [componentStyle, style]\n\n private defaultSlot = new SlotController(this)\n private buttonRef = createRef<HTMLButtonElement>()\n private events = new EventController(this)\n private lightDom = new LightDomController(this, {\n render: () => this.renderLightDom(),\n })\n\n /**\n * The style variant of the button.\n */\n @property({ reflect: true }) variant: 'default' | 'primary' | 'dashed' | 'plain' | 'danger' = 'default'\n\n /**\n * The type of the button.\n */\n @property({ reflect: true }) type: 'button' | 'submit' | 'reset' = 'submit'\n\n /**\n * The size of the button.\n * This affects font-size and padding.\n */\n @property({ reflect: true }) size: 's' | 'm' | 'l' = 'm'\n\n /**\n * This does not need to be documented,\n * since it is only for forwarding the aria-expanded attribute\n * to the internal button element.\n * @private\n */\n @property({ attribute: 'aria-expanded' }) accessibleExpanded?: 'true' | 'false'\n\n /**\n * This does not need to be documented,\n * since it is only for forwarding the aria-haspopup attribute\n * to the internal button element.\n * @private\n */\n @property({ attribute: 'aria-haspopup' }) accessibleHasPopup?:\n | 'false'\n | 'true'\n | 'menu'\n | 'listbox'\n | 'tree'\n | 'grid'\n | 'dialog'\n\n /**\n * When provided, renders the button as a link,\n * with its href attribute set to the given value.\n */\n @property({ reflect: true }) href?: string\n\n /**\n * When provided together with a href property, the button will\n * trigger a file download instead of a page visit.\n */\n @property({ reflect: true, type: Boolean }) download = false\n\n /**\n * When provided together with a href property, determines where\n * to open the linked URL. The keywords have special meanings for\n * where to load the URL: “_self” means the current browsing context,\n * “_blank” usually a new tab but users can configure browsers this to\n * open a new window instead, “_parent” means the parent browsing\n * context of the current one, but if no parent exists, behaves as\n * _self, and finally “top” means the topmost browsing context.\n */\n @property() target: '_self' | '_blank' | '_parent' | '_top' = '_self'\n\n /**\n * Controls whether the button expands to fill the width of its container.\n */\n @property({ reflect: true, type: Boolean }) expand = false\n\n /**\n * When provided, the button is rendered as a square button. Use this for icon only buttons.\n */\n @property({ reflect: true, type: Boolean }) square = false\n\n /**\n * Controls whether the button is in loading state. Please note that the spinner\n * is hidden from assistive technologies, so you need to make sure to announce\n * the loading state to e.g. screen reader users. We also recommend disabling\n * all user interactions on the button itself while in loading state.\n */\n @property({ reflect: true, type: Boolean }) loading = false\n\n /**\n * Controls whether the `interface-dropdown-small` icon from Nordicons is hidden\n * in the `end` slot when used as a dropdown toggle.\n */\n @property({ reflect: true, type: Boolean, attribute: 'hide-dropdown-icon' }) hideDropdownIcon = false\n\n connectedCallback() {\n super.connectedCallback()\n this.events.listen(this, 'click', this.handleOuterClick, true)\n }\n\n render() {\n const isDropdownToggle = this.assignedSlot?.name === 'toggle'\n const isIconButton = this.defaultSlot.assigned.some(node => node.localName === 'nord-icon')\n const shouldShowDropdownIcon = isDropdownToggle && !this.hideDropdownIcon && !isIconButton && !this.href\n\n const innards = html`\n <slot name=\"start\"></slot>\n <div class=\"n-content\">\n <slot></slot>\n </div>\n <nord-spinner\n class=\"n-button-spinner\"\n color=\"currentColor\"\n ?hidden=${!this.loading || Boolean(this.href)}\n ></nord-spinner>\n <slot name=\"end\">\n ${shouldShowDropdownIcon\n ? html`<nord-icon name=\"interface-dropdown-small\" class=\"n-toggle-icon\"></nord-icon>`\n : nothing}\n </slot>\n `\n\n return this.href ? this.renderLink(innards) : this.renderButton(innards)\n }\n\n /**\n * We jump through some hoops here to ensure the link is treated correctly when \"disabled\".\n * Links cannot be disabled natively, so we need to rely on some aria magic to get the correct semantics.\n * Along with the advice in the article below, we also set tabindex to \"-1\", so it is taken out of tab order.\n *\n * @see https://www.scottohara.me/blog/2021/05/28/disabled-links.html\n */\n private renderLink(innards: TemplateResult) {\n return html`\n <a\n ${ref(this.focusableRef)}\n class=\"n-button\"\n target=${this.target}\n ?download=${this.download}\n href=${cond(this.disabled, nothing, this.href)}\n tabindex=${cond(this.disabled, '-1')}\n aria-disabled=${cond(this.disabled, 'true')}\n role=${cond(this.disabled, 'link')}\n >${innards}\n </a>\n `\n }\n\n private renderButton(innards: TemplateResult) {\n return html`\n <slot name=\"proxy\" @slotchange=${this.handleProxyChange}></slot>\n <button\n ${ref(this.focusableRef)}\n class=\"n-button\"\n ?disabled=${this.disabled}\n name=${cond(this.name)}\n value=${cond(this.value)}\n @click=${this.handleClick}\n aria-disabled=${cond(this.loading, 'true')}\n aria-expanded=${cond(this.accessibleExpanded)}\n aria-haspopup=${cond(this.accessibleHasPopup)}\n >\n ${innards}\n </button>\n `\n }\n\n private renderLightDom() {\n if (this.href || !this.form) {\n return nothing\n }\n\n return html`\n <button\n ${ref(this.buttonRef)}\n slot=\"proxy\"\n name=${cond(this.name)}\n value=${cond(this.value)}\n ?disabled=${this.disabled}\n form=${cond(this._formId)}\n type=${this.type}\n ></button>\n `\n }\n\n private handleOuterClick = (e: MouseEvent) => {\n // we want to avoid emitting click events when a click\n // happens in blank space in the host, but not on the button\n // so we stop propagation of any events if click didn't happen on the internal or proxy button\n const isInternalButton = e\n .composedPath()\n .some(node => node === this.focusableRef.value || node === this.buttonRef.value)\n\n if (!isInternalButton) {\n e.stopPropagation()\n }\n }\n\n private handleClick(e: Event) {\n if (this.buttonRef.value) {\n // prevents two events: one from internal button, one from proxy button\n e.stopPropagation()\n this.buttonRef.value.click()\n }\n }\n\n /**\n * React/Vue etc may remove our proxy button when updating button text, since they are unaware of its existence.\n * So we listen for a slotchange event, and if the element is no longer connected to the DOM we add it back in.\n */\n private handleProxyChange(e: Event) {\n const slot = e.target as HTMLSlotElement\n const proxyButton = this.buttonRef.value\n\n if (proxyButton && proxyButton.assignedSlot !== slot) {\n this.appendChild(proxyButton)\n }\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'nord-button': Button\n }\n}\n"],"names":["Button","InputMixin","FocusableMixin","LitElement","constructor","this","defaultSlot","SlotController","buttonRef","createRef","events","EventController","lightDom","LightDomController","render","renderLightDom","variant","type","size","download","target","expand","square","loading","hideDropdownIcon","handleOuterClick","e","composedPath","some","node","focusableRef","value","stopPropagation","connectedCallback","super","listen","isDropdownToggle","_a","assignedSlot","name","isIconButton","assigned","localName","shouldShowDropdownIcon","href","innards","html","Boolean","nothing","renderLink","renderButton","ref","cond","disabled","handleProxyChange","handleClick","accessibleExpanded","accessibleHasPopup","form","_formId","click","slot","proxyButton","appendChild","shadowRootOptions","delegatesFocus","styles","componentStyle","style","__decorate","property","reflect","prototype","attribute","customElement"],"mappings":"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"}