@ds-mo/ui 0.11.0 → 0.11.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/ds-bar-nav.js +1 -1
- package/dist/components/ds-bar-nav.js.map +1 -1
- package/dist/components/ds-panel-nav.js +1 -1
- package/dist/components/ds-panel-nav.js.map +1 -1
- package/dist/components/ds-tab-group.js +1 -1
- package/dist/components/p-BvASlk3n.js +2 -0
- package/dist/components/p-BvASlk3n.js.map +1 -0
- package/dist/types/.stencil/BarNav/BarNav.d.ts +6 -0
- package/dist/types/.stencil/BarNav/bar-nav-utils.d.ts +3 -1
- package/dist/types/.stencil/PanelNav/PanelNav.d.ts +5 -0
- package/dist/types/.stencil/PanelNav/panel-nav-utils.d.ts +4 -0
- package/package.json +1 -1
- package/dist/components/p-D1cpKdRU.js +0 -2
- package/dist/components/p-D1cpKdRU.js.map +0 -1
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{p as s,H as a,c as e,h as t,a as n,t as r}from"./index.js";import{d as i}from"./p-CdSPCP4g.js";import{d as o}from"./p-CkBXHj0e.js";import{d as c}from"./p-
|
|
1
|
+
import{p as s,H as a,c as e,h as t,a as n,t as r}from"./index.js";import{d as i}from"./p-CdSPCP4g.js";import{d as o}from"./p-CkBXHj0e.js";import{d as c}from"./p-BvASlk3n.js";function h(s,a,e){if(!s||!a){return{value:"",hideTabs:false}}if(s!==a&&!s.startsWith(`${a}/`)){return{value:"",hideTabs:false}}const t=s.slice(a.length);const n=t.startsWith("/")?t.slice(1).split("/")[0]:"";if(!n){return{value:e[0]?.id??"",hideTabs:false}}const r=e.find((s=>s.id===n));if(r){return{value:r.id,hideTabs:false}}return{value:"",hideTabs:true}}function d(s,a,e,t,n,r){const i=e?l(e,[]):a??[];const o=r?l(r,[]):n??[];return s.length===0&&i.length>0||t.length===0&&o.length>0}function l(s,a){if(Array.isArray(s))return s;if(typeof s==="string"&&s!==""){try{const e=JSON.parse(s);return Array.isArray(e)?e:a}catch{return a}}return a}const b=()=>`.sc-ds-bar-nav-h{display:block}.bar-nav.sc-ds-bar-nav{display:flex;align-items:center;height:var(--dimension-size-600);padding:var(--dimension-space-100);gap:var(--dimension-space-100);box-sizing:border-box;border-bottom:var(--dimension-stroke-width-012) solid var(--color-border-tertiary)}.bg-primary.sc-ds-bar-nav{background-color:var(--color-background-primary)}.bg-secondary.sc-ds-bar-nav{background-color:var(--color-background-secondary)}.bg-transparent.sc-ds-bar-nav{background-color:transparent}.bg-translucent.sc-ds-bar-nav{background-color:var(--color-background-translucent);backdrop-filter:blur(var(--effect-blur-md));-webkit-backdrop-filter:blur(var(--effect-blur-md))}.bar-nav__left.sc-ds-bar-nav{flex:1;min-width:0;display:flex;align-items:center}.bar-nav__left.sc-ds-bar-nav ds-tab-group.sc-ds-bar-nav{flex:1;min-width:0}.bar-nav__heading.sc-ds-bar-nav{display:inline-flex;align-items:center;height:var(--dimension-size-400);padding-inline:var(--dimension-space-100);color:var(--color-foreground-primary);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.bar-nav__actions.sc-ds-bar-nav{display:flex;align-items:center;gap:var(--dimension-space-050);flex-shrink:0}`;const u=s(class s extends a{constructor(s){super();if(s!==false){this.__registerHost()}this.dsTabChange=e(this,"dsTabChange");this.dsActionChange=e(this,"dsActionChange");this.tabs=[];this.tabsJson="";this.value="";this.actions=[];this.actionsJson="";this.background="secondary";this.basePath="";this.currentUrl="";this.resolvedTabs=[];this.resolvedActions=[];this.urlDerivedValue="";this.hideTabsForDetailRoute=false}get effectiveValue(){if(this.currentUrl&&this.basePath){return this.urlDerivedValue}return this.value}onPropsChange(){this.resolvedTabs=this.tabsJson?l(this.tabsJson,[]):this.tabs??[];this.resolvedActions=this.actionsJson?l(this.actionsJson,[]):this.actions??[];this.syncValueFromUrl()}onUrlChange(){this.syncValueFromUrl()}componentWillLoad(){this.onPropsChange()}componentDidLoad(){this.syncHostPropsIfNeeded();this.scheduleDeferredHostPropSync()}syncHostPropsIfNeeded(){if(d(this.resolvedTabs,this.tabs,this.tabsJson,this.resolvedActions,this.actions,this.actionsJson)){this.onPropsChange()}else if(this.currentUrl&&this.basePath){this.syncValueFromUrl()}}scheduleDeferredHostPropSync(){let a=s.HOST_PROP_SYNC_BUDGET;const e=()=>{this.syncHostPropsIfNeeded();if(--a>0){requestAnimationFrame(e)}};queueMicrotask(e)}syncValueFromUrl(){if(!this.currentUrl||!this.basePath){this.urlDerivedValue="";this.hideTabsForDetailRoute=false;return}const{value:s,hideTabs:a}=h(this.currentUrl,this.basePath,this.resolvedTabs);this.urlDerivedValue=s;this.hideTabsForDetailRoute=a}handleTabChange(s){const a=s.detail;this.value=a;this.dsTabChange.emit(a)}handleActionChange(s,a){const e=a.detail;this.dsActionChange.emit({id:s,selected:e})}render(){const s=this.resolvedTabs.length>0&&!this.hideTabsForDetailRoute;const a={};const e=a[this.background];return t(n,{key:"d20e8a492cb72e5299ea8158a9cd1b40c656e8e1"},t("header",{key:"b4a2bda3ec1148a2210dbf72a2e01fdeaaf5fdf2",class:{"bar-nav":true,[`bg-${this.background}`]:true}},t("div",{key:"865b41835bfddc8f1c8f22d9bcb2ecc5cd3b7b10",class:"bar-nav__left"},s?t("ds-tab-group",{tabs:this.resolvedTabs,value:this.effectiveValue,onDsChange:s=>this.handleTabChange(s)}):this.heading&&t("span",{class:"bar-nav__heading text-body-medium-emphasis"},this.heading)),this.resolvedActions.length>0&&t("div",{key:"e5e8e1223f37a3af67e66290333b9f84fb994027",class:"bar-nav__actions"},this.resolvedActions.map((s=>t("ds-bar-nav-action",{key:s.id,icon:s.icon,selected:s.selected??false,dot:s.dot??false,inactive:s.inactive,"aria-label":s.ariaLabel??s.id,background:e,onDsChange:a=>this.handleActionChange(s.id,a)}))))))}static get watchers(){return{tabs:[{onPropsChange:0}],tabsJson:[{onPropsChange:0}],actions:[{onPropsChange:0}],actionsJson:[{onPropsChange:0}],currentUrl:[{onUrlChange:0}],basePath:[{onUrlChange:0}]}}static get style(){return b()}},[2,"ds-bar-nav",{tabs:[16],tabsJson:[1,"tabs-json"],value:[1025],actions:[16],actionsJson:[1,"actions-json"],heading:[1],background:[1],basePath:[1,"base-path"],currentUrl:[1,"current-url"],resolvedTabs:[32],resolvedActions:[32],urlDerivedValue:[32],hideTabsForDetailRoute:[32]},undefined,{tabs:[{onPropsChange:0}],tabsJson:[{onPropsChange:0}],actions:[{onPropsChange:0}],actionsJson:[{onPropsChange:0}],currentUrl:[{onUrlChange:0}],basePath:[{onUrlChange:0}]}]);u.HOST_PROP_SYNC_BUDGET=8;function f(){if(typeof customElements==="undefined"){return}const s=["ds-bar-nav","ds-bar-nav-action","ds-icon","ds-tab-group"];s.forEach((s=>{switch(s){case"ds-bar-nav":if(!customElements.get(r(s))){customElements.define(r(s),u)}break;case"ds-bar-nav-action":if(!customElements.get(r(s))){i()}break;case"ds-icon":if(!customElements.get(r(s))){o()}break;case"ds-tab-group":if(!customElements.get(r(s))){c()}break}}))}f();const g=u;const v=f;export{g as DsBarNav,v as defineCustomElement};
|
|
2
2
|
//# sourceMappingURL=ds-bar-nav.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["deriveBarNavValueFromUrl","path","basePath","tabs","value","hideTabs","startsWith","remainder","slice","length","seg","split","id","tab","find","t","parseJsonArrayProp","fallback","Array","isArray","parsed","JSON","parse","barNavCss","BarNav","__stencil_proxyCustomElement","HTMLElement","constructor","registerHost","this","tabsJson","actions","actionsJson","background","currentUrl","resolvedTabs","resolvedActions","urlDerivedValue","hideTabsForDetailRoute","effectiveValue","onPropsChange","syncValueFromUrl","onUrlChange","componentWillLoad","handleTabChange","e","detail","dsTabChange","emit","handleActionChange","actionId","selected","dsActionChange","render","hasTabs","bgToActionBg","actionBg","h","Host","key","class","onDsChange","heading","map","action","icon","dot","inactive","ariaLabel"],"sources":["src/wc/components/BarNav/bar-nav-utils.ts","src/wc/components/BarNav/BarNav.css?tag=ds-bar-nav&encapsulation=scoped","src/wc/components/BarNav/BarNav.tsx"],"sourcesContent":["import type { BarNavTab } from './BarNav';\n\nexport interface BarNavUrlState {\n /** Tab id derived from the URL, or empty when none applies. */\n value: string;\n /** When true, tabs should be hidden (e.g. non-tab child routes like detail pages). */\n hideTabs: boolean;\n}\n\n/** Derive BarNav tab selection from `currentUrl` relative to `basePath`.\n * Mirrors motive-webapp-lab `resolveBarNavState` tab-selection semantics. */\nexport function deriveBarNavValueFromUrl(\n path: string,\n basePath: string,\n tabs: BarNavTab[],\n): BarNavUrlState {\n if (!path || !basePath) {\n return { value: '', hideTabs: false };\n }\n\n if (path !== basePath && !path.startsWith(`${basePath}/`)) {\n return { value: '', hideTabs: false };\n }\n\n const remainder = path.slice(basePath.length);\n const seg = remainder.startsWith('/') ? remainder.slice(1).split('/')[0] : '';\n\n if (!seg) {\n return { value: tabs[0]?.id ?? '', hideTabs: false };\n }\n\n const tab = tabs.find(t => t.id === seg);\n if (tab) {\n return { value: tab.id, hideTabs: false };\n }\n\n return { value: '', hideTabs: true };\n}\n\n/** Parse tabs/actions from either a JSON attribute string or a JS property array. */\nexport function parseJsonArrayProp<T>(value: string | T[] | undefined, fallback: T[]): T[] {\n if (Array.isArray(value)) return value;\n if (typeof value === 'string' && value !== '') {\n try {\n const parsed = JSON.parse(value);\n return Array.isArray(parsed) ? parsed : fallback;\n } catch {\n return fallback;\n }\n }\n return fallback;\n}\n",":host {\n display: block;\n}\n\n.bar-nav {\n display: flex;\n align-items: center;\n height: var(--dimension-size-600);\n padding: var(--dimension-space-100);\n gap: var(--dimension-space-100);\n box-sizing: border-box;\n border-bottom: var(--dimension-stroke-width-012) solid var(--color-border-tertiary);\n}\n\n/* Background variants — mirrors ds-header pattern */\n.bg-primary { background-color: var(--color-background-primary); }\n.bg-secondary { background-color: var(--color-background-secondary); }\n.bg-transparent { background-color: transparent; }\n.bg-translucent {\n background-color: var(--color-background-translucent);\n backdrop-filter: blur(var(--effect-blur-md));\n -webkit-backdrop-filter: blur(var(--effect-blur-md));\n}\n\n/* Left zone — tabs fill available space */\n.bar-nav__left {\n flex: 1;\n min-width: 0;\n display: flex;\n align-items: center;\n}\n\n.bar-nav__left ds-tab-group {\n flex: 1;\n min-width: 0;\n}\n\n/* Fallback heading when no tabs — matches a single (selected) tab so the area\n name shares the tab label's type token, color, height and horizontal inset.\n Inset = tab button padding (space-075, 6px) + label inset (space-025, 2px). */\n.bar-nav__heading {\n display: inline-flex;\n align-items: center;\n height: var(--dimension-size-400);\n padding-inline: var(--dimension-space-100);\n color: var(--color-foreground-primary);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n/* Right zone — action buttons, hugging right */\n.bar-nav__actions {\n display: flex;\n align-items: center;\n gap: var(--dimension-space-050);\n flex-shrink: 0;\n}\n","import { Component, Prop, Event, EventEmitter, Watch, State, h, Host } from '@stencil/core';\nimport type { BarNavActionBackground } from '../BarNavAction/BarNavAction';\nimport type { TabItem } from '../TabGroup/TabGroup';\nimport {\n deriveBarNavValueFromUrl,\n parseJsonArrayProp,\n} from './bar-nav-utils';\n\nexport type BarNavBackground = 'primary' | 'secondary' | 'transparent' | 'translucent';\n\nexport interface BarNavTab {\n id: string;\n label: string;\n disabled?: boolean;\n /** Notification dot on this tab. */\n dot?: boolean;\n}\n\nexport interface BarNavActionItem {\n id: string;\n /** Icon name for <ds-icon>. */\n icon: string;\n /** Whether this action button is currently pressed/active. */\n selected?: boolean;\n /** Show a notification dot. */\n dot?: boolean;\n inactive?: boolean;\n ariaLabel?: string;\n}\n\n@Component({\n tag: 'ds-bar-nav',\n styleUrl: 'BarNav.css',\n scoped: true,\n})\nexport class BarNav {\n /**\n * Tab items for the left section.\n * Set via JS property: `el.tabs = [...]`. Replace the array reference to update.\n */\n @Prop() tabs: BarNavTab[] = [];\n\n /** JSON fallback for `tabs` — useful when framework bindings don't propagate arrays. */\n @Prop({ attribute: 'tabs-json' }) tabsJson: string = '';\n\n /** ID of the currently active tab. Overridden when `currentUrl` + `basePath` are set. */\n @Prop({ mutable: true }) value: string = '';\n\n /**\n * Action items rendered in the right section.\n * Set via JS property: `el.actions = [...]`. Replace the array reference to update.\n */\n @Prop() actions: BarNavActionItem[] = [];\n\n /** JSON fallback for `actions` — useful when framework bindings don't propagate arrays. */\n @Prop({ attribute: 'actions-json' }) actionsJson: string = '';\n\n /**\n * Fallback heading shown when no tabs are provided.\n * When tabs are present the heading is hidden.\n */\n @Prop() heading: string | undefined;\n\n /** Surface background variant. */\n @Prop() background: BarNavBackground = 'secondary';\n\n /** Section base path (e.g. `/dashboard/safety`). Used with `currentUrl` to derive `value`. */\n @Prop() basePath: string = '';\n\n /** Current route URL. When set with `basePath`, the active tab is derived automatically. */\n @Prop() currentUrl: string = '';\n\n /** Emitted when the active tab changes. Detail = tab id. */\n @Event() dsTabChange!: EventEmitter<string>;\n\n /** Emitted when an action button is toggled. Detail = { id, selected }. */\n @Event() dsActionChange!: EventEmitter<{ id: string; selected: boolean }>;\n\n @State() private resolvedTabs: BarNavTab[] = [];\n @State() private resolvedActions: BarNavActionItem[] = [];\n @State() private urlDerivedValue: string = '';\n @State() private hideTabsForDetailRoute = false;\n\n private get effectiveValue(): string {\n if (this.currentUrl && this.basePath) {\n return this.urlDerivedValue;\n }\n return this.value;\n }\n\n @Watch('tabs')\n @Watch('tabsJson')\n @Watch('actions')\n @Watch('actionsJson')\n onPropsChange() {\n this.resolvedTabs = this.tabsJson\n ? parseJsonArrayProp(this.tabsJson, [])\n : (this.tabs ?? []);\n this.resolvedActions = this.actionsJson\n ? parseJsonArrayProp(this.actionsJson, [])\n : (this.actions ?? []);\n this.syncValueFromUrl();\n }\n\n @Watch('currentUrl')\n @Watch('basePath')\n onUrlChange() {\n this.syncValueFromUrl();\n }\n\n componentWillLoad() {\n this.onPropsChange();\n }\n\n private syncValueFromUrl() {\n if (!this.currentUrl || !this.basePath) {\n this.urlDerivedValue = '';\n this.hideTabsForDetailRoute = false;\n return;\n }\n\n const { value, hideTabs } = deriveBarNavValueFromUrl(\n this.currentUrl,\n this.basePath,\n this.resolvedTabs,\n );\n this.urlDerivedValue = value;\n this.hideTabsForDetailRoute = hideTabs;\n }\n\n private handleTabChange(e: Event) {\n const id = (e as CustomEvent<string>).detail;\n this.value = id;\n this.dsTabChange.emit(id);\n }\n\n private handleActionChange(actionId: string, e: Event) {\n const selected = (e as CustomEvent<boolean>).detail;\n this.dsActionChange.emit({ id: actionId, selected });\n }\n\n render() {\n const hasTabs = this.resolvedTabs.length > 0 && !this.hideTabsForDetailRoute;\n\n // Map BarNavBackground → BarNavActionBackground for context pass-through.\n // primary/secondary/transparent/translucent are all light — no override needed.\n const bgToActionBg: Partial<Record<BarNavBackground, BarNavActionBackground>> = {};\n const actionBg: BarNavActionBackground | undefined = bgToActionBg[this.background];\n\n return (\n <Host>\n <header class={{ 'bar-nav': true, [`bg-${this.background}`]: true }}>\n\n <div class=\"bar-nav__left\">\n {hasTabs\n ? (\n <ds-tab-group\n tabs={this.resolvedTabs as TabItem[]}\n value={this.effectiveValue}\n onDsChange={(e: Event) => this.handleTabChange(e)}\n />\n )\n : this.heading && (\n <span class=\"bar-nav__heading text-body-medium-emphasis\">{this.heading}</span>\n )\n }\n </div>\n\n {this.resolvedActions.length > 0 && (\n <div class=\"bar-nav__actions\">\n {this.resolvedActions.map(action => (\n <ds-bar-nav-action\n key={action.id}\n icon={action.icon}\n selected={action.selected ?? false}\n dot={action.dot ?? false}\n inactive={action.inactive}\n aria-label={action.ariaLabel ?? action.id}\n background={actionBg}\n onDsChange={(e: Event) => this.handleActionChange(action.id, e)}\n />\n ))}\n </div>\n )}\n\n </header>\n </Host>\n );\n }\n}\n"],"mappings":"uLAWgBA,EACdC,EACAC,EACAC,GAEA,IAAKF,IAASC,EAAU,CACtB,MAAO,CAAEE,MAAO,GAAIC,SAAU,M,CAGhC,GAAIJ,IAASC,IAAaD,EAAKK,WAAW,GAAGJ,MAAc,CACzD,MAAO,CAAEE,MAAO,GAAIC,SAAU,M,CAGhC,MAAME,EAAYN,EAAKO,MAAMN,EAASO,QACtC,MAAMC,EAAMH,EAAUD,WAAW,KAAOC,EAAUC,MAAM,GAAGG,MAAM,KAAK,GAAK,GAE3E,IAAKD,EAAK,CACR,MAAO,CAAEN,MAAOD,EAAK,IAAIS,IAAM,GAAIP,SAAU,M,CAG/C,MAAMQ,EAAMV,EAAKW,MAAKC,GAAKA,EAAEH,KAAOF,IACpC,GAAIG,EAAK,CACP,MAAO,CAAET,MAAOS,EAAID,GAAIP,SAAU,M,CAGpC,MAAO,CAAED,MAAO,GAAIC,SAAU,KAChC,CAGM,SAAUW,EAAsBZ,EAAiCa,GACrE,GAAIC,MAAMC,QAAQf,GAAQ,OAAOA,EACjC,UAAWA,IAAU,UAAYA,IAAU,GAAI,CAC7C,IACE,MAAMgB,EAASC,KAAKC,MAAMlB,GAC1B,OAAOc,MAAMC,QAAQC,GAAUA,EAASH,C,CACxC,MACA,OAAOA,C,EAGX,OAAOA,CACT,CCnDA,MAAMM,EAAY,IAAM,uqC,MCmCXC,EAAMC,EAAA,MAAAD,UAAAE,EALnB,WAAAC,CAAAC,G,gIAUUC,KAAA1B,KAAoB,GAGM0B,KAAAC,SAAmB,GAG5BD,KAAAzB,MAAgB,GAMjCyB,KAAAE,QAA8B,GAGDF,KAAAG,YAAsB,GASnDH,KAAAI,WAA+B,YAG/BJ,KAAA3B,SAAmB,GAGnB2B,KAAAK,WAAqB,GAQZL,KAAAM,aAA4B,GAC5BN,KAAAO,gBAAsC,GACtCP,KAAAQ,gBAA0B,GAC1BR,KAAAS,uBAAyB,KA4G3C,CA1GC,kBAAYC,GACV,GAAIV,KAAKK,YAAcL,KAAK3B,SAAU,CACpC,OAAO2B,KAAKQ,e,CAEd,OAAOR,KAAKzB,K,CAOd,aAAAoC,GACEX,KAAKM,aAAeN,KAAKC,SACrBd,EAAmBa,KAAKC,SAAU,IACjCD,KAAK1B,MAAQ,GAClB0B,KAAKO,gBAAkBP,KAAKG,YACxBhB,EAAmBa,KAAKG,YAAa,IACpCH,KAAKE,SAAW,GACrBF,KAAKY,kB,CAKP,WAAAC,GACEb,KAAKY,kB,CAGP,iBAAAE,GACEd,KAAKW,e,CAGC,gBAAAC,GACN,IAAKZ,KAAKK,aAAeL,KAAK3B,SAAU,CACtC2B,KAAKQ,gBAAkB,GACvBR,KAAKS,uBAAyB,MAC9B,M,CAGF,MAAMlC,MAAEA,EAAKC,SAAEA,GAAaL,EAC1B6B,KAAKK,WACLL,KAAK3B,SACL2B,KAAKM,cAEPN,KAAKQ,gBAAkBjC,EACvByB,KAAKS,uBAAyBjC,C,CAGxB,eAAAuC,CAAgBC,GACtB,MAAMjC,EAAMiC,EAA0BC,OACtCjB,KAAKzB,MAAQQ,EACbiB,KAAKkB,YAAYC,KAAKpC,E,CAGhB,kBAAAqC,CAAmBC,EAAkBL,GAC3C,MAAMM,EAAYN,EAA2BC,OAC7CjB,KAAKuB,eAAeJ,KAAK,CAAEpC,GAAIsC,EAAUC,Y,CAG3C,MAAAE,GACE,MAAMC,EAAUzB,KAAKM,aAAa1B,OAAS,IAAMoB,KAAKS,uBAItD,MAAMiB,EAA0E,GAChF,MAAMC,EAA+CD,EAAa1B,KAAKI,YAEvE,OACEwB,EAACC,EAAI,CAAAC,IAAA,4CACHF,EAAA,UAAAE,IAAA,2CAAQC,MAAO,CAAE,UAAW,KAAM,CAAC,MAAM/B,KAAKI,cAAe,OAE3DwB,EAAA,OAAAE,IAAA,2CAAKC,MAAM,iBACRN,EAEGG,EAAA,gBACEtD,KAAM0B,KAAKM,aACX/B,MAAOyB,KAAKU,eACZsB,WAAahB,GAAahB,KAAKe,gBAAgBC,KAGjDhB,KAAKiC,SACLL,EAAA,QAAMG,MAAM,8CAA8C/B,KAAKiC,UAKpEjC,KAAKO,gBAAgB3B,OAAS,GAC7BgD,EAAA,OAAAE,IAAA,2CAAKC,MAAM,oBACR/B,KAAKO,gBAAgB2B,KAAIC,GACxBP,EAAA,qBACEE,IAAKK,EAAOpD,GACZqD,KAAMD,EAAOC,KACbd,SAAUa,EAAOb,UAAY,MAC7Be,IAAKF,EAAOE,KAAO,MACnBC,SAAUH,EAAOG,SAAQ,aACbH,EAAOI,WAAaJ,EAAOpD,GACvCqB,WAAYuB,EACZK,WAAahB,GAAahB,KAAKoB,mBAAmBe,EAAOpD,GAAIiC,S","ignoreList":[]}
|
|
1
|
+
{"version":3,"names":["deriveBarNavValueFromUrl","path","basePath","tabs","value","hideTabs","startsWith","remainder","slice","length","seg","split","id","tab","find","t","shouldResyncBarNavProps","resolvedTabs","tabsJson","resolvedActions","actions","actionsJson","tabsIncoming","parseJsonArrayProp","actionsIncoming","fallback","Array","isArray","parsed","JSON","parse","barNavCss","BarNav","__stencil_proxyCustomElement","HTMLElement","constructor","registerHost","this","background","currentUrl","urlDerivedValue","hideTabsForDetailRoute","effectiveValue","onPropsChange","syncValueFromUrl","onUrlChange","componentWillLoad","componentDidLoad","syncHostPropsIfNeeded","scheduleDeferredHostPropSync","remaining","HOST_PROP_SYNC_BUDGET","tick","requestAnimationFrame","queueMicrotask","handleTabChange","e","detail","dsTabChange","emit","handleActionChange","actionId","selected","dsActionChange","render","hasTabs","bgToActionBg","actionBg","h","Host","key","class","onDsChange","heading","map","action","icon","dot","inactive","ariaLabel"],"sources":["src/wc/components/BarNav/bar-nav-utils.ts","src/wc/components/BarNav/BarNav.css?tag=ds-bar-nav&encapsulation=scoped","src/wc/components/BarNav/BarNav.tsx"],"sourcesContent":["import type { BarNavActionItem, BarNavTab } from './BarNav';\n\nexport interface BarNavUrlState {\n /** Tab id derived from the URL, or empty when none applies. */\n value: string;\n /** When true, tabs should be hidden (e.g. non-tab child routes like detail pages). */\n hideTabs: boolean;\n}\n\n/** Derive BarNav tab selection from `currentUrl` relative to `basePath`.\n * Mirrors motive-webapp-lab `resolveBarNavState` tab-selection semantics. */\nexport function deriveBarNavValueFromUrl(\n path: string,\n basePath: string,\n tabs: BarNavTab[],\n): BarNavUrlState {\n if (!path || !basePath) {\n return { value: '', hideTabs: false };\n }\n\n if (path !== basePath && !path.startsWith(`${basePath}/`)) {\n return { value: '', hideTabs: false };\n }\n\n const remainder = path.slice(basePath.length);\n const seg = remainder.startsWith('/') ? remainder.slice(1).split('/')[0] : '';\n\n if (!seg) {\n return { value: tabs[0]?.id ?? '', hideTabs: false };\n }\n\n const tab = tabs.find(t => t.id === seg);\n if (tab) {\n return { value: tab.id, hideTabs: false };\n }\n\n return { value: '', hideTabs: true };\n}\n\n/** True when host array/json props have data but resolved internal state is still empty. */\nexport function shouldResyncBarNavProps(\n resolvedTabs: BarNavTab[],\n tabs: BarNavTab[],\n tabsJson: string,\n resolvedActions: BarNavActionItem[],\n actions: BarNavActionItem[],\n actionsJson: string,\n): boolean {\n const tabsIncoming = tabsJson\n ? parseJsonArrayProp(tabsJson, [])\n : (tabs ?? []);\n const actionsIncoming = actionsJson\n ? parseJsonArrayProp(actionsJson, [])\n : (actions ?? []);\n\n return (\n (resolvedTabs.length === 0 && tabsIncoming.length > 0) ||\n (resolvedActions.length === 0 && actionsIncoming.length > 0)\n );\n}\n\n/** Parse tabs/actions from either a JSON attribute string or a JS property array. */\nexport function parseJsonArrayProp<T>(value: string | T[] | undefined, fallback: T[]): T[] {\n if (Array.isArray(value)) return value;\n if (typeof value === 'string' && value !== '') {\n try {\n const parsed = JSON.parse(value);\n return Array.isArray(parsed) ? parsed : fallback;\n } catch {\n return fallback;\n }\n }\n return fallback;\n}\n",":host {\n display: block;\n}\n\n.bar-nav {\n display: flex;\n align-items: center;\n height: var(--dimension-size-600);\n padding: var(--dimension-space-100);\n gap: var(--dimension-space-100);\n box-sizing: border-box;\n border-bottom: var(--dimension-stroke-width-012) solid var(--color-border-tertiary);\n}\n\n/* Background variants — mirrors ds-header pattern */\n.bg-primary { background-color: var(--color-background-primary); }\n.bg-secondary { background-color: var(--color-background-secondary); }\n.bg-transparent { background-color: transparent; }\n.bg-translucent {\n background-color: var(--color-background-translucent);\n backdrop-filter: blur(var(--effect-blur-md));\n -webkit-backdrop-filter: blur(var(--effect-blur-md));\n}\n\n/* Left zone — tabs fill available space */\n.bar-nav__left {\n flex: 1;\n min-width: 0;\n display: flex;\n align-items: center;\n}\n\n.bar-nav__left ds-tab-group {\n flex: 1;\n min-width: 0;\n}\n\n/* Fallback heading when no tabs — matches a single (selected) tab so the area\n name shares the tab label's type token, color, height and horizontal inset.\n Inset = tab button padding (space-075, 6px) + label inset (space-025, 2px). */\n.bar-nav__heading {\n display: inline-flex;\n align-items: center;\n height: var(--dimension-size-400);\n padding-inline: var(--dimension-space-100);\n color: var(--color-foreground-primary);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n/* Right zone — action buttons, hugging right */\n.bar-nav__actions {\n display: flex;\n align-items: center;\n gap: var(--dimension-space-050);\n flex-shrink: 0;\n}\n","import { Component, Prop, Event, EventEmitter, Watch, State, h, Host } from '@stencil/core';\nimport type { BarNavActionBackground } from '../BarNavAction/BarNavAction';\nimport type { TabItem } from '../TabGroup/TabGroup';\nimport {\n deriveBarNavValueFromUrl,\n parseJsonArrayProp,\n shouldResyncBarNavProps,\n} from './bar-nav-utils';\n\nexport type BarNavBackground = 'primary' | 'secondary' | 'transparent' | 'translucent';\n\nexport interface BarNavTab {\n id: string;\n label: string;\n disabled?: boolean;\n /** Notification dot on this tab. */\n dot?: boolean;\n}\n\nexport interface BarNavActionItem {\n id: string;\n /** Icon name for <ds-icon>. */\n icon: string;\n /** Whether this action button is currently pressed/active. */\n selected?: boolean;\n /** Show a notification dot. */\n dot?: boolean;\n inactive?: boolean;\n ariaLabel?: string;\n}\n\n@Component({\n tag: 'ds-bar-nav',\n styleUrl: 'BarNav.css',\n scoped: true,\n})\nexport class BarNav {\n /**\n * Tab items for the left section.\n * Set via JS property: `el.tabs = [...]`. Replace the array reference to update.\n */\n @Prop() tabs: BarNavTab[] = [];\n\n /** JSON fallback for `tabs` — useful when framework bindings don't propagate arrays. */\n @Prop({ attribute: 'tabs-json' }) tabsJson: string = '';\n\n /** ID of the currently active tab. Overridden when `currentUrl` + `basePath` are set. */\n @Prop({ mutable: true }) value: string = '';\n\n /**\n * Action items rendered in the right section.\n * Set via JS property: `el.actions = [...]`. Replace the array reference to update.\n */\n @Prop() actions: BarNavActionItem[] = [];\n\n /** JSON fallback for `actions` — useful when framework bindings don't propagate arrays. */\n @Prop({ attribute: 'actions-json' }) actionsJson: string = '';\n\n /**\n * Fallback heading shown when no tabs are provided.\n * When tabs are present the heading is hidden.\n */\n @Prop() heading: string | undefined;\n\n /** Surface background variant. */\n @Prop() background: BarNavBackground = 'secondary';\n\n /** Section base path (e.g. `/dashboard/safety`). Used with `currentUrl` to derive `value`. */\n @Prop() basePath: string = '';\n\n /** Current route URL. When set with `basePath`, the active tab is derived automatically. */\n @Prop() currentUrl: string = '';\n\n /** Emitted when the active tab changes. Detail = tab id. */\n @Event() dsTabChange!: EventEmitter<string>;\n\n /** Emitted when an action button is toggled. Detail = { id, selected }. */\n @Event() dsActionChange!: EventEmitter<{ id: string; selected: boolean }>;\n\n @State() private resolvedTabs: BarNavTab[] = [];\n @State() private resolvedActions: BarNavActionItem[] = [];\n @State() private urlDerivedValue: string = '';\n @State() private hideTabsForDetailRoute = false;\n\n private static readonly HOST_PROP_SYNC_BUDGET = 8;\n\n private get effectiveValue(): string {\n if (this.currentUrl && this.basePath) {\n return this.urlDerivedValue;\n }\n return this.value;\n }\n\n @Watch('tabs')\n @Watch('tabsJson')\n @Watch('actions')\n @Watch('actionsJson')\n onPropsChange() {\n this.resolvedTabs = this.tabsJson\n ? parseJsonArrayProp(this.tabsJson, [])\n : (this.tabs ?? []);\n this.resolvedActions = this.actionsJson\n ? parseJsonArrayProp(this.actionsJson, [])\n : (this.actions ?? []);\n this.syncValueFromUrl();\n }\n\n @Watch('currentUrl')\n @Watch('basePath')\n onUrlChange() {\n this.syncValueFromUrl();\n }\n\n componentWillLoad() {\n this.onPropsChange();\n }\n\n componentDidLoad() {\n this.syncHostPropsIfNeeded();\n this.scheduleDeferredHostPropSync();\n }\n\n /** Re-resolve props assigned by the host after componentWillLoad (Angular ngAfterViewInit). */\n private syncHostPropsIfNeeded() {\n if (\n shouldResyncBarNavProps(\n this.resolvedTabs,\n this.tabs,\n this.tabsJson,\n this.resolvedActions,\n this.actions,\n this.actionsJson,\n )\n ) {\n this.onPropsChange();\n } else if (this.currentUrl && this.basePath) {\n this.syncValueFromUrl();\n }\n }\n\n /** Poll across animation frames — host props may land without triggering @Watch. */\n private scheduleDeferredHostPropSync() {\n let remaining = BarNav.HOST_PROP_SYNC_BUDGET;\n const tick = () => {\n this.syncHostPropsIfNeeded();\n if (--remaining > 0) {\n requestAnimationFrame(tick);\n }\n };\n queueMicrotask(tick);\n }\n\n private syncValueFromUrl() {\n if (!this.currentUrl || !this.basePath) {\n this.urlDerivedValue = '';\n this.hideTabsForDetailRoute = false;\n return;\n }\n\n const { value, hideTabs } = deriveBarNavValueFromUrl(\n this.currentUrl,\n this.basePath,\n this.resolvedTabs,\n );\n this.urlDerivedValue = value;\n this.hideTabsForDetailRoute = hideTabs;\n }\n\n private handleTabChange(e: Event) {\n const id = (e as CustomEvent<string>).detail;\n this.value = id;\n this.dsTabChange.emit(id);\n }\n\n private handleActionChange(actionId: string, e: Event) {\n const selected = (e as CustomEvent<boolean>).detail;\n this.dsActionChange.emit({ id: actionId, selected });\n }\n\n render() {\n const hasTabs = this.resolvedTabs.length > 0 && !this.hideTabsForDetailRoute;\n\n // Map BarNavBackground → BarNavActionBackground for context pass-through.\n // primary/secondary/transparent/translucent are all light — no override needed.\n const bgToActionBg: Partial<Record<BarNavBackground, BarNavActionBackground>> = {};\n const actionBg: BarNavActionBackground | undefined = bgToActionBg[this.background];\n\n return (\n <Host>\n <header class={{ 'bar-nav': true, [`bg-${this.background}`]: true }}>\n\n <div class=\"bar-nav__left\">\n {hasTabs\n ? (\n <ds-tab-group\n tabs={this.resolvedTabs as TabItem[]}\n value={this.effectiveValue}\n onDsChange={(e: Event) => this.handleTabChange(e)}\n />\n )\n : this.heading && (\n <span class=\"bar-nav__heading text-body-medium-emphasis\">{this.heading}</span>\n )\n }\n </div>\n\n {this.resolvedActions.length > 0 && (\n <div class=\"bar-nav__actions\">\n {this.resolvedActions.map(action => (\n <ds-bar-nav-action\n key={action.id}\n icon={action.icon}\n selected={action.selected ?? false}\n dot={action.dot ?? false}\n inactive={action.inactive}\n aria-label={action.ariaLabel ?? action.id}\n background={actionBg}\n onDsChange={(e: Event) => this.handleActionChange(action.id, e)}\n />\n ))}\n </div>\n )}\n\n </header>\n </Host>\n );\n }\n}\n"],"mappings":"uLAWgBA,EACdC,EACAC,EACAC,GAEA,IAAKF,IAASC,EAAU,CACtB,MAAO,CAAEE,MAAO,GAAIC,SAAU,M,CAGhC,GAAIJ,IAASC,IAAaD,EAAKK,WAAW,GAAGJ,MAAc,CACzD,MAAO,CAAEE,MAAO,GAAIC,SAAU,M,CAGhC,MAAME,EAAYN,EAAKO,MAAMN,EAASO,QACtC,MAAMC,EAAMH,EAAUD,WAAW,KAAOC,EAAUC,MAAM,GAAGG,MAAM,KAAK,GAAK,GAE3E,IAAKD,EAAK,CACR,MAAO,CAAEN,MAAOD,EAAK,IAAIS,IAAM,GAAIP,SAAU,M,CAG/C,MAAMQ,EAAMV,EAAKW,MAAKC,GAAKA,EAAEH,KAAOF,IACpC,GAAIG,EAAK,CACP,MAAO,CAAET,MAAOS,EAAID,GAAIP,SAAU,M,CAGpC,MAAO,CAAED,MAAO,GAAIC,SAAU,KAChC,CAGM,SAAUW,EACdC,EACAd,EACAe,EACAC,EACAC,EACAC,GAEA,MAAMC,EAAeJ,EACjBK,EAAmBL,EAAU,IAC5Bf,GAAQ,GACb,MAAMqB,EAAkBH,EACpBE,EAAmBF,EAAa,IAC/BD,GAAW,GAEhB,OACGH,EAAaR,SAAW,GAAKa,EAAab,OAAS,GACnDU,EAAgBV,SAAW,GAAKe,EAAgBf,OAAS,CAE9D,CAGM,SAAUc,EAAsBnB,EAAiCqB,GACrE,GAAIC,MAAMC,QAAQvB,GAAQ,OAAOA,EACjC,UAAWA,IAAU,UAAYA,IAAU,GAAI,CAC7C,IACE,MAAMwB,EAASC,KAAKC,MAAM1B,GAC1B,OAAOsB,MAAMC,QAAQC,GAAUA,EAASH,C,CACxC,MACA,OAAOA,C,EAGX,OAAOA,CACT,CCzEA,MAAMM,EAAY,IAAM,uqC,MCoCXC,EAAMC,EAAA,MAAAD,UAAAE,EALnB,WAAAC,CAAAC,G,gIAUUC,KAAAlC,KAAoB,GAGMkC,KAAAnB,SAAmB,GAG5BmB,KAAAjC,MAAgB,GAMjCiC,KAAAjB,QAA8B,GAGDiB,KAAAhB,YAAsB,GASnDgB,KAAAC,WAA+B,YAG/BD,KAAAnC,SAAmB,GAGnBmC,KAAAE,WAAqB,GAQZF,KAAApB,aAA4B,GAC5BoB,KAAAlB,gBAAsC,GACtCkB,KAAAG,gBAA0B,GAC1BH,KAAAI,uBAAyB,KAiJ3C,CA7IC,kBAAYC,GACV,GAAIL,KAAKE,YAAcF,KAAKnC,SAAU,CACpC,OAAOmC,KAAKG,e,CAEd,OAAOH,KAAKjC,K,CAOd,aAAAuC,GACEN,KAAKpB,aAAeoB,KAAKnB,SACrBK,EAAmBc,KAAKnB,SAAU,IACjCmB,KAAKlC,MAAQ,GAClBkC,KAAKlB,gBAAkBkB,KAAKhB,YACxBE,EAAmBc,KAAKhB,YAAa,IACpCgB,KAAKjB,SAAW,GACrBiB,KAAKO,kB,CAKP,WAAAC,GACER,KAAKO,kB,CAGP,iBAAAE,GACET,KAAKM,e,CAGP,gBAAAI,GACEV,KAAKW,wBACLX,KAAKY,8B,CAIC,qBAAAD,GACN,GACEhC,EACEqB,KAAKpB,aACLoB,KAAKlC,KACLkC,KAAKnB,SACLmB,KAAKlB,gBACLkB,KAAKjB,QACLiB,KAAKhB,aAEP,CACAgB,KAAKM,e,MACA,GAAIN,KAAKE,YAAcF,KAAKnC,SAAU,CAC3CmC,KAAKO,kB,EAKD,4BAAAK,GACN,IAAIC,EAAYlB,EAAOmB,sBACvB,MAAMC,EAAO,KACXf,KAAKW,wBACL,KAAME,EAAY,EAAG,CACnBG,sBAAsBD,E,GAG1BE,eAAeF,E,CAGT,gBAAAR,GACN,IAAKP,KAAKE,aAAeF,KAAKnC,SAAU,CACtCmC,KAAKG,gBAAkB,GACvBH,KAAKI,uBAAyB,MAC9B,M,CAGF,MAAMrC,MAAEA,EAAKC,SAAEA,GAAaL,EAC1BqC,KAAKE,WACLF,KAAKnC,SACLmC,KAAKpB,cAEPoB,KAAKG,gBAAkBpC,EACvBiC,KAAKI,uBAAyBpC,C,CAGxB,eAAAkD,CAAgBC,GACtB,MAAM5C,EAAM4C,EAA0BC,OACtCpB,KAAKjC,MAAQQ,EACbyB,KAAKqB,YAAYC,KAAK/C,E,CAGhB,kBAAAgD,CAAmBC,EAAkBL,GAC3C,MAAMM,EAAYN,EAA2BC,OAC7CpB,KAAK0B,eAAeJ,KAAK,CAAE/C,GAAIiD,EAAUC,Y,CAG3C,MAAAE,GACE,MAAMC,EAAU5B,KAAKpB,aAAaR,OAAS,IAAM4B,KAAKI,uBAItD,MAAMyB,EAA0E,GAChF,MAAMC,EAA+CD,EAAa7B,KAAKC,YAEvE,OACE8B,EAACC,EAAI,CAAAC,IAAA,4CACHF,EAAA,UAAAE,IAAA,2CAAQC,MAAO,CAAE,UAAW,KAAM,CAAC,MAAMlC,KAAKC,cAAe,OAE3D8B,EAAA,OAAAE,IAAA,2CAAKC,MAAM,iBACRN,EAEGG,EAAA,gBACEjE,KAAMkC,KAAKpB,aACXb,MAAOiC,KAAKK,eACZ8B,WAAahB,GAAanB,KAAKkB,gBAAgBC,KAGjDnB,KAAKoC,SACLL,EAAA,QAAMG,MAAM,8CAA8ClC,KAAKoC,UAKpEpC,KAAKlB,gBAAgBV,OAAS,GAC7B2D,EAAA,OAAAE,IAAA,2CAAKC,MAAM,oBACRlC,KAAKlB,gBAAgBuD,KAAIC,GACxBP,EAAA,qBACEE,IAAKK,EAAO/D,GACZgE,KAAMD,EAAOC,KACbd,SAAUa,EAAOb,UAAY,MAC7Be,IAAKF,EAAOE,KAAO,MACnBC,SAAUH,EAAOG,SAAQ,aACbH,EAAOI,WAAaJ,EAAO/D,GACvC0B,WAAY6B,EACZK,WAAahB,GAAanB,KAAKuB,mBAAmBe,EAAO/D,GAAI4C,S,wrBArIrDxB,EAAAmB,sBAAwB,E","ignoreList":[]}
|
|
@@ -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-CkBXHj0e.js";import{d as r}from"./p-C_mk7BoY.js";function d(a,e){return a===e||a.startsWith(`${e}/`)}function l(a,e){if(!a)return"";let n="";let t=0;for(const i of e){if(!i.href)continue;if(d(a,i.href)&&i.href.length>t){n=i.id;t=i.href.length}}return n}function c(a){if(Array.isArray(a))return a;if(typeof a==="string"){try{const e=JSON.parse(a);return Array.isArray(e)?e:[]}catch{return[]}}return[]}let v=false;function p(){if(v)return;const a="ds-panel-nav-vt-style";if(document.getElementById(a)){v=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);v=true}const h=()=>`.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;height:var(--dimension-size-600, 48px);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}}`;const f=new WeakMap;const m=new WeakMap;function u(a,e){const n=a.trim();const t=parseFloat(n);if(Number.isNaN(t))return e;if(/ms\s*$/.test(n))return t;if(/s\s*$/.test(n))return t*1e3;return t}const b=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.dsNavUserAction=n(this,"dsNavUserAction");this.variant="dashboard";this.disableViewTransition=false;this.groups="[]";this.routerMode="anchor";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){this.parsedGroups=c(a);this.syncRovingIndex();this.syncActiveFromUrl()}onActiveIdChange(){this.syncRovingIndex()}onCurrentUrlChange(){this.syncActiveFromUrl()}async onVariantChange(a){if(this.disableViewTransition){this.renderedVariant=a;return}f.get(this)?.skipTransition();f.delete(this);const e=document;if(typeof e.startViewTransition!=="function"){this.renderedVariant=a;return}p();const n=this.el.querySelector(".panel-nav__footer-btn");const t=n?.getBoundingClientRect();const i=t?Math.round(t.left+t.width/2):Math.round(window.innerWidth/2);const o=t?Math.round(t.top+t.height/2):Math.round(window.innerHeight/2);const s=Math.ceil(Math.hypot(Math.max(i,window.innerWidth-i),Math.max(o,window.innerHeight-o)));document.documentElement.style.setProperty("--vt-x",`${i}px`);document.documentElement.style.setProperty("--vt-y",`${o}px`);const r=e.startViewTransition((()=>new Promise((e=>{m.set(this,e);this.renderedVariant=a}))));f.set(this,r);r.finished.finally((()=>{if(f.get(this)===r)f.delete(this);m.delete(this)}));r.ready.then((()=>{const a=getComputedStyle(document.documentElement).getPropertyValue("--effect-animation-duration-long-1").trim();const e=u(a,750);document.documentElement.animate({clipPath:[`circle(0px at ${i}px ${o}px)`,`circle(${s}px at ${i}px ${o}px)`]},{duration:e,easing:"ease-in-out",fill:"forwards",pseudoElement:"::view-transition-new(root)"})})).catch((()=>{}))}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()}componentDidRender(){const a=m.get(this);if(a){m.delete(this);a()}}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}this.urlDerivedActiveId=l(this.currentUrl,this.getAllItems())}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;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()}handleUserAction(){this.dsNavUserAction.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}};const r=this.routerMode==="anchor"&&a.href;return r?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:"75bb0d291cd86ec3eae2b9ccea8f793469b4be31",style:{display:"block",position:"relative"}},t("nav",{key:"ff8505395360bdbb7a9dbfd798da478d311c385c",class:s,"aria-label":a?"Main navigation":"Settings navigation"},t("div",{key:"acff3333081b70551dae34cd4f342fea13ab0e9b",class:"panel-nav__header"},t("button",{key:"625125b971d8b2f93af391a4838da27dc1c9f73d",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:"7c65260ffe07903f988d27ee16a664ee27efabb6",class:"panel-nav__header-logo","aria-hidden":"true"},t("svg",{key:"92979f00985a081989ef6fc358105132216e2d62",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:"245c75d8bbd4635626c56c10652b2ef0f1422394",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:"b4aee5f2b3bd518ebbdb8f77076c47ce3ec9eb2d",class:"panel-nav__header-toggle","aria-hidden":"true"},t("ds-icon",{key:"57866598644db254b503593c0e153506adb78f05",name:e?"LeftExpandB":"LeftCollapseB",size:"md",color:"inherit"})))),t("div",{key:"69bbbb451b9504bc6f75bef574fab34a2dc92ee2",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:"586c6557c15251e1510106950ee1460149a20e6b",class:"panel-nav__footer"},t("button",{key:"48da25f162e689b66b97fb69cb51550226309c3f",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:"b73353e1dd43d67b1733c94aab7bc83cbb004cfe",name:a?"Gear":"Dashboard",size:"md",color:"inherit"})),t("button",{key:"9bf1498f790784abc5c2e8c2c66e06f14e38b5ba",type:"button",class:"panel-nav__item panel-nav__footer-user","aria-label":e?`User: ${n}`:`User menu for ${n}`,onClick:()=>this.handleUserAction()},t("span",{key:"f2d084429e7e68dba7856739892335b3657bf64e",class:"panel-nav__item-label panel-nav__footer-user-label"},t("span",{key:"b94ac406ca7f0de5fa695c0c348cd06e4c56f6a4",class:"panel-nav__item-label-text"},t("ds-text",{key:"30f3f044f5f234fbe896f8e304c62e6d15f0967a",as:"span",variant:"text-body-medium-emphasis",color:"inherit"},n))),t("span",{key:"3fc742d82ee1f0478b8be976b6fbcad87d75d58b",class:"panel-nav__item-icon panel-nav__footer-user-icon","aria-hidden":"true"},t("span",{key:"94d16279915843a6d9664b6909cd50701f4175db",class:"panel-nav__footer-icon-expanded"},t("ds-icon",{key:"b3de58f2e93b7473efcefb2cfa1148c194594271",name:"ChevronUpDown",size:"md",color:"inherit"})),t("span",{key:"01b6c5720f4d97f01fd0ff738b709def6ba1c998",class:"panel-nav__footer-icon-collapsed"},t("ds-icon",{key:"1958490bb8c7b9d02b23759e4db00649b02716e7",name:"Circle",size:"md",color:"inherit"}),t("span",{key:"d70d6528f5ae27be803a7ead80f86e9ce48e2d5d",class:"panel-nav__user-initial"},t("ds-text",{key:"34a0616c5adad854873d79b6582c9ac97fe43eda",as:"span",variant:"text-caption-emphasis",color:"inherit"},o))))))),!this.viewportNarrow&&t("div",{key:"9b97c0b48d2c39e90dd02a09f8d58ac0dddd4979",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 h()}},[2,"ds-panel-nav",{variant:[1],disableViewTransition:[4,"disable-view-transition"],groups:[1],routerMode:[1,"router-mode"],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 _(){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),b)}break;case"ds-icon":if(!customElements.get(o(a))){s()}break;case"ds-text":if(!customElements.get(o(a))){r()}break}}))}_();const g=b;const x=_;export{g as DsPanelNav,x 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-CkBXHj0e.js";import{d as r}from"./p-C_mk7BoY.js";function l(a,e){return a===e||a.startsWith(`${e}/`)}function d(a,e){if(!a)return"";let n="";let t=0;for(const i of e){if(!i.href)continue;if(l(a,i.href)&&i.href.length>t){n=i.id;t=i.href.length}}return n}function c(a){return a.reduce(((a,e)=>a+e.items.length),0)}function v(a){if(Array.isArray(a))return a;if(typeof a==="string"){try{const e=JSON.parse(a);return Array.isArray(e)?e:[]}catch{return[]}}return[]}function p(a,e){return c(a)===0&&c(v(e))>0}let h=false;function f(){if(h)return;const a="ds-panel-nav-vt-style";if(document.getElementById(a)){h=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);h=true}const m=()=>`.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;height:var(--dimension-size-600, 48px);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}}`;const u=new WeakMap;const _=new WeakMap;function b(a,e){const n=a.trim();const t=parseFloat(n);if(Number.isNaN(t))return e;if(/ms\s*$/.test(n))return t;if(/s\s*$/.test(n))return t*1e3;return t}const g=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.dsNavUserAction=n(this,"dsNavUserAction");this.variant="dashboard";this.disableViewTransition=false;this.groups="[]";this.routerMode="anchor";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){this.parsedGroups=v(a);this.syncRovingIndex();this.syncActiveFromUrl()}onActiveIdChange(){this.syncRovingIndex()}onCurrentUrlChange(){this.syncActiveFromUrl()}async onVariantChange(a){if(this.disableViewTransition){this.renderedVariant=a;return}u.get(this)?.skipTransition();u.delete(this);const e=document;if(typeof e.startViewTransition!=="function"){this.renderedVariant=a;return}f();const n=this.el.querySelector(".panel-nav__footer-btn");const t=n?.getBoundingClientRect();const i=t?Math.round(t.left+t.width/2):Math.round(window.innerWidth/2);const o=t?Math.round(t.top+t.height/2):Math.round(window.innerHeight/2);const s=Math.ceil(Math.hypot(Math.max(i,window.innerWidth-i),Math.max(o,window.innerHeight-o)));document.documentElement.style.setProperty("--vt-x",`${i}px`);document.documentElement.style.setProperty("--vt-y",`${o}px`);const r=e.startViewTransition((()=>new Promise((e=>{_.set(this,e);this.renderedVariant=a}))));u.set(this,r);r.finished.finally((()=>{if(u.get(this)===r)u.delete(this);_.delete(this)}));r.ready.then((()=>{const a=getComputedStyle(document.documentElement).getPropertyValue("--effect-animation-duration-long-1").trim();const e=b(a,750);document.documentElement.animate({clipPath:[`circle(0px at ${i}px ${o}px)`,`circle(${s}px at ${i}px ${o}px)`]},{duration:e,easing:"ease-in-out",fill:"forwards",pseudoElement:"::view-transition-new(root)"})})).catch((()=>{}))}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.syncHostPropsIfNeeded();this.scheduleDeferredHostPropSync();this.checkScroll();if(this.breakpoint>0)this.connectResizeObserver()}componentDidRender(){const a=_.get(this);if(a){_.delete(this);a()}}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}this.urlDerivedActiveId=d(this.currentUrl,this.getAllItems())}syncHostPropsIfNeeded(){if(p(this.parsedGroups,this.groups)){this.onGroupsChange(this.groups)}else if(this.currentUrl){this.syncActiveFromUrl()}}scheduleDeferredHostPropSync(){let e=a.HOST_PROP_SYNC_BUDGET;const n=()=>{this.syncHostPropsIfNeeded();if(--e>0){requestAnimationFrame(n)}};queueMicrotask(n)}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;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()}handleUserAction(){this.dsNavUserAction.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}};const r=this.routerMode==="anchor"&&a.href;return r?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:"e828867ff1a7fb6c8f59b26703c43ab522393367",style:{display:"block",position:"relative"}},t("nav",{key:"0fc64c34dec32781810539745367d13267a9992b",class:s,"aria-label":a?"Main navigation":"Settings navigation"},t("div",{key:"2f91c64d04f3b473d4ff0109eec50b7b437ec391",class:"panel-nav__header"},t("button",{key:"6ae7d111615f9487b6781600b15d0168f677a553",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:"c77c040972731036989c96c6d3de612009d798a4",class:"panel-nav__header-logo","aria-hidden":"true"},t("svg",{key:"3a14f3c31e74a80d66f1485d9d6ac3d7a792d2d3",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:"9051072dcd0998ef984cb470f37fbd20267b64bb",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:"5d979cdbc10d7254a6c6567e9992e6678511e5ad",class:"panel-nav__header-toggle","aria-hidden":"true"},t("ds-icon",{key:"cf667613ec805e11c5b22858b6003cbefe3b1edb",name:e?"LeftExpandB":"LeftCollapseB",size:"md",color:"inherit"})))),t("div",{key:"6ed94b758a7a2598587312559cd560e96cb5eb3f",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:"d51d18654814cc1b6d4678befe3ba6c22b00e4fd",class:"panel-nav__footer"},t("button",{key:"426fe8d64977ad18633496fa87762415af49b45f",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:"ce7f9ef79162c10bd79c3fa3c100d6909fbcb443",name:a?"Gear":"Dashboard",size:"md",color:"inherit"})),t("button",{key:"d12a0501212c9722f26457e275885e7cee310f97",type:"button",class:"panel-nav__item panel-nav__footer-user","aria-label":e?`User: ${n}`:`User menu for ${n}`,onClick:()=>this.handleUserAction()},t("span",{key:"ee0734de28bfaa008739e6546f6eb040c693c995",class:"panel-nav__item-label panel-nav__footer-user-label"},t("span",{key:"6b4a966b8eb89a25597f160e9d611eae8f2e7983",class:"panel-nav__item-label-text"},t("ds-text",{key:"27c0aefb685930d9964e6596dabd952b29eee456",as:"span",variant:"text-body-medium-emphasis",color:"inherit"},n))),t("span",{key:"f85ddb2ca93f467398ed76222f723a4dc98732cf",class:"panel-nav__item-icon panel-nav__footer-user-icon","aria-hidden":"true"},t("span",{key:"62484ccb5585bf052949fabf4909b4dcbd84a11c",class:"panel-nav__footer-icon-expanded"},t("ds-icon",{key:"51e2b8d08af417548d6501b0e586bc2681c36ce5",name:"ChevronUpDown",size:"md",color:"inherit"})),t("span",{key:"435519ea9b5ccfe5bb35fd1a762529d5b02019ef",class:"panel-nav__footer-icon-collapsed"},t("ds-icon",{key:"0e09e448e7b4816bf04fb67ecd0f1b77b2369dab",name:"Circle",size:"md",color:"inherit"}),t("span",{key:"7285eae4135963bfa44d8b358bbd0f45f0a65082",class:"panel-nav__user-initial"},t("ds-text",{key:"c7f5196fed17be4f3d4d458667d589409cc830a8",as:"span",variant:"text-caption-emphasis",color:"inherit"},o))))))),!this.viewportNarrow&&t("div",{key:"3b9fb5f0250dc77409ad3b37dc2170d7e988aab2",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 m()}},[2,"ds-panel-nav",{variant:[1],disableViewTransition:[4,"disable-view-transition"],groups:[1],routerMode:[1,"router-mode"],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}]}]);g.HOST_PROP_SYNC_BUDGET=8;function x(){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),g)}break;case"ds-icon":if(!customElements.get(o(a))){s()}break;case"ds-text":if(!customElements.get(o(a))){r()}break}}))}x();const y=g;const w=x;export{y as DsPanelNav,w as defineCustomElement};
|
|
2
2
|
//# sourceMappingURL=ds-panel-nav.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["hrefMatchesPath","path","href","startsWith","deriveActiveIdFromUrl","items","bestId","bestLen","item","length","id","parsePanelNavGroups","groups","Array","isArray","parsed","JSON","parse","vtStyleInjected","ensurePanelNavVtStyle","document","getElementById","style","createElement","textContent","join","head","appendChild","panelNavCss","vtTransitions","WeakMap","vtResolvers","parseCssTimeMs","value","fallback","v","trim","num","parseFloat","Number","isNaN","test","PanelNav","__stencil_proxyCustomElement","HTMLElement","constructor","registerHost","this","variant","disableViewTransition","routerMode","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","syncRovingIndex","syncActiveFromUrl","onActiveIdChange","onCurrentUrlChange","onVariantChange","newVal","get","skipTransition","delete","doc","startViewTransition","btn","el","querySelector","rect","getBoundingClientRect","x","Math","round","left","width","window","innerWidth","y","top","height","innerHeight","maxR","ceil","hypot","max","documentElement","setProperty","transition","Promise","resolve","set","finished","finally","ready","then","durToken","getComputedStyle","getPropertyValue","duration","animate","clipPath","easing","fill","pseudoElement","catch","componentWillLoad","stored","localStorage","getItem","componentDidLoad","checkScroll","componentDidRender","disconnectedCallback","clearEdgeOverlayTimer","globalMouseMoveHandler","removeEventListener","globalMouseUpHandler","panel","transitionEndHandler","e","propertyName","addEventListener","resizeObserver","ResizeObserver","observe","disconnect","undefined","getAllItems","applyToggle","next","dsNavToggle","emit","setItem","String","flatMap","g","idx","findIndex","i","handleItemKeyDown","index","total","key","preventDefault","from","querySelectorAll","focus","body","remaining","scrollHeight","scrollTop","clientHeight","handleBodyScroll","target","handleItemClick","dsNavSelect","handleToggle","handleFooterAction","dsNavFooterAction","handleUserAction","dsNavUserAction","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","useAnchor","type","render","isDashboard","navCls","Host","display","position","viewBox","xmlns","focusable","d","onScroll","flatIdx","map","group","onMouseEnter","onMouseLeave","onMouseDown"],"sources":["src/wc/components/PanelNav/panel-nav-utils.ts","src/wc/components/PanelNav/PanelNav.css?tag=ds-panel-nav&encapsulation=scoped","src/wc/components/PanelNav/PanelNav.tsx"],"sourcesContent":["import type { PanelNavGroup, PanelNavItem } from './PanelNav';\n\n/** Whether `path` matches `href` at a segment boundary (exact or child path). */\nexport function hrefMatchesPath(path: string, href: string): boolean {\n return path === href || path.startsWith(`${href}/`);\n}\n\n/** Derive the active nav item id from a URL using longest segment-boundary prefix match. */\nexport function deriveActiveIdFromUrl(path: string, items: PanelNavItem[]): string {\n if (!path) return '';\n\n let bestId = '';\n let bestLen = 0;\n\n for (const item of items) {\n if (!item.href) continue;\n if (hrefMatchesPath(path, item.href) && item.href.length > bestLen) {\n bestId = item.id;\n bestLen = item.href.length;\n }\n }\n\n return bestId;\n}\n\n/** Parse `groups` from either a JSON attribute string or a JS property array. */\nexport function parsePanelNavGroups(groups: string | unknown): PanelNavGroup[] {\n if (Array.isArray(groups)) return groups;\n if (typeof groups === 'string') {\n try {\n const parsed = JSON.parse(groups);\n return Array.isArray(parsed) ? parsed : [];\n } catch {\n return [];\n }\n }\n return [];\n}\n\nlet vtStyleInjected = false;\n\n/** Inject view-transition suppress styles once per document (shared by PanelNav + hosts). */\nexport function ensurePanelNavVtStyle(): void {\n if (vtStyleInjected) return;\n const id = 'ds-panel-nav-vt-style';\n if (document.getElementById(id)) {\n vtStyleInjected = true;\n return;\n }\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","/* ── 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 height: var(--dimension-size-600, 48px);\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';\nimport {\n deriveActiveIdFromUrl,\n ensurePanelNavVtStyle,\n parsePanelNavGroups,\n} from './panel-nav-utils';\n\n// Module-level WeakMaps for per-instance VT state. Using WeakMaps instead of\n// class fields avoids Stencil generating getter-only property descriptors on\n// the host element for private fields, which throws in strict environments.\n// Minimal typings for the View Transitions API, which isn't in our TS lib target.\ninterface ViewTransition {\n finished: Promise<void>;\n skipTransition: () => void;\n}\ntype DocumentWithViewTransition = Document & {\n startViewTransition?: (callback: () => void | Promise<void>) => ViewTransition;\n};\n\nconst vtTransitions = new WeakMap<object, ViewTransition>();\nconst vtResolvers = new WeakMap<object, () => void>();\n\n/** Parse a CSS <time> value to milliseconds. Handles `s`, `ms`, and unitless\n * (assumed ms). Critically: `parseFloat('.75s')` is 0.75 — WAAPI treats that as\n * 0.75ms (instant/invisible), so the `s` unit must be scaled to ms. Design\n * tokens (e.g. `--effect-animation-duration-long-1`) are authored in seconds. */\nfunction parseCssTimeMs(value: string, fallback: number): number {\n const v = value.trim();\n const num = parseFloat(v);\n if (Number.isNaN(num)) return fallback;\n if (/ms\\s*$/.test(v)) return num;\n if (/s\\s*$/.test(v)) return num * 1000;\n return num;\n}\n\nexport type PanelNavVariant = 'dashboard' | 'settings';\nexport type PanelNavRouterMode = 'anchor' | 'event';\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 /** Route path used for `currentUrl` matching. In `anchor` mode also sets `<a href>`.\n * In `event` mode navigation is delegated to the host via `dsNavSelect`. */\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 /** When `true`, the component does not run its own View Transition on variant\n * change — it just updates the rendered surface synchronously. Use this when\n * the host app orchestrates the page transition itself (e.g. Angular Router's\n * `withViewTransitions`), so the two don't fight or nest. */\n @Prop() disableViewTransition: boolean = false;\n\n /** Nav groups — set via JS property (`el.groups = [...]`) or JSON string attribute. */\n @Prop() groups: string | PanelNavGroup[] = '[]';\n\n /** How items with `href` render:\n * - `anchor` (default): native `<a href>` — works with routers that intercept anchors.\n * - `event`: always `<button>`; host handles navigation via `dsNavSelect`. */\n @Prop() routerMode: PanelNavRouterMode = 'anchor';\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 segment-boundary 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 /** Emitted when the footer user button is clicked. */\n @Event() dsNavUserAction!: 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 | PanelNavGroup[]) {\n this.parsedGroups = parsePanelNavGroups(val);\n this.syncRovingIndex();\n this.syncActiveFromUrl();\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 // Host app drives the transition (e.g. Angular Router withViewTransitions):\n // just reflect the new surface so the app's transition captures it.\n if (this.disableViewTransition) {\n this.renderedVariant = newVal;\n return;\n }\n\n // Skip any in-progress transition immediately so rapid variant changes\n // (e.g. quick dashboard → settings → dashboard) never leave the nav stuck.\n vtTransitions.get(this)?.skipTransition();\n vtTransitions.delete(this);\n\n const doc = document as DocumentWithViewTransition;\n if (typeof doc.startViewTransition !== 'function') {\n this.renderedVariant = newVal;\n return;\n }\n\n ensurePanelNavVtStyle();\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 = doc.startViewTransition(() =>\n new Promise<void>(resolve => {\n vtResolvers.set(this, resolve);\n this.renderedVariant = newVal;\n })\n );\n\n vtTransitions.set(this, transition);\n transition.finished.finally(() => {\n if (vtTransitions.get(this) === transition) vtTransitions.delete(this);\n vtResolvers.delete(this);\n });\n\n transition.ready.then(() => {\n const durToken = getComputedStyle(document.documentElement)\n .getPropertyValue('--effect-animation-duration-long-1').trim();\n const duration = parseCssTimeMs(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 }).catch(() => { /* transition was skipped or aborted */ });\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 }\n\n componentDidRender() {\n // If a view transition callback is waiting for the render to complete,\n // resolve it now so the \"after\" snapshot is taken at exactly the right moment.\n const resolve = vtResolvers.get(this);\n if (resolve) {\n vtResolvers.delete(this);\n resolve();\n }\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 segment-boundary prefix match. */\n private syncActiveFromUrl() {\n if (!this.currentUrl) {\n this.urlDerivedActiveId = '';\n return;\n }\n this.urlDerivedActiveId = deriveActiveIdFromUrl(this.currentUrl, this.getAllItems());\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: number;\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 handleUserAction() {\n this.dsNavUserAction.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 const useAnchor = this.routerMode === 'anchor' && item.href;\n\n return useAnchor\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 onClick={() => this.handleUserAction()}\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":[]}
|
|
1
|
+
{"version":3,"names":["hrefMatchesPath","path","href","startsWith","deriveActiveIdFromUrl","items","bestId","bestLen","item","length","id","countPanelNavItems","groups","reduce","sum","g","parsePanelNavGroups","Array","isArray","parsed","JSON","parse","shouldResyncPanelNavGroups","parsedGroups","vtStyleInjected","ensurePanelNavVtStyle","document","getElementById","style","createElement","textContent","join","head","appendChild","panelNavCss","vtTransitions","WeakMap","vtResolvers","parseCssTimeMs","value","fallback","v","trim","num","parseFloat","Number","isNaN","test","PanelNav","__stencil_proxyCustomElement","HTMLElement","constructor","registerHost","this","variant","disableViewTransition","routerMode","activeId","collapsed","breakpoint","storageKey","currentUrl","userName","userInitial","renderedVariant","atBottom","isAnimating","rovingIndex","viewportNarrow","urlDerivedActiveId","isDragging","dragStartX","didSnap","lastDeltaX","edgeOverlayTimer","showEdgeOverlay","effectiveActiveId","onCollapsedChange","startCollapseAnimation","onBreakpointChange","disconnectResizeObserver","connectResizeObserver","onGroupsChange","val","syncRovingIndex","syncActiveFromUrl","onActiveIdChange","onCurrentUrlChange","onVariantChange","newVal","get","skipTransition","delete","doc","startViewTransition","btn","el","querySelector","rect","getBoundingClientRect","x","Math","round","left","width","window","innerWidth","y","top","height","innerHeight","maxR","ceil","hypot","max","documentElement","setProperty","transition","Promise","resolve","set","finished","finally","ready","then","durToken","getComputedStyle","getPropertyValue","duration","animate","clipPath","easing","fill","pseudoElement","catch","componentWillLoad","stored","localStorage","getItem","componentDidLoad","syncHostPropsIfNeeded","scheduleDeferredHostPropSync","checkScroll","componentDidRender","disconnectedCallback","clearEdgeOverlayTimer","globalMouseMoveHandler","removeEventListener","globalMouseUpHandler","panel","transitionEndHandler","e","propertyName","addEventListener","resizeObserver","ResizeObserver","observe","disconnect","undefined","getAllItems","remaining","HOST_PROP_SYNC_BUDGET","tick","requestAnimationFrame","queueMicrotask","applyToggle","next","dsNavToggle","emit","setItem","String","flatMap","idx","findIndex","i","handleItemKeyDown","index","total","key","preventDefault","from","querySelectorAll","focus","body","scrollHeight","scrollTop","clientHeight","handleBodyScroll","target","handleItemClick","dsNavSelect","handleToggle","handleFooterAction","dsNavFooterAction","handleUserAction","dsNavUserAction","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","useAnchor","type","render","isDashboard","navCls","Host","display","position","viewBox","xmlns","focusable","d","onScroll","flatIdx","map","group","onMouseEnter","onMouseLeave","onMouseDown"],"sources":["src/wc/components/PanelNav/panel-nav-utils.ts","src/wc/components/PanelNav/PanelNav.css?tag=ds-panel-nav&encapsulation=scoped","src/wc/components/PanelNav/PanelNav.tsx"],"sourcesContent":["import type { PanelNavGroup, PanelNavItem } from './PanelNav';\n\n/** Whether `path` matches `href` at a segment boundary (exact or child path). */\nexport function hrefMatchesPath(path: string, href: string): boolean {\n return path === href || path.startsWith(`${href}/`);\n}\n\n/** Derive the active nav item id from a URL using longest segment-boundary prefix match. */\nexport function deriveActiveIdFromUrl(path: string, items: PanelNavItem[]): string {\n if (!path) return '';\n\n let bestId = '';\n let bestLen = 0;\n\n for (const item of items) {\n if (!item.href) continue;\n if (hrefMatchesPath(path, item.href) && item.href.length > bestLen) {\n bestId = item.id;\n bestLen = item.href.length;\n }\n }\n\n return bestId;\n}\n\n/** Total nav item count across all groups. */\nexport function countPanelNavItems(groups: PanelNavGroup[]): number {\n return groups.reduce((sum, g) => sum + g.items.length, 0);\n}\n\n/** Parse `groups` from either a JSON attribute string or a JS property array. */\nexport function parsePanelNavGroups(groups: string | unknown): PanelNavGroup[] {\n if (Array.isArray(groups)) return groups;\n if (typeof groups === 'string') {\n try {\n const parsed = JSON.parse(groups);\n return Array.isArray(parsed) ? parsed : [];\n } catch {\n return [];\n }\n }\n return [];\n}\n\n/** True when host `groups` has items but internal parsed state is still empty. */\nexport function shouldResyncPanelNavGroups(\n parsedGroups: PanelNavGroup[],\n groups: string | unknown,\n): boolean {\n return (\n countPanelNavItems(parsedGroups) === 0 &&\n countPanelNavItems(parsePanelNavGroups(groups)) > 0\n );\n}\n\nlet vtStyleInjected = false;\n\n/** Inject view-transition suppress styles once per document (shared by PanelNav + hosts). */\nexport function ensurePanelNavVtStyle(): void {\n if (vtStyleInjected) return;\n const id = 'ds-panel-nav-vt-style';\n if (document.getElementById(id)) {\n vtStyleInjected = true;\n return;\n }\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","/* ── 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 height: var(--dimension-size-600, 48px);\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';\nimport {\n deriveActiveIdFromUrl,\n ensurePanelNavVtStyle,\n parsePanelNavGroups,\n shouldResyncPanelNavGroups,\n} from './panel-nav-utils';\n\n// Module-level WeakMaps for per-instance VT state. Using WeakMaps instead of\n// class fields avoids Stencil generating getter-only property descriptors on\n// the host element for private fields, which throws in strict environments.\n// Minimal typings for the View Transitions API, which isn't in our TS lib target.\ninterface ViewTransition {\n finished: Promise<void>;\n skipTransition: () => void;\n}\ntype DocumentWithViewTransition = Document & {\n startViewTransition?: (callback: () => void | Promise<void>) => ViewTransition;\n};\n\nconst vtTransitions = new WeakMap<object, ViewTransition>();\nconst vtResolvers = new WeakMap<object, () => void>();\n\n/** Parse a CSS <time> value to milliseconds. Handles `s`, `ms`, and unitless\n * (assumed ms). Critically: `parseFloat('.75s')` is 0.75 — WAAPI treats that as\n * 0.75ms (instant/invisible), so the `s` unit must be scaled to ms. Design\n * tokens (e.g. `--effect-animation-duration-long-1`) are authored in seconds. */\nfunction parseCssTimeMs(value: string, fallback: number): number {\n const v = value.trim();\n const num = parseFloat(v);\n if (Number.isNaN(num)) return fallback;\n if (/ms\\s*$/.test(v)) return num;\n if (/s\\s*$/.test(v)) return num * 1000;\n return num;\n}\n\nexport type PanelNavVariant = 'dashboard' | 'settings';\nexport type PanelNavRouterMode = 'anchor' | 'event';\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 /** Route path used for `currentUrl` matching. In `anchor` mode also sets `<a href>`.\n * In `event` mode navigation is delegated to the host via `dsNavSelect`. */\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 /** When `true`, the component does not run its own View Transition on variant\n * change — it just updates the rendered surface synchronously. Use this when\n * the host app orchestrates the page transition itself (e.g. Angular Router's\n * `withViewTransitions`), so the two don't fight or nest. */\n @Prop() disableViewTransition: boolean = false;\n\n /** Nav groups — set via JS property (`el.groups = [...]`) or JSON string attribute. */\n @Prop() groups: string | PanelNavGroup[] = '[]';\n\n /** How items with `href` render:\n * - `anchor` (default): native `<a href>` — works with routers that intercept anchors.\n * - `event`: always `<button>`; host handles navigation via `dsNavSelect`. */\n @Prop() routerMode: PanelNavRouterMode = 'anchor';\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 segment-boundary 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 /** Emitted when the footer user button is clicked. */\n @Event() dsNavUserAction!: 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 private static readonly HOST_PROP_SYNC_BUDGET = 8;\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 | PanelNavGroup[]) {\n this.parsedGroups = parsePanelNavGroups(val);\n this.syncRovingIndex();\n this.syncActiveFromUrl();\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 // Host app drives the transition (e.g. Angular Router withViewTransitions):\n // just reflect the new surface so the app's transition captures it.\n if (this.disableViewTransition) {\n this.renderedVariant = newVal;\n return;\n }\n\n // Skip any in-progress transition immediately so rapid variant changes\n // (e.g. quick dashboard → settings → dashboard) never leave the nav stuck.\n vtTransitions.get(this)?.skipTransition();\n vtTransitions.delete(this);\n\n const doc = document as DocumentWithViewTransition;\n if (typeof doc.startViewTransition !== 'function') {\n this.renderedVariant = newVal;\n return;\n }\n\n ensurePanelNavVtStyle();\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 = doc.startViewTransition(() =>\n new Promise<void>(resolve => {\n vtResolvers.set(this, resolve);\n this.renderedVariant = newVal;\n })\n );\n\n vtTransitions.set(this, transition);\n transition.finished.finally(() => {\n if (vtTransitions.get(this) === transition) vtTransitions.delete(this);\n vtResolvers.delete(this);\n });\n\n transition.ready.then(() => {\n const durToken = getComputedStyle(document.documentElement)\n .getPropertyValue('--effect-animation-duration-long-1').trim();\n const duration = parseCssTimeMs(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 }).catch(() => { /* transition was skipped or aborted */ });\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.syncHostPropsIfNeeded();\n this.scheduleDeferredHostPropSync();\n this.checkScroll();\n if (this.breakpoint > 0) this.connectResizeObserver();\n }\n\n componentDidRender() {\n // If a view transition callback is waiting for the render to complete,\n // resolve it now so the \"after\" snapshot is taken at exactly the right moment.\n const resolve = vtResolvers.get(this);\n if (resolve) {\n vtResolvers.delete(this);\n resolve();\n }\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 segment-boundary prefix match. */\n private syncActiveFromUrl() {\n if (!this.currentUrl) {\n this.urlDerivedActiveId = '';\n return;\n }\n this.urlDerivedActiveId = deriveActiveIdFromUrl(this.currentUrl, this.getAllItems());\n }\n\n /** Re-parse props assigned by the host after componentWillLoad (Angular ngAfterViewInit). */\n private syncHostPropsIfNeeded() {\n if (shouldResyncPanelNavGroups(this.parsedGroups, this.groups)) {\n this.onGroupsChange(this.groups);\n } else if (this.currentUrl) {\n this.syncActiveFromUrl();\n }\n }\n\n /** Poll across animation frames — host props may land without triggering @Watch. */\n private scheduleDeferredHostPropSync() {\n let remaining = PanelNav.HOST_PROP_SYNC_BUDGET;\n const tick = () => {\n this.syncHostPropsIfNeeded();\n if (--remaining > 0) {\n requestAnimationFrame(tick);\n }\n };\n queueMicrotask(tick);\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: number;\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 handleUserAction() {\n this.dsNavUserAction.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 const useAnchor = this.routerMode === 'anchor' && item.href;\n\n return useAnchor\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 onClick={() => this.handleUserAction()}\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":[]}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{T as o,d as s}from"./p-
|
|
1
|
+
import{T as o,d as s}from"./p-BvASlk3n.js";const t=o;const n=s;export{t as DsTabGroup,n as defineCustomElement};
|
|
2
2
|
//# sourceMappingURL=ds-tab-group.js.map
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{p as r,H as o,c as a,h as e,a as t,t as s}from"./index.js";const n=()=>`.sc-ds-tab-group-h{display:block}.tab-list.sc-ds-tab-group{display:flex;align-items:center;gap:var(--dimension-space-050);position:relative}.tab-list--vertical.sc-ds-tab-group{flex-direction:column;align-items:stretch}.tab.sc-ds-tab-group{display:flex;align-items:center;justify-content:center;gap:var(--dimension-space-050);padding:var(--dimension-space-075);background-color:transparent;border:none;border-radius:var(--dimension-radius-025);cursor:pointer;-webkit-user-select:none;user-select:none;color:var(--color-foreground-secondary);position:relative;transition:color var(--effect-motion-short-2);flex-shrink:0;height:var(--dimension-size-400);min-width:var(--dimension-size-400);box-sizing:border-box;--_hover-bg:var(--color-interaction-hover);--_pressed-bg:var(--color-interaction-pressed);--_dot:var(--color-foreground-bold-brand);--_dot-ring:var(--color-background-secondary)}.tab.sc-ds-tab-group::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)}.tab.sc-ds-tab-group:hover::after{background:var(--_hover-bg);transition:none}.tab.sc-ds-tab-group:active::after{background:var(--_pressed-bg);transition:none}.tab--selected.sc-ds-tab-group{color:var(--color-foreground-primary)}.tab.sc-ds-tab-group:focus-visible{outline:var(--dimension-stroke-width-025) solid var(--color-interaction-focus);outline-offset:var(--dimension-space-025)}.tab.sc-ds-tab-group:disabled,.tab[aria-disabled="true"].sc-ds-tab-group{opacity:0.5;cursor:not-allowed;pointer-events:none}.tab-list--vertical.sc-ds-tab-group .tab.sc-ds-tab-group{justify-content:flex-start}.tab__label.sc-ds-tab-group{position:relative;display:inline-flex;align-items:center;padding:0 var(--dimension-space-025);white-space:nowrap}.tab__label--dot.sc-ds-tab-group{padding-right:calc(var(--dimension-space-025) + var(--dimension-space-050))}.tab__dot.sc-ds-tab-group{position:absolute;top:0;right:0;width:var(--dimension-space-075);height:var(--dimension-space-075);border-radius:var(--dimension-radius-full, 9999px);background:var(--_dot);box-shadow:0 0 0 var(--dimension-stroke-width-018, 1.5px) var(--_dot-ring);pointer-events:none}.on-medium.sc-ds-tab-group{--_hover-bg:var(--color-interaction-on-medium-background-hover);--_pressed-bg:var(--color-interaction-on-medium-background-pressed);--_dot-ring:var(--color-background-medium)}.on-bold.sc-ds-tab-group{--_hover-bg:var(--color-interaction-on-bold-background-hover);--_pressed-bg:var(--color-interaction-on-bold-background-pressed);--_dot-ring:var(--color-background-bold);color:var(--color-foreground-on-bold-background-secondary)}.on-bold.tab--selected.sc-ds-tab-group{color:var(--color-foreground-on-bold-background-primary)}.on-strong.sc-ds-tab-group{--_hover-bg:var(--color-interaction-on-strong-background-hover);--_pressed-bg:var(--color-interaction-on-strong-background-pressed);--_dot-ring:var(--color-background-strong);color:var(--color-foreground-on-bold-background-secondary)}.on-strong.tab--selected.sc-ds-tab-group{color:var(--color-foreground-on-bold-background-primary)}.on-always-dark.sc-ds-tab-group{--_hover-bg:var(--color-always-dark-interaction-hover);--_pressed-bg:var(--color-always-dark-interaction-pressed);--_dot-ring:var(--color-always-dark-background);color:var(--color-always-dark-foreground-secondary)}.on-always-dark.tab--selected.sc-ds-tab-group{color:var(--color-always-dark-foreground-primary)}`;const i=r(class r extends o{constructor(r){super();if(r!==false){this.__registerHost()}this.dsChange=a(this,"dsChange");this.value="";this.tabs=[];this.orientation="horizontal"}selectTab(r){this.value=r;this.dsChange.emit(r)}findEnabled(r,o){const a=this.tabs.length;for(let e=0;e<a;e++){const t=(r+o*(e+1)+a*(e+1))%a;if(!this.tabs[t]?.disabled)return t}return r}handleKeyDown(r){if(!this.tabs.length)return;const o=this.tabs.findIndex((r=>r.id===this.value));const a=this.orientation==="vertical";let e=null;if(!a){if(r.key==="ArrowRight"){e=this.findEnabled(o,1)}else if(r.key==="ArrowLeft"){e=this.findEnabled(o,-1)}}else{if(r.key==="ArrowDown"){e=this.findEnabled(o,1)}else if(r.key==="ArrowUp"){e=this.findEnabled(o,-1)}}if(r.key==="Home"){const r=this.tabs.findIndex((r=>!r.disabled));e=r===-1?null:r}else if(r.key==="End"){const r=[...this.tabs].reverse().findIndex((r=>!r.disabled));e=r===-1?null:this.tabs.length-1-r}if(e!==null){r.preventDefault();const o=this.tabs[e];this.selectTab(o.id);const a=this.el.querySelector(`[data-tab-id="${o.id}"]`);a?.focus()}}getBgClass(){if(!this.background||this.background==="faint")return"";if(this.background==="always-dark")return"on-always-dark";return`on-${this.background}`}render(){const r=this.getBgClass();const o=this.orientation==="vertical";return e(t,{key:"eb385dcf5e03e68f92c6d310a13fe754af8258d0",class:"tab-group-host"},e("div",{key:"62cbb809e40f92dbf503a76438666411eabba2df",role:"tablist",class:{"tab-list":true,"tab-list--vertical":o},"aria-label":this.ariaLabel,"aria-labelledby":this.ariaLabelledby,"aria-orientation":o?"vertical":undefined},this.tabs.map((o=>{const a=o.id===this.value;return e("button",{key:o.id,type:"button",role:"tab","data-tab-id":o.id,class:{tab:true,"tab--selected":a,[r]:!!r},"aria-selected":a,"aria-disabled":o.disabled||undefined,"aria-controls":o.panelId??undefined,disabled:o.disabled,tabIndex:a?0:-1,onClick:()=>!o.disabled&&this.selectTab(o.id)},e("span",{class:{tab__label:true,"tab__label--dot":!!o.dot,[a?"text-body-medium-emphasis":"text-body-medium"]:true}},o.label,o.dot&&e("span",{class:"tab__dot","aria-hidden":"true"})))}))))}get el(){return this}static get style(){return n()}},[2,"ds-tab-group",{value:[1025],tabs:[16],background:[1],ariaLabel:[1,"aria-label"],ariaLabelledby:[1,"aria-labelledby"],orientation:[1]},[[0,"keydown","handleKeyDown"]]]);function d(){if(typeof customElements==="undefined"){return}const r=["ds-tab-group"];r.forEach((r=>{switch(r){case"ds-tab-group":if(!customElements.get(s(r))){customElements.define(s(r),i)}break}}))}d();export{i as T,d};
|
|
2
|
+
//# sourceMappingURL=p-BvASlk3n.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["tabGroupCss","TabGroup","__stencil_proxyCustomElement","HTMLElement","constructor","registerHost","this","value","tabs","orientation","selectTab","id","dsChange","emit","findEnabled","from","step","len","length","n","i","disabled","handleKeyDown","e","currentIdx","findIndex","t","isVertical","nextIdx","key","first","lastEnabled","reverse","preventDefault","next","btn","el","querySelector","focus","getBgClass","background","render","bgClass","h","Host","class","role","ariaLabel","ariaLabelledby","undefined","map","tab","isSelected","type","panelId","tabIndex","onClick","tab__label","dot","label"],"sources":["src/wc/components/TabGroup/TabGroup.css?tag=ds-tab-group&encapsulation=scoped","src/wc/components/TabGroup/TabGroup.tsx"],"sourcesContent":[":host {\n display: block;\n}\n\n.tab-list {\n display: flex;\n align-items: center;\n gap: var(--dimension-space-050);\n position: relative;\n}\n\n/* Vertical orientation */\n.tab-list--vertical {\n flex-direction: column;\n align-items: stretch;\n}\n\n.tab {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: var(--dimension-space-050);\n padding: var(--dimension-space-075);\n background-color: transparent;\n border: none;\n border-radius: var(--dimension-radius-025);\n cursor: pointer;\n -webkit-user-select: none;\n user-select: none;\n color: var(--color-foreground-secondary);\n position: relative;\n /* Only transition color — hover bg is instant (see ::after below) */\n transition: color var(--effect-motion-short-2);\n flex-shrink: 0;\n height: var(--dimension-size-400);\n min-width: var(--dimension-size-400);\n box-sizing: border-box;\n --_hover-bg: var(--color-interaction-hover);\n --_pressed-bg: var(--color-interaction-pressed);\n --_dot: var(--color-foreground-bold-brand);\n --_dot-ring: var(--color-background-secondary);\n}\n\n/* Interaction layer — same pattern as PanelNav */\n.tab::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.tab:hover::after { background: var(--_hover-bg); transition: none; }\n.tab:active::after { background: var(--_pressed-bg); transition: none; }\n\n.tab--selected { color: var(--color-foreground-primary); }\n\n.tab:focus-visible {\n outline: var(--dimension-stroke-width-025) solid var(--color-interaction-focus);\n outline-offset: var(--dimension-space-025);\n}\n\n.tab:disabled,\n.tab[aria-disabled=\"true\"] {\n opacity: 0.5;\n cursor: not-allowed;\n pointer-events: none;\n}\n\n/* Vertical tabs read as a list — left-align the label */\n.tab-list--vertical .tab { justify-content: flex-start; }\n\n/* Label — dot anchors top-right like BarNav action icon zone */\n.tab__label {\n position: relative;\n display: inline-flex;\n align-items: center;\n padding: 0 var(--dimension-space-025);\n white-space: nowrap;\n}\n\n/* Clearance for top-right dot — slightly less than space-025 + space-075 */\n.tab__label--dot {\n padding-right: calc(var(--dimension-space-025) + var(--dimension-space-050));\n}\n\n/* Notification dot — top-right corner of the label zone */\n.tab__dot {\n position: absolute;\n top: 0;\n right: 0;\n width: var(--dimension-space-075);\n height: var(--dimension-space-075);\n border-radius: var(--dimension-radius-full, 9999px);\n background: var(--_dot);\n box-shadow: 0 0 0 var(--dimension-stroke-width-018, 1.5px) var(--_dot-ring);\n pointer-events: none;\n}\n\n/* Background context */\n.on-medium {\n --_hover-bg: var(--color-interaction-on-medium-background-hover);\n --_pressed-bg: var(--color-interaction-on-medium-background-pressed);\n --_dot-ring: var(--color-background-medium);\n}\n\n.on-bold {\n --_hover-bg: var(--color-interaction-on-bold-background-hover);\n --_pressed-bg: var(--color-interaction-on-bold-background-pressed);\n --_dot-ring: var(--color-background-bold);\n color: var(--color-foreground-on-bold-background-secondary);\n}\n\n.on-bold.tab--selected { color: var(--color-foreground-on-bold-background-primary); }\n\n.on-strong {\n --_hover-bg: var(--color-interaction-on-strong-background-hover);\n --_pressed-bg: var(--color-interaction-on-strong-background-pressed);\n --_dot-ring: var(--color-background-strong);\n color: var(--color-foreground-on-bold-background-secondary);\n}\n\n.on-strong.tab--selected { color: var(--color-foreground-on-bold-background-primary); }\n\n.on-always-dark {\n --_hover-bg: var(--color-always-dark-interaction-hover);\n --_pressed-bg: var(--color-always-dark-interaction-pressed);\n --_dot-ring: var(--color-always-dark-background);\n color: var(--color-always-dark-foreground-secondary);\n}\n\n.on-always-dark.tab--selected { color: var(--color-always-dark-foreground-primary); }\n","import { Component, Prop, Event, EventEmitter, Element, Listen, h, Host } from '@stencil/core';\n\nexport type TabBackground = 'faint' | 'medium' | 'bold' | 'strong' | 'always-dark';\n\nexport interface TabItem {\n id: string;\n label: string;\n disabled?: boolean;\n /** id of the tabpanel this tab controls */\n panelId?: string;\n /** Show a notification dot (brand) on the tab — no count, matching panel-nav. */\n dot?: boolean;\n}\n\n@Component({\n tag: 'ds-tab-group',\n styleUrl: 'TabGroup.css',\n scoped: true,\n})\nexport class TabGroup {\n @Element() el!: HTMLElement;\n\n @Prop({ mutable: true }) value: string = '';\n @Prop() tabs: TabItem[] = [];\n @Prop() background: TabBackground | undefined;\n @Prop({ attribute: 'aria-label' }) ariaLabel: string | undefined;\n @Prop({ attribute: 'aria-labelledby' }) ariaLabelledby: string | undefined;\n @Prop() orientation: 'horizontal' | 'vertical' = 'horizontal';\n\n @Event() dsChange!: EventEmitter<string>;\n\n private selectTab(id: string) {\n this.value = id;\n this.dsChange.emit(id);\n }\n\n /**\n * Find the next non-disabled tab index relative to `from`, stepping by `step`.\n * Returns `from` unchanged if no other enabled tab exists.\n */\n private findEnabled(from: number, step: 1 | -1): number {\n const len = this.tabs.length;\n for (let n = 0; n < len; n++) {\n const i = (from + step * (n + 1) + len * (n + 1)) % len;\n if (!this.tabs[i]?.disabled) return i;\n }\n return from;\n }\n\n @Listen('keydown')\n handleKeyDown(e: KeyboardEvent) {\n if (!this.tabs.length) return;\n\n const currentIdx = this.tabs.findIndex(t => t.id === this.value);\n const isVertical = this.orientation === 'vertical';\n\n let nextIdx: number | null = null;\n\n if (!isVertical) {\n if (e.key === 'ArrowRight') {\n nextIdx = this.findEnabled(currentIdx, 1);\n } else if (e.key === 'ArrowLeft') {\n nextIdx = this.findEnabled(currentIdx, -1);\n }\n } else {\n if (e.key === 'ArrowDown') {\n nextIdx = this.findEnabled(currentIdx, 1);\n } else if (e.key === 'ArrowUp') {\n nextIdx = this.findEnabled(currentIdx, -1);\n }\n }\n\n if (e.key === 'Home') {\n const first = this.tabs.findIndex(t => !t.disabled);\n nextIdx = first === -1 ? null : first;\n } else if (e.key === 'End') {\n const lastEnabled = [...this.tabs].reverse().findIndex(t => !t.disabled);\n nextIdx = lastEnabled === -1 ? null : this.tabs.length - 1 - lastEnabled;\n }\n\n if (nextIdx !== null) {\n e.preventDefault();\n const next = this.tabs[nextIdx];\n this.selectTab(next.id);\n const btn = this.el.querySelector(`[data-tab-id=\"${next.id}\"]`) as HTMLElement | null;\n btn?.focus();\n }\n }\n\n private getBgClass(): string {\n if (!this.background || this.background === 'faint') return '';\n if (this.background === 'always-dark') return 'on-always-dark';\n return `on-${this.background}`;\n }\n\n render() {\n const bgClass = this.getBgClass();\n const isVertical = this.orientation === 'vertical';\n\n return (\n <Host class=\"tab-group-host\">\n <div\n role=\"tablist\"\n class={{ 'tab-list': true, 'tab-list--vertical': isVertical }}\n aria-label={this.ariaLabel}\n aria-labelledby={this.ariaLabelledby}\n aria-orientation={isVertical ? 'vertical' : undefined}\n >\n {this.tabs.map(tab => {\n const isSelected = tab.id === this.value;\n return (\n <button\n key={tab.id}\n type=\"button\"\n role=\"tab\"\n data-tab-id={tab.id}\n class={{\n tab: true,\n 'tab--selected': isSelected,\n [bgClass]: !!bgClass,\n }}\n aria-selected={isSelected}\n aria-disabled={tab.disabled || undefined}\n aria-controls={tab.panelId ?? undefined}\n disabled={tab.disabled}\n tabIndex={isSelected ? 0 : -1}\n onClick={() => !tab.disabled && this.selectTab(tab.id)}\n >\n <span class={{\n tab__label: true,\n 'tab__label--dot': !!tab.dot,\n [isSelected ? 'text-body-medium-emphasis' : 'text-body-medium']: true,\n }}>\n {tab.label}\n {tab.dot && <span class=\"tab__dot\" aria-hidden=\"true\" />}\n </span>\n </button>\n );\n })}\n </div>\n </Host>\n );\n }\n}\n"],"mappings":"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","ignoreList":[]}
|
|
@@ -57,10 +57,16 @@ export declare class BarNav {
|
|
|
57
57
|
private resolvedActions;
|
|
58
58
|
private urlDerivedValue;
|
|
59
59
|
private hideTabsForDetailRoute;
|
|
60
|
+
private static readonly HOST_PROP_SYNC_BUDGET;
|
|
60
61
|
private get effectiveValue();
|
|
61
62
|
onPropsChange(): void;
|
|
62
63
|
onUrlChange(): void;
|
|
63
64
|
componentWillLoad(): void;
|
|
65
|
+
componentDidLoad(): void;
|
|
66
|
+
/** Re-resolve props assigned by the host after componentWillLoad (Angular ngAfterViewInit). */
|
|
67
|
+
private syncHostPropsIfNeeded;
|
|
68
|
+
/** Poll across animation frames — host props may land without triggering @Watch. */
|
|
69
|
+
private scheduleDeferredHostPropSync;
|
|
64
70
|
private syncValueFromUrl;
|
|
65
71
|
private handleTabChange;
|
|
66
72
|
private handleActionChange;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { BarNavTab } from './BarNav';
|
|
1
|
+
import type { BarNavActionItem, BarNavTab } from './BarNav';
|
|
2
2
|
export interface BarNavUrlState {
|
|
3
3
|
/** Tab id derived from the URL, or empty when none applies. */
|
|
4
4
|
value: string;
|
|
@@ -8,6 +8,8 @@ export interface BarNavUrlState {
|
|
|
8
8
|
/** Derive BarNav tab selection from `currentUrl` relative to `basePath`.
|
|
9
9
|
* Mirrors motive-webapp-lab `resolveBarNavState` tab-selection semantics. */
|
|
10
10
|
export declare function deriveBarNavValueFromUrl(path: string, basePath: string, tabs: BarNavTab[]): BarNavUrlState;
|
|
11
|
+
/** True when host array/json props have data but resolved internal state is still empty. */
|
|
12
|
+
export declare function shouldResyncBarNavProps(resolvedTabs: BarNavTab[], tabs: BarNavTab[], tabsJson: string, resolvedActions: BarNavActionItem[], actions: BarNavActionItem[], actionsJson: string): boolean;
|
|
11
13
|
/** Parse tabs/actions from either a JSON attribute string or a JS property array. */
|
|
12
14
|
export declare function parseJsonArrayProp<T>(value: string | T[] | undefined, fallback: T[]): T[];
|
|
13
15
|
//# sourceMappingURL=bar-nav-utils.d.ts.map
|
|
@@ -79,6 +79,7 @@ export declare class PanelNav {
|
|
|
79
79
|
private globalMouseMoveHandler?;
|
|
80
80
|
private globalMouseUpHandler?;
|
|
81
81
|
private showEdgeOverlay;
|
|
82
|
+
private static readonly HOST_PROP_SYNC_BUDGET;
|
|
82
83
|
/** The ID that should be treated as active. `currentUrl` matching takes precedence
|
|
83
84
|
* over `activeId` when both are present. */
|
|
84
85
|
private get effectiveActiveId();
|
|
@@ -98,6 +99,10 @@ export declare class PanelNav {
|
|
|
98
99
|
/** Derive active ID from the current URL by matching item `href` values.
|
|
99
100
|
* Uses longest segment-boundary prefix match. */
|
|
100
101
|
private syncActiveFromUrl;
|
|
102
|
+
/** Re-parse props assigned by the host after componentWillLoad (Angular ngAfterViewInit). */
|
|
103
|
+
private syncHostPropsIfNeeded;
|
|
104
|
+
/** Poll across animation frames — host props may land without triggering @Watch. */
|
|
105
|
+
private scheduleDeferredHostPropSync;
|
|
101
106
|
/** Centralised toggle: always emits `dsNavToggle`; when `storageKey` is set
|
|
102
107
|
* also mutates `collapsed` directly and persists to `localStorage`. */
|
|
103
108
|
private applyToggle;
|
|
@@ -3,8 +3,12 @@ import type { PanelNavGroup, PanelNavItem } from './PanelNav';
|
|
|
3
3
|
export declare function hrefMatchesPath(path: string, href: string): boolean;
|
|
4
4
|
/** Derive the active nav item id from a URL using longest segment-boundary prefix match. */
|
|
5
5
|
export declare function deriveActiveIdFromUrl(path: string, items: PanelNavItem[]): string;
|
|
6
|
+
/** Total nav item count across all groups. */
|
|
7
|
+
export declare function countPanelNavItems(groups: PanelNavGroup[]): number;
|
|
6
8
|
/** Parse `groups` from either a JSON attribute string or a JS property array. */
|
|
7
9
|
export declare function parsePanelNavGroups(groups: string | unknown): PanelNavGroup[];
|
|
10
|
+
/** True when host `groups` has items but internal parsed state is still empty. */
|
|
11
|
+
export declare function shouldResyncPanelNavGroups(parsedGroups: PanelNavGroup[], groups: string | unknown): boolean;
|
|
8
12
|
/** Inject view-transition suppress styles once per document (shared by PanelNav + hosts). */
|
|
9
13
|
export declare function ensurePanelNavVtStyle(): void;
|
|
10
14
|
//# sourceMappingURL=panel-nav-utils.d.ts.map
|
package/package.json
CHANGED
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
import{p as r,H as o,c as e,h as t,a,t as s}from"./index.js";const n=()=>`.sc-ds-tab-group-h{display:block}.tab-list.sc-ds-tab-group{display:flex;align-items:center;gap:var(--dimension-space-050);position:relative}.tab-list--vertical.sc-ds-tab-group{flex-direction:column;align-items:stretch}.tab.sc-ds-tab-group{display:flex;align-items:center;justify-content:center;gap:var(--dimension-space-050);padding:var(--dimension-space-075);background-color:transparent;border:none;border-radius:var(--dimension-radius-025);cursor:pointer;-webkit-user-select:none;user-select:none;color:var(--color-foreground-secondary);position:relative;transition:color var(--effect-motion-short-2);flex-shrink:0;height:var(--dimension-size-400);min-width:var(--dimension-size-400);box-sizing:border-box;--_hover-bg:var(--color-interaction-hover);--_pressed-bg:var(--color-interaction-pressed);--_dot:var(--color-foreground-bold-brand)}.tab.sc-ds-tab-group::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)}.tab.sc-ds-tab-group:hover::after{background:var(--_hover-bg);transition:none}.tab.sc-ds-tab-group:active::after{background:var(--_pressed-bg);transition:none}.tab--selected.sc-ds-tab-group{color:var(--color-foreground-primary)}.tab.sc-ds-tab-group:focus-visible{outline:var(--dimension-stroke-width-025) solid var(--color-interaction-focus);outline-offset:var(--dimension-space-025)}.tab.sc-ds-tab-group:disabled,.tab[aria-disabled="true"].sc-ds-tab-group{opacity:0.5;cursor:not-allowed;pointer-events:none}.tab-list--vertical.sc-ds-tab-group .tab.sc-ds-tab-group{justify-content:flex-start}.tab__label.sc-ds-tab-group{position:relative;padding:0 var(--dimension-space-025);white-space:nowrap}.tab__dot-wrap.sc-ds-tab-group{position:relative;display:inline-flex;align-items:center;justify-content:center;width:20px;height:20px;flex-shrink:0}.tab__dot.sc-ds-tab-group{width:var(--dimension-space-075);height:var(--dimension-space-075);border-radius:var(--dimension-radius-full, 9999px);background:var(--_dot)}.on-medium.sc-ds-tab-group{--_hover-bg:var(--color-interaction-on-medium-background-hover);--_pressed-bg:var(--color-interaction-on-medium-background-pressed)}.on-bold.sc-ds-tab-group{--_hover-bg:var(--color-interaction-on-bold-background-hover);--_pressed-bg:var(--color-interaction-on-bold-background-pressed);color:var(--color-foreground-on-bold-background-secondary)}.on-bold.tab--selected.sc-ds-tab-group{color:var(--color-foreground-on-bold-background-primary)}.on-strong.sc-ds-tab-group{--_hover-bg:var(--color-interaction-on-strong-background-hover);--_pressed-bg:var(--color-interaction-on-strong-background-pressed);color:var(--color-foreground-on-bold-background-secondary)}.on-strong.tab--selected.sc-ds-tab-group{color:var(--color-foreground-on-bold-background-primary)}.on-always-dark.sc-ds-tab-group{--_hover-bg:var(--color-always-dark-interaction-hover);--_pressed-bg:var(--color-always-dark-interaction-pressed);color:var(--color-always-dark-foreground-secondary)}.on-always-dark.tab--selected.sc-ds-tab-group{color:var(--color-always-dark-foreground-primary)}`;const i=r(class r extends o{constructor(r){super();if(r!==false){this.__registerHost()}this.dsChange=e(this,"dsChange");this.value="";this.tabs=[];this.orientation="horizontal"}selectTab(r){this.value=r;this.dsChange.emit(r)}findEnabled(r,o){const e=this.tabs.length;for(let t=0;t<e;t++){const a=(r+o*(t+1)+e*(t+1))%e;if(!this.tabs[a]?.disabled)return a}return r}handleKeyDown(r){if(!this.tabs.length)return;const o=this.tabs.findIndex((r=>r.id===this.value));const e=this.orientation==="vertical";let t=null;if(!e){if(r.key==="ArrowRight"){t=this.findEnabled(o,1)}else if(r.key==="ArrowLeft"){t=this.findEnabled(o,-1)}}else{if(r.key==="ArrowDown"){t=this.findEnabled(o,1)}else if(r.key==="ArrowUp"){t=this.findEnabled(o,-1)}}if(r.key==="Home"){const r=this.tabs.findIndex((r=>!r.disabled));t=r===-1?null:r}else if(r.key==="End"){const r=[...this.tabs].reverse().findIndex((r=>!r.disabled));t=r===-1?null:this.tabs.length-1-r}if(t!==null){r.preventDefault();const o=this.tabs[t];this.selectTab(o.id);const e=this.el.querySelector(`[data-tab-id="${o.id}"]`);e?.focus()}}getBgClass(){if(!this.background||this.background==="faint")return"";if(this.background==="always-dark")return"on-always-dark";return`on-${this.background}`}render(){const r=this.getBgClass();const o=this.orientation==="vertical";return t(a,{key:"eb385dcf5e03e68f92c6d310a13fe754af8258d0",class:"tab-group-host"},t("div",{key:"62cbb809e40f92dbf503a76438666411eabba2df",role:"tablist",class:{"tab-list":true,"tab-list--vertical":o},"aria-label":this.ariaLabel,"aria-labelledby":this.ariaLabelledby,"aria-orientation":o?"vertical":undefined},this.tabs.map((o=>{const e=o.id===this.value;return t("button",{key:o.id,type:"button",role:"tab","data-tab-id":o.id,class:{tab:true,"tab--selected":e,[r]:!!r},"aria-selected":e,"aria-disabled":o.disabled||undefined,"aria-controls":o.panelId??undefined,disabled:o.disabled,tabIndex:e?0:-1,onClick:()=>!o.disabled&&this.selectTab(o.id)},t("span",{class:`tab__label ${e?"text-body-medium-emphasis":"text-body-medium"}`},o.label),o.dot&&t("span",{class:"tab__dot-wrap","aria-hidden":"true"},t("span",{class:"tab__dot"})))}))))}get el(){return this}static get style(){return n()}},[2,"ds-tab-group",{value:[1025],tabs:[16],background:[1],ariaLabel:[1,"aria-label"],ariaLabelledby:[1,"aria-labelledby"],orientation:[1]},[[0,"keydown","handleKeyDown"]]]);function d(){if(typeof customElements==="undefined"){return}const r=["ds-tab-group"];r.forEach((r=>{switch(r){case"ds-tab-group":if(!customElements.get(s(r))){customElements.define(s(r),i)}break}}))}d();export{i as T,d};
|
|
2
|
-
//# sourceMappingURL=p-D1cpKdRU.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"names":["tabGroupCss","TabGroup","__stencil_proxyCustomElement","HTMLElement","constructor","registerHost","this","value","tabs","orientation","selectTab","id","dsChange","emit","findEnabled","from","step","len","length","n","i","disabled","handleKeyDown","e","currentIdx","findIndex","t","isVertical","nextIdx","key","first","lastEnabled","reverse","preventDefault","next","btn","el","querySelector","focus","getBgClass","background","render","bgClass","h","Host","class","role","ariaLabel","ariaLabelledby","undefined","map","tab","isSelected","type","panelId","tabIndex","onClick","label","dot"],"sources":["src/wc/components/TabGroup/TabGroup.css?tag=ds-tab-group&encapsulation=scoped","src/wc/components/TabGroup/TabGroup.tsx"],"sourcesContent":[":host {\n display: block;\n}\n\n.tab-list {\n display: flex;\n align-items: center;\n gap: var(--dimension-space-050);\n position: relative;\n}\n\n/* Vertical orientation */\n.tab-list--vertical {\n flex-direction: column;\n align-items: stretch;\n}\n\n.tab {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: var(--dimension-space-050);\n padding: var(--dimension-space-075);\n background-color: transparent;\n border: none;\n border-radius: var(--dimension-radius-025);\n cursor: pointer;\n -webkit-user-select: none;\n user-select: none;\n color: var(--color-foreground-secondary);\n position: relative;\n /* Only transition color — hover bg is instant (see ::after below) */\n transition: color var(--effect-motion-short-2);\n flex-shrink: 0;\n height: var(--dimension-size-400);\n min-width: var(--dimension-size-400);\n box-sizing: border-box;\n --_hover-bg: var(--color-interaction-hover);\n --_pressed-bg: var(--color-interaction-pressed);\n --_dot: var(--color-foreground-bold-brand);\n}\n\n/* Interaction layer — same pattern as PanelNav */\n.tab::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.tab:hover::after { background: var(--_hover-bg); transition: none; }\n.tab:active::after { background: var(--_pressed-bg); transition: none; }\n\n.tab--selected { color: var(--color-foreground-primary); }\n\n.tab:focus-visible {\n outline: var(--dimension-stroke-width-025) solid var(--color-interaction-focus);\n outline-offset: var(--dimension-space-025);\n}\n\n.tab:disabled,\n.tab[aria-disabled=\"true\"] {\n opacity: 0.5;\n cursor: not-allowed;\n pointer-events: none;\n}\n\n/* Vertical tabs read as a list — left-align the label */\n.tab-list--vertical .tab { justify-content: flex-start; }\n\n/* Label — 2px inset matches the Figma text-label frame */\n.tab__label {\n position: relative;\n padding: 0 var(--dimension-space-025);\n white-space: nowrap;\n}\n\n/* Notification dot — 20×20 icon-slot wrapper, dot circle inside */\n.tab__dot-wrap {\n position: relative;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 20px;\n height: 20px;\n flex-shrink: 0;\n}\n\n.tab__dot {\n width: var(--dimension-space-075);\n height: var(--dimension-space-075);\n border-radius: var(--dimension-radius-full, 9999px);\n background: var(--_dot);\n}\n\n/* Background context */\n.on-medium {\n --_hover-bg: var(--color-interaction-on-medium-background-hover);\n --_pressed-bg: var(--color-interaction-on-medium-background-pressed);\n}\n\n.on-bold {\n --_hover-bg: var(--color-interaction-on-bold-background-hover);\n --_pressed-bg: var(--color-interaction-on-bold-background-pressed);\n color: var(--color-foreground-on-bold-background-secondary);\n}\n\n.on-bold.tab--selected { color: var(--color-foreground-on-bold-background-primary); }\n\n.on-strong {\n --_hover-bg: var(--color-interaction-on-strong-background-hover);\n --_pressed-bg: var(--color-interaction-on-strong-background-pressed);\n color: var(--color-foreground-on-bold-background-secondary);\n}\n\n.on-strong.tab--selected { color: var(--color-foreground-on-bold-background-primary); }\n\n.on-always-dark {\n --_hover-bg: var(--color-always-dark-interaction-hover);\n --_pressed-bg: var(--color-always-dark-interaction-pressed);\n color: var(--color-always-dark-foreground-secondary);\n}\n\n.on-always-dark.tab--selected { color: var(--color-always-dark-foreground-primary); }\n","import { Component, Prop, Event, EventEmitter, Element, Listen, h, Host } from '@stencil/core';\n\nexport type TabBackground = 'faint' | 'medium' | 'bold' | 'strong' | 'always-dark';\n\nexport interface TabItem {\n id: string;\n label: string;\n disabled?: boolean;\n /** id of the tabpanel this tab controls */\n panelId?: string;\n /** Show a notification dot (brand) on the tab — no count, matching panel-nav. */\n dot?: boolean;\n}\n\n@Component({\n tag: 'ds-tab-group',\n styleUrl: 'TabGroup.css',\n scoped: true,\n})\nexport class TabGroup {\n @Element() el!: HTMLElement;\n\n @Prop({ mutable: true }) value: string = '';\n @Prop() tabs: TabItem[] = [];\n @Prop() background: TabBackground | undefined;\n @Prop({ attribute: 'aria-label' }) ariaLabel: string | undefined;\n @Prop({ attribute: 'aria-labelledby' }) ariaLabelledby: string | undefined;\n @Prop() orientation: 'horizontal' | 'vertical' = 'horizontal';\n\n @Event() dsChange!: EventEmitter<string>;\n\n private selectTab(id: string) {\n this.value = id;\n this.dsChange.emit(id);\n }\n\n /**\n * Find the next non-disabled tab index relative to `from`, stepping by `step`.\n * Returns `from` unchanged if no other enabled tab exists.\n */\n private findEnabled(from: number, step: 1 | -1): number {\n const len = this.tabs.length;\n for (let n = 0; n < len; n++) {\n const i = (from + step * (n + 1) + len * (n + 1)) % len;\n if (!this.tabs[i]?.disabled) return i;\n }\n return from;\n }\n\n @Listen('keydown')\n handleKeyDown(e: KeyboardEvent) {\n if (!this.tabs.length) return;\n\n const currentIdx = this.tabs.findIndex(t => t.id === this.value);\n const isVertical = this.orientation === 'vertical';\n\n let nextIdx: number | null = null;\n\n if (!isVertical) {\n if (e.key === 'ArrowRight') {\n nextIdx = this.findEnabled(currentIdx, 1);\n } else if (e.key === 'ArrowLeft') {\n nextIdx = this.findEnabled(currentIdx, -1);\n }\n } else {\n if (e.key === 'ArrowDown') {\n nextIdx = this.findEnabled(currentIdx, 1);\n } else if (e.key === 'ArrowUp') {\n nextIdx = this.findEnabled(currentIdx, -1);\n }\n }\n\n if (e.key === 'Home') {\n const first = this.tabs.findIndex(t => !t.disabled);\n nextIdx = first === -1 ? null : first;\n } else if (e.key === 'End') {\n const lastEnabled = [...this.tabs].reverse().findIndex(t => !t.disabled);\n nextIdx = lastEnabled === -1 ? null : this.tabs.length - 1 - lastEnabled;\n }\n\n if (nextIdx !== null) {\n e.preventDefault();\n const next = this.tabs[nextIdx];\n this.selectTab(next.id);\n const btn = this.el.querySelector(`[data-tab-id=\"${next.id}\"]`) as HTMLElement | null;\n btn?.focus();\n }\n }\n\n private getBgClass(): string {\n if (!this.background || this.background === 'faint') return '';\n if (this.background === 'always-dark') return 'on-always-dark';\n return `on-${this.background}`;\n }\n\n render() {\n const bgClass = this.getBgClass();\n const isVertical = this.orientation === 'vertical';\n\n return (\n <Host class=\"tab-group-host\">\n <div\n role=\"tablist\"\n class={{ 'tab-list': true, 'tab-list--vertical': isVertical }}\n aria-label={this.ariaLabel}\n aria-labelledby={this.ariaLabelledby}\n aria-orientation={isVertical ? 'vertical' : undefined}\n >\n {this.tabs.map(tab => {\n const isSelected = tab.id === this.value;\n return (\n <button\n key={tab.id}\n type=\"button\"\n role=\"tab\"\n data-tab-id={tab.id}\n class={{\n tab: true,\n 'tab--selected': isSelected,\n [bgClass]: !!bgClass,\n }}\n aria-selected={isSelected}\n aria-disabled={tab.disabled || undefined}\n aria-controls={tab.panelId ?? undefined}\n disabled={tab.disabled}\n tabIndex={isSelected ? 0 : -1}\n onClick={() => !tab.disabled && this.selectTab(tab.id)}\n >\n <span class={`tab__label ${isSelected ? 'text-body-medium-emphasis' : 'text-body-medium'}`}>\n {tab.label}\n </span>\n {tab.dot && (\n <span class=\"tab__dot-wrap\" aria-hidden=\"true\">\n <span class=\"tab__dot\" />\n </span>\n )}\n </button>\n );\n })}\n </div>\n </Host>\n );\n }\n}\n"],"mappings":"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","ignoreList":[]}
|