@ds-mo/ui 1.11.2 → 1.11.4

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.
@@ -11,6 +11,7 @@ import {
11
11
  } from '@stencil/core';
12
12
  import type { NavChromeStyle } from '../../nav/nav-chrome';
13
13
  import type { MenuItemData } from '../Menu/menu-types';
14
+ import { isTabDivider } from '../TabGroup/tab-item-utils';
14
15
  import type { BarNavActionItem, BarNavTab } from './bar-nav-types';
15
16
  import {
16
17
  deriveBarNavValueFromUrl,
@@ -34,8 +35,8 @@ import {
34
35
  ChromeTransitionDepth,
35
36
  createRafCoalescer,
36
37
  readChromeTransitionSource,
38
+ readChromeTransitionPhase,
37
39
  } from '../../nav/chrome-transition';
38
- import { readCssVarWidthPx } from '../../nav/shell-chrome-metrics';
39
40
 
40
41
  @Component({
41
42
  tag: 'ds-bar-nav',
@@ -99,6 +100,7 @@ export class BarNav {
99
100
 
100
101
  private static readonly HOST_PROP_SYNC_BUDGET = 8;
101
102
  private static readonly INTRINSIC_WIDTH_RETRY_MAX = 3;
103
+ private static readonly TAB_LAYOUT_COMMIT_MAX_FRAMES = 16;
102
104
  private static readonly OVERFLOW_HYSTERESIS_PX = 8;
103
105
 
104
106
  private headerEl: HTMLElement | null = null;
@@ -110,13 +112,14 @@ export class BarNav {
110
112
  private probeTabGroupEl: QueryableHost | null = null;
111
113
  private resizeObserver: ResizeObserver | null = null;
112
114
  private intrinsicWidthRetryCount = 0;
115
+ private tabLayoutPendingFrames = 0;
113
116
  private readonly panelNavTransition = new ChromeTransitionDepth();
117
+ private readonly panelToolsTransition = new ChromeTransitionDepth();
114
118
  private readonly overflowCoalescer = createRafCoalescer(() => {
115
119
  this.updateTabsCollapsed();
116
120
  this.updateTriggerLabelTruncation();
117
121
  });
118
122
  private chromeTransitionShell: HTMLElement | null = null;
119
- private toolsDrawerOpening = false;
120
123
  /** Matches `basePath` once tabs + URL are reconciled for the active section. */
121
124
  @State() private committedSection = '';
122
125
  /** Section waiting for URL + tabs to catch up after a cross-area navigation. */
@@ -137,6 +140,18 @@ export class BarNav {
137
140
  return !!getActiveTab(this.resolvedTabs, this.effectiveValue)?.dot;
138
141
  }
139
142
 
143
+ private digestTabsConfig(tabs: BarNavTab[]): string {
144
+ return tabs.map(t => (isTabDivider(t) ? '|' : t.id)).join(',');
145
+ }
146
+
147
+ private resetTabOverflowLayout() {
148
+ this.tabLayoutCommitted = false;
149
+ this.tabsCollapsed = false;
150
+ this.menuOpen = false;
151
+ this.intrinsicWidthRetryCount = 0;
152
+ this.tabLayoutPendingFrames = 0;
153
+ }
154
+
140
155
  @Watch('tabs')
141
156
  @Watch('tabsJson')
142
157
  @Watch('actions')
@@ -194,7 +209,7 @@ export class BarNav {
194
209
  }
195
210
 
196
211
  componentDidRender() {
197
- if (this.panelNavTransition.isActive) {
212
+ if (this.chromeOverflowPaused()) {
198
213
  this.syncMenuAnchor();
199
214
  return;
200
215
  }
@@ -241,16 +256,17 @@ export class BarNav {
241
256
  return;
242
257
  }
243
258
  if (source === 'panel-tools') {
244
- this.toolsDrawerOpening = true;
245
- this.el.classList.add('bar-nav--tools-drawer-opening');
246
- if (this.resolvedTabs.length > 0 && !this.hideTabsForDetailRoute) {
247
- this.tabsCollapsed = true;
248
- this.menuOpen = false;
259
+ const phase = readChromeTransitionPhase(event) ?? 'opening';
260
+ if (phase === 'closing') {
261
+ this.panelToolsTransition.enter();
249
262
  }
250
- this.scheduleOverflowCheck();
251
263
  }
252
264
  };
253
265
 
266
+ private chromeOverflowPaused(): boolean {
267
+ return this.panelNavTransition.isActive || this.panelToolsTransition.isActive;
268
+ }
269
+
254
270
  private onChromeTransitionEnd = (event: Event) => {
255
271
  const source = readChromeTransitionSource(event);
256
272
  if (source === 'panel-nav') {
@@ -261,8 +277,7 @@ export class BarNav {
261
277
  return;
262
278
  }
263
279
  if (source === 'panel-tools') {
264
- this.toolsDrawerOpening = false;
265
- this.el.classList.remove('bar-nav--tools-drawer-opening');
280
+ this.panelToolsTransition.exit();
266
281
  this.scheduleOverflowCheck();
267
282
  }
268
283
  };
@@ -301,14 +316,14 @@ export class BarNav {
301
316
  if (typeof ResizeObserver === 'undefined' || !this.headerEl) return;
302
317
  this.resizeObserver?.disconnect();
303
318
  this.resizeObserver = new ResizeObserver(() => {
304
- if (this.panelNavTransition.isActive) return;
319
+ if (this.chromeOverflowPaused()) return;
305
320
  this.scheduleOverflowCheck();
306
321
  });
307
322
  this.resizeObserver.observe(this.headerEl);
308
323
  }
309
324
 
310
325
  private scheduleOverflowCheck() {
311
- if (this.panelNavTransition.isActive) return;
326
+ if (this.chromeOverflowPaused()) return;
312
327
  this.overflowCoalescer.schedule();
313
328
  }
314
329
 
@@ -366,20 +381,7 @@ export class BarNav {
366
381
  const available =
367
382
  this.headerEl.clientWidth - horizontalPadding - actionsWidth - spacing;
368
383
 
369
- return Math.max(0, available - this.toolsDrawerOpeningReservePx());
370
- }
371
-
372
- /** While the tools drawer is opening, reserve width not yet claimed by flex shrink. */
373
- private toolsDrawerOpeningReservePx(): number {
374
- if (!this.toolsDrawerOpening) return 0;
375
-
376
- const tools = this.el.closest('ds-app-shell')?.querySelector('ds-panel-tools') as HTMLElement | null;
377
- if (!tools) return 0;
378
-
379
- const drawerTarget = readCssVarWidthPx(tools, '--_panel-tools-drawer-width');
380
- const drawerNow =
381
- tools.querySelector('.panel-tools__drawer')?.getBoundingClientRect().width ?? 0;
382
- return Math.max(0, drawerTarget - drawerNow);
384
+ return Math.max(0, available);
383
385
  }
384
386
 
385
387
  private getTabsIntrinsicWidth(): number {
@@ -393,6 +395,16 @@ export class BarNav {
393
395
  requestAnimationFrame(() => this.scheduleOverflowCheck());
394
396
  }
395
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
+
396
408
  private updateTabsCollapsed() {
397
409
  if (!this.headerEl || this.resolvedTabs.length === 0 || this.hideTabsForDetailRoute) {
398
410
  if (this.tabsCollapsed) {
@@ -400,15 +412,25 @@ export class BarNav {
400
412
  this.menuOpen = false;
401
413
  }
402
414
  this.intrinsicWidthRetryCount = 0;
415
+ this.tabLayoutPendingFrames = 0;
416
+ this.tabLayoutCommitted = true;
403
417
  return;
404
418
  }
405
419
 
406
420
  const intrinsicWidth = this.getTabsIntrinsicWidth();
407
421
  if (intrinsicWidth === 0) {
408
- this.scheduleIntrinsicWidthRetry();
422
+ this.tabLayoutPendingFrames += 1;
423
+ if (this.intrinsicWidthRetryCount >= BarNav.INTRINSIC_WIDTH_RETRY_MAX) {
424
+ this.forceTabLayoutCommit(false);
425
+ } else if (this.tabLayoutPendingFrames >= BarNav.TAB_LAYOUT_COMMIT_MAX_FRAMES) {
426
+ this.forceTabLayoutCommit(false);
427
+ } else {
428
+ this.scheduleIntrinsicWidthRetry();
429
+ }
409
430
  return;
410
431
  }
411
432
  this.intrinsicWidthRetryCount = 0;
433
+ this.tabLayoutPendingFrames = 0;
412
434
 
413
435
  const shouldCollapse = tabsOverflowContainer(
414
436
  intrinsicWidth,
@@ -423,6 +445,8 @@ export class BarNav {
423
445
  this.menuOpen = false;
424
446
  }
425
447
  }
448
+
449
+ this.tabLayoutCommitted = true;
426
450
  }
427
451
 
428
452
  private syncMenuAnchor() {
@@ -469,6 +493,7 @@ export class BarNav {
469
493
  this.pendingSection = nextBasePath;
470
494
  this.resolvedTabs = [];
471
495
  this.resolvedActions = this.incomingActions();
496
+ this.resetTabOverflowLayout();
472
497
  return;
473
498
  }
474
499
 
@@ -486,11 +511,17 @@ export class BarNav {
486
511
  this.pendingSection = '';
487
512
  }
488
513
 
489
- this.resolvedTabs = this.incomingTabs();
514
+ const tabs = this.incomingTabs();
515
+ const tabsConfigChanged =
516
+ this.digestTabsConfig(tabs) !== this.digestTabsConfig(this.resolvedTabs);
517
+
518
+ this.resolvedTabs = tabs;
490
519
  this.resolvedActions = this.incomingActions();
491
520
  this.syncValueFromUrl();
492
521
  this.committedSection = nextBasePath;
493
- this.intrinsicWidthRetryCount = 0;
522
+ if (tabsConfigChanged) {
523
+ this.resetTabOverflowLayout();
524
+ }
494
525
  this.scheduleOverflowCheck();
495
526
  }
496
527
 
@@ -593,7 +624,8 @@ export class BarNav {
593
624
  'bar-nav': true,
594
625
  'bar-nav--dashboard': this.navStyle === 'dashboard',
595
626
  'bar-nav--settings': this.navStyle === 'settings',
596
- 'bar-nav--tabs-collapsed': hasTabs && this.tabsCollapsed,
627
+ 'bar-nav--tabs-collapsed':
628
+ hasTabs && this.tabLayoutCommitted && this.tabsCollapsed,
597
629
  }}
598
630
  ref={el => {
599
631
  this.headerEl = el as HTMLElement;
@@ -607,9 +639,12 @@ export class BarNav {
607
639
  <ds-tab-group-nav
608
640
  key={`probe-${tabGroupKey}`}
609
641
  ref={el => {
610
- this.probeTabGroupEl = el ?? null;
611
- if (el) {
642
+ const next = el ?? null;
643
+ if (next === this.probeTabGroupEl) return;
644
+ this.probeTabGroupEl = next;
645
+ if (next) {
612
646
  this.intrinsicWidthRetryCount = 0;
647
+ this.tabLayoutPendingFrames = 0;
613
648
  this.scheduleOverflowCheck();
614
649
  }
615
650
  }}
@@ -619,7 +654,7 @@ export class BarNav {
619
654
  </div>
620
655
  )}
621
656
 
622
- {hasTabs && !this.tabsCollapsed && (
657
+ {hasTabs && this.tabLayoutCommitted && !this.tabsCollapsed && (
623
658
  <div class="bar-nav__left">
624
659
  <ds-tab-group-nav
625
660
  key={`visible-${tabGroupKey}`}
@@ -634,11 +669,15 @@ export class BarNav {
634
669
  </div>
635
670
  )}
636
671
 
672
+ {hasTabs && !this.tabLayoutCommitted && (
673
+ <div class="bar-nav__tabs-pending" aria-hidden="true" />
674
+ )}
675
+
637
676
  {!hasTabs && this.heading && (
638
677
  <span class="bar-nav__heading text-body-medium-emphasis">{this.heading}</span>
639
678
  )}
640
679
 
641
- {hasTabs && this.tabsCollapsed && (
680
+ {hasTabs && this.tabLayoutCommitted && this.tabsCollapsed && (
642
681
  <button
643
682
  type="button"
644
683
  class={{
@@ -697,7 +736,10 @@ export class BarNav {
697
736
  </button>
698
737
  )}
699
738
 
700
- {hasTabs && this.tabsCollapsed && this.resolvedActions.length > 0 && (
739
+ {hasTabs &&
740
+ this.tabLayoutCommitted &&
741
+ this.tabsCollapsed &&
742
+ this.resolvedActions.length > 0 && (
701
743
  <div class="bar-nav__between" aria-hidden="true" />
702
744
  )}
703
745
 
@@ -719,7 +761,7 @@ export class BarNav {
719
761
 
720
762
  </header>
721
763
 
722
- {hasTabs && this.tabsCollapsed && (
764
+ {hasTabs && this.tabLayoutCommitted && this.tabsCollapsed && (
723
765
  <ds-menu
724
766
  ref={el => {
725
767
  this.menuEl = (el as HTMLDsMenuElement) ?? null;
@@ -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
- onCollapsedChange() {
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.isAnimating = false;
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() {
@@ -82,7 +82,10 @@ export class PanelTools {
82
82
  openChanged(isOpen: boolean, wasOpen?: boolean) {
83
83
  if (this.readyForMotion && wasOpen !== undefined && wasOpen !== isOpen) {
84
84
  this.motion = isOpen ? 'opening' : 'closing';
85
- this.dsChromeTransitionStart.emit({ source: 'panel-tools' });
85
+ this.dsChromeTransitionStart.emit({
86
+ source: 'panel-tools',
87
+ phase: isOpen ? 'opening' : 'closing',
88
+ });
86
89
  }
87
90
  this.deferMotionEnable();
88
91
  }
@@ -7,6 +7,8 @@ export type ChromeTransitionSource = 'panel-nav' | 'panel-tools';
7
7
 
8
8
  export interface ChromeTransitionDetail {
9
9
  source: ChromeTransitionSource;
10
+ /** Panel-tools drawer motion direction; omitted for panel-nav. */
11
+ phase?: 'opening' | 'closing';
10
12
  }
11
13
 
12
14
  /** Reference-counted gate — used while panel-nav width is transitioning. */
@@ -54,3 +56,7 @@ export function createRafCoalescer(onFrame: () => void): RafCoalescer {
54
56
  export function readChromeTransitionSource(event: Event): ChromeTransitionSource | undefined {
55
57
  return (event as CustomEvent<ChromeTransitionDetail>).detail?.source;
56
58
  }
59
+
60
+ export function readChromeTransitionPhase(event: Event): ChromeTransitionDetail['phase'] {
61
+ return (event as CustomEvent<ChromeTransitionDetail>).detail?.phase;
62
+ }
@@ -34,8 +34,9 @@ export {
34
34
  shellGradientPositionBar,
35
35
  shellChromeSurfacePosition,
36
36
  shellChromeLayerActive,
37
+ readShellViewportDimensions,
37
38
  } from './shell-gradient';
38
- export type { ShellGradientLayout } from './shell-gradient';
39
+ export type { ShellGradientLayout, ShellViewportDimensions } from './shell-gradient';
39
40
  export type { ChromeTransitionDetail, ChromeTransitionSource } from './chrome-transition';
40
41
  export {
41
42
  CHROME_TRANSITION_END,
@@ -43,6 +44,7 @@ export {
43
44
  ChromeTransitionDepth,
44
45
  createRafCoalescer,
45
46
  readChromeTransitionSource,
47
+ readChromeTransitionPhase,
46
48
  } from './chrome-transition';
47
49
  export {
48
50
  barGradientPositionFromPanelWidth,
@@ -30,13 +30,35 @@ export function shellGradientImage(): string {
30
30
  return buildShellRadialGradient();
31
31
  }
32
32
 
33
+ export interface ShellViewportDimensions {
34
+ width: number;
35
+ height: number;
36
+ }
37
+
38
+ /**
39
+ * Viewport size for shell chrome with `background-attachment: fixed`.
40
+ * Must not use the `ds-app-shell` element box — the shell can be shorter or
41
+ * taller than the viewport when host height chains break or content overflows.
42
+ */
43
+ export function readShellViewportDimensions(win?: Window): ShellViewportDimensions {
44
+ const w = win ?? (typeof globalThis.window !== 'undefined' ? globalThis.window : undefined);
45
+ if (!w) return { width: 0, height: 0 };
46
+
47
+ const visual = w.visualViewport;
48
+ return {
49
+ width: Math.round(visual?.width ?? w.innerWidth),
50
+ height: Math.round(visual?.height ?? w.innerHeight),
51
+ };
52
+ }
53
+
33
54
  export interface ShellGradientLayout {
34
55
  width: number;
35
56
  height: number;
36
57
  panelWidth: number;
37
58
  }
38
59
 
39
- export function shellGradientSize(layout: ShellGradientLayout): string {
60
+ /** Pixel `background-size` for the fixed-attachment radial wash. */
61
+ export function shellGradientSize(layout: Pick<ShellGradientLayout, 'width' | 'height'>): string {
40
62
  return `${Math.round(layout.width)}px ${Math.round(layout.height)}px`;
41
63
  }
42
64
 
@@ -1,2 +0,0 @@
1
- const t="dsChromeTransitionStart";const n="dsChromeTransitionEnd";class e{constructor(){this.depth=0}enter(){this.depth+=1}exit(){this.depth=Math.max(0,this.depth-1)}get isActive(){return this.depth>0}}function i(t){let n=0;return{schedule(){if(n!==0)return;n=requestAnimationFrame((()=>{n=0;t()}))},cancel(){if(n!==0){cancelAnimationFrame(n);n=0}}}}function r(t){return t.detail?.source}function s(t,n){if(typeof document==="undefined")return 0;const e=document.createElement("div");e.style.cssText="position:absolute;visibility:hidden;pointer-events:none;height:0;width:var("+n+");";t.appendChild(e);const i=e.getBoundingClientRect().width;t.removeChild(e);return i}function a(t){return{expandedPx:s(t,"--_nav-width"),collapsedPx:s(t,"--_nav-width-collapsed")}}function o(t,n){if(n?.classList.contains("panel-nav--collapsed"))return true;const e=t?.collapsed;return e===true}function c(t,n){return n?t.collapsedPx:t.expandedPx}export{e as C,t as a,n as b,i as c,a as d,s as e,o as i,c as p,r};
2
- //# sourceMappingURL=p-1HlgCxGN.js.map
@@ -1 +0,0 @@
1
- {"version":3,"names":["CHROME_TRANSITION_START","CHROME_TRANSITION_END","ChromeTransitionDepth","constructor","this","depth","enter","exit","Math","max","isActive","createRafCoalescer","onFrame","rafId","schedule","requestAnimationFrame","cancel","cancelAnimationFrame","readChromeTransitionSource","event","detail","source","readCssVarWidthPx","context","cssVarName","document","probe","createElement","style","cssText","appendChild","width","getBoundingClientRect","removeChild","readPanelNavWidthTokens","navRoot","expandedPx","collapsedPx","isPanelNavCollapsed","panelNavHost","classList","contains","collapsedProp","collapsed","panelWidthPxFromTokens","tokens"],"sources":["src/wc/nav/chrome-transition.ts","src/wc/nav/shell-chrome-metrics.ts"],"sourcesContent":["/** Bubbling/composed events — `ds-app-shell` and `ds-bar-nav` coordinate during width motion. */\n\nexport const CHROME_TRANSITION_START = 'dsChromeTransitionStart';\nexport const CHROME_TRANSITION_END = 'dsChromeTransitionEnd';\n\nexport type ChromeTransitionSource = 'panel-nav' | 'panel-tools';\n\nexport interface ChromeTransitionDetail {\n source: ChromeTransitionSource;\n}\n\n/** Reference-counted gate — used while panel-nav width is transitioning. */\nexport class ChromeTransitionDepth {\n private depth = 0;\n\n enter(): void {\n this.depth += 1;\n }\n\n exit(): void {\n this.depth = Math.max(0, this.depth - 1);\n }\n\n get isActive(): boolean {\n return this.depth > 0;\n }\n}\n\nexport interface RafCoalescer {\n schedule(): void;\n cancel(): void;\n}\n\n/** Coalesce bursty layout work (ResizeObserver, prop churn) to one callback per frame. */\nexport function createRafCoalescer(onFrame: () => void): RafCoalescer {\n let rafId = 0;\n return {\n schedule() {\n if (rafId !== 0) return;\n rafId = requestAnimationFrame(() => {\n rafId = 0;\n onFrame();\n });\n },\n cancel() {\n if (rafId !== 0) {\n cancelAnimationFrame(rafId);\n rafId = 0;\n }\n },\n };\n}\n\nexport function readChromeTransitionSource(event: Event): ChromeTransitionSource | undefined {\n return (event as CustomEvent<ChromeTransitionDetail>).detail?.source;\n}\n","import { shellGradientPositionBar } from './shell-gradient';\n\n/** Read a `var(--token)` width in px using a hidden probe (works with calc() tokens). */\nexport function readCssVarWidthPx(context: HTMLElement, cssVarName: string): number {\n if (typeof document === 'undefined') return 0;\n\n const probe = document.createElement('div');\n probe.style.cssText =\n 'position:absolute;visibility:hidden;pointer-events:none;height:0;width:var(' +\n cssVarName +\n ');';\n context.appendChild(probe);\n const width = probe.getBoundingClientRect().width;\n context.removeChild(probe);\n return width;\n}\n\nexport interface PanelNavWidthTokens {\n expandedPx: number;\n collapsedPx: number;\n}\n\n/** Resolve panel-nav expanded/collapsed widths from scoped CSS vars on `.panel-nav`. */\nexport function readPanelNavWidthTokens(navRoot: HTMLElement): PanelNavWidthTokens {\n return {\n expandedPx: readCssVarWidthPx(navRoot, '--_nav-width'),\n collapsedPx: readCssVarWidthPx(navRoot, '--_nav-width-collapsed'),\n };\n}\n\nexport function isPanelNavCollapsed(\n panelNavHost: HTMLElement | null,\n navRoot: HTMLElement | null,\n): boolean {\n if (navRoot?.classList.contains('panel-nav--collapsed')) return true;\n const collapsedProp = (panelNavHost as (HTMLElement & { collapsed?: boolean }) | null)?.collapsed;\n return collapsedProp === true;\n}\n\nexport function panelWidthPxFromTokens(\n tokens: PanelNavWidthTokens,\n collapsed: boolean,\n): number {\n return collapsed ? tokens.collapsedPx : tokens.expandedPx;\n}\n\nexport function barGradientPositionFromPanelWidth(panelWidthPx: number): string {\n return shellGradientPositionBar(panelWidthPx);\n}\n"],"mappings":"AAEO,MAAMA,EAA0B,0BAChC,MAAMC,EAAwB,wB,MASxBC,EAAb,WAAAC,GACUC,KAAAC,MAAQ,C,CAEhB,KAAAC,GACEF,KAAKC,OAAS,C,CAGhB,IAAAE,GACEH,KAAKC,MAAQG,KAAKC,IAAI,EAAGL,KAAKC,MAAQ,E,CAGxC,YAAIK,GACF,OAAON,KAAKC,MAAQ,C,EAUlB,SAAUM,EAAmBC,GACjC,IAAIC,EAAQ,EACZ,MAAO,CACL,QAAAC,GACE,GAAID,IAAU,EAAG,OACjBA,EAAQE,uBAAsB,KAC5BF,EAAQ,EACRD,GAAS,G,EAGb,MAAAI,GACE,GAAIH,IAAU,EAAG,CACfI,qBAAqBJ,GACrBA,EAAQ,C,GAIhB,CAEM,SAAUK,EAA2BC,GACzC,OAAQA,EAA8CC,QAAQC,MAChE,CCpDM,SAAUC,EAAkBC,EAAsBC,GACtD,UAAWC,WAAa,YAAa,OAAO,EAE5C,MAAMC,EAAQD,SAASE,cAAc,OACrCD,EAAME,MAAMC,QACV,8EACAL,EACA,KACFD,EAAQO,YAAYJ,GACpB,MAAMK,EAAQL,EAAMM,wBAAwBD,MAC5CR,EAAQU,YAAYP,GACpB,OAAOK,CACT,CAQM,SAAUG,EAAwBC,GACtC,MAAO,CACLC,WAAYd,EAAkBa,EAAS,gBACvCE,YAAaf,EAAkBa,EAAS,0BAE5C,CAEM,SAAUG,EACdC,EACAJ,GAEA,GAAIA,GAASK,UAAUC,SAAS,wBAAyB,OAAO,KAChE,MAAMC,EAAiBH,GAAiEI,UACxF,OAAOD,IAAkB,IAC3B,CAEM,SAAUE,EACdC,EACAF,GAEA,OAAOA,EAAYE,EAAOR,YAAcQ,EAAOT,UACjD,Q","ignoreList":[]}