@ds-mo/ui 1.10.1 → 1.11.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/.build-stamp +1 -1
- package/dist/components/ds-app-shell.js +1 -1
- package/dist/components/ds-app-shell.js.map +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/components/ds-panel-tools.js +1 -1
- package/dist/components/ds-panel-tools.js.map +1 -1
- package/dist/components/p-1HlgCxGN.js +2 -0
- package/dist/components/p-1HlgCxGN.js.map +1 -0
- package/dist/types/components/AppShell/AppShell.d.ts +6 -3
- package/dist/types/components/BarNav/BarNav.d.ts +2 -2
- package/dist/types/components/PanelTools/panel-tools-utils.d.ts +3 -0
- package/dist/types/components.d.ts +13 -2
- package/dist/types/nav/chrome-transition.d.ts +14 -0
- package/dist/types/nav/index.d.ts +2 -2
- package/dist/types/nav/shell-gradient.d.ts +2 -0
- package/package.json +2 -2
- package/src/angular/proxies.ts +2 -2
- package/src/wc/components/AppShell/AppShell.tsx +48 -33
- package/src/wc/components/BarNav/BarNav.tsx +18 -19
- package/src/wc/components/PanelTools/PanelTools.tsx +3 -1
- package/src/wc/components/PanelTools/panel-tools-utils.ts +7 -0
- package/src/wc/components.d.ts +13 -2
- package/src/wc/nav/chrome-transition.ts +46 -0
- package/src/wc/nav/index.ts +4 -0
- package/src/wc/nav/shell-gradient.ts +5 -0
- package/dist/components/p-DN65lYYc.js +0 -2
- package/dist/components/p-DN65lYYc.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["PANEL_TOOLS_PRIMARY_TOOL_ID","PANEL_TOOLS_LABELS","search","messages","stacks","activity","agents","parsePanelToolsItems","items","itemsJson","parseJsonArrayProp","panelToolsCss","PanelTools","__stencil_proxyCustomElement","HTMLElement","constructor","registerHost","this","open","activeTool","motion","readyForMotion","motionEnableGeneration","handleTransitionEnd","event","target","el","querySelector","propertyName","dsChromeTransitionEnd","emit","source","handleToolChange","id","selected","isRailSelected","dsToolChange","railItems","disconnectedCallback","removeEventListener","deferMotionEnable","generation","requestAnimationFrame","connectedCallback","addEventListener","openChanged","isOpen","wasOpen","undefined","dsChromeTransitionStart","activeToolChanged","componentDidLoad","itemsChanged","isDrawerPresent","isViewActive","headerLabel","renderRailAction","item","h","key","class","icon","dot","inactive","ariaLabel","onDsChange","render","headerItem","find","bodyItems","filter","showDrawerChrome","Host","role","hidden","name","map"],"sources":["src/wc/components/PanelTools/panel-tools-types.ts","src/wc/components/PanelTools/panel-tools-utils.ts","src/wc/components/PanelTools/PanelTools.css?tag=ds-panel-tools&encapsulation=scoped","src/wc/components/PanelTools/PanelTools.tsx"],"sourcesContent":["/** Tool ids for the `ds-panel-tools` rail and drawer views. */\nexport type PanelToolsToolId = 'search' | 'agents' | 'messages' | 'stacks' | 'activity';\n\nexport const PANEL_TOOLS_TOOL_IDS: PanelToolsToolId[] = [\n 'search',\n 'agents',\n 'messages',\n 'stacks',\n 'activity',\n];\n\n/** Rail header slot — mirrors panel-nav M logo row. */\nexport const PANEL_TOOLS_PRIMARY_TOOL_ID: PanelToolsToolId = 'search';\n\nexport const PANEL_TOOLS_LABELS: Record<PanelToolsToolId, string> = {\n search: 'Search',\n messages: 'Messages',\n stacks: 'Stacks',\n activity: 'Activity',\n agents: 'Agents',\n};\n\nexport interface PanelToolsItem {\n id: PanelToolsToolId;\n /** Icon name for <ds-icon>. */\n icon: string;\n /** Whether this rail button is currently pressed/active. */\n selected?: boolean;\n /** Show a notification dot. */\n dot?: boolean;\n inactive?: boolean;\n ariaLabel?: string;\n}\n","import { parseJsonArrayProp } from '../BarNav/bar-nav-utils';\nimport type { PanelToolsItem } from './panel-tools-types';\n\nexport function parsePanelToolsItems(\n items: PanelToolsItem[],\n itemsJson: string,\n): PanelToolsItem[] {\n if (itemsJson) {\n return parseJsonArrayProp<PanelToolsItem>(itemsJson, []);\n }\n return items ?? [];\n}\n\nexport function shouldResyncPanelToolsItems(\n prev: PanelToolsItem[],\n next: PanelToolsItem[],\n): boolean {\n if (prev.length !== next.length) return true;\n return next.some((item, index) => {\n const prior = prev[index];\n if (!prior) return true;\n return (\n prior.id !== item.id ||\n prior.icon !== item.icon ||\n prior.selected !== item.selected ||\n prior.dot !== item.dot ||\n prior.inactive !== item.inactive ||\n prior.ariaLabel !== item.ariaLabel\n );\n });\n}\n",":host {\n display: block;\n flex: 0 0 auto;\n box-sizing: border-box;\n height: 100%;\n min-height: 0;\n overflow: hidden;\n --_panel-tools-rail-width: var(--dimension-size-600);\n --_panel-tools-drawer-width: var(--dimension-panel-width-sm);\n --_panel-tools-border: var(--dimension-stroke-width-012, 1px) solid var(--color-border-tertiary);\n /* Match panel-nav expand timing (medium-1); easing differs by direction. */\n --_panel-tools-expand-dur: var(--effect-animation-duration-medium-1, 300ms);\n}\n\n.panel-tools__layout {\n display: flex;\n flex-direction: row;\n align-items: stretch;\n height: 100%;\n min-height: 0;\n box-sizing: border-box;\n}\n\n/* Clip frame — only max-width animates (slide reveal, same as pre-rail :host). */\n.panel-tools__drawer {\n flex: 0 0 auto;\n max-width: 0;\n min-width: 0;\n height: 100%;\n min-height: 0;\n overflow: hidden;\n box-sizing: border-box;\n transition: none;\n}\n\n:host(.panel-tools--ready.panel-tools--motion-opening) .panel-tools__drawer {\n transition: max-width var(--_panel-tools-expand-dur)\n var(--effect-animation-easing-ease-out, ease-out);\n}\n\n:host(.panel-tools--ready.panel-tools--motion-closing) .panel-tools__drawer {\n transition: max-width var(--_panel-tools-expand-dur)\n var(--effect-animation-easing-ease-in, ease-in);\n}\n\n:host(.panel-tools--open) .panel-tools__drawer {\n max-width: var(--_panel-tools-drawer-width);\n}\n\n.panel-tools__drawer--visible {\n border-left: var(--_panel-tools-border);\n}\n\n/* Fixed-width surface — stays 400px while the clip frame opens/closes. */\n.panel-tools__drawer-surface {\n display: flex;\n flex-direction: column;\n width: var(--_panel-tools-drawer-width);\n height: 100%;\n min-height: 0;\n box-sizing: border-box;\n background-color: var(--color-background-primary);\n}\n\n/* Shell-owned chrome (bg + wash + grid) — drawer is a transparent frame. */\n:host-context(ds-app-shell[gradient]) .panel-tools__drawer-surface {\n background-color: transparent;\n}\n\n/* Aligns with ds-bar-nav row — same height, inset, border, and title token. */\n.panel-tools__header {\n flex: 0 0 auto;\n display: flex;\n align-items: center;\n height: var(--dimension-size-600);\n padding: var(--dimension-space-100);\n border-bottom: var(--_panel-tools-border);\n color: var(--color-foreground-primary);\n box-sizing: border-box;\n -webkit-user-select: none;\n user-select: none;\n}\n\n.panel-tools__title {\n display: inline-flex;\n align-items: center;\n height: var(--dimension-size-400);\n padding-inline: var(--dimension-space-100);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.panel-tools__body {\n flex: 1 1 auto;\n min-height: 0;\n overflow: auto;\n position: relative;\n}\n\n.panel-tools__view {\n display: none;\n height: 100%;\n min-height: 0;\n box-sizing: border-box;\n padding: var(--dimension-space-200);\n color: var(--color-foreground-secondary);\n font-family: var(--typography-font-family);\n font-size: var(--typography-fontsize-md);\n line-height: var(--typography-lineheight-md);\n font-weight: var(--typography-weight-regular);\n letter-spacing: var(--typography-letterspacing-negative-half);\n}\n\n.panel-tools__view ::slotted(*) {\n margin: 0;\n color: var(--color-foreground-secondary);\n font-family: var(--typography-font-family);\n font-size: var(--typography-fontsize-md);\n line-height: var(--typography-lineheight-md);\n font-weight: var(--typography-weight-regular);\n letter-spacing: var(--typography-letterspacing-negative-half);\n}\n\n.panel-tools__view--active {\n display: block;\n}\n\n/* Rail — mirrors collapsed panel-nav width, padding, and item rhythm. */\n.panel-tools__rail {\n flex: 0 0 var(--_panel-tools-rail-width);\n width: var(--_panel-tools-rail-width);\n min-width: var(--_panel-tools-rail-width);\n display: flex;\n flex-direction: column;\n box-sizing: border-box;\n height: 100%;\n min-height: 0;\n border-left: var(--_panel-tools-border);\n}\n\n.panel-tools__rail-header {\n display: flex;\n align-items: center;\n justify-content: center;\n height: var(--dimension-size-600);\n padding: var(--dimension-space-100);\n flex-shrink: 0;\n box-sizing: border-box;\n border-bottom: var(--_panel-tools-border);\n}\n\n.panel-tools__rail-body {\n flex: 1 1 auto;\n min-height: 0;\n overflow-y: auto;\n overflow-x: hidden;\n display: flex;\n flex-direction: column;\n gap: var(--dimension-space-050);\n padding: var(--dimension-space-100);\n box-sizing: border-box;\n scrollbar-width: none;\n}\n\n.panel-tools__rail-body::-webkit-scrollbar {\n display: none;\n}\n\n.panel-tools__rail-action {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 100%;\n height: var(--dimension-size-400);\n flex-shrink: 0;\n}\n","import { Component, Prop, Event, EventEmitter, Element, State, Watch, h, Host } from '@stencil/core';\nimport type { ChromeTransitionDetail } from '../../nav/chrome-transition';\nimport {\n PANEL_TOOLS_LABELS,\n PANEL_TOOLS_PRIMARY_TOOL_ID,\n type PanelToolsItem,\n type PanelToolsToolId,\n} from './panel-tools-types';\nimport { parsePanelToolsItems } from './panel-tools-utils';\n\n@Component({\n tag: 'ds-panel-tools',\n styleUrl: 'PanelTools.css',\n scoped: true,\n})\nexport class PanelTools {\n @Element() el!: HTMLElement;\n\n /** When false, only the icon rail is shown. */\n @Prop({ mutable: true, reflect: true }) open: boolean = false;\n\n /** Active tool view — `search`, `agents`, `messages`, `stacks`, or `activity`. */\n @Prop({ mutable: true, attribute: 'active-tool', reflect: true })\n activeTool: PanelToolsToolId | '' = '';\n\n /**\n * Rail items rendered in the right column.\n * Set via JS property: `el.items = [...]`. Replace the array reference to update.\n */\n @Prop() items: PanelToolsItem[] = [];\n\n /** JSON fallback for `items` — useful when framework bindings don't propagate arrays. */\n @Prop({ attribute: 'items-json' }) itemsJson: string = '';\n\n /** Emitted when a rail button is toggled. Detail = { id, selected }. */\n @Event({ bubbles: true, composed: true }) dsToolChange!: EventEmitter<{\n id: PanelToolsToolId;\n selected: boolean;\n }>;\n\n /** Bubbling lifecycle — `ds-bar-nav` defers overflow checks during drawer motion. */\n @Event({ bubbles: true, composed: true })\n dsChromeTransitionStart!: EventEmitter<ChromeTransitionDetail>;\n\n @Event({ bubbles: true, composed: true })\n dsChromeTransitionEnd!: EventEmitter<ChromeTransitionDetail>;\n\n /** Arms open vs close easing for the in-flight width transition. */\n @State() private motion: 'opening' | 'closing' | 'idle' = 'idle';\n\n /** Suppresses width transition until the host has painted its initial open state. */\n @State() private readyForMotion = false;\n\n private motionEnableGeneration = 0;\n\n private get railItems(): PanelToolsItem[] {\n return parsePanelToolsItems(this.items, this.itemsJson);\n }\n\n disconnectedCallback() {\n this.el.removeEventListener('transitionend', this.handleTransitionEnd);\n this.motionEnableGeneration += 1;\n }\n\n private deferMotionEnable() {\n if (this.readyForMotion) return;\n const generation = ++this.motionEnableGeneration;\n requestAnimationFrame(() => {\n if (generation !== this.motionEnableGeneration) return;\n requestAnimationFrame(() => {\n if (generation !== this.motionEnableGeneration) return;\n this.readyForMotion = true;\n });\n });\n }\n\n connectedCallback() {\n this.el.addEventListener('transitionend', this.handleTransitionEnd);\n }\n\n @Watch('open')\n openChanged(isOpen: boolean, wasOpen?: boolean) {\n if (this.readyForMotion && wasOpen !== undefined && wasOpen !== isOpen) {\n this.motion = isOpen ? 'opening' : 'closing';\n this.dsChromeTransitionStart.emit({ source: 'panel-tools' });\n }\n this.deferMotionEnable();\n }\n\n @Watch('activeTool')\n activeToolChanged() {\n this.deferMotionEnable();\n }\n\n componentDidLoad() {\n this.deferMotionEnable();\n }\n\n @Watch('items')\n @Watch('itemsJson')\n itemsChanged() {\n this.deferMotionEnable();\n }\n\n private handleTransitionEnd = (event: TransitionEvent) => {\n if (event.target !== this.el.querySelector('.panel-tools__drawer')) return;\n if (event.propertyName !== 'max-width') return;\n this.motion = 'idle';\n this.dsChromeTransitionEnd.emit({ source: 'panel-tools' });\n };\n\n /** Rail selection follows `open` immediately — independent of the slide animation. */\n private isRailSelected(id: PanelToolsToolId): boolean {\n return this.open && this.activeTool === id;\n }\n\n /** Drawer body stays mounted while the clip frame animates closed. */\n private isDrawerPresent(): boolean {\n return this.open || this.motion === 'closing';\n }\n\n private isViewActive(id: PanelToolsToolId): boolean {\n return this.isDrawerPresent() && this.activeTool === id;\n }\n\n private headerLabel(): string {\n if (!this.isDrawerPresent() || !this.activeTool) return '';\n return PANEL_TOOLS_LABELS[this.activeTool as PanelToolsToolId] ?? '';\n }\n\n private handleToolChange = (id: PanelToolsToolId) => {\n const selected = !this.isRailSelected(id);\n if (selected) {\n this.open = true;\n this.activeTool = id;\n } else {\n this.open = false;\n }\n this.dsToolChange.emit({ id, selected });\n };\n\n private renderRailAction(item: PanelToolsItem) {\n return (\n <ds-bar-nav-action\n key={item.id}\n class=\"panel-tools__rail-action\"\n icon={item.icon}\n selected={this.isRailSelected(item.id)}\n dot={item.dot ?? false}\n inactive={item.inactive}\n aria-label={item.ariaLabel ?? PANEL_TOOLS_LABELS[item.id]}\n onDsChange={() => this.handleToolChange(item.id)}\n />\n );\n }\n\n render() {\n const headerLabel = this.headerLabel();\n const railItems = this.railItems;\n const headerItem = railItems.find(item => item.id === PANEL_TOOLS_PRIMARY_TOOL_ID);\n const bodyItems = railItems.filter(item => item.id !== PANEL_TOOLS_PRIMARY_TOOL_ID);\n const showDrawerChrome = this.isDrawerPresent();\n\n return (\n <Host\n class={{\n 'panel-tools': true,\n 'panel-tools--open': this.open,\n 'panel-tools--ready': this.readyForMotion,\n 'panel-tools--motion-opening': this.motion === 'opening',\n 'panel-tools--motion-closing': this.motion === 'closing',\n }}\n role=\"complementary\"\n aria-label=\"Tools\"\n >\n <div class=\"panel-tools__layout\">\n <div\n class={{\n 'panel-tools__drawer': true,\n 'panel-tools__drawer--visible': showDrawerChrome,\n }}\n aria-hidden={showDrawerChrome ? null : 'true'}\n >\n <div class=\"panel-tools__drawer-surface\">\n <header class=\"panel-tools__header\">\n <span class=\"panel-tools__title text-body-medium-emphasis\">{headerLabel}</span>\n </header>\n <div class=\"panel-tools__body\">\n <div\n class={{\n 'panel-tools__view': true,\n 'panel-tools__view--active': this.isViewActive('search'),\n 'text-body-medium': true,\n }}\n hidden={!this.isViewActive('search')}\n >\n <slot name=\"search\" />\n </div>\n <div\n class={{\n 'panel-tools__view': true,\n 'panel-tools__view--active': this.isViewActive('messages'),\n 'text-body-medium': true,\n }}\n hidden={!this.isViewActive('messages')}\n >\n <slot name=\"messages\" />\n </div>\n <div\n class={{\n 'panel-tools__view': true,\n 'panel-tools__view--active': this.isViewActive('stacks'),\n 'text-body-medium': true,\n }}\n hidden={!this.isViewActive('stacks')}\n >\n <slot name=\"stacks\" />\n </div>\n <div\n class={{\n 'panel-tools__view': true,\n 'panel-tools__view--active': this.isViewActive('activity'),\n 'text-body-medium': true,\n }}\n hidden={!this.isViewActive('activity')}\n >\n <slot name=\"activity\" />\n </div>\n <div\n class={{\n 'panel-tools__view': true,\n 'panel-tools__view--active': this.isViewActive('agents'),\n 'text-body-medium': true,\n }}\n hidden={!this.isViewActive('agents')}\n >\n <slot name=\"agents\" />\n </div>\n </div>\n </div>\n </div>\n\n <nav class=\"panel-tools__rail\" aria-label=\"Tool shortcuts\">\n {headerItem ? (\n <div class=\"panel-tools__rail-header\">{this.renderRailAction(headerItem)}</div>\n ) : null}\n <div class=\"panel-tools__rail-body\">\n {bodyItems.map(item => this.renderRailAction(item))}\n </div>\n </nav>\n </div>\n </Host>\n );\n }\n}\n"],"mappings":"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","ignoreList":[]}
|
|
1
|
+
{"version":3,"names":["PANEL_TOOLS_PRIMARY_TOOL_ID","PANEL_TOOLS_LABELS","search","messages","stacks","activity","agents","parsePanelToolsItems","items","itemsJson","parseJsonArrayProp","panelToolsDrawerResting","open","motion","panelToolsCss","PanelTools","__stencil_proxyCustomElement","HTMLElement","constructor","registerHost","this","activeTool","readyForMotion","motionEnableGeneration","handleTransitionEnd","event","target","el","querySelector","propertyName","dsChromeTransitionEnd","emit","source","handleToolChange","id","selected","isRailSelected","dsToolChange","railItems","disconnectedCallback","removeEventListener","deferMotionEnable","generation","requestAnimationFrame","connectedCallback","addEventListener","openChanged","isOpen","wasOpen","undefined","dsChromeTransitionStart","activeToolChanged","componentDidLoad","itemsChanged","isDrawerPresent","isViewActive","headerLabel","renderRailAction","item","h","key","class","icon","dot","inactive","ariaLabel","onDsChange","render","headerItem","find","bodyItems","filter","showDrawerChrome","drawerResting","Host","role","hidden","name","map"],"sources":["src/wc/components/PanelTools/panel-tools-types.ts","src/wc/components/PanelTools/panel-tools-utils.ts","src/wc/components/PanelTools/PanelTools.css?tag=ds-panel-tools&encapsulation=scoped","src/wc/components/PanelTools/PanelTools.tsx"],"sourcesContent":["/** Tool ids for the `ds-panel-tools` rail and drawer views. */\nexport type PanelToolsToolId = 'search' | 'agents' | 'messages' | 'stacks' | 'activity';\n\nexport const PANEL_TOOLS_TOOL_IDS: PanelToolsToolId[] = [\n 'search',\n 'agents',\n 'messages',\n 'stacks',\n 'activity',\n];\n\n/** Rail header slot — mirrors panel-nav M logo row. */\nexport const PANEL_TOOLS_PRIMARY_TOOL_ID: PanelToolsToolId = 'search';\n\nexport const PANEL_TOOLS_LABELS: Record<PanelToolsToolId, string> = {\n search: 'Search',\n messages: 'Messages',\n stacks: 'Stacks',\n activity: 'Activity',\n agents: 'Agents',\n};\n\nexport interface PanelToolsItem {\n id: PanelToolsToolId;\n /** Icon name for <ds-icon>. */\n icon: string;\n /** Whether this rail button is currently pressed/active. */\n selected?: boolean;\n /** Show a notification dot. */\n dot?: boolean;\n inactive?: boolean;\n ariaLabel?: string;\n}\n","import { parseJsonArrayProp } from '../BarNav/bar-nav-utils';\nimport type { PanelToolsItem } from './panel-tools-types';\n\nexport function parsePanelToolsItems(\n items: PanelToolsItem[],\n itemsJson: string,\n): PanelToolsItem[] {\n if (itemsJson) {\n return parseJsonArrayProp<PanelToolsItem>(itemsJson, []);\n }\n return items ?? [];\n}\n\nexport function shouldResyncPanelToolsItems(\n prev: PanelToolsItem[],\n next: PanelToolsItem[],\n): boolean {\n if (prev.length !== next.length) return true;\n return next.some((item, index) => {\n const prior = prev[index];\n if (!prior) return true;\n return (\n prior.id !== item.id ||\n prior.icon !== item.icon ||\n prior.selected !== item.selected ||\n prior.dot !== item.dot ||\n prior.inactive !== item.inactive ||\n prior.ariaLabel !== item.ariaLabel\n );\n });\n}\n\nexport type PanelToolsMotion = 'opening' | 'closing' | 'idle';\n\n/** True when the drawer clip is fully closed — safe to skip painting slot content. */\nexport function panelToolsDrawerResting(open: boolean, motion: PanelToolsMotion): boolean {\n return !open && motion === 'idle';\n}\n",":host {\n display: block;\n flex: 0 0 auto;\n box-sizing: border-box;\n height: 100%;\n min-height: 0;\n overflow: hidden;\n --_panel-tools-rail-width: var(--dimension-size-600);\n --_panel-tools-drawer-width: var(--dimension-panel-width-sm);\n --_panel-tools-border: var(--dimension-stroke-width-012, 1px) solid var(--color-border-tertiary);\n --_panel-tools-surface-bg: var(--color-background-secondary);\n /* Match panel-nav expand timing (medium-1); easing differs by direction. */\n --_panel-tools-expand-dur: var(--effect-animation-duration-medium-1, 300ms);\n}\n\n.panel-tools__layout {\n display: flex;\n flex-direction: row;\n align-items: stretch;\n height: 100%;\n min-height: 0;\n box-sizing: border-box;\n}\n\n/* Clip frame — only max-width animates (slide reveal, same as pre-rail :host). */\n.panel-tools__drawer {\n flex: 0 0 auto;\n max-width: 0;\n min-width: 0;\n height: 100%;\n min-height: 0;\n overflow: hidden;\n box-sizing: border-box;\n transition: none;\n}\n\n:host(.panel-tools--ready.panel-tools--motion-opening) .panel-tools__drawer {\n transition: max-width var(--_panel-tools-expand-dur)\n var(--effect-animation-easing-ease-out, ease-out);\n}\n\n:host(.panel-tools--ready.panel-tools--motion-closing) .panel-tools__drawer {\n transition: max-width var(--_panel-tools-expand-dur)\n var(--effect-animation-easing-ease-in, ease-in);\n}\n\n:host(.panel-tools--open) .panel-tools__drawer {\n max-width: var(--_panel-tools-drawer-width);\n}\n\n.panel-tools__drawer--visible {\n border-left: var(--_panel-tools-border);\n}\n\n/* Fixed-width surface — stays 400px while the clip frame opens/closes. */\n.panel-tools__drawer-surface {\n display: flex;\n flex-direction: column;\n width: var(--_panel-tools-drawer-width);\n height: 100%;\n min-height: 0;\n box-sizing: border-box;\n background-color: var(--_panel-tools-surface-bg);\n}\n\n/* Shell-owned chrome (bg + wash +/or grid) — drawer + rail are transparent frames. */\n:host-context(ds-app-shell[gradient]) .panel-tools__drawer-surface,\n:host-context(ds-app-shell[grid]) .panel-tools__drawer-surface,\n:host-context(ds-app-shell[gradient]) .panel-tools__rail,\n:host-context(ds-app-shell[grid]) .panel-tools__rail {\n background-color: transparent;\n}\n\n/* Aligns with ds-bar-nav row — same height, inset, border, and title token. */\n.panel-tools__header {\n flex: 0 0 auto;\n display: flex;\n align-items: center;\n height: var(--dimension-size-600);\n padding: var(--dimension-space-100);\n border-bottom: var(--_panel-tools-border);\n color: var(--color-foreground-primary);\n box-sizing: border-box;\n -webkit-user-select: none;\n user-select: none;\n}\n\n.panel-tools__title {\n display: inline-flex;\n align-items: center;\n height: var(--dimension-size-400);\n padding-inline: var(--dimension-space-100);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.panel-tools__body {\n flex: 1 1 auto;\n min-height: 0;\n overflow: auto;\n position: relative;\n}\n\n/* Skip paint while fully closed; cleared during open/close motion for WYSIWYG slide. */\n:host(.panel-tools--drawer-resting) .panel-tools__body {\n content-visibility: auto;\n contain-intrinsic-size: auto 200px;\n}\n\n.panel-tools__view {\n display: none;\n height: 100%;\n min-height: 0;\n box-sizing: border-box;\n padding: var(--dimension-space-200);\n color: var(--color-foreground-secondary);\n font-family: var(--typography-font-family);\n font-size: var(--typography-fontsize-md);\n line-height: var(--typography-lineheight-md);\n font-weight: var(--typography-weight-regular);\n letter-spacing: var(--typography-letterspacing-negative-half);\n}\n\n.panel-tools__view ::slotted(*) {\n margin: 0;\n color: var(--color-foreground-secondary);\n font-family: var(--typography-font-family);\n font-size: var(--typography-fontsize-md);\n line-height: var(--typography-lineheight-md);\n font-weight: var(--typography-weight-regular);\n letter-spacing: var(--typography-letterspacing-negative-half);\n}\n\n.panel-tools__view--active {\n display: block;\n}\n\n/* Rail — mirrors collapsed panel-nav width, padding, and item rhythm. */\n.panel-tools__rail {\n flex: 0 0 var(--_panel-tools-rail-width);\n width: var(--_panel-tools-rail-width);\n min-width: var(--_panel-tools-rail-width);\n display: flex;\n flex-direction: column;\n box-sizing: border-box;\n height: 100%;\n min-height: 0;\n border-left: var(--_panel-tools-border);\n background-color: var(--_panel-tools-surface-bg);\n}\n\n.panel-tools__rail-header {\n display: flex;\n align-items: center;\n justify-content: center;\n height: var(--dimension-size-600);\n padding: var(--dimension-space-100);\n flex-shrink: 0;\n box-sizing: border-box;\n border-bottom: var(--_panel-tools-border);\n}\n\n.panel-tools__rail-body {\n flex: 1 1 auto;\n min-height: 0;\n overflow-y: auto;\n overflow-x: hidden;\n display: flex;\n flex-direction: column;\n gap: var(--dimension-space-050);\n padding: var(--dimension-space-100);\n box-sizing: border-box;\n scrollbar-width: none;\n}\n\n.panel-tools__rail-body::-webkit-scrollbar {\n display: none;\n}\n\n.panel-tools__rail-action {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 100%;\n height: var(--dimension-size-400);\n flex-shrink: 0;\n}\n","import { Component, Prop, Event, EventEmitter, Element, State, Watch, h, Host } from '@stencil/core';\nimport type { ChromeTransitionDetail } from '../../nav/chrome-transition';\nimport {\n PANEL_TOOLS_LABELS,\n PANEL_TOOLS_PRIMARY_TOOL_ID,\n type PanelToolsItem,\n type PanelToolsToolId,\n} from './panel-tools-types';\nimport { parsePanelToolsItems, panelToolsDrawerResting } from './panel-tools-utils';\n\n@Component({\n tag: 'ds-panel-tools',\n styleUrl: 'PanelTools.css',\n scoped: true,\n})\nexport class PanelTools {\n @Element() el!: HTMLElement;\n\n /** When false, only the icon rail is shown. */\n @Prop({ mutable: true, reflect: true }) open: boolean = false;\n\n /** Active tool view — `search`, `agents`, `messages`, `stacks`, or `activity`. */\n @Prop({ mutable: true, attribute: 'active-tool', reflect: true })\n activeTool: PanelToolsToolId | '' = '';\n\n /**\n * Rail items rendered in the right column.\n * Set via JS property: `el.items = [...]`. Replace the array reference to update.\n */\n @Prop() items: PanelToolsItem[] = [];\n\n /** JSON fallback for `items` — useful when framework bindings don't propagate arrays. */\n @Prop({ attribute: 'items-json' }) itemsJson: string = '';\n\n /** Emitted when a rail button is toggled. Detail = { id, selected }. */\n @Event({ bubbles: true, composed: true }) dsToolChange!: EventEmitter<{\n id: PanelToolsToolId;\n selected: boolean;\n }>;\n\n /** Bubbling lifecycle — `ds-bar-nav` defers overflow checks during drawer motion. */\n @Event({ bubbles: true, composed: true })\n dsChromeTransitionStart!: EventEmitter<ChromeTransitionDetail>;\n\n @Event({ bubbles: true, composed: true })\n dsChromeTransitionEnd!: EventEmitter<ChromeTransitionDetail>;\n\n /** Arms open vs close easing for the in-flight width transition. */\n @State() private motion: 'opening' | 'closing' | 'idle' = 'idle';\n\n /** Suppresses width transition until the host has painted its initial open state. */\n @State() private readyForMotion = false;\n\n private motionEnableGeneration = 0;\n\n private get railItems(): PanelToolsItem[] {\n return parsePanelToolsItems(this.items, this.itemsJson);\n }\n\n disconnectedCallback() {\n this.el.removeEventListener('transitionend', this.handleTransitionEnd);\n this.motionEnableGeneration += 1;\n }\n\n private deferMotionEnable() {\n if (this.readyForMotion) return;\n const generation = ++this.motionEnableGeneration;\n requestAnimationFrame(() => {\n if (generation !== this.motionEnableGeneration) return;\n requestAnimationFrame(() => {\n if (generation !== this.motionEnableGeneration) return;\n this.readyForMotion = true;\n });\n });\n }\n\n connectedCallback() {\n this.el.addEventListener('transitionend', this.handleTransitionEnd);\n }\n\n @Watch('open')\n openChanged(isOpen: boolean, wasOpen?: boolean) {\n if (this.readyForMotion && wasOpen !== undefined && wasOpen !== isOpen) {\n this.motion = isOpen ? 'opening' : 'closing';\n this.dsChromeTransitionStart.emit({ source: 'panel-tools' });\n }\n this.deferMotionEnable();\n }\n\n @Watch('activeTool')\n activeToolChanged() {\n this.deferMotionEnable();\n }\n\n componentDidLoad() {\n this.deferMotionEnable();\n }\n\n @Watch('items')\n @Watch('itemsJson')\n itemsChanged() {\n this.deferMotionEnable();\n }\n\n private handleTransitionEnd = (event: TransitionEvent) => {\n if (event.target !== this.el.querySelector('.panel-tools__drawer')) return;\n if (event.propertyName !== 'max-width') return;\n this.motion = 'idle';\n this.dsChromeTransitionEnd.emit({ source: 'panel-tools' });\n };\n\n /** Rail selection follows `open` immediately — independent of the slide animation. */\n private isRailSelected(id: PanelToolsToolId): boolean {\n return this.open && this.activeTool === id;\n }\n\n /** Drawer body stays mounted while the clip frame animates closed. */\n private isDrawerPresent(): boolean {\n return this.open || this.motion === 'closing';\n }\n\n private isViewActive(id: PanelToolsToolId): boolean {\n return this.isDrawerPresent() && this.activeTool === id;\n }\n\n private headerLabel(): string {\n if (!this.isDrawerPresent() || !this.activeTool) return '';\n return PANEL_TOOLS_LABELS[this.activeTool as PanelToolsToolId] ?? '';\n }\n\n private handleToolChange = (id: PanelToolsToolId) => {\n const selected = !this.isRailSelected(id);\n if (selected) {\n this.open = true;\n this.activeTool = id;\n } else {\n this.open = false;\n }\n this.dsToolChange.emit({ id, selected });\n };\n\n private renderRailAction(item: PanelToolsItem) {\n return (\n <ds-bar-nav-action\n key={item.id}\n class=\"panel-tools__rail-action\"\n icon={item.icon}\n selected={this.isRailSelected(item.id)}\n dot={item.dot ?? false}\n inactive={item.inactive}\n aria-label={item.ariaLabel ?? PANEL_TOOLS_LABELS[item.id]}\n onDsChange={() => this.handleToolChange(item.id)}\n />\n );\n }\n\n render() {\n const headerLabel = this.headerLabel();\n const railItems = this.railItems;\n const headerItem = railItems.find(item => item.id === PANEL_TOOLS_PRIMARY_TOOL_ID);\n const bodyItems = railItems.filter(item => item.id !== PANEL_TOOLS_PRIMARY_TOOL_ID);\n const showDrawerChrome = this.isDrawerPresent();\n const drawerResting = panelToolsDrawerResting(this.open, this.motion);\n\n return (\n <Host\n class={{\n 'panel-tools': true,\n 'panel-tools--open': this.open,\n 'panel-tools--ready': this.readyForMotion,\n 'panel-tools--motion-opening': this.motion === 'opening',\n 'panel-tools--motion-closing': this.motion === 'closing',\n 'panel-tools--drawer-resting': drawerResting,\n }}\n role=\"complementary\"\n aria-label=\"Tools\"\n >\n <div class=\"panel-tools__layout\">\n <div\n class={{\n 'panel-tools__drawer': true,\n 'panel-tools__drawer--visible': showDrawerChrome,\n }}\n aria-hidden={showDrawerChrome ? null : 'true'}\n >\n <div class=\"panel-tools__drawer-surface\">\n <header class=\"panel-tools__header\">\n <span class=\"panel-tools__title text-body-medium-emphasis\">{headerLabel}</span>\n </header>\n <div class=\"panel-tools__body\">\n <div\n class={{\n 'panel-tools__view': true,\n 'panel-tools__view--active': this.isViewActive('search'),\n 'text-body-medium': true,\n }}\n hidden={!this.isViewActive('search')}\n >\n <slot name=\"search\" />\n </div>\n <div\n class={{\n 'panel-tools__view': true,\n 'panel-tools__view--active': this.isViewActive('messages'),\n 'text-body-medium': true,\n }}\n hidden={!this.isViewActive('messages')}\n >\n <slot name=\"messages\" />\n </div>\n <div\n class={{\n 'panel-tools__view': true,\n 'panel-tools__view--active': this.isViewActive('stacks'),\n 'text-body-medium': true,\n }}\n hidden={!this.isViewActive('stacks')}\n >\n <slot name=\"stacks\" />\n </div>\n <div\n class={{\n 'panel-tools__view': true,\n 'panel-tools__view--active': this.isViewActive('activity'),\n 'text-body-medium': true,\n }}\n hidden={!this.isViewActive('activity')}\n >\n <slot name=\"activity\" />\n </div>\n <div\n class={{\n 'panel-tools__view': true,\n 'panel-tools__view--active': this.isViewActive('agents'),\n 'text-body-medium': true,\n }}\n hidden={!this.isViewActive('agents')}\n >\n <slot name=\"agents\" />\n </div>\n </div>\n </div>\n </div>\n\n <nav class=\"panel-tools__rail\" aria-label=\"Tool shortcuts\">\n {headerItem ? (\n <div class=\"panel-tools__rail-header\">{this.renderRailAction(headerItem)}</div>\n ) : null}\n <div class=\"panel-tools__rail-body\">\n {bodyItems.map(item => this.renderRailAction(item))}\n </div>\n </nav>\n </div>\n </Host>\n );\n }\n}\n"],"mappings":"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","ignoreList":[]}
|
|
@@ -0,0 +1,2 @@
|
|
|
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
|
|
@@ -0,0 +1 @@
|
|
|
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":[]}
|
|
@@ -2,8 +2,10 @@ import type { NavChromeStyle } from '../../nav/nav-chrome';
|
|
|
2
2
|
export declare class AppShell {
|
|
3
3
|
/** Chrome style propagated to slotted `ds-panel-nav` and `ds-bar-nav`. */
|
|
4
4
|
navStyle: NavChromeStyle;
|
|
5
|
-
/** When `true`, paints the
|
|
5
|
+
/** When `true`, paints the radial wash behind panel, bar, and tools (synced to shell layout). */
|
|
6
6
|
gradient: boolean;
|
|
7
|
+
/** When `true`, paints the diagonal grid overlay on the shared chrome layer. Independent of `gradient`. */
|
|
8
|
+
grid: boolean;
|
|
7
9
|
/**
|
|
8
10
|
* Optional custom gradient for `background-image` (e.g. SVG URL).
|
|
9
11
|
* When set, overrides the built-in radial wash.
|
|
@@ -11,8 +13,8 @@ export declare class AppShell {
|
|
|
11
13
|
gradientSrc: string;
|
|
12
14
|
el: HTMLElement;
|
|
13
15
|
private resizeObserver;
|
|
14
|
-
private
|
|
15
|
-
private
|
|
16
|
+
private readonly panelNavTransition;
|
|
17
|
+
private readonly chromeSyncCoalescer;
|
|
16
18
|
private panelWidthTokens;
|
|
17
19
|
private cachedShellWidth;
|
|
18
20
|
private cachedShellHeight;
|
|
@@ -32,6 +34,7 @@ export declare class AppShell {
|
|
|
32
34
|
private syncChromeLayoutVars;
|
|
33
35
|
private resolvePanelWidthPx;
|
|
34
36
|
private resolveShellDimensions;
|
|
37
|
+
private chromeLayerActive;
|
|
35
38
|
private syncChrome;
|
|
36
39
|
render(): any;
|
|
37
40
|
}
|
|
@@ -57,9 +57,9 @@ export declare class BarNav {
|
|
|
57
57
|
private visibleTabGroupEl;
|
|
58
58
|
private probeTabGroupEl;
|
|
59
59
|
private resizeObserver;
|
|
60
|
-
private overflowCheckScheduled;
|
|
61
60
|
private intrinsicWidthRetryCount;
|
|
62
|
-
private
|
|
61
|
+
private readonly panelNavTransition;
|
|
62
|
+
private readonly overflowCoalescer;
|
|
63
63
|
private chromeTransitionShell;
|
|
64
64
|
private toolsDrawerOpening;
|
|
65
65
|
/** Matches `basePath` once tabs + URL are reconciled for the active section. */
|
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
import type { PanelToolsItem } from './panel-tools-types';
|
|
2
2
|
export declare function parsePanelToolsItems(items: PanelToolsItem[], itemsJson: string): PanelToolsItem[];
|
|
3
3
|
export declare function shouldResyncPanelToolsItems(prev: PanelToolsItem[], next: PanelToolsItem[]): boolean;
|
|
4
|
+
export type PanelToolsMotion = 'opening' | 'closing' | 'idle';
|
|
5
|
+
/** True when the drawer clip is fully closed — safe to skip painting slot content. */
|
|
6
|
+
export declare function panelToolsDrawerResting(open: boolean, motion: PanelToolsMotion): boolean;
|
|
4
7
|
//# sourceMappingURL=panel-tools-utils.d.ts.map
|
|
@@ -99,7 +99,7 @@ export namespace Components {
|
|
|
99
99
|
}
|
|
100
100
|
interface DsAppShell {
|
|
101
101
|
/**
|
|
102
|
-
* When `true`, paints the
|
|
102
|
+
* When `true`, paints the radial wash behind panel, bar, and tools (synced to shell layout).
|
|
103
103
|
* @default false
|
|
104
104
|
*/
|
|
105
105
|
"gradient": boolean;
|
|
@@ -108,6 +108,11 @@ export namespace Components {
|
|
|
108
108
|
* @default ''
|
|
109
109
|
*/
|
|
110
110
|
"gradientSrc": string;
|
|
111
|
+
/**
|
|
112
|
+
* When `true`, paints the diagonal grid overlay on the shared chrome layer. Independent of `gradient`.
|
|
113
|
+
* @default false
|
|
114
|
+
*/
|
|
115
|
+
"grid": boolean;
|
|
111
116
|
/**
|
|
112
117
|
* Chrome style propagated to slotted `ds-panel-nav` and `ds-bar-nav`.
|
|
113
118
|
* @default 'dashboard'
|
|
@@ -1781,7 +1786,7 @@ declare namespace LocalJSX {
|
|
|
1781
1786
|
}
|
|
1782
1787
|
interface DsAppShell {
|
|
1783
1788
|
/**
|
|
1784
|
-
* When `true`, paints the
|
|
1789
|
+
* When `true`, paints the radial wash behind panel, bar, and tools (synced to shell layout).
|
|
1785
1790
|
* @default false
|
|
1786
1791
|
*/
|
|
1787
1792
|
"gradient"?: boolean;
|
|
@@ -1790,6 +1795,11 @@ declare namespace LocalJSX {
|
|
|
1790
1795
|
* @default ''
|
|
1791
1796
|
*/
|
|
1792
1797
|
"gradientSrc"?: string;
|
|
1798
|
+
/**
|
|
1799
|
+
* When `true`, paints the diagonal grid overlay on the shared chrome layer. Independent of `gradient`.
|
|
1800
|
+
* @default false
|
|
1801
|
+
*/
|
|
1802
|
+
"grid"?: boolean;
|
|
1793
1803
|
/**
|
|
1794
1804
|
* Chrome style propagated to slotted `ds-panel-nav` and `ds-bar-nav`.
|
|
1795
1805
|
* @default 'dashboard'
|
|
@@ -2871,6 +2881,7 @@ declare namespace LocalJSX {
|
|
|
2871
2881
|
interface DsAppShellAttributes {
|
|
2872
2882
|
"navStyle": NavChromeStyle;
|
|
2873
2883
|
"gradient": boolean;
|
|
2884
|
+
"grid": boolean;
|
|
2874
2885
|
"gradientSrc": string;
|
|
2875
2886
|
}
|
|
2876
2887
|
interface DsBadgeAttributes {
|
|
@@ -5,4 +5,18 @@ export type ChromeTransitionSource = 'panel-nav' | 'panel-tools';
|
|
|
5
5
|
export interface ChromeTransitionDetail {
|
|
6
6
|
source: ChromeTransitionSource;
|
|
7
7
|
}
|
|
8
|
+
/** Reference-counted gate — used while panel-nav width is transitioning. */
|
|
9
|
+
export declare class ChromeTransitionDepth {
|
|
10
|
+
private depth;
|
|
11
|
+
enter(): void;
|
|
12
|
+
exit(): void;
|
|
13
|
+
get isActive(): boolean;
|
|
14
|
+
}
|
|
15
|
+
export interface RafCoalescer {
|
|
16
|
+
schedule(): void;
|
|
17
|
+
cancel(): void;
|
|
18
|
+
}
|
|
19
|
+
/** Coalesce bursty layout work (ResizeObserver, prop churn) to one callback per frame. */
|
|
20
|
+
export declare function createRafCoalescer(onFrame: () => void): RafCoalescer;
|
|
21
|
+
export declare function readChromeTransitionSource(event: Event): ChromeTransitionSource | undefined;
|
|
8
22
|
//# sourceMappingURL=chrome-transition.d.ts.map
|
|
@@ -2,10 +2,10 @@ export type { NavChromeStyle } from './nav-chrome';
|
|
|
2
2
|
export { NAV_STYLE_HINT_ATTR, readNavStyleAttr, setNavStyleHint, clearNavStyleHint, resolveNavChromeStyle, shouldResyncNavChromeStyle, } from './nav-chrome';
|
|
3
3
|
export { SHELL_BAR_NAV_VT_NAME, SHELL_NAV_REVEAL_DURATION_VAR, SHELL_NAV_REVEAL_EASING_VAR, parseCssTimeMs, ensureShellNavVtStyle, resolveShellNavRevealOrigin, setShellNavRevealOriginVars, animateShellNavRadialReveal, runShellNavStyleRevealOnReady, } from './shell-view-transition';
|
|
4
4
|
export type { ShellNavRevealOrigin } from './shell-view-transition';
|
|
5
|
-
export { SHELL_GRADIENT_IMAGE_VAR, SHELL_GRADIENT_SIZE_VAR, SHELL_GRADIENT_POSITION_PANEL_VAR, SHELL_GRADIENT_POSITION_BAR_VAR, SHELL_GRADIENT_OPACITY_VAR, SHELL_CHROME_SURFACE_POSITION_VAR, SHELL_GRADIENT_OPACITY, buildShellRadialGradient, shellGradientImage, shellGradientSize, shellGradientPositionPanel, shellGradientPositionBar, shellChromeSurfacePosition, } from './shell-gradient';
|
|
5
|
+
export { SHELL_GRADIENT_IMAGE_VAR, SHELL_GRADIENT_SIZE_VAR, SHELL_GRADIENT_POSITION_PANEL_VAR, SHELL_GRADIENT_POSITION_BAR_VAR, SHELL_GRADIENT_OPACITY_VAR, SHELL_CHROME_SURFACE_POSITION_VAR, SHELL_GRADIENT_OPACITY, buildShellRadialGradient, shellGradientImage, shellGradientSize, shellGradientPositionPanel, shellGradientPositionBar, shellChromeSurfacePosition, shellChromeLayerActive, } from './shell-gradient';
|
|
6
6
|
export type { ShellGradientLayout } from './shell-gradient';
|
|
7
7
|
export type { ChromeTransitionDetail, ChromeTransitionSource } from './chrome-transition';
|
|
8
|
-
export { CHROME_TRANSITION_END, CHROME_TRANSITION_START, } from './chrome-transition';
|
|
8
|
+
export { CHROME_TRANSITION_END, CHROME_TRANSITION_START, ChromeTransitionDepth, createRafCoalescer, readChromeTransitionSource, } from './chrome-transition';
|
|
9
9
|
export { barGradientPositionFromPanelWidth, isPanelNavCollapsed, panelWidthPxFromTokens, readCssVarWidthPx, readPanelNavWidthTokens, } from './shell-chrome-metrics';
|
|
10
10
|
export type { PanelNavWidthTokens } from './shell-chrome-metrics';
|
|
11
11
|
export { BADGE_GRADIENT_POSITION_VAR, badgeGradientPosition, findGradientSurface, isShellGradientActive, parseCssBackgroundPosition, readShellGradientPosition, syncBadgeGradientPosition, } from './badge-gradient-ring';
|
|
@@ -8,6 +8,8 @@ export declare const SHELL_GRADIENT_OPACITY_VAR = "--ds-shell-gradient-opacity";
|
|
|
8
8
|
export declare const SHELL_CHROME_SURFACE_POSITION_VAR = "--ds-shell-chrome-surface-position";
|
|
9
9
|
/** Layer opacity for the nav gradient wash. */
|
|
10
10
|
export declare const SHELL_GRADIENT_OPACITY = "0.1";
|
|
11
|
+
/** Whether the shared chrome layer (bg + optional wash/grid) should mount. */
|
|
12
|
+
export declare function shellChromeLayerActive(gradient: boolean, grid: boolean): boolean;
|
|
11
13
|
/**
|
|
12
14
|
* Shell radial wash — same for panel and bar nav.
|
|
13
15
|
* Tokens follow `data-theme` (light/dark blue intent).
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ds-mo/ui",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.11.1",
|
|
4
4
|
"description": "CompoMo — composable web components styled with TokoMo design tokens",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"web-components",
|
|
@@ -69,7 +69,7 @@
|
|
|
69
69
|
"pretest": "node scripts/generate-icon-catalog.mjs",
|
|
70
70
|
"build": "stencil build && node scripts/patch-index-types.mjs && node scripts/verify-icons-externalized.mjs && node scripts/write-build-stamp.mjs",
|
|
71
71
|
"verify:pack": "node scripts/verify-npm-pack.mjs && node scripts/verify-nav-import.mjs",
|
|
72
|
-
"test": "node --import tsx/esm --test tests/panel-nav-utils.test.ts tests/panel-tools.test.ts tests/shell-view-transition.test.ts tests/shell-gradient.test.ts tests/shell-chrome-metrics.test.ts tests/bar-nav-overflow-hysteresis.test.ts tests/badge-gradient-ring.test.ts tests/scroll-edge-fade.test.ts tests/resolve-css-length-px.test.ts tests/resolve-css-time-ms.test.ts tests/overlay-positioning.test.ts tests/menu-position.test.ts tests/bar-nav-utils.test.ts tests/bar-nav-tabs-menu-utils.test.ts tests/bar-nav-dom-utils.test.ts tests/icon-catalog.test.ts",
|
|
72
|
+
"test": "node --import tsx/esm --test tests/panel-nav-utils.test.ts tests/panel-tools.test.ts tests/shell-view-transition.test.ts tests/shell-gradient.test.ts tests/shell-chrome-metrics.test.ts tests/shell-chrome-layer.test.ts tests/chrome-transition.test.ts tests/bar-nav-overflow-hysteresis.test.ts tests/badge-gradient-ring.test.ts tests/scroll-edge-fade.test.ts tests/resolve-css-length-px.test.ts tests/resolve-css-time-ms.test.ts tests/overlay-positioning.test.ts tests/menu-position.test.ts tests/bar-nav-utils.test.ts tests/bar-nav-tabs-menu-utils.test.ts tests/bar-nav-dom-utils.test.ts tests/icon-catalog.test.ts",
|
|
73
73
|
"test:e2e": "npm run build && playwright test",
|
|
74
74
|
"test:e2e:install": "playwright install chromium",
|
|
75
75
|
"build:docs": "stencil build --docs",
|
package/src/angular/proxies.ts
CHANGED
|
@@ -36,14 +36,14 @@ export declare interface DsAccordion extends Components.DsAccordion {
|
|
|
36
36
|
|
|
37
37
|
|
|
38
38
|
@ProxyCmp({
|
|
39
|
-
inputs: ['gradient', 'gradientSrc', 'navStyle']
|
|
39
|
+
inputs: ['gradient', 'gradientSrc', 'grid', 'navStyle']
|
|
40
40
|
})
|
|
41
41
|
@Component({
|
|
42
42
|
selector: 'ds-app-shell',
|
|
43
43
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
44
44
|
template: '<ng-content></ng-content>',
|
|
45
45
|
// eslint-disable-next-line @angular-eslint/no-inputs-metadata-property
|
|
46
|
-
inputs: ['gradient', 'gradientSrc', 'navStyle'],
|
|
46
|
+
inputs: ['gradient', 'gradientSrc', 'grid', 'navStyle'],
|
|
47
47
|
standalone: false
|
|
48
48
|
})
|
|
49
49
|
export class DsAppShell {
|
|
@@ -3,7 +3,9 @@ import type { NavChromeStyle } from '../../nav/nav-chrome';
|
|
|
3
3
|
import {
|
|
4
4
|
CHROME_TRANSITION_END,
|
|
5
5
|
CHROME_TRANSITION_START,
|
|
6
|
-
|
|
6
|
+
ChromeTransitionDepth,
|
|
7
|
+
createRafCoalescer,
|
|
8
|
+
readChromeTransitionSource,
|
|
7
9
|
} from '../../nav/chrome-transition';
|
|
8
10
|
import {
|
|
9
11
|
isPanelNavCollapsed,
|
|
@@ -33,9 +35,12 @@ export class AppShell {
|
|
|
33
35
|
/** Chrome style propagated to slotted `ds-panel-nav` and `ds-bar-nav`. */
|
|
34
36
|
@Prop({ attribute: 'nav-style', reflect: true }) navStyle: NavChromeStyle = 'dashboard';
|
|
35
37
|
|
|
36
|
-
/** When `true`, paints the
|
|
38
|
+
/** When `true`, paints the radial wash behind panel, bar, and tools (synced to shell layout). */
|
|
37
39
|
@Prop({ reflect: true }) gradient: boolean = false;
|
|
38
40
|
|
|
41
|
+
/** When `true`, paints the diagonal grid overlay on the shared chrome layer. Independent of `gradient`. */
|
|
42
|
+
@Prop({ reflect: true }) grid: boolean = false;
|
|
43
|
+
|
|
39
44
|
/**
|
|
40
45
|
* Optional custom gradient for `background-image` (e.g. SVG URL).
|
|
41
46
|
* When set, overrides the built-in radial wash.
|
|
@@ -45,8 +50,8 @@ export class AppShell {
|
|
|
45
50
|
@Element() el!: HTMLElement;
|
|
46
51
|
|
|
47
52
|
private resizeObserver: ResizeObserver | null = null;
|
|
48
|
-
private
|
|
49
|
-
private
|
|
53
|
+
private readonly panelNavTransition = new ChromeTransitionDepth();
|
|
54
|
+
private readonly chromeSyncCoalescer = createRafCoalescer(() => this.syncChrome());
|
|
50
55
|
private panelWidthTokens: PanelNavWidthTokens = { expandedPx: 0, collapsedPx: 0 };
|
|
51
56
|
private cachedShellWidth = 0;
|
|
52
57
|
private cachedShellHeight = 0;
|
|
@@ -65,16 +70,14 @@ export class AppShell {
|
|
|
65
70
|
disconnectedCallback() {
|
|
66
71
|
this.resizeObserver?.disconnect();
|
|
67
72
|
this.resizeObserver = null;
|
|
68
|
-
|
|
69
|
-
cancelAnimationFrame(this.chromeSyncRafId);
|
|
70
|
-
this.chromeSyncRafId = 0;
|
|
71
|
-
}
|
|
73
|
+
this.chromeSyncCoalescer.cancel();
|
|
72
74
|
this.el.removeEventListener(CHROME_TRANSITION_START, this.onChromeTransitionStart);
|
|
73
75
|
this.el.removeEventListener(CHROME_TRANSITION_END, this.onChromeTransitionEnd);
|
|
74
76
|
}
|
|
75
77
|
|
|
76
78
|
@Watch('navStyle')
|
|
77
79
|
@Watch('gradient')
|
|
80
|
+
@Watch('grid')
|
|
78
81
|
@Watch('gradientSrc')
|
|
79
82
|
onShellPropsChange() {
|
|
80
83
|
this.syncSlottedNavStyle();
|
|
@@ -82,32 +85,26 @@ export class AppShell {
|
|
|
82
85
|
}
|
|
83
86
|
|
|
84
87
|
private onChromeTransitionStart = (event: Event) => {
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
}
|
|
88
|
+
if (readChromeTransitionSource(event) !== 'panel-nav') return;
|
|
89
|
+
|
|
90
|
+
this.panelNavTransition.enter();
|
|
91
|
+
this.cachedShellWidth = this.el.clientWidth;
|
|
92
|
+
this.cachedShellHeight = this.el.clientHeight;
|
|
93
|
+
this.syncChrome();
|
|
92
94
|
};
|
|
93
95
|
|
|
94
96
|
private onChromeTransitionEnd = (event: Event) => {
|
|
95
|
-
|
|
96
|
-
if (source !== 'panel-nav') return;
|
|
97
|
+
if (readChromeTransitionSource(event) !== 'panel-nav') return;
|
|
97
98
|
|
|
98
|
-
this.
|
|
99
|
-
if (this.
|
|
99
|
+
this.panelNavTransition.exit();
|
|
100
|
+
if (!this.panelNavTransition.isActive) {
|
|
100
101
|
this.scheduleChromeSync();
|
|
101
102
|
}
|
|
102
103
|
};
|
|
103
104
|
|
|
104
105
|
/** Coalesce ResizeObserver bursts to one layout read per frame. */
|
|
105
106
|
private scheduleChromeSync() {
|
|
106
|
-
|
|
107
|
-
this.chromeSyncRafId = requestAnimationFrame(() => {
|
|
108
|
-
this.chromeSyncRafId = 0;
|
|
109
|
-
this.syncChrome();
|
|
110
|
-
});
|
|
107
|
+
this.chromeSyncCoalescer.schedule();
|
|
111
108
|
}
|
|
112
109
|
|
|
113
110
|
private syncSlottedNavStyle() {
|
|
@@ -125,7 +122,7 @@ export class AppShell {
|
|
|
125
122
|
|
|
126
123
|
private connectMetricsObserver() {
|
|
127
124
|
this.resizeObserver = new ResizeObserver(() => {
|
|
128
|
-
if (this.
|
|
125
|
+
if (this.panelNavTransition.isActive) return;
|
|
129
126
|
this.scheduleChromeSync();
|
|
130
127
|
});
|
|
131
128
|
this.resizeObserver.observe(this.el);
|
|
@@ -176,7 +173,7 @@ export class AppShell {
|
|
|
176
173
|
private resolvePanelWidthPx(panelNav: HTMLElement | null): number {
|
|
177
174
|
const navRoot = panelNav?.querySelector('.panel-nav') as HTMLElement | null;
|
|
178
175
|
|
|
179
|
-
if (this.
|
|
176
|
+
if (this.panelNavTransition.isActive && this.panelWidthTokens.expandedPx > 0) {
|
|
180
177
|
const collapsed = isPanelNavCollapsed(panelNav, navRoot);
|
|
181
178
|
return panelWidthPxFromTokens(this.panelWidthTokens, collapsed);
|
|
182
179
|
}
|
|
@@ -194,7 +191,7 @@ export class AppShell {
|
|
|
194
191
|
}
|
|
195
192
|
|
|
196
193
|
private resolveShellDimensions(): { width: number; height: number } {
|
|
197
|
-
if (this.
|
|
194
|
+
if (this.panelNavTransition.isActive && this.cachedShellWidth > 0) {
|
|
198
195
|
return {
|
|
199
196
|
width: this.cachedShellWidth,
|
|
200
197
|
height: this.cachedShellHeight || this.el.clientHeight,
|
|
@@ -205,11 +202,32 @@ export class AppShell {
|
|
|
205
202
|
return { width: rect.width, height: rect.height };
|
|
206
203
|
}
|
|
207
204
|
|
|
205
|
+
private chromeLayerActive(): boolean {
|
|
206
|
+
return this.gradient || this.grid;
|
|
207
|
+
}
|
|
208
|
+
|
|
208
209
|
private syncChrome() {
|
|
209
210
|
const panelNav = this.el.querySelector('ds-panel-nav') as HTMLElement | null;
|
|
210
211
|
const bar = this.el.querySelector('ds-bar-nav') as HTMLElement | null;
|
|
211
212
|
const targets = [this.el, panelNav, bar].filter((el): el is HTMLElement => el !== null);
|
|
212
213
|
|
|
214
|
+
const clearLayoutVars = () => {
|
|
215
|
+
if (panelNav) panelNav.style.removeProperty(SHELL_GRADIENT_POSITION_PANEL_VAR);
|
|
216
|
+
if (bar) bar.style.removeProperty(SHELL_GRADIENT_POSITION_BAR_VAR);
|
|
217
|
+
};
|
|
218
|
+
|
|
219
|
+
if (!this.chromeLayerActive()) {
|
|
220
|
+
this.clearGradientPaintVars(targets);
|
|
221
|
+
clearLayoutVars();
|
|
222
|
+
return;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
if (!this.gradient) {
|
|
226
|
+
this.clearGradientPaintVars(targets);
|
|
227
|
+
clearLayoutVars();
|
|
228
|
+
return;
|
|
229
|
+
}
|
|
230
|
+
|
|
213
231
|
const { width: shellWidth, height: shellHeight } = this.resolveShellDimensions();
|
|
214
232
|
const panelWidth = this.resolvePanelWidthPx(panelNav);
|
|
215
233
|
|
|
@@ -218,11 +236,6 @@ export class AppShell {
|
|
|
218
236
|
|
|
219
237
|
this.syncChromeLayoutVars(panelNav, bar, panelPosition, barPosition);
|
|
220
238
|
|
|
221
|
-
if (!this.gradient) {
|
|
222
|
-
this.clearGradientPaintVars(targets);
|
|
223
|
-
return;
|
|
224
|
-
}
|
|
225
|
-
|
|
226
239
|
const image = this.gradientSrc.trim()
|
|
227
240
|
? `url(${this.gradientSrc.trim()})`
|
|
228
241
|
: buildShellRadialGradient();
|
|
@@ -258,16 +271,18 @@ export class AppShell {
|
|
|
258
271
|
}
|
|
259
272
|
|
|
260
273
|
render() {
|
|
274
|
+
const chromeActive = this.chromeLayerActive();
|
|
261
275
|
const shellCls: Record<string, boolean> = {
|
|
262
276
|
'app-shell': true,
|
|
263
277
|
'app-shell--gradient': this.gradient,
|
|
278
|
+
'app-shell--grid': this.grid,
|
|
264
279
|
[`app-shell--${this.navStyle}`]: true,
|
|
265
280
|
};
|
|
266
281
|
|
|
267
282
|
return (
|
|
268
283
|
<Host class={shellCls}>
|
|
269
284
|
<div class="app-shell__row">
|
|
270
|
-
{
|
|
285
|
+
{chromeActive ? <div class="app-shell__chrome" aria-hidden="true" /> : null}
|
|
271
286
|
<div class="app-shell__panel">
|
|
272
287
|
<slot name="panel" />
|
|
273
288
|
</div>
|
|
@@ -31,7 +31,9 @@ import {
|
|
|
31
31
|
import {
|
|
32
32
|
CHROME_TRANSITION_END,
|
|
33
33
|
CHROME_TRANSITION_START,
|
|
34
|
-
|
|
34
|
+
ChromeTransitionDepth,
|
|
35
|
+
createRafCoalescer,
|
|
36
|
+
readChromeTransitionSource,
|
|
35
37
|
} from '../../nav/chrome-transition';
|
|
36
38
|
import { readCssVarWidthPx } from '../../nav/shell-chrome-metrics';
|
|
37
39
|
|
|
@@ -107,9 +109,12 @@ export class BarNav {
|
|
|
107
109
|
private visibleTabGroupEl: QueryableHost | null = null;
|
|
108
110
|
private probeTabGroupEl: QueryableHost | null = null;
|
|
109
111
|
private resizeObserver: ResizeObserver | null = null;
|
|
110
|
-
private overflowCheckScheduled = false;
|
|
111
112
|
private intrinsicWidthRetryCount = 0;
|
|
112
|
-
private
|
|
113
|
+
private readonly panelNavTransition = new ChromeTransitionDepth();
|
|
114
|
+
private readonly overflowCoalescer = createRafCoalescer(() => {
|
|
115
|
+
this.updateTabsCollapsed();
|
|
116
|
+
this.updateTriggerLabelTruncation();
|
|
117
|
+
});
|
|
113
118
|
private chromeTransitionShell: HTMLElement | null = null;
|
|
114
119
|
private toolsDrawerOpening = false;
|
|
115
120
|
/** Matches `basePath` once tabs + URL are reconciled for the active section. */
|
|
@@ -189,7 +194,7 @@ export class BarNav {
|
|
|
189
194
|
}
|
|
190
195
|
|
|
191
196
|
componentDidRender() {
|
|
192
|
-
if (this.
|
|
197
|
+
if (this.panelNavTransition.isActive) {
|
|
193
198
|
this.syncMenuAnchor();
|
|
194
199
|
return;
|
|
195
200
|
}
|
|
@@ -203,6 +208,7 @@ export class BarNav {
|
|
|
203
208
|
disconnectedCallback() {
|
|
204
209
|
this.resizeObserver?.disconnect();
|
|
205
210
|
this.resizeObserver = null;
|
|
211
|
+
this.overflowCoalescer.cancel();
|
|
206
212
|
this.syncTriggerLabelObserver(true);
|
|
207
213
|
this.unbindChromeTransitionListeners();
|
|
208
214
|
}
|
|
@@ -229,9 +235,9 @@ export class BarNav {
|
|
|
229
235
|
}
|
|
230
236
|
|
|
231
237
|
private onChromeTransitionStart = (event: Event) => {
|
|
232
|
-
const source = (event
|
|
238
|
+
const source = readChromeTransitionSource(event);
|
|
233
239
|
if (source === 'panel-nav') {
|
|
234
|
-
this.
|
|
240
|
+
this.panelNavTransition.enter();
|
|
235
241
|
return;
|
|
236
242
|
}
|
|
237
243
|
if (source === 'panel-tools') {
|
|
@@ -246,10 +252,10 @@ export class BarNav {
|
|
|
246
252
|
};
|
|
247
253
|
|
|
248
254
|
private onChromeTransitionEnd = (event: Event) => {
|
|
249
|
-
const source = (event
|
|
255
|
+
const source = readChromeTransitionSource(event);
|
|
250
256
|
if (source === 'panel-nav') {
|
|
251
|
-
this.
|
|
252
|
-
if (this.
|
|
257
|
+
this.panelNavTransition.exit();
|
|
258
|
+
if (!this.panelNavTransition.isActive) {
|
|
253
259
|
this.scheduleOverflowCheck();
|
|
254
260
|
}
|
|
255
261
|
return;
|
|
@@ -295,22 +301,15 @@ export class BarNav {
|
|
|
295
301
|
if (typeof ResizeObserver === 'undefined' || !this.headerEl) return;
|
|
296
302
|
this.resizeObserver?.disconnect();
|
|
297
303
|
this.resizeObserver = new ResizeObserver(() => {
|
|
298
|
-
if (this.
|
|
304
|
+
if (this.panelNavTransition.isActive) return;
|
|
299
305
|
this.scheduleOverflowCheck();
|
|
300
306
|
});
|
|
301
307
|
this.resizeObserver.observe(this.headerEl);
|
|
302
308
|
}
|
|
303
309
|
|
|
304
310
|
private scheduleOverflowCheck() {
|
|
305
|
-
if (this.
|
|
306
|
-
|
|
307
|
-
this.overflowCheckScheduled = true;
|
|
308
|
-
requestAnimationFrame(() => {
|
|
309
|
-
this.overflowCheckScheduled = false;
|
|
310
|
-
if (this.chromeTransitionDepth > 0) return;
|
|
311
|
-
this.updateTabsCollapsed();
|
|
312
|
-
this.updateTriggerLabelTruncation();
|
|
313
|
-
});
|
|
311
|
+
if (this.panelNavTransition.isActive) return;
|
|
312
|
+
this.overflowCoalescer.schedule();
|
|
314
313
|
}
|
|
315
314
|
|
|
316
315
|
private syncTriggerLabelObserver(disconnectOnly = false) {
|
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
type PanelToolsItem,
|
|
7
7
|
type PanelToolsToolId,
|
|
8
8
|
} from './panel-tools-types';
|
|
9
|
-
import { parsePanelToolsItems } from './panel-tools-utils';
|
|
9
|
+
import { parsePanelToolsItems, panelToolsDrawerResting } from './panel-tools-utils';
|
|
10
10
|
|
|
11
11
|
@Component({
|
|
12
12
|
tag: 'ds-panel-tools',
|
|
@@ -160,6 +160,7 @@ export class PanelTools {
|
|
|
160
160
|
const headerItem = railItems.find(item => item.id === PANEL_TOOLS_PRIMARY_TOOL_ID);
|
|
161
161
|
const bodyItems = railItems.filter(item => item.id !== PANEL_TOOLS_PRIMARY_TOOL_ID);
|
|
162
162
|
const showDrawerChrome = this.isDrawerPresent();
|
|
163
|
+
const drawerResting = panelToolsDrawerResting(this.open, this.motion);
|
|
163
164
|
|
|
164
165
|
return (
|
|
165
166
|
<Host
|
|
@@ -169,6 +170,7 @@ export class PanelTools {
|
|
|
169
170
|
'panel-tools--ready': this.readyForMotion,
|
|
170
171
|
'panel-tools--motion-opening': this.motion === 'opening',
|
|
171
172
|
'panel-tools--motion-closing': this.motion === 'closing',
|
|
173
|
+
'panel-tools--drawer-resting': drawerResting,
|
|
172
174
|
}}
|
|
173
175
|
role="complementary"
|
|
174
176
|
aria-label="Tools"
|
|
@@ -29,3 +29,10 @@ export function shouldResyncPanelToolsItems(
|
|
|
29
29
|
);
|
|
30
30
|
});
|
|
31
31
|
}
|
|
32
|
+
|
|
33
|
+
export type PanelToolsMotion = 'opening' | 'closing' | 'idle';
|
|
34
|
+
|
|
35
|
+
/** True when the drawer clip is fully closed — safe to skip painting slot content. */
|
|
36
|
+
export function panelToolsDrawerResting(open: boolean, motion: PanelToolsMotion): boolean {
|
|
37
|
+
return !open && motion === 'idle';
|
|
38
|
+
}
|