@ds-mo/ui 0.8.4 → 0.8.6
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.
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{p as a,H as e,c as n,h as t,a as i,t as o}from"./index.js";import{d as s}from"./p-Cakvrs26.js";import{d as r}from"./p-CIMd6yG3.js";const l=()=>`.panel-nav.sc-ds-panel-nav{--_nav-width:200px;--_nav-width-collapsed:48px;--_nav-item-height:var(--dimension-size-400, 32px);--_nav-padding-x:var(--dimension-space-100, 8px);--_nav-padding-y:var(--dimension-space-100, 8px);--_nav-footer-btn-w:calc(var(--dimension-space-075, 6px) * 2 + var(--dimension-iconography-md, 20px));--_nav-speed:var(--ds-panel-nav-speed, 1);--_nav-expand-dur:calc(var(--_nav-speed) * var(--effect-animation-duration-medium-1, 300ms));--_nav-expand-motion:var(--_nav-expand-dur) var(--effect-animation-easing-ease-in-out, ease);--_nav-label-fade-dur:calc(var(--_nav-speed) * var(--effect-animation-duration-medium-1, 300ms));--_nav-label-fade-motion:var(--_nav-label-fade-dur) var(--effect-animation-easing-ease-in-out, ease);--_nav-label-fade-out-dur:calc(var(--_nav-speed) * var(--effect-animation-duration-medium-1, 300ms));--_nav-label-fade-out-motion:var(--_nav-label-fade-out-dur) var(--effect-animation-easing-ease-in-out, ease);--_nav-bg:var(--color-navigation-background);--_nav-border:var(--color-navigation-border-tertiary);--_nav-divider:var(--color-navigation-divider);--_nav-fg-primary:var(--color-navigation-foreground-primary);--_nav-fg-secondary:var(--color-navigation-foreground-secondary);--_nav-fg-tertiary:var(--color-navigation-foreground-tertiary);--_nav-hover:var(--color-navigation-interaction-hover);--_nav-pressed:var(--color-navigation-interaction-pressed);--_nav-focus:var(--color-navigation-interaction-focus);--_nav-dot:var(--color-navigation-foreground-brand)}.panel-nav--settings.sc-ds-panel-nav{--_nav-bg:var(--color-background-secondary);--_nav-border:var(--color-border-tertiary);--_nav-divider:var(--color-border-tertiary);--_nav-fg-primary:var(--color-foreground-primary);--_nav-fg-secondary:var(--color-foreground-secondary);--_nav-fg-tertiary:var(--color-foreground-tertiary);--_nav-hover:var(--color-interaction-hover);--_nav-pressed:var(--color-interaction-pressed);--_nav-focus:var(--color-interaction-focus);--_nav-dot:var(--color-foreground-bold-brand)}.panel-nav.sc-ds-panel-nav{display:flex;flex-direction:column;width:var(--_nav-width);min-width:var(--_nav-width);height:100%;background:var(--_nav-bg);border-right:var(--dimension-stroke-width-012, 1px) solid var(--_nav-border);box-sizing:border-box;overflow:hidden;transition:width var(--_nav-expand-motion), min-width var(--_nav-expand-motion), background var(--effect-motion-short-3, 200ms ease), border-color var(--effect-motion-short-3, 200ms ease);font-family:var(--typography-font-family, system-ui)}.panel-nav--collapsed.sc-ds-panel-nav{width:var(--_nav-width-collapsed);min-width:var(--_nav-width-collapsed)}.panel-nav__header.sc-ds-panel-nav{display:flex;align-items:center;padding:var(--dimension-space-100, 8px);flex-shrink:0;box-sizing:border-box;border-bottom:var(--dimension-stroke-width-012, 1px) solid var(--_nav-divider);transition:border-color var(--effect-motion-short-3, 200ms ease)}.panel-nav__header-btn.sc-ds-panel-nav{all:unset;display:flex;align-items:center;justify-content:center;padding:var(--dimension-space-075, 6px);border-radius:var(--dimension-radius-025, 2px);cursor:pointer;color:var(--_nav-fg-primary);position:relative;flex-shrink:0;box-sizing:border-box;transition:color var(--effect-motion-short-3, 200ms ease)}.panel-nav__header-btn.sc-ds-panel-nav::after{content:'';position:absolute;inset:0;border-radius:var(--dimension-radius-025, 2px);background:transparent;transition:background var(--effect-animation-duration-short-3, 200ms) var(--effect-animation-easing-ease-out, ease);pointer-events:none}.panel-nav__header-btn.sc-ds-panel-nav:hover::after{background:var(--_nav-hover);transition:none}.panel-nav__header-btn.sc-ds-panel-nav:active::after{background:var(--_nav-pressed);transition:none}.panel-nav__header-btn.sc-ds-panel-nav:focus-visible{outline:none}.panel-nav__header-btn.sc-ds-panel-nav:focus-visible::after{outline:var(--dimension-stroke-width-025, 2px) solid var(--_nav-focus);outline-offset:calc(-1 * var(--dimension-stroke-width-025, 2px))}.panel-nav__header-logo.sc-ds-panel-nav{display:flex;align-items:center;justify-content:center;position:relative;z-index:1;transition:opacity var(--effect-motion-short-2, 100ms ease)}.panel-nav__m-mark.sc-ds-panel-nav{width:var(--dimension-iconography-md, 20px);height:var(--dimension-iconography-md, 20px);display:block}.panel-nav__header-toggle.sc-ds-panel-nav{display:none;align-items:center;justify-content:center;position:absolute;inset:0;z-index:1}.panel-nav__header-btn.sc-ds-panel-nav:hover .panel-nav__header-logo.sc-ds-panel-nav{opacity:0}.panel-nav__header-btn.sc-ds-panel-nav:hover .panel-nav__header-toggle.sc-ds-panel-nav{display:flex}.panel-nav__body.sc-ds-panel-nav{flex:1;overflow-y:auto;overflow-x:hidden;display:flex;flex-direction:column;gap:var(--dimension-space-200, 16px);padding:var(--dimension-space-100, 8px);box-sizing:border-box;scrollbar-width:none}.panel-nav__body.sc-ds-panel-nav::-webkit-scrollbar{display:none}.panel-nav__group.sc-ds-panel-nav{display:flex;flex-direction:column;gap:var(--dimension-space-050, 4px)}.panel-nav__group-label.sc-ds-panel-nav{display:flex;align-items:center;text-transform:uppercase;color:var(--_nav-fg-secondary);user-select:none;height:var(--dimension-size-300, 24px);line-height:var(--typography-lineheight-xs, 12px);padding:0 var(--dimension-space-075, 6px);white-space:nowrap;overflow:hidden;max-height:var(--dimension-size-300, 24px);opacity:1;transition:opacity var(--_nav-label-fade-motion) 0ms, max-height var(--_nav-label-fade-motion) 0ms, padding var(--_nav-label-fade-motion) 0ms, color var(--effect-motion-short-3, 200ms ease)}.panel-nav__item.sc-ds-panel-nav{all:unset;display:flex;align-items:center;gap:var(--dimension-space-050, 4px);padding:0 var(--dimension-space-075, 6px);height:var(--dimension-size-400, 32px);border-radius:var(--dimension-radius-025, 2px);cursor:pointer;color:var(--_nav-fg-secondary);position:relative;box-sizing:border-box;width:100%;user-select:none;transition:color var(--effect-motion-short-1, 50ms ease-in-out)}.panel-nav__item.sc-ds-panel-nav::after{content:'';position:absolute;inset:0;border-radius:var(--dimension-radius-025, 2px);background:transparent;pointer-events:none;transition:background var(--effect-animation-duration-short-3, 200ms) var(--effect-animation-easing-ease-out, ease)}.panel-nav__item.sc-ds-panel-nav:hover::after{background:var(--_nav-hover);transition:none}.panel-nav__item.sc-ds-panel-nav:active::after{background:var(--_nav-pressed);transition:none}.panel-nav__item.sc-ds-panel-nav:focus-visible{outline:none}.panel-nav__item.sc-ds-panel-nav:focus-visible::after{outline:var(--dimension-stroke-width-025, 2px) solid var(--_nav-focus);outline-offset:calc(-1 * var(--dimension-stroke-width-025, 2px))}.panel-nav__item--active.sc-ds-panel-nav{color:var(--_nav-fg-primary)}.panel-nav__item-icon.sc-ds-panel-nav{display:flex;align-items:center;justify-content:center;flex-shrink:0;width:var(--dimension-iconography-md, 20px);height:var(--dimension-iconography-md, 20px);position:relative}.panel-nav__item-label.sc-ds-panel-nav{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;min-width:0;height:var(--typography-lineheight-md, 20px);line-height:var(--typography-lineheight-md, 20px);opacity:1;mask-image:linear-gradient(to right, black 50%, transparent 100%);-webkit-mask-image:linear-gradient(to right, black 50%, transparent 100%);mask-size:200% 100%;-webkit-mask-size:200% 100%;mask-position:0% 0;-webkit-mask-position:0% 0;transition:opacity var(--_nav-label-fade-motion) 0ms, mask-position var(--_nav-label-fade-motion) 0ms, -webkit-mask-position var(--_nav-label-fade-motion) 0ms}.panel-nav__item-label-text.sc-ds-panel-nav{padding:0 var(--dimension-space-025, 2px)}.panel-nav__item-dot.sc-ds-panel-nav{position:absolute;right:13px;top:13px;width:var(--dimension-space-075, 6px);height:var(--dimension-space-075, 6px);border-radius:var(--dimension-radius-full, 999px);background:var(--_nav-dot);box-shadow:0 0 0 var(--dimension-stroke-width-018, 1.5px) var(--_nav-bg);pointer-events:none;transition:right var(--_nav-expand-motion), top var(--_nav-expand-motion), background var(--effect-motion-short-3, 200ms ease), box-shadow var(--effect-motion-short-3, 200ms ease)}.panel-nav--collapsed.sc-ds-panel-nav .panel-nav__item-dot.sc-ds-panel-nav{right:6px;top:6px}.panel-nav--animating.sc-ds-panel-nav .panel-nav__item-label.sc-ds-panel-nav{text-overflow:clip}.panel-nav--collapsed.sc-ds-panel-nav .panel-nav__item-label.sc-ds-panel-nav{opacity:0;pointer-events:none;text-overflow:clip;mask-position:100% 0;-webkit-mask-position:100% 0;transition:opacity var(--_nav-label-fade-out-motion) 0ms, mask-position var(--_nav-label-fade-out-motion) 0ms, -webkit-mask-position var(--_nav-label-fade-out-motion) 0ms}.panel-nav--collapsed.sc-ds-panel-nav .panel-nav__group-label.sc-ds-panel-nav{opacity:0;max-height:0;padding:0 var(--dimension-space-075, 6px);pointer-events:none;transition:opacity var(--_nav-label-fade-out-motion) 0ms, max-height var(--_nav-label-fade-out-motion) 0ms, padding var(--_nav-label-fade-out-motion) 0ms, color var(--effect-motion-short-3, 200ms ease)}.panel-nav__footer.sc-ds-panel-nav{position:relative;flex-shrink:0;height:calc(var(--_nav-padding-y) * 2 + var(--_nav-item-height));transition:height var(--_nav-expand-motion)}.panel-nav--collapsed.sc-ds-panel-nav .panel-nav__footer.sc-ds-panel-nav{height:calc(var(--_nav-padding-y) * 2 + var(--_nav-item-height) * 2 + var(--dimension-space-050, 4px))}.panel-nav__footer.sc-ds-panel-nav::before{content:'';position:absolute;bottom:100%;left:0;right:0;height:calc(var(--dimension-base, 8px) * 6);background:linear-gradient(to bottom, transparent, var(--_nav-bg));pointer-events:none;opacity:1;transition:opacity var(--effect-animation-duration-short-3, 200ms) var(--effect-animation-easing-ease-out, ease)}.panel-nav--at-bottom.sc-ds-panel-nav .panel-nav__footer.sc-ds-panel-nav::before{opacity:0}.panel-nav__footer-btn.sc-ds-panel-nav{all:unset;display:flex;align-items:center;justify-content:center;position:absolute;left:var(--_nav-padding-x);top:var(--_nav-padding-y);padding:var(--dimension-space-075, 6px);border-radius:var(--dimension-radius-half, 9999px);cursor:pointer;color:var(--_nav-fg-secondary);box-sizing:border-box;background:transparent;transition:background var(--effect-animation-duration-short-3, 200ms) var(--effect-animation-easing-ease-out, ease), color var(--effect-animation-duration-short-3, 200ms) var(--effect-animation-easing-ease-out, ease)}.panel-nav__footer-btn.sc-ds-panel-nav:focus-visible{outline:var(--dimension-stroke-width-025, 2px) solid var(--_nav-focus);outline-offset:calc(-1 * var(--dimension-stroke-width-025, 2px))}.panel-nav--dashboard.sc-ds-panel-nav .panel-nav__footer-btn.sc-ds-panel-nav:hover{background:var(--color-background-secondary);color:var(--color-foreground-secondary);transition:none}.panel-nav--settings.sc-ds-panel-nav .panel-nav__footer-btn.sc-ds-panel-nav:hover{background:var(--color-navigation-background);color:var(--color-navigation-foreground-secondary);transition:none}.panel-nav__footer-user.sc-ds-panel-nav{position:absolute;left:calc(var(--_nav-padding-x) + var(--_nav-footer-btn-w) + var(--dimension-space-050, 4px));right:var(--_nav-padding-x);top:var(--_nav-padding-y);width:auto;min-width:0;transition:left var(--_nav-expand-motion), right var(--_nav-expand-motion), top var(--_nav-expand-motion), color var(--effect-motion-short-3, 200ms ease)}.panel-nav--collapsed.sc-ds-panel-nav .panel-nav__footer-user.sc-ds-panel-nav{left:var(--_nav-padding-x);right:var(--_nav-padding-x);top:calc(var(--_nav-padding-y) + var(--_nav-item-height) + var(--dimension-space-050, 4px))}.panel-nav__footer-user-label.sc-ds-panel-nav{text-align:right;position:absolute;left:var(--dimension-space-075, 6px);right:calc(var(--dimension-space-075, 6px) + var(--dimension-iconography-md, 20px) + var(--dimension-space-050, 4px));top:50%;transform:translateY(-50%);flex:none;height:var(--typography-lineheight-md, 20px);mask-image:none;-webkit-mask-image:none;transition:opacity var(--_nav-label-fade-motion)}.panel-nav--collapsed.sc-ds-panel-nav .panel-nav__footer-user-label.sc-ds-panel-nav{opacity:0;pointer-events:none;mask-position:0% 0;-webkit-mask-position:0% 0}.panel-nav__footer-user-icon.sc-ds-panel-nav{position:absolute;left:calc(100% - var(--dimension-iconography-md, 20px) - var(--dimension-space-075, 6px));top:0;bottom:0;height:auto;display:flex;align-items:center;justify-content:center;transition:left var(--_nav-expand-motion)}.panel-nav--collapsed.sc-ds-panel-nav .panel-nav__footer-user-icon.sc-ds-panel-nav{left:var(--dimension-space-075, 6px)}.panel-nav__footer-icon-expanded.sc-ds-panel-nav,.panel-nav__footer-icon-collapsed.sc-ds-panel-nav{position:absolute;inset:0;display:flex;align-items:center;justify-content:center}.panel-nav__footer-icon-expanded.sc-ds-panel-nav{opacity:1;transition:opacity var(--_nav-label-fade-motion)}.panel-nav__footer-icon-collapsed.sc-ds-panel-nav{opacity:0;transition:opacity var(--_nav-label-fade-motion)}.panel-nav--collapsed.sc-ds-panel-nav .panel-nav__footer-icon-expanded.sc-ds-panel-nav{opacity:0;transition:opacity var(--_nav-label-fade-out-motion)}.panel-nav--collapsed.sc-ds-panel-nav .panel-nav__footer-icon-collapsed.sc-ds-panel-nav{opacity:1;transition:opacity var(--_nav-label-fade-out-motion)}.panel-nav__user-initial.sc-ds-panel-nav{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;color:var(--_nav-fg-secondary);text-transform:uppercase;line-height:1;pointer-events:none;transition:color var(--effect-motion-short-3, 200ms ease)}.panel-nav__resize-handle.sc-ds-panel-nav{position:absolute;top:0;right:var(--dimension-space-n050, -4px);bottom:0;width:var(--dimension-space-100, 8px);z-index:10;cursor:ew-resize;touch-action:none;background:transparent}.panel-nav__resize-handle.sc-ds-panel-nav::before{content:'';position:absolute;inset:0;background:rgba(0, 0, 0, 0.001)}.panel-nav__resize-handle.sc-ds-panel-nav::after{content:'';position:absolute;top:0;bottom:0;left:50%;transform:translateX(-50%);width:var(--dimension-stroke-width-025, 2px);background:var(--color-background-bold-brand);opacity:0;pointer-events:none;transition:opacity var(--effect-motion-short-2, 100ms ease-in-out)}.panel-nav__resize-handle--overlay.sc-ds-panel-nav::after{opacity:1}@media (prefers-reduced-motion: reduce){.panel-nav.sc-ds-panel-nav,.panel-nav.sc-ds-panel-nav *.sc-ds-panel-nav,.panel-nav.sc-ds-panel-nav *.sc-ds-panel-nav::before,.panel-nav.sc-ds-panel-nav *.sc-ds-panel-nav::after,.panel-nav__resize-handle.sc-ds-panel-nav,.panel-nav__resize-handle.sc-ds-panel-nav::before,.panel-nav__resize-handle.sc-ds-panel-nav::after{transition-duration:0.01ms !important;animation-duration:0.01ms !important;transition-delay:0.01ms !important}}`;let d=false;function c(){if(d)return;const a="ds-panel-nav-vt-style";if(document.getElementById(a)){d=true;return}const e=document.createElement("style");e.id=a;e.textContent=["::view-transition-old(root),::view-transition-new(root){animation:none;mix-blend-mode:normal}","::view-transition-new(root){clip-path:circle(0px at var(--vt-x,50%) var(--vt-y,50%))}"].join("\n");document.head.appendChild(e);d=true}const v=a(class a extends e{constructor(a){super();if(a!==false){this.__registerHost()}this.dsNavSelect=n(this,"dsNavSelect");this.dsNavToggle=n(this,"dsNavToggle");this.dsNavFooterAction=n(this,"dsNavFooterAction");this.variant="dashboard";this.groups="[]";this.activeId="";this.collapsed=false;this.breakpoint=0;this.storageKey="";this.currentUrl="";this.userName="";this.userInitial="";this.renderedVariant="dashboard";this.parsedGroups=[];this.atBottom=false;this.isAnimating=false;this.rovingIndex=0;this.viewportNarrow=false;this.urlDerivedActiveId="";this.isDragging=false;this.dragStartX=0;this.didSnap=false;this.lastDeltaX=0;this.edgeOverlayTimer=null;this.showEdgeOverlay=false}get effectiveActiveId(){return this.urlDerivedActiveId||this.activeId}onCollapsedChange(){this.startCollapseAnimation()}onBreakpointChange(){this.disconnectResizeObserver();if(this.breakpoint>0)this.connectResizeObserver()}onGroupsChange(a){try{this.parsedGroups=JSON.parse(a)}catch{this.parsedGroups=[]}this.syncRovingIndex()}onActiveIdChange(){this.syncRovingIndex()}onCurrentUrlChange(){this.syncActiveFromUrl()}async onVariantChange(a){if(typeof document.startViewTransition!=="function"){this.renderedVariant=a;return}c();const e=this.el.querySelector(".panel-nav__footer-btn");const n=e?.getBoundingClientRect();const t=n?Math.round(n.left+n.width/2):Math.round(window.innerWidth/2);const i=n?Math.round(n.top+n.height/2):Math.round(window.innerHeight/2);const o=Math.ceil(Math.hypot(Math.max(t,window.innerWidth-t),Math.max(i,window.innerHeight-i)));document.documentElement.style.setProperty("--vt-x",`${t}px`);document.documentElement.style.setProperty("--vt-y",`${i}px`);const s=document.startViewTransition((async()=>{this.renderedVariant=a;await Promise.resolve();await Promise.resolve();await Promise.resolve();await Promise.resolve()}));s.ready.then((()=>{const a=getComputedStyle(document.documentElement).getPropertyValue("--effect-animation-duration-long-1").trim();const e=parseFloat(a)||750;document.documentElement.animate({clipPath:[`circle(0px at ${t}px ${i}px)`,`circle(${o}px at ${t}px ${i}px)`]},{duration:e,easing:"ease-in-out",fill:"forwards",pseudoElement:"::view-transition-new(root)"})}))}componentWillLoad(){this.renderedVariant=this.variant;if(this.storageKey){try{const a=localStorage.getItem(this.storageKey);if(a!==null)this.collapsed=a==="true"}catch{}}this.onGroupsChange(this.groups);this.syncActiveFromUrl()}componentDidLoad(){this.checkScroll();if(this.breakpoint>0)this.connectResizeObserver();this.onGroupsChange(this.groups)}disconnectedCallback(){this.disconnectResizeObserver();this.clearEdgeOverlayTimer();if(this.globalMouseMoveHandler){window.removeEventListener("mousemove",this.globalMouseMoveHandler)}if(this.globalMouseUpHandler){window.removeEventListener("mouseup",this.globalMouseUpHandler)}}startCollapseAnimation(){this.isAnimating=true;const a=this.el.querySelector(".panel-nav");if(this.transitionEndHandler){a?.removeEventListener("transitionend",this.transitionEndHandler)}this.transitionEndHandler=e=>{if(e.propertyName==="width"){this.isAnimating=false;a?.removeEventListener("transitionend",this.transitionEndHandler)}};a?.addEventListener("transitionend",this.transitionEndHandler)}connectResizeObserver(){this.viewportNarrow=window.innerWidth<this.breakpoint;this.resizeObserver=new ResizeObserver((()=>{this.viewportNarrow=window.innerWidth<this.breakpoint}));this.resizeObserver.observe(document.documentElement)}disconnectResizeObserver(){this.resizeObserver?.disconnect();this.resizeObserver=undefined}syncActiveFromUrl(){if(!this.currentUrl){this.urlDerivedActiveId="";return}let a;let e=0;for(const n of this.getAllItems()){if(n.href&&this.currentUrl.startsWith(n.href)&&n.href.length>e){a=n;e=n.href.length}}this.urlDerivedActiveId=a?.id??""}applyToggle(a){this.dsNavToggle.emit(a);if(this.storageKey){this.collapsed=a;try{localStorage.setItem(this.storageKey,String(a))}catch{}}}getAllItems(){return this.parsedGroups.flatMap((a=>a.items))}syncRovingIndex(){const a=this.getAllItems().findIndex((a=>a.id===this.effectiveActiveId));this.rovingIndex=a>=0?a:0}handleItemKeyDown(a,e){const n=this.getAllItems().length;let t=e;switch(a.key){case"ArrowDown":a.preventDefault();t=(e+1)%n;break;case"ArrowUp":a.preventDefault();t=(e-1+n)%n;break;case"Home":a.preventDefault();t=0;break;case"End":a.preventDefault();t=n-1;break;default:return}this.rovingIndex=t;const i=Array.from(this.el.querySelectorAll(".panel-nav__body .panel-nav__item"));i[t]?.focus()}checkScroll(){const a=this.el.querySelector(".panel-nav__body");if(!a)return;const e=a.scrollHeight-a.scrollTop-a.clientHeight;this.atBottom=e<2}handleBodyScroll(a){const e=a.target;const n=e.scrollHeight-e.scrollTop-e.clientHeight;this.atBottom=n<2}handleItemClick(a){this.dsNavSelect.emit(a)}handleToggle(){this.applyToggle(!this.collapsed)}handleFooterAction(){this.dsNavFooterAction.emit()}clearEdgeOverlayTimer(){if(this.edgeOverlayTimer!==null){window.clearTimeout(this.edgeOverlayTimer);this.edgeOverlayTimer=null}}handleResizeHandleMouseEnter(){this.clearEdgeOverlayTimer();this.edgeOverlayTimer=window.setTimeout((()=>{this.showEdgeOverlay=true;this.edgeOverlayTimer=null}),500)}handleResizeHandleMouseLeave(){if(this.isDragging)return;this.clearEdgeOverlayTimer();this.showEdgeOverlay=false}handleResizeHandleMouseDown(a){a.preventDefault();a.stopPropagation();const e=this.collapsed||this.viewportNarrow;this.isDragging=true;this.dragStartX=a.clientX;this.didSnap=false;this.lastDeltaX=0;this.clearEdgeOverlayTimer();this.showEdgeOverlay=false;document.body.style.cursor="ew-resize";document.body.style.userSelect="none";const n=a=>{const n=a.clientX-this.dragStartX;this.lastDeltaX=Math.abs(n);if(this.didSnap)return;if(!e&&n<-8){this.applyToggle(true);this.didSnap=true}else if(e&&n>8){this.applyToggle(false);this.didSnap=true}};const t=()=>{if(!this.didSnap&&this.lastDeltaX<3){this.applyToggle(!e)}this.isDragging=false;this.showEdgeOverlay=false;document.body.style.cursor="";document.body.style.userSelect="";window.removeEventListener("mousemove",n);window.removeEventListener("mouseup",t);this.globalMouseMoveHandler=undefined;this.globalMouseUpHandler=undefined};this.globalMouseMoveHandler=n;this.globalMouseUpHandler=t;window.addEventListener("mousemove",n);window.addEventListener("mouseup",t)}renderNavItem(a,e,n){const i=a.id===this.effectiveActiveId;const o=[t("span",{class:"panel-nav__item-icon"},t("ds-icon",{name:a.icon,size:"md",color:"inherit",flag:a.flag})),t("span",{class:"panel-nav__item-label"},t("span",{class:"panel-nav__item-label-text"},t("ds-text",{as:"span",variant:i?"text-body-medium-emphasis":"text-body-medium",color:"inherit"},a.label))),a.dot&&t("span",{class:"panel-nav__item-dot","aria-hidden":"true"})];const s={class:{"panel-nav__item":true,"panel-nav__item--active":i},"aria-current":i?"page":undefined,title:n?a.label:undefined,tabIndex:e===this.rovingIndex?0:-1,onClick:()=>this.handleItemClick(a.id),onKeyDown:a=>this.handleItemKeyDown(a,e),onFocus:()=>{this.rovingIndex=e}};return a.href?t("a",{...s,href:a.href},o):t("button",{...s,type:"button"},o)}render(){const a=this.renderedVariant==="dashboard";const e=this.collapsed||this.viewportNarrow;const{userName:n,userInitial:o}=this;const s={"panel-nav":true,"panel-nav--dashboard":a,"panel-nav--settings":!a,"panel-nav--collapsed":e,"panel-nav--animating":this.isAnimating,"panel-nav--at-bottom":this.atBottom};return t(i,{key:"ae680687fa2bf9d14324b91e156f5971aa036abb",style:{display:"block",position:"relative"}},t("nav",{key:"ddb499a46e187847c0a5b7d29ef87973ece9dfd3",class:s,"aria-label":a?"Main navigation":"Settings navigation"},t("div",{key:"4e715e4e75c10c2ba1819ce186dcd6130f830204",class:"panel-nav__header"},t("button",{key:"c61e6d78858dafd7285eac47125b201ab84b09e2",type:"button",class:"panel-nav__header-btn",onClick:()=>this.handleToggle(),"aria-label":e?"Expand navigation":"Collapse navigation","aria-expanded":e?"false":"true"},t("span",{key:"c58991c7a62d377e7df581a2b0206689ce2c3447",class:"panel-nav__header-logo","aria-hidden":"true"},t("svg",{key:"ece4e1e16b9959716f080cd2c467d8d90040f3d5",class:"panel-nav__m-mark",viewBox:"0 0 20 20",fill:"none",xmlns:"http://www.w3.org/2000/svg",focusable:"false","aria-hidden":"true"},t("path",{key:"5232bf49ea3788f97a7d834436be29eadd40b091",d:"M11.1159 4.31537H7.67021L2.53401 13.0703H0V15.6875H4.02716L8.24319 8.49978V15.6846H11.6342L15.8289 8.47829V15.6846H18.7122V4.3125H15.2559L11.1159 11.3648V4.31537Z",fill:"currentColor"}))),t("span",{key:"e825b745999ce40260a8f97ed84f321f7fe21a3c",class:"panel-nav__header-toggle","aria-hidden":"true"},t("ds-icon",{key:"b59e959cf62da9b2f12a3f1eb8f65e47659ff0b0",name:e?"LeftExpandB":"LeftCollapseB",size:"md",color:"inherit"})))),t("div",{key:"73c8a24dbe915be57911f2124e2a31cfdccf75cb",class:"panel-nav__body",onScroll:a=>this.handleBodyScroll(a)},(()=>{let a=0;return this.parsedGroups.map((n=>t("div",{class:"panel-nav__group"},n.label&&t("span",{class:"panel-nav__group-label"},t("span",{class:"panel-nav__item-label-text"},t("ds-text",{as:"span",variant:"text-caption-emphasis",color:"inherit"},n.label))),n.items.map((n=>this.renderNavItem(n,a++,e))))))})()),t("div",{key:"76bad45780f3b5fdaf71c172a1f48c911341af1f",class:"panel-nav__footer"},t("button",{key:"5ab73257be15c305f3715654c0ef225b37bb14a9",type:"button",class:"panel-nav__footer-btn",title:a?"Settings":"Dashboard","aria-label":a?"Open settings":"Go to dashboard",onClick:()=>this.handleFooterAction()},t("ds-icon",{key:"1ccbd6db2248bc756e433e053fe6e6aa6271c0f7",name:a?"Gear":"Dashboard",size:"md",color:"inherit"})),t("button",{key:"b75798e7bfe95bb5ddf0c12bf282373acabf4dc9",type:"button",class:"panel-nav__item panel-nav__footer-user","aria-label":e?`User: ${n}`:`User menu for ${n}`},t("span",{key:"0b55239a03a3ec5d0b93084f72cdf3b6e6977473",class:"panel-nav__item-label panel-nav__footer-user-label"},t("span",{key:"42f5ded6be9e9cffcb847ee56735771fe8ffd25c",class:"panel-nav__item-label-text"},t("ds-text",{key:"2ebd74576ec47e63b5d80738e4c9a29b0bee6d1b",as:"span",variant:"text-body-medium-emphasis",color:"inherit"},n))),t("span",{key:"bcf0a5e3cb239fabffa59b791d80ffd30d477bb3",class:"panel-nav__item-icon panel-nav__footer-user-icon","aria-hidden":"true"},t("span",{key:"394f221d2000c1f59deda4b487fd3685ae1e2f38",class:"panel-nav__footer-icon-expanded"},t("ds-icon",{key:"bb31116d2443fd0560f316ea96ed9fd6d0fe0c96",name:"ChevronUpDown",size:"md",color:"inherit"})),t("span",{key:"cccf3c2d011b31a2b2c09fa3122cc14e497493c0",class:"panel-nav__footer-icon-collapsed"},t("ds-icon",{key:"a108c6fae071538633ed9bdb79ec8182aab03baf",name:"Circle",size:"md",color:"inherit"}),t("span",{key:"803540450ca913f8b507010441a58d9f30dd8f81",class:"panel-nav__user-initial"},t("ds-text",{key:"4255abd7287f24008293ba70f58f57264a224e15",as:"span",variant:"text-caption-emphasis",color:"inherit"},o))))))),!this.viewportNarrow&&t("div",{key:"2c7dfaeb3eee146484a6a6bd773a788e87de2321",class:{"panel-nav__resize-handle":true,"panel-nav__resize-handle--overlay":this.showEdgeOverlay},onMouseEnter:()=>this.handleResizeHandleMouseEnter(),onMouseLeave:()=>this.handleResizeHandleMouseLeave(),onMouseDown:a=>this.handleResizeHandleMouseDown(a),"aria-hidden":"true"}))}get el(){return this}static get watchers(){return{collapsed:[{onCollapsedChange:0}],viewportNarrow:[{onCollapsedChange:0}],breakpoint:[{onBreakpointChange:0}],groups:[{onGroupsChange:0}],activeId:[{onActiveIdChange:0}],urlDerivedActiveId:[{onActiveIdChange:0}],currentUrl:[{onCurrentUrlChange:0}],variant:[{onVariantChange:0}]}}static get style(){return l()}},[2,"ds-panel-nav",{variant:[1],groups:[1],activeId:[1,"active-id"],collapsed:[1028],breakpoint:[2],storageKey:[1,"storage-key"],currentUrl:[1,"current-url"],userName:[1,"user-name"],userInitial:[1,"user-initial"],renderedVariant:[32],parsedGroups:[32],atBottom:[32],isAnimating:[32],rovingIndex:[32],viewportNarrow:[32],urlDerivedActiveId:[32],showEdgeOverlay:[32]},undefined,{collapsed:[{onCollapsedChange:0}],viewportNarrow:[{onCollapsedChange:0}],breakpoint:[{onBreakpointChange:0}],groups:[{onGroupsChange:0}],activeId:[{onActiveIdChange:0}],urlDerivedActiveId:[{onActiveIdChange:0}],currentUrl:[{onCurrentUrlChange:0}],variant:[{onVariantChange:0}]}]);function p(){if(typeof customElements==="undefined"){return}const a=["ds-panel-nav","ds-icon","ds-text"];a.forEach((a=>{switch(a){case"ds-panel-nav":if(!customElements.get(o(a))){customElements.define(o(a),v)}break;case"ds-icon":if(!customElements.get(o(a))){s()}break;case"ds-text":if(!customElements.get(o(a))){r()}break}}))}p();const h=v;const f=p;export{h as DsPanelNav,f as defineCustomElement};
|
|
1
|
+
import{p as a,H as e,c as n,h as t,a as i,t as o}from"./index.js";import{d as s}from"./p-Cakvrs26.js";import{d as r}from"./p-CIMd6yG3.js";const l=()=>`.panel-nav.sc-ds-panel-nav{--_nav-width:200px;--_nav-width-collapsed:48px;--_nav-item-height:var(--dimension-size-400, 32px);--_nav-padding-x:var(--dimension-space-100, 8px);--_nav-padding-y:var(--dimension-space-100, 8px);--_nav-footer-btn-w:calc(var(--dimension-space-075, 6px) * 2 + var(--dimension-iconography-md, 20px));--_nav-speed:var(--ds-panel-nav-speed, 1);--_nav-expand-dur:calc(var(--_nav-speed) * var(--effect-animation-duration-medium-1, 300ms));--_nav-expand-motion:var(--_nav-expand-dur) var(--effect-animation-easing-ease-in-out, ease);--_nav-label-fade-dur:calc(var(--_nav-speed) * var(--effect-animation-duration-medium-1, 300ms));--_nav-label-fade-motion:var(--_nav-label-fade-dur) var(--effect-animation-easing-ease-in-out, ease);--_nav-label-fade-out-dur:calc(var(--_nav-speed) * var(--effect-animation-duration-medium-1, 300ms));--_nav-label-fade-out-motion:var(--_nav-label-fade-out-dur) var(--effect-animation-easing-ease-in-out, ease);--_nav-bg:var(--color-navigation-background);--_nav-border:var(--color-navigation-border-tertiary);--_nav-divider:var(--color-navigation-divider);--_nav-fg-primary:var(--color-navigation-foreground-primary);--_nav-fg-secondary:var(--color-navigation-foreground-secondary);--_nav-fg-tertiary:var(--color-navigation-foreground-tertiary);--_nav-hover:var(--color-navigation-interaction-hover);--_nav-pressed:var(--color-navigation-interaction-pressed);--_nav-focus:var(--color-navigation-interaction-focus);--_nav-dot:var(--color-navigation-foreground-brand)}.panel-nav--settings.sc-ds-panel-nav{--_nav-bg:var(--color-background-secondary);--_nav-border:var(--color-border-tertiary);--_nav-divider:var(--color-border-tertiary);--_nav-fg-primary:var(--color-foreground-primary);--_nav-fg-secondary:var(--color-foreground-secondary);--_nav-fg-tertiary:var(--color-foreground-tertiary);--_nav-hover:var(--color-interaction-hover);--_nav-pressed:var(--color-interaction-pressed);--_nav-focus:var(--color-interaction-focus);--_nav-dot:var(--color-foreground-bold-brand)}.panel-nav.sc-ds-panel-nav{display:flex;flex-direction:column;width:var(--_nav-width);min-width:var(--_nav-width);height:100%;background:var(--_nav-bg);border-right:var(--dimension-stroke-width-012, 1px) solid var(--_nav-border);box-sizing:border-box;overflow:hidden;transition:width var(--_nav-expand-motion), min-width var(--_nav-expand-motion), background var(--effect-motion-short-3, 200ms ease), border-color var(--effect-motion-short-3, 200ms ease);font-family:var(--typography-font-family, system-ui)}.panel-nav--collapsed.sc-ds-panel-nav{width:var(--_nav-width-collapsed);min-width:var(--_nav-width-collapsed)}.panel-nav__header.sc-ds-panel-nav{display:flex;align-items:center;padding:var(--dimension-space-100, 8px);flex-shrink:0;box-sizing:border-box;border-bottom:var(--dimension-stroke-width-012, 1px) solid var(--_nav-divider);transition:border-color var(--effect-motion-short-3, 200ms ease)}.panel-nav__header-btn.sc-ds-panel-nav{all:unset;display:flex;align-items:center;justify-content:center;padding:var(--dimension-space-075, 6px);border-radius:var(--dimension-radius-025, 2px);cursor:pointer;color:var(--_nav-fg-primary);position:relative;flex-shrink:0;box-sizing:border-box;transition:color var(--effect-motion-short-3, 200ms ease)}.panel-nav__header-btn.sc-ds-panel-nav::after{content:'';position:absolute;inset:0;border-radius:var(--dimension-radius-025, 2px);background:transparent;transition:background var(--effect-animation-duration-short-3, 200ms) var(--effect-animation-easing-ease-out, ease);pointer-events:none}.panel-nav__header-btn.sc-ds-panel-nav:hover::after{background:var(--_nav-hover);transition:none}.panel-nav__header-btn.sc-ds-panel-nav:active::after{background:var(--_nav-pressed);transition:none}.panel-nav__header-btn.sc-ds-panel-nav:focus-visible{outline:none}.panel-nav__header-btn.sc-ds-panel-nav:focus-visible::after{outline:var(--dimension-stroke-width-025, 2px) solid var(--_nav-focus);outline-offset:calc(-1 * var(--dimension-stroke-width-025, 2px))}.panel-nav__header-logo.sc-ds-panel-nav{display:flex;align-items:center;justify-content:center;position:relative;z-index:1;transition:opacity var(--effect-motion-short-2, 100ms ease)}.panel-nav__m-mark.sc-ds-panel-nav{width:var(--dimension-iconography-md, 20px);height:var(--dimension-iconography-md, 20px);display:block}.panel-nav__header-toggle.sc-ds-panel-nav{display:none;align-items:center;justify-content:center;position:absolute;inset:0;z-index:1}.panel-nav__header-btn.sc-ds-panel-nav:hover .panel-nav__header-logo.sc-ds-panel-nav{opacity:0}.panel-nav__header-btn.sc-ds-panel-nav:hover .panel-nav__header-toggle.sc-ds-panel-nav{display:flex}.panel-nav__body.sc-ds-panel-nav{flex:1;overflow-y:auto;overflow-x:hidden;display:flex;flex-direction:column;gap:var(--dimension-space-200, 16px);padding:var(--dimension-space-100, 8px);box-sizing:border-box;scrollbar-width:none}.panel-nav__body.sc-ds-panel-nav::-webkit-scrollbar{display:none}.panel-nav__group.sc-ds-panel-nav{display:flex;flex-direction:column;gap:var(--dimension-space-050, 4px)}.panel-nav__group-label.sc-ds-panel-nav{display:flex;align-items:center;text-transform:uppercase;color:var(--_nav-fg-secondary);user-select:none;height:var(--dimension-size-300, 24px);line-height:var(--typography-lineheight-xs, 12px);padding:0 var(--dimension-space-075, 6px);white-space:nowrap;overflow:hidden;max-height:var(--dimension-size-300, 24px);opacity:1;transition:opacity var(--_nav-label-fade-motion) 0ms, max-height var(--_nav-label-fade-motion) 0ms, padding var(--_nav-label-fade-motion) 0ms, color var(--effect-motion-short-3, 200ms ease)}.panel-nav__item.sc-ds-panel-nav{all:unset;display:flex;align-items:center;gap:var(--dimension-space-050, 4px);padding:0 var(--dimension-space-075, 6px);height:var(--dimension-size-400, 32px);border-radius:var(--dimension-radius-025, 2px);cursor:pointer;color:var(--_nav-fg-secondary);position:relative;box-sizing:border-box;width:100%;user-select:none;transition:color var(--effect-motion-short-1, 50ms ease-in-out)}.panel-nav__item.sc-ds-panel-nav::after{content:'';position:absolute;inset:0;border-radius:var(--dimension-radius-025, 2px);background:transparent;pointer-events:none;transition:background var(--effect-animation-duration-short-3, 200ms) var(--effect-animation-easing-ease-out, ease)}.panel-nav__item.sc-ds-panel-nav:hover::after{background:var(--_nav-hover);transition:none}.panel-nav__item.sc-ds-panel-nav:active::after{background:var(--_nav-pressed);transition:none}.panel-nav__item.sc-ds-panel-nav:focus-visible{outline:none}.panel-nav__item.sc-ds-panel-nav:focus-visible::after{outline:var(--dimension-stroke-width-025, 2px) solid var(--_nav-focus);outline-offset:calc(-1 * var(--dimension-stroke-width-025, 2px))}.panel-nav__item--active.sc-ds-panel-nav{color:var(--_nav-fg-primary)}.panel-nav__item-icon.sc-ds-panel-nav{display:flex;align-items:center;justify-content:center;flex-shrink:0;width:var(--dimension-iconography-md, 20px);height:var(--dimension-iconography-md, 20px);position:relative}.panel-nav__item-label.sc-ds-panel-nav{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;min-width:0;height:var(--typography-lineheight-md, 20px);line-height:var(--typography-lineheight-md, 20px);opacity:1;mask-image:linear-gradient(to right, black 50%, transparent 100%);-webkit-mask-image:linear-gradient(to right, black 50%, transparent 100%);mask-size:200% 100%;-webkit-mask-size:200% 100%;mask-position:0% 0;-webkit-mask-position:0% 0;transition:opacity var(--_nav-label-fade-motion) 0ms, mask-position var(--_nav-label-fade-motion) 0ms, -webkit-mask-position var(--_nav-label-fade-motion) 0ms}.panel-nav__item-label-text.sc-ds-panel-nav{padding:0 var(--dimension-space-025, 2px)}.panel-nav__item-dot.sc-ds-panel-nav{position:absolute;right:13px;top:13px;width:var(--dimension-space-075, 6px);height:var(--dimension-space-075, 6px);border-radius:var(--dimension-radius-full, 999px);background:var(--_nav-dot);box-shadow:0 0 0 var(--dimension-stroke-width-018, 1.5px) var(--_nav-bg);pointer-events:none;transition:right var(--_nav-expand-motion), top var(--_nav-expand-motion), background var(--effect-motion-short-3, 200ms ease), box-shadow var(--effect-motion-short-3, 200ms ease)}.panel-nav--collapsed.sc-ds-panel-nav .panel-nav__item-dot.sc-ds-panel-nav{right:6px;top:6px}.panel-nav--animating.sc-ds-panel-nav .panel-nav__item-label.sc-ds-panel-nav{text-overflow:clip}.panel-nav--collapsed.sc-ds-panel-nav .panel-nav__item-label.sc-ds-panel-nav{opacity:0;pointer-events:none;text-overflow:clip;mask-position:100% 0;-webkit-mask-position:100% 0;transition:opacity var(--_nav-label-fade-out-motion) 0ms, mask-position var(--_nav-label-fade-out-motion) 0ms, -webkit-mask-position var(--_nav-label-fade-out-motion) 0ms}.panel-nav--collapsed.sc-ds-panel-nav .panel-nav__group-label.sc-ds-panel-nav{opacity:0;max-height:0;padding:0 var(--dimension-space-075, 6px);pointer-events:none;transition:opacity var(--_nav-label-fade-out-motion) 0ms, max-height var(--_nav-label-fade-out-motion) 0ms, padding var(--_nav-label-fade-out-motion) 0ms, color var(--effect-motion-short-3, 200ms ease)}.panel-nav__footer.sc-ds-panel-nav{position:relative;flex-shrink:0;height:calc(var(--_nav-padding-y) * 2 + var(--_nav-item-height));transition:height var(--_nav-expand-motion)}.panel-nav--collapsed.sc-ds-panel-nav .panel-nav__footer.sc-ds-panel-nav{height:calc(var(--_nav-padding-y) * 2 + var(--_nav-item-height) * 2 + var(--dimension-space-050, 4px))}.panel-nav__footer.sc-ds-panel-nav::before{content:'';position:absolute;bottom:100%;left:0;right:0;height:calc(var(--dimension-base, 8px) * 6);background:linear-gradient(to bottom, transparent, var(--_nav-bg));pointer-events:none;opacity:1;transition:opacity var(--effect-animation-duration-short-3, 200ms) var(--effect-animation-easing-ease-out, ease)}.panel-nav--at-bottom.sc-ds-panel-nav .panel-nav__footer.sc-ds-panel-nav::before{opacity:0}.panel-nav__footer-btn.sc-ds-panel-nav{all:unset;display:flex;align-items:center;justify-content:center;position:absolute;left:var(--_nav-padding-x);top:var(--_nav-padding-y);padding:var(--dimension-space-075, 6px);border-radius:var(--dimension-radius-half, 9999px);cursor:pointer;color:var(--_nav-fg-secondary);box-sizing:border-box;background:transparent;transition:background var(--effect-animation-duration-short-3, 200ms) var(--effect-animation-easing-ease-out, ease), color var(--effect-animation-duration-short-3, 200ms) var(--effect-animation-easing-ease-out, ease)}.panel-nav__footer-btn.sc-ds-panel-nav:focus-visible{outline:var(--dimension-stroke-width-025, 2px) solid var(--_nav-focus);outline-offset:calc(-1 * var(--dimension-stroke-width-025, 2px))}.panel-nav--dashboard.sc-ds-panel-nav .panel-nav__footer-btn.sc-ds-panel-nav:hover{background:var(--color-background-secondary);color:var(--color-foreground-secondary);transition:none}.panel-nav--settings.sc-ds-panel-nav .panel-nav__footer-btn.sc-ds-panel-nav:hover{background:var(--color-navigation-background);color:var(--color-navigation-foreground-secondary);transition:none}.panel-nav__footer-user.sc-ds-panel-nav{position:absolute;left:calc(var(--_nav-padding-x) + var(--_nav-footer-btn-w) + var(--dimension-space-050, 4px));right:var(--_nav-padding-x);top:var(--_nav-padding-y);width:auto;min-width:0;transition:left var(--_nav-expand-motion), right var(--_nav-expand-motion), top var(--_nav-expand-motion), color var(--effect-motion-short-3, 200ms ease)}.panel-nav--collapsed.sc-ds-panel-nav .panel-nav__footer-user.sc-ds-panel-nav{left:var(--_nav-padding-x);right:var(--_nav-padding-x);top:calc(var(--_nav-padding-y) + var(--_nav-item-height) + var(--dimension-space-050, 4px))}.panel-nav__footer-user-label.sc-ds-panel-nav{text-align:right;position:absolute;left:var(--dimension-space-075, 6px);right:calc(var(--dimension-space-075, 6px) + var(--dimension-iconography-md, 20px) + var(--dimension-space-050, 4px));top:50%;transform:translateY(-50%);flex:none;height:var(--typography-lineheight-md, 20px);mask-image:none;-webkit-mask-image:none;transition:opacity var(--_nav-label-fade-motion)}.panel-nav--collapsed.sc-ds-panel-nav .panel-nav__footer-user-label.sc-ds-panel-nav{opacity:0;pointer-events:none;mask-position:0% 0;-webkit-mask-position:0% 0}.panel-nav__footer-user-icon.sc-ds-panel-nav{position:absolute;left:calc(100% - var(--dimension-iconography-md, 20px) - var(--dimension-space-075, 6px));top:0;bottom:0;height:auto;display:flex;align-items:center;justify-content:center;transition:left var(--_nav-expand-motion)}.panel-nav--collapsed.sc-ds-panel-nav .panel-nav__footer-user-icon.sc-ds-panel-nav{left:var(--dimension-space-075, 6px)}.panel-nav__footer-icon-expanded.sc-ds-panel-nav,.panel-nav__footer-icon-collapsed.sc-ds-panel-nav{position:absolute;inset:0;display:flex;align-items:center;justify-content:center}.panel-nav__footer-icon-expanded.sc-ds-panel-nav{opacity:1;transition:opacity var(--_nav-label-fade-motion)}.panel-nav__footer-icon-collapsed.sc-ds-panel-nav{opacity:0;transition:opacity var(--_nav-label-fade-motion)}.panel-nav--collapsed.sc-ds-panel-nav .panel-nav__footer-icon-expanded.sc-ds-panel-nav{opacity:0;transition:opacity var(--_nav-label-fade-out-motion)}.panel-nav--collapsed.sc-ds-panel-nav .panel-nav__footer-icon-collapsed.sc-ds-panel-nav{opacity:1;transition:opacity var(--_nav-label-fade-out-motion)}.panel-nav__user-initial.sc-ds-panel-nav{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;color:var(--_nav-fg-secondary);text-transform:uppercase;line-height:1;pointer-events:none;transition:color var(--effect-motion-short-3, 200ms ease)}.panel-nav__resize-handle.sc-ds-panel-nav{position:absolute;top:0;right:var(--dimension-space-n050, -4px);bottom:0;width:var(--dimension-space-100, 8px);z-index:10;cursor:ew-resize;touch-action:none;background:transparent}.panel-nav__resize-handle.sc-ds-panel-nav::before{content:'';position:absolute;inset:0;background:rgba(0, 0, 0, 0.001)}.panel-nav__resize-handle.sc-ds-panel-nav::after{content:'';position:absolute;top:0;bottom:0;left:50%;transform:translateX(-50%);width:var(--dimension-stroke-width-025, 2px);background:var(--color-background-bold-brand);opacity:0;pointer-events:none;transition:opacity var(--effect-motion-short-2, 100ms ease-in-out)}.panel-nav__resize-handle--overlay.sc-ds-panel-nav::after{opacity:1}@media (prefers-reduced-motion: reduce){.panel-nav.sc-ds-panel-nav,.panel-nav.sc-ds-panel-nav *.sc-ds-panel-nav,.panel-nav.sc-ds-panel-nav *.sc-ds-panel-nav::before,.panel-nav.sc-ds-panel-nav *.sc-ds-panel-nav::after,.panel-nav__resize-handle.sc-ds-panel-nav,.panel-nav__resize-handle.sc-ds-panel-nav::before,.panel-nav__resize-handle.sc-ds-panel-nav::after{transition-duration:0.01ms !important;animation-duration:0.01ms !important;transition-delay:0.01ms !important}}`;let d=false;function v(){if(d)return;const a="ds-panel-nav-vt-style";if(document.getElementById(a)){d=true;return}const e=document.createElement("style");e.id=a;e.textContent=["::view-transition-old(root),::view-transition-new(root){animation:none;mix-blend-mode:normal}","::view-transition-new(root){clip-path:circle(0px at var(--vt-x,50%) var(--vt-y,50%))}"].join("\n");document.head.appendChild(e);d=true}const c=a(class a extends e{constructor(a){super();if(a!==false){this.__registerHost()}this.dsNavSelect=n(this,"dsNavSelect");this.dsNavToggle=n(this,"dsNavToggle");this.dsNavFooterAction=n(this,"dsNavFooterAction");this.variant="dashboard";this.groups="[]";this.activeId="";this.collapsed=false;this.breakpoint=0;this.storageKey="";this.currentUrl="";this.userName="";this.userInitial="";this.renderedVariant="dashboard";this.parsedGroups=[];this.atBottom=false;this.isAnimating=false;this.rovingIndex=0;this.viewportNarrow=false;this.urlDerivedActiveId="";this.isDragging=false;this.dragStartX=0;this.didSnap=false;this.lastDeltaX=0;this.edgeOverlayTimer=null;this.showEdgeOverlay=false}get effectiveActiveId(){return this.urlDerivedActiveId||this.activeId}onCollapsedChange(){this.startCollapseAnimation()}onBreakpointChange(){this.disconnectResizeObserver();if(this.breakpoint>0)this.connectResizeObserver()}onGroupsChange(a){try{this.parsedGroups=JSON.parse(a)}catch{this.parsedGroups=[]}this.syncRovingIndex()}onActiveIdChange(){this.syncRovingIndex()}onCurrentUrlChange(){this.syncActiveFromUrl()}async onVariantChange(a){this.activeViewTransition?.skipTransition();this.activeViewTransition=undefined;if(typeof document.startViewTransition!=="function"){this.renderedVariant=a;return}v();const e=this.el.querySelector(".panel-nav__footer-btn");const n=e?.getBoundingClientRect();const t=n?Math.round(n.left+n.width/2):Math.round(window.innerWidth/2);const i=n?Math.round(n.top+n.height/2):Math.round(window.innerHeight/2);const o=Math.ceil(Math.hypot(Math.max(t,window.innerWidth-t),Math.max(i,window.innerHeight-i)));document.documentElement.style.setProperty("--vt-x",`${t}px`);document.documentElement.style.setProperty("--vt-y",`${i}px`);const s=document.startViewTransition((async()=>{this.renderedVariant=a;await new Promise((a=>requestAnimationFrame((()=>requestAnimationFrame((()=>a()))))))}));this.activeViewTransition=s;s.finished.finally((()=>{if(this.activeViewTransition===s)this.activeViewTransition=undefined}));s.ready.then((()=>{const a=getComputedStyle(document.documentElement).getPropertyValue("--effect-animation-duration-long-1").trim();const e=parseFloat(a)||750;document.documentElement.animate({clipPath:[`circle(0px at ${t}px ${i}px)`,`circle(${o}px at ${t}px ${i}px)`]},{duration:e,easing:"ease-in-out",fill:"forwards",pseudoElement:"::view-transition-new(root)"})}))}componentWillLoad(){this.renderedVariant=this.variant;if(this.storageKey){try{const a=localStorage.getItem(this.storageKey);if(a!==null)this.collapsed=a==="true"}catch{}}this.onGroupsChange(this.groups);this.syncActiveFromUrl()}componentDidLoad(){this.checkScroll();if(this.breakpoint>0)this.connectResizeObserver();this.onGroupsChange(this.groups)}disconnectedCallback(){this.disconnectResizeObserver();this.clearEdgeOverlayTimer();if(this.globalMouseMoveHandler){window.removeEventListener("mousemove",this.globalMouseMoveHandler)}if(this.globalMouseUpHandler){window.removeEventListener("mouseup",this.globalMouseUpHandler)}}startCollapseAnimation(){this.isAnimating=true;const a=this.el.querySelector(".panel-nav");if(this.transitionEndHandler){a?.removeEventListener("transitionend",this.transitionEndHandler)}this.transitionEndHandler=e=>{if(e.propertyName==="width"){this.isAnimating=false;a?.removeEventListener("transitionend",this.transitionEndHandler)}};a?.addEventListener("transitionend",this.transitionEndHandler)}connectResizeObserver(){this.viewportNarrow=window.innerWidth<this.breakpoint;this.resizeObserver=new ResizeObserver((()=>{this.viewportNarrow=window.innerWidth<this.breakpoint}));this.resizeObserver.observe(document.documentElement)}disconnectResizeObserver(){this.resizeObserver?.disconnect();this.resizeObserver=undefined}syncActiveFromUrl(){if(!this.currentUrl){this.urlDerivedActiveId="";return}let a;let e=0;for(const n of this.getAllItems()){if(n.href&&this.currentUrl.startsWith(n.href)&&n.href.length>e){a=n;e=n.href.length}}this.urlDerivedActiveId=a?.id??""}applyToggle(a){this.dsNavToggle.emit(a);if(this.storageKey){this.collapsed=a;try{localStorage.setItem(this.storageKey,String(a))}catch{}}}getAllItems(){return this.parsedGroups.flatMap((a=>a.items))}syncRovingIndex(){const a=this.getAllItems().findIndex((a=>a.id===this.effectiveActiveId));this.rovingIndex=a>=0?a:0}handleItemKeyDown(a,e){const n=this.getAllItems().length;let t=e;switch(a.key){case"ArrowDown":a.preventDefault();t=(e+1)%n;break;case"ArrowUp":a.preventDefault();t=(e-1+n)%n;break;case"Home":a.preventDefault();t=0;break;case"End":a.preventDefault();t=n-1;break;default:return}this.rovingIndex=t;const i=Array.from(this.el.querySelectorAll(".panel-nav__body .panel-nav__item"));i[t]?.focus()}checkScroll(){const a=this.el.querySelector(".panel-nav__body");if(!a)return;const e=a.scrollHeight-a.scrollTop-a.clientHeight;this.atBottom=e<2}handleBodyScroll(a){const e=a.target;const n=e.scrollHeight-e.scrollTop-e.clientHeight;this.atBottom=n<2}handleItemClick(a){this.dsNavSelect.emit(a)}handleToggle(){this.applyToggle(!this.collapsed)}handleFooterAction(){this.dsNavFooterAction.emit()}clearEdgeOverlayTimer(){if(this.edgeOverlayTimer!==null){window.clearTimeout(this.edgeOverlayTimer);this.edgeOverlayTimer=null}}handleResizeHandleMouseEnter(){this.clearEdgeOverlayTimer();this.edgeOverlayTimer=window.setTimeout((()=>{this.showEdgeOverlay=true;this.edgeOverlayTimer=null}),500)}handleResizeHandleMouseLeave(){if(this.isDragging)return;this.clearEdgeOverlayTimer();this.showEdgeOverlay=false}handleResizeHandleMouseDown(a){a.preventDefault();a.stopPropagation();const e=this.collapsed||this.viewportNarrow;this.isDragging=true;this.dragStartX=a.clientX;this.didSnap=false;this.lastDeltaX=0;this.clearEdgeOverlayTimer();this.showEdgeOverlay=false;document.body.style.cursor="ew-resize";document.body.style.userSelect="none";const n=a=>{const n=a.clientX-this.dragStartX;this.lastDeltaX=Math.abs(n);if(this.didSnap)return;if(!e&&n<-8){this.applyToggle(true);this.didSnap=true}else if(e&&n>8){this.applyToggle(false);this.didSnap=true}};const t=()=>{if(!this.didSnap&&this.lastDeltaX<3){this.applyToggle(!e)}this.isDragging=false;this.showEdgeOverlay=false;document.body.style.cursor="";document.body.style.userSelect="";window.removeEventListener("mousemove",n);window.removeEventListener("mouseup",t);this.globalMouseMoveHandler=undefined;this.globalMouseUpHandler=undefined};this.globalMouseMoveHandler=n;this.globalMouseUpHandler=t;window.addEventListener("mousemove",n);window.addEventListener("mouseup",t)}renderNavItem(a,e,n){const i=a.id===this.effectiveActiveId;const o=[t("span",{class:"panel-nav__item-icon"},t("ds-icon",{name:a.icon,size:"md",color:"inherit",flag:a.flag})),t("span",{class:"panel-nav__item-label"},t("span",{class:"panel-nav__item-label-text"},t("ds-text",{as:"span",variant:i?"text-body-medium-emphasis":"text-body-medium",color:"inherit"},a.label))),a.dot&&t("span",{class:"panel-nav__item-dot","aria-hidden":"true"})];const s={class:{"panel-nav__item":true,"panel-nav__item--active":i},"aria-current":i?"page":undefined,title:n?a.label:undefined,tabIndex:e===this.rovingIndex?0:-1,onClick:()=>this.handleItemClick(a.id),onKeyDown:a=>this.handleItemKeyDown(a,e),onFocus:()=>{this.rovingIndex=e}};return a.href?t("a",{...s,href:a.href},o):t("button",{...s,type:"button"},o)}render(){const a=this.renderedVariant==="dashboard";const e=this.collapsed||this.viewportNarrow;const{userName:n,userInitial:o}=this;const s={"panel-nav":true,"panel-nav--dashboard":a,"panel-nav--settings":!a,"panel-nav--collapsed":e,"panel-nav--animating":this.isAnimating,"panel-nav--at-bottom":this.atBottom};return t(i,{key:"853a4140b77422fd5a2ea9abc0480e89535aca6a",style:{display:"block",position:"relative"}},t("nav",{key:"60de50346d670dc6262137e14e77725faeed7331",class:s,"aria-label":a?"Main navigation":"Settings navigation"},t("div",{key:"1576b08b7f7b485ef2d437bed4e82af5b06485f8",class:"panel-nav__header"},t("button",{key:"ba19cc1643e1bca09f3fb72e39961105819092ae",type:"button",class:"panel-nav__header-btn",onClick:()=>this.handleToggle(),"aria-label":e?"Expand navigation":"Collapse navigation","aria-expanded":e?"false":"true"},t("span",{key:"a52a464691e8ec19cf3f2294f7f3525665c4d359",class:"panel-nav__header-logo","aria-hidden":"true"},t("svg",{key:"b97b89770e655da3c6f018225efa237ba12f93c2",class:"panel-nav__m-mark",viewBox:"0 0 20 20",fill:"none",xmlns:"http://www.w3.org/2000/svg",focusable:"false","aria-hidden":"true"},t("path",{key:"7f980c808fa31a646ccf58672b9660a8aeed45fa",d:"M11.1159 4.31537H7.67021L2.53401 13.0703H0V15.6875H4.02716L8.24319 8.49978V15.6846H11.6342L15.8289 8.47829V15.6846H18.7122V4.3125H15.2559L11.1159 11.3648V4.31537Z",fill:"currentColor"}))),t("span",{key:"fa25a93fe006b096e393f73796c10251abaf2e84",class:"panel-nav__header-toggle","aria-hidden":"true"},t("ds-icon",{key:"79d9d92aebb8adf2e66fd1e19973c55e22a80ce7",name:e?"LeftExpandB":"LeftCollapseB",size:"md",color:"inherit"})))),t("div",{key:"0dc9a6b35d8050bbfdedf0a0692825b6619179ca",class:"panel-nav__body",onScroll:a=>this.handleBodyScroll(a)},(()=>{let a=0;return this.parsedGroups.map((n=>t("div",{class:"panel-nav__group"},n.label&&t("span",{class:"panel-nav__group-label"},t("span",{class:"panel-nav__item-label-text"},t("ds-text",{as:"span",variant:"text-caption-emphasis",color:"inherit"},n.label))),n.items.map((n=>this.renderNavItem(n,a++,e))))))})()),t("div",{key:"f56439050100d2b56da69a8ced684d18562a77cd",class:"panel-nav__footer"},t("button",{key:"90eac8c506823f0b807be221f28a950ddadfe58a",type:"button",class:"panel-nav__footer-btn",title:a?"Settings":"Dashboard","aria-label":a?"Open settings":"Go to dashboard",onClick:()=>this.handleFooterAction()},t("ds-icon",{key:"a838f2f89572185fd627b919c70512d3ade0c5fa",name:a?"Gear":"Dashboard",size:"md",color:"inherit"})),t("button",{key:"8558b00a5558fe2b1309e18f0b6ee86c014a618b",type:"button",class:"panel-nav__item panel-nav__footer-user","aria-label":e?`User: ${n}`:`User menu for ${n}`},t("span",{key:"5ae25099ffaa59f1b7e5fac86c46ace63d9e0914",class:"panel-nav__item-label panel-nav__footer-user-label"},t("span",{key:"13392135d82d49735a0b05632a0b76b9df6064ff",class:"panel-nav__item-label-text"},t("ds-text",{key:"b6c269808411b630b4a21cd97f6f290a0e4bf8c2",as:"span",variant:"text-body-medium-emphasis",color:"inherit"},n))),t("span",{key:"5399e79f0c52a02e297487f64240ba92f14f1dea",class:"panel-nav__item-icon panel-nav__footer-user-icon","aria-hidden":"true"},t("span",{key:"70436f0102ae522698c78465148712eb25e23de5",class:"panel-nav__footer-icon-expanded"},t("ds-icon",{key:"201497da5628d9ff5655cd2df14fe9e78889b658",name:"ChevronUpDown",size:"md",color:"inherit"})),t("span",{key:"e7ffea6158d3bd8c0c88b7b70b5532704f018085",class:"panel-nav__footer-icon-collapsed"},t("ds-icon",{key:"fa3f5729e6d584f8331bccd497f21be477baf44d",name:"Circle",size:"md",color:"inherit"}),t("span",{key:"3242f3b65b88db45116c2040722bdb1e95cb9788",class:"panel-nav__user-initial"},t("ds-text",{key:"45bb67acf04e62a9f3246be4974926dbdc57a3ad",as:"span",variant:"text-caption-emphasis",color:"inherit"},o))))))),!this.viewportNarrow&&t("div",{key:"c943a5452d920bf1c1c210f63f88e6a02d7409ec",class:{"panel-nav__resize-handle":true,"panel-nav__resize-handle--overlay":this.showEdgeOverlay},onMouseEnter:()=>this.handleResizeHandleMouseEnter(),onMouseLeave:()=>this.handleResizeHandleMouseLeave(),onMouseDown:a=>this.handleResizeHandleMouseDown(a),"aria-hidden":"true"}))}get el(){return this}static get watchers(){return{collapsed:[{onCollapsedChange:0}],viewportNarrow:[{onCollapsedChange:0}],breakpoint:[{onBreakpointChange:0}],groups:[{onGroupsChange:0}],activeId:[{onActiveIdChange:0}],urlDerivedActiveId:[{onActiveIdChange:0}],currentUrl:[{onCurrentUrlChange:0}],variant:[{onVariantChange:0}]}}static get style(){return l()}},[2,"ds-panel-nav",{variant:[1],groups:[1],activeId:[1,"active-id"],collapsed:[1028],breakpoint:[2],storageKey:[1,"storage-key"],currentUrl:[1,"current-url"],userName:[1,"user-name"],userInitial:[1,"user-initial"],renderedVariant:[32],parsedGroups:[32],atBottom:[32],isAnimating:[32],rovingIndex:[32],viewportNarrow:[32],urlDerivedActiveId:[32],showEdgeOverlay:[32]},undefined,{collapsed:[{onCollapsedChange:0}],viewportNarrow:[{onCollapsedChange:0}],breakpoint:[{onBreakpointChange:0}],groups:[{onGroupsChange:0}],activeId:[{onActiveIdChange:0}],urlDerivedActiveId:[{onActiveIdChange:0}],currentUrl:[{onCurrentUrlChange:0}],variant:[{onVariantChange:0}]}]);function p(){if(typeof customElements==="undefined"){return}const a=["ds-panel-nav","ds-icon","ds-text"];a.forEach((a=>{switch(a){case"ds-panel-nav":if(!customElements.get(o(a))){customElements.define(o(a),c)}break;case"ds-icon":if(!customElements.get(o(a))){s()}break;case"ds-text":if(!customElements.get(o(a))){r()}break}}))}p();const h=c;const f=p;export{h as DsPanelNav,f as defineCustomElement};
|
|
2
2
|
//# sourceMappingURL=ds-panel-nav.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["panelNavCss","vtStyleInjected","ensureVtStyle","id","document","getElementById","style","createElement","textContent","join","head","appendChild","PanelNav","__stencil_proxyCustomElement","HTMLElement","constructor","registerHost","this","variant","groups","activeId","collapsed","breakpoint","storageKey","currentUrl","userName","userInitial","renderedVariant","parsedGroups","atBottom","isAnimating","rovingIndex","viewportNarrow","urlDerivedActiveId","isDragging","dragStartX","didSnap","lastDeltaX","edgeOverlayTimer","showEdgeOverlay","effectiveActiveId","onCollapsedChange","startCollapseAnimation","onBreakpointChange","disconnectResizeObserver","connectResizeObserver","onGroupsChange","val","JSON","parse","syncRovingIndex","onActiveIdChange","onCurrentUrlChange","syncActiveFromUrl","onVariantChange","newVal","startViewTransition","btn","el","querySelector","rect","getBoundingClientRect","x","Math","round","left","width","window","innerWidth","y","top","height","innerHeight","maxR","ceil","hypot","max","documentElement","setProperty","transition","async","Promise","resolve","ready","then","durToken","getComputedStyle","getPropertyValue","trim","duration","parseFloat","animate","clipPath","easing","fill","pseudoElement","componentWillLoad","stored","localStorage","getItem","componentDidLoad","checkScroll","disconnectedCallback","clearEdgeOverlayTimer","globalMouseMoveHandler","removeEventListener","globalMouseUpHandler","panel","transitionEndHandler","e","propertyName","addEventListener","resizeObserver","ResizeObserver","observe","disconnect","undefined","best","bestLen","item","getAllItems","href","startsWith","length","applyToggle","next","dsNavToggle","emit","setItem","String","flatMap","g","items","idx","findIndex","i","handleItemKeyDown","index","total","key","preventDefault","Array","from","querySelectorAll","focus","body","remaining","scrollHeight","scrollTop","clientHeight","handleBodyScroll","target","handleItemClick","dsNavSelect","handleToggle","handleFooterAction","dsNavFooterAction","clearTimeout","handleResizeHandleMouseEnter","setTimeout","handleResizeHandleMouseLeave","handleResizeHandleMouseDown","stopPropagation","wasCollapsed","clientX","cursor","userSelect","onMove","ev","deltaX","abs","onUp","renderNavItem","isActive","itemContent","h","class","name","icon","size","color","flag","as","label","dot","sharedProps","title","tabIndex","onClick","onKeyDown","onFocus","type","render","isDashboard","navCls","Host","display","position","viewBox","xmlns","focusable","d","onScroll","flatIdx","map","group","onMouseEnter","onMouseLeave","onMouseDown"],"sources":["src/wc/components/PanelNav/PanelNav.css?tag=ds-panel-nav&encapsulation=scoped","src/wc/components/PanelNav/PanelNav.tsx"],"sourcesContent":["/* ── Theme variables ──────────────────────────────────────────────────────── */\n\n.panel-nav {\n /* ─ Sizes ─ */\n --_nav-width: 200px;\n --_nav-width-collapsed: 48px;\n --_nav-item-height: var(--dimension-size-400, 32px);\n --_nav-padding-x: var(--dimension-space-100, 8px);\n --_nav-padding-y: var(--dimension-space-100, 8px);\n /* gear button outer width = padding*2 + icon */\n --_nav-footer-btn-w: calc(var(--dimension-space-075, 6px) * 2 + var(--dimension-iconography-md, 20px));\n\n /* ─ Motion ─ */\n /* --ds-panel-nav-speed: unitless multiplier; override in Storybook to slow-mo (e.g. 10 = 10×).\n *-dur vars are duration-only (needed for calc); *-motion vars add easing for use in transitions. */\n --_nav-speed: var(--ds-panel-nav-speed, 1);\n --_nav-expand-dur: calc(var(--_nav-speed) * var(--effect-animation-duration-medium-1, 300ms));\n --_nav-expand-motion: var(--_nav-expand-dur) var(--effect-animation-easing-ease-in-out, ease);\n --_nav-label-fade-dur: calc(var(--_nav-speed) * var(--effect-animation-duration-medium-1, 300ms));\n --_nav-label-fade-motion: var(--_nav-label-fade-dur) var(--effect-animation-easing-ease-in-out, ease);\n --_nav-label-fade-out-dur: calc(var(--_nav-speed) * var(--effect-animation-duration-medium-1, 300ms));\n --_nav-label-fade-out-motion: var(--_nav-label-fade-out-dur) var(--effect-animation-easing-ease-in-out, ease);\n\n /* ─ Colours – dashboard defaults ─ */\n --_nav-bg: var(--color-navigation-background);\n --_nav-border: var(--color-navigation-border-tertiary);\n --_nav-divider: var(--color-navigation-divider);\n --_nav-fg-primary: var(--color-navigation-foreground-primary);\n --_nav-fg-secondary: var(--color-navigation-foreground-secondary);\n --_nav-fg-tertiary: var(--color-navigation-foreground-tertiary);\n --_nav-hover: var(--color-navigation-interaction-hover);\n --_nav-pressed: var(--color-navigation-interaction-pressed);\n --_nav-focus: var(--color-navigation-interaction-focus);\n --_nav-dot: var(--color-navigation-foreground-brand);\n}\n\n/* ── Settings overrides ──────────────────────────────────────────────────── */\n\n.panel-nav--settings {\n --_nav-bg: var(--color-background-secondary);\n --_nav-border: var(--color-border-tertiary);\n --_nav-divider: var(--color-border-tertiary);\n --_nav-fg-primary: var(--color-foreground-primary);\n --_nav-fg-secondary: var(--color-foreground-secondary);\n --_nav-fg-tertiary: var(--color-foreground-tertiary);\n --_nav-hover: var(--color-interaction-hover);\n --_nav-pressed: var(--color-interaction-pressed);\n --_nav-focus: var(--color-interaction-focus);\n --_nav-dot: var(--color-foreground-bold-brand);\n}\n\n/* ── Shell ───────────────────────────────────────────────────────────────── */\n\n.panel-nav {\n display: flex;\n flex-direction: column;\n width: var(--_nav-width);\n min-width: var(--_nav-width);\n height: 100%;\n background: var(--_nav-bg);\n border-right: var(--dimension-stroke-width-012, 1px) solid var(--_nav-border);\n box-sizing: border-box;\n overflow: hidden;\n transition:\n width var(--_nav-expand-motion),\n min-width var(--_nav-expand-motion),\n background var(--effect-motion-short-3, 200ms ease),\n border-color var(--effect-motion-short-3, 200ms ease);\n font-family: var(--typography-font-family, system-ui);\n}\n\n.panel-nav--collapsed {\n width: var(--_nav-width-collapsed);\n min-width: var(--_nav-width-collapsed);\n}\n\n/* ── Header ──────────────────────────────────────────────────────────────── */\n\n.panel-nav__header {\n display: flex;\n align-items: center;\n padding: var(--dimension-space-100, 8px);\n flex-shrink: 0;\n box-sizing: border-box;\n border-bottom: var(--dimension-stroke-width-012, 1px) solid var(--_nav-divider);\n transition: border-color var(--effect-motion-short-3, 200ms ease);\n}\n\n.panel-nav__header-btn {\n all: unset;\n display: flex;\n align-items: center;\n justify-content: center;\n padding: var(--dimension-space-075, 6px);\n border-radius: var(--dimension-radius-025, 2px);\n cursor: pointer;\n color: var(--_nav-fg-primary);\n position: relative;\n flex-shrink: 0;\n box-sizing: border-box;\n transition: color var(--effect-motion-short-3, 200ms ease);\n}\n\n/* Interaction pseudo-element */\n.panel-nav__header-btn::after {\n content: '';\n position: absolute;\n inset: 0;\n border-radius: var(--dimension-radius-025, 2px);\n background: transparent;\n transition: background var(--effect-animation-duration-short-3, 200ms) var(--effect-animation-easing-ease-out, ease);\n pointer-events: none;\n}\n\n.panel-nav__header-btn:hover::after { background: var(--_nav-hover); transition: none; }\n.panel-nav__header-btn:active::after { background: var(--_nav-pressed); transition: none; }\n.panel-nav__header-btn:focus-visible { outline: none; }\n.panel-nav__header-btn:focus-visible::after {\n outline: var(--dimension-stroke-width-025, 2px) solid var(--_nav-focus);\n outline-offset: calc(-1 * var(--dimension-stroke-width-025, 2px));\n}\n\n/* Logo — visible by default, hidden on hover */\n.panel-nav__header-logo {\n display: flex;\n align-items: center;\n justify-content: center;\n position: relative;\n z-index: 1;\n transition: opacity var(--effect-motion-short-2, 100ms ease);\n}\n\n.panel-nav__m-mark {\n width: var(--dimension-iconography-md, 20px);\n height: var(--dimension-iconography-md, 20px);\n display: block;\n}\n\n/* Toggle icon — hidden by default, revealed on hover */\n.panel-nav__header-toggle {\n display: none;\n align-items: center;\n justify-content: center;\n position: absolute;\n inset: 0;\n z-index: 1;\n}\n\n.panel-nav__header-btn:hover .panel-nav__header-logo { opacity: 0; }\n.panel-nav__header-btn:hover .panel-nav__header-toggle { display: flex; }\n\n/* ── Scrollable body ─────────────────────────────────────────────────────── */\n\n.panel-nav__body {\n flex: 1;\n overflow-y: auto;\n overflow-x: hidden;\n display: flex;\n flex-direction: column;\n gap: var(--dimension-space-200, 16px);\n padding: var(--dimension-space-100, 8px);\n box-sizing: border-box;\n scrollbar-width: none;\n}\n\n.panel-nav__body::-webkit-scrollbar { display: none; }\n\n/* ── Group ───────────────────────────────────────────────────────────────── */\n\n.panel-nav__group {\n display: flex;\n flex-direction: column;\n gap: var(--dimension-space-050, 4px);\n}\n\n.panel-nav__group-label {\n /* Typography via ds-text (text-caption-emphasis) */\n display: flex;\n align-items: center;\n text-transform: uppercase;\n color: var(--_nav-fg-secondary);\n user-select: none;\n height: var(--dimension-size-300, 24px);\n line-height: var(--typography-lineheight-xs, 12px);\n padding: 0 var(--dimension-space-075, 6px);\n white-space: nowrap;\n overflow: hidden;\n max-height: var(--dimension-size-300, 24px);\n opacity: 1;\n transition:\n opacity var(--_nav-label-fade-motion) 0ms,\n max-height var(--_nav-label-fade-motion) 0ms,\n padding var(--_nav-label-fade-motion) 0ms,\n color var(--effect-motion-short-3, 200ms ease);\n}\n\n/* ── Item ────────────────────────────────────────────────────────────────── */\n\n.panel-nav__item {\n all: unset;\n display: flex;\n align-items: center;\n gap: var(--dimension-space-050, 4px);\n padding: 0 var(--dimension-space-075, 6px);\n height: var(--dimension-size-400, 32px);\n border-radius: var(--dimension-radius-025, 2px);\n cursor: pointer;\n color: var(--_nav-fg-secondary);\n position: relative;\n box-sizing: border-box;\n width: 100%;\n user-select: none;\n transition: color var(--effect-motion-short-1, 50ms ease-in-out);\n}\n\n/* Interaction layer */\n.panel-nav__item::after {\n content: '';\n position: absolute;\n inset: 0;\n border-radius: var(--dimension-radius-025, 2px);\n background: transparent;\n pointer-events: none;\n transition: background var(--effect-animation-duration-short-3, 200ms) var(--effect-animation-easing-ease-out, ease);\n}\n\n.panel-nav__item:hover::after { background: var(--_nav-hover); transition: none; }\n.panel-nav__item:active::after { background: var(--_nav-pressed); transition: none; }\n.panel-nav__item:focus-visible { outline: none; }\n.panel-nav__item:focus-visible::after {\n outline: var(--dimension-stroke-width-025, 2px) solid var(--_nav-focus);\n outline-offset: calc(-1 * var(--dimension-stroke-width-025, 2px));\n}\n\n/* ── Active item ─────────────────────────────────────────────────────────── */\n\n.panel-nav__item--active {\n color: var(--_nav-fg-primary);\n /* Bold typography via ds-text (text-body-medium-emphasis) on the label */\n}\n\n/* ── Item icon ───────────────────────────────────────────────────────────── */\n\n.panel-nav__item-icon {\n display: flex;\n align-items: center;\n justify-content: center;\n flex-shrink: 0;\n width: var(--dimension-iconography-md, 20px);\n height: var(--dimension-iconography-md, 20px);\n position: relative;\n}\n\n/* ── Item label ──────────────────────────────────────────────────────────── */\n\n.panel-nav__item-label {\n flex: 1;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n min-width: 0;\n height: var(--typography-lineheight-md, 20px);\n line-height: var(--typography-lineheight-md, 20px);\n opacity: 1;\n /* Gradient wipe: mask sweeps left-to-right on expand, right-to-left on collapse */\n mask-image: linear-gradient(to right, black 50%, transparent 100%);\n -webkit-mask-image: linear-gradient(to right, black 50%, transparent 100%);\n mask-size: 200% 100%;\n -webkit-mask-size: 200% 100%;\n mask-position: 0% 0;\n -webkit-mask-position: 0% 0;\n transition:\n opacity var(--_nav-label-fade-motion) 0ms,\n mask-position var(--_nav-label-fade-motion) 0ms,\n -webkit-mask-position var(--_nav-label-fade-motion) 0ms;\n}\n\n.panel-nav__item-label-text {\n padding: 0 var(--dimension-space-025, 2px);\n}\n\n/* ── Item notification dot — single element, moves between positions ──────── */\n\n.panel-nav__item-dot {\n position: absolute;\n right: 13px;\n top: 13px;\n width: var(--dimension-space-075, 6px);\n height: var(--dimension-space-075, 6px);\n border-radius: var(--dimension-radius-full, 999px);\n background: var(--_nav-dot);\n box-shadow: 0 0 0 var(--dimension-stroke-width-018, 1.5px) var(--_nav-bg);\n pointer-events: none;\n transition:\n right var(--_nav-expand-motion),\n top var(--_nav-expand-motion),\n background var(--effect-motion-short-3, 200ms ease),\n box-shadow var(--effect-motion-short-3, 200ms ease);\n}\n\n.panel-nav--collapsed .panel-nav__item-dot {\n right: 6px;\n top: 6px;\n}\n\n.panel-nav--animating .panel-nav__item-label {\n text-overflow: clip;\n}\n\n.panel-nav--collapsed .panel-nav__item-label {\n opacity: 0;\n pointer-events: none;\n text-overflow: clip;\n mask-position: 100% 0;\n -webkit-mask-position: 100% 0;\n transition:\n opacity var(--_nav-label-fade-out-motion) 0ms,\n mask-position var(--_nav-label-fade-out-motion) 0ms,\n -webkit-mask-position var(--_nav-label-fade-out-motion) 0ms;\n}\n\n.panel-nav--collapsed .panel-nav__group-label {\n opacity: 0;\n max-height: 0;\n padding: 0 var(--dimension-space-075, 6px);\n pointer-events: none;\n transition:\n opacity var(--_nav-label-fade-out-motion) 0ms,\n max-height var(--_nav-label-fade-out-motion) 0ms,\n padding var(--_nav-label-fade-out-motion) 0ms,\n color var(--effect-motion-short-3, 200ms ease);\n}\n\n/* ── Footer ──────────────────────────────────────────────────────────────── */\n\n.panel-nav__footer {\n position: relative;\n flex-shrink: 0;\n /* single row: padding*2 + item */\n height: calc(var(--_nav-padding-y) * 2 + var(--_nav-item-height));\n transition: height var(--_nav-expand-motion);\n}\n\n.panel-nav--collapsed .panel-nav__footer {\n /* two rows: padding*2 + item*2 + gap */\n height: calc(var(--_nav-padding-y) * 2 + var(--_nav-item-height) * 2 + var(--dimension-space-050, 4px));\n}\n\n/* Gradient fade — floats above the footer, masks content scrolling under it */\n.panel-nav__footer::before {\n content: '';\n position: absolute;\n bottom: 100%;\n left: 0;\n right: 0;\n height: calc(var(--dimension-base, 8px) * 6);\n background: linear-gradient(to bottom, transparent, var(--_nav-bg));\n pointer-events: none;\n opacity: 1;\n transition: opacity var(--effect-animation-duration-short-3, 200ms) var(--effect-animation-easing-ease-out, ease);\n}\n\n.panel-nav--at-bottom .panel-nav__footer::before {\n opacity: 0;\n}\n\n/* ── Footer icon button (gear / dashboard) ───────────────────────────────── */\n\n.panel-nav__footer-btn {\n all: unset;\n display: flex;\n align-items: center;\n justify-content: center;\n position: absolute;\n left: var(--_nav-padding-x);\n top: var(--_nav-padding-y);\n padding: var(--dimension-space-075, 6px);\n border-radius: var(--dimension-radius-half, 9999px);\n cursor: pointer;\n color: var(--_nav-fg-secondary);\n box-sizing: border-box;\n background: transparent;\n transition:\n background var(--effect-animation-duration-short-3, 200ms) var(--effect-animation-easing-ease-out, ease),\n color var(--effect-animation-duration-short-3, 200ms) var(--effect-animation-easing-ease-out, ease);\n}\n\n.panel-nav__footer-btn:focus-visible {\n outline: var(--dimension-stroke-width-025, 2px) solid var(--_nav-focus);\n outline-offset: calc(-1 * var(--dimension-stroke-width-025, 2px));\n}\n\n/* Dashboard: hover previews settings surface */\n.panel-nav--dashboard .panel-nav__footer-btn:hover {\n background: var(--color-background-secondary);\n color: var(--color-foreground-secondary);\n transition: none;\n}\n\n/* Settings: hover previews dashboard nav surface */\n.panel-nav--settings .panel-nav__footer-btn:hover {\n background: var(--color-navigation-background);\n color: var(--color-navigation-foreground-secondary);\n transition: none;\n}\n\n/* ── Footer user — absolutely positioned, slides from row to below gear ───── */\n\n.panel-nav__footer-user {\n position: absolute;\n /* row position: right of gear button */\n left: calc(var(--_nav-padding-x) + var(--_nav-footer-btn-w) + var(--dimension-space-050, 4px));\n right: var(--_nav-padding-x);\n top: var(--_nav-padding-y);\n width: auto;\n min-width: 0;\n transition:\n left var(--_nav-expand-motion),\n right var(--_nav-expand-motion),\n top var(--_nav-expand-motion),\n color var(--effect-motion-short-3, 200ms ease);\n}\n\n.panel-nav--collapsed .panel-nav__footer-user {\n /* column position: full-width row below gear */\n left: var(--_nav-padding-x);\n right: var(--_nav-padding-x);\n top: calc(var(--_nav-padding-y) + var(--_nav-item-height) + var(--dimension-space-050, 4px));\n}\n\n/* Label: absolute so it stays in place and only fades — no mask slide */\n.panel-nav__footer-user-label {\n text-align: right;\n position: absolute;\n left: var(--dimension-space-075, 6px);\n right: calc(var(--dimension-space-075, 6px) + var(--dimension-iconography-md, 20px) + var(--dimension-space-050, 4px));\n top: 50%;\n transform: translateY(-50%);\n flex: none;\n height: var(--typography-lineheight-md, 20px);\n mask-image: none;\n -webkit-mask-image: none;\n transition: opacity var(--_nav-label-fade-motion);\n}\n\n.panel-nav--collapsed .panel-nav__footer-user-label {\n opacity: 0;\n pointer-events: none;\n mask-position: 0% 0;\n -webkit-mask-position: 0% 0;\n}\n\n/* ── Footer user icon — cross-fades chevron ↔ circle+initial ────────────── */\n\n/* Absolute — right-anchored in expanded, left-anchored in collapsed to match regular item icon position */\n.panel-nav__footer-user-icon {\n position: absolute;\n left: calc(100% - var(--dimension-iconography-md, 20px) - var(--dimension-space-075, 6px));\n top: 0;\n bottom: 0;\n height: auto;\n display: flex;\n align-items: center;\n justify-content: center;\n transition: left var(--_nav-expand-motion);\n}\n\n.panel-nav--collapsed .panel-nav__footer-user-icon {\n left: var(--dimension-space-075, 6px);\n}\n\n.panel-nav__footer-icon-expanded,\n.panel-nav__footer-icon-collapsed {\n position: absolute;\n inset: 0;\n display: flex;\n align-items: center;\n justify-content: center;\n}\n\n.panel-nav__footer-icon-expanded {\n opacity: 1;\n transition: opacity var(--_nav-label-fade-motion);\n}\n\n.panel-nav__footer-icon-collapsed {\n opacity: 0;\n transition: opacity var(--_nav-label-fade-motion);\n}\n\n.panel-nav--collapsed .panel-nav__footer-icon-expanded {\n opacity: 0;\n transition: opacity var(--_nav-label-fade-out-motion);\n}\n\n.panel-nav--collapsed .panel-nav__footer-icon-collapsed {\n opacity: 1;\n transition: opacity var(--_nav-label-fade-out-motion);\n}\n\n/* ── User initial — overlaid on the circle icon ─────────────────────────── */\n\n.panel-nav__user-initial {\n position: absolute;\n inset: 0;\n display: flex;\n align-items: center;\n justify-content: center;\n color: var(--_nav-fg-secondary);\n text-transform: uppercase;\n line-height: 1;\n pointer-events: none;\n transition: color var(--effect-motion-short-3, 200ms ease);\n}\n\n/* ── Drag-to-resize handle ───────────────────────────────────────────────── */\n\n.panel-nav__resize-handle {\n position: absolute;\n top: 0;\n /* Centered on the right edge: --dimension-space-050 outside + inside = --dimension-space-100 hit zone */\n right: var(--dimension-space-n050, -4px);\n bottom: 0;\n width: var(--dimension-space-100, 8px);\n z-index: 10;\n cursor: ew-resize;\n touch-action: none;\n /* Needs a real background for Safari hit-testing */\n background: transparent;\n}\n\n/* Safari requires non-transparent fill for hit testing */\n.panel-nav__resize-handle::before {\n content: '';\n position: absolute;\n inset: 0;\n background: rgba(0, 0, 0, 0.001);\n}\n\n/* The brand-bold overlay line */\n.panel-nav__resize-handle::after {\n content: '';\n position: absolute;\n top: 0;\n bottom: 0;\n left: 50%;\n transform: translateX(-50%);\n width: var(--dimension-stroke-width-025, 2px);\n background: var(--color-background-bold-brand);\n opacity: 0;\n pointer-events: none;\n transition: opacity var(--effect-motion-short-2, 100ms ease-in-out);\n}\n\n.panel-nav__resize-handle--overlay::after {\n opacity: 1;\n}\n\n/* ── Reduced motion ──────────────────────────────────────────────────────── */\n\n@media (prefers-reduced-motion: reduce) {\n .panel-nav,\n .panel-nav *,\n .panel-nav *::before,\n .panel-nav *::after,\n .panel-nav__resize-handle,\n .panel-nav__resize-handle::before,\n .panel-nav__resize-handle::after {\n transition-duration: 0.01ms !important;\n animation-duration: 0.01ms !important;\n transition-delay: 0.01ms !important;\n }\n}\n","import { Component, Prop, Event, EventEmitter, Watch, State, Element, h, Host } from '@stencil/core';\n\n// Injected once per document to suppress the browser default cross-fade and\n// pin the new-state snapshot to a 0px circle so the WAAPI reveal has no flash.\nlet vtStyleInjected = false;\nfunction ensureVtStyle() {\n if (vtStyleInjected) return;\n const id = 'ds-panel-nav-vt-style';\n if (document.getElementById(id)) { vtStyleInjected = true; return; }\n const style = document.createElement('style');\n style.id = id;\n style.textContent = [\n '::view-transition-old(root),::view-transition-new(root){animation:none;mix-blend-mode:normal}',\n '::view-transition-new(root){clip-path:circle(0px at var(--vt-x,50%) var(--vt-y,50%))}',\n ].join('\\n');\n document.head.appendChild(style);\n vtStyleInjected = true;\n}\n\nexport type PanelNavVariant = 'dashboard' | 'settings';\n\nexport interface PanelNavItem {\n id: string;\n icon: string;\n label: string;\n /** Show a notification dot badge on the item */\n dot?: boolean;\n flag?: boolean;\n /** When set, the item renders as an `<a>` element so any router that\n * intercepts anchor clicks (Angular Router, React Router, etc.) handles\n * navigation automatically. `dsNavSelect` still fires on click. */\n href?: string;\n}\n\nexport interface PanelNavGroup {\n id?: string;\n label?: string;\n items: PanelNavItem[];\n}\n\n@Component({\n tag: 'ds-panel-nav',\n styleUrl: 'PanelNav.css',\n scoped: true,\n})\nexport class PanelNav {\n /** Visual variant: `dashboard` = always-dark surface, `settings` = light surface */\n @Prop() variant: PanelNavVariant = 'dashboard';\n\n /** JSON string of `PanelNavGroup[]` */\n @Prop() groups: string = '[]';\n\n /** ID of the currently active/selected nav item. Overridden by `currentUrl` matching when set. */\n @Prop() activeId: string = '';\n\n /** Whether the nav is in collapsed (icon-only) state.\n * When `storageKey` is set the component manages this internally —\n * do not also bind it from outside in that mode. */\n @Prop({ mutable: true }) collapsed: boolean = false;\n\n /** Viewport width (px) below which the nav auto-collapses to icon-only mode. 0 = disabled. */\n @Prop() breakpoint: number = 0;\n\n /** `localStorage` key used to persist the collapsed state across page loads.\n * When set the component is self-managing for collapsed state; `dsNavToggle`\n * still fires for consumers that want to observe the change. */\n @Prop() storageKey: string = '';\n\n /** Current route URL (e.g. `window.location.pathname` or the router's active URL).\n * When set the component derives the active item by matching item `href` values\n * against this string (longest prefix wins), overriding `activeId`. */\n @Prop() currentUrl: string = '';\n\n /** Display name for the footer user section */\n @Prop() userName: string = '';\n\n /** Single character shown in the collapsed avatar */\n @Prop() userInitial: string = '';\n\n /** Emitted when a nav item is clicked. Detail = the item's `id`. */\n @Event() dsNavSelect!: EventEmitter<string>;\n\n /** Emitted when the collapse toggle is clicked. Detail = new collapsed state. */\n @Event() dsNavToggle!: EventEmitter<boolean>;\n\n /** Emitted when the footer left button (gear / dashboard) is clicked. */\n @Event() dsNavFooterAction!: EventEmitter<void>;\n\n @Element() el!: HTMLElement;\n\n /** Mirrors `variant` but is only updated inside the VT callback so the\n * View Transition captures a clean before/after snapshot. */\n @State() private renderedVariant: PanelNavVariant = 'dashboard';\n\n @State() private parsedGroups: PanelNavGroup[] = [];\n @State() private atBottom = false;\n @State() private isAnimating = false;\n @State() private rovingIndex: number = 0;\n @State() private viewportNarrow: boolean = false;\n @State() private urlDerivedActiveId: string = '';\n\n private transitionEndHandler?: (e: TransitionEvent) => void;\n private resizeObserver?: ResizeObserver;\n\n // Drag-to-resize state (not @State — no re-render needed)\n private isDragging = false;\n private dragStartX = 0;\n private didSnap = false;\n private lastDeltaX = 0;\n private edgeOverlayTimer: number | null = null;\n private globalMouseMoveHandler?: (e: MouseEvent) => void;\n private globalMouseUpHandler?: () => void;\n\n @State() private showEdgeOverlay = false;\n\n /** The ID that should be treated as active. `currentUrl` matching takes precedence\n * over `activeId` when both are present. */\n private get effectiveActiveId(): string {\n return this.urlDerivedActiveId || this.activeId;\n }\n\n @Watch('collapsed')\n @Watch('viewportNarrow')\n onCollapsedChange() {\n this.startCollapseAnimation();\n }\n\n @Watch('breakpoint')\n onBreakpointChange() {\n this.disconnectResizeObserver();\n if (this.breakpoint > 0) this.connectResizeObserver();\n }\n\n @Watch('groups')\n onGroupsChange(val: string) {\n try {\n this.parsedGroups = JSON.parse(val);\n } catch {\n this.parsedGroups = [];\n }\n this.syncRovingIndex();\n }\n\n @Watch('activeId')\n @Watch('urlDerivedActiveId')\n onActiveIdChange() {\n this.syncRovingIndex();\n }\n\n @Watch('currentUrl')\n onCurrentUrlChange() {\n this.syncActiveFromUrl();\n }\n\n @Watch('variant')\n async onVariantChange(newVal: PanelNavVariant) {\n if (typeof (document as any).startViewTransition !== 'function') {\n this.renderedVariant = newVal;\n return;\n }\n\n ensureVtStyle();\n\n const btn = this.el.querySelector('.panel-nav__footer-btn') as HTMLElement | null;\n const rect = btn?.getBoundingClientRect();\n const x = rect ? Math.round(rect.left + rect.width / 2) : Math.round(window.innerWidth / 2);\n const y = rect ? Math.round(rect.top + rect.height / 2) : Math.round(window.innerHeight / 2);\n const maxR = Math.ceil(Math.hypot(\n Math.max(x, window.innerWidth - x),\n Math.max(y, window.innerHeight - y),\n ));\n\n document.documentElement.style.setProperty('--vt-x', `${x}px`);\n document.documentElement.style.setProperty('--vt-y', `${y}px`);\n\n const transition = (document as any).startViewTransition(async () => {\n this.renderedVariant = newVal;\n // Four microtask ticks let Stencil's render queue fully flush so the VT\n // captures a complete new-state snapshot before the animation starts.\n await Promise.resolve();\n await Promise.resolve();\n await Promise.resolve();\n await Promise.resolve();\n });\n\n transition.ready.then(() => {\n const durToken = getComputedStyle(document.documentElement)\n .getPropertyValue('--effect-animation-duration-long-1').trim();\n const duration = parseFloat(durToken) || 750;\n document.documentElement.animate(\n { clipPath: [`circle(0px at ${x}px ${y}px)`, `circle(${maxR}px at ${x}px ${y}px)`] },\n { duration, easing: 'ease-in-out', fill: 'forwards', pseudoElement: '::view-transition-new(root)' },\n );\n });\n }\n\n componentWillLoad() {\n this.renderedVariant = this.variant;\n if (this.storageKey) {\n try {\n const stored = localStorage.getItem(this.storageKey);\n if (stored !== null) this.collapsed = stored === 'true';\n } catch { /* localStorage unavailable */ }\n }\n this.onGroupsChange(this.groups);\n this.syncActiveFromUrl();\n }\n\n componentDidLoad() {\n this.checkScroll();\n if (this.breakpoint > 0) this.connectResizeObserver();\n // Re-sync groups in case componentWillLoad ran before host bindings were applied\n // (common with Angular's property binding timing)\n this.onGroupsChange(this.groups);\n }\n\n disconnectedCallback() {\n this.disconnectResizeObserver();\n this.clearEdgeOverlayTimer();\n if (this.globalMouseMoveHandler) {\n window.removeEventListener('mousemove', this.globalMouseMoveHandler);\n }\n if (this.globalMouseUpHandler) {\n window.removeEventListener('mouseup', this.globalMouseUpHandler);\n }\n }\n\n private startCollapseAnimation() {\n this.isAnimating = true;\n const panel = this.el.querySelector('.panel-nav') as HTMLElement | null;\n if (this.transitionEndHandler) {\n panel?.removeEventListener('transitionend', this.transitionEndHandler);\n }\n this.transitionEndHandler = (e: TransitionEvent) => {\n if (e.propertyName === 'width') {\n this.isAnimating = false;\n panel?.removeEventListener('transitionend', this.transitionEndHandler!);\n }\n };\n panel?.addEventListener('transitionend', this.transitionEndHandler);\n }\n\n private connectResizeObserver() {\n this.viewportNarrow = window.innerWidth < this.breakpoint;\n this.resizeObserver = new ResizeObserver(() => {\n this.viewportNarrow = window.innerWidth < this.breakpoint;\n });\n this.resizeObserver.observe(document.documentElement);\n }\n\n private disconnectResizeObserver() {\n this.resizeObserver?.disconnect();\n this.resizeObserver = undefined;\n }\n\n /** Derive active ID from the current URL by matching item `href` values.\n * Uses longest-prefix match so `/fleet/detail` correctly activates `/fleet`. */\n private syncActiveFromUrl() {\n if (!this.currentUrl) {\n this.urlDerivedActiveId = '';\n return;\n }\n let best: PanelNavItem | undefined;\n let bestLen = 0;\n for (const item of this.getAllItems()) {\n if (item.href && this.currentUrl.startsWith(item.href) && item.href.length > bestLen) {\n best = item;\n bestLen = item.href.length;\n }\n }\n this.urlDerivedActiveId = best?.id ?? '';\n }\n\n /** Centralised toggle: always emits `dsNavToggle`; when `storageKey` is set\n * also mutates `collapsed` directly and persists to `localStorage`. */\n private applyToggle(next: boolean) {\n this.dsNavToggle.emit(next);\n if (this.storageKey) {\n this.collapsed = next;\n try { localStorage.setItem(this.storageKey, String(next)); } catch { /* unavailable */ }\n }\n }\n\n private getAllItems(): PanelNavItem[] {\n return this.parsedGroups.flatMap(g => g.items);\n }\n\n private syncRovingIndex() {\n const idx = this.getAllItems().findIndex(i => i.id === this.effectiveActiveId);\n this.rovingIndex = idx >= 0 ? idx : 0;\n }\n\n private handleItemKeyDown(e: KeyboardEvent, index: number) {\n const total = this.getAllItems().length;\n let next = index;\n switch (e.key) {\n case 'ArrowDown': e.preventDefault(); next = (index + 1) % total; break;\n case 'ArrowUp': e.preventDefault(); next = (index - 1 + total) % total; break;\n case 'Home': e.preventDefault(); next = 0; break;\n case 'End': e.preventDefault(); next = total - 1; break;\n default: return;\n }\n this.rovingIndex = next;\n const items = Array.from(\n this.el.querySelectorAll<HTMLElement>('.panel-nav__body .panel-nav__item')\n );\n items[next]?.focus();\n }\n\n private checkScroll() {\n const body = this.el.querySelector('.panel-nav__body') as HTMLElement | null;\n if (!body) return;\n const remaining = body.scrollHeight - body.scrollTop - body.clientHeight;\n this.atBottom = remaining < 2;\n }\n\n private handleBodyScroll(e: Event) {\n const body = e.target as HTMLElement;\n const remaining = body.scrollHeight - body.scrollTop - body.clientHeight;\n this.atBottom = remaining < 2;\n }\n\n private handleItemClick(id: string) {\n this.dsNavSelect.emit(id);\n }\n\n private handleToggle() {\n this.applyToggle(!this.collapsed);\n }\n\n private handleFooterAction() {\n this.dsNavFooterAction.emit();\n }\n\n private clearEdgeOverlayTimer() {\n if (this.edgeOverlayTimer !== null) {\n window.clearTimeout(this.edgeOverlayTimer);\n this.edgeOverlayTimer = null;\n }\n }\n\n private handleResizeHandleMouseEnter() {\n this.clearEdgeOverlayTimer();\n this.edgeOverlayTimer = window.setTimeout(() => {\n this.showEdgeOverlay = true;\n this.edgeOverlayTimer = null;\n }, 500);\n }\n\n private handleResizeHandleMouseLeave() {\n if (this.isDragging) return;\n this.clearEdgeOverlayTimer();\n this.showEdgeOverlay = false;\n }\n\n private handleResizeHandleMouseDown(e: MouseEvent) {\n e.preventDefault();\n e.stopPropagation();\n\n const wasCollapsed = this.collapsed || this.viewportNarrow;\n this.isDragging = true;\n this.dragStartX = e.clientX;\n this.didSnap = false;\n this.lastDeltaX = 0;\n this.clearEdgeOverlayTimer();\n this.showEdgeOverlay = false;\n\n document.body.style.cursor = 'ew-resize';\n document.body.style.userSelect = 'none';\n\n const onMove = (ev: MouseEvent) => {\n const deltaX = ev.clientX - this.dragStartX;\n this.lastDeltaX = Math.abs(deltaX);\n if (this.didSnap) return;\n if (!wasCollapsed && deltaX < -8) {\n this.applyToggle(true);\n this.didSnap = true;\n } else if (wasCollapsed && deltaX > 8) {\n this.applyToggle(false);\n this.didSnap = true;\n }\n };\n\n const onUp = () => {\n if (!this.didSnap && this.lastDeltaX < 3) {\n this.applyToggle(!wasCollapsed);\n }\n this.isDragging = false;\n this.showEdgeOverlay = false;\n document.body.style.cursor = '';\n document.body.style.userSelect = '';\n window.removeEventListener('mousemove', onMove);\n window.removeEventListener('mouseup', onUp);\n this.globalMouseMoveHandler = undefined;\n this.globalMouseUpHandler = undefined;\n };\n\n this.globalMouseMoveHandler = onMove;\n this.globalMouseUpHandler = onUp;\n window.addEventListener('mousemove', onMove);\n window.addEventListener('mouseup', onUp);\n }\n\n private renderNavItem(item: PanelNavItem, idx: number, collapsed: boolean) {\n const isActive = item.id === this.effectiveActiveId;\n\n const itemContent = [\n <span class=\"panel-nav__item-icon\">\n <ds-icon name={item.icon} size=\"md\" color=\"inherit\" flag={item.flag} />\n </span>,\n <span class=\"panel-nav__item-label\">\n <span class=\"panel-nav__item-label-text\">\n <ds-text\n as=\"span\"\n variant={isActive ? 'text-body-medium-emphasis' : 'text-body-medium'}\n color=\"inherit\"\n >\n {item.label}\n </ds-text>\n </span>\n </span>,\n item.dot && <span class=\"panel-nav__item-dot\" aria-hidden=\"true\" />,\n ];\n\n const sharedProps = {\n class: { 'panel-nav__item': true, 'panel-nav__item--active': isActive },\n 'aria-current': isActive ? ('page' as const) : undefined,\n title: collapsed ? item.label : undefined,\n tabIndex: idx === this.rovingIndex ? 0 : -1,\n onClick: () => this.handleItemClick(item.id),\n onKeyDown: (e: KeyboardEvent) => this.handleItemKeyDown(e, idx),\n onFocus: () => { this.rovingIndex = idx; },\n };\n\n return item.href\n ? <a {...sharedProps} href={item.href}>{itemContent}</a>\n : <button {...sharedProps} type=\"button\">{itemContent}</button>;\n }\n\n render() {\n const isDashboard = this.renderedVariant === 'dashboard';\n const collapsed = this.collapsed || this.viewportNarrow;\n const { userName, userInitial } = this;\n\n const navCls: Record<string, boolean> = {\n 'panel-nav': true,\n 'panel-nav--dashboard': isDashboard,\n 'panel-nav--settings': !isDashboard,\n 'panel-nav--collapsed': collapsed,\n 'panel-nav--animating': this.isAnimating,\n 'panel-nav--at-bottom': this.atBottom,\n };\n\n return (\n <Host style={{ display: 'block', position: 'relative' }}>\n <nav class={navCls} aria-label={isDashboard ? 'Main navigation' : 'Settings navigation'}>\n\n {/* ── Header: Motive logo, reveals collapse toggle on hover ── */}\n <div class=\"panel-nav__header\">\n <button\n type=\"button\"\n class=\"panel-nav__header-btn\"\n onClick={() => this.handleToggle()}\n aria-label={collapsed ? 'Expand navigation' : 'Collapse navigation'}\n aria-expanded={collapsed ? 'false' : 'true'}\n >\n {/* Motive M mark — fades out on hover to reveal collapse toggle */}\n <span class=\"panel-nav__header-logo\" aria-hidden=\"true\">\n <svg class=\"panel-nav__m-mark\" viewBox=\"0 0 20 20\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" focusable=\"false\" aria-hidden=\"true\">\n <path d=\"M11.1159 4.31537H7.67021L2.53401 13.0703H0V15.6875H4.02716L8.24319 8.49978V15.6846H11.6342L15.8289 8.47829V15.6846H18.7122V4.3125H15.2559L11.1159 11.3648V4.31537Z\" fill=\"currentColor\"/>\n </svg>\n </span>\n {/* Collapse / expand icon — revealed on hover via CSS */}\n <span class=\"panel-nav__header-toggle\" aria-hidden=\"true\">\n <ds-icon\n name={collapsed ? 'LeftExpandB' : 'LeftCollapseB'}\n size=\"md\"\n color=\"inherit\"\n />\n </span>\n </button>\n </div>\n\n {/* ── Scrollable body ── */}\n <div class=\"panel-nav__body\" onScroll={e => this.handleBodyScroll(e)}>\n {(() => {\n let flatIdx = 0;\n return this.parsedGroups.map(group => (\n <div class=\"panel-nav__group\">\n {group.label && (\n <span class=\"panel-nav__group-label\">\n <span class=\"panel-nav__item-label-text\">\n <ds-text as=\"span\" variant=\"text-caption-emphasis\" color=\"inherit\">\n {group.label}\n </ds-text>\n </span>\n </span>\n )}\n {group.items.map(item => this.renderNavItem(item, flatIdx++, collapsed))}\n </div>\n ));\n })()}\n </div>\n\n {/* ── Footer ── */}\n <div class=\"panel-nav__footer\">\n {/* Left icon button — Gear in dashboard, Dashboard in settings */}\n <button\n type=\"button\"\n class=\"panel-nav__footer-btn\"\n title={isDashboard ? 'Settings' : 'Dashboard'}\n aria-label={isDashboard ? 'Open settings' : 'Go to dashboard'}\n onClick={() => this.handleFooterAction()}\n >\n <ds-icon name={isDashboard ? 'Gear' : 'Dashboard'} size=\"md\" color=\"inherit\" />\n </button>\n\n {/* Right user — label fades like nav items; right icon cross-fades chevron ↔ circle+initial */}\n <button\n type=\"button\"\n class=\"panel-nav__item panel-nav__footer-user\"\n aria-label={collapsed ? `User: ${userName}` : `User menu for ${userName}`}\n >\n <span class=\"panel-nav__item-label panel-nav__footer-user-label\">\n <span class=\"panel-nav__item-label-text\">\n <ds-text as=\"span\" variant=\"text-body-medium-emphasis\" color=\"inherit\">\n {userName}\n </ds-text>\n </span>\n </span>\n <span class=\"panel-nav__item-icon panel-nav__footer-user-icon\" aria-hidden=\"true\">\n <span class=\"panel-nav__footer-icon-expanded\">\n <ds-icon name=\"ChevronUpDown\" size=\"md\" color=\"inherit\" />\n </span>\n <span class=\"panel-nav__footer-icon-collapsed\">\n <ds-icon name=\"Circle\" size=\"md\" color=\"inherit\" />\n <span class=\"panel-nav__user-initial\">\n <ds-text as=\"span\" variant=\"text-caption-emphasis\" color=\"inherit\">\n {userInitial}\n </ds-text>\n </span>\n </span>\n </span>\n </button>\n </div>\n\n </nav>\n\n {/* Drag-to-resize handle — always rendered, hidden only when auto-collapsed by breakpoint */}\n {!this.viewportNarrow && (\n <div\n class={{\n 'panel-nav__resize-handle': true,\n 'panel-nav__resize-handle--overlay': this.showEdgeOverlay,\n }}\n onMouseEnter={() => this.handleResizeHandleMouseEnter()}\n onMouseLeave={() => this.handleResizeHandleMouseLeave()}\n onMouseDown={(e: MouseEvent) => this.handleResizeHandleMouseDown(e)}\n aria-hidden=\"true\"\n />\n )}\n </Host>\n );\n }\n}\n"],"mappings":"0IAAA,MAAMA,EAAc,IAAM,+jdCI1B,IAAIC,EAAkB,MACtB,SAASC,IACP,GAAID,EAAiB,OACrB,MAAME,EAAK,wBACX,GAAIC,SAASC,eAAeF,GAAK,CAAEF,EAAkB,KAAM,M,CAC3D,MAAMK,EAAQF,SAASG,cAAc,SACrCD,EAAMH,GAAKA,EACXG,EAAME,YAAc,CAClB,gGACA,yFACAC,KAAK,MACPL,SAASM,KAAKC,YAAYL,GAC1BL,EAAkB,IACpB,C,MA4BaW,EAAQC,EAAA,MAAAD,UAAAE,EALrB,WAAAC,CAAAC,G,6KAOUC,KAAAC,QAA2B,YAG3BD,KAAAE,OAAiB,KAGjBF,KAAAG,SAAmB,GAKFH,KAAAI,UAAqB,MAGtCJ,KAAAK,WAAqB,EAKrBL,KAAAM,WAAqB,GAKrBN,KAAAO,WAAqB,GAGrBP,KAAAQ,SAAmB,GAGnBR,KAAAS,YAAsB,GAebT,KAAAU,gBAAmC,YAEnCV,KAAAW,aAAgC,GAChCX,KAAAY,SAAW,MACXZ,KAAAa,YAAc,MACdb,KAAAc,YAAsB,EACtBd,KAAAe,eAA0B,MAC1Bf,KAAAgB,mBAA6B,GAMtChB,KAAAiB,WAAa,MACbjB,KAAAkB,WAAa,EACblB,KAAAmB,QAAU,MACVnB,KAAAoB,WAAa,EACbpB,KAAAqB,iBAAkC,KAIzBrB,KAAAsB,gBAAkB,KAmcpC,CA/bC,qBAAYC,GACV,OAAOvB,KAAKgB,oBAAsBhB,KAAKG,Q,CAKzC,iBAAAqB,GACExB,KAAKyB,wB,CAIP,kBAAAC,GACE1B,KAAK2B,2BACL,GAAI3B,KAAKK,WAAa,EAAGL,KAAK4B,uB,CAIhC,cAAAC,CAAeC,GACb,IACE9B,KAAKW,aAAeoB,KAAKC,MAAMF,E,CAC/B,MACA9B,KAAKW,aAAe,E,CAEtBX,KAAKiC,iB,CAKP,gBAAAC,GACElC,KAAKiC,iB,CAIP,kBAAAE,GACEnC,KAAKoC,mB,CAIP,qBAAMC,CAAgBC,GACpB,UAAYnD,SAAiBoD,sBAAwB,WAAY,CAC/DvC,KAAKU,gBAAkB4B,EACvB,M,CAGFrD,IAEA,MAAMuD,EAAMxC,KAAKyC,GAAGC,cAAc,0BAClC,MAAMC,EAAOH,GAAKI,wBAClB,MAAMC,EAAIF,EAAOG,KAAKC,MAAMJ,EAAKK,KAAOL,EAAKM,MAAQ,GAAKH,KAAKC,MAAMG,OAAOC,WAAa,GACzF,MAAMC,EAAIT,EAAOG,KAAKC,MAAMJ,EAAKU,IAAMV,EAAKW,OAAS,GAAKR,KAAKC,MAAMG,OAAOK,YAAc,GAC1F,MAAMC,EAAOV,KAAKW,KAAKX,KAAKY,MAC1BZ,KAAKa,IAAId,EAAGK,OAAOC,WAAaN,GAChCC,KAAKa,IAAIP,EAAGF,OAAOK,YAAcH,KAGnCjE,SAASyE,gBAAgBvE,MAAMwE,YAAY,SAAU,GAAGhB,OACxD1D,SAASyE,gBAAgBvE,MAAMwE,YAAY,SAAU,GAAGT,OAExD,MAAMU,EAAc3E,SAAiBoD,qBAAoBwB,UACvD/D,KAAKU,gBAAkB4B,QAGjB0B,QAAQC,gBACRD,QAAQC,gBACRD,QAAQC,gBACRD,QAAQC,SAAS,IAGzBH,EAAWI,MAAMC,MAAK,KACpB,MAAMC,EAAWC,iBAAiBlF,SAASyE,iBACxCU,iBAAiB,sCAAsCC,OAC1D,MAAMC,EAAWC,WAAWL,IAAa,IACzCjF,SAASyE,gBAAgBc,QACvB,CAAEC,SAAU,CAAC,iBAAiB9B,OAAOO,OAAQ,UAAUI,UAAaX,OAAOO,SAC3E,CAAEoB,WAAUI,OAAQ,cAAeC,KAAM,WAAYC,cAAe,+BACrE,G,CAIL,iBAAAC,GACE/E,KAAKU,gBAAkBV,KAAKC,QAC5B,GAAID,KAAKM,WAAY,CACnB,IACE,MAAM0E,EAASC,aAAaC,QAAQlF,KAAKM,YACzC,GAAI0E,IAAW,KAAMhF,KAAKI,UAAY4E,IAAW,M,CACjD,MAAM,C,CAEVhF,KAAK6B,eAAe7B,KAAKE,QACzBF,KAAKoC,mB,CAGP,gBAAA+C,GACEnF,KAAKoF,cACL,GAAIpF,KAAKK,WAAa,EAAGL,KAAK4B,wBAG9B5B,KAAK6B,eAAe7B,KAAKE,O,CAG3B,oBAAAmF,GACErF,KAAK2B,2BACL3B,KAAKsF,wBACL,GAAItF,KAAKuF,uBAAwB,CAC/BrC,OAAOsC,oBAAoB,YAAaxF,KAAKuF,uB,CAE/C,GAAIvF,KAAKyF,qBAAsB,CAC7BvC,OAAOsC,oBAAoB,UAAWxF,KAAKyF,qB,EAIvC,sBAAAhE,GACNzB,KAAKa,YAAc,KACnB,MAAM6E,EAAQ1F,KAAKyC,GAAGC,cAAc,cACpC,GAAI1C,KAAK2F,qBAAsB,CAC7BD,GAAOF,oBAAoB,gBAAiBxF,KAAK2F,qB,CAEnD3F,KAAK2F,qBAAwBC,IAC3B,GAAIA,EAAEC,eAAiB,QAAS,CAC9B7F,KAAKa,YAAc,MACnB6E,GAAOF,oBAAoB,gBAAiBxF,KAAK2F,qB,GAGrDD,GAAOI,iBAAiB,gBAAiB9F,KAAK2F,qB,CAGxC,qBAAA/D,GACN5B,KAAKe,eAAiBmC,OAAOC,WAAanD,KAAKK,WAC/CL,KAAK+F,eAAiB,IAAIC,gBAAe,KACvChG,KAAKe,eAAiBmC,OAAOC,WAAanD,KAAKK,UAAU,IAE3DL,KAAK+F,eAAeE,QAAQ9G,SAASyE,gB,CAG/B,wBAAAjC,GACN3B,KAAK+F,gBAAgBG,aACrBlG,KAAK+F,eAAiBI,S,CAKhB,iBAAA/D,GACN,IAAKpC,KAAKO,WAAY,CACpBP,KAAKgB,mBAAqB,GAC1B,M,CAEF,IAAIoF,EACJ,IAAIC,EAAU,EACd,IAAK,MAAMC,KAAQtG,KAAKuG,cAAe,CACrC,GAAID,EAAKE,MAAQxG,KAAKO,WAAWkG,WAAWH,EAAKE,OAASF,EAAKE,KAAKE,OAASL,EAAS,CACpFD,EAAOE,EACPD,EAAUC,EAAKE,KAAKE,M,EAGxB1G,KAAKgB,mBAAqBoF,GAAMlH,IAAM,E,CAKhC,WAAAyH,CAAYC,GAClB5G,KAAK6G,YAAYC,KAAKF,GACtB,GAAI5G,KAAKM,WAAY,CACnBN,KAAKI,UAAYwG,EACjB,IAAM3B,aAAa8B,QAAQ/G,KAAKM,WAAY0G,OAAOJ,G,CAAU,MAAM,C,EAI/D,WAAAL,GACN,OAAOvG,KAAKW,aAAasG,SAAQC,GAAKA,EAAEC,O,CAGlC,eAAAlF,GACN,MAAMmF,EAAMpH,KAAKuG,cAAcc,WAAUC,GAAKA,EAAEpI,KAAOc,KAAKuB,oBAC5DvB,KAAKc,YAAcsG,GAAO,EAAIA,EAAM,C,CAG9B,iBAAAG,CAAkB3B,EAAkB4B,GAC1C,MAAMC,EAAQzH,KAAKuG,cAAcG,OACjC,IAAIE,EAAOY,EACX,OAAQ5B,EAAE8B,KACR,IAAK,YAAa9B,EAAE+B,iBAAkBf,GAAQY,EAAQ,GAAKC,EAAO,MAClE,IAAK,UAAa7B,EAAE+B,iBAAkBf,GAAQY,EAAQ,EAAIC,GAASA,EAAO,MAC1E,IAAK,OAAa7B,EAAE+B,iBAAkBf,EAAO,EAAG,MAChD,IAAK,MAAahB,EAAE+B,iBAAkBf,EAAOa,EAAQ,EAAG,MACxD,QAAS,OAEXzH,KAAKc,YAAc8F,EACnB,MAAMO,EAAQS,MAAMC,KAClB7H,KAAKyC,GAAGqF,iBAA8B,sCAExCX,EAAMP,IAAOmB,O,CAGP,WAAA3C,GACN,MAAM4C,EAAOhI,KAAKyC,GAAGC,cAAc,oBACnC,IAAKsF,EAAM,OACX,MAAMC,EAAYD,EAAKE,aAAeF,EAAKG,UAAYH,EAAKI,aAC5DpI,KAAKY,SAAWqH,EAAY,C,CAGtB,gBAAAI,CAAiBzC,GACvB,MAAMoC,EAAOpC,EAAE0C,OACf,MAAML,EAAYD,EAAKE,aAAeF,EAAKG,UAAYH,EAAKI,aAC5DpI,KAAKY,SAAWqH,EAAY,C,CAGtB,eAAAM,CAAgBrJ,GACtBc,KAAKwI,YAAY1B,KAAK5H,E,CAGhB,YAAAuJ,GACNzI,KAAK2G,aAAa3G,KAAKI,U,CAGjB,kBAAAsI,GACN1I,KAAK2I,kBAAkB7B,M,CAGjB,qBAAAxB,GACN,GAAItF,KAAKqB,mBAAqB,KAAM,CAClC6B,OAAO0F,aAAa5I,KAAKqB,kBACzBrB,KAAKqB,iBAAmB,I,EAIpB,4BAAAwH,GACN7I,KAAKsF,wBACLtF,KAAKqB,iBAAmB6B,OAAO4F,YAAW,KACxC9I,KAAKsB,gBAAkB,KACvBtB,KAAKqB,iBAAmB,IAAI,GAC3B,I,CAGG,4BAAA0H,GACN,GAAI/I,KAAKiB,WAAY,OACrBjB,KAAKsF,wBACLtF,KAAKsB,gBAAkB,K,CAGjB,2BAAA0H,CAA4BpD,GAClCA,EAAE+B,iBACF/B,EAAEqD,kBAEF,MAAMC,EAAelJ,KAAKI,WAAaJ,KAAKe,eAC5Cf,KAAKiB,WAAa,KAClBjB,KAAKkB,WAAa0E,EAAEuD,QACpBnJ,KAAKmB,QAAU,MACfnB,KAAKoB,WAAa,EAClBpB,KAAKsF,wBACLtF,KAAKsB,gBAAkB,MAEvBnC,SAAS6I,KAAK3I,MAAM+J,OAAS,YAC7BjK,SAAS6I,KAAK3I,MAAMgK,WAAa,OAEjC,MAAMC,EAAUC,IACd,MAAMC,EAASD,EAAGJ,QAAUnJ,KAAKkB,WACjClB,KAAKoB,WAAa0B,KAAK2G,IAAID,GAC3B,GAAIxJ,KAAKmB,QAAS,OAClB,IAAK+H,GAAgBM,GAAS,EAAI,CAChCxJ,KAAK2G,YAAY,MACjB3G,KAAKmB,QAAU,I,MACV,GAAI+H,GAAgBM,EAAS,EAAG,CACrCxJ,KAAK2G,YAAY,OACjB3G,KAAKmB,QAAU,I,GAInB,MAAMuI,EAAO,KACX,IAAK1J,KAAKmB,SAAWnB,KAAKoB,WAAa,EAAG,CACxCpB,KAAK2G,aAAauC,E,CAEpBlJ,KAAKiB,WAAa,MAClBjB,KAAKsB,gBAAkB,MACvBnC,SAAS6I,KAAK3I,MAAM+J,OAAS,GAC7BjK,SAAS6I,KAAK3I,MAAMgK,WAAa,GACjCnG,OAAOsC,oBAAoB,YAAa8D,GACxCpG,OAAOsC,oBAAoB,UAAWkE,GACtC1J,KAAKuF,uBAAyBY,UAC9BnG,KAAKyF,qBAAuBU,SAAS,EAGvCnG,KAAKuF,uBAAyB+D,EAC9BtJ,KAAKyF,qBAAuBiE,EAC5BxG,OAAO4C,iBAAiB,YAAawD,GACrCpG,OAAO4C,iBAAiB,UAAW4D,E,CAG7B,aAAAC,CAAcrD,EAAoBc,EAAahH,GACrD,MAAMwJ,EAAWtD,EAAKpH,KAAOc,KAAKuB,kBAElC,MAAMsI,EAAc,CAClBC,EAAA,QAAMC,MAAM,wBACVD,EAAA,WAASE,KAAM1D,EAAK2D,KAAMC,KAAK,KAAKC,MAAM,UAAUC,KAAM9D,EAAK8D,QAEjEN,EAAA,QAAMC,MAAM,yBACVD,EAAA,QAAMC,MAAM,8BACVD,EAAA,WACEO,GAAG,OACHpK,QAAS2J,EAAW,4BAA8B,mBAClDO,MAAM,WAEL7D,EAAKgE,SAIZhE,EAAKiE,KAAOT,EAAA,QAAMC,MAAM,sBAAqB,cAAa,UAG5D,MAAMS,EAAc,CAClBT,MAAO,CAAE,kBAAmB,KAAM,0BAA2BH,GAC7D,eAAgBA,EAAY,OAAmBzD,UAC/CsE,MAAOrK,EAAYkG,EAAKgE,MAAQnE,UAChCuE,SAAUtD,IAAQpH,KAAKc,YAAc,GAAI,EACzC6J,QAAS,IAAM3K,KAAKuI,gBAAgBjC,EAAKpH,IACzC0L,UAAYhF,GAAqB5F,KAAKuH,kBAAkB3B,EAAGwB,GAC3DyD,QAAS,KAAQ7K,KAAKc,YAAcsG,CAAG,GAGzC,OAAOd,EAAKE,KACRsD,EAAA,QAAOU,EAAahE,KAAMF,EAAKE,MAAOqD,GACtCC,EAAA,aAAYU,EAAaM,KAAK,UAAUjB,E,CAG9C,MAAAkB,GACE,MAAMC,EAAchL,KAAKU,kBAAoB,YAC7C,MAAMN,EAAYJ,KAAKI,WAAaJ,KAAKe,eACzC,MAAMP,SAAEA,EAAQC,YAAEA,GAAgBT,KAElC,MAAMiL,EAAkC,CACtC,YAAa,KACb,uBAAwBD,EACxB,uBAAwBA,EACxB,uBAAwB5K,EACxB,uBAAwBJ,KAAKa,YAC7B,uBAAwBb,KAAKY,UAG/B,OACEkJ,EAACoB,EAAI,CAAAxD,IAAA,2CAACrI,MAAO,CAAE8L,QAAS,QAASC,SAAU,aACzCtB,EAAA,OAAApC,IAAA,2CAAKqC,MAAOkB,EAAM,aAAcD,EAAc,kBAAoB,uBAGhElB,EAAA,OAAApC,IAAA,2CAAKqC,MAAM,qBACTD,EAAA,UAAApC,IAAA,2CACEoD,KAAK,SACLf,MAAM,wBACNY,QAAS,IAAM3K,KAAKyI,eAAc,aACtBrI,EAAY,oBAAsB,sBAAqB,gBACpDA,EAAY,QAAU,QAGrC0J,EAAA,QAAApC,IAAA,2CAAMqC,MAAM,yBAAwB,cAAa,QAC/CD,EAAA,OAAApC,IAAA,2CAAKqC,MAAM,oBAAoBsB,QAAQ,YAAYxG,KAAK,OAAOyG,MAAM,6BAA6BC,UAAU,QAAO,cAAa,QAC9HzB,EAAA,QAAApC,IAAA,2CAAM8D,EAAE,qKAAqK3G,KAAK,mBAItLiF,EAAA,QAAApC,IAAA,2CAAMqC,MAAM,2BAA0B,cAAa,QACjDD,EAAA,WAAApC,IAAA,2CACEsC,KAAM5J,EAAY,cAAgB,gBAClC8J,KAAK,KACLC,MAAM,eAOdL,EAAA,OAAApC,IAAA,2CAAKqC,MAAM,kBAAkB0B,SAAU7F,GAAK5F,KAAKqI,iBAAiBzC,IAC/D,MACC,IAAI8F,EAAU,EACd,OAAO1L,KAAKW,aAAagL,KAAIC,GAC3B9B,EAAA,OAAKC,MAAM,oBACR6B,EAAMtB,OACLR,EAAA,QAAMC,MAAM,0BACVD,EAAA,QAAMC,MAAM,8BACVD,EAAA,WAASO,GAAG,OAAOpK,QAAQ,wBAAwBkK,MAAM,WACtDyB,EAAMtB,SAKdsB,EAAMzE,MAAMwE,KAAIrF,GAAQtG,KAAK2J,cAAcrD,EAAMoF,IAAWtL,OAGlE,EAhBA,IAoBH0J,EAAA,OAAApC,IAAA,2CAAKqC,MAAM,qBAETD,EAAA,UAAApC,IAAA,2CACEoD,KAAK,SACLf,MAAM,wBACNU,MAAOO,EAAc,WAAa,YAAW,aACjCA,EAAc,gBAAkB,kBAC5CL,QAAS,IAAM3K,KAAK0I,sBAEpBoB,EAAA,WAAApC,IAAA,2CAASsC,KAAMgB,EAAc,OAAS,YAAad,KAAK,KAAKC,MAAM,aAIrEL,EAAA,UAAApC,IAAA,2CACEoD,KAAK,SACLf,MAAM,yCAAwC,aAClC3J,EAAY,SAASI,IAAa,iBAAiBA,KAE/DsJ,EAAA,QAAApC,IAAA,2CAAMqC,MAAM,sDACVD,EAAA,QAAApC,IAAA,2CAAMqC,MAAM,8BACVD,EAAA,WAAApC,IAAA,2CAAS2C,GAAG,OAAOpK,QAAQ,4BAA4BkK,MAAM,WAC1D3J,KAIPsJ,EAAA,QAAApC,IAAA,2CAAMqC,MAAM,mDAAkD,cAAa,QACzED,EAAA,QAAApC,IAAA,2CAAMqC,MAAM,mCACVD,EAAA,WAAApC,IAAA,2CAASsC,KAAK,gBAAgBE,KAAK,KAAKC,MAAM,aAEhDL,EAAA,QAAApC,IAAA,2CAAMqC,MAAM,oCACVD,EAAA,WAAApC,IAAA,2CAASsC,KAAK,SAASE,KAAK,KAAKC,MAAM,YACvCL,EAAA,QAAApC,IAAA,2CAAMqC,MAAM,2BACVD,EAAA,WAAApC,IAAA,2CAAS2C,GAAG,OAAOpK,QAAQ,wBAAwBkK,MAAM,WACtD1J,UAWbT,KAAKe,gBACL+I,EAAA,OAAApC,IAAA,2CACEqC,MAAO,CACL,2BAA4B,KAC5B,oCAAqC/J,KAAKsB,iBAE5CuK,aAAc,IAAM7L,KAAK6I,+BACzBiD,aAAc,IAAM9L,KAAK+I,+BACzBgD,YAAcnG,GAAkB5F,KAAKgJ,4BAA4BpD,GAAE,cACvD,S","ignoreList":[]}
|
|
1
|
+
{"version":3,"names":["panelNavCss","vtStyleInjected","ensureVtStyle","id","document","getElementById","style","createElement","textContent","join","head","appendChild","PanelNav","__stencil_proxyCustomElement","HTMLElement","constructor","registerHost","this","variant","groups","activeId","collapsed","breakpoint","storageKey","currentUrl","userName","userInitial","renderedVariant","parsedGroups","atBottom","isAnimating","rovingIndex","viewportNarrow","urlDerivedActiveId","isDragging","dragStartX","didSnap","lastDeltaX","edgeOverlayTimer","showEdgeOverlay","effectiveActiveId","onCollapsedChange","startCollapseAnimation","onBreakpointChange","disconnectResizeObserver","connectResizeObserver","onGroupsChange","val","JSON","parse","syncRovingIndex","onActiveIdChange","onCurrentUrlChange","syncActiveFromUrl","onVariantChange","newVal","activeViewTransition","skipTransition","undefined","startViewTransition","btn","el","querySelector","rect","getBoundingClientRect","x","Math","round","left","width","window","innerWidth","y","top","height","innerHeight","maxR","ceil","hypot","max","documentElement","setProperty","transition","async","Promise","resolve","requestAnimationFrame","finished","finally","ready","then","durToken","getComputedStyle","getPropertyValue","trim","duration","parseFloat","animate","clipPath","easing","fill","pseudoElement","componentWillLoad","stored","localStorage","getItem","componentDidLoad","checkScroll","disconnectedCallback","clearEdgeOverlayTimer","globalMouseMoveHandler","removeEventListener","globalMouseUpHandler","panel","transitionEndHandler","e","propertyName","addEventListener","resizeObserver","ResizeObserver","observe","disconnect","best","bestLen","item","getAllItems","href","startsWith","length","applyToggle","next","dsNavToggle","emit","setItem","String","flatMap","g","items","idx","findIndex","i","handleItemKeyDown","index","total","key","preventDefault","Array","from","querySelectorAll","focus","body","remaining","scrollHeight","scrollTop","clientHeight","handleBodyScroll","target","handleItemClick","dsNavSelect","handleToggle","handleFooterAction","dsNavFooterAction","clearTimeout","handleResizeHandleMouseEnter","setTimeout","handleResizeHandleMouseLeave","handleResizeHandleMouseDown","stopPropagation","wasCollapsed","clientX","cursor","userSelect","onMove","ev","deltaX","abs","onUp","renderNavItem","isActive","itemContent","h","class","name","icon","size","color","flag","as","label","dot","sharedProps","title","tabIndex","onClick","onKeyDown","onFocus","type","render","isDashboard","navCls","Host","display","position","viewBox","xmlns","focusable","d","onScroll","flatIdx","map","group","onMouseEnter","onMouseLeave","onMouseDown"],"sources":["src/wc/components/PanelNav/PanelNav.css?tag=ds-panel-nav&encapsulation=scoped","src/wc/components/PanelNav/PanelNav.tsx"],"sourcesContent":["/* ── Theme variables ──────────────────────────────────────────────────────── */\n\n.panel-nav {\n /* ─ Sizes ─ */\n --_nav-width: 200px;\n --_nav-width-collapsed: 48px;\n --_nav-item-height: var(--dimension-size-400, 32px);\n --_nav-padding-x: var(--dimension-space-100, 8px);\n --_nav-padding-y: var(--dimension-space-100, 8px);\n /* gear button outer width = padding*2 + icon */\n --_nav-footer-btn-w: calc(var(--dimension-space-075, 6px) * 2 + var(--dimension-iconography-md, 20px));\n\n /* ─ Motion ─ */\n /* --ds-panel-nav-speed: unitless multiplier; override in Storybook to slow-mo (e.g. 10 = 10×).\n *-dur vars are duration-only (needed for calc); *-motion vars add easing for use in transitions. */\n --_nav-speed: var(--ds-panel-nav-speed, 1);\n --_nav-expand-dur: calc(var(--_nav-speed) * var(--effect-animation-duration-medium-1, 300ms));\n --_nav-expand-motion: var(--_nav-expand-dur) var(--effect-animation-easing-ease-in-out, ease);\n --_nav-label-fade-dur: calc(var(--_nav-speed) * var(--effect-animation-duration-medium-1, 300ms));\n --_nav-label-fade-motion: var(--_nav-label-fade-dur) var(--effect-animation-easing-ease-in-out, ease);\n --_nav-label-fade-out-dur: calc(var(--_nav-speed) * var(--effect-animation-duration-medium-1, 300ms));\n --_nav-label-fade-out-motion: var(--_nav-label-fade-out-dur) var(--effect-animation-easing-ease-in-out, ease);\n\n /* ─ Colours – dashboard defaults ─ */\n --_nav-bg: var(--color-navigation-background);\n --_nav-border: var(--color-navigation-border-tertiary);\n --_nav-divider: var(--color-navigation-divider);\n --_nav-fg-primary: var(--color-navigation-foreground-primary);\n --_nav-fg-secondary: var(--color-navigation-foreground-secondary);\n --_nav-fg-tertiary: var(--color-navigation-foreground-tertiary);\n --_nav-hover: var(--color-navigation-interaction-hover);\n --_nav-pressed: var(--color-navigation-interaction-pressed);\n --_nav-focus: var(--color-navigation-interaction-focus);\n --_nav-dot: var(--color-navigation-foreground-brand);\n}\n\n/* ── Settings overrides ──────────────────────────────────────────────────── */\n\n.panel-nav--settings {\n --_nav-bg: var(--color-background-secondary);\n --_nav-border: var(--color-border-tertiary);\n --_nav-divider: var(--color-border-tertiary);\n --_nav-fg-primary: var(--color-foreground-primary);\n --_nav-fg-secondary: var(--color-foreground-secondary);\n --_nav-fg-tertiary: var(--color-foreground-tertiary);\n --_nav-hover: var(--color-interaction-hover);\n --_nav-pressed: var(--color-interaction-pressed);\n --_nav-focus: var(--color-interaction-focus);\n --_nav-dot: var(--color-foreground-bold-brand);\n}\n\n/* ── Shell ───────────────────────────────────────────────────────────────── */\n\n.panel-nav {\n display: flex;\n flex-direction: column;\n width: var(--_nav-width);\n min-width: var(--_nav-width);\n height: 100%;\n background: var(--_nav-bg);\n border-right: var(--dimension-stroke-width-012, 1px) solid var(--_nav-border);\n box-sizing: border-box;\n overflow: hidden;\n transition:\n width var(--_nav-expand-motion),\n min-width var(--_nav-expand-motion),\n background var(--effect-motion-short-3, 200ms ease),\n border-color var(--effect-motion-short-3, 200ms ease);\n font-family: var(--typography-font-family, system-ui);\n}\n\n.panel-nav--collapsed {\n width: var(--_nav-width-collapsed);\n min-width: var(--_nav-width-collapsed);\n}\n\n/* ── Header ──────────────────────────────────────────────────────────────── */\n\n.panel-nav__header {\n display: flex;\n align-items: center;\n padding: var(--dimension-space-100, 8px);\n flex-shrink: 0;\n box-sizing: border-box;\n border-bottom: var(--dimension-stroke-width-012, 1px) solid var(--_nav-divider);\n transition: border-color var(--effect-motion-short-3, 200ms ease);\n}\n\n.panel-nav__header-btn {\n all: unset;\n display: flex;\n align-items: center;\n justify-content: center;\n padding: var(--dimension-space-075, 6px);\n border-radius: var(--dimension-radius-025, 2px);\n cursor: pointer;\n color: var(--_nav-fg-primary);\n position: relative;\n flex-shrink: 0;\n box-sizing: border-box;\n transition: color var(--effect-motion-short-3, 200ms ease);\n}\n\n/* Interaction pseudo-element */\n.panel-nav__header-btn::after {\n content: '';\n position: absolute;\n inset: 0;\n border-radius: var(--dimension-radius-025, 2px);\n background: transparent;\n transition: background var(--effect-animation-duration-short-3, 200ms) var(--effect-animation-easing-ease-out, ease);\n pointer-events: none;\n}\n\n.panel-nav__header-btn:hover::after { background: var(--_nav-hover); transition: none; }\n.panel-nav__header-btn:active::after { background: var(--_nav-pressed); transition: none; }\n.panel-nav__header-btn:focus-visible { outline: none; }\n.panel-nav__header-btn:focus-visible::after {\n outline: var(--dimension-stroke-width-025, 2px) solid var(--_nav-focus);\n outline-offset: calc(-1 * var(--dimension-stroke-width-025, 2px));\n}\n\n/* Logo — visible by default, hidden on hover */\n.panel-nav__header-logo {\n display: flex;\n align-items: center;\n justify-content: center;\n position: relative;\n z-index: 1;\n transition: opacity var(--effect-motion-short-2, 100ms ease);\n}\n\n.panel-nav__m-mark {\n width: var(--dimension-iconography-md, 20px);\n height: var(--dimension-iconography-md, 20px);\n display: block;\n}\n\n/* Toggle icon — hidden by default, revealed on hover */\n.panel-nav__header-toggle {\n display: none;\n align-items: center;\n justify-content: center;\n position: absolute;\n inset: 0;\n z-index: 1;\n}\n\n.panel-nav__header-btn:hover .panel-nav__header-logo { opacity: 0; }\n.panel-nav__header-btn:hover .panel-nav__header-toggle { display: flex; }\n\n/* ── Scrollable body ─────────────────────────────────────────────────────── */\n\n.panel-nav__body {\n flex: 1;\n overflow-y: auto;\n overflow-x: hidden;\n display: flex;\n flex-direction: column;\n gap: var(--dimension-space-200, 16px);\n padding: var(--dimension-space-100, 8px);\n box-sizing: border-box;\n scrollbar-width: none;\n}\n\n.panel-nav__body::-webkit-scrollbar { display: none; }\n\n/* ── Group ───────────────────────────────────────────────────────────────── */\n\n.panel-nav__group {\n display: flex;\n flex-direction: column;\n gap: var(--dimension-space-050, 4px);\n}\n\n.panel-nav__group-label {\n /* Typography via ds-text (text-caption-emphasis) */\n display: flex;\n align-items: center;\n text-transform: uppercase;\n color: var(--_nav-fg-secondary);\n user-select: none;\n height: var(--dimension-size-300, 24px);\n line-height: var(--typography-lineheight-xs, 12px);\n padding: 0 var(--dimension-space-075, 6px);\n white-space: nowrap;\n overflow: hidden;\n max-height: var(--dimension-size-300, 24px);\n opacity: 1;\n transition:\n opacity var(--_nav-label-fade-motion) 0ms,\n max-height var(--_nav-label-fade-motion) 0ms,\n padding var(--_nav-label-fade-motion) 0ms,\n color var(--effect-motion-short-3, 200ms ease);\n}\n\n/* ── Item ────────────────────────────────────────────────────────────────── */\n\n.panel-nav__item {\n all: unset;\n display: flex;\n align-items: center;\n gap: var(--dimension-space-050, 4px);\n padding: 0 var(--dimension-space-075, 6px);\n height: var(--dimension-size-400, 32px);\n border-radius: var(--dimension-radius-025, 2px);\n cursor: pointer;\n color: var(--_nav-fg-secondary);\n position: relative;\n box-sizing: border-box;\n width: 100%;\n user-select: none;\n transition: color var(--effect-motion-short-1, 50ms ease-in-out);\n}\n\n/* Interaction layer */\n.panel-nav__item::after {\n content: '';\n position: absolute;\n inset: 0;\n border-radius: var(--dimension-radius-025, 2px);\n background: transparent;\n pointer-events: none;\n transition: background var(--effect-animation-duration-short-3, 200ms) var(--effect-animation-easing-ease-out, ease);\n}\n\n.panel-nav__item:hover::after { background: var(--_nav-hover); transition: none; }\n.panel-nav__item:active::after { background: var(--_nav-pressed); transition: none; }\n.panel-nav__item:focus-visible { outline: none; }\n.panel-nav__item:focus-visible::after {\n outline: var(--dimension-stroke-width-025, 2px) solid var(--_nav-focus);\n outline-offset: calc(-1 * var(--dimension-stroke-width-025, 2px));\n}\n\n/* ── Active item ─────────────────────────────────────────────────────────── */\n\n.panel-nav__item--active {\n color: var(--_nav-fg-primary);\n /* Bold typography via ds-text (text-body-medium-emphasis) on the label */\n}\n\n/* ── Item icon ───────────────────────────────────────────────────────────── */\n\n.panel-nav__item-icon {\n display: flex;\n align-items: center;\n justify-content: center;\n flex-shrink: 0;\n width: var(--dimension-iconography-md, 20px);\n height: var(--dimension-iconography-md, 20px);\n position: relative;\n}\n\n/* ── Item label ──────────────────────────────────────────────────────────── */\n\n.panel-nav__item-label {\n flex: 1;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n min-width: 0;\n height: var(--typography-lineheight-md, 20px);\n line-height: var(--typography-lineheight-md, 20px);\n opacity: 1;\n /* Gradient wipe: mask sweeps left-to-right on expand, right-to-left on collapse */\n mask-image: linear-gradient(to right, black 50%, transparent 100%);\n -webkit-mask-image: linear-gradient(to right, black 50%, transparent 100%);\n mask-size: 200% 100%;\n -webkit-mask-size: 200% 100%;\n mask-position: 0% 0;\n -webkit-mask-position: 0% 0;\n transition:\n opacity var(--_nav-label-fade-motion) 0ms,\n mask-position var(--_nav-label-fade-motion) 0ms,\n -webkit-mask-position var(--_nav-label-fade-motion) 0ms;\n}\n\n.panel-nav__item-label-text {\n padding: 0 var(--dimension-space-025, 2px);\n}\n\n/* ── Item notification dot — single element, moves between positions ──────── */\n\n.panel-nav__item-dot {\n position: absolute;\n right: 13px;\n top: 13px;\n width: var(--dimension-space-075, 6px);\n height: var(--dimension-space-075, 6px);\n border-radius: var(--dimension-radius-full, 999px);\n background: var(--_nav-dot);\n box-shadow: 0 0 0 var(--dimension-stroke-width-018, 1.5px) var(--_nav-bg);\n pointer-events: none;\n transition:\n right var(--_nav-expand-motion),\n top var(--_nav-expand-motion),\n background var(--effect-motion-short-3, 200ms ease),\n box-shadow var(--effect-motion-short-3, 200ms ease);\n}\n\n.panel-nav--collapsed .panel-nav__item-dot {\n right: 6px;\n top: 6px;\n}\n\n.panel-nav--animating .panel-nav__item-label {\n text-overflow: clip;\n}\n\n.panel-nav--collapsed .panel-nav__item-label {\n opacity: 0;\n pointer-events: none;\n text-overflow: clip;\n mask-position: 100% 0;\n -webkit-mask-position: 100% 0;\n transition:\n opacity var(--_nav-label-fade-out-motion) 0ms,\n mask-position var(--_nav-label-fade-out-motion) 0ms,\n -webkit-mask-position var(--_nav-label-fade-out-motion) 0ms;\n}\n\n.panel-nav--collapsed .panel-nav__group-label {\n opacity: 0;\n max-height: 0;\n padding: 0 var(--dimension-space-075, 6px);\n pointer-events: none;\n transition:\n opacity var(--_nav-label-fade-out-motion) 0ms,\n max-height var(--_nav-label-fade-out-motion) 0ms,\n padding var(--_nav-label-fade-out-motion) 0ms,\n color var(--effect-motion-short-3, 200ms ease);\n}\n\n/* ── Footer ──────────────────────────────────────────────────────────────── */\n\n.panel-nav__footer {\n position: relative;\n flex-shrink: 0;\n /* single row: padding*2 + item */\n height: calc(var(--_nav-padding-y) * 2 + var(--_nav-item-height));\n transition: height var(--_nav-expand-motion);\n}\n\n.panel-nav--collapsed .panel-nav__footer {\n /* two rows: padding*2 + item*2 + gap */\n height: calc(var(--_nav-padding-y) * 2 + var(--_nav-item-height) * 2 + var(--dimension-space-050, 4px));\n}\n\n/* Gradient fade — floats above the footer, masks content scrolling under it */\n.panel-nav__footer::before {\n content: '';\n position: absolute;\n bottom: 100%;\n left: 0;\n right: 0;\n height: calc(var(--dimension-base, 8px) * 6);\n background: linear-gradient(to bottom, transparent, var(--_nav-bg));\n pointer-events: none;\n opacity: 1;\n transition: opacity var(--effect-animation-duration-short-3, 200ms) var(--effect-animation-easing-ease-out, ease);\n}\n\n.panel-nav--at-bottom .panel-nav__footer::before {\n opacity: 0;\n}\n\n/* ── Footer icon button (gear / dashboard) ───────────────────────────────── */\n\n.panel-nav__footer-btn {\n all: unset;\n display: flex;\n align-items: center;\n justify-content: center;\n position: absolute;\n left: var(--_nav-padding-x);\n top: var(--_nav-padding-y);\n padding: var(--dimension-space-075, 6px);\n border-radius: var(--dimension-radius-half, 9999px);\n cursor: pointer;\n color: var(--_nav-fg-secondary);\n box-sizing: border-box;\n background: transparent;\n transition:\n background var(--effect-animation-duration-short-3, 200ms) var(--effect-animation-easing-ease-out, ease),\n color var(--effect-animation-duration-short-3, 200ms) var(--effect-animation-easing-ease-out, ease);\n}\n\n.panel-nav__footer-btn:focus-visible {\n outline: var(--dimension-stroke-width-025, 2px) solid var(--_nav-focus);\n outline-offset: calc(-1 * var(--dimension-stroke-width-025, 2px));\n}\n\n/* Dashboard: hover previews settings surface */\n.panel-nav--dashboard .panel-nav__footer-btn:hover {\n background: var(--color-background-secondary);\n color: var(--color-foreground-secondary);\n transition: none;\n}\n\n/* Settings: hover previews dashboard nav surface */\n.panel-nav--settings .panel-nav__footer-btn:hover {\n background: var(--color-navigation-background);\n color: var(--color-navigation-foreground-secondary);\n transition: none;\n}\n\n/* ── Footer user — absolutely positioned, slides from row to below gear ───── */\n\n.panel-nav__footer-user {\n position: absolute;\n /* row position: right of gear button */\n left: calc(var(--_nav-padding-x) + var(--_nav-footer-btn-w) + var(--dimension-space-050, 4px));\n right: var(--_nav-padding-x);\n top: var(--_nav-padding-y);\n width: auto;\n min-width: 0;\n transition:\n left var(--_nav-expand-motion),\n right var(--_nav-expand-motion),\n top var(--_nav-expand-motion),\n color var(--effect-motion-short-3, 200ms ease);\n}\n\n.panel-nav--collapsed .panel-nav__footer-user {\n /* column position: full-width row below gear */\n left: var(--_nav-padding-x);\n right: var(--_nav-padding-x);\n top: calc(var(--_nav-padding-y) + var(--_nav-item-height) + var(--dimension-space-050, 4px));\n}\n\n/* Label: absolute so it stays in place and only fades — no mask slide */\n.panel-nav__footer-user-label {\n text-align: right;\n position: absolute;\n left: var(--dimension-space-075, 6px);\n right: calc(var(--dimension-space-075, 6px) + var(--dimension-iconography-md, 20px) + var(--dimension-space-050, 4px));\n top: 50%;\n transform: translateY(-50%);\n flex: none;\n height: var(--typography-lineheight-md, 20px);\n mask-image: none;\n -webkit-mask-image: none;\n transition: opacity var(--_nav-label-fade-motion);\n}\n\n.panel-nav--collapsed .panel-nav__footer-user-label {\n opacity: 0;\n pointer-events: none;\n mask-position: 0% 0;\n -webkit-mask-position: 0% 0;\n}\n\n/* ── Footer user icon — cross-fades chevron ↔ circle+initial ────────────── */\n\n/* Absolute — right-anchored in expanded, left-anchored in collapsed to match regular item icon position */\n.panel-nav__footer-user-icon {\n position: absolute;\n left: calc(100% - var(--dimension-iconography-md, 20px) - var(--dimension-space-075, 6px));\n top: 0;\n bottom: 0;\n height: auto;\n display: flex;\n align-items: center;\n justify-content: center;\n transition: left var(--_nav-expand-motion);\n}\n\n.panel-nav--collapsed .panel-nav__footer-user-icon {\n left: var(--dimension-space-075, 6px);\n}\n\n.panel-nav__footer-icon-expanded,\n.panel-nav__footer-icon-collapsed {\n position: absolute;\n inset: 0;\n display: flex;\n align-items: center;\n justify-content: center;\n}\n\n.panel-nav__footer-icon-expanded {\n opacity: 1;\n transition: opacity var(--_nav-label-fade-motion);\n}\n\n.panel-nav__footer-icon-collapsed {\n opacity: 0;\n transition: opacity var(--_nav-label-fade-motion);\n}\n\n.panel-nav--collapsed .panel-nav__footer-icon-expanded {\n opacity: 0;\n transition: opacity var(--_nav-label-fade-out-motion);\n}\n\n.panel-nav--collapsed .panel-nav__footer-icon-collapsed {\n opacity: 1;\n transition: opacity var(--_nav-label-fade-out-motion);\n}\n\n/* ── User initial — overlaid on the circle icon ─────────────────────────── */\n\n.panel-nav__user-initial {\n position: absolute;\n inset: 0;\n display: flex;\n align-items: center;\n justify-content: center;\n color: var(--_nav-fg-secondary);\n text-transform: uppercase;\n line-height: 1;\n pointer-events: none;\n transition: color var(--effect-motion-short-3, 200ms ease);\n}\n\n/* ── Drag-to-resize handle ───────────────────────────────────────────────── */\n\n.panel-nav__resize-handle {\n position: absolute;\n top: 0;\n /* Centered on the right edge: --dimension-space-050 outside + inside = --dimension-space-100 hit zone */\n right: var(--dimension-space-n050, -4px);\n bottom: 0;\n width: var(--dimension-space-100, 8px);\n z-index: 10;\n cursor: ew-resize;\n touch-action: none;\n /* Needs a real background for Safari hit-testing */\n background: transparent;\n}\n\n/* Safari requires non-transparent fill for hit testing */\n.panel-nav__resize-handle::before {\n content: '';\n position: absolute;\n inset: 0;\n background: rgba(0, 0, 0, 0.001);\n}\n\n/* The brand-bold overlay line */\n.panel-nav__resize-handle::after {\n content: '';\n position: absolute;\n top: 0;\n bottom: 0;\n left: 50%;\n transform: translateX(-50%);\n width: var(--dimension-stroke-width-025, 2px);\n background: var(--color-background-bold-brand);\n opacity: 0;\n pointer-events: none;\n transition: opacity var(--effect-motion-short-2, 100ms ease-in-out);\n}\n\n.panel-nav__resize-handle--overlay::after {\n opacity: 1;\n}\n\n/* ── Reduced motion ──────────────────────────────────────────────────────── */\n\n@media (prefers-reduced-motion: reduce) {\n .panel-nav,\n .panel-nav *,\n .panel-nav *::before,\n .panel-nav *::after,\n .panel-nav__resize-handle,\n .panel-nav__resize-handle::before,\n .panel-nav__resize-handle::after {\n transition-duration: 0.01ms !important;\n animation-duration: 0.01ms !important;\n transition-delay: 0.01ms !important;\n }\n}\n","import { Component, Prop, Event, EventEmitter, Watch, State, Element, h, Host } from '@stencil/core';\n\n// Injected once per document to suppress the browser default cross-fade and\n// pin the new-state snapshot to a 0px circle so the WAAPI reveal has no flash.\nlet vtStyleInjected = false;\nfunction ensureVtStyle() {\n if (vtStyleInjected) return;\n const id = 'ds-panel-nav-vt-style';\n if (document.getElementById(id)) { vtStyleInjected = true; return; }\n const style = document.createElement('style');\n style.id = id;\n style.textContent = [\n '::view-transition-old(root),::view-transition-new(root){animation:none;mix-blend-mode:normal}',\n '::view-transition-new(root){clip-path:circle(0px at var(--vt-x,50%) var(--vt-y,50%))}',\n ].join('\\n');\n document.head.appendChild(style);\n vtStyleInjected = true;\n}\n\nexport type PanelNavVariant = 'dashboard' | 'settings';\n\nexport interface PanelNavItem {\n id: string;\n icon: string;\n label: string;\n /** Show a notification dot badge on the item */\n dot?: boolean;\n flag?: boolean;\n /** When set, the item renders as an `<a>` element so any router that\n * intercepts anchor clicks (Angular Router, React Router, etc.) handles\n * navigation automatically. `dsNavSelect` still fires on click. */\n href?: string;\n}\n\nexport interface PanelNavGroup {\n id?: string;\n label?: string;\n items: PanelNavItem[];\n}\n\n@Component({\n tag: 'ds-panel-nav',\n styleUrl: 'PanelNav.css',\n scoped: true,\n})\nexport class PanelNav {\n /** Visual variant: `dashboard` = always-dark surface, `settings` = light surface */\n @Prop() variant: PanelNavVariant = 'dashboard';\n\n /** JSON string of `PanelNavGroup[]` */\n @Prop() groups: string = '[]';\n\n /** ID of the currently active/selected nav item. Overridden by `currentUrl` matching when set. */\n @Prop() activeId: string = '';\n\n /** Whether the nav is in collapsed (icon-only) state.\n * When `storageKey` is set the component manages this internally —\n * do not also bind it from outside in that mode. */\n @Prop({ mutable: true }) collapsed: boolean = false;\n\n /** Viewport width (px) below which the nav auto-collapses to icon-only mode. 0 = disabled. */\n @Prop() breakpoint: number = 0;\n\n /** `localStorage` key used to persist the collapsed state across page loads.\n * When set the component is self-managing for collapsed state; `dsNavToggle`\n * still fires for consumers that want to observe the change. */\n @Prop() storageKey: string = '';\n\n /** Current route URL (e.g. `window.location.pathname` or the router's active URL).\n * When set the component derives the active item by matching item `href` values\n * against this string (longest prefix wins), overriding `activeId`. */\n @Prop() currentUrl: string = '';\n\n /** Display name for the footer user section */\n @Prop() userName: string = '';\n\n /** Single character shown in the collapsed avatar */\n @Prop() userInitial: string = '';\n\n /** Emitted when a nav item is clicked. Detail = the item's `id`. */\n @Event() dsNavSelect!: EventEmitter<string>;\n\n /** Emitted when the collapse toggle is clicked. Detail = new collapsed state. */\n @Event() dsNavToggle!: EventEmitter<boolean>;\n\n /** Emitted when the footer left button (gear / dashboard) is clicked. */\n @Event() dsNavFooterAction!: EventEmitter<void>;\n\n @Element() el!: HTMLElement;\n\n /** Mirrors `variant` but is only updated inside the VT callback so the\n * View Transition captures a clean before/after snapshot. */\n @State() private renderedVariant: PanelNavVariant = 'dashboard';\n\n @State() private parsedGroups: PanelNavGroup[] = [];\n @State() private atBottom = false;\n @State() private isAnimating = false;\n @State() private rovingIndex: number = 0;\n @State() private viewportNarrow: boolean = false;\n @State() private urlDerivedActiveId: string = '';\n\n private transitionEndHandler?: (e: TransitionEvent) => void;\n private resizeObserver?: ResizeObserver;\n private activeViewTransition?: { skipTransition: () => void };\n\n // Drag-to-resize state (not @State — no re-render needed)\n private isDragging = false;\n private dragStartX = 0;\n private didSnap = false;\n private lastDeltaX = 0;\n private edgeOverlayTimer: number | null = null;\n private globalMouseMoveHandler?: (e: MouseEvent) => void;\n private globalMouseUpHandler?: () => void;\n\n @State() private showEdgeOverlay = false;\n\n /** The ID that should be treated as active. `currentUrl` matching takes precedence\n * over `activeId` when both are present. */\n private get effectiveActiveId(): string {\n return this.urlDerivedActiveId || this.activeId;\n }\n\n @Watch('collapsed')\n @Watch('viewportNarrow')\n onCollapsedChange() {\n this.startCollapseAnimation();\n }\n\n @Watch('breakpoint')\n onBreakpointChange() {\n this.disconnectResizeObserver();\n if (this.breakpoint > 0) this.connectResizeObserver();\n }\n\n @Watch('groups')\n onGroupsChange(val: string) {\n try {\n this.parsedGroups = JSON.parse(val);\n } catch {\n this.parsedGroups = [];\n }\n this.syncRovingIndex();\n }\n\n @Watch('activeId')\n @Watch('urlDerivedActiveId')\n onActiveIdChange() {\n this.syncRovingIndex();\n }\n\n @Watch('currentUrl')\n onCurrentUrlChange() {\n this.syncActiveFromUrl();\n }\n\n @Watch('variant')\n async onVariantChange(newVal: PanelNavVariant) {\n // Skip any in-progress transition immediately so rapid variant changes\n // (e.g. quick dashboard → settings → dashboard) never leave the nav stuck.\n this.activeViewTransition?.skipTransition();\n this.activeViewTransition = undefined;\n\n if (typeof (document as any).startViewTransition !== 'function') {\n this.renderedVariant = newVal;\n return;\n }\n\n ensureVtStyle();\n\n const btn = this.el.querySelector('.panel-nav__footer-btn') as HTMLElement | null;\n const rect = btn?.getBoundingClientRect();\n const x = rect ? Math.round(rect.left + rect.width / 2) : Math.round(window.innerWidth / 2);\n const y = rect ? Math.round(rect.top + rect.height / 2) : Math.round(window.innerHeight / 2);\n const maxR = Math.ceil(Math.hypot(\n Math.max(x, window.innerWidth - x),\n Math.max(y, window.innerHeight - y),\n ));\n\n document.documentElement.style.setProperty('--vt-x', `${x}px`);\n document.documentElement.style.setProperty('--vt-y', `${y}px`);\n\n const transition = (document as any).startViewTransition(async () => {\n this.renderedVariant = newVal;\n // Wait two animation frames instead of microtasks. Promise.resolve() ticks\n // are intercepted by zone.js in Angular apps, causing the VT to capture\n // the \"after\" snapshot before Stencil has repainted. rAF fires after the\n // browser has actually painted, guaranteeing the render is complete.\n await new Promise<void>(resolve => requestAnimationFrame(() => requestAnimationFrame(() => resolve())));\n });\n\n this.activeViewTransition = transition;\n transition.finished.finally(() => {\n if (this.activeViewTransition === transition) this.activeViewTransition = undefined;\n });\n\n transition.ready.then(() => {\n const durToken = getComputedStyle(document.documentElement)\n .getPropertyValue('--effect-animation-duration-long-1').trim();\n const duration = parseFloat(durToken) || 750;\n document.documentElement.animate(\n { clipPath: [`circle(0px at ${x}px ${y}px)`, `circle(${maxR}px at ${x}px ${y}px)`] },\n { duration, easing: 'ease-in-out', fill: 'forwards', pseudoElement: '::view-transition-new(root)' },\n );\n });\n }\n\n componentWillLoad() {\n this.renderedVariant = this.variant;\n if (this.storageKey) {\n try {\n const stored = localStorage.getItem(this.storageKey);\n if (stored !== null) this.collapsed = stored === 'true';\n } catch { /* localStorage unavailable */ }\n }\n this.onGroupsChange(this.groups);\n this.syncActiveFromUrl();\n }\n\n componentDidLoad() {\n this.checkScroll();\n if (this.breakpoint > 0) this.connectResizeObserver();\n // Re-sync groups in case componentWillLoad ran before host bindings were applied\n // (common with Angular's property binding timing)\n this.onGroupsChange(this.groups);\n }\n\n disconnectedCallback() {\n this.disconnectResizeObserver();\n this.clearEdgeOverlayTimer();\n if (this.globalMouseMoveHandler) {\n window.removeEventListener('mousemove', this.globalMouseMoveHandler);\n }\n if (this.globalMouseUpHandler) {\n window.removeEventListener('mouseup', this.globalMouseUpHandler);\n }\n }\n\n private startCollapseAnimation() {\n this.isAnimating = true;\n const panel = this.el.querySelector('.panel-nav') as HTMLElement | null;\n if (this.transitionEndHandler) {\n panel?.removeEventListener('transitionend', this.transitionEndHandler);\n }\n this.transitionEndHandler = (e: TransitionEvent) => {\n if (e.propertyName === 'width') {\n this.isAnimating = false;\n panel?.removeEventListener('transitionend', this.transitionEndHandler!);\n }\n };\n panel?.addEventListener('transitionend', this.transitionEndHandler);\n }\n\n private connectResizeObserver() {\n this.viewportNarrow = window.innerWidth < this.breakpoint;\n this.resizeObserver = new ResizeObserver(() => {\n this.viewportNarrow = window.innerWidth < this.breakpoint;\n });\n this.resizeObserver.observe(document.documentElement);\n }\n\n private disconnectResizeObserver() {\n this.resizeObserver?.disconnect();\n this.resizeObserver = undefined;\n }\n\n /** Derive active ID from the current URL by matching item `href` values.\n * Uses longest-prefix match so `/fleet/detail` correctly activates `/fleet`. */\n private syncActiveFromUrl() {\n if (!this.currentUrl) {\n this.urlDerivedActiveId = '';\n return;\n }\n let best: PanelNavItem | undefined;\n let bestLen = 0;\n for (const item of this.getAllItems()) {\n if (item.href && this.currentUrl.startsWith(item.href) && item.href.length > bestLen) {\n best = item;\n bestLen = item.href.length;\n }\n }\n this.urlDerivedActiveId = best?.id ?? '';\n }\n\n /** Centralised toggle: always emits `dsNavToggle`; when `storageKey` is set\n * also mutates `collapsed` directly and persists to `localStorage`. */\n private applyToggle(next: boolean) {\n this.dsNavToggle.emit(next);\n if (this.storageKey) {\n this.collapsed = next;\n try { localStorage.setItem(this.storageKey, String(next)); } catch { /* unavailable */ }\n }\n }\n\n private getAllItems(): PanelNavItem[] {\n return this.parsedGroups.flatMap(g => g.items);\n }\n\n private syncRovingIndex() {\n const idx = this.getAllItems().findIndex(i => i.id === this.effectiveActiveId);\n this.rovingIndex = idx >= 0 ? idx : 0;\n }\n\n private handleItemKeyDown(e: KeyboardEvent, index: number) {\n const total = this.getAllItems().length;\n let next = index;\n switch (e.key) {\n case 'ArrowDown': e.preventDefault(); next = (index + 1) % total; break;\n case 'ArrowUp': e.preventDefault(); next = (index - 1 + total) % total; break;\n case 'Home': e.preventDefault(); next = 0; break;\n case 'End': e.preventDefault(); next = total - 1; break;\n default: return;\n }\n this.rovingIndex = next;\n const items = Array.from(\n this.el.querySelectorAll<HTMLElement>('.panel-nav__body .panel-nav__item')\n );\n items[next]?.focus();\n }\n\n private checkScroll() {\n const body = this.el.querySelector('.panel-nav__body') as HTMLElement | null;\n if (!body) return;\n const remaining = body.scrollHeight - body.scrollTop - body.clientHeight;\n this.atBottom = remaining < 2;\n }\n\n private handleBodyScroll(e: Event) {\n const body = e.target as HTMLElement;\n const remaining = body.scrollHeight - body.scrollTop - body.clientHeight;\n this.atBottom = remaining < 2;\n }\n\n private handleItemClick(id: string) {\n this.dsNavSelect.emit(id);\n }\n\n private handleToggle() {\n this.applyToggle(!this.collapsed);\n }\n\n private handleFooterAction() {\n this.dsNavFooterAction.emit();\n }\n\n private clearEdgeOverlayTimer() {\n if (this.edgeOverlayTimer !== null) {\n window.clearTimeout(this.edgeOverlayTimer);\n this.edgeOverlayTimer = null;\n }\n }\n\n private handleResizeHandleMouseEnter() {\n this.clearEdgeOverlayTimer();\n this.edgeOverlayTimer = window.setTimeout(() => {\n this.showEdgeOverlay = true;\n this.edgeOverlayTimer = null;\n }, 500);\n }\n\n private handleResizeHandleMouseLeave() {\n if (this.isDragging) return;\n this.clearEdgeOverlayTimer();\n this.showEdgeOverlay = false;\n }\n\n private handleResizeHandleMouseDown(e: MouseEvent) {\n e.preventDefault();\n e.stopPropagation();\n\n const wasCollapsed = this.collapsed || this.viewportNarrow;\n this.isDragging = true;\n this.dragStartX = e.clientX;\n this.didSnap = false;\n this.lastDeltaX = 0;\n this.clearEdgeOverlayTimer();\n this.showEdgeOverlay = false;\n\n document.body.style.cursor = 'ew-resize';\n document.body.style.userSelect = 'none';\n\n const onMove = (ev: MouseEvent) => {\n const deltaX = ev.clientX - this.dragStartX;\n this.lastDeltaX = Math.abs(deltaX);\n if (this.didSnap) return;\n if (!wasCollapsed && deltaX < -8) {\n this.applyToggle(true);\n this.didSnap = true;\n } else if (wasCollapsed && deltaX > 8) {\n this.applyToggle(false);\n this.didSnap = true;\n }\n };\n\n const onUp = () => {\n if (!this.didSnap && this.lastDeltaX < 3) {\n this.applyToggle(!wasCollapsed);\n }\n this.isDragging = false;\n this.showEdgeOverlay = false;\n document.body.style.cursor = '';\n document.body.style.userSelect = '';\n window.removeEventListener('mousemove', onMove);\n window.removeEventListener('mouseup', onUp);\n this.globalMouseMoveHandler = undefined;\n this.globalMouseUpHandler = undefined;\n };\n\n this.globalMouseMoveHandler = onMove;\n this.globalMouseUpHandler = onUp;\n window.addEventListener('mousemove', onMove);\n window.addEventListener('mouseup', onUp);\n }\n\n private renderNavItem(item: PanelNavItem, idx: number, collapsed: boolean) {\n const isActive = item.id === this.effectiveActiveId;\n\n const itemContent = [\n <span class=\"panel-nav__item-icon\">\n <ds-icon name={item.icon} size=\"md\" color=\"inherit\" flag={item.flag} />\n </span>,\n <span class=\"panel-nav__item-label\">\n <span class=\"panel-nav__item-label-text\">\n <ds-text\n as=\"span\"\n variant={isActive ? 'text-body-medium-emphasis' : 'text-body-medium'}\n color=\"inherit\"\n >\n {item.label}\n </ds-text>\n </span>\n </span>,\n item.dot && <span class=\"panel-nav__item-dot\" aria-hidden=\"true\" />,\n ];\n\n const sharedProps = {\n class: { 'panel-nav__item': true, 'panel-nav__item--active': isActive },\n 'aria-current': isActive ? ('page' as const) : undefined,\n title: collapsed ? item.label : undefined,\n tabIndex: idx === this.rovingIndex ? 0 : -1,\n onClick: () => this.handleItemClick(item.id),\n onKeyDown: (e: KeyboardEvent) => this.handleItemKeyDown(e, idx),\n onFocus: () => { this.rovingIndex = idx; },\n };\n\n return item.href\n ? <a {...sharedProps} href={item.href}>{itemContent}</a>\n : <button {...sharedProps} type=\"button\">{itemContent}</button>;\n }\n\n render() {\n const isDashboard = this.renderedVariant === 'dashboard';\n const collapsed = this.collapsed || this.viewportNarrow;\n const { userName, userInitial } = this;\n\n const navCls: Record<string, boolean> = {\n 'panel-nav': true,\n 'panel-nav--dashboard': isDashboard,\n 'panel-nav--settings': !isDashboard,\n 'panel-nav--collapsed': collapsed,\n 'panel-nav--animating': this.isAnimating,\n 'panel-nav--at-bottom': this.atBottom,\n };\n\n return (\n <Host style={{ display: 'block', position: 'relative' }}>\n <nav class={navCls} aria-label={isDashboard ? 'Main navigation' : 'Settings navigation'}>\n\n {/* ── Header: Motive logo, reveals collapse toggle on hover ── */}\n <div class=\"panel-nav__header\">\n <button\n type=\"button\"\n class=\"panel-nav__header-btn\"\n onClick={() => this.handleToggle()}\n aria-label={collapsed ? 'Expand navigation' : 'Collapse navigation'}\n aria-expanded={collapsed ? 'false' : 'true'}\n >\n {/* Motive M mark — fades out on hover to reveal collapse toggle */}\n <span class=\"panel-nav__header-logo\" aria-hidden=\"true\">\n <svg class=\"panel-nav__m-mark\" viewBox=\"0 0 20 20\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" focusable=\"false\" aria-hidden=\"true\">\n <path d=\"M11.1159 4.31537H7.67021L2.53401 13.0703H0V15.6875H4.02716L8.24319 8.49978V15.6846H11.6342L15.8289 8.47829V15.6846H18.7122V4.3125H15.2559L11.1159 11.3648V4.31537Z\" fill=\"currentColor\"/>\n </svg>\n </span>\n {/* Collapse / expand icon — revealed on hover via CSS */}\n <span class=\"panel-nav__header-toggle\" aria-hidden=\"true\">\n <ds-icon\n name={collapsed ? 'LeftExpandB' : 'LeftCollapseB'}\n size=\"md\"\n color=\"inherit\"\n />\n </span>\n </button>\n </div>\n\n {/* ── Scrollable body ── */}\n <div class=\"panel-nav__body\" onScroll={e => this.handleBodyScroll(e)}>\n {(() => {\n let flatIdx = 0;\n return this.parsedGroups.map(group => (\n <div class=\"panel-nav__group\">\n {group.label && (\n <span class=\"panel-nav__group-label\">\n <span class=\"panel-nav__item-label-text\">\n <ds-text as=\"span\" variant=\"text-caption-emphasis\" color=\"inherit\">\n {group.label}\n </ds-text>\n </span>\n </span>\n )}\n {group.items.map(item => this.renderNavItem(item, flatIdx++, collapsed))}\n </div>\n ));\n })()}\n </div>\n\n {/* ── Footer ── */}\n <div class=\"panel-nav__footer\">\n {/* Left icon button — Gear in dashboard, Dashboard in settings */}\n <button\n type=\"button\"\n class=\"panel-nav__footer-btn\"\n title={isDashboard ? 'Settings' : 'Dashboard'}\n aria-label={isDashboard ? 'Open settings' : 'Go to dashboard'}\n onClick={() => this.handleFooterAction()}\n >\n <ds-icon name={isDashboard ? 'Gear' : 'Dashboard'} size=\"md\" color=\"inherit\" />\n </button>\n\n {/* Right user — label fades like nav items; right icon cross-fades chevron ↔ circle+initial */}\n <button\n type=\"button\"\n class=\"panel-nav__item panel-nav__footer-user\"\n aria-label={collapsed ? `User: ${userName}` : `User menu for ${userName}`}\n >\n <span class=\"panel-nav__item-label panel-nav__footer-user-label\">\n <span class=\"panel-nav__item-label-text\">\n <ds-text as=\"span\" variant=\"text-body-medium-emphasis\" color=\"inherit\">\n {userName}\n </ds-text>\n </span>\n </span>\n <span class=\"panel-nav__item-icon panel-nav__footer-user-icon\" aria-hidden=\"true\">\n <span class=\"panel-nav__footer-icon-expanded\">\n <ds-icon name=\"ChevronUpDown\" size=\"md\" color=\"inherit\" />\n </span>\n <span class=\"panel-nav__footer-icon-collapsed\">\n <ds-icon name=\"Circle\" size=\"md\" color=\"inherit\" />\n <span class=\"panel-nav__user-initial\">\n <ds-text as=\"span\" variant=\"text-caption-emphasis\" color=\"inherit\">\n {userInitial}\n </ds-text>\n </span>\n </span>\n </span>\n </button>\n </div>\n\n </nav>\n\n {/* Drag-to-resize handle — always rendered, hidden only when auto-collapsed by breakpoint */}\n {!this.viewportNarrow && (\n <div\n class={{\n 'panel-nav__resize-handle': true,\n 'panel-nav__resize-handle--overlay': this.showEdgeOverlay,\n }}\n onMouseEnter={() => this.handleResizeHandleMouseEnter()}\n onMouseLeave={() => this.handleResizeHandleMouseLeave()}\n onMouseDown={(e: MouseEvent) => this.handleResizeHandleMouseDown(e)}\n aria-hidden=\"true\"\n />\n )}\n </Host>\n );\n }\n}\n"],"mappings":"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","ignoreList":[]}
|