@ds-mo/ui 1.11.3 → 1.11.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +38 -62
- package/dist/.build-stamp +1 -1
- 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/types/components/BarNav/BarNav.d.ts +3 -0
- package/dist/types/components/PanelNav/PanelNav.d.ts +3 -1
- package/package.json +1 -1
- package/src/wc/components/BarNav/BarNav.tsx +24 -5
- package/src/wc/components/PanelNav/PanelNav.tsx +27 -4
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
[](https://www.npmjs.com/package/@ds-mo/ui)
|
|
4
4
|
|
|
5
|
-
Composable web UI components (Stencil custom elements) styled with [TokoMo](https://github.com/zainadeel/TokoMo) design tokens. Works in any framework — ships Angular proxies
|
|
5
|
+
Composable web UI components (Stencil custom elements) styled with [TokoMo](https://github.com/zainadeel/TokoMo) design tokens. Works in any framework — ships **Stencil-generated** Angular proxies and React wrappers alongside the `<ds-*>` custom elements.
|
|
6
6
|
|
|
7
7
|
## Install
|
|
8
8
|
|
|
@@ -19,84 +19,60 @@ npm install @ds-mo/ui @ds-mo/tokens @ds-mo/icons
|
|
|
19
19
|
|
|
20
20
|
Import TokoMo tokens globally (once, at your app root):
|
|
21
21
|
|
|
22
|
-
```
|
|
23
|
-
import '@ds-mo/tokens';
|
|
24
|
-
import '@ds-mo/tokens/reset';
|
|
25
|
-
import '@ds-mo/tokens/globals';
|
|
22
|
+
```ts
|
|
23
|
+
import '@ds-mo/tokens';
|
|
24
|
+
import '@ds-mo/tokens/reset';
|
|
25
|
+
import '@ds-mo/tokens/globals';
|
|
26
26
|
```
|
|
27
27
|
|
|
28
|
-
Register custom elements
|
|
28
|
+
Register the custom elements you render (each import auto-defines its tag):
|
|
29
29
|
|
|
30
30
|
```ts
|
|
31
|
-
import
|
|
32
|
-
import '@ds-mo/
|
|
33
|
-
import
|
|
34
|
-
|
|
35
|
-
defineCustomElements();
|
|
31
|
+
import '@ds-mo/ui/dist/components/ds-button.js';
|
|
32
|
+
import '@ds-mo/ui/dist/components/ds-bar-nav.js';
|
|
33
|
+
// …import only the <ds-*> tags your app uses
|
|
36
34
|
```
|
|
37
35
|
|
|
38
|
-
Your app bundler
|
|
36
|
+
Your app bundler must resolve `@ds-mo/icons` when it bundles `ds-icon` — install `@ds-mo/icons` alongside `@ds-mo/ui`.
|
|
37
|
+
|
|
38
|
+
### Framework wrappers (optional)
|
|
39
|
+
|
|
40
|
+
| Host | Import | Usage |
|
|
41
|
+
| --- | --- | --- |
|
|
42
|
+
| **Angular** | `@ds-mo/ui/angular` | Stencil proxy directives — property/event bindings in templates |
|
|
43
|
+
| **React** | `@ds-mo/ui/react` | `DsButton`, `DsBarNav`, … — thin wrappers around the same custom elements |
|
|
44
|
+
| **Any** | `@ds-mo/ui/dist/components/ds-*.js` | Use `<ds-*>` directly (motive-webapp-lab pattern) |
|
|
39
45
|
|
|
40
|
-
|
|
46
|
+
There is **no** published `@ds-mo/ui/loader` or global `@ds-mo/ui/css` bundle — styles ship scoped inside each custom-element module.
|
|
41
47
|
|
|
42
|
-
**SPA hosts (Angular / React):** `ds-panel-nav` and `ds-bar-nav` need a [first-paint integration contract](docs/framework-integration.md) on hard reload —
|
|
48
|
+
**SPA hosts (Angular / React):** `ds-panel-nav` and `ds-bar-nav` need a [first-paint integration contract](docs/framework-integration.md) on hard reload — seed bar-nav state and stamp `data-nav-style` before custom elements upgrade.
|
|
43
49
|
|
|
44
50
|
## Components
|
|
45
51
|
|
|
52
|
+
All tags are `ds-*` custom elements. Grouped by role (see Storybook for props and stories):
|
|
53
|
+
|
|
46
54
|
### Primitives
|
|
47
|
-
- **Text**
|
|
48
|
-
- **Surface** — container with backgrounds, elevation, edges, radius, interactive states
|
|
49
|
-
- **Card** — elevated content container with header/footer slots
|
|
50
|
-
- **Input** — text input field
|
|
51
|
-
- **Slider** — range slider
|
|
52
|
-
- **Field** — label + input wrapper
|
|
53
|
-
- **Divider** — horizontal or vertical rule
|
|
55
|
+
- **Text**, **Surface**, **Card**, **Input**, **Slider**, **Field**, **Divider**, **Icon**
|
|
54
56
|
|
|
55
57
|
### Actions
|
|
56
|
-
- **Button** — primary/secondary
|
|
57
|
-
- **ButtonGroup**
|
|
58
|
-
- **ToggleButton** — two-state selectable button
|
|
59
|
-
- **ToggleButtonGroup** — radio-style group of toggle buttons
|
|
58
|
+
- **Button** — `primary` / `secondary` variants, intents, sizes, icon slot
|
|
59
|
+
- **ButtonGroup**, **ToggleButton**, **ToggleButtonGroup**
|
|
60
60
|
|
|
61
61
|
### Controls
|
|
62
|
-
- **Toggle**
|
|
63
|
-
- **Checkbox** — with label and indeterminate state
|
|
64
|
-
- **Radio** — single-select radio input
|
|
62
|
+
- **Toggle**, **Checkbox**, **RadioGroup**
|
|
65
63
|
|
|
66
64
|
### Data display
|
|
67
|
-
- **Tag**
|
|
68
|
-
- **Chip** — interactive or removable metadata chip
|
|
69
|
-
- **Badge** — compact counter or notification dot
|
|
70
|
-
- **Table** — sortable, paginated data table
|
|
71
|
-
- **Accordion** — collapsible content sections
|
|
72
|
-
- **Pagination** — page navigation control
|
|
65
|
+
- **Tag**, **Chip**, **Badge**, **Table**, **Accordion**, **Pagination**
|
|
73
66
|
|
|
74
67
|
### Overlays
|
|
75
|
-
- **Modal**
|
|
76
|
-
- **Menu** — dropdown with sections, selection, positioning
|
|
77
|
-
- **Tooltip** — hover tooltip with shortcut key support
|
|
78
|
-
- **Select** — dropdown select
|
|
79
|
-
- **Toast** — transient notification
|
|
80
|
-
- **Banner** — notification bar with intents, toast mode
|
|
68
|
+
- **Modal**, **Menu**, **Tooltip**, **Select**, **ToastProvider** (`ds-toast-provider`), **Banner**
|
|
81
69
|
|
|
82
70
|
### Navigation
|
|
83
|
-
- **
|
|
84
|
-
- **
|
|
85
|
-
- **Breadcrumb** — path navigation
|
|
86
|
-
|
|
87
|
-
### Status
|
|
88
|
-
- **EmptyState** — placeholder states
|
|
89
|
-
- **Loader** — loading indicator
|
|
90
|
-
- **Skeleton** — content placeholder
|
|
91
|
-
|
|
92
|
-
### Layout
|
|
93
|
-
- **Header** — page header with left/center/right slots
|
|
94
|
-
- **PanelNav** — collapsible side navigation panel with sections and items
|
|
71
|
+
- **TabGroup** (pill), **TabGroupNav** (underline), **Breadcrumb**
|
|
72
|
+
- **AppShell**, **PanelNav**, **BarNav**, **BarNavAction**, **PanelTools**
|
|
95
73
|
|
|
96
|
-
###
|
|
97
|
-
- **
|
|
98
|
-
- **Scrollbar** — custom scrollbar
|
|
99
|
-
- **ErrorBoundary** — error catch with fallback
|
|
74
|
+
### Status & layout
|
|
75
|
+
- **EmptyState**, **Loader**, **Skeleton**, **Header**, **Fade**, **Scrollbar**
|
|
100
76
|
|
|
101
77
|
## Token dependency
|
|
102
78
|
|
|
@@ -104,16 +80,16 @@ All styling uses TokoMo CSS custom properties. No hardcoded colors, sizes, or sh
|
|
|
104
80
|
|
|
105
81
|
## Icon pattern
|
|
106
82
|
|
|
107
|
-
Components that accept icons use named
|
|
83
|
+
Components that accept icons use a named `icon` slot:
|
|
108
84
|
|
|
109
85
|
```html
|
|
110
86
|
<ds-button>
|
|
111
|
-
<ds-icon
|
|
87
|
+
<ds-icon name="ArrowRight" slot="icon"></ds-icon>
|
|
112
88
|
Save
|
|
113
89
|
</ds-button>
|
|
114
90
|
```
|
|
115
91
|
|
|
116
|
-
|
|
92
|
+
`name` must be a canonical IcoMo export key (`ArrowRight`, `Bell`, …). You can also slot any custom SVG element.
|
|
117
93
|
|
|
118
94
|
## Figma Code Connect
|
|
119
95
|
|
|
@@ -121,7 +97,7 @@ This repo includes [Figma Code Connect](https://developers.figma.com/docs/code-c
|
|
|
121
97
|
|
|
122
98
|
**Prerequisites**
|
|
123
99
|
|
|
124
|
-
- Node
|
|
100
|
+
- Node **20.19+** or **22.12+** (see `package.json` `engines` and `.nvmrc`).
|
|
125
101
|
- A Figma **personal access token** with **Code Connect (Write)** and **File content (Read)**. Set it locally only — never commit it:
|
|
126
102
|
- `export FIGMA_ACCESS_TOKEN='…'`, or
|
|
127
103
|
- pass `--token` / `-t` when publishing.
|
|
@@ -161,7 +137,7 @@ Each contributor can use their own `FIGMA_ACCESS_TOKEN` in a local shell or `.en
|
|
|
161
137
|
### Dev Mode vs “the template” (why mapping exists)
|
|
162
138
|
|
|
163
139
|
- **The canvas** is whatever designers built (variant names like `Type = Main`, nested structure, etc.). Code Connect does **not** rewrite your Figma file.
|
|
164
|
-
- **Dev Mode** can show a **link to your repo file** (e.g. `Icon.tsx`) when you connect the repo — that answers “where is this implemented?”
|
|
140
|
+
- **Dev Mode** can show a **link to your repo file** (e.g. `src/wc/components/Icon/Icon.tsx`) when you connect the repo — that answers “where is this implemented?”
|
|
165
141
|
- The **Code Connect snippet** is the **copy-paste example** Figma shows in Inspect. That text comes from **your published templates** (or from other Figma features like MCP context), not from magically knowing your Stencil prop names.
|
|
166
142
|
|
|
167
143
|
So: if Figma props already match your API (`intent`, `appearance`), your template can be a thin wrapper and life is easy. If they **do not** match (legacy `Type` vs real `intent`), the template is where you **translate** so the snippet still shows **canonical** usage. You are not making Dev Mode “look like Figma internals” — you are choosing what **engineers should copy** when names diverge.
|
|
@@ -192,4 +168,4 @@ If the secret is missing, the publish step fails with Figma’s “no access tok
|
|
|
192
168
|
|
|
193
169
|
### Icon batch files — CompoMo vs IcoMo
|
|
194
170
|
|
|
195
|
-
**Publish from CompoMo.**
|
|
171
|
+
**Publish from CompoMo.** Code Connect templates live under `code-connect/published/` (see `code-connect/examples/` for starters). `figma connect publish` runs from this repo. Snippets should show **`@ds-mo/ui`** usage (e.g. `<ds-icon name="Bell" />` or `<DsIcon name="Bell" />` from `@ds-mo/ui/react`). **`name`** values must match **IcoMo** export keys.
|
package/dist/.build-stamp
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
1783106409493
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{p as s,H as t,c as a,h as e,a as i,t as n}from"./index.js";import{g as r,i as o}from"./p-BQq26pt9.js";import{s as h,p as d,d as b}from"./p-Dl1GvZQ2.js";import{C as c,c as l,a as v,b as f,r as u,d as g}from"./p-BiM6QBTF.js";import{d as p}from"./p-CiEc8Nwx.js";import{d as m}from"./p-CCZFMcLe.js";import{d as _}from"./p-DqBCQRRd.js";import{d as y}from"./p-CuuuDjJJ.js";import{d as w}from"./p-DlsexsFg.js";function k(s,t){if(!s)return null;const a=s.shadowRoot??s;return a.querySelector(t)}function x(s){return k(s,'[role="tablist"]')}function C(s,t){const a=[];let e=[];for(const i of s){if(o(i)){if(e.length>0){a.push({items:e});e=[]}continue}e.push({label:i.label,value:i.id,isSelected:i.id===t,isInactive:i.disabled})}if(e.length>0){a.push({items:e})}return a}function T(s,t){const a=r(s);return a.find((s=>s.id===t))??a[0]}function z(s,t){return T(s,t)?.label??"Tabs"}function D(s,t,a,e=2){const i=s-t;if(a){return i>-e}return i>e}const P=()=>`.sc-ds-bar-nav-h{display:block}.bar-nav.sc-ds-bar-nav{display:flex;align-items:center;position:relative;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(--_bar-nav-border);background-color:var(--_bar-nav-bg);color:var(--_bar-nav-fg-primary);-webkit-user-select:none;user-select:none;overflow:hidden;transition:background-color var(--effect-motion-short-3), border-color var(--effect-motion-short-3), color var(--effect-motion-short-3);isolation:isolate;--_bar-nav-bg:var(--color-background-secondary);--_bar-nav-border:var(--color-border-tertiary);--_bar-nav-fg-primary:var(--color-foreground-primary);--_bar-nav-fg-secondary:var(--color-foreground-secondary);--_bar-nav-hover:var(--color-interaction-hover);--_bar-nav-pressed:var(--color-interaction-pressed);--_bar-nav-focus:var(--color-interaction-focus);--_bar-nav-dot:var(--color-foreground-bold-brand)}.bar-nav--dashboard.sc-ds-bar-nav,.bar-nav--settings.sc-ds-bar-nav{--_bar-nav-bg:var(--color-background-secondary);--_bar-nav-border:var(--color-border-tertiary);--_bar-nav-fg-primary:var(--color-foreground-primary);--_bar-nav-fg-secondary:var(--color-foreground-secondary);--_bar-nav-hover:var(--color-interaction-hover);--_bar-nav-pressed:var(--color-interaction-pressed);--_bar-nav-focus:var(--color-interaction-focus);--_bar-nav-dot:var(--color-foreground-bold-brand)}ds-app-shell[gradient].sc-ds-bar-nav-h .bar-nav.sc-ds-bar-nav,ds-app-shell[gradient] .sc-ds-bar-nav-h .bar-nav.sc-ds-bar-nav,ds-app-shell[grid].sc-ds-bar-nav-h .bar-nav.sc-ds-bar-nav,ds-app-shell[grid] .sc-ds-bar-nav-h .bar-nav.sc-ds-bar-nav{background-color:transparent}.bar-nav.sc-ds-bar-nav>.sc-ds-bar-nav:not(.bar-nav__tabs-probe){position:relative;z-index:3}.bar-nav--tabs-collapsed.sc-ds-bar-nav{display:flex;gap:0;align-items:center}.bar-nav--tabs-collapsed.sc-ds-bar-nav .bar-nav__tab-trigger.sc-ds-bar-nav{flex:0 1 auto;min-width:0;max-width:100%;width:auto;overflow:hidden}.bar-nav__between.sc-ds-bar-nav{flex:1 1 0%;min-width:var(--dimension-space-100)}.bar-nav--tabs-collapsed.sc-ds-bar-nav .bar-nav__tab-trigger-label.sc-ds-bar-nav{flex:1 1 0%;min-width:0}.bar-nav--tabs-collapsed.sc-ds-bar-nav .bar-nav__tab-trigger-label-text.sc-ds-bar-nav{display:block;flex:1 1 0%;min-width:0}.bar-nav--tabs-collapsed.sc-ds-bar-nav .bar-nav__actions.sc-ds-bar-nav{flex:0 0 auto;margin-left:0}.bar-nav__left.sc-ds-bar-nav{position:relative;flex:1 1 0%;min-width:0;width:0;display:flex;align-items:center}.bar-nav__left.sc-ds-bar-nav ds-tab-group-nav.sc-ds-bar-nav{flex:1;min-width:0}.bar-nav__tabs-probe.sc-ds-bar-nav{position:absolute;left:0;top:0;visibility:hidden;pointer-events:none;overflow:visible;width:max-content;max-width:none;z-index:-1}.bar-nav__tabs-probe.sc-ds-bar-nav ds-tab-group-nav.sc-ds-bar-nav{width:max-content}.bar-nav__tabs-visible.sc-ds-bar-nav{width:100%;min-width:0}.bar-nav__tabs-pending.sc-ds-bar-nav{flex:1;min-width:0;height:var(--dimension-size-400);visibility:hidden;pointer-events:none}.bar-nav__tab-trigger.sc-ds-bar-nav{all:unset;box-sizing:border-box;display:inline-flex;align-items:center;gap:var(--dimension-space-050);flex:0 1 auto;min-width:0;width:max-content;max-width:100%;padding:var(--dimension-space-075);background-color:transparent;border-radius:var(--dimension-radius-025);cursor:pointer;-webkit-user-select:none;user-select:none;color:var(--_bar-nav-fg-primary);position:relative;transition:color var(--effect-motion-short-2);height:var(--dimension-size-400);--_hover-bg:var(--_bar-nav-hover);--_pressed-bg:var(--_bar-nav-pressed);--_dot:var(--_bar-nav-dot);--_dot-ring:var(--_bar-nav-bg)}.bar-nav__tab-trigger.sc-ds-bar-nav::after{content:'';position:absolute;inset:0;border-radius:var(--dimension-radius-025);background:transparent;pointer-events:none;transition:none;z-index:2}.bar-nav__tab-trigger.sc-ds-bar-nav:hover::after{background:var(--_hover-bg);transition:none}.bar-nav__tab-trigger.sc-ds-bar-nav:active::after{background:var(--_pressed-bg);transition:none}.bar-nav__tab-trigger.sc-ds-bar-nav:focus-visible{outline:var(--dimension-stroke-width-025) solid var(--_bar-nav-focus);outline-offset:var(--dimension-space-025)}.bar-nav__tab-trigger--open.sc-ds-bar-nav::after{background:var(--_hover-bg)}.bar-nav__tab-trigger-label.sc-ds-bar-nav{position:relative;display:inline-flex;align-items:center;flex:0 1 auto;min-width:0;max-width:100%;padding:0 var(--dimension-space-025);overflow:hidden;--_fade-right:var(--dimension-space-025);z-index:1}.bar-nav__tab-trigger-label--truncated.sc-ds-bar-nav::after{content:'';position:absolute;top:0;right:var(--_fade-right);bottom:0;width:var(--dimension-size-400);background:linear-gradient(to right, transparent, var(--_bar-nav-bg));pointer-events:none;z-index:1}.bar-nav__tab-trigger-label-text.sc-ds-bar-nav{display:inline-block;max-width:100%;box-sizing:border-box;white-space:nowrap;overflow:hidden;vertical-align:bottom}.bar-nav__tab-trigger-label-text--truncated.sc-ds-bar-nav{-webkit-mask-image:linear-gradient( to right, #000 0, #000 calc(100% - var(--dimension-size-400)), transparent 100% );mask-image:linear-gradient( to right, #000 0, #000 calc(100% - var(--dimension-size-400)), transparent 100% );-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:100% 100%;mask-size:100% 100%}.bar-nav__tab-trigger-label--dot.sc-ds-bar-nav{padding-right:calc(var(--dimension-space-025) + var(--dimension-space-050));--_fade-right:calc(var(--dimension-space-025) + var(--dimension-space-050))}.bar-nav__tab-trigger-dot.sc-ds-bar-nav{position:absolute;top:0;right:0;pointer-events:none;z-index:1}.bar-nav__tab-trigger-chevron.sc-ds-bar-nav{position:relative;flex-shrink:0;z-index:1}.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(--_bar-nav-fg-primary);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;-webkit-user-select:none;user-select:none}.bar-nav__actions.sc-ds-bar-nav{display:flex;align-items:center;gap:var(--dimension-space-050);flex:0 0 auto;margin-left:auto}`;const S=s(class s extends t{constructor(s){super();if(s!==false){this.__registerHost()}this.dsTabChange=a(this,"dsTabChange");this.dsActionChange=a(this,"dsActionChange");this.navStyle="dashboard";this.tabs=[];this.tabsJson="";this.value="";this.actions=[];this.actionsJson="";this.basePath="";this.currentUrl="";this.resolvedTabs=[];this.resolvedActions=[];this.urlDerivedValue="";this.hideTabsForDetailRoute=false;this.tabsCollapsed=false;this.menuOpen=false;this.tabLayoutCommitted=false;this.triggerLabelTruncated=false;this.headerEl=null;this.triggerEl=null;this.triggerLabelTextEl=null;this.triggerLabelObserver=null;this.menuEl=null;this.visibleTabGroupEl=null;this.probeTabGroupEl=null;this.resizeObserver=null;this.intrinsicWidthRetryCount=0;this.panelNavTransition=new c;this.panelToolsTransition=new c;this.overflowCoalescer=l((()=>{this.updateTabsCollapsed();this.updateTriggerLabelTruncation()}));this.chromeTransitionShell=null;this.committedSection="";this.pendingSection="";this.onChromeTransitionStart=s=>{const t=u(s);if(t==="panel-nav"){this.panelNavTransition.enter();return}if(t==="panel-tools"){const t=g(s)??"opening";if(t==="closing"){this.panelToolsTransition.enter()}}};this.onChromeTransitionEnd=s=>{const t=u(s);if(t==="panel-nav"){this.panelNavTransition.exit();if(!this.panelNavTransition.isActive){this.scheduleOverflowCheck()}return}if(t==="panel-tools"){this.panelToolsTransition.exit();this.scheduleOverflowCheck()}}}get effectiveValue(){if(this.currentUrl&&this.basePath){return this.urlDerivedValue}return this.value}get activeTabLabel(){return z(this.resolvedTabs,this.effectiveValue)}get activeTabHasDot(){return!!T(this.resolvedTabs,this.effectiveValue)?.dot}digestTabsConfig(s){return s.map((s=>o(s)?"|":s.id)).join(",")}resetTabOverflowLayout(){this.tabLayoutCommitted=false;this.tabsCollapsed=false;this.menuOpen=false;this.intrinsicWidthRetryCount=0}onHostPropsChange(){this.applyHostProps()}onSelectionChange(){this.syncMenuSections();this.scheduleOverflowCheck()}onMenuOpenChange(s){if(this.menuEl){this.menuEl.open=s}}onTabsCollapsedChange(s,t){if(!s){this.menuOpen=false;this.menuEl=null;this.triggerLabelTruncated=false;this.syncTriggerLabelObserver()}this.scheduleOverflowCheck();if(s){requestAnimationFrame((()=>{this.scheduleOverflowCheck();this.syncTriggerLabelObserver()}))}if(s||t===undefined||t===s)return;requestAnimationFrame((()=>this.scheduleOverflowCheck()));queueMicrotask((()=>this.focusVisibleSelectedTab()))}componentWillLoad(){this.applyHostProps()}componentDidLoad(){this.syncHostPropsIfNeeded();this.scheduleDeferredHostPropSync();this.setupOverflowObserver();this.bindChromeTransitionListeners();this.scheduleOverflowCheck()}componentDidRender(){if(this.chromeOverflowPaused()){this.syncMenuAnchor();return}this.scheduleOverflowCheck();this.syncMenuAnchor();if(this.tabsCollapsed){this.updateTriggerLabelTruncation()}}disconnectedCallback(){this.resizeObserver?.disconnect();this.resizeObserver=null;this.overflowCoalescer.cancel();this.syncTriggerLabelObserver(true);this.unbindChromeTransitionListeners()}bindChromeTransitionListeners(){const s=this.el.closest("ds-app-shell");if(!s)return;this.chromeTransitionShell=s;s.addEventListener(v,this.onChromeTransitionStart);s.addEventListener(f,this.onChromeTransitionEnd)}unbindChromeTransitionListeners(){if(!this.chromeTransitionShell)return;this.chromeTransitionShell.removeEventListener(v,this.onChromeTransitionStart);this.chromeTransitionShell.removeEventListener(f,this.onChromeTransitionEnd);this.chromeTransitionShell=null}chromeOverflowPaused(){return this.panelNavTransition.isActive||this.panelToolsTransition.isActive}syncHostPropsIfNeeded(){if(h(this.resolvedTabs,this.tabs,this.tabsJson,this.resolvedActions,this.actions,this.actionsJson)){this.applyHostProps()}else if(this.currentUrl&&this.basePath){this.syncValueFromUrl()}}scheduleDeferredHostPropSync(){let t=s.HOST_PROP_SYNC_BUDGET;const a=()=>{this.syncHostPropsIfNeeded();if(--t>0){requestAnimationFrame(a)}};queueMicrotask(a)}setupOverflowObserver(){if(typeof ResizeObserver==="undefined"||!this.headerEl)return;this.resizeObserver?.disconnect();this.resizeObserver=new ResizeObserver((()=>{if(this.chromeOverflowPaused())return;this.scheduleOverflowCheck()}));this.resizeObserver.observe(this.headerEl)}scheduleOverflowCheck(){if(this.chromeOverflowPaused())return;this.overflowCoalescer.schedule()}syncTriggerLabelObserver(s=false){this.triggerLabelObserver?.disconnect();this.triggerLabelObserver=null;if(s||!this.tabsCollapsed||!this.triggerLabelTextEl){return}this.updateTriggerLabelTruncation();if(typeof ResizeObserver==="undefined")return;this.triggerLabelObserver=new ResizeObserver((()=>this.updateTriggerLabelTruncation()));this.triggerLabelObserver.observe(this.triggerLabelTextEl);if(this.triggerEl){this.triggerLabelObserver.observe(this.triggerEl)}}updateTriggerLabelTruncation(){const s=this.triggerLabelTextEl;if(!s||!this.tabsCollapsed){if(this.triggerLabelTruncated){this.triggerLabelTruncated=false}return}const t=s.scrollWidth>s.clientWidth+1;if(t!==this.triggerLabelTruncated){this.triggerLabelTruncated=t}}getLeftZoneAvailableWidth(){if(!this.headerEl)return 0;const s=getComputedStyle(this.headerEl);const t=parseFloat(s.columnGap||s.gap||"0")||0;const a=this.headerEl.querySelector(".bar-nav__actions");const e=a?.offsetWidth??0;const i=(parseFloat(s.paddingLeft)||0)+(parseFloat(s.paddingRight)||0);const n=this.tabsCollapsed?parseFloat(getComputedStyle(this.headerEl).getPropertyValue("--dimension-space-100").trim())||8:a?t:0;const r=this.headerEl.clientWidth-i-e-n;return Math.max(0,r)}getTabsIntrinsicWidth(){const s=x(this.probeTabGroupEl);return s?.scrollWidth??0}scheduleIntrinsicWidthRetry(){if(this.intrinsicWidthRetryCount>=s.INTRINSIC_WIDTH_RETRY_MAX)return;this.intrinsicWidthRetryCount++;requestAnimationFrame((()=>this.scheduleOverflowCheck()))}updateTabsCollapsed(){if(!this.headerEl||this.resolvedTabs.length===0||this.hideTabsForDetailRoute){if(this.tabsCollapsed){this.tabsCollapsed=false;this.menuOpen=false}this.intrinsicWidthRetryCount=0;this.tabLayoutCommitted=true;return}const t=this.getTabsIntrinsicWidth();if(t===0){if(this.intrinsicWidthRetryCount>=s.INTRINSIC_WIDTH_RETRY_MAX){this.intrinsicWidthRetryCount=0;this.tabsCollapsed=false;this.tabLayoutCommitted=true}else{this.scheduleIntrinsicWidthRetry()}return}this.intrinsicWidthRetryCount=0;const a=D(t,this.getLeftZoneAvailableWidth(),this.tabsCollapsed,s.OVERFLOW_HYSTERESIS_PX);if(a!==this.tabsCollapsed){this.tabsCollapsed=a;if(!a){this.menuOpen=false}}this.tabLayoutCommitted=true}syncMenuAnchor(){if(!this.menuEl||!this.triggerEl||!this.tabsCollapsed)return;this.menuEl.anchor=this.triggerEl;this.syncMenuSections()}handleMenuSelect(s){const t=s.detail;if(t?.value!==undefined){this.selectTab(String(t.value))}this.menuOpen=false}handleMenuClose(){this.menuOpen=false}incomingTabs(){return this.tabsJson?d(this.tabsJson,[]):this.tabs??[]}incomingActions(){return this.actionsJson?d(this.actionsJson,[]):this.actions??[]}applyHostProps(){const s=this.basePath;const t=s!==""&&this.committedSection!==""&&s!==this.committedSection;if(t){this.urlDerivedValue="";this.committedSection="";this.pendingSection=s;this.resolvedTabs=[];this.resolvedActions=this.incomingActions();this.resetTabOverflowLayout();return}if(this.pendingSection){if(!s||s!==this.pendingSection){return}if(!this.currentUrl.startsWith(this.pendingSection)){return}const t=this.incomingTabs();if(t.length===0){return}this.pendingSection=""}const a=this.incomingTabs();const e=this.digestTabsConfig(a)!==this.digestTabsConfig(this.resolvedTabs);this.resolvedTabs=a;this.resolvedActions=this.incomingActions();this.syncValueFromUrl();this.committedSection=s;if(e){this.resetTabOverflowLayout()}this.scheduleOverflowCheck()}syncMenuSections(){if(!this.menuEl)return;this.menuEl.sections=C(this.resolvedTabs,this.effectiveValue)}syncValueFromUrl(){const s=this.incomingTabs();if(!this.currentUrl||!this.basePath){this.urlDerivedValue="";this.hideTabsForDetailRoute=false;return}if(!this.currentUrl.startsWith(this.basePath)){this.urlDerivedValue="";this.hideTabsForDetailRoute=false;return}const{value:t,hideTabs:a}=b(this.currentUrl,this.basePath,s);this.urlDerivedValue=t;this.hideTabsForDetailRoute=a}selectTab(s){this.value=s;this.dsTabChange.emit(s)}focusVisibleSelectedTab(){const s=k(this.visibleTabGroupEl,`[data-tab-id="${this.effectiveValue}"]`);s?.focus({preventScroll:true})}handleTabChange(s){this.selectTab(s.detail)}handleActionChange(s,t){const a=t.detail;this.dsActionChange.emit({id:s,selected:a})}toggleTabMenu(){if(this.menuOpen){this.menuOpen=false;return}if(this.menuEl&&this.triggerEl){this.menuEl.anchor=this.triggerEl;this.menuEl.minWidth=`${this.triggerEl.offsetWidth}px`;this.syncMenuSections()}this.menuOpen=true}handleTriggerKeyDown(s){if(!this.tabsCollapsed)return;if(!this.menuOpen){if(s.key==="ArrowDown"||s.key==="ArrowUp"||s.key==="Enter"||s.key===" "){s.preventDefault();this.toggleTabMenu()}return}if(s.key==="Escape"){s.preventDefault();this.menuOpen=false;this.triggerEl?.focus()}}render(){const s=this.basePath!==""&&this.committedSection===this.basePath;const t=s&&this.resolvedTabs.length>0&&!this.hideTabsForDetailRoute;const a=this.basePath||"no-section";return e(i,{key:"0fe9ad4dce337f60816d83e2fa4b429b8ba2abc2"},e("header",{key:"93e47d7fc8904ed497fb46e22207b500cca1d21a",class:{"bar-nav":true,"bar-nav--dashboard":this.navStyle==="dashboard","bar-nav--settings":this.navStyle==="settings","bar-nav--tabs-collapsed":t&&this.tabLayoutCommitted&&this.tabsCollapsed},ref:s=>{this.headerEl=s;if(s&&!this.resizeObserver){this.setupOverflowObserver()}}},t&&e("div",{key:"9c0f45979dceedcc723fad6ec56ea9f1e32f8b91",class:"bar-nav__tabs-probe","aria-hidden":"true",inert:true},e("ds-tab-group-nav",{key:`probe-${a}`,ref:s=>{this.probeTabGroupEl=s??null;if(s){this.intrinsicWidthRetryCount=0;this.scheduleOverflowCheck()}},tabs:this.resolvedTabs,value:this.effectiveValue})),t&&this.tabLayoutCommitted&&!this.tabsCollapsed&&e("div",{key:"f44fdc5851fefa0808b8190ce60105d2065aa4a3",class:"bar-nav__left"},e("ds-tab-group-nav",{key:`visible-${a}`,class:"bar-nav__tabs-visible",ref:s=>{this.visibleTabGroupEl=s??null},tabs:this.resolvedTabs,value:this.effectiveValue,onDsChange:s=>this.handleTabChange(s)})),t&&!this.tabLayoutCommitted&&e("div",{key:"c61a579abbaccc9f78c6f576499f96adfff77c4d",class:"bar-nav__tabs-pending","aria-hidden":"true"}),!t&&this.heading&&e("span",{key:"36fa3f24992cc5d9aadfbd6f1e4097260d163fdb",class:"bar-nav__heading text-body-medium-emphasis"},this.heading),t&&this.tabLayoutCommitted&&this.tabsCollapsed&&e("button",{key:"6a5a2949ab5d165b6b2fa5deb3dfa27db6eae6ec",type:"button",class:{"bar-nav__tab-trigger":true,"bar-nav__tab-trigger--open":this.menuOpen},ref:s=>{this.triggerEl=s},"aria-haspopup":"menu","aria-expanded":this.menuOpen,"aria-label":`${this.activeTabLabel} tabs`,onClick:()=>this.toggleTabMenu(),onKeyDown:s=>this.handleTriggerKeyDown(s)},e("span",{key:"307c2cbe34c2e2b53c8d8f8dbe7a542d70fad6a0",class:{"bar-nav__tab-trigger-label":true,"bar-nav__tab-trigger-label--dot":this.activeTabHasDot,"bar-nav__tab-trigger-label--truncated":this.triggerLabelTruncated}},e("span",{key:"41f8374cacca208f39b0b56f2f4f3842aca07615",class:{"bar-nav__tab-trigger-label-text":true,"bar-nav__tab-trigger-label-text--truncated":this.triggerLabelTruncated,"text-body-medium-emphasis":true},ref:s=>{const t=s??null;if(t===this.triggerLabelTextEl)return;this.triggerLabelTextEl=t;if(this.tabsCollapsed){this.syncTriggerLabelObserver()}}},this.activeTabLabel),this.activeTabHasDot&&e("ds-badge",{key:"d33c8c4e9d67bebe6e308558b60ed09be055e1fa",class:"bar-nav__tab-trigger-dot",variant:"dot",background:"var(--_bar-nav-bg)",label:"","aria-hidden":"true"})),e("ds-icon",{key:"cc0e5de1f9d733fbf6baaa7c0427cd8289816532",name:"ChevronDown",size:"md",color:"inherit",class:"bar-nav__tab-trigger-chevron"})),t&&this.tabLayoutCommitted&&this.tabsCollapsed&&this.resolvedActions.length>0&&e("div",{key:"7b887750b91ee98f820e91730f6e667a3726a106",class:"bar-nav__between","aria-hidden":"true"}),this.resolvedActions.length>0&&e("div",{key:"e6ca99bf0869cef1718a0d090d3004fb884be917",class:"bar-nav__actions"},this.resolvedActions.map((s=>e("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,onDsChange:t=>this.handleActionChange(s.id,t)}))))),t&&this.tabLayoutCommitted&&this.tabsCollapsed&&e("ds-menu",{key:"84dda1ba294255a06356577a8480f11502dd84b3",ref:s=>{this.menuEl=s??null},open:this.menuOpen,sections:C(this.resolvedTabs,this.effectiveValue),side:"bottom",align:"start",onDsSelect:s=>this.handleMenuSelect(s),onDsClose:()=>this.handleMenuClose()}))}get el(){return this}static get watchers(){return{tabs:[{onHostPropsChange:0}],tabsJson:[{onHostPropsChange:0}],actions:[{onHostPropsChange:0}],actionsJson:[{onHostPropsChange:0}],currentUrl:[{onHostPropsChange:0}],basePath:[{onHostPropsChange:0}],value:[{onSelectionChange:0}],urlDerivedValue:[{onSelectionChange:0}],menuOpen:[{onMenuOpenChange:0}],tabsCollapsed:[{onTabsCollapsedChange:0}]}}static get style(){return P()}},[2,"ds-bar-nav",{navStyle:[513,"nav-style"],tabs:[16],tabsJson:[1,"tabs-json"],value:[1025],actions:[16],actionsJson:[1,"actions-json"],heading:[1],basePath:[1,"base-path"],currentUrl:[1,"current-url"],resolvedTabs:[32],resolvedActions:[32],urlDerivedValue:[32],hideTabsForDetailRoute:[32],tabsCollapsed:[32],menuOpen:[32],tabLayoutCommitted:[32],triggerLabelTruncated:[32],committedSection:[32],pendingSection:[32]},undefined,{tabs:[{onHostPropsChange:0}],tabsJson:[{onHostPropsChange:0}],actions:[{onHostPropsChange:0}],actionsJson:[{onHostPropsChange:0}],currentUrl:[{onHostPropsChange:0}],basePath:[{onHostPropsChange:0}],value:[{onSelectionChange:0}],urlDerivedValue:[{onSelectionChange:0}],menuOpen:[{onMenuOpenChange:0}],tabsCollapsed:[{onTabsCollapsedChange:0}]}]);S.HOST_PROP_SYNC_BUDGET=8;S.INTRINSIC_WIDTH_RETRY_MAX=3;S.OVERFLOW_HYSTERESIS_PX=8;function H(){if(typeof customElements==="undefined"){return}const s=["ds-bar-nav","ds-badge","ds-bar-nav-action","ds-icon","ds-menu","ds-tab-group-nav"];s.forEach((s=>{switch(s){case"ds-bar-nav":if(!customElements.get(n(s))){customElements.define(n(s),S)}break;case"ds-badge":if(!customElements.get(n(s))){p()}break;case"ds-bar-nav-action":if(!customElements.get(n(s))){m()}break;case"ds-icon":if(!customElements.get(n(s))){_()}break;case"ds-menu":if(!customElements.get(n(s))){y()}break;case"ds-tab-group-nav":if(!customElements.get(n(s))){w()}break}}))}H();const O=S;const F=H;export{O as DsBarNav,F as defineCustomElement};
|
|
1
|
+
import{p as s,H as t,c as e,h as a,a as i,t as n}from"./index.js";import{g as r,i as o}from"./p-BQq26pt9.js";import{s as h,p as d,d as b}from"./p-Dl1GvZQ2.js";import{C as l,c,a as v,b as f,r as u,d as g}from"./p-BiM6QBTF.js";import{d as p}from"./p-CiEc8Nwx.js";import{d as m}from"./p-CCZFMcLe.js";import{d as _}from"./p-DqBCQRRd.js";import{d as y}from"./p-CuuuDjJJ.js";import{d as w}from"./p-DlsexsFg.js";function k(s,t){if(!s)return null;const e=s.shadowRoot??s;return e.querySelector(t)}function C(s){return k(s,'[role="tablist"]')}function x(s,t){const e=[];let a=[];for(const i of s){if(o(i)){if(a.length>0){e.push({items:a});a=[]}continue}a.push({label:i.label,value:i.id,isSelected:i.id===t,isInactive:i.disabled})}if(a.length>0){e.push({items:a})}return e}function T(s,t){const e=r(s);return e.find((s=>s.id===t))??e[0]}function z(s,t){return T(s,t)?.label??"Tabs"}function D(s,t,e,a=2){const i=s-t;if(e){return i>-a}return i>a}const P=()=>`.sc-ds-bar-nav-h{display:block}.bar-nav.sc-ds-bar-nav{display:flex;align-items:center;position:relative;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(--_bar-nav-border);background-color:var(--_bar-nav-bg);color:var(--_bar-nav-fg-primary);-webkit-user-select:none;user-select:none;overflow:hidden;transition:background-color var(--effect-motion-short-3), border-color var(--effect-motion-short-3), color var(--effect-motion-short-3);isolation:isolate;--_bar-nav-bg:var(--color-background-secondary);--_bar-nav-border:var(--color-border-tertiary);--_bar-nav-fg-primary:var(--color-foreground-primary);--_bar-nav-fg-secondary:var(--color-foreground-secondary);--_bar-nav-hover:var(--color-interaction-hover);--_bar-nav-pressed:var(--color-interaction-pressed);--_bar-nav-focus:var(--color-interaction-focus);--_bar-nav-dot:var(--color-foreground-bold-brand)}.bar-nav--dashboard.sc-ds-bar-nav,.bar-nav--settings.sc-ds-bar-nav{--_bar-nav-bg:var(--color-background-secondary);--_bar-nav-border:var(--color-border-tertiary);--_bar-nav-fg-primary:var(--color-foreground-primary);--_bar-nav-fg-secondary:var(--color-foreground-secondary);--_bar-nav-hover:var(--color-interaction-hover);--_bar-nav-pressed:var(--color-interaction-pressed);--_bar-nav-focus:var(--color-interaction-focus);--_bar-nav-dot:var(--color-foreground-bold-brand)}ds-app-shell[gradient].sc-ds-bar-nav-h .bar-nav.sc-ds-bar-nav,ds-app-shell[gradient] .sc-ds-bar-nav-h .bar-nav.sc-ds-bar-nav,ds-app-shell[grid].sc-ds-bar-nav-h .bar-nav.sc-ds-bar-nav,ds-app-shell[grid] .sc-ds-bar-nav-h .bar-nav.sc-ds-bar-nav{background-color:transparent}.bar-nav.sc-ds-bar-nav>.sc-ds-bar-nav:not(.bar-nav__tabs-probe){position:relative;z-index:3}.bar-nav--tabs-collapsed.sc-ds-bar-nav{display:flex;gap:0;align-items:center}.bar-nav--tabs-collapsed.sc-ds-bar-nav .bar-nav__tab-trigger.sc-ds-bar-nav{flex:0 1 auto;min-width:0;max-width:100%;width:auto;overflow:hidden}.bar-nav__between.sc-ds-bar-nav{flex:1 1 0%;min-width:var(--dimension-space-100)}.bar-nav--tabs-collapsed.sc-ds-bar-nav .bar-nav__tab-trigger-label.sc-ds-bar-nav{flex:1 1 0%;min-width:0}.bar-nav--tabs-collapsed.sc-ds-bar-nav .bar-nav__tab-trigger-label-text.sc-ds-bar-nav{display:block;flex:1 1 0%;min-width:0}.bar-nav--tabs-collapsed.sc-ds-bar-nav .bar-nav__actions.sc-ds-bar-nav{flex:0 0 auto;margin-left:0}.bar-nav__left.sc-ds-bar-nav{position:relative;flex:1 1 0%;min-width:0;width:0;display:flex;align-items:center}.bar-nav__left.sc-ds-bar-nav ds-tab-group-nav.sc-ds-bar-nav{flex:1;min-width:0}.bar-nav__tabs-probe.sc-ds-bar-nav{position:absolute;left:0;top:0;visibility:hidden;pointer-events:none;overflow:visible;width:max-content;max-width:none;z-index:-1}.bar-nav__tabs-probe.sc-ds-bar-nav ds-tab-group-nav.sc-ds-bar-nav{width:max-content}.bar-nav__tabs-visible.sc-ds-bar-nav{width:100%;min-width:0}.bar-nav__tabs-pending.sc-ds-bar-nav{flex:1;min-width:0;height:var(--dimension-size-400);visibility:hidden;pointer-events:none}.bar-nav__tab-trigger.sc-ds-bar-nav{all:unset;box-sizing:border-box;display:inline-flex;align-items:center;gap:var(--dimension-space-050);flex:0 1 auto;min-width:0;width:max-content;max-width:100%;padding:var(--dimension-space-075);background-color:transparent;border-radius:var(--dimension-radius-025);cursor:pointer;-webkit-user-select:none;user-select:none;color:var(--_bar-nav-fg-primary);position:relative;transition:color var(--effect-motion-short-2);height:var(--dimension-size-400);--_hover-bg:var(--_bar-nav-hover);--_pressed-bg:var(--_bar-nav-pressed);--_dot:var(--_bar-nav-dot);--_dot-ring:var(--_bar-nav-bg)}.bar-nav__tab-trigger.sc-ds-bar-nav::after{content:'';position:absolute;inset:0;border-radius:var(--dimension-radius-025);background:transparent;pointer-events:none;transition:none;z-index:2}.bar-nav__tab-trigger.sc-ds-bar-nav:hover::after{background:var(--_hover-bg);transition:none}.bar-nav__tab-trigger.sc-ds-bar-nav:active::after{background:var(--_pressed-bg);transition:none}.bar-nav__tab-trigger.sc-ds-bar-nav:focus-visible{outline:var(--dimension-stroke-width-025) solid var(--_bar-nav-focus);outline-offset:var(--dimension-space-025)}.bar-nav__tab-trigger--open.sc-ds-bar-nav::after{background:var(--_hover-bg)}.bar-nav__tab-trigger-label.sc-ds-bar-nav{position:relative;display:inline-flex;align-items:center;flex:0 1 auto;min-width:0;max-width:100%;padding:0 var(--dimension-space-025);overflow:hidden;--_fade-right:var(--dimension-space-025);z-index:1}.bar-nav__tab-trigger-label--truncated.sc-ds-bar-nav::after{content:'';position:absolute;top:0;right:var(--_fade-right);bottom:0;width:var(--dimension-size-400);background:linear-gradient(to right, transparent, var(--_bar-nav-bg));pointer-events:none;z-index:1}.bar-nav__tab-trigger-label-text.sc-ds-bar-nav{display:inline-block;max-width:100%;box-sizing:border-box;white-space:nowrap;overflow:hidden;vertical-align:bottom}.bar-nav__tab-trigger-label-text--truncated.sc-ds-bar-nav{-webkit-mask-image:linear-gradient( to right, #000 0, #000 calc(100% - var(--dimension-size-400)), transparent 100% );mask-image:linear-gradient( to right, #000 0, #000 calc(100% - var(--dimension-size-400)), transparent 100% );-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:100% 100%;mask-size:100% 100%}.bar-nav__tab-trigger-label--dot.sc-ds-bar-nav{padding-right:calc(var(--dimension-space-025) + var(--dimension-space-050));--_fade-right:calc(var(--dimension-space-025) + var(--dimension-space-050))}.bar-nav__tab-trigger-dot.sc-ds-bar-nav{position:absolute;top:0;right:0;pointer-events:none;z-index:1}.bar-nav__tab-trigger-chevron.sc-ds-bar-nav{position:relative;flex-shrink:0;z-index:1}.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(--_bar-nav-fg-primary);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;-webkit-user-select:none;user-select:none}.bar-nav__actions.sc-ds-bar-nav{display:flex;align-items:center;gap:var(--dimension-space-050);flex:0 0 auto;margin-left:auto}`;const S=s(class s extends t{constructor(s){super();if(s!==false){this.__registerHost()}this.dsTabChange=e(this,"dsTabChange");this.dsActionChange=e(this,"dsActionChange");this.navStyle="dashboard";this.tabs=[];this.tabsJson="";this.value="";this.actions=[];this.actionsJson="";this.basePath="";this.currentUrl="";this.resolvedTabs=[];this.resolvedActions=[];this.urlDerivedValue="";this.hideTabsForDetailRoute=false;this.tabsCollapsed=false;this.menuOpen=false;this.tabLayoutCommitted=false;this.triggerLabelTruncated=false;this.headerEl=null;this.triggerEl=null;this.triggerLabelTextEl=null;this.triggerLabelObserver=null;this.menuEl=null;this.visibleTabGroupEl=null;this.probeTabGroupEl=null;this.resizeObserver=null;this.intrinsicWidthRetryCount=0;this.tabLayoutPendingFrames=0;this.panelNavTransition=new l;this.panelToolsTransition=new l;this.overflowCoalescer=c((()=>{this.updateTabsCollapsed();this.updateTriggerLabelTruncation()}));this.chromeTransitionShell=null;this.committedSection="";this.pendingSection="";this.onChromeTransitionStart=s=>{const t=u(s);if(t==="panel-nav"){this.panelNavTransition.enter();return}if(t==="panel-tools"){const t=g(s)??"opening";if(t==="closing"){this.panelToolsTransition.enter()}}};this.onChromeTransitionEnd=s=>{const t=u(s);if(t==="panel-nav"){this.panelNavTransition.exit();if(!this.panelNavTransition.isActive){this.scheduleOverflowCheck()}return}if(t==="panel-tools"){this.panelToolsTransition.exit();this.scheduleOverflowCheck()}}}get effectiveValue(){if(this.currentUrl&&this.basePath){return this.urlDerivedValue}return this.value}get activeTabLabel(){return z(this.resolvedTabs,this.effectiveValue)}get activeTabHasDot(){return!!T(this.resolvedTabs,this.effectiveValue)?.dot}digestTabsConfig(s){return s.map((s=>o(s)?"|":s.id)).join(",")}resetTabOverflowLayout(){this.tabLayoutCommitted=false;this.tabsCollapsed=false;this.menuOpen=false;this.intrinsicWidthRetryCount=0;this.tabLayoutPendingFrames=0}onHostPropsChange(){this.applyHostProps()}onSelectionChange(){this.syncMenuSections();this.scheduleOverflowCheck()}onMenuOpenChange(s){if(this.menuEl){this.menuEl.open=s}}onTabsCollapsedChange(s,t){if(!s){this.menuOpen=false;this.menuEl=null;this.triggerLabelTruncated=false;this.syncTriggerLabelObserver()}this.scheduleOverflowCheck();if(s){requestAnimationFrame((()=>{this.scheduleOverflowCheck();this.syncTriggerLabelObserver()}))}if(s||t===undefined||t===s)return;requestAnimationFrame((()=>this.scheduleOverflowCheck()));queueMicrotask((()=>this.focusVisibleSelectedTab()))}componentWillLoad(){this.applyHostProps()}componentDidLoad(){this.syncHostPropsIfNeeded();this.scheduleDeferredHostPropSync();this.setupOverflowObserver();this.bindChromeTransitionListeners();this.scheduleOverflowCheck()}componentDidRender(){if(this.chromeOverflowPaused()){this.syncMenuAnchor();return}this.scheduleOverflowCheck();this.syncMenuAnchor();if(this.tabsCollapsed){this.updateTriggerLabelTruncation()}}disconnectedCallback(){this.resizeObserver?.disconnect();this.resizeObserver=null;this.overflowCoalescer.cancel();this.syncTriggerLabelObserver(true);this.unbindChromeTransitionListeners()}bindChromeTransitionListeners(){const s=this.el.closest("ds-app-shell");if(!s)return;this.chromeTransitionShell=s;s.addEventListener(v,this.onChromeTransitionStart);s.addEventListener(f,this.onChromeTransitionEnd)}unbindChromeTransitionListeners(){if(!this.chromeTransitionShell)return;this.chromeTransitionShell.removeEventListener(v,this.onChromeTransitionStart);this.chromeTransitionShell.removeEventListener(f,this.onChromeTransitionEnd);this.chromeTransitionShell=null}chromeOverflowPaused(){return this.panelNavTransition.isActive||this.panelToolsTransition.isActive}syncHostPropsIfNeeded(){if(h(this.resolvedTabs,this.tabs,this.tabsJson,this.resolvedActions,this.actions,this.actionsJson)){this.applyHostProps()}else if(this.currentUrl&&this.basePath){this.syncValueFromUrl()}}scheduleDeferredHostPropSync(){let t=s.HOST_PROP_SYNC_BUDGET;const e=()=>{this.syncHostPropsIfNeeded();if(--t>0){requestAnimationFrame(e)}};queueMicrotask(e)}setupOverflowObserver(){if(typeof ResizeObserver==="undefined"||!this.headerEl)return;this.resizeObserver?.disconnect();this.resizeObserver=new ResizeObserver((()=>{if(this.chromeOverflowPaused())return;this.scheduleOverflowCheck()}));this.resizeObserver.observe(this.headerEl)}scheduleOverflowCheck(){if(this.chromeOverflowPaused())return;this.overflowCoalescer.schedule()}syncTriggerLabelObserver(s=false){this.triggerLabelObserver?.disconnect();this.triggerLabelObserver=null;if(s||!this.tabsCollapsed||!this.triggerLabelTextEl){return}this.updateTriggerLabelTruncation();if(typeof ResizeObserver==="undefined")return;this.triggerLabelObserver=new ResizeObserver((()=>this.updateTriggerLabelTruncation()));this.triggerLabelObserver.observe(this.triggerLabelTextEl);if(this.triggerEl){this.triggerLabelObserver.observe(this.triggerEl)}}updateTriggerLabelTruncation(){const s=this.triggerLabelTextEl;if(!s||!this.tabsCollapsed){if(this.triggerLabelTruncated){this.triggerLabelTruncated=false}return}const t=s.scrollWidth>s.clientWidth+1;if(t!==this.triggerLabelTruncated){this.triggerLabelTruncated=t}}getLeftZoneAvailableWidth(){if(!this.headerEl)return 0;const s=getComputedStyle(this.headerEl);const t=parseFloat(s.columnGap||s.gap||"0")||0;const e=this.headerEl.querySelector(".bar-nav__actions");const a=e?.offsetWidth??0;const i=(parseFloat(s.paddingLeft)||0)+(parseFloat(s.paddingRight)||0);const n=this.tabsCollapsed?parseFloat(getComputedStyle(this.headerEl).getPropertyValue("--dimension-space-100").trim())||8:e?t:0;const r=this.headerEl.clientWidth-i-a-n;return Math.max(0,r)}getTabsIntrinsicWidth(){const s=C(this.probeTabGroupEl);return s?.scrollWidth??0}scheduleIntrinsicWidthRetry(){if(this.intrinsicWidthRetryCount>=s.INTRINSIC_WIDTH_RETRY_MAX)return;this.intrinsicWidthRetryCount++;requestAnimationFrame((()=>this.scheduleOverflowCheck()))}forceTabLayoutCommit(s){this.intrinsicWidthRetryCount=0;this.tabLayoutPendingFrames=0;this.tabsCollapsed=s;if(!s){this.menuOpen=false}this.tabLayoutCommitted=true}updateTabsCollapsed(){if(!this.headerEl||this.resolvedTabs.length===0||this.hideTabsForDetailRoute){if(this.tabsCollapsed){this.tabsCollapsed=false;this.menuOpen=false}this.intrinsicWidthRetryCount=0;this.tabLayoutPendingFrames=0;this.tabLayoutCommitted=true;return}const t=this.getTabsIntrinsicWidth();if(t===0){this.tabLayoutPendingFrames+=1;if(this.intrinsicWidthRetryCount>=s.INTRINSIC_WIDTH_RETRY_MAX){this.forceTabLayoutCommit(false)}else if(this.tabLayoutPendingFrames>=s.TAB_LAYOUT_COMMIT_MAX_FRAMES){this.forceTabLayoutCommit(false)}else{this.scheduleIntrinsicWidthRetry()}return}this.intrinsicWidthRetryCount=0;this.tabLayoutPendingFrames=0;const e=D(t,this.getLeftZoneAvailableWidth(),this.tabsCollapsed,s.OVERFLOW_HYSTERESIS_PX);if(e!==this.tabsCollapsed){this.tabsCollapsed=e;if(!e){this.menuOpen=false}}this.tabLayoutCommitted=true}syncMenuAnchor(){if(!this.menuEl||!this.triggerEl||!this.tabsCollapsed)return;this.menuEl.anchor=this.triggerEl;this.syncMenuSections()}handleMenuSelect(s){const t=s.detail;if(t?.value!==undefined){this.selectTab(String(t.value))}this.menuOpen=false}handleMenuClose(){this.menuOpen=false}incomingTabs(){return this.tabsJson?d(this.tabsJson,[]):this.tabs??[]}incomingActions(){return this.actionsJson?d(this.actionsJson,[]):this.actions??[]}applyHostProps(){const s=this.basePath;const t=s!==""&&this.committedSection!==""&&s!==this.committedSection;if(t){this.urlDerivedValue="";this.committedSection="";this.pendingSection=s;this.resolvedTabs=[];this.resolvedActions=this.incomingActions();this.resetTabOverflowLayout();return}if(this.pendingSection){if(!s||s!==this.pendingSection){return}if(!this.currentUrl.startsWith(this.pendingSection)){return}const t=this.incomingTabs();if(t.length===0){return}this.pendingSection=""}const e=this.incomingTabs();const a=this.digestTabsConfig(e)!==this.digestTabsConfig(this.resolvedTabs);this.resolvedTabs=e;this.resolvedActions=this.incomingActions();this.syncValueFromUrl();this.committedSection=s;if(a){this.resetTabOverflowLayout()}this.scheduleOverflowCheck()}syncMenuSections(){if(!this.menuEl)return;this.menuEl.sections=x(this.resolvedTabs,this.effectiveValue)}syncValueFromUrl(){const s=this.incomingTabs();if(!this.currentUrl||!this.basePath){this.urlDerivedValue="";this.hideTabsForDetailRoute=false;return}if(!this.currentUrl.startsWith(this.basePath)){this.urlDerivedValue="";this.hideTabsForDetailRoute=false;return}const{value:t,hideTabs:e}=b(this.currentUrl,this.basePath,s);this.urlDerivedValue=t;this.hideTabsForDetailRoute=e}selectTab(s){this.value=s;this.dsTabChange.emit(s)}focusVisibleSelectedTab(){const s=k(this.visibleTabGroupEl,`[data-tab-id="${this.effectiveValue}"]`);s?.focus({preventScroll:true})}handleTabChange(s){this.selectTab(s.detail)}handleActionChange(s,t){const e=t.detail;this.dsActionChange.emit({id:s,selected:e})}toggleTabMenu(){if(this.menuOpen){this.menuOpen=false;return}if(this.menuEl&&this.triggerEl){this.menuEl.anchor=this.triggerEl;this.menuEl.minWidth=`${this.triggerEl.offsetWidth}px`;this.syncMenuSections()}this.menuOpen=true}handleTriggerKeyDown(s){if(!this.tabsCollapsed)return;if(!this.menuOpen){if(s.key==="ArrowDown"||s.key==="ArrowUp"||s.key==="Enter"||s.key===" "){s.preventDefault();this.toggleTabMenu()}return}if(s.key==="Escape"){s.preventDefault();this.menuOpen=false;this.triggerEl?.focus()}}render(){const s=this.basePath!==""&&this.committedSection===this.basePath;const t=s&&this.resolvedTabs.length>0&&!this.hideTabsForDetailRoute;const e=this.basePath||"no-section";return a(i,{key:"1cde49608302dfc07daf4be285e1f591e459f8be"},a("header",{key:"ad1737b497bfc6f342153ffb063a368fb0383917",class:{"bar-nav":true,"bar-nav--dashboard":this.navStyle==="dashboard","bar-nav--settings":this.navStyle==="settings","bar-nav--tabs-collapsed":t&&this.tabLayoutCommitted&&this.tabsCollapsed},ref:s=>{this.headerEl=s;if(s&&!this.resizeObserver){this.setupOverflowObserver()}}},t&&a("div",{key:"d48411a47b95232ac268014dc6d02d9cd235a0c0",class:"bar-nav__tabs-probe","aria-hidden":"true",inert:true},a("ds-tab-group-nav",{key:`probe-${e}`,ref:s=>{const t=s??null;if(t===this.probeTabGroupEl)return;this.probeTabGroupEl=t;if(t){this.intrinsicWidthRetryCount=0;this.tabLayoutPendingFrames=0;this.scheduleOverflowCheck()}},tabs:this.resolvedTabs,value:this.effectiveValue})),t&&this.tabLayoutCommitted&&!this.tabsCollapsed&&a("div",{key:"0c26c6441a225e80f1f636e8f11b77bddbd5343d",class:"bar-nav__left"},a("ds-tab-group-nav",{key:`visible-${e}`,class:"bar-nav__tabs-visible",ref:s=>{this.visibleTabGroupEl=s??null},tabs:this.resolvedTabs,value:this.effectiveValue,onDsChange:s=>this.handleTabChange(s)})),t&&!this.tabLayoutCommitted&&a("div",{key:"e2966af46e3fd044b81981923bb15e1fa49d95ef",class:"bar-nav__tabs-pending","aria-hidden":"true"}),!t&&this.heading&&a("span",{key:"a900ef7d0b4f84aa88d98c9900478a63fbb5c421",class:"bar-nav__heading text-body-medium-emphasis"},this.heading),t&&this.tabLayoutCommitted&&this.tabsCollapsed&&a("button",{key:"78d3e9ece92a2efa508883e529cadd19df26c3b3",type:"button",class:{"bar-nav__tab-trigger":true,"bar-nav__tab-trigger--open":this.menuOpen},ref:s=>{this.triggerEl=s},"aria-haspopup":"menu","aria-expanded":this.menuOpen,"aria-label":`${this.activeTabLabel} tabs`,onClick:()=>this.toggleTabMenu(),onKeyDown:s=>this.handleTriggerKeyDown(s)},a("span",{key:"523a16138115e9605d61cb06e5ee8289d880636b",class:{"bar-nav__tab-trigger-label":true,"bar-nav__tab-trigger-label--dot":this.activeTabHasDot,"bar-nav__tab-trigger-label--truncated":this.triggerLabelTruncated}},a("span",{key:"8de1274c331cbfde36b19d64e6c036f7e0a0508f",class:{"bar-nav__tab-trigger-label-text":true,"bar-nav__tab-trigger-label-text--truncated":this.triggerLabelTruncated,"text-body-medium-emphasis":true},ref:s=>{const t=s??null;if(t===this.triggerLabelTextEl)return;this.triggerLabelTextEl=t;if(this.tabsCollapsed){this.syncTriggerLabelObserver()}}},this.activeTabLabel),this.activeTabHasDot&&a("ds-badge",{key:"1ae32e4f112a5ccb54112d0e8f1550a355f5dfee",class:"bar-nav__tab-trigger-dot",variant:"dot",background:"var(--_bar-nav-bg)",label:"","aria-hidden":"true"})),a("ds-icon",{key:"1532597b5bd6a7e9dcc303fc8592c3237425b540",name:"ChevronDown",size:"md",color:"inherit",class:"bar-nav__tab-trigger-chevron"})),t&&this.tabLayoutCommitted&&this.tabsCollapsed&&this.resolvedActions.length>0&&a("div",{key:"3079fa97e37c59cdd2b7c7d821f93e3196154d90",class:"bar-nav__between","aria-hidden":"true"}),this.resolvedActions.length>0&&a("div",{key:"840d8437a554eb14e4c4742f7ebb7cb2e1c5cbc9",class:"bar-nav__actions"},this.resolvedActions.map((s=>a("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,onDsChange:t=>this.handleActionChange(s.id,t)}))))),t&&this.tabLayoutCommitted&&this.tabsCollapsed&&a("ds-menu",{key:"3338457ed4eaa059c87272ff7418d5f32052e823",ref:s=>{this.menuEl=s??null},open:this.menuOpen,sections:x(this.resolvedTabs,this.effectiveValue),side:"bottom",align:"start",onDsSelect:s=>this.handleMenuSelect(s),onDsClose:()=>this.handleMenuClose()}))}get el(){return this}static get watchers(){return{tabs:[{onHostPropsChange:0}],tabsJson:[{onHostPropsChange:0}],actions:[{onHostPropsChange:0}],actionsJson:[{onHostPropsChange:0}],currentUrl:[{onHostPropsChange:0}],basePath:[{onHostPropsChange:0}],value:[{onSelectionChange:0}],urlDerivedValue:[{onSelectionChange:0}],menuOpen:[{onMenuOpenChange:0}],tabsCollapsed:[{onTabsCollapsedChange:0}]}}static get style(){return P()}},[2,"ds-bar-nav",{navStyle:[513,"nav-style"],tabs:[16],tabsJson:[1,"tabs-json"],value:[1025],actions:[16],actionsJson:[1,"actions-json"],heading:[1],basePath:[1,"base-path"],currentUrl:[1,"current-url"],resolvedTabs:[32],resolvedActions:[32],urlDerivedValue:[32],hideTabsForDetailRoute:[32],tabsCollapsed:[32],menuOpen:[32],tabLayoutCommitted:[32],triggerLabelTruncated:[32],committedSection:[32],pendingSection:[32]},undefined,{tabs:[{onHostPropsChange:0}],tabsJson:[{onHostPropsChange:0}],actions:[{onHostPropsChange:0}],actionsJson:[{onHostPropsChange:0}],currentUrl:[{onHostPropsChange:0}],basePath:[{onHostPropsChange:0}],value:[{onSelectionChange:0}],urlDerivedValue:[{onSelectionChange:0}],menuOpen:[{onMenuOpenChange:0}],tabsCollapsed:[{onTabsCollapsedChange:0}]}]);S.HOST_PROP_SYNC_BUDGET=8;S.INTRINSIC_WIDTH_RETRY_MAX=3;S.TAB_LAYOUT_COMMIT_MAX_FRAMES=16;S.OVERFLOW_HYSTERESIS_PX=8;function H(){if(typeof customElements==="undefined"){return}const s=["ds-bar-nav","ds-badge","ds-bar-nav-action","ds-icon","ds-menu","ds-tab-group-nav"];s.forEach((s=>{switch(s){case"ds-bar-nav":if(!customElements.get(n(s))){customElements.define(n(s),S)}break;case"ds-badge":if(!customElements.get(n(s))){p()}break;case"ds-bar-nav-action":if(!customElements.get(n(s))){m()}break;case"ds-icon":if(!customElements.get(n(s))){_()}break;case"ds-menu":if(!customElements.get(n(s))){y()}break;case"ds-tab-group-nav":if(!customElements.get(n(s))){w()}break}}))}H();const O=S;const F=H;export{O as DsBarNav,F as defineCustomElement};
|
|
2
2
|
//# sourceMappingURL=ds-bar-nav.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["queryWithinComponentHost","host","selector","root","shadowRoot","querySelector","getTabListFromTabGroup","tabGroupEl","tabsToMenuSections","tabs","selectedId","sections","batch","item","isTabDivider","length","push","items","label","value","id","isSelected","isInactive","disabled","getActiveTab","selectable","getSelectableTabs","find","tab","getActiveTabLabel","tabsOverflowContainer","tabListScrollWidth","containerClientWidth","collapsed","hysteresis","diff","barNavCss","BarNav","__stencil_proxyCustomElement","HTMLElement","constructor","registerHost","this","navStyle","tabsJson","actions","actionsJson","basePath","currentUrl","resolvedTabs","resolvedActions","urlDerivedValue","hideTabsForDetailRoute","tabsCollapsed","menuOpen","tabLayoutCommitted","triggerLabelTruncated","headerEl","triggerEl","triggerLabelTextEl","triggerLabelObserver","menuEl","visibleTabGroupEl","probeTabGroupEl","resizeObserver","intrinsicWidthRetryCount","panelNavTransition","ChromeTransitionDepth","panelToolsTransition","overflowCoalescer","createRafCoalescer","updateTabsCollapsed","updateTriggerLabelTruncation","chromeTransitionShell","committedSection","pendingSection","onChromeTransitionStart","event","source","readChromeTransitionSource","enter","phase","readChromeTransitionPhase","onChromeTransitionEnd","exit","isActive","scheduleOverflowCheck","effectiveValue","activeTabLabel","activeTabHasDot","dot","digestTabsConfig","map","t","join","resetTabOverflowLayout","onHostPropsChange","applyHostProps","onSelectionChange","syncMenuSections","onMenuOpenChange","open","onTabsCollapsedChange","prevCollapsed","syncTriggerLabelObserver","requestAnimationFrame","undefined","queueMicrotask","focusVisibleSelectedTab","componentWillLoad","componentDidLoad","syncHostPropsIfNeeded","scheduleDeferredHostPropSync","setupOverflowObserver","bindChromeTransitionListeners","componentDidRender","chromeOverflowPaused","syncMenuAnchor","disconnectedCallback","disconnect","cancel","unbindChromeTransitionListeners","shell","el","closest","addEventListener","CHROME_TRANSITION_START","CHROME_TRANSITION_END","removeEventListener","shouldResyncBarNavProps","syncValueFromUrl","remaining","HOST_PROP_SYNC_BUDGET","tick","ResizeObserver","observe","schedule","disconnectOnly","truncated","scrollWidth","clientWidth","getLeftZoneAvailableWidth","headerStyle","getComputedStyle","gap","parseFloat","columnGap","actionsEl","actionsWidth","offsetWidth","horizontalPadding","paddingLeft","paddingRight","spacing","getPropertyValue","trim","available","Math","max","getTabsIntrinsicWidth","tabList","scheduleIntrinsicWidthRetry","INTRINSIC_WIDTH_RETRY_MAX","intrinsicWidth","shouldCollapse","OVERFLOW_HYSTERESIS_PX","anchor","handleMenuSelect","e","detail","selectTab","String","handleMenuClose","incomingTabs","parseJsonArrayProp","incomingActions","nextBasePath","sectionChanged","startsWith","tabsConfigChanged","hideTabs","deriveBarNavValueFromUrl","dsTabChange","emit","focus","preventScroll","handleTabChange","handleActionChange","actionId","selected","dsActionChange","toggleTabMenu","minWidth","handleTriggerKeyDown","key","preventDefault","render","sectionReady","hasTabs","tabGroupKey","h","Host","class","ref","inert","onDsChange","heading","type","onClick","onKeyDown","next","variant","background","name","size","color","action","icon","inactive","ariaLabel","side","align","onDsSelect","onDsClose"],"sources":["src/wc/components/BarNav/bar-nav-dom-utils.ts","src/wc/components/BarNav/bar-nav-tabs-menu-utils.ts","src/wc/components/BarNav/BarNav.css?tag=ds-bar-nav&encapsulation=scoped","src/wc/components/BarNav/BarNav.tsx"],"sourcesContent":["export type QueryableHost = {\n shadowRoot?: ShadowRoot | null;\n querySelector: (selectors: string) => Element | null;\n};\n\n/**\n * Query within a Stencil custom-element host (light DOM today, shadow DOM–safe).\n * Use for child nodes owned by ds-tab-group and similar components.\n */\nexport function queryWithinComponentHost(\n host: QueryableHost | null | undefined,\n selector: string,\n): HTMLElement | null {\n if (!host) return null;\n const root = host.shadowRoot ?? host;\n return root.querySelector(selector) as HTMLElement | null;\n}\n\nexport function getTabListFromTabGroup(\n tabGroupEl: QueryableHost | null | undefined,\n): HTMLElement | null {\n return queryWithinComponentHost(tabGroupEl, '[role=\"tablist\"]');\n}\n","import type { MenuSection } from '../Menu/menu-types';\nimport {\n getSelectableTabs,\n isTabDivider,\n type TabItem,\n type TabItemTab,\n} from '../TabGroup/tab-item-utils';\n\n/** Map BarNav tabs to menu sections, preserving `{ type: 'divider' }` as section breaks. */\nexport function tabsToMenuSections(tabs: TabItem[], selectedId: string): MenuSection[] {\n const sections: MenuSection[] = [];\n let batch: MenuSection['items'] = [];\n\n for (const item of tabs) {\n if (isTabDivider(item)) {\n if (batch.length > 0) {\n sections.push({ items: batch });\n batch = [];\n }\n continue;\n }\n\n batch.push({\n label: item.label,\n value: item.id,\n isSelected: item.id === selectedId,\n isInactive: item.disabled,\n });\n }\n\n if (batch.length > 0) {\n sections.push({ items: batch });\n }\n\n return sections;\n}\n\nexport function getActiveTab(tabs: TabItem[], selectedId: string): TabItemTab | undefined {\n const selectable = getSelectableTabs(tabs);\n return selectable.find(tab => tab.id === selectedId) ?? selectable[0];\n}\n\nexport function getActiveTabLabel(tabs: TabItem[], selectedId: string): string {\n return getActiveTab(tabs, selectedId)?.label ?? 'Tabs';\n}\n\n/** True when tablist content exceeds the available width (with optional hysteresis). */\nexport function tabsOverflowContainer(\n tabListScrollWidth: number,\n containerClientWidth: number,\n collapsed: boolean,\n hysteresis = 2,\n): boolean {\n const diff = tabListScrollWidth - containerClientWidth;\n if (collapsed) {\n return diff > -hysteresis;\n }\n return diff > hysteresis;\n}\n",":host {\n display: block;\n}\n\n.bar-nav {\n display: flex;\n align-items: center;\n position: relative;\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(--_bar-nav-border);\n background-color: var(--_bar-nav-bg);\n color: var(--_bar-nav-fg-primary);\n -webkit-user-select: none;\n user-select: none;\n overflow: hidden;\n transition:\n background-color var(--effect-motion-short-3),\n border-color var(--effect-motion-short-3),\n color var(--effect-motion-short-3);\n isolation: isolate;\n /* Default chrome — app surfaces */\n --_bar-nav-bg: var(--color-background-secondary);\n --_bar-nav-border: var(--color-border-tertiary);\n --_bar-nav-fg-primary: var(--color-foreground-primary);\n --_bar-nav-fg-secondary: var(--color-foreground-secondary);\n --_bar-nav-hover: var(--color-interaction-hover);\n --_bar-nav-pressed: var(--color-interaction-pressed);\n --_bar-nav-focus: var(--color-interaction-focus);\n --_bar-nav-dot: var(--color-foreground-bold-brand);\n}\n\n/* Style slots — same app tokens for now; hook texture/glyph layers later. */\n.bar-nav--dashboard,\n.bar-nav--settings {\n --_bar-nav-bg: var(--color-background-secondary);\n --_bar-nav-border: var(--color-border-tertiary);\n --_bar-nav-fg-primary: var(--color-foreground-primary);\n --_bar-nav-fg-secondary: var(--color-foreground-secondary);\n --_bar-nav-hover: var(--color-interaction-hover);\n --_bar-nav-pressed: var(--color-interaction-pressed);\n --_bar-nav-focus: var(--color-interaction-focus);\n --_bar-nav-dot: var(--color-foreground-bold-brand);\n}\n\n/* Shell-owned chrome (bg + wash + grid) — bar is a transparent frame. */\n:host-context(ds-app-shell[gradient]) .bar-nav,\n:host-context(ds-app-shell[grid]) .bar-nav {\n background-color: transparent;\n}\n\n.bar-nav > :not(.bar-nav__tabs-probe) {\n position: relative;\n z-index: 3;\n}\n\n/* Collapsed — explicit spacer keeps the 8px minimum while the trigger can shrink. */\n.bar-nav--tabs-collapsed {\n display: flex;\n gap: 0;\n align-items: center;\n}\n\n.bar-nav--tabs-collapsed .bar-nav__tab-trigger {\n flex: 0 1 auto;\n min-width: 0;\n max-width: 100%;\n width: auto;\n overflow: hidden;\n}\n\n.bar-nav__between {\n flex: 1 1 0%;\n min-width: var(--dimension-space-100);\n}\n\n.bar-nav--tabs-collapsed .bar-nav__tab-trigger-label {\n flex: 1 1 0%;\n min-width: 0;\n}\n\n.bar-nav--tabs-collapsed .bar-nav__tab-trigger-label-text {\n display: block;\n flex: 1 1 0%;\n min-width: 0;\n}\n\n.bar-nav--tabs-collapsed .bar-nav__actions {\n flex: 0 0 auto;\n margin-left: 0;\n}\n\n/* Expanded tabs — consume space between start and actions */\n.bar-nav__left {\n position: relative;\n flex: 1 1 0%;\n min-width: 0;\n width: 0;\n display: flex;\n align-items: center;\n}\n\n.bar-nav__left ds-tab-group-nav {\n flex: 1;\n min-width: 0;\n}\n\n/* Unconstrained width so intrinsic tab row width is measurable while collapsed */\n.bar-nav__tabs-probe {\n position: absolute;\n left: 0;\n top: 0;\n visibility: hidden;\n pointer-events: none;\n overflow: visible;\n width: max-content;\n max-width: none;\n z-index: -1;\n}\n\n.bar-nav__tabs-probe ds-tab-group-nav {\n width: max-content;\n}\n\n.bar-nav__tabs-visible {\n width: 100%;\n min-width: 0;\n}\n\n/* In-flow placeholder while measuring overflow — avoids layout jump; hidden from view/accessibility. */\n.bar-nav__tabs-pending {\n flex: 1;\n min-width: 0;\n height: var(--dimension-size-400);\n visibility: hidden;\n pointer-events: none;\n}\n\n/* Collapsed tab trigger — mirrors TabGroup .tab (+ chevron when collapsed) */\n.bar-nav__tab-trigger {\n all: unset;\n box-sizing: border-box;\n display: inline-flex;\n align-items: center;\n gap: var(--dimension-space-050);\n flex: 0 1 auto;\n min-width: 0;\n width: max-content;\n max-width: 100%;\n padding: var(--dimension-space-075);\n background-color: transparent;\n border-radius: var(--dimension-radius-025);\n cursor: pointer;\n -webkit-user-select: none;\n user-select: none;\n color: var(--_bar-nav-fg-primary);\n position: relative;\n transition: color var(--effect-motion-short-2);\n height: var(--dimension-size-400);\n --_hover-bg: var(--_bar-nav-hover);\n --_pressed-bg: var(--_bar-nav-pressed);\n --_dot: var(--_bar-nav-dot);\n --_dot-ring: var(--_bar-nav-bg);\n}\n\n.bar-nav__tab-trigger::after {\n content: '';\n position: absolute;\n inset: 0;\n border-radius: var(--dimension-radius-025);\n background: transparent;\n pointer-events: none;\n transition: none;\n z-index: 2;\n}\n\n.bar-nav__tab-trigger:hover::after {\n background: var(--_hover-bg);\n transition: none;\n}\n\n.bar-nav__tab-trigger:active::after {\n background: var(--_pressed-bg);\n transition: none;\n}\n\n.bar-nav__tab-trigger:focus-visible {\n outline: var(--dimension-stroke-width-025) solid var(--_bar-nav-focus);\n outline-offset: var(--dimension-space-025);\n}\n\n.bar-nav__tab-trigger--open::after {\n background: var(--_hover-bg);\n}\n\n.bar-nav__tab-trigger-label {\n position: relative;\n display: inline-flex;\n align-items: center;\n flex: 0 1 auto;\n min-width: 0;\n max-width: 100%;\n padding: 0 var(--dimension-space-025);\n overflow: hidden;\n --_fade-right: var(--dimension-space-025);\n z-index: 1;\n}\n\n.bar-nav__tab-trigger-label--truncated::after {\n content: '';\n position: absolute;\n top: 0;\n right: var(--_fade-right);\n bottom: 0;\n width: var(--dimension-size-400);\n background: linear-gradient(to right, transparent, var(--_bar-nav-bg));\n pointer-events: none;\n z-index: 1;\n}\n\n.bar-nav__tab-trigger-label-text {\n display: inline-block;\n max-width: 100%;\n box-sizing: border-box;\n white-space: nowrap;\n overflow: hidden;\n vertical-align: bottom;\n}\n\n.bar-nav__tab-trigger-label-text--truncated {\n -webkit-mask-image: linear-gradient(\n to right,\n #000 0,\n #000 calc(100% - var(--dimension-size-400)),\n transparent 100%\n );\n mask-image: linear-gradient(\n to right,\n #000 0,\n #000 calc(100% - var(--dimension-size-400)),\n transparent 100%\n );\n -webkit-mask-repeat: no-repeat;\n mask-repeat: no-repeat;\n -webkit-mask-size: 100% 100%;\n mask-size: 100% 100%;\n}\n\n.bar-nav__tab-trigger-label--dot {\n padding-right: calc(var(--dimension-space-025) + var(--dimension-space-050));\n --_fade-right: calc(var(--dimension-space-025) + var(--dimension-space-050));\n}\n\n.bar-nav__tab-trigger-dot {\n position: absolute;\n top: 0;\n right: 0;\n pointer-events: none;\n z-index: 1;\n}\n\n.bar-nav__tab-trigger-chevron {\n position: relative;\n flex-shrink: 0;\n z-index: 1;\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(--_bar-nav-fg-primary);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n -webkit-user-select: none;\n user-select: none;\n}\n\n/* Right zone — pinned to the end; header gap is the min space from tabs/trigger */\n.bar-nav__actions {\n display: flex;\n align-items: center;\n gap: var(--dimension-space-050);\n flex: 0 0 auto;\n margin-left: auto;\n}\n","import {\n Component,\n Prop,\n Event,\n EventEmitter,\n Element,\n Watch,\n State,\n h,\n Host,\n} from '@stencil/core';\nimport type { NavChromeStyle } from '../../nav/nav-chrome';\nimport type { MenuItemData } from '../Menu/menu-types';\nimport { isTabDivider } from '../TabGroup/tab-item-utils';\nimport type { BarNavActionItem, BarNavTab } from './bar-nav-types';\nimport {\n deriveBarNavValueFromUrl,\n parseJsonArrayProp,\n shouldResyncBarNavProps,\n} from './bar-nav-utils';\nimport {\n getTabListFromTabGroup,\n queryWithinComponentHost,\n type QueryableHost,\n} from './bar-nav-dom-utils';\nimport {\n getActiveTab,\n getActiveTabLabel,\n tabsOverflowContainer,\n tabsToMenuSections,\n} from './bar-nav-tabs-menu-utils';\nimport {\n CHROME_TRANSITION_END,\n CHROME_TRANSITION_START,\n ChromeTransitionDepth,\n createRafCoalescer,\n readChromeTransitionSource,\n readChromeTransitionPhase,\n} from '../../nav/chrome-transition';\n\n@Component({\n tag: 'ds-bar-nav',\n styleUrl: 'BarNav.css',\n scoped: true,\n})\nexport class BarNav {\n /** Style slot: `dashboard` or `settings`. Colors match for now. */\n @Prop({ attribute: 'nav-style', reflect: true }) navStyle: NavChromeStyle = 'dashboard';\n\n @Element() el!: HTMLElement;\n\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 /** 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 @State() private tabsCollapsed = false;\n @State() private menuOpen = false;\n /** When false, defer showing expanded tabs or the collapsed trigger until intrinsic width is measured (avoids a one-frame flash of the full tab row on narrow viewports). */\n @State() private tabLayoutCommitted = false;\n @State() private triggerLabelTruncated = false;\n\n private static readonly HOST_PROP_SYNC_BUDGET = 8;\n private static readonly INTRINSIC_WIDTH_RETRY_MAX = 3;\n private static readonly OVERFLOW_HYSTERESIS_PX = 8;\n\n private headerEl: HTMLElement | null = null;\n private triggerEl: HTMLButtonElement | null = null;\n private triggerLabelTextEl: HTMLElement | null = null;\n private triggerLabelObserver: ResizeObserver | null = null;\n private menuEl: HTMLDsMenuElement | null = null;\n private visibleTabGroupEl: QueryableHost | null = null;\n private probeTabGroupEl: QueryableHost | null = null;\n private resizeObserver: ResizeObserver | null = null;\n private intrinsicWidthRetryCount = 0;\n private readonly panelNavTransition = new ChromeTransitionDepth();\n private readonly panelToolsTransition = new ChromeTransitionDepth();\n private readonly overflowCoalescer = createRafCoalescer(() => {\n this.updateTabsCollapsed();\n this.updateTriggerLabelTruncation();\n });\n private chromeTransitionShell: HTMLElement | null = null;\n /** Matches `basePath` once tabs + URL are reconciled for the active section. */\n @State() private committedSection = '';\n /** Section waiting for URL + tabs to catch up after a cross-area navigation. */\n @State() private pendingSection = '';\n\n private get effectiveValue(): string {\n if (this.currentUrl && this.basePath) {\n return this.urlDerivedValue;\n }\n return this.value;\n }\n\n private get activeTabLabel(): string {\n return getActiveTabLabel(this.resolvedTabs, this.effectiveValue);\n }\n\n private get activeTabHasDot(): boolean {\n return !!getActiveTab(this.resolvedTabs, this.effectiveValue)?.dot;\n }\n\n private digestTabsConfig(tabs: BarNavTab[]): string {\n return tabs.map(t => (isTabDivider(t) ? '|' : t.id)).join(',');\n }\n\n private resetTabOverflowLayout() {\n this.tabLayoutCommitted = false;\n this.tabsCollapsed = false;\n this.menuOpen = false;\n this.intrinsicWidthRetryCount = 0;\n }\n\n @Watch('tabs')\n @Watch('tabsJson')\n @Watch('actions')\n @Watch('actionsJson')\n @Watch('currentUrl')\n @Watch('basePath')\n onHostPropsChange() {\n this.applyHostProps();\n }\n\n @Watch('value')\n @Watch('urlDerivedValue')\n onSelectionChange() {\n this.syncMenuSections();\n this.scheduleOverflowCheck();\n }\n\n @Watch('menuOpen')\n onMenuOpenChange(open: boolean) {\n if (this.menuEl) {\n this.menuEl.open = open;\n }\n }\n\n @Watch('tabsCollapsed')\n onTabsCollapsedChange(collapsed: boolean, prevCollapsed: boolean | undefined) {\n if (!collapsed) {\n this.menuOpen = false;\n this.menuEl = null;\n this.triggerLabelTruncated = false;\n this.syncTriggerLabelObserver();\n }\n this.scheduleOverflowCheck();\n if (collapsed) {\n requestAnimationFrame(() => {\n this.scheduleOverflowCheck();\n this.syncTriggerLabelObserver();\n });\n }\n if (collapsed || prevCollapsed === undefined || prevCollapsed === collapsed) return;\n requestAnimationFrame(() => this.scheduleOverflowCheck());\n queueMicrotask(() => this.focusVisibleSelectedTab());\n }\n\n componentWillLoad() {\n this.applyHostProps();\n }\n\n componentDidLoad() {\n this.syncHostPropsIfNeeded();\n this.scheduleDeferredHostPropSync();\n this.setupOverflowObserver();\n this.bindChromeTransitionListeners();\n this.scheduleOverflowCheck();\n }\n\n componentDidRender() {\n if (this.chromeOverflowPaused()) {\n this.syncMenuAnchor();\n return;\n }\n this.scheduleOverflowCheck();\n this.syncMenuAnchor();\n if (this.tabsCollapsed) {\n this.updateTriggerLabelTruncation();\n }\n }\n\n disconnectedCallback() {\n this.resizeObserver?.disconnect();\n this.resizeObserver = null;\n this.overflowCoalescer.cancel();\n this.syncTriggerLabelObserver(true);\n this.unbindChromeTransitionListeners();\n }\n\n private bindChromeTransitionListeners() {\n const shell = this.el.closest('ds-app-shell');\n if (!shell) return;\n this.chromeTransitionShell = shell;\n shell.addEventListener(CHROME_TRANSITION_START, this.onChromeTransitionStart);\n shell.addEventListener(CHROME_TRANSITION_END, this.onChromeTransitionEnd);\n }\n\n private unbindChromeTransitionListeners() {\n if (!this.chromeTransitionShell) return;\n this.chromeTransitionShell.removeEventListener(\n CHROME_TRANSITION_START,\n this.onChromeTransitionStart,\n );\n this.chromeTransitionShell.removeEventListener(\n CHROME_TRANSITION_END,\n this.onChromeTransitionEnd,\n );\n this.chromeTransitionShell = null;\n }\n\n private onChromeTransitionStart = (event: Event) => {\n const source = readChromeTransitionSource(event);\n if (source === 'panel-nav') {\n this.panelNavTransition.enter();\n return;\n }\n if (source === 'panel-tools') {\n const phase = readChromeTransitionPhase(event) ?? 'opening';\n if (phase === 'closing') {\n this.panelToolsTransition.enter();\n }\n }\n };\n\n private chromeOverflowPaused(): boolean {\n return this.panelNavTransition.isActive || this.panelToolsTransition.isActive;\n }\n\n private onChromeTransitionEnd = (event: Event) => {\n const source = readChromeTransitionSource(event);\n if (source === 'panel-nav') {\n this.panelNavTransition.exit();\n if (!this.panelNavTransition.isActive) {\n this.scheduleOverflowCheck();\n }\n return;\n }\n if (source === 'panel-tools') {\n this.panelToolsTransition.exit();\n this.scheduleOverflowCheck();\n }\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.applyHostProps();\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 setupOverflowObserver() {\n if (typeof ResizeObserver === 'undefined' || !this.headerEl) return;\n this.resizeObserver?.disconnect();\n this.resizeObserver = new ResizeObserver(() => {\n if (this.chromeOverflowPaused()) return;\n this.scheduleOverflowCheck();\n });\n this.resizeObserver.observe(this.headerEl);\n }\n\n private scheduleOverflowCheck() {\n if (this.chromeOverflowPaused()) return;\n this.overflowCoalescer.schedule();\n }\n\n private syncTriggerLabelObserver(disconnectOnly = false) {\n this.triggerLabelObserver?.disconnect();\n this.triggerLabelObserver = null;\n\n if (disconnectOnly || !this.tabsCollapsed || !this.triggerLabelTextEl) {\n return;\n }\n\n this.updateTriggerLabelTruncation();\n\n if (typeof ResizeObserver === 'undefined') return;\n\n this.triggerLabelObserver = new ResizeObserver(() => this.updateTriggerLabelTruncation());\n this.triggerLabelObserver.observe(this.triggerLabelTextEl);\n if (this.triggerEl) {\n this.triggerLabelObserver.observe(this.triggerEl);\n }\n }\n\n private updateTriggerLabelTruncation() {\n const el = this.triggerLabelTextEl;\n if (!el || !this.tabsCollapsed) {\n if (this.triggerLabelTruncated) {\n this.triggerLabelTruncated = false;\n }\n return;\n }\n\n const truncated = el.scrollWidth > el.clientWidth + 1;\n if (truncated !== this.triggerLabelTruncated) {\n this.triggerLabelTruncated = truncated;\n }\n }\n\n /** Width left for tabs after actions. */\n private getLeftZoneAvailableWidth(): number {\n if (!this.headerEl) return 0;\n\n const headerStyle = getComputedStyle(this.headerEl);\n const gap = parseFloat(headerStyle.columnGap || headerStyle.gap || '0') || 0;\n const actionsEl = this.headerEl.querySelector('.bar-nav__actions') as HTMLElement | null;\n const actionsWidth = actionsEl?.offsetWidth ?? 0;\n const horizontalPadding =\n (parseFloat(headerStyle.paddingLeft) || 0) + (parseFloat(headerStyle.paddingRight) || 0);\n const spacing = this.tabsCollapsed\n ? parseFloat(\n getComputedStyle(this.headerEl).getPropertyValue('--dimension-space-100').trim(),\n ) || 8\n : actionsEl\n ? gap\n : 0;\n const available =\n this.headerEl.clientWidth - horizontalPadding - actionsWidth - spacing;\n\n return Math.max(0, available);\n }\n\n private getTabsIntrinsicWidth(): number {\n const tabList = getTabListFromTabGroup(this.probeTabGroupEl);\n return tabList?.scrollWidth ?? 0;\n }\n\n private scheduleIntrinsicWidthRetry() {\n if (this.intrinsicWidthRetryCount >= BarNav.INTRINSIC_WIDTH_RETRY_MAX) return;\n this.intrinsicWidthRetryCount++;\n requestAnimationFrame(() => this.scheduleOverflowCheck());\n }\n\n private updateTabsCollapsed() {\n if (!this.headerEl || this.resolvedTabs.length === 0 || this.hideTabsForDetailRoute) {\n if (this.tabsCollapsed) {\n this.tabsCollapsed = false;\n this.menuOpen = false;\n }\n this.intrinsicWidthRetryCount = 0;\n this.tabLayoutCommitted = true;\n return;\n }\n\n const intrinsicWidth = this.getTabsIntrinsicWidth();\n if (intrinsicWidth === 0) {\n if (this.intrinsicWidthRetryCount >= BarNav.INTRINSIC_WIDTH_RETRY_MAX) {\n this.intrinsicWidthRetryCount = 0;\n this.tabsCollapsed = false;\n this.tabLayoutCommitted = true;\n } else {\n this.scheduleIntrinsicWidthRetry();\n }\n return;\n }\n this.intrinsicWidthRetryCount = 0;\n\n const shouldCollapse = tabsOverflowContainer(\n intrinsicWidth,\n this.getLeftZoneAvailableWidth(),\n this.tabsCollapsed,\n BarNav.OVERFLOW_HYSTERESIS_PX,\n );\n\n if (shouldCollapse !== this.tabsCollapsed) {\n this.tabsCollapsed = shouldCollapse;\n if (!shouldCollapse) {\n this.menuOpen = false;\n }\n }\n\n this.tabLayoutCommitted = true;\n }\n\n private syncMenuAnchor() {\n if (!this.menuEl || !this.triggerEl || !this.tabsCollapsed) return;\n this.menuEl.anchor = this.triggerEl;\n this.syncMenuSections();\n }\n\n private handleMenuSelect(e: Event) {\n const detail = (e as CustomEvent<MenuItemData>).detail;\n if (detail?.value !== undefined) {\n this.selectTab(String(detail.value));\n }\n this.menuOpen = false;\n }\n\n private handleMenuClose() {\n this.menuOpen = false;\n }\n\n /** Batch tabs/basePath/currentUrl updates so URL derivation never runs with a mixed section. */\n private incomingTabs(): BarNavTab[] {\n return this.tabsJson\n ? parseJsonArrayProp(this.tabsJson, [])\n : (this.tabs ?? []);\n }\n\n private incomingActions(): BarNavActionItem[] {\n return this.actionsJson\n ? parseJsonArrayProp(this.actionsJson, [])\n : (this.actions ?? []);\n }\n\n private applyHostProps() {\n const nextBasePath = this.basePath;\n const sectionChanged =\n nextBasePath !== '' &&\n this.committedSection !== '' &&\n nextBasePath !== this.committedSection;\n\n if (sectionChanged) {\n this.urlDerivedValue = '';\n this.committedSection = '';\n this.pendingSection = nextBasePath;\n this.resolvedTabs = [];\n this.resolvedActions = this.incomingActions();\n this.resetTabOverflowLayout();\n return;\n }\n\n if (this.pendingSection) {\n if (!nextBasePath || nextBasePath !== this.pendingSection) {\n return;\n }\n if (!this.currentUrl.startsWith(this.pendingSection)) {\n return;\n }\n const tabs = this.incomingTabs();\n if (tabs.length === 0) {\n return;\n }\n this.pendingSection = '';\n }\n\n const tabs = this.incomingTabs();\n const tabsConfigChanged =\n this.digestTabsConfig(tabs) !== this.digestTabsConfig(this.resolvedTabs);\n\n this.resolvedTabs = tabs;\n this.resolvedActions = this.incomingActions();\n this.syncValueFromUrl();\n this.committedSection = nextBasePath;\n if (tabsConfigChanged) {\n this.resetTabOverflowLayout();\n }\n this.scheduleOverflowCheck();\n }\n\n private syncMenuSections() {\n if (!this.menuEl) return;\n this.menuEl.sections = tabsToMenuSections(this.resolvedTabs, this.effectiveValue);\n }\n\n private syncValueFromUrl() {\n const tabs = this.incomingTabs();\n\n if (!this.currentUrl || !this.basePath) {\n this.urlDerivedValue = '';\n this.hideTabsForDetailRoute = false;\n return;\n }\n\n if (!this.currentUrl.startsWith(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 tabs,\n );\n this.urlDerivedValue = value;\n this.hideTabsForDetailRoute = hideTabs;\n }\n\n private selectTab(id: string) {\n this.value = id;\n this.dsTabChange.emit(id);\n }\n\n /** Move focus to the selected tab in the visible tab group (after expand). */\n private focusVisibleSelectedTab() {\n const tab = queryWithinComponentHost(\n this.visibleTabGroupEl,\n `[data-tab-id=\"${this.effectiveValue}\"]`,\n );\n tab?.focus({ preventScroll: true });\n }\n\n private handleTabChange(e: Event) {\n this.selectTab((e as CustomEvent<string>).detail);\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 private toggleTabMenu() {\n if (this.menuOpen) {\n this.menuOpen = false;\n return;\n }\n\n if (this.menuEl && this.triggerEl) {\n this.menuEl.anchor = this.triggerEl;\n this.menuEl.minWidth = `${this.triggerEl.offsetWidth}px`;\n this.syncMenuSections();\n }\n\n this.menuOpen = true;\n }\n\n private handleTriggerKeyDown(e: KeyboardEvent) {\n if (!this.tabsCollapsed) return;\n\n if (!this.menuOpen) {\n if (e.key === 'ArrowDown' || e.key === 'ArrowUp' || e.key === 'Enter' || e.key === ' ') {\n e.preventDefault();\n this.toggleTabMenu();\n }\n return;\n }\n\n if (e.key === 'Escape') {\n e.preventDefault();\n this.menuOpen = false;\n this.triggerEl?.focus();\n }\n }\n\n render() {\n const sectionReady =\n this.basePath !== '' && this.committedSection === this.basePath;\n const hasTabs =\n sectionReady && this.resolvedTabs.length > 0 && !this.hideTabsForDetailRoute;\n const tabGroupKey = this.basePath || 'no-section';\n\n return (\n <Host>\n <header\n class={{\n 'bar-nav': true,\n 'bar-nav--dashboard': this.navStyle === 'dashboard',\n 'bar-nav--settings': this.navStyle === 'settings',\n 'bar-nav--tabs-collapsed':\n hasTabs && this.tabLayoutCommitted && this.tabsCollapsed,\n }}\n ref={el => {\n this.headerEl = el as HTMLElement;\n if (el && !this.resizeObserver) {\n this.setupOverflowObserver();\n }\n }}\n >\n {hasTabs && (\n <div class=\"bar-nav__tabs-probe\" aria-hidden=\"true\" inert>\n <ds-tab-group-nav\n key={`probe-${tabGroupKey}`}\n ref={el => {\n this.probeTabGroupEl = el ?? null;\n if (el) {\n this.intrinsicWidthRetryCount = 0;\n this.scheduleOverflowCheck();\n }\n }}\n tabs={this.resolvedTabs}\n value={this.effectiveValue}\n />\n </div>\n )}\n\n {hasTabs && this.tabLayoutCommitted && !this.tabsCollapsed && (\n <div class=\"bar-nav__left\">\n <ds-tab-group-nav\n key={`visible-${tabGroupKey}`}\n class=\"bar-nav__tabs-visible\"\n ref={el => {\n this.visibleTabGroupEl = el ?? null;\n }}\n tabs={this.resolvedTabs}\n value={this.effectiveValue}\n onDsChange={(e: Event) => this.handleTabChange(e)}\n />\n </div>\n )}\n\n {hasTabs && !this.tabLayoutCommitted && (\n <div class=\"bar-nav__tabs-pending\" aria-hidden=\"true\" />\n )}\n\n {!hasTabs && this.heading && (\n <span class=\"bar-nav__heading text-body-medium-emphasis\">{this.heading}</span>\n )}\n\n {hasTabs && this.tabLayoutCommitted && this.tabsCollapsed && (\n <button\n type=\"button\"\n class={{\n 'bar-nav__tab-trigger': true,\n 'bar-nav__tab-trigger--open': this.menuOpen,\n }}\n ref={el => {\n this.triggerEl = el as HTMLButtonElement;\n }}\n aria-haspopup=\"menu\"\n aria-expanded={this.menuOpen}\n aria-label={`${this.activeTabLabel} tabs`}\n onClick={() => this.toggleTabMenu()}\n onKeyDown={(e: KeyboardEvent) => this.handleTriggerKeyDown(e)}\n >\n <span\n class={{\n 'bar-nav__tab-trigger-label': true,\n 'bar-nav__tab-trigger-label--dot': this.activeTabHasDot,\n 'bar-nav__tab-trigger-label--truncated': this.triggerLabelTruncated,\n }}\n >\n <span\n class={{\n 'bar-nav__tab-trigger-label-text': true,\n 'bar-nav__tab-trigger-label-text--truncated': this.triggerLabelTruncated,\n 'text-body-medium-emphasis': true,\n }}\n ref={el => {\n const next = el ?? null;\n if (next === this.triggerLabelTextEl) return;\n this.triggerLabelTextEl = next;\n if (this.tabsCollapsed) {\n this.syncTriggerLabelObserver();\n }\n }}\n >\n {this.activeTabLabel}\n </span>\n {this.activeTabHasDot && (\n <ds-badge\n class=\"bar-nav__tab-trigger-dot\"\n variant=\"dot\"\n background=\"var(--_bar-nav-bg)\"\n label=\"\"\n aria-hidden=\"true\"\n />\n )}\n </span>\n <ds-icon\n name=\"ChevronDown\"\n size=\"md\"\n color=\"inherit\"\n class=\"bar-nav__tab-trigger-chevron\"\n />\n </button>\n )}\n\n {hasTabs &&\n this.tabLayoutCommitted &&\n this.tabsCollapsed &&\n this.resolvedActions.length > 0 && (\n <div class=\"bar-nav__between\" aria-hidden=\"true\" />\n )}\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 onDsChange={(e: Event) => this.handleActionChange(action.id, e)}\n />\n ))}\n </div>\n )}\n\n </header>\n\n {hasTabs && this.tabLayoutCommitted && this.tabsCollapsed && (\n <ds-menu\n ref={el => {\n this.menuEl = (el as HTMLDsMenuElement) ?? null;\n }}\n open={this.menuOpen}\n sections={tabsToMenuSections(this.resolvedTabs, this.effectiveValue)}\n side=\"bottom\"\n align=\"start\"\n onDsSelect={(e: Event) => this.handleMenuSelect(e)}\n onDsClose={() => this.handleMenuClose()}\n />\n )}\n </Host>\n );\n }\n}\n"],"mappings":"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","ignoreList":[]}
|
|
1
|
+
{"version":3,"names":["queryWithinComponentHost","host","selector","root","shadowRoot","querySelector","getTabListFromTabGroup","tabGroupEl","tabsToMenuSections","tabs","selectedId","sections","batch","item","isTabDivider","length","push","items","label","value","id","isSelected","isInactive","disabled","getActiveTab","selectable","getSelectableTabs","find","tab","getActiveTabLabel","tabsOverflowContainer","tabListScrollWidth","containerClientWidth","collapsed","hysteresis","diff","barNavCss","BarNav","__stencil_proxyCustomElement","HTMLElement","constructor","registerHost","this","navStyle","tabsJson","actions","actionsJson","basePath","currentUrl","resolvedTabs","resolvedActions","urlDerivedValue","hideTabsForDetailRoute","tabsCollapsed","menuOpen","tabLayoutCommitted","triggerLabelTruncated","headerEl","triggerEl","triggerLabelTextEl","triggerLabelObserver","menuEl","visibleTabGroupEl","probeTabGroupEl","resizeObserver","intrinsicWidthRetryCount","tabLayoutPendingFrames","panelNavTransition","ChromeTransitionDepth","panelToolsTransition","overflowCoalescer","createRafCoalescer","updateTabsCollapsed","updateTriggerLabelTruncation","chromeTransitionShell","committedSection","pendingSection","onChromeTransitionStart","event","source","readChromeTransitionSource","enter","phase","readChromeTransitionPhase","onChromeTransitionEnd","exit","isActive","scheduleOverflowCheck","effectiveValue","activeTabLabel","activeTabHasDot","dot","digestTabsConfig","map","t","join","resetTabOverflowLayout","onHostPropsChange","applyHostProps","onSelectionChange","syncMenuSections","onMenuOpenChange","open","onTabsCollapsedChange","prevCollapsed","syncTriggerLabelObserver","requestAnimationFrame","undefined","queueMicrotask","focusVisibleSelectedTab","componentWillLoad","componentDidLoad","syncHostPropsIfNeeded","scheduleDeferredHostPropSync","setupOverflowObserver","bindChromeTransitionListeners","componentDidRender","chromeOverflowPaused","syncMenuAnchor","disconnectedCallback","disconnect","cancel","unbindChromeTransitionListeners","shell","el","closest","addEventListener","CHROME_TRANSITION_START","CHROME_TRANSITION_END","removeEventListener","shouldResyncBarNavProps","syncValueFromUrl","remaining","HOST_PROP_SYNC_BUDGET","tick","ResizeObserver","observe","schedule","disconnectOnly","truncated","scrollWidth","clientWidth","getLeftZoneAvailableWidth","headerStyle","getComputedStyle","gap","parseFloat","columnGap","actionsEl","actionsWidth","offsetWidth","horizontalPadding","paddingLeft","paddingRight","spacing","getPropertyValue","trim","available","Math","max","getTabsIntrinsicWidth","tabList","scheduleIntrinsicWidthRetry","INTRINSIC_WIDTH_RETRY_MAX","forceTabLayoutCommit","fallbackCollapsed","intrinsicWidth","TAB_LAYOUT_COMMIT_MAX_FRAMES","shouldCollapse","OVERFLOW_HYSTERESIS_PX","anchor","handleMenuSelect","e","detail","selectTab","String","handleMenuClose","incomingTabs","parseJsonArrayProp","incomingActions","nextBasePath","sectionChanged","startsWith","tabsConfigChanged","hideTabs","deriveBarNavValueFromUrl","dsTabChange","emit","focus","preventScroll","handleTabChange","handleActionChange","actionId","selected","dsActionChange","toggleTabMenu","minWidth","handleTriggerKeyDown","key","preventDefault","render","sectionReady","hasTabs","tabGroupKey","h","Host","class","ref","inert","next","onDsChange","heading","type","onClick","onKeyDown","variant","background","name","size","color","action","icon","inactive","ariaLabel","side","align","onDsSelect","onDsClose"],"sources":["src/wc/components/BarNav/bar-nav-dom-utils.ts","src/wc/components/BarNav/bar-nav-tabs-menu-utils.ts","src/wc/components/BarNav/BarNav.css?tag=ds-bar-nav&encapsulation=scoped","src/wc/components/BarNav/BarNav.tsx"],"sourcesContent":["export type QueryableHost = {\n shadowRoot?: ShadowRoot | null;\n querySelector: (selectors: string) => Element | null;\n};\n\n/**\n * Query within a Stencil custom-element host (light DOM today, shadow DOM–safe).\n * Use for child nodes owned by ds-tab-group and similar components.\n */\nexport function queryWithinComponentHost(\n host: QueryableHost | null | undefined,\n selector: string,\n): HTMLElement | null {\n if (!host) return null;\n const root = host.shadowRoot ?? host;\n return root.querySelector(selector) as HTMLElement | null;\n}\n\nexport function getTabListFromTabGroup(\n tabGroupEl: QueryableHost | null | undefined,\n): HTMLElement | null {\n return queryWithinComponentHost(tabGroupEl, '[role=\"tablist\"]');\n}\n","import type { MenuSection } from '../Menu/menu-types';\nimport {\n getSelectableTabs,\n isTabDivider,\n type TabItem,\n type TabItemTab,\n} from '../TabGroup/tab-item-utils';\n\n/** Map BarNav tabs to menu sections, preserving `{ type: 'divider' }` as section breaks. */\nexport function tabsToMenuSections(tabs: TabItem[], selectedId: string): MenuSection[] {\n const sections: MenuSection[] = [];\n let batch: MenuSection['items'] = [];\n\n for (const item of tabs) {\n if (isTabDivider(item)) {\n if (batch.length > 0) {\n sections.push({ items: batch });\n batch = [];\n }\n continue;\n }\n\n batch.push({\n label: item.label,\n value: item.id,\n isSelected: item.id === selectedId,\n isInactive: item.disabled,\n });\n }\n\n if (batch.length > 0) {\n sections.push({ items: batch });\n }\n\n return sections;\n}\n\nexport function getActiveTab(tabs: TabItem[], selectedId: string): TabItemTab | undefined {\n const selectable = getSelectableTabs(tabs);\n return selectable.find(tab => tab.id === selectedId) ?? selectable[0];\n}\n\nexport function getActiveTabLabel(tabs: TabItem[], selectedId: string): string {\n return getActiveTab(tabs, selectedId)?.label ?? 'Tabs';\n}\n\n/** True when tablist content exceeds the available width (with optional hysteresis). */\nexport function tabsOverflowContainer(\n tabListScrollWidth: number,\n containerClientWidth: number,\n collapsed: boolean,\n hysteresis = 2,\n): boolean {\n const diff = tabListScrollWidth - containerClientWidth;\n if (collapsed) {\n return diff > -hysteresis;\n }\n return diff > hysteresis;\n}\n",":host {\n display: block;\n}\n\n.bar-nav {\n display: flex;\n align-items: center;\n position: relative;\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(--_bar-nav-border);\n background-color: var(--_bar-nav-bg);\n color: var(--_bar-nav-fg-primary);\n -webkit-user-select: none;\n user-select: none;\n overflow: hidden;\n transition:\n background-color var(--effect-motion-short-3),\n border-color var(--effect-motion-short-3),\n color var(--effect-motion-short-3);\n isolation: isolate;\n /* Default chrome — app surfaces */\n --_bar-nav-bg: var(--color-background-secondary);\n --_bar-nav-border: var(--color-border-tertiary);\n --_bar-nav-fg-primary: var(--color-foreground-primary);\n --_bar-nav-fg-secondary: var(--color-foreground-secondary);\n --_bar-nav-hover: var(--color-interaction-hover);\n --_bar-nav-pressed: var(--color-interaction-pressed);\n --_bar-nav-focus: var(--color-interaction-focus);\n --_bar-nav-dot: var(--color-foreground-bold-brand);\n}\n\n/* Style slots — same app tokens for now; hook texture/glyph layers later. */\n.bar-nav--dashboard,\n.bar-nav--settings {\n --_bar-nav-bg: var(--color-background-secondary);\n --_bar-nav-border: var(--color-border-tertiary);\n --_bar-nav-fg-primary: var(--color-foreground-primary);\n --_bar-nav-fg-secondary: var(--color-foreground-secondary);\n --_bar-nav-hover: var(--color-interaction-hover);\n --_bar-nav-pressed: var(--color-interaction-pressed);\n --_bar-nav-focus: var(--color-interaction-focus);\n --_bar-nav-dot: var(--color-foreground-bold-brand);\n}\n\n/* Shell-owned chrome (bg + wash + grid) — bar is a transparent frame. */\n:host-context(ds-app-shell[gradient]) .bar-nav,\n:host-context(ds-app-shell[grid]) .bar-nav {\n background-color: transparent;\n}\n\n.bar-nav > :not(.bar-nav__tabs-probe) {\n position: relative;\n z-index: 3;\n}\n\n/* Collapsed — explicit spacer keeps the 8px minimum while the trigger can shrink. */\n.bar-nav--tabs-collapsed {\n display: flex;\n gap: 0;\n align-items: center;\n}\n\n.bar-nav--tabs-collapsed .bar-nav__tab-trigger {\n flex: 0 1 auto;\n min-width: 0;\n max-width: 100%;\n width: auto;\n overflow: hidden;\n}\n\n.bar-nav__between {\n flex: 1 1 0%;\n min-width: var(--dimension-space-100);\n}\n\n.bar-nav--tabs-collapsed .bar-nav__tab-trigger-label {\n flex: 1 1 0%;\n min-width: 0;\n}\n\n.bar-nav--tabs-collapsed .bar-nav__tab-trigger-label-text {\n display: block;\n flex: 1 1 0%;\n min-width: 0;\n}\n\n.bar-nav--tabs-collapsed .bar-nav__actions {\n flex: 0 0 auto;\n margin-left: 0;\n}\n\n/* Expanded tabs — consume space between start and actions */\n.bar-nav__left {\n position: relative;\n flex: 1 1 0%;\n min-width: 0;\n width: 0;\n display: flex;\n align-items: center;\n}\n\n.bar-nav__left ds-tab-group-nav {\n flex: 1;\n min-width: 0;\n}\n\n/* Unconstrained width so intrinsic tab row width is measurable while collapsed */\n.bar-nav__tabs-probe {\n position: absolute;\n left: 0;\n top: 0;\n visibility: hidden;\n pointer-events: none;\n overflow: visible;\n width: max-content;\n max-width: none;\n z-index: -1;\n}\n\n.bar-nav__tabs-probe ds-tab-group-nav {\n width: max-content;\n}\n\n.bar-nav__tabs-visible {\n width: 100%;\n min-width: 0;\n}\n\n/* In-flow placeholder while measuring overflow — avoids layout jump; hidden from view/accessibility. */\n.bar-nav__tabs-pending {\n flex: 1;\n min-width: 0;\n height: var(--dimension-size-400);\n visibility: hidden;\n pointer-events: none;\n}\n\n/* Collapsed tab trigger — mirrors TabGroup .tab (+ chevron when collapsed) */\n.bar-nav__tab-trigger {\n all: unset;\n box-sizing: border-box;\n display: inline-flex;\n align-items: center;\n gap: var(--dimension-space-050);\n flex: 0 1 auto;\n min-width: 0;\n width: max-content;\n max-width: 100%;\n padding: var(--dimension-space-075);\n background-color: transparent;\n border-radius: var(--dimension-radius-025);\n cursor: pointer;\n -webkit-user-select: none;\n user-select: none;\n color: var(--_bar-nav-fg-primary);\n position: relative;\n transition: color var(--effect-motion-short-2);\n height: var(--dimension-size-400);\n --_hover-bg: var(--_bar-nav-hover);\n --_pressed-bg: var(--_bar-nav-pressed);\n --_dot: var(--_bar-nav-dot);\n --_dot-ring: var(--_bar-nav-bg);\n}\n\n.bar-nav__tab-trigger::after {\n content: '';\n position: absolute;\n inset: 0;\n border-radius: var(--dimension-radius-025);\n background: transparent;\n pointer-events: none;\n transition: none;\n z-index: 2;\n}\n\n.bar-nav__tab-trigger:hover::after {\n background: var(--_hover-bg);\n transition: none;\n}\n\n.bar-nav__tab-trigger:active::after {\n background: var(--_pressed-bg);\n transition: none;\n}\n\n.bar-nav__tab-trigger:focus-visible {\n outline: var(--dimension-stroke-width-025) solid var(--_bar-nav-focus);\n outline-offset: var(--dimension-space-025);\n}\n\n.bar-nav__tab-trigger--open::after {\n background: var(--_hover-bg);\n}\n\n.bar-nav__tab-trigger-label {\n position: relative;\n display: inline-flex;\n align-items: center;\n flex: 0 1 auto;\n min-width: 0;\n max-width: 100%;\n padding: 0 var(--dimension-space-025);\n overflow: hidden;\n --_fade-right: var(--dimension-space-025);\n z-index: 1;\n}\n\n.bar-nav__tab-trigger-label--truncated::after {\n content: '';\n position: absolute;\n top: 0;\n right: var(--_fade-right);\n bottom: 0;\n width: var(--dimension-size-400);\n background: linear-gradient(to right, transparent, var(--_bar-nav-bg));\n pointer-events: none;\n z-index: 1;\n}\n\n.bar-nav__tab-trigger-label-text {\n display: inline-block;\n max-width: 100%;\n box-sizing: border-box;\n white-space: nowrap;\n overflow: hidden;\n vertical-align: bottom;\n}\n\n.bar-nav__tab-trigger-label-text--truncated {\n -webkit-mask-image: linear-gradient(\n to right,\n #000 0,\n #000 calc(100% - var(--dimension-size-400)),\n transparent 100%\n );\n mask-image: linear-gradient(\n to right,\n #000 0,\n #000 calc(100% - var(--dimension-size-400)),\n transparent 100%\n );\n -webkit-mask-repeat: no-repeat;\n mask-repeat: no-repeat;\n -webkit-mask-size: 100% 100%;\n mask-size: 100% 100%;\n}\n\n.bar-nav__tab-trigger-label--dot {\n padding-right: calc(var(--dimension-space-025) + var(--dimension-space-050));\n --_fade-right: calc(var(--dimension-space-025) + var(--dimension-space-050));\n}\n\n.bar-nav__tab-trigger-dot {\n position: absolute;\n top: 0;\n right: 0;\n pointer-events: none;\n z-index: 1;\n}\n\n.bar-nav__tab-trigger-chevron {\n position: relative;\n flex-shrink: 0;\n z-index: 1;\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(--_bar-nav-fg-primary);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n -webkit-user-select: none;\n user-select: none;\n}\n\n/* Right zone — pinned to the end; header gap is the min space from tabs/trigger */\n.bar-nav__actions {\n display: flex;\n align-items: center;\n gap: var(--dimension-space-050);\n flex: 0 0 auto;\n margin-left: auto;\n}\n","import {\n Component,\n Prop,\n Event,\n EventEmitter,\n Element,\n Watch,\n State,\n h,\n Host,\n} from '@stencil/core';\nimport type { NavChromeStyle } from '../../nav/nav-chrome';\nimport type { MenuItemData } from '../Menu/menu-types';\nimport { isTabDivider } from '../TabGroup/tab-item-utils';\nimport type { BarNavActionItem, BarNavTab } from './bar-nav-types';\nimport {\n deriveBarNavValueFromUrl,\n parseJsonArrayProp,\n shouldResyncBarNavProps,\n} from './bar-nav-utils';\nimport {\n getTabListFromTabGroup,\n queryWithinComponentHost,\n type QueryableHost,\n} from './bar-nav-dom-utils';\nimport {\n getActiveTab,\n getActiveTabLabel,\n tabsOverflowContainer,\n tabsToMenuSections,\n} from './bar-nav-tabs-menu-utils';\nimport {\n CHROME_TRANSITION_END,\n CHROME_TRANSITION_START,\n ChromeTransitionDepth,\n createRafCoalescer,\n readChromeTransitionSource,\n readChromeTransitionPhase,\n} from '../../nav/chrome-transition';\n\n@Component({\n tag: 'ds-bar-nav',\n styleUrl: 'BarNav.css',\n scoped: true,\n})\nexport class BarNav {\n /** Style slot: `dashboard` or `settings`. Colors match for now. */\n @Prop({ attribute: 'nav-style', reflect: true }) navStyle: NavChromeStyle = 'dashboard';\n\n @Element() el!: HTMLElement;\n\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 /** 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 @State() private tabsCollapsed = false;\n @State() private menuOpen = false;\n /** When false, defer showing expanded tabs or the collapsed trigger until intrinsic width is measured (avoids a one-frame flash of the full tab row on narrow viewports). */\n @State() private tabLayoutCommitted = false;\n @State() private triggerLabelTruncated = false;\n\n private static readonly HOST_PROP_SYNC_BUDGET = 8;\n private static readonly INTRINSIC_WIDTH_RETRY_MAX = 3;\n private static readonly TAB_LAYOUT_COMMIT_MAX_FRAMES = 16;\n private static readonly OVERFLOW_HYSTERESIS_PX = 8;\n\n private headerEl: HTMLElement | null = null;\n private triggerEl: HTMLButtonElement | null = null;\n private triggerLabelTextEl: HTMLElement | null = null;\n private triggerLabelObserver: ResizeObserver | null = null;\n private menuEl: HTMLDsMenuElement | null = null;\n private visibleTabGroupEl: QueryableHost | null = null;\n private probeTabGroupEl: QueryableHost | null = null;\n private resizeObserver: ResizeObserver | null = null;\n private intrinsicWidthRetryCount = 0;\n private tabLayoutPendingFrames = 0;\n private readonly panelNavTransition = new ChromeTransitionDepth();\n private readonly panelToolsTransition = new ChromeTransitionDepth();\n private readonly overflowCoalescer = createRafCoalescer(() => {\n this.updateTabsCollapsed();\n this.updateTriggerLabelTruncation();\n });\n private chromeTransitionShell: HTMLElement | null = null;\n /** Matches `basePath` once tabs + URL are reconciled for the active section. */\n @State() private committedSection = '';\n /** Section waiting for URL + tabs to catch up after a cross-area navigation. */\n @State() private pendingSection = '';\n\n private get effectiveValue(): string {\n if (this.currentUrl && this.basePath) {\n return this.urlDerivedValue;\n }\n return this.value;\n }\n\n private get activeTabLabel(): string {\n return getActiveTabLabel(this.resolvedTabs, this.effectiveValue);\n }\n\n private get activeTabHasDot(): boolean {\n return !!getActiveTab(this.resolvedTabs, this.effectiveValue)?.dot;\n }\n\n private digestTabsConfig(tabs: BarNavTab[]): string {\n return tabs.map(t => (isTabDivider(t) ? '|' : t.id)).join(',');\n }\n\n private resetTabOverflowLayout() {\n this.tabLayoutCommitted = false;\n this.tabsCollapsed = false;\n this.menuOpen = false;\n this.intrinsicWidthRetryCount = 0;\n this.tabLayoutPendingFrames = 0;\n }\n\n @Watch('tabs')\n @Watch('tabsJson')\n @Watch('actions')\n @Watch('actionsJson')\n @Watch('currentUrl')\n @Watch('basePath')\n onHostPropsChange() {\n this.applyHostProps();\n }\n\n @Watch('value')\n @Watch('urlDerivedValue')\n onSelectionChange() {\n this.syncMenuSections();\n this.scheduleOverflowCheck();\n }\n\n @Watch('menuOpen')\n onMenuOpenChange(open: boolean) {\n if (this.menuEl) {\n this.menuEl.open = open;\n }\n }\n\n @Watch('tabsCollapsed')\n onTabsCollapsedChange(collapsed: boolean, prevCollapsed: boolean | undefined) {\n if (!collapsed) {\n this.menuOpen = false;\n this.menuEl = null;\n this.triggerLabelTruncated = false;\n this.syncTriggerLabelObserver();\n }\n this.scheduleOverflowCheck();\n if (collapsed) {\n requestAnimationFrame(() => {\n this.scheduleOverflowCheck();\n this.syncTriggerLabelObserver();\n });\n }\n if (collapsed || prevCollapsed === undefined || prevCollapsed === collapsed) return;\n requestAnimationFrame(() => this.scheduleOverflowCheck());\n queueMicrotask(() => this.focusVisibleSelectedTab());\n }\n\n componentWillLoad() {\n this.applyHostProps();\n }\n\n componentDidLoad() {\n this.syncHostPropsIfNeeded();\n this.scheduleDeferredHostPropSync();\n this.setupOverflowObserver();\n this.bindChromeTransitionListeners();\n this.scheduleOverflowCheck();\n }\n\n componentDidRender() {\n if (this.chromeOverflowPaused()) {\n this.syncMenuAnchor();\n return;\n }\n this.scheduleOverflowCheck();\n this.syncMenuAnchor();\n if (this.tabsCollapsed) {\n this.updateTriggerLabelTruncation();\n }\n }\n\n disconnectedCallback() {\n this.resizeObserver?.disconnect();\n this.resizeObserver = null;\n this.overflowCoalescer.cancel();\n this.syncTriggerLabelObserver(true);\n this.unbindChromeTransitionListeners();\n }\n\n private bindChromeTransitionListeners() {\n const shell = this.el.closest('ds-app-shell');\n if (!shell) return;\n this.chromeTransitionShell = shell;\n shell.addEventListener(CHROME_TRANSITION_START, this.onChromeTransitionStart);\n shell.addEventListener(CHROME_TRANSITION_END, this.onChromeTransitionEnd);\n }\n\n private unbindChromeTransitionListeners() {\n if (!this.chromeTransitionShell) return;\n this.chromeTransitionShell.removeEventListener(\n CHROME_TRANSITION_START,\n this.onChromeTransitionStart,\n );\n this.chromeTransitionShell.removeEventListener(\n CHROME_TRANSITION_END,\n this.onChromeTransitionEnd,\n );\n this.chromeTransitionShell = null;\n }\n\n private onChromeTransitionStart = (event: Event) => {\n const source = readChromeTransitionSource(event);\n if (source === 'panel-nav') {\n this.panelNavTransition.enter();\n return;\n }\n if (source === 'panel-tools') {\n const phase = readChromeTransitionPhase(event) ?? 'opening';\n if (phase === 'closing') {\n this.panelToolsTransition.enter();\n }\n }\n };\n\n private chromeOverflowPaused(): boolean {\n return this.panelNavTransition.isActive || this.panelToolsTransition.isActive;\n }\n\n private onChromeTransitionEnd = (event: Event) => {\n const source = readChromeTransitionSource(event);\n if (source === 'panel-nav') {\n this.panelNavTransition.exit();\n if (!this.panelNavTransition.isActive) {\n this.scheduleOverflowCheck();\n }\n return;\n }\n if (source === 'panel-tools') {\n this.panelToolsTransition.exit();\n this.scheduleOverflowCheck();\n }\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.applyHostProps();\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 setupOverflowObserver() {\n if (typeof ResizeObserver === 'undefined' || !this.headerEl) return;\n this.resizeObserver?.disconnect();\n this.resizeObserver = new ResizeObserver(() => {\n if (this.chromeOverflowPaused()) return;\n this.scheduleOverflowCheck();\n });\n this.resizeObserver.observe(this.headerEl);\n }\n\n private scheduleOverflowCheck() {\n if (this.chromeOverflowPaused()) return;\n this.overflowCoalescer.schedule();\n }\n\n private syncTriggerLabelObserver(disconnectOnly = false) {\n this.triggerLabelObserver?.disconnect();\n this.triggerLabelObserver = null;\n\n if (disconnectOnly || !this.tabsCollapsed || !this.triggerLabelTextEl) {\n return;\n }\n\n this.updateTriggerLabelTruncation();\n\n if (typeof ResizeObserver === 'undefined') return;\n\n this.triggerLabelObserver = new ResizeObserver(() => this.updateTriggerLabelTruncation());\n this.triggerLabelObserver.observe(this.triggerLabelTextEl);\n if (this.triggerEl) {\n this.triggerLabelObserver.observe(this.triggerEl);\n }\n }\n\n private updateTriggerLabelTruncation() {\n const el = this.triggerLabelTextEl;\n if (!el || !this.tabsCollapsed) {\n if (this.triggerLabelTruncated) {\n this.triggerLabelTruncated = false;\n }\n return;\n }\n\n const truncated = el.scrollWidth > el.clientWidth + 1;\n if (truncated !== this.triggerLabelTruncated) {\n this.triggerLabelTruncated = truncated;\n }\n }\n\n /** Width left for tabs after actions. */\n private getLeftZoneAvailableWidth(): number {\n if (!this.headerEl) return 0;\n\n const headerStyle = getComputedStyle(this.headerEl);\n const gap = parseFloat(headerStyle.columnGap || headerStyle.gap || '0') || 0;\n const actionsEl = this.headerEl.querySelector('.bar-nav__actions') as HTMLElement | null;\n const actionsWidth = actionsEl?.offsetWidth ?? 0;\n const horizontalPadding =\n (parseFloat(headerStyle.paddingLeft) || 0) + (parseFloat(headerStyle.paddingRight) || 0);\n const spacing = this.tabsCollapsed\n ? parseFloat(\n getComputedStyle(this.headerEl).getPropertyValue('--dimension-space-100').trim(),\n ) || 8\n : actionsEl\n ? gap\n : 0;\n const available =\n this.headerEl.clientWidth - horizontalPadding - actionsWidth - spacing;\n\n return Math.max(0, available);\n }\n\n private getTabsIntrinsicWidth(): number {\n const tabList = getTabListFromTabGroup(this.probeTabGroupEl);\n return tabList?.scrollWidth ?? 0;\n }\n\n private scheduleIntrinsicWidthRetry() {\n if (this.intrinsicWidthRetryCount >= BarNav.INTRINSIC_WIDTH_RETRY_MAX) return;\n this.intrinsicWidthRetryCount++;\n requestAnimationFrame(() => this.scheduleOverflowCheck());\n }\n\n private forceTabLayoutCommit(fallbackCollapsed: boolean) {\n this.intrinsicWidthRetryCount = 0;\n this.tabLayoutPendingFrames = 0;\n this.tabsCollapsed = fallbackCollapsed;\n if (!fallbackCollapsed) {\n this.menuOpen = false;\n }\n this.tabLayoutCommitted = true;\n }\n\n private updateTabsCollapsed() {\n if (!this.headerEl || this.resolvedTabs.length === 0 || this.hideTabsForDetailRoute) {\n if (this.tabsCollapsed) {\n this.tabsCollapsed = false;\n this.menuOpen = false;\n }\n this.intrinsicWidthRetryCount = 0;\n this.tabLayoutPendingFrames = 0;\n this.tabLayoutCommitted = true;\n return;\n }\n\n const intrinsicWidth = this.getTabsIntrinsicWidth();\n if (intrinsicWidth === 0) {\n this.tabLayoutPendingFrames += 1;\n if (this.intrinsicWidthRetryCount >= BarNav.INTRINSIC_WIDTH_RETRY_MAX) {\n this.forceTabLayoutCommit(false);\n } else if (this.tabLayoutPendingFrames >= BarNav.TAB_LAYOUT_COMMIT_MAX_FRAMES) {\n this.forceTabLayoutCommit(false);\n } else {\n this.scheduleIntrinsicWidthRetry();\n }\n return;\n }\n this.intrinsicWidthRetryCount = 0;\n this.tabLayoutPendingFrames = 0;\n\n const shouldCollapse = tabsOverflowContainer(\n intrinsicWidth,\n this.getLeftZoneAvailableWidth(),\n this.tabsCollapsed,\n BarNav.OVERFLOW_HYSTERESIS_PX,\n );\n\n if (shouldCollapse !== this.tabsCollapsed) {\n this.tabsCollapsed = shouldCollapse;\n if (!shouldCollapse) {\n this.menuOpen = false;\n }\n }\n\n this.tabLayoutCommitted = true;\n }\n\n private syncMenuAnchor() {\n if (!this.menuEl || !this.triggerEl || !this.tabsCollapsed) return;\n this.menuEl.anchor = this.triggerEl;\n this.syncMenuSections();\n }\n\n private handleMenuSelect(e: Event) {\n const detail = (e as CustomEvent<MenuItemData>).detail;\n if (detail?.value !== undefined) {\n this.selectTab(String(detail.value));\n }\n this.menuOpen = false;\n }\n\n private handleMenuClose() {\n this.menuOpen = false;\n }\n\n /** Batch tabs/basePath/currentUrl updates so URL derivation never runs with a mixed section. */\n private incomingTabs(): BarNavTab[] {\n return this.tabsJson\n ? parseJsonArrayProp(this.tabsJson, [])\n : (this.tabs ?? []);\n }\n\n private incomingActions(): BarNavActionItem[] {\n return this.actionsJson\n ? parseJsonArrayProp(this.actionsJson, [])\n : (this.actions ?? []);\n }\n\n private applyHostProps() {\n const nextBasePath = this.basePath;\n const sectionChanged =\n nextBasePath !== '' &&\n this.committedSection !== '' &&\n nextBasePath !== this.committedSection;\n\n if (sectionChanged) {\n this.urlDerivedValue = '';\n this.committedSection = '';\n this.pendingSection = nextBasePath;\n this.resolvedTabs = [];\n this.resolvedActions = this.incomingActions();\n this.resetTabOverflowLayout();\n return;\n }\n\n if (this.pendingSection) {\n if (!nextBasePath || nextBasePath !== this.pendingSection) {\n return;\n }\n if (!this.currentUrl.startsWith(this.pendingSection)) {\n return;\n }\n const tabs = this.incomingTabs();\n if (tabs.length === 0) {\n return;\n }\n this.pendingSection = '';\n }\n\n const tabs = this.incomingTabs();\n const tabsConfigChanged =\n this.digestTabsConfig(tabs) !== this.digestTabsConfig(this.resolvedTabs);\n\n this.resolvedTabs = tabs;\n this.resolvedActions = this.incomingActions();\n this.syncValueFromUrl();\n this.committedSection = nextBasePath;\n if (tabsConfigChanged) {\n this.resetTabOverflowLayout();\n }\n this.scheduleOverflowCheck();\n }\n\n private syncMenuSections() {\n if (!this.menuEl) return;\n this.menuEl.sections = tabsToMenuSections(this.resolvedTabs, this.effectiveValue);\n }\n\n private syncValueFromUrl() {\n const tabs = this.incomingTabs();\n\n if (!this.currentUrl || !this.basePath) {\n this.urlDerivedValue = '';\n this.hideTabsForDetailRoute = false;\n return;\n }\n\n if (!this.currentUrl.startsWith(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 tabs,\n );\n this.urlDerivedValue = value;\n this.hideTabsForDetailRoute = hideTabs;\n }\n\n private selectTab(id: string) {\n this.value = id;\n this.dsTabChange.emit(id);\n }\n\n /** Move focus to the selected tab in the visible tab group (after expand). */\n private focusVisibleSelectedTab() {\n const tab = queryWithinComponentHost(\n this.visibleTabGroupEl,\n `[data-tab-id=\"${this.effectiveValue}\"]`,\n );\n tab?.focus({ preventScroll: true });\n }\n\n private handleTabChange(e: Event) {\n this.selectTab((e as CustomEvent<string>).detail);\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 private toggleTabMenu() {\n if (this.menuOpen) {\n this.menuOpen = false;\n return;\n }\n\n if (this.menuEl && this.triggerEl) {\n this.menuEl.anchor = this.triggerEl;\n this.menuEl.minWidth = `${this.triggerEl.offsetWidth}px`;\n this.syncMenuSections();\n }\n\n this.menuOpen = true;\n }\n\n private handleTriggerKeyDown(e: KeyboardEvent) {\n if (!this.tabsCollapsed) return;\n\n if (!this.menuOpen) {\n if (e.key === 'ArrowDown' || e.key === 'ArrowUp' || e.key === 'Enter' || e.key === ' ') {\n e.preventDefault();\n this.toggleTabMenu();\n }\n return;\n }\n\n if (e.key === 'Escape') {\n e.preventDefault();\n this.menuOpen = false;\n this.triggerEl?.focus();\n }\n }\n\n render() {\n const sectionReady =\n this.basePath !== '' && this.committedSection === this.basePath;\n const hasTabs =\n sectionReady && this.resolvedTabs.length > 0 && !this.hideTabsForDetailRoute;\n const tabGroupKey = this.basePath || 'no-section';\n\n return (\n <Host>\n <header\n class={{\n 'bar-nav': true,\n 'bar-nav--dashboard': this.navStyle === 'dashboard',\n 'bar-nav--settings': this.navStyle === 'settings',\n 'bar-nav--tabs-collapsed':\n hasTabs && this.tabLayoutCommitted && this.tabsCollapsed,\n }}\n ref={el => {\n this.headerEl = el as HTMLElement;\n if (el && !this.resizeObserver) {\n this.setupOverflowObserver();\n }\n }}\n >\n {hasTabs && (\n <div class=\"bar-nav__tabs-probe\" aria-hidden=\"true\" inert>\n <ds-tab-group-nav\n key={`probe-${tabGroupKey}`}\n ref={el => {\n const next = el ?? null;\n if (next === this.probeTabGroupEl) return;\n this.probeTabGroupEl = next;\n if (next) {\n this.intrinsicWidthRetryCount = 0;\n this.tabLayoutPendingFrames = 0;\n this.scheduleOverflowCheck();\n }\n }}\n tabs={this.resolvedTabs}\n value={this.effectiveValue}\n />\n </div>\n )}\n\n {hasTabs && this.tabLayoutCommitted && !this.tabsCollapsed && (\n <div class=\"bar-nav__left\">\n <ds-tab-group-nav\n key={`visible-${tabGroupKey}`}\n class=\"bar-nav__tabs-visible\"\n ref={el => {\n this.visibleTabGroupEl = el ?? null;\n }}\n tabs={this.resolvedTabs}\n value={this.effectiveValue}\n onDsChange={(e: Event) => this.handleTabChange(e)}\n />\n </div>\n )}\n\n {hasTabs && !this.tabLayoutCommitted && (\n <div class=\"bar-nav__tabs-pending\" aria-hidden=\"true\" />\n )}\n\n {!hasTabs && this.heading && (\n <span class=\"bar-nav__heading text-body-medium-emphasis\">{this.heading}</span>\n )}\n\n {hasTabs && this.tabLayoutCommitted && this.tabsCollapsed && (\n <button\n type=\"button\"\n class={{\n 'bar-nav__tab-trigger': true,\n 'bar-nav__tab-trigger--open': this.menuOpen,\n }}\n ref={el => {\n this.triggerEl = el as HTMLButtonElement;\n }}\n aria-haspopup=\"menu\"\n aria-expanded={this.menuOpen}\n aria-label={`${this.activeTabLabel} tabs`}\n onClick={() => this.toggleTabMenu()}\n onKeyDown={(e: KeyboardEvent) => this.handleTriggerKeyDown(e)}\n >\n <span\n class={{\n 'bar-nav__tab-trigger-label': true,\n 'bar-nav__tab-trigger-label--dot': this.activeTabHasDot,\n 'bar-nav__tab-trigger-label--truncated': this.triggerLabelTruncated,\n }}\n >\n <span\n class={{\n 'bar-nav__tab-trigger-label-text': true,\n 'bar-nav__tab-trigger-label-text--truncated': this.triggerLabelTruncated,\n 'text-body-medium-emphasis': true,\n }}\n ref={el => {\n const next = el ?? null;\n if (next === this.triggerLabelTextEl) return;\n this.triggerLabelTextEl = next;\n if (this.tabsCollapsed) {\n this.syncTriggerLabelObserver();\n }\n }}\n >\n {this.activeTabLabel}\n </span>\n {this.activeTabHasDot && (\n <ds-badge\n class=\"bar-nav__tab-trigger-dot\"\n variant=\"dot\"\n background=\"var(--_bar-nav-bg)\"\n label=\"\"\n aria-hidden=\"true\"\n />\n )}\n </span>\n <ds-icon\n name=\"ChevronDown\"\n size=\"md\"\n color=\"inherit\"\n class=\"bar-nav__tab-trigger-chevron\"\n />\n </button>\n )}\n\n {hasTabs &&\n this.tabLayoutCommitted &&\n this.tabsCollapsed &&\n this.resolvedActions.length > 0 && (\n <div class=\"bar-nav__between\" aria-hidden=\"true\" />\n )}\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 onDsChange={(e: Event) => this.handleActionChange(action.id, e)}\n />\n ))}\n </div>\n )}\n\n </header>\n\n {hasTabs && this.tabLayoutCommitted && this.tabsCollapsed && (\n <ds-menu\n ref={el => {\n this.menuEl = (el as HTMLDsMenuElement) ?? null;\n }}\n open={this.menuOpen}\n sections={tabsToMenuSections(this.resolvedTabs, this.effectiveValue)}\n side=\"bottom\"\n align=\"start\"\n onDsSelect={(e: Event) => this.handleMenuSelect(e)}\n onDsClose={() => this.handleMenuClose()}\n />\n )}\n </Host>\n );\n }\n}\n"],"mappings":"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","ignoreList":[]}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{p as e,H as a,c as n,h as t,a as s,t as i}from"./index.js";import{d as o}from"./p-CiEc8Nwx.js";import{d as r}from"./p-DqBCQRRd.js";import{d as l}from"./p-C9za7IDu.js";const d="data-nav-style";function c(e){if(e==="dashboard"||e==="settings")return e;return undefined}function p(e,a,n){const t=c(a);if(t)return t;const s=typeof document!=="undefined"?document.documentElement.getAttribute(d):null;const i=c(s);if(i)return i;return e}function v(e,a){return e!==a}function h(e,a){return e===a||e.startsWith(`${a}/`)}function f(e,a){if(!e)return"";let n="";let t=0;for(const s of a){if(!s.href)continue;if(h(e,s.href)&&s.href.length>t){n=s.id;t=s.href.length}}return n}function m(e){return e.reduce(((e,a)=>e+a.items.length),0)}function u(e){if(Array.isArray(e))return e;if(typeof e==="string"){try{const a=JSON.parse(e);return Array.isArray(a)?a:[]}catch{return[]}}return[]}function _(e,a){if(e)return true;if(a===null)return false;return a!=="false"}function b(e,a){return m(e)===0&&m(u(a))>0}const g="ds-panel-nav-user-menu-anchor";const y=()=>`.scroll-edge-fade.sc-ds-panel-nav{--ds-scroll-edge-fade-size:var(--dimension-size-600)}.scroll-edge-fade--top.sc-ds-panel-nav{-webkit-mask-image:linear-gradient(to bottom, #000 0, #000 50%, transparent 100%);mask-image:linear-gradient(to bottom, #000 0, #000 50%, transparent 100%);-webkit-mask-size:100% 100%;mask-size:100% 100%}.scroll-edge-fade--bottom.sc-ds-panel-nav{-webkit-mask-image:linear-gradient( to bottom, #000 0, #000 calc(100% - var(--ds-scroll-edge-fade-size)), transparent 100% );mask-image:linear-gradient( to bottom, #000 0, #000 calc(100% - var(--ds-scroll-edge-fade-size)), transparent 100% );-webkit-mask-size:100% 100%;mask-size:100% 100%}.scroll-edge-fade--left.sc-ds-panel-nav{-webkit-mask-image:linear-gradient( to right, #000 0, #000 calc(100% - var(--ds-scroll-edge-fade-size)), transparent 100% );mask-image:linear-gradient( to right, #000 0, #000 calc(100% - var(--ds-scroll-edge-fade-size)), transparent 100% );-webkit-mask-size:100% 100%;mask-size:100% 100%}.scroll-edge-fade--right.sc-ds-panel-nav{-webkit-mask-image:linear-gradient( to left, #000 0, #000 calc(100% - var(--ds-scroll-edge-fade-size)), transparent 100% );mask-image:linear-gradient( to left, #000 0, #000 calc(100% - var(--ds-scroll-edge-fade-size)), transparent 100% );-webkit-mask-size:100% 100%;mask-size:100% 100%}.scroll-edge-fade--at-end.sc-ds-panel-nav{-webkit-mask-image:none;mask-image:none}`;const x=()=>`.panel-nav.sc-ds-panel-nav{--_nav-width:var(--dimension-panel-width-2xs);--_nav-width-collapsed:var(--dimension-size-600);--_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-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)}ds-app-shell[gradient].sc-ds-panel-nav-h .panel-nav.sc-ds-panel-nav,ds-app-shell[gradient] .sc-ds-panel-nav-h .panel-nav.sc-ds-panel-nav,ds-app-shell[grid].sc-ds-panel-nav-h .panel-nav.sc-ds-panel-nav,ds-app-shell[grid] .sc-ds-panel-nav-h .panel-nav.sc-ds-panel-nav{background-color:transparent}.panel-nav--dashboard.sc-ds-panel-nav,.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;position:relative;width:var(--_nav-width);min-width:var(--_nav-width);height:100%;background-color:var(--_nav-bg);border-right:var(--dimension-stroke-width-012, 1px) solid var(--_nav-border);box-sizing:border-box;overflow:visible;transition:width var(--_nav-expand-motion), min-width var(--_nav-expand-motion), background-color var(--effect-motion-short-3, 200ms ease), border-color var(--effect-motion-short-3, 200ms ease);font-family:var(--typography-font-family, system-ui);isolation:isolate}.panel-nav__header.sc-ds-panel-nav,.panel-nav__body.sc-ds-panel-nav,.panel-nav__footer.sc-ds-panel-nav{position:relative;z-index:3}.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;pointer-events:none;transition: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;opacity:1}.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:flex;align-items:center;justify-content:center;position:absolute;inset:0;z-index:2;opacity:0;pointer-events:none}.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{opacity:1}.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:none}.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;pointer-events:none;transition:right var(--_nav-expand-motion), top var(--_nav-expand-motion)}.panel-nav--collapsed.sc-ds-panel-nav .panel-nav__item-dot.sc-ds-panel-nav{right:var(--dimension-space-075);top:var(--dimension-space-075)}.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-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-025, 2px);cursor:pointer;color:var(--_nav-fg-secondary);box-sizing:border-box;background:transparent;transition:color var(--effect-motion-short-3, 200ms ease)}.panel-nav__footer-btn.sc-ds-panel-nav::after{content:'';position:absolute;inset:0;border-radius:var(--dimension-radius-025, 2px);background:transparent;pointer-events:none;transition:none}.panel-nav__footer-btn.sc-ds-panel-nav:hover::after{background:var(--_nav-hover);transition:none}.panel-nav__footer-btn.sc-ds-panel-nav:active::after{background:var(--_nav-pressed);transition:none}.panel-nav__footer-btn.sc-ds-panel-nav:focus-visible{outline:none}.panel-nav__footer-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__footer-btn.sc-ds-panel-nav ds-icon.sc-ds-panel-nav{position:relative;z-index:1}.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:20;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-foreground-primary);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 k=e(class e extends a{constructor(e){super();if(e!==false){this.__registerHost()}this.dsNavSelect=n(this,"dsNavSelect");this.dsNavToggle=n(this,"dsNavToggle");this.dsChromeTransitionStart=n(this,"dsChromeTransitionStart");this.dsChromeTransitionEnd=n(this,"dsChromeTransitionEnd");this.dsNavFooterAction=n(this,"dsNavFooterAction");this.dsNavUserAction=n(this,"dsNavUserAction");this.navStyle="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.renderedStyle="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}get effectiveDisableViewTransition(){return _(this.disableViewTransition,this.el.getAttribute("disable-view-transition"))}onCollapsedChange(){this.startCollapseAnimation()}onBreakpointChange(){this.disconnectResizeObserver();if(this.breakpoint>0)this.connectResizeObserver()}onGroupsChange(e){this.parsedGroups=u(e);this.syncRovingIndex();this.syncActiveFromUrl()}onActiveIdChange(){this.syncRovingIndex()}onCurrentUrlChange(){this.syncActiveFromUrl()}onNavStyleChange(e){this.renderedStyle=e}componentWillLoad(){this.renderedStyle=p(this.navStyle,this.el.getAttribute("nav-style"));if(this.storageKey){try{const e=localStorage.getItem(this.storageKey);if(e!==null)this.collapsed=e==="true"}catch{}}this.onGroupsChange(this.groups);this.syncActiveFromUrl()}componentDidLoad(){this.syncHostPropsIfNeeded();this.scheduleDeferredHostPropSync();this.checkScroll();if(this.breakpoint>0)this.connectResizeObserver()}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;this.dsChromeTransitionStart.emit({source:"panel-nav"});const e=this.el.querySelector(".panel-nav");if(this.transitionEndHandler){e?.removeEventListener("transitionend",this.transitionEndHandler)}this.transitionEndHandler=a=>{if(a.propertyName==="width"){this.isAnimating=false;this.dsChromeTransitionEnd.emit({source:"panel-nav"});e?.removeEventListener("transitionend",this.transitionEndHandler)}};e?.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=f(this.currentUrl,this.getAllItems())}syncHostPropsIfNeeded(){if(v(this.renderedStyle,this.navStyle)){this.renderedStyle=this.navStyle}if(b(this.parsedGroups,this.groups)){this.onGroupsChange(this.groups)}else if(this.currentUrl){this.syncActiveFromUrl()}}scheduleDeferredHostPropSync(){let a=e.HOST_PROP_SYNC_BUDGET;const n=()=>{this.syncHostPropsIfNeeded();if(--a>0){requestAnimationFrame(n)}};queueMicrotask(n)}applyToggle(e){this.collapsed=e;if(this.storageKey){try{localStorage.setItem(this.storageKey,String(e))}catch{}}this.dsNavToggle.emit(e)}getAllItems(){return this.parsedGroups.flatMap((e=>e.items))}syncRovingIndex(){const e=this.getAllItems().findIndex((e=>e.id===this.effectiveActiveId));this.rovingIndex=e>=0?e:0}handleItemKeyDown(e,a){const n=this.getAllItems().length;let t;switch(e.key){case"ArrowDown":e.preventDefault();t=(a+1)%n;break;case"ArrowUp":e.preventDefault();t=(a-1+n)%n;break;case"Home":e.preventDefault();t=0;break;case"End":e.preventDefault();t=n-1;break;default:return}this.rovingIndex=t;const s=Array.from(this.el.querySelectorAll(".panel-nav__body .panel-nav__item"));s[t]?.focus()}checkScroll(){const e=this.el.querySelector(".panel-nav__body");if(!e)return;const a=e.scrollHeight-e.scrollTop-e.clientHeight;this.atBottom=a<2}handleBodyScroll(e){const a=e.target;const n=a.scrollHeight-a.scrollTop-a.clientHeight;this.atBottom=n<2}handleItemClick(e){this.dsNavSelect.emit(e)}handleToggle(){this.applyToggle(!this.collapsed)}handleFooterAction(){this.dsNavFooterAction.emit()}handleUserAction(e){const a=e.currentTarget;this.dsNavUserAction.emit({anchor:a})}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(e){e.preventDefault();e.stopPropagation();const a=this.collapsed||this.viewportNarrow;this.isDragging=true;this.dragStartX=e.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=e=>{const n=e.clientX-this.dragStartX;this.lastDeltaX=Math.abs(n);if(this.didSnap)return;if(!a&&n<-8){this.applyToggle(true);this.didSnap=true}else if(a&&n>8){this.applyToggle(false);this.didSnap=true}};const t=()=>{if(!this.didSnap&&this.lastDeltaX<3){this.applyToggle(!a)}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(e,a,n){const s=e.id===this.effectiveActiveId;const i=[t("span",{class:"panel-nav__item-icon"},t("ds-icon",{name:e.icon,size:"md",color:"inherit",flag:e.flag})),t("span",{class:"panel-nav__item-label"},t("span",{class:"panel-nav__item-label-text"},t("ds-text",{as:"span",variant:s?"text-body-medium-emphasis":"text-body-medium",color:"inherit"},e.label))),e.dot&&t("ds-badge",{class:"panel-nav__item-dot",variant:"dot",background:"var(--_nav-bg)",label:"","aria-hidden":"true"})];const o={class:{"panel-nav__item":true,"panel-nav__item--active":s},"aria-current":s?"page":undefined,title:n?e.label:undefined,tabIndex:a===this.rovingIndex?0:-1,onClick:()=>this.handleItemClick(e.id),onKeyDown:e=>this.handleItemKeyDown(e,a),onFocus:()=>{this.rovingIndex=a}};const r=this.routerMode==="anchor"&&e.href;return r?t("a",{...o,href:e.href},i):t("button",{...o,type:"button"},i)}render(){const e=this.renderedStyle;const a=e==="dashboard";const n=this.navStyle==="dashboard";const i=this.collapsed||this.viewportNarrow;const{userName:o,userInitial:r}=this;const l={"panel-nav":true,"panel-nav--dashboard":n,"panel-nav--settings":!n,"panel-nav--collapsed":i,"panel-nav--animating":this.isAnimating,"panel-nav--at-bottom":this.atBottom};return t(s,{key:"39ea73fbe1b5056ac8ad6ce31c81760be97f10ae",style:{display:"block",position:"relative"}},t("nav",{key:"536ac8cd1afe021a4b485426fd2cc9fa5013d6f1",class:l,"aria-label":a?"Dashboard navigation":"Settings navigation"},t("div",{key:"46476c929dc8013cdc36bca6955b061169a9f3c8",class:"panel-nav__header"},t("button",{key:"a4cc9c237158ea676e54e9ced00d06559cb86fe2",type:"button",class:"panel-nav__header-btn",onClick:()=>this.handleToggle(),"aria-label":i?"Expand navigation":"Collapse navigation","aria-expanded":i?"false":"true"},t("span",{key:"d032cc3b28f866870310f27fb10e8cc9cfc21790",class:"panel-nav__header-logo","aria-hidden":"true"},t("svg",{key:"341e6d531d8dcb06d3dcee4a0a2ee52724731790",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:"6a5149b0e8f1880233fe6a2a548b60818406b4e1",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:"479330467c65cfa2cd7b06dfdd7736731043732d",class:"panel-nav__header-toggle","aria-hidden":"true"},t("ds-icon",{key:"51b77b233587a3efd5750e0f98b2bdaf482c25f0",name:i?"LeftExpandB":"LeftCollapseB",size:"md",color:"inherit"})))),t("div",{key:"af08e363639b45d60afd077ead11ab3c4d387450",class:{"panel-nav__body":true,"scroll-edge-fade":true,"scroll-edge-fade--bottom":true,"scroll-edge-fade--at-end":this.atBottom},onScroll:e=>this.handleBodyScroll(e)},(()=>{let e=0;return this.parsedGroups.map((a=>t("div",{class:"panel-nav__group"},a.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"},a.label))),a.items.map((a=>this.renderNavItem(a,e++,i))))))})()),t("div",{key:"aad0c7b541b0c81c1326881fea9be22f9b2a509a",class:"panel-nav__footer"},t("button",{key:"7df29ded013a586a9d4718e79db86bb37e377b50",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:"c9a8809b0a684ce022b7285ca10b9b60e98f9c90",name:a?"Gear":"Dashboard",size:"md",color:"inherit"})),t("button",{key:"7c33a4d3d82ff88929429f3b58288691d5a7e2da",type:"button",id:g,class:"panel-nav__item panel-nav__footer-user","aria-label":i?`User: ${o}`:`User menu for ${o}`,onClick:e=>this.handleUserAction(e)},t("span",{key:"6b4fbe6542f37601c05f88ccb58f3adb591b67fe",class:"panel-nav__item-label panel-nav__footer-user-label"},t("span",{key:"df89c93f87815956dfb0550de25171d1f8b9b6e8",class:"panel-nav__item-label-text"},t("ds-text",{key:"d98cc9e82dcadba1cf402eeb8fe99dad4284d19a",as:"span",variant:"text-body-medium-emphasis",color:"inherit"},o))),t("span",{key:"85988e4ec7f9d77737ee441786865acf6ae160d4",class:"panel-nav__item-icon panel-nav__footer-user-icon","aria-hidden":"true"},t("span",{key:"ab7179242a824f0105c660367cb379616e3877a2",class:"panel-nav__footer-icon-expanded"},t("ds-icon",{key:"f1a2cb692f13a77c940da25fc3a8128dc5042aaf",name:"ChevronUpDown",size:"md",color:"inherit"})),t("span",{key:"969b63eeaee1201632f2d0541da855182cf4c469",class:"panel-nav__footer-icon-collapsed"},t("ds-icon",{key:"038ca91e26795dfcf72468fc7f1a54086ca11a00",name:"Circle",size:"md",color:"inherit"}),t("span",{key:"661d1142974f69bd0bf9dfe286578d5a267d10b7",class:"panel-nav__user-initial"},t("ds-text",{key:"d54cd929dd50097539e682aa3a91a82814844c66",as:"span",variant:"text-caption-emphasis",color:"inherit"},r))))))),!this.viewportNarrow&&t("div",{key:"4d0743eafefb77551e3530bdbb988db63eeecf8d",class:{"panel-nav__resize-handle":true,"panel-nav__resize-handle--overlay":this.showEdgeOverlay},onMouseEnter:()=>this.handleResizeHandleMouseEnter(),onMouseLeave:()=>this.handleResizeHandleMouseLeave(),onMouseDown:e=>this.handleResizeHandleMouseDown(e),"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}],navStyle:[{onNavStyleChange:0}]}}static get style(){return y()+x()}},[2,"ds-panel-nav",{navStyle:[513,"nav-style"],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"],renderedStyle:[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}],navStyle:[{onNavStyleChange:0}]}]);k.HOST_PROP_SYNC_BUDGET=8;function w(){if(typeof customElements==="undefined"){return}const e=["ds-panel-nav","ds-badge","ds-icon","ds-text"];e.forEach((e=>{switch(e){case"ds-panel-nav":if(!customElements.get(i(e))){customElements.define(i(e),k)}break;case"ds-badge":if(!customElements.get(i(e))){o()}break;case"ds-icon":if(!customElements.get(i(e))){r()}break;case"ds-text":if(!customElements.get(i(e))){l()}break}}))}w();const z=k;const C=w;export{z as DsPanelNav,C as defineCustomElement};
|
|
1
|
+
import{p as e,H as a,c as n,h as t,a as i,t as s}from"./index.js";import{d as o}from"./p-CiEc8Nwx.js";import{d as r}from"./p-DqBCQRRd.js";import{d as l}from"./p-C9za7IDu.js";const d="data-nav-style";function c(e){if(e==="dashboard"||e==="settings")return e;return undefined}function p(e,a,n){const t=c(a);if(t)return t;const i=typeof document!=="undefined"?document.documentElement.getAttribute(d):null;const s=c(i);if(s)return s;return e}function v(e,a){return e!==a}function h(e,a){return e===a||e.startsWith(`${a}/`)}function f(e,a){if(!e)return"";let n="";let t=0;for(const i of a){if(!i.href)continue;if(h(e,i.href)&&i.href.length>t){n=i.id;t=i.href.length}}return n}function m(e){return e.reduce(((e,a)=>e+a.items.length),0)}function u(e){if(Array.isArray(e))return e;if(typeof e==="string"){try{const a=JSON.parse(e);return Array.isArray(a)?a:[]}catch{return[]}}return[]}function b(e,a){if(e)return true;if(a===null)return false;return a!=="false"}function _(e,a){return m(e)===0&&m(u(a))>0}const g="ds-panel-nav-user-menu-anchor";const y=()=>`.scroll-edge-fade.sc-ds-panel-nav{--ds-scroll-edge-fade-size:var(--dimension-size-600)}.scroll-edge-fade--top.sc-ds-panel-nav{-webkit-mask-image:linear-gradient(to bottom, #000 0, #000 50%, transparent 100%);mask-image:linear-gradient(to bottom, #000 0, #000 50%, transparent 100%);-webkit-mask-size:100% 100%;mask-size:100% 100%}.scroll-edge-fade--bottom.sc-ds-panel-nav{-webkit-mask-image:linear-gradient( to bottom, #000 0, #000 calc(100% - var(--ds-scroll-edge-fade-size)), transparent 100% );mask-image:linear-gradient( to bottom, #000 0, #000 calc(100% - var(--ds-scroll-edge-fade-size)), transparent 100% );-webkit-mask-size:100% 100%;mask-size:100% 100%}.scroll-edge-fade--left.sc-ds-panel-nav{-webkit-mask-image:linear-gradient( to right, #000 0, #000 calc(100% - var(--ds-scroll-edge-fade-size)), transparent 100% );mask-image:linear-gradient( to right, #000 0, #000 calc(100% - var(--ds-scroll-edge-fade-size)), transparent 100% );-webkit-mask-size:100% 100%;mask-size:100% 100%}.scroll-edge-fade--right.sc-ds-panel-nav{-webkit-mask-image:linear-gradient( to left, #000 0, #000 calc(100% - var(--ds-scroll-edge-fade-size)), transparent 100% );mask-image:linear-gradient( to left, #000 0, #000 calc(100% - var(--ds-scroll-edge-fade-size)), transparent 100% );-webkit-mask-size:100% 100%;mask-size:100% 100%}.scroll-edge-fade--at-end.sc-ds-panel-nav{-webkit-mask-image:none;mask-image:none}`;const x=()=>`.panel-nav.sc-ds-panel-nav{--_nav-width:var(--dimension-panel-width-2xs);--_nav-width-collapsed:var(--dimension-size-600);--_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-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)}ds-app-shell[gradient].sc-ds-panel-nav-h .panel-nav.sc-ds-panel-nav,ds-app-shell[gradient] .sc-ds-panel-nav-h .panel-nav.sc-ds-panel-nav,ds-app-shell[grid].sc-ds-panel-nav-h .panel-nav.sc-ds-panel-nav,ds-app-shell[grid] .sc-ds-panel-nav-h .panel-nav.sc-ds-panel-nav{background-color:transparent}.panel-nav--dashboard.sc-ds-panel-nav,.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;position:relative;width:var(--_nav-width);min-width:var(--_nav-width);height:100%;background-color:var(--_nav-bg);border-right:var(--dimension-stroke-width-012, 1px) solid var(--_nav-border);box-sizing:border-box;overflow:visible;transition:width var(--_nav-expand-motion), min-width var(--_nav-expand-motion), background-color var(--effect-motion-short-3, 200ms ease), border-color var(--effect-motion-short-3, 200ms ease);font-family:var(--typography-font-family, system-ui);isolation:isolate}.panel-nav__header.sc-ds-panel-nav,.panel-nav__body.sc-ds-panel-nav,.panel-nav__footer.sc-ds-panel-nav{position:relative;z-index:3}.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;pointer-events:none;transition: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;opacity:1}.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:flex;align-items:center;justify-content:center;position:absolute;inset:0;z-index:2;opacity:0;pointer-events:none}.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{opacity:1}.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:none}.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;pointer-events:none;transition:right var(--_nav-expand-motion), top var(--_nav-expand-motion)}.panel-nav--collapsed.sc-ds-panel-nav .panel-nav__item-dot.sc-ds-panel-nav{right:var(--dimension-space-075);top:var(--dimension-space-075)}.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-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-025, 2px);cursor:pointer;color:var(--_nav-fg-secondary);box-sizing:border-box;background:transparent;transition:color var(--effect-motion-short-3, 200ms ease)}.panel-nav__footer-btn.sc-ds-panel-nav::after{content:'';position:absolute;inset:0;border-radius:var(--dimension-radius-025, 2px);background:transparent;pointer-events:none;transition:none}.panel-nav__footer-btn.sc-ds-panel-nav:hover::after{background:var(--_nav-hover);transition:none}.panel-nav__footer-btn.sc-ds-panel-nav:active::after{background:var(--_nav-pressed);transition:none}.panel-nav__footer-btn.sc-ds-panel-nav:focus-visible{outline:none}.panel-nav__footer-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__footer-btn.sc-ds-panel-nav ds-icon.sc-ds-panel-nav{position:relative;z-index:1}.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:20;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-foreground-primary);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 k=e(class e extends a{constructor(e){super();if(e!==false){this.__registerHost()}this.dsNavSelect=n(this,"dsNavSelect");this.dsNavToggle=n(this,"dsNavToggle");this.dsChromeTransitionStart=n(this,"dsChromeTransitionStart");this.dsChromeTransitionEnd=n(this,"dsChromeTransitionEnd");this.dsNavFooterAction=n(this,"dsNavFooterAction");this.dsNavUserAction=n(this,"dsNavUserAction");this.navStyle="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.renderedStyle="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}get effectiveDisableViewTransition(){return b(this.disableViewTransition,this.el.getAttribute("disable-view-transition"))}onCollapsedChange(e,a){if(a===undefined)return;this.startCollapseAnimation()}onViewportNarrowChange(e,a){if(a===undefined)return;this.startCollapseAnimation()}onBreakpointChange(){this.disconnectResizeObserver();if(this.breakpoint>0)this.connectResizeObserver()}onGroupsChange(e){this.parsedGroups=u(e);this.syncRovingIndex();this.syncActiveFromUrl()}onActiveIdChange(){this.syncRovingIndex()}onCurrentUrlChange(){this.syncActiveFromUrl()}onNavStyleChange(e){this.renderedStyle=e}componentWillLoad(){this.renderedStyle=p(this.navStyle,this.el.getAttribute("nav-style"));if(this.storageKey){try{const e=localStorage.getItem(this.storageKey);if(e!==null)this.collapsed=e==="true"}catch{}}this.onGroupsChange(this.groups);this.syncActiveFromUrl()}componentDidLoad(){this.syncHostPropsIfNeeded();this.scheduleDeferredHostPropSync();this.checkScroll();if(this.breakpoint>0)this.connectResizeObserver()}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;this.dsChromeTransitionStart.emit({source:"panel-nav"});const e=this.el.querySelector(".panel-nav");const a=e?.getBoundingClientRect().width??0;if(this.transitionEndHandler){e?.removeEventListener("transitionend",this.transitionEndHandler)}this.transitionEndHandler=a=>{if(a.propertyName==="width"){this.finishCollapseAnimation(e)}};e?.addEventListener("transitionend",this.transitionEndHandler);requestAnimationFrame((()=>{requestAnimationFrame((()=>{if(!this.isAnimating)return;const n=e?.getBoundingClientRect().width??0;if(Math.abs(n-a)<1){this.finishCollapseAnimation(e)}}))}))}finishCollapseAnimation(e){if(!this.isAnimating)return;this.isAnimating=false;this.dsChromeTransitionEnd.emit({source:"panel-nav"});if(this.transitionEndHandler){e?.removeEventListener("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=f(this.currentUrl,this.getAllItems())}syncHostPropsIfNeeded(){if(v(this.renderedStyle,this.navStyle)){this.renderedStyle=this.navStyle}if(_(this.parsedGroups,this.groups)){this.onGroupsChange(this.groups)}else if(this.currentUrl){this.syncActiveFromUrl()}}scheduleDeferredHostPropSync(){let a=e.HOST_PROP_SYNC_BUDGET;const n=()=>{this.syncHostPropsIfNeeded();if(--a>0){requestAnimationFrame(n)}};queueMicrotask(n)}applyToggle(e){this.collapsed=e;if(this.storageKey){try{localStorage.setItem(this.storageKey,String(e))}catch{}}this.dsNavToggle.emit(e)}getAllItems(){return this.parsedGroups.flatMap((e=>e.items))}syncRovingIndex(){const e=this.getAllItems().findIndex((e=>e.id===this.effectiveActiveId));this.rovingIndex=e>=0?e:0}handleItemKeyDown(e,a){const n=this.getAllItems().length;let t;switch(e.key){case"ArrowDown":e.preventDefault();t=(a+1)%n;break;case"ArrowUp":e.preventDefault();t=(a-1+n)%n;break;case"Home":e.preventDefault();t=0;break;case"End":e.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 e=this.el.querySelector(".panel-nav__body");if(!e)return;const a=e.scrollHeight-e.scrollTop-e.clientHeight;this.atBottom=a<2}handleBodyScroll(e){const a=e.target;const n=a.scrollHeight-a.scrollTop-a.clientHeight;this.atBottom=n<2}handleItemClick(e){this.dsNavSelect.emit(e)}handleToggle(){this.applyToggle(!this.collapsed)}handleFooterAction(){this.dsNavFooterAction.emit()}handleUserAction(e){const a=e.currentTarget;this.dsNavUserAction.emit({anchor:a})}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(e){e.preventDefault();e.stopPropagation();const a=this.collapsed||this.viewportNarrow;this.isDragging=true;this.dragStartX=e.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=e=>{const n=e.clientX-this.dragStartX;this.lastDeltaX=Math.abs(n);if(this.didSnap)return;if(!a&&n<-8){this.applyToggle(true);this.didSnap=true}else if(a&&n>8){this.applyToggle(false);this.didSnap=true}};const t=()=>{if(!this.didSnap&&this.lastDeltaX<3){this.applyToggle(!a)}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(e,a,n){const i=e.id===this.effectiveActiveId;const s=[t("span",{class:"panel-nav__item-icon"},t("ds-icon",{name:e.icon,size:"md",color:"inherit",flag:e.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"},e.label))),e.dot&&t("ds-badge",{class:"panel-nav__item-dot",variant:"dot",background:"var(--_nav-bg)",label:"","aria-hidden":"true"})];const o={class:{"panel-nav__item":true,"panel-nav__item--active":i},"aria-current":i?"page":undefined,title:n?e.label:undefined,tabIndex:a===this.rovingIndex?0:-1,onClick:()=>this.handleItemClick(e.id),onKeyDown:e=>this.handleItemKeyDown(e,a),onFocus:()=>{this.rovingIndex=a}};const r=this.routerMode==="anchor"&&e.href;return r?t("a",{...o,href:e.href},s):t("button",{...o,type:"button"},s)}render(){const e=this.renderedStyle;const a=e==="dashboard";const n=this.navStyle==="dashboard";const s=this.collapsed||this.viewportNarrow;const{userName:o,userInitial:r}=this;const l={"panel-nav":true,"panel-nav--dashboard":n,"panel-nav--settings":!n,"panel-nav--collapsed":s,"panel-nav--animating":this.isAnimating,"panel-nav--at-bottom":this.atBottom};return t(i,{key:"e41e517fc7bb587faf489ba45ea737bbd3dbc359",style:{display:"block",position:"relative"}},t("nav",{key:"e9e0d6a2e33a49cbc9c0f8b9ee16b85a32ccd897",class:l,"aria-label":a?"Dashboard navigation":"Settings navigation"},t("div",{key:"9f9bbf9f0e6a5f6baba8bcd78d3a0043272f011d",class:"panel-nav__header"},t("button",{key:"4d6cb47b636896750fdde25f86f534836863554b",type:"button",class:"panel-nav__header-btn",onClick:()=>this.handleToggle(),"aria-label":s?"Expand navigation":"Collapse navigation","aria-expanded":s?"false":"true"},t("span",{key:"6a86a42d85aa60eb71d348bfe6302841fa1a03d1",class:"panel-nav__header-logo","aria-hidden":"true"},t("svg",{key:"db467c1fc53b06dd29775d16ee6a82d343c99802",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:"1e4e16a7d436003b831ce5cd7b5167c5d6850e1e",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:"87a420824763c0830232d59f710aa760b0f69013",class:"panel-nav__header-toggle","aria-hidden":"true"},t("ds-icon",{key:"5b0bc8661377bad5809aac625ee9f38c1c807e81",name:s?"LeftExpandB":"LeftCollapseB",size:"md",color:"inherit"})))),t("div",{key:"ccb60c37ed7cc8ba63b392b0f67ce835e2485a02",class:{"panel-nav__body":true,"scroll-edge-fade":true,"scroll-edge-fade--bottom":true,"scroll-edge-fade--at-end":this.atBottom},onScroll:e=>this.handleBodyScroll(e)},(()=>{let e=0;return this.parsedGroups.map((a=>t("div",{class:"panel-nav__group"},a.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"},a.label))),a.items.map((a=>this.renderNavItem(a,e++,s))))))})()),t("div",{key:"aa6a8465fa0749cc37d2b36fd0f6928abe35c16c",class:"panel-nav__footer"},t("button",{key:"68df6533926b9901b64a1a44b0db9df373425f16",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:"07ec6a8c45ad5510d6c9b158a11d50898f1615b3",name:a?"Gear":"Dashboard",size:"md",color:"inherit"})),t("button",{key:"c126f994da55665f95533c9c1e7c16d02021b532",type:"button",id:g,class:"panel-nav__item panel-nav__footer-user","aria-label":s?`User: ${o}`:`User menu for ${o}`,onClick:e=>this.handleUserAction(e)},t("span",{key:"5da6821cb21900317a648f80fe664231b55b1614",class:"panel-nav__item-label panel-nav__footer-user-label"},t("span",{key:"0d7850c90c316b08056e30a116bd6975ce938233",class:"panel-nav__item-label-text"},t("ds-text",{key:"4826a6d5ce11a39f79e1798eb0079b88b4bc4976",as:"span",variant:"text-body-medium-emphasis",color:"inherit"},o))),t("span",{key:"94f54c383c89d24611e9aee709502027d860d621",class:"panel-nav__item-icon panel-nav__footer-user-icon","aria-hidden":"true"},t("span",{key:"c3508bf356d63064787f4d7fe23700b3285e5adb",class:"panel-nav__footer-icon-expanded"},t("ds-icon",{key:"363c2a0e591915db0cd1fcc39f41af44e452f12a",name:"ChevronUpDown",size:"md",color:"inherit"})),t("span",{key:"ffae61637fe80d07d568036d0bbc7c42e621f522",class:"panel-nav__footer-icon-collapsed"},t("ds-icon",{key:"3d468abdb2256713e9c547f2f23f3656c579654f",name:"Circle",size:"md",color:"inherit"}),t("span",{key:"ea35cc4bcbf8cf8d1b9fb0bad2d70ea803440917",class:"panel-nav__user-initial"},t("ds-text",{key:"f79afa7d77336777fe41c757f712acca0053c2ee",as:"span",variant:"text-caption-emphasis",color:"inherit"},r))))))),!this.viewportNarrow&&t("div",{key:"29dd512cf30bb1a86e462f8523e203c79b681f82",class:{"panel-nav__resize-handle":true,"panel-nav__resize-handle--overlay":this.showEdgeOverlay},onMouseEnter:()=>this.handleResizeHandleMouseEnter(),onMouseLeave:()=>this.handleResizeHandleMouseLeave(),onMouseDown:e=>this.handleResizeHandleMouseDown(e),"aria-hidden":"true"}))}get el(){return this}static get watchers(){return{collapsed:[{onCollapsedChange:0}],viewportNarrow:[{onViewportNarrowChange:0}],breakpoint:[{onBreakpointChange:0}],groups:[{onGroupsChange:0}],activeId:[{onActiveIdChange:0}],urlDerivedActiveId:[{onActiveIdChange:0}],currentUrl:[{onCurrentUrlChange:0}],navStyle:[{onNavStyleChange:0}]}}static get style(){return y()+x()}},[2,"ds-panel-nav",{navStyle:[513,"nav-style"],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"],renderedStyle:[32],parsedGroups:[32],atBottom:[32],isAnimating:[32],rovingIndex:[32],viewportNarrow:[32],urlDerivedActiveId:[32],showEdgeOverlay:[32]},undefined,{collapsed:[{onCollapsedChange:0}],viewportNarrow:[{onViewportNarrowChange:0}],breakpoint:[{onBreakpointChange:0}],groups:[{onGroupsChange:0}],activeId:[{onActiveIdChange:0}],urlDerivedActiveId:[{onActiveIdChange:0}],currentUrl:[{onCurrentUrlChange:0}],navStyle:[{onNavStyleChange:0}]}]);k.HOST_PROP_SYNC_BUDGET=8;function w(){if(typeof customElements==="undefined"){return}const e=["ds-panel-nav","ds-badge","ds-icon","ds-text"];e.forEach((e=>{switch(e){case"ds-panel-nav":if(!customElements.get(s(e))){customElements.define(s(e),k)}break;case"ds-badge":if(!customElements.get(s(e))){o()}break;case"ds-icon":if(!customElements.get(s(e))){r()}break;case"ds-text":if(!customElements.get(s(e))){l()}break}}))}w();const z=k;const C=w;export{z as DsPanelNav,C as defineCustomElement};
|
|
2
2
|
//# sourceMappingURL=ds-panel-nav.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["NAV_STYLE_HINT_ATTR","readNavStyleAttr","attr","undefined","resolveNavChromeStyle","styleProp","hostAttr","docHint","fromHost","hint","document","documentElement","getAttribute","fromHint","shouldResyncNavChromeStyle","renderedStyle","style","hrefMatchesPath","path","href","startsWith","deriveActiveIdFromUrl","items","bestId","bestLen","item","length","id","countPanelNavItems","groups","reduce","sum","g","parsePanelNavGroups","Array","isArray","parsed","JSON","parse","resolvePanelNavDisableVt","prop","shouldResyncPanelNavGroups","parsedGroups","PANEL_NAV_USER_MENU_ANCHOR_ID","scrollEdgeFadeCss","panelNavCss","PanelNav","__stencil_proxyCustomElement","HTMLElement","constructor","registerHost","this","navStyle","disableViewTransition","routerMode","activeId","collapsed","breakpoint","storageKey","currentUrl","userName","userInitial","atBottom","isAnimating","rovingIndex","viewportNarrow","urlDerivedActiveId","isDragging","dragStartX","didSnap","lastDeltaX","edgeOverlayTimer","showEdgeOverlay","effectiveActiveId","effectiveDisableViewTransition","el","onCollapsedChange","startCollapseAnimation","onBreakpointChange","disconnectResizeObserver","connectResizeObserver","onGroupsChange","val","syncRovingIndex","syncActiveFromUrl","onActiveIdChange","onCurrentUrlChange","onNavStyleChange","newVal","componentWillLoad","resolvePanelNavStyle","stored","localStorage","getItem","componentDidLoad","syncHostPropsIfNeeded","scheduleDeferredHostPropSync","checkScroll","disconnectedCallback","clearEdgeOverlayTimer","globalMouseMoveHandler","window","removeEventListener","globalMouseUpHandler","dsChromeTransitionStart","emit","source","panel","querySelector","transitionEndHandler","e","propertyName","dsChromeTransitionEnd","addEventListener","innerWidth","resizeObserver","ResizeObserver","observe","disconnect","getAllItems","shouldResyncPanelNavStyle","remaining","HOST_PROP_SYNC_BUDGET","tick","requestAnimationFrame","queueMicrotask","applyToggle","next","setItem","String","dsNavToggle","flatMap","idx","findIndex","i","handleItemKeyDown","index","total","key","preventDefault","from","querySelectorAll","focus","body","scrollHeight","scrollTop","clientHeight","handleBodyScroll","target","handleItemClick","dsNavSelect","handleToggle","handleFooterAction","dsNavFooterAction","handleUserAction","anchor","currentTarget","dsNavUserAction","clearTimeout","handleResizeHandleMouseEnter","setTimeout","handleResizeHandleMouseLeave","handleResizeHandleMouseDown","stopPropagation","wasCollapsed","clientX","cursor","userSelect","onMove","ev","deltaX","Math","abs","onUp","renderNavItem","isActive","itemContent","h","class","name","icon","size","color","flag","as","variant","label","dot","background","sharedProps","title","tabIndex","onClick","onKeyDown","onFocus","useAnchor","type","render","chromeStyle","isDashboardChrome","isDashboard","navCls","Host","display","position","viewBox","fill","xmlns","focusable","d","onScroll","flatIdx","map","group","onMouseEnter","onMouseLeave","onMouseDown"],"sources":["src/wc/nav/nav-chrome.ts","src/wc/components/PanelNav/panel-nav-utils.ts","src/wc/components/PanelNav/panel-nav-types.ts","src/wc/utils/scroll-edge-fade.css?tag=ds-panel-nav&encapsulation=scoped","src/wc/components/PanelNav/PanelNav.css?tag=ds-panel-nav&encapsulation=scoped","src/wc/components/PanelNav/PanelNav.tsx"],"sourcesContent":["/** Nav chrome style slot — dashboard vs settings. Colors unified for now; texture hooks use the class. */\nexport type NavChromeStyle = 'dashboard' | 'settings';\n\n/** Document hint for first paint before framework props land. */\nexport const NAV_STYLE_HINT_ATTR = 'data-nav-style';\n\nexport function readNavStyleAttr(attr: string | null): NavChromeStyle | undefined {\n if (attr === 'dashboard' || attr === 'settings') return attr;\n return undefined;\n}\n\nexport function setNavStyleHint(style: NavChromeStyle): void {\n if (typeof document === 'undefined') return;\n document.documentElement.setAttribute(NAV_STYLE_HINT_ATTR, style);\n}\n\nexport function clearNavStyleHint(): void {\n if (typeof document === 'undefined') return;\n document.documentElement.removeAttribute(NAV_STYLE_HINT_ATTR);\n}\n\nexport function resolveNavChromeStyle(\n styleProp: NavChromeStyle,\n hostAttr: string | null,\n docHint?: string | null,\n): NavChromeStyle {\n const fromHost = readNavStyleAttr(hostAttr);\n if (fromHost) return fromHost;\n\n const hint =\n docHint !== undefined\n ? docHint\n : typeof document !== 'undefined'\n ? document.documentElement.getAttribute(NAV_STYLE_HINT_ATTR)\n : null;\n const fromHint = readNavStyleAttr(hint);\n if (fromHint) return fromHint;\n\n return styleProp;\n}\n\nexport function shouldResyncNavChromeStyle(\n renderedStyle: NavChromeStyle,\n style: NavChromeStyle,\n): boolean {\n return renderedStyle !== style;\n}\n","import type { PanelNavGroup, PanelNavItem } from './panel-nav-types';\nimport {\n NAV_STYLE_HINT_ATTR,\n clearNavStyleHint,\n readNavStyleAttr,\n resolveNavChromeStyle,\n setNavStyleHint,\n shouldResyncNavChromeStyle,\n} from '../../nav/nav-chrome';\n\nexport type { NavChromeStyle } from '../../nav/nav-chrome';\nexport {\n NAV_STYLE_HINT_ATTR,\n readNavStyleAttr,\n setNavStyleHint,\n clearNavStyleHint,\n resolveNavChromeStyle as resolvePanelNavStyle,\n shouldResyncNavChromeStyle as shouldResyncPanelNavStyle,\n};\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/** Resolve whether VT is disabled from prop and/or host attribute. */\nexport function resolvePanelNavDisableVt(prop: boolean, attr: string | null): boolean {\n if (prop) return true;\n if (attr === null) return false;\n return attr !== 'false';\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","import type { NavChromeStyle } from '../../nav/nav-chrome';\n\nexport type { NavChromeStyle };\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/** Stable `id` on the footer user button — use with `ds-menu` `anchor-id`. */\nexport const PANEL_NAV_USER_MENU_ANCHOR_ID = 'ds-panel-nav-user-menu-anchor';\n\n/** Detail for `dsNavUserAction` — anchor element for an external `ds-menu`. */\nexport interface PanelNavUserActionDetail {\n anchor: HTMLElement;\n}\n","/**\n * Scroll-edge alpha fade — apply to overflow scroll containers.\n * Pair with `.scroll-edge-fade--at-end` when the relevant edge is flush (no fade).\n *\n * @see scroll-edge-fade.ts for programmatic mask helpers.\n */\n\n.scroll-edge-fade {\n --ds-scroll-edge-fade-size: var(--dimension-size-600);\n}\n\n.scroll-edge-fade--top {\n -webkit-mask-image: linear-gradient(to bottom, #000 0, #000 50%, transparent 100%);\n mask-image: linear-gradient(to bottom, #000 0, #000 50%, transparent 100%);\n -webkit-mask-size: 100% 100%;\n mask-size: 100% 100%;\n}\n\n.scroll-edge-fade--bottom {\n -webkit-mask-image: linear-gradient(\n to bottom,\n #000 0,\n #000 calc(100% - var(--ds-scroll-edge-fade-size)),\n transparent 100%\n );\n mask-image: linear-gradient(\n to bottom,\n #000 0,\n #000 calc(100% - var(--ds-scroll-edge-fade-size)),\n transparent 100%\n );\n -webkit-mask-size: 100% 100%;\n mask-size: 100% 100%;\n}\n\n.scroll-edge-fade--left {\n -webkit-mask-image: linear-gradient(\n to right,\n #000 0,\n #000 calc(100% - var(--ds-scroll-edge-fade-size)),\n transparent 100%\n );\n mask-image: linear-gradient(\n to right,\n #000 0,\n #000 calc(100% - var(--ds-scroll-edge-fade-size)),\n transparent 100%\n );\n -webkit-mask-size: 100% 100%;\n mask-size: 100% 100%;\n}\n\n.scroll-edge-fade--right {\n -webkit-mask-image: linear-gradient(\n to left,\n #000 0,\n #000 calc(100% - var(--ds-scroll-edge-fade-size)),\n transparent 100%\n );\n mask-image: linear-gradient(\n to left,\n #000 0,\n #000 calc(100% - var(--ds-scroll-edge-fade-size)),\n transparent 100%\n );\n -webkit-mask-size: 100% 100%;\n mask-size: 100% 100%;\n}\n\n.scroll-edge-fade--at-end {\n -webkit-mask-image: none;\n mask-image: none;\n}\n","/* ── Theme variables ──────────────────────────────────────────────────────── */\n\n.panel-nav {\n /* ─ Sizes ─ */\n --_nav-width: var(--dimension-panel-width-2xs);\n --_nav-width-collapsed: var(--dimension-size-600);\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 – default chrome (app surfaces) ─ */\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/* ── Default chrome overrides ────────────────────────────────────────────── */\n\n/* Shell-owned chrome (bg + wash +/or grid) — panel is a transparent frame. */\n:host-context(ds-app-shell[gradient]) .panel-nav,\n:host-context(ds-app-shell[grid]) .panel-nav {\n background-color: transparent;\n}\n\n/* Style slots — same app tokens for now; hook texture/glyph layers on these classes later. */\n.panel-nav--dashboard,\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 position: relative;\n width: var(--_nav-width);\n min-width: var(--_nav-width);\n height: 100%;\n background-color: var(--_nav-bg);\n border-right: var(--dimension-stroke-width-012, 1px) solid var(--_nav-border);\n box-sizing: border-box;\n overflow: visible;\n transition:\n width var(--_nav-expand-motion),\n min-width var(--_nav-expand-motion),\n background-color 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 isolation: isolate;\n}\n\n.panel-nav__header,\n.panel-nav__body,\n.panel-nav__footer {\n position: relative;\n z-index: 3;\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 pointer-events: none;\n transition: 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; instant swap to toggle on hover (no crossfade overlap) */\n.panel-nav__header-logo {\n display: flex;\n align-items: center;\n justify-content: center;\n position: relative;\n z-index: 1;\n opacity: 1;\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 — stacked on logo; opacity swap matches hover bg (instant) */\n.panel-nav__header-toggle {\n display: flex;\n align-items: center;\n justify-content: center;\n position: absolute;\n inset: 0;\n z-index: 2;\n opacity: 0;\n pointer-events: none;\n}\n\n.panel-nav__header-btn:hover .panel-nav__header-logo { opacity: 0; }\n.panel-nav__header-btn:hover .panel-nav__header-toggle { opacity: 1; }\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: none;\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 pointer-events: none;\n transition:\n right var(--_nav-expand-motion),\n top var(--_nav-expand-motion);\n}\n\n.panel-nav--collapsed .panel-nav__item-dot {\n right: var(--dimension-space-075);\n top: var(--dimension-space-075);\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/* ── 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-025, 2px);\n cursor: pointer;\n color: var(--_nav-fg-secondary);\n box-sizing: border-box;\n background: transparent;\n transition: color var(--effect-motion-short-3, 200ms ease);\n}\n\n.panel-nav__footer-btn::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: none;\n}\n\n.panel-nav__footer-btn:hover::after { background: var(--_nav-hover); transition: none; }\n.panel-nav__footer-btn:active::after { background: var(--_nav-pressed); transition: none; }\n.panel-nav__footer-btn:focus-visible { outline: none; }\n.panel-nav__footer-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.panel-nav__footer-btn ds-icon {\n position: relative;\n z-index: 1;\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/* The resize overlay line — extends past the right edge; keep above workspace content */\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: 20;\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/* Resize 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-foreground-primary);\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 type { ChromeTransitionDetail } from '../../nav/chrome-transition';\nimport type { NavChromeStyle } from '../../nav/nav-chrome';\nimport {\n deriveActiveIdFromUrl,\n parsePanelNavGroups,\n resolvePanelNavDisableVt,\n resolvePanelNavStyle,\n shouldResyncPanelNavGroups,\n shouldResyncPanelNavStyle,\n} from './panel-nav-utils';\nimport {\n PANEL_NAV_USER_MENU_ANCHOR_ID,\n type PanelNavGroup,\n type PanelNavItem,\n type PanelNavRouterMode,\n type PanelNavUserActionDetail,\n} from './panel-nav-types';\n\n@Component({\n tag: 'ds-panel-nav',\n styleUrl: 'PanelNav.css',\n styleUrls: ['../../utils/scroll-edge-fade.css'],\n scoped: true,\n})\nexport class PanelNav {\n /** Style slot: `dashboard` or `settings`. Colors match for now;\n * class hooks reserved for texture/glyph layers. Property: `navStyle`. Attribute: `nav-style`. */\n @Prop({ attribute: 'nav-style', reflect: true }) navStyle: NavChromeStyle = 'dashboard';\n\n /** When `true`, style changes apply synchronously — host app owns view transitions. */\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 * Set `storageKey` to persist across reloads. `dsNavToggle` still fires on change. */\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, collapsed state is restored on mount and written on each toggle. */\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 /** Bubbling lifecycle — `ds-app-shell` pauses chrome metrics during width motion. */\n @Event({ bubbles: true, composed: true })\n dsChromeTransitionStart!: EventEmitter<ChromeTransitionDetail>;\n\n @Event({ bubbles: true, composed: true })\n dsChromeTransitionEnd!: EventEmitter<ChromeTransitionDetail>;\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. Detail includes the anchor for `ds-menu`. */\n @Event() dsNavUserAction!: EventEmitter<PanelNavUserActionDetail>;\n\n @Element() el!: HTMLElement;\n\n @State() private renderedStyle: NavChromeStyle = 'dashboard';\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 private get effectiveDisableViewTransition(): boolean {\n return resolvePanelNavDisableVt(\n this.disableViewTransition,\n this.el.getAttribute('disable-view-transition'),\n );\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('navStyle')\n onNavStyleChange(newVal: NavChromeStyle) {\n this.renderedStyle = newVal;\n }\n\n componentWillLoad() {\n this.renderedStyle = resolvePanelNavStyle(this.navStyle, this.el.getAttribute('nav-style'));\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 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 this.dsChromeTransitionStart.emit({ source: 'panel-nav' });\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 this.dsChromeTransitionEnd.emit({ source: 'panel-nav' });\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 (shouldResyncPanelNavStyle(this.renderedStyle, this.navStyle)) {\n this.renderedStyle = this.navStyle;\n }\n\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: updates `collapsed`, emits `dsNavToggle`, optionally persists. */\n private applyToggle(next: boolean) {\n this.collapsed = next;\n if (this.storageKey) {\n try { localStorage.setItem(this.storageKey, String(next)); } catch { /* unavailable */ }\n }\n this.dsNavToggle.emit(next);\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(e: MouseEvent) {\n const anchor = e.currentTarget as HTMLElement;\n this.dsNavUserAction.emit({ anchor });\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 && (\n <ds-badge\n class=\"panel-nav__item-dot\"\n variant=\"dot\"\n background=\"var(--_nav-bg)\"\n label=\"\"\n aria-hidden=\"true\"\n />\n ),\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 chromeStyle = this.renderedStyle;\n const isDashboardChrome = chromeStyle === 'dashboard';\n const isDashboard = this.navStyle === '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\n class={navCls}\n aria-label={isDashboardChrome ? 'Dashboard navigation' : 'Settings navigation'}\n >\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\n class={{\n 'panel-nav__body': true,\n 'scroll-edge-fade': true,\n 'scroll-edge-fade--bottom': true,\n 'scroll-edge-fade--at-end': this.atBottom,\n }}\n onScroll={e => this.handleBodyScroll(e)}\n >\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 <button\n type=\"button\"\n class=\"panel-nav__footer-btn\"\n title={isDashboardChrome ? 'Settings' : 'Dashboard'}\n aria-label={isDashboardChrome ? 'Open settings' : 'Go to dashboard'}\n onClick={() => this.handleFooterAction()}\n >\n <ds-icon name={isDashboardChrome ? '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 id={PANEL_NAV_USER_MENU_ANCHOR_ID}\n class=\"panel-nav__item panel-nav__footer-user\"\n aria-label={collapsed ? `User: ${userName}` : `User menu for ${userName}`}\n onClick={(e) => this.handleUserAction(e)}\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":["NAV_STYLE_HINT_ATTR","readNavStyleAttr","attr","undefined","resolveNavChromeStyle","styleProp","hostAttr","docHint","fromHost","hint","document","documentElement","getAttribute","fromHint","shouldResyncNavChromeStyle","renderedStyle","style","hrefMatchesPath","path","href","startsWith","deriveActiveIdFromUrl","items","bestId","bestLen","item","length","id","countPanelNavItems","groups","reduce","sum","g","parsePanelNavGroups","Array","isArray","parsed","JSON","parse","resolvePanelNavDisableVt","prop","shouldResyncPanelNavGroups","parsedGroups","PANEL_NAV_USER_MENU_ANCHOR_ID","scrollEdgeFadeCss","panelNavCss","PanelNav","__stencil_proxyCustomElement","HTMLElement","constructor","registerHost","this","navStyle","disableViewTransition","routerMode","activeId","collapsed","breakpoint","storageKey","currentUrl","userName","userInitial","atBottom","isAnimating","rovingIndex","viewportNarrow","urlDerivedActiveId","isDragging","dragStartX","didSnap","lastDeltaX","edgeOverlayTimer","showEdgeOverlay","effectiveActiveId","effectiveDisableViewTransition","el","onCollapsedChange","_next","prev","startCollapseAnimation","onViewportNarrowChange","onBreakpointChange","disconnectResizeObserver","connectResizeObserver","onGroupsChange","val","syncRovingIndex","syncActiveFromUrl","onActiveIdChange","onCurrentUrlChange","onNavStyleChange","newVal","componentWillLoad","resolvePanelNavStyle","stored","localStorage","getItem","componentDidLoad","syncHostPropsIfNeeded","scheduleDeferredHostPropSync","checkScroll","disconnectedCallback","clearEdgeOverlayTimer","globalMouseMoveHandler","window","removeEventListener","globalMouseUpHandler","dsChromeTransitionStart","emit","source","panel","querySelector","widthBefore","getBoundingClientRect","width","transitionEndHandler","e","propertyName","finishCollapseAnimation","addEventListener","requestAnimationFrame","widthAfter","Math","abs","dsChromeTransitionEnd","innerWidth","resizeObserver","ResizeObserver","observe","disconnect","getAllItems","shouldResyncPanelNavStyle","remaining","HOST_PROP_SYNC_BUDGET","tick","queueMicrotask","applyToggle","next","setItem","String","dsNavToggle","flatMap","idx","findIndex","i","handleItemKeyDown","index","total","key","preventDefault","from","querySelectorAll","focus","body","scrollHeight","scrollTop","clientHeight","handleBodyScroll","target","handleItemClick","dsNavSelect","handleToggle","handleFooterAction","dsNavFooterAction","handleUserAction","anchor","currentTarget","dsNavUserAction","clearTimeout","handleResizeHandleMouseEnter","setTimeout","handleResizeHandleMouseLeave","handleResizeHandleMouseDown","stopPropagation","wasCollapsed","clientX","cursor","userSelect","onMove","ev","deltaX","onUp","renderNavItem","isActive","itemContent","h","class","name","icon","size","color","flag","as","variant","label","dot","background","sharedProps","title","tabIndex","onClick","onKeyDown","onFocus","useAnchor","type","render","chromeStyle","isDashboardChrome","isDashboard","navCls","Host","display","position","viewBox","fill","xmlns","focusable","d","onScroll","flatIdx","map","group","onMouseEnter","onMouseLeave","onMouseDown"],"sources":["src/wc/nav/nav-chrome.ts","src/wc/components/PanelNav/panel-nav-utils.ts","src/wc/components/PanelNav/panel-nav-types.ts","src/wc/utils/scroll-edge-fade.css?tag=ds-panel-nav&encapsulation=scoped","src/wc/components/PanelNav/PanelNav.css?tag=ds-panel-nav&encapsulation=scoped","src/wc/components/PanelNav/PanelNav.tsx"],"sourcesContent":["/** Nav chrome style slot — dashboard vs settings. Colors unified for now; texture hooks use the class. */\nexport type NavChromeStyle = 'dashboard' | 'settings';\n\n/** Document hint for first paint before framework props land. */\nexport const NAV_STYLE_HINT_ATTR = 'data-nav-style';\n\nexport function readNavStyleAttr(attr: string | null): NavChromeStyle | undefined {\n if (attr === 'dashboard' || attr === 'settings') return attr;\n return undefined;\n}\n\nexport function setNavStyleHint(style: NavChromeStyle): void {\n if (typeof document === 'undefined') return;\n document.documentElement.setAttribute(NAV_STYLE_HINT_ATTR, style);\n}\n\nexport function clearNavStyleHint(): void {\n if (typeof document === 'undefined') return;\n document.documentElement.removeAttribute(NAV_STYLE_HINT_ATTR);\n}\n\nexport function resolveNavChromeStyle(\n styleProp: NavChromeStyle,\n hostAttr: string | null,\n docHint?: string | null,\n): NavChromeStyle {\n const fromHost = readNavStyleAttr(hostAttr);\n if (fromHost) return fromHost;\n\n const hint =\n docHint !== undefined\n ? docHint\n : typeof document !== 'undefined'\n ? document.documentElement.getAttribute(NAV_STYLE_HINT_ATTR)\n : null;\n const fromHint = readNavStyleAttr(hint);\n if (fromHint) return fromHint;\n\n return styleProp;\n}\n\nexport function shouldResyncNavChromeStyle(\n renderedStyle: NavChromeStyle,\n style: NavChromeStyle,\n): boolean {\n return renderedStyle !== style;\n}\n","import type { PanelNavGroup, PanelNavItem } from './panel-nav-types';\nimport {\n NAV_STYLE_HINT_ATTR,\n clearNavStyleHint,\n readNavStyleAttr,\n resolveNavChromeStyle,\n setNavStyleHint,\n shouldResyncNavChromeStyle,\n} from '../../nav/nav-chrome';\n\nexport type { NavChromeStyle } from '../../nav/nav-chrome';\nexport {\n NAV_STYLE_HINT_ATTR,\n readNavStyleAttr,\n setNavStyleHint,\n clearNavStyleHint,\n resolveNavChromeStyle as resolvePanelNavStyle,\n shouldResyncNavChromeStyle as shouldResyncPanelNavStyle,\n};\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/** Resolve whether VT is disabled from prop and/or host attribute. */\nexport function resolvePanelNavDisableVt(prop: boolean, attr: string | null): boolean {\n if (prop) return true;\n if (attr === null) return false;\n return attr !== 'false';\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","import type { NavChromeStyle } from '../../nav/nav-chrome';\n\nexport type { NavChromeStyle };\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/** Stable `id` on the footer user button — use with `ds-menu` `anchor-id`. */\nexport const PANEL_NAV_USER_MENU_ANCHOR_ID = 'ds-panel-nav-user-menu-anchor';\n\n/** Detail for `dsNavUserAction` — anchor element for an external `ds-menu`. */\nexport interface PanelNavUserActionDetail {\n anchor: HTMLElement;\n}\n","/**\n * Scroll-edge alpha fade — apply to overflow scroll containers.\n * Pair with `.scroll-edge-fade--at-end` when the relevant edge is flush (no fade).\n *\n * @see scroll-edge-fade.ts for programmatic mask helpers.\n */\n\n.scroll-edge-fade {\n --ds-scroll-edge-fade-size: var(--dimension-size-600);\n}\n\n.scroll-edge-fade--top {\n -webkit-mask-image: linear-gradient(to bottom, #000 0, #000 50%, transparent 100%);\n mask-image: linear-gradient(to bottom, #000 0, #000 50%, transparent 100%);\n -webkit-mask-size: 100% 100%;\n mask-size: 100% 100%;\n}\n\n.scroll-edge-fade--bottom {\n -webkit-mask-image: linear-gradient(\n to bottom,\n #000 0,\n #000 calc(100% - var(--ds-scroll-edge-fade-size)),\n transparent 100%\n );\n mask-image: linear-gradient(\n to bottom,\n #000 0,\n #000 calc(100% - var(--ds-scroll-edge-fade-size)),\n transparent 100%\n );\n -webkit-mask-size: 100% 100%;\n mask-size: 100% 100%;\n}\n\n.scroll-edge-fade--left {\n -webkit-mask-image: linear-gradient(\n to right,\n #000 0,\n #000 calc(100% - var(--ds-scroll-edge-fade-size)),\n transparent 100%\n );\n mask-image: linear-gradient(\n to right,\n #000 0,\n #000 calc(100% - var(--ds-scroll-edge-fade-size)),\n transparent 100%\n );\n -webkit-mask-size: 100% 100%;\n mask-size: 100% 100%;\n}\n\n.scroll-edge-fade--right {\n -webkit-mask-image: linear-gradient(\n to left,\n #000 0,\n #000 calc(100% - var(--ds-scroll-edge-fade-size)),\n transparent 100%\n );\n mask-image: linear-gradient(\n to left,\n #000 0,\n #000 calc(100% - var(--ds-scroll-edge-fade-size)),\n transparent 100%\n );\n -webkit-mask-size: 100% 100%;\n mask-size: 100% 100%;\n}\n\n.scroll-edge-fade--at-end {\n -webkit-mask-image: none;\n mask-image: none;\n}\n","/* ── Theme variables ──────────────────────────────────────────────────────── */\n\n.panel-nav {\n /* ─ Sizes ─ */\n --_nav-width: var(--dimension-panel-width-2xs);\n --_nav-width-collapsed: var(--dimension-size-600);\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 – default chrome (app surfaces) ─ */\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/* ── Default chrome overrides ────────────────────────────────────────────── */\n\n/* Shell-owned chrome (bg + wash +/or grid) — panel is a transparent frame. */\n:host-context(ds-app-shell[gradient]) .panel-nav,\n:host-context(ds-app-shell[grid]) .panel-nav {\n background-color: transparent;\n}\n\n/* Style slots — same app tokens for now; hook texture/glyph layers on these classes later. */\n.panel-nav--dashboard,\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 position: relative;\n width: var(--_nav-width);\n min-width: var(--_nav-width);\n height: 100%;\n background-color: var(--_nav-bg);\n border-right: var(--dimension-stroke-width-012, 1px) solid var(--_nav-border);\n box-sizing: border-box;\n overflow: visible;\n transition:\n width var(--_nav-expand-motion),\n min-width var(--_nav-expand-motion),\n background-color 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 isolation: isolate;\n}\n\n.panel-nav__header,\n.panel-nav__body,\n.panel-nav__footer {\n position: relative;\n z-index: 3;\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 pointer-events: none;\n transition: 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; instant swap to toggle on hover (no crossfade overlap) */\n.panel-nav__header-logo {\n display: flex;\n align-items: center;\n justify-content: center;\n position: relative;\n z-index: 1;\n opacity: 1;\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 — stacked on logo; opacity swap matches hover bg (instant) */\n.panel-nav__header-toggle {\n display: flex;\n align-items: center;\n justify-content: center;\n position: absolute;\n inset: 0;\n z-index: 2;\n opacity: 0;\n pointer-events: none;\n}\n\n.panel-nav__header-btn:hover .panel-nav__header-logo { opacity: 0; }\n.panel-nav__header-btn:hover .panel-nav__header-toggle { opacity: 1; }\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: none;\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 pointer-events: none;\n transition:\n right var(--_nav-expand-motion),\n top var(--_nav-expand-motion);\n}\n\n.panel-nav--collapsed .panel-nav__item-dot {\n right: var(--dimension-space-075);\n top: var(--dimension-space-075);\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/* ── 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-025, 2px);\n cursor: pointer;\n color: var(--_nav-fg-secondary);\n box-sizing: border-box;\n background: transparent;\n transition: color var(--effect-motion-short-3, 200ms ease);\n}\n\n.panel-nav__footer-btn::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: none;\n}\n\n.panel-nav__footer-btn:hover::after { background: var(--_nav-hover); transition: none; }\n.panel-nav__footer-btn:active::after { background: var(--_nav-pressed); transition: none; }\n.panel-nav__footer-btn:focus-visible { outline: none; }\n.panel-nav__footer-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.panel-nav__footer-btn ds-icon {\n position: relative;\n z-index: 1;\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/* The resize overlay line — extends past the right edge; keep above workspace content */\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: 20;\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/* Resize 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-foreground-primary);\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 type { ChromeTransitionDetail } from '../../nav/chrome-transition';\nimport type { NavChromeStyle } from '../../nav/nav-chrome';\nimport {\n deriveActiveIdFromUrl,\n parsePanelNavGroups,\n resolvePanelNavDisableVt,\n resolvePanelNavStyle,\n shouldResyncPanelNavGroups,\n shouldResyncPanelNavStyle,\n} from './panel-nav-utils';\nimport {\n PANEL_NAV_USER_MENU_ANCHOR_ID,\n type PanelNavGroup,\n type PanelNavItem,\n type PanelNavRouterMode,\n type PanelNavUserActionDetail,\n} from './panel-nav-types';\n\n@Component({\n tag: 'ds-panel-nav',\n styleUrl: 'PanelNav.css',\n styleUrls: ['../../utils/scroll-edge-fade.css'],\n scoped: true,\n})\nexport class PanelNav {\n /** Style slot: `dashboard` or `settings`. Colors match for now;\n * class hooks reserved for texture/glyph layers. Property: `navStyle`. Attribute: `nav-style`. */\n @Prop({ attribute: 'nav-style', reflect: true }) navStyle: NavChromeStyle = 'dashboard';\n\n /** When `true`, style changes apply synchronously — host app owns view transitions. */\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 * Set `storageKey` to persist across reloads. `dsNavToggle` still fires on change. */\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, collapsed state is restored on mount and written on each toggle. */\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 /** Bubbling lifecycle — `ds-app-shell` pauses chrome metrics during width motion. */\n @Event({ bubbles: true, composed: true })\n dsChromeTransitionStart!: EventEmitter<ChromeTransitionDetail>;\n\n @Event({ bubbles: true, composed: true })\n dsChromeTransitionEnd!: EventEmitter<ChromeTransitionDetail>;\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. Detail includes the anchor for `ds-menu`. */\n @Event() dsNavUserAction!: EventEmitter<PanelNavUserActionDetail>;\n\n @Element() el!: HTMLElement;\n\n @State() private renderedStyle: NavChromeStyle = 'dashboard';\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 private get effectiveDisableViewTransition(): boolean {\n return resolvePanelNavDisableVt(\n this.disableViewTransition,\n this.el.getAttribute('disable-view-transition'),\n );\n }\n\n @Watch('collapsed')\n onCollapsedChange(_next: boolean, prev: boolean | undefined) {\n if (prev === undefined) return;\n this.startCollapseAnimation();\n }\n\n @Watch('viewportNarrow')\n onViewportNarrowChange(_next: boolean, prev: boolean | undefined) {\n if (prev === undefined) return;\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('navStyle')\n onNavStyleChange(newVal: NavChromeStyle) {\n this.renderedStyle = newVal;\n }\n\n componentWillLoad() {\n this.renderedStyle = resolvePanelNavStyle(this.navStyle, this.el.getAttribute('nav-style'));\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 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 this.dsChromeTransitionStart.emit({ source: 'panel-nav' });\n const panel = this.el.querySelector('.panel-nav') as HTMLElement | null;\n const widthBefore = panel?.getBoundingClientRect().width ?? 0;\n if (this.transitionEndHandler) {\n panel?.removeEventListener('transitionend', this.transitionEndHandler);\n }\n this.transitionEndHandler = (e: TransitionEvent) => {\n if (e.propertyName === 'width') {\n this.finishCollapseAnimation(panel);\n }\n };\n panel?.addEventListener('transitionend', this.transitionEndHandler);\n requestAnimationFrame(() => {\n requestAnimationFrame(() => {\n if (!this.isAnimating) return;\n const widthAfter = panel?.getBoundingClientRect().width ?? 0;\n if (Math.abs(widthAfter - widthBefore) < 1) {\n this.finishCollapseAnimation(panel);\n }\n });\n });\n }\n\n private finishCollapseAnimation(panel: HTMLElement | null) {\n if (!this.isAnimating) return;\n this.isAnimating = false;\n this.dsChromeTransitionEnd.emit({ source: 'panel-nav' });\n if (this.transitionEndHandler) {\n panel?.removeEventListener('transitionend', this.transitionEndHandler);\n }\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 (shouldResyncPanelNavStyle(this.renderedStyle, this.navStyle)) {\n this.renderedStyle = this.navStyle;\n }\n\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: updates `collapsed`, emits `dsNavToggle`, optionally persists. */\n private applyToggle(next: boolean) {\n this.collapsed = next;\n if (this.storageKey) {\n try { localStorage.setItem(this.storageKey, String(next)); } catch { /* unavailable */ }\n }\n this.dsNavToggle.emit(next);\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(e: MouseEvent) {\n const anchor = e.currentTarget as HTMLElement;\n this.dsNavUserAction.emit({ anchor });\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 && (\n <ds-badge\n class=\"panel-nav__item-dot\"\n variant=\"dot\"\n background=\"var(--_nav-bg)\"\n label=\"\"\n aria-hidden=\"true\"\n />\n ),\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 chromeStyle = this.renderedStyle;\n const isDashboardChrome = chromeStyle === 'dashboard';\n const isDashboard = this.navStyle === '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\n class={navCls}\n aria-label={isDashboardChrome ? 'Dashboard navigation' : 'Settings navigation'}\n >\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\n class={{\n 'panel-nav__body': true,\n 'scroll-edge-fade': true,\n 'scroll-edge-fade--bottom': true,\n 'scroll-edge-fade--at-end': this.atBottom,\n }}\n onScroll={e => this.handleBodyScroll(e)}\n >\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 <button\n type=\"button\"\n class=\"panel-nav__footer-btn\"\n title={isDashboardChrome ? 'Settings' : 'Dashboard'}\n aria-label={isDashboardChrome ? 'Open settings' : 'Go to dashboard'}\n onClick={() => this.handleFooterAction()}\n >\n <ds-icon name={isDashboardChrome ? '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 id={PANEL_NAV_USER_MENU_ANCHOR_ID}\n class=\"panel-nav__item panel-nav__footer-user\"\n aria-label={collapsed ? `User: ${userName}` : `User menu for ${userName}`}\n onClick={(e) => this.handleUserAction(e)}\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":[]}
|
|
@@ -48,6 +48,7 @@ export declare class BarNav {
|
|
|
48
48
|
private triggerLabelTruncated;
|
|
49
49
|
private static readonly HOST_PROP_SYNC_BUDGET;
|
|
50
50
|
private static readonly INTRINSIC_WIDTH_RETRY_MAX;
|
|
51
|
+
private static readonly TAB_LAYOUT_COMMIT_MAX_FRAMES;
|
|
51
52
|
private static readonly OVERFLOW_HYSTERESIS_PX;
|
|
52
53
|
private headerEl;
|
|
53
54
|
private triggerEl;
|
|
@@ -58,6 +59,7 @@ export declare class BarNav {
|
|
|
58
59
|
private probeTabGroupEl;
|
|
59
60
|
private resizeObserver;
|
|
60
61
|
private intrinsicWidthRetryCount;
|
|
62
|
+
private tabLayoutPendingFrames;
|
|
61
63
|
private readonly panelNavTransition;
|
|
62
64
|
private readonly panelToolsTransition;
|
|
63
65
|
private readonly overflowCoalescer;
|
|
@@ -96,6 +98,7 @@ export declare class BarNav {
|
|
|
96
98
|
private getLeftZoneAvailableWidth;
|
|
97
99
|
private getTabsIntrinsicWidth;
|
|
98
100
|
private scheduleIntrinsicWidthRetry;
|
|
101
|
+
private forceTabLayoutCommit;
|
|
99
102
|
private updateTabsCollapsed;
|
|
100
103
|
private syncMenuAnchor;
|
|
101
104
|
private handleMenuSelect;
|
|
@@ -66,7 +66,8 @@ export declare class PanelNav {
|
|
|
66
66
|
* over `activeId` when both are present. */
|
|
67
67
|
private get effectiveActiveId();
|
|
68
68
|
private get effectiveDisableViewTransition();
|
|
69
|
-
onCollapsedChange(): void;
|
|
69
|
+
onCollapsedChange(_next: boolean, prev: boolean | undefined): void;
|
|
70
|
+
onViewportNarrowChange(_next: boolean, prev: boolean | undefined): void;
|
|
70
71
|
onBreakpointChange(): void;
|
|
71
72
|
onGroupsChange(val: string | PanelNavGroup[]): void;
|
|
72
73
|
onActiveIdChange(): void;
|
|
@@ -76,6 +77,7 @@ export declare class PanelNav {
|
|
|
76
77
|
componentDidLoad(): void;
|
|
77
78
|
disconnectedCallback(): void;
|
|
78
79
|
private startCollapseAnimation;
|
|
80
|
+
private finishCollapseAnimation;
|
|
79
81
|
private connectResizeObserver;
|
|
80
82
|
private disconnectResizeObserver;
|
|
81
83
|
/** Derive active ID from the current URL by matching item `href` values.
|
package/package.json
CHANGED
|
@@ -100,6 +100,7 @@ export class BarNav {
|
|
|
100
100
|
|
|
101
101
|
private static readonly HOST_PROP_SYNC_BUDGET = 8;
|
|
102
102
|
private static readonly INTRINSIC_WIDTH_RETRY_MAX = 3;
|
|
103
|
+
private static readonly TAB_LAYOUT_COMMIT_MAX_FRAMES = 16;
|
|
103
104
|
private static readonly OVERFLOW_HYSTERESIS_PX = 8;
|
|
104
105
|
|
|
105
106
|
private headerEl: HTMLElement | null = null;
|
|
@@ -111,6 +112,7 @@ export class BarNav {
|
|
|
111
112
|
private probeTabGroupEl: QueryableHost | null = null;
|
|
112
113
|
private resizeObserver: ResizeObserver | null = null;
|
|
113
114
|
private intrinsicWidthRetryCount = 0;
|
|
115
|
+
private tabLayoutPendingFrames = 0;
|
|
114
116
|
private readonly panelNavTransition = new ChromeTransitionDepth();
|
|
115
117
|
private readonly panelToolsTransition = new ChromeTransitionDepth();
|
|
116
118
|
private readonly overflowCoalescer = createRafCoalescer(() => {
|
|
@@ -147,6 +149,7 @@ export class BarNav {
|
|
|
147
149
|
this.tabsCollapsed = false;
|
|
148
150
|
this.menuOpen = false;
|
|
149
151
|
this.intrinsicWidthRetryCount = 0;
|
|
152
|
+
this.tabLayoutPendingFrames = 0;
|
|
150
153
|
}
|
|
151
154
|
|
|
152
155
|
@Watch('tabs')
|
|
@@ -392,6 +395,16 @@ export class BarNav {
|
|
|
392
395
|
requestAnimationFrame(() => this.scheduleOverflowCheck());
|
|
393
396
|
}
|
|
394
397
|
|
|
398
|
+
private forceTabLayoutCommit(fallbackCollapsed: boolean) {
|
|
399
|
+
this.intrinsicWidthRetryCount = 0;
|
|
400
|
+
this.tabLayoutPendingFrames = 0;
|
|
401
|
+
this.tabsCollapsed = fallbackCollapsed;
|
|
402
|
+
if (!fallbackCollapsed) {
|
|
403
|
+
this.menuOpen = false;
|
|
404
|
+
}
|
|
405
|
+
this.tabLayoutCommitted = true;
|
|
406
|
+
}
|
|
407
|
+
|
|
395
408
|
private updateTabsCollapsed() {
|
|
396
409
|
if (!this.headerEl || this.resolvedTabs.length === 0 || this.hideTabsForDetailRoute) {
|
|
397
410
|
if (this.tabsCollapsed) {
|
|
@@ -399,22 +412,25 @@ export class BarNav {
|
|
|
399
412
|
this.menuOpen = false;
|
|
400
413
|
}
|
|
401
414
|
this.intrinsicWidthRetryCount = 0;
|
|
415
|
+
this.tabLayoutPendingFrames = 0;
|
|
402
416
|
this.tabLayoutCommitted = true;
|
|
403
417
|
return;
|
|
404
418
|
}
|
|
405
419
|
|
|
406
420
|
const intrinsicWidth = this.getTabsIntrinsicWidth();
|
|
407
421
|
if (intrinsicWidth === 0) {
|
|
422
|
+
this.tabLayoutPendingFrames += 1;
|
|
408
423
|
if (this.intrinsicWidthRetryCount >= BarNav.INTRINSIC_WIDTH_RETRY_MAX) {
|
|
409
|
-
this.
|
|
410
|
-
|
|
411
|
-
this.
|
|
424
|
+
this.forceTabLayoutCommit(false);
|
|
425
|
+
} else if (this.tabLayoutPendingFrames >= BarNav.TAB_LAYOUT_COMMIT_MAX_FRAMES) {
|
|
426
|
+
this.forceTabLayoutCommit(false);
|
|
412
427
|
} else {
|
|
413
428
|
this.scheduleIntrinsicWidthRetry();
|
|
414
429
|
}
|
|
415
430
|
return;
|
|
416
431
|
}
|
|
417
432
|
this.intrinsicWidthRetryCount = 0;
|
|
433
|
+
this.tabLayoutPendingFrames = 0;
|
|
418
434
|
|
|
419
435
|
const shouldCollapse = tabsOverflowContainer(
|
|
420
436
|
intrinsicWidth,
|
|
@@ -623,9 +639,12 @@ export class BarNav {
|
|
|
623
639
|
<ds-tab-group-nav
|
|
624
640
|
key={`probe-${tabGroupKey}`}
|
|
625
641
|
ref={el => {
|
|
626
|
-
|
|
627
|
-
if (
|
|
642
|
+
const next = el ?? null;
|
|
643
|
+
if (next === this.probeTabGroupEl) return;
|
|
644
|
+
this.probeTabGroupEl = next;
|
|
645
|
+
if (next) {
|
|
628
646
|
this.intrinsicWidthRetryCount = 0;
|
|
647
|
+
this.tabLayoutPendingFrames = 0;
|
|
629
648
|
this.scheduleOverflowCheck();
|
|
630
649
|
}
|
|
631
650
|
}}
|
|
@@ -123,8 +123,14 @@ export class PanelNav {
|
|
|
123
123
|
}
|
|
124
124
|
|
|
125
125
|
@Watch('collapsed')
|
|
126
|
+
onCollapsedChange(_next: boolean, prev: boolean | undefined) {
|
|
127
|
+
if (prev === undefined) return;
|
|
128
|
+
this.startCollapseAnimation();
|
|
129
|
+
}
|
|
130
|
+
|
|
126
131
|
@Watch('viewportNarrow')
|
|
127
|
-
|
|
132
|
+
onViewportNarrowChange(_next: boolean, prev: boolean | undefined) {
|
|
133
|
+
if (prev === undefined) return;
|
|
128
134
|
this.startCollapseAnimation();
|
|
129
135
|
}
|
|
130
136
|
|
|
@@ -191,17 +197,34 @@ export class PanelNav {
|
|
|
191
197
|
this.isAnimating = true;
|
|
192
198
|
this.dsChromeTransitionStart.emit({ source: 'panel-nav' });
|
|
193
199
|
const panel = this.el.querySelector('.panel-nav') as HTMLElement | null;
|
|
200
|
+
const widthBefore = panel?.getBoundingClientRect().width ?? 0;
|
|
194
201
|
if (this.transitionEndHandler) {
|
|
195
202
|
panel?.removeEventListener('transitionend', this.transitionEndHandler);
|
|
196
203
|
}
|
|
197
204
|
this.transitionEndHandler = (e: TransitionEvent) => {
|
|
198
205
|
if (e.propertyName === 'width') {
|
|
199
|
-
this.
|
|
200
|
-
this.dsChromeTransitionEnd.emit({ source: 'panel-nav' });
|
|
201
|
-
panel?.removeEventListener('transitionend', this.transitionEndHandler!);
|
|
206
|
+
this.finishCollapseAnimation(panel);
|
|
202
207
|
}
|
|
203
208
|
};
|
|
204
209
|
panel?.addEventListener('transitionend', this.transitionEndHandler);
|
|
210
|
+
requestAnimationFrame(() => {
|
|
211
|
+
requestAnimationFrame(() => {
|
|
212
|
+
if (!this.isAnimating) return;
|
|
213
|
+
const widthAfter = panel?.getBoundingClientRect().width ?? 0;
|
|
214
|
+
if (Math.abs(widthAfter - widthBefore) < 1) {
|
|
215
|
+
this.finishCollapseAnimation(panel);
|
|
216
|
+
}
|
|
217
|
+
});
|
|
218
|
+
});
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
private finishCollapseAnimation(panel: HTMLElement | null) {
|
|
222
|
+
if (!this.isAnimating) return;
|
|
223
|
+
this.isAnimating = false;
|
|
224
|
+
this.dsChromeTransitionEnd.emit({ source: 'panel-nav' });
|
|
225
|
+
if (this.transitionEndHandler) {
|
|
226
|
+
panel?.removeEventListener('transitionend', this.transitionEndHandler);
|
|
227
|
+
}
|
|
205
228
|
}
|
|
206
229
|
|
|
207
230
|
private connectResizeObserver() {
|