@ds-mo/ui 2.13.0 → 2.14.0

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.
Files changed (39) hide show
  1. package/dist/.build-stamp +1 -1
  2. package/dist/components/ds-bar-nav.js +1 -1
  3. package/dist/components/ds-chart-donut.js +1 -1
  4. package/dist/components/ds-menu.js +1 -1
  5. package/dist/components/ds-panel-nav.js +1 -1
  6. package/dist/components/ds-panel-nav.js.map +1 -1
  7. package/dist/components/ds-panel-tools.js +1 -1
  8. package/dist/components/ds-panel-tools.js.map +1 -1
  9. package/dist/components/ds-select.js +1 -1
  10. package/dist/components/ds-shell-gradient-picker.js +1 -1
  11. package/dist/components/ds-shell-gradient-swatch.js +1 -1
  12. package/dist/components/ds-slider.js +1 -1
  13. package/dist/components/ds-toggle.js +1 -1
  14. package/dist/components/ds-tooltip-data-viz.js +1 -1
  15. package/dist/components/ds-tooltip.js +1 -1
  16. package/dist/components/{p-CQmf2CeW.js → p-BAeKw2pj.js} +2 -2
  17. package/dist/components/p-CDHRJqea.js +2 -0
  18. package/dist/components/p-CDHRJqea.js.map +1 -0
  19. package/dist/components/{p-Be3PHv34.js → p-CYmVDfd_.js} +2 -2
  20. package/dist/components/{p-CwARVsZQ.js → p-Dlol7hPg.js} +2 -2
  21. package/dist/components/{p-CwARVsZQ.js.map → p-Dlol7hPg.js.map} +1 -1
  22. package/dist/components/{p-3FLHei19.js → p-XNHSe8Mg.js} +2 -2
  23. package/dist/types/components/PanelTools/PanelTools.d.ts +1 -1
  24. package/dist/types/components/PanelTools/index.d.ts +1 -1
  25. package/dist/types/components/PanelTools/panel-tools-types.d.ts +3 -1
  26. package/dist/types/components/Tooltip/Tooltip.d.ts +4 -0
  27. package/dist/types/components.d.ts +8 -2
  28. package/package.json +1 -1
  29. package/src/wc/components/PanelNav/PanelNav.tsx +10 -9
  30. package/src/wc/components/PanelTools/PanelTools.tsx +33 -4
  31. package/src/wc/components/PanelTools/index.ts +1 -0
  32. package/src/wc/components/PanelTools/panel-tools-types.ts +12 -1
  33. package/src/wc/components/Tooltip/Tooltip.tsx +12 -0
  34. package/src/wc/components.d.ts +8 -2
  35. package/dist/components/p-DohXlNCJ.js +0 -2
  36. package/dist/components/p-DohXlNCJ.js.map +0 -1
  37. /package/dist/components/{p-CQmf2CeW.js.map → p-BAeKw2pj.js.map} +0 -0
  38. /package/dist/components/{p-Be3PHv34.js.map → p-CYmVDfd_.js.map} +0 -0
  39. /package/dist/components/{p-3FLHei19.js.map → p-XNHSe8Mg.js.map} +0 -0
@@ -1 +1 @@
1
- {"version":3,"names":["computeMenuPosition","input","anchorRect","a","popupWidth","pw","popupHeight","ph","side","align","sideOffsetPx","alignOffsetPx","viewportPadPx","vpPad","viewportWidth","viewportHeight","x","y","top","left","right","width","bottom","height","Math","min","max","isMenuGradientPickerSection","section","variant","menuCss","POSITION_RETRY_BUDGET","Menu","__stencil_proxyCustomElement","HTMLElement","constructor","registerHost","this","open","items","sections","sideOffset","TOKEN_CSS_LENGTHS","space050","alignOffset","initialFocusVisible","shouldRender","closing","pos","focusedIndex","positionReady","focusRingVisible","clickOutsideHandler","scrollResizeHandler","closeTimer","itemEls","positionRetryRaf","listenersReady","componentDidLoad","onOpenChange","disconnectedCallback","cancelPositionRetry","teardownListeners","isOpen","setupListeners","schedulePositionUpdate","focusInitialItem","setTimeout","closeAnimationMs","onAnchorChange","onPositionPropsChange","resolveCssLengthPx","TOKEN_DEFAULTS","popupFallbackWidthPx","menuWidthXs","popupFallbackHeightPx","menuFallbackHeight","resolveCssTimeMs","motionShort2","animationDurationShort3","resolvedAnchor","anchor","anchorId","document","getElementById","activeSections","length","flatItems","flatMap","cancelAnimationFrame","onReady","remaining","attempt","calculatePosition","requestAnimationFrame","anchorEl","popup","el","querySelector","getBoundingClientRect","offsetWidth","offsetHeight","window","innerWidth","innerHeight","flat","selectedIdx","findIndex","it","isSelected","isInactive","firstEnabledIdx","btns","querySelectorAll","focus","e","t","target","contains","close","addEventListener","removeEventListener","clearTimeout","dsClose","emit","handleKeyDown","enabled","map","i","filter","cur","indexOf","safe","key","preventDefault","stopPropagation","focusItem","idx","handleItemClick","item","dsSelect","handleGradientSelect","preset","dsGradientSelect","render","h","Host","style","display","flatIdx","popupStyle","position","transform","round","zIndex","visibility","menuWidth","minWidth","class","role","si","header","undefined","value","onDsChange","detail","isFocused","type","isDestructive","disabled","tabIndex","onMouseDown","onClick","onFocus","label","subtext","showToggle","toggleValue"],"sources":["src/wc/components/Menu/menu-position.ts","src/wc/components/Menu/menu-types.ts","src/wc/components/Menu/Menu.css?tag=ds-menu&encapsulation=scoped","src/wc/components/Menu/Menu.tsx"],"sourcesContent":["export type MenuSide = 'top' | 'right' | 'bottom' | 'left';\nexport type MenuAlign = 'start' | 'center' | 'end';\n\nexport interface MenuPositionInput {\n anchorRect: Pick<DOMRectReadOnly, 'top' | 'left' | 'right' | 'bottom' | 'width' | 'height'>;\n popupWidth: number;\n popupHeight: number;\n side: MenuSide;\n align: MenuAlign;\n sideOffsetPx: number;\n alignOffsetPx: number;\n viewportPadPx: number;\n viewportWidth: number;\n viewportHeight: number;\n}\n\n/** Pure layout math for ds-menu — anchor rect + placement props → viewport-fixed x/y. */\nexport function computeMenuPosition(input: MenuPositionInput): { x: number; y: number } {\n const {\n anchorRect: a,\n popupWidth: pw,\n popupHeight: ph,\n side,\n align,\n sideOffsetPx,\n alignOffsetPx,\n viewportPadPx: vpPad,\n viewportWidth,\n viewportHeight,\n } = input;\n\n let x = 0;\n let y = 0;\n\n switch (side) {\n case 'top':\n y = a.top - ph - sideOffsetPx;\n x =\n align === 'start'\n ? a.left + alignOffsetPx\n : align === 'end'\n ? a.right - pw + alignOffsetPx\n : a.left + a.width / 2 - pw / 2 + alignOffsetPx;\n break;\n case 'bottom':\n y = a.bottom + sideOffsetPx;\n x =\n align === 'start'\n ? a.left + alignOffsetPx\n : align === 'end'\n ? a.right - pw + alignOffsetPx\n : a.left + a.width / 2 - pw / 2 + alignOffsetPx;\n break;\n case 'left':\n x = a.left - pw - sideOffsetPx;\n y =\n align === 'start'\n ? a.top + alignOffsetPx\n : align === 'end'\n ? a.bottom - ph + alignOffsetPx\n : a.top + a.height / 2 - ph / 2 + alignOffsetPx;\n break;\n case 'right':\n x = a.right + sideOffsetPx;\n y =\n align === 'start'\n ? a.top + alignOffsetPx\n : align === 'end'\n ? a.bottom - ph + alignOffsetPx\n : a.top + a.height / 2 - ph / 2 + alignOffsetPx;\n break;\n }\n\n return {\n x: Math.min(Math.max(x, vpPad), viewportWidth - pw - vpPad),\n y: Math.min(Math.max(y, vpPad), viewportHeight - ph - vpPad),\n };\n}\n","import type { ShellGradientPreset } from '../../nav/shell-gradient-presets';\n\nexport type { MenuAlign, MenuSide } from './menu-position';\nexport {\n PANEL_NAV_USER_MENU_PLACEMENT,\n type MenuPlacement,\n} from './menu-placement';\n\nexport interface MenuItemData {\n label: string;\n value?: string;\n subtext?: string;\n isSelected?: boolean;\n isInactive?: boolean;\n isDestructive?: boolean;\n showToggle?: boolean;\n toggleValue?: boolean;\n}\n\nexport interface MenuItemsSection {\n header?: string;\n items: MenuItemData[];\n}\n\nexport interface MenuGradientPickerSection {\n header?: string;\n variant: 'gradient-picker';\n value: ShellGradientPreset;\n}\n\nexport type MenuSection = MenuItemsSection | MenuGradientPickerSection;\n\nexport function isMenuGradientPickerSection(\n section: MenuSection,\n): section is MenuGradientPickerSection {\n return 'variant' in section && section.variant === 'gradient-picker';\n}\n\nexport type { ShellGradientPreset };\n","@import '../../utils/focus-ring.css';\n@import '../../utils/interaction-fill.css';\n@import '../../utils/control-inactive.css';\n@import '../../utils/control-density.css';\n\n/* ── Popup shell ─────────────────────────────────────────────────────────── */\n\n.menu-popup {\n box-sizing: border-box;\n display: flex;\n flex-direction: column;\n min-width: var(--dimension-menu-width-xs);\n max-width: var(--dimension-panel-width-lg);\n overflow: hidden;\n background-color: var(--color-background-primary);\n border-radius: var(--dimension-radius-075);\n /* Split elevation: shadow on shell, inset highlight on ::after (combined token clips with overflow:hidden). */\n box-shadow: var(--effect-shadow-elevated-floating);\n animation: menuFadeIn var(--effect-motion-short-2) forwards;\n backface-visibility: hidden;\n will-change: transform;\n}\n\n.menu-popup::after {\n content: '';\n position: absolute;\n inset: 0;\n border-radius: inherit;\n pointer-events: none;\n box-shadow: var(--effect-highlight-elevated-floating);\n}\n\n.menu-popup--closing {\n animation: menuFadeOut var(--effect-motion-short-2) forwards;\n}\n\n@keyframes menuFadeIn {\n from { opacity: 0; transform: inherit scale(0.97); }\n to { opacity: 1; }\n}\n\n@keyframes menuFadeOut {\n from { opacity: 1; }\n to { opacity: 0; }\n}\n\n/* ── Sections ────────────────────────────────────────────────────────────── */\n\n.menu-section {\n display: flex;\n flex-direction: column;\n gap: var(--dimension-space-050);\n padding: var(--dimension-space-050);\n}\n\n.menu-section--divided {\n border-bottom: var(--dimension-stroke-width-015) solid var(--color-border-tertiary);\n}\n\n.section-header {\n display: flex;\n align-items: center;\n height: var(--ds-control-height, var(--dimension-size-400));\n padding: 0 var(--ds-control-padding-inline, var(--dimension-space-075));\n -webkit-user-select: none;\n user-select: none;\n}\n\n.section-label {\n display: inline-flex;\n align-items: center;\n padding: 0 var(--ds-control-label-inset, var(--dimension-space-025));\n color: var(--color-foreground-primary);\n}\n\n/* ── Items (md control-density + interaction-fill + inset focus) ─────────── */\n\n.menu-item {\n display: flex;\n align-items: center;\n gap: var(--ds-control-gap, var(--dimension-space-050));\n width: 100%;\n height: var(--ds-control-height, var(--dimension-size-400));\n padding: 0 var(--ds-control-padding-inline, var(--dimension-space-075));\n box-sizing: border-box;\n border: none;\n border-radius: var(--ds-control-radius, var(--dimension-radius-025));\n background: transparent;\n cursor: pointer;\n -webkit-user-select: none;\n user-select: none;\n text-align: left;\n color: inherit;\n /* Hover/press/selected: `.ds-interaction-fill` (+ `--selected`). */\n}\n\n/* Subtext rows grow past the density height. */\n.menu-item:has(.menu-item__subtext) {\n height: auto;\n min-height: var(--ds-control-height, var(--dimension-size-400));\n padding-block: var(--ds-control-padding-inline, var(--dimension-space-075));\n align-items: flex-start;\n}\n\n/* Inactive: `.ds-control-inactive` (25% opacity). */\n\n.menu-item--destructive .menu-item__label {\n color: var(--color-foreground-bold-negative) !important;\n}\n\n.menu-item__content {\n display: flex;\n flex: 1;\n flex-direction: column;\n gap: var(--dimension-space-025);\n min-width: 0;\n}\n\n.menu-item__label {\n padding: 0 var(--ds-control-label-inset, var(--dimension-space-025));\n color: var(--color-foreground-secondary);\n}\n\n.menu-item:has(.menu-item__subtext) .menu-item__label,\n.menu-item--selected .menu-item__label {\n color: var(--color-foreground-primary);\n}\n\n.menu-item__subtext {\n padding: 0 var(--ds-control-label-inset, var(--dimension-space-025));\n color: var(--color-foreground-secondary);\n line-height: 1.4;\n}\n\n/* ── Toggle suffix ───────────────────────────────────────────────────────── */\n\n.menu-item__toggle {\n position: relative;\n flex-shrink: 0;\n width: calc(var(--dimension-size-400) + var(--dimension-size-050));\n height: var(--dimension-size-250);\n border-radius: var(--dimension-radius-125);\n background-color: var(--color-background-translucent);\n transition: background-color var(--effect-motion-short-2);\n}\n\n.menu-item__toggle--on {\n background-color: var(--color-background-medium-brand);\n}\n\n.menu-item__toggle-thumb {\n position: absolute;\n top: var(--dimension-space-025);\n left: var(--dimension-space-025);\n width: var(--dimension-size-200);\n height: var(--dimension-size-200);\n border-radius: 50%;\n background-color: var(--color-foreground-on-bold-background-primary);\n transition: transform var(--effect-motion-short-2);\n}\n\n.menu-item__toggle--on .menu-item__toggle-thumb {\n transform: translateX(var(--dimension-offset-200, 16px));\n}\n","import { Component, Prop, State, Event, EventEmitter, Element, Watch, Listen, h, Host } from '@stencil/core';\nimport { resolveCssLengthPx, resolveCssTimeMs, TOKEN_CSS_LENGTHS, TOKEN_DEFAULTS } from '../../utils';\nimport { computeMenuPosition, type MenuAlign, type MenuSide } from './menu-position';\nimport type { MenuItemData, MenuSection } from './menu-types';\nimport { isMenuGradientPickerSection } from './menu-types';\nimport type { ShellGradientPreset } from '../../nav/shell-gradient-presets';\n\n/** rAF retries while the popup mounts or the anchor resolves. */\nconst POSITION_RETRY_BUDGET = 8;\n\n@Component({\n tag: 'ds-menu',\n styleUrl: 'Menu.css',\n scoped: true,\n})\nexport class Menu {\n @Element() el!: HTMLElement;\n\n @Prop({ mutable: true }) open: boolean = false;\n @Prop() items: MenuItemData[] = [];\n @Prop() sections: MenuSection[] = [];\n @Prop() side: MenuSide = 'bottom';\n @Prop() align: MenuAlign = 'start';\n /** Gap between anchor and menu — number (px) or TokoMo length (`var(--dimension-space-050)`, etc.). */\n @Prop() sideOffset: number | string = TOKEN_CSS_LENGTHS.space050;\n /** Cross-axis offset — number (px) or TokoMo length. */\n @Prop() alignOffset: number | string = 0;\n @Prop() menuWidth: string | undefined;\n @Prop() minWidth: string | undefined;\n /** External trigger element to position against. Set via JS: menuEl.anchor = buttonEl */\n @Prop() anchor: HTMLElement | undefined;\n /** ID of the external trigger element for positioning */\n @Prop() anchorId: string | undefined;\n /** Show a visible ring on the initially focused menu item. Use only when the opener was keyboard-driven. */\n @Prop() initialFocusVisible: boolean = false;\n\n @State() private shouldRender: boolean = false;\n @State() private closing: boolean = false;\n @State() private pos: { x: number; y: number } = { x: 0, y: 0 };\n @State() private focusedIndex: number = 0;\n @State() private positionReady: boolean = false;\n @State() private focusRingVisible: boolean = false;\n\n @Event() dsClose!: EventEmitter<void>;\n @Event() dsSelect!: EventEmitter<MenuItemData>;\n /** Emitted when a `gradient-picker` section swatch is chosen. */\n @Event() dsGradientSelect!: EventEmitter<ShellGradientPreset>;\n\n private clickOutsideHandler: ((e: MouseEvent) => void) | null = null;\n private scrollResizeHandler: (() => void) | null = null;\n private closeTimer: ReturnType<typeof setTimeout> | null = null;\n private itemEls: HTMLElement[] = [];\n private positionRetryRaf: number | null = null;\n private listenersReady = false;\n\n componentDidLoad() {\n if (this.open) this.onOpenChange(true);\n }\n\n disconnectedCallback() {\n this.cancelPositionRetry();\n this.teardownListeners();\n }\n\n @Watch('open')\n onOpenChange(isOpen: boolean) {\n if (isOpen) {\n this.teardownListeners();\n this.shouldRender = true;\n this.closing = false;\n this.positionReady = false;\n this.listenersReady = false;\n this.focusRingVisible = this.initialFocusVisible;\n this.listenersReady = true;\n this.setupListeners();\n this.schedulePositionUpdate(() => {\n this.focusInitialItem();\n });\n } else if (this.shouldRender) {\n this.cancelPositionRetry();\n this.closing = true;\n this.teardownListeners();\n this.listenersReady = false;\n this.closeTimer = setTimeout(() => {\n this.shouldRender = false;\n this.closing = false;\n this.closeTimer = null;\n }, this.closeAnimationMs);\n }\n }\n\n @Watch('anchor')\n @Watch('anchorId')\n onAnchorChange() {\n if (this.open) this.schedulePositionUpdate();\n }\n\n @Watch('side')\n @Watch('align')\n @Watch('sideOffset')\n @Watch('alignOffset')\n onPositionPropsChange() {\n if (this.open) this.schedulePositionUpdate();\n }\n\n private get viewportPadPx(): number {\n return resolveCssLengthPx(TOKEN_DEFAULTS.space050, TOKEN_DEFAULTS.space050);\n }\n\n private get sideOffsetPx(): number {\n return resolveCssLengthPx(this.sideOffset, TOKEN_DEFAULTS.space050);\n }\n\n private get alignOffsetPx(): number {\n return resolveCssLengthPx(this.alignOffset, 0);\n }\n\n private get popupFallbackWidthPx(): number {\n return resolveCssLengthPx(TOKEN_DEFAULTS.menuWidthXs, TOKEN_DEFAULTS.menuWidthXs);\n }\n\n private get popupFallbackHeightPx(): number {\n return resolveCssLengthPx(TOKEN_DEFAULTS.menuFallbackHeight, TOKEN_DEFAULTS.menuFallbackHeight);\n }\n\n private get closeAnimationMs(): number {\n return resolveCssTimeMs(TOKEN_DEFAULTS.motionShort2, TOKEN_DEFAULTS.animationDurationShort3);\n }\n\n private get resolvedAnchor(): HTMLElement | null {\n if (this.anchor) return this.anchor;\n if (this.anchorId) return document.getElementById(this.anchorId);\n return null;\n }\n\n private get activeSections(): MenuSection[] {\n if (this.sections.length > 0) return this.sections;\n if (this.items.length > 0) return [{ items: this.items }];\n return [];\n }\n\n private get flatItems(): MenuItemData[] {\n return this.activeSections.flatMap(section =>\n isMenuGradientPickerSection(section) ? [] : section.items,\n );\n }\n\n private cancelPositionRetry() {\n if (this.positionRetryRaf !== null) {\n cancelAnimationFrame(this.positionRetryRaf);\n this.positionRetryRaf = null;\n }\n }\n\n /** Retry until anchor + popup exist — do not reveal at 0,0 on a failed first pass. */\n private schedulePositionUpdate(onReady?: () => void) {\n if (!this.open) return;\n\n this.cancelPositionRetry();\n this.positionReady = false;\n\n let remaining = POSITION_RETRY_BUDGET;\n\n const attempt = () => {\n this.positionRetryRaf = null;\n if (!this.open) return;\n\n if (this.calculatePosition()) {\n this.positionReady = true;\n onReady?.();\n return;\n }\n\n if (remaining > 0) {\n remaining -= 1;\n this.positionRetryRaf = requestAnimationFrame(attempt);\n }\n };\n\n this.positionRetryRaf = requestAnimationFrame(attempt);\n }\n\n /** @returns `true` when anchor and popup were found and `pos` was updated. */\n private calculatePosition(): boolean {\n const anchorEl = this.resolvedAnchor;\n if (!anchorEl) return false;\n const popup = this.el.querySelector('.menu-popup') as HTMLElement | null;\n if (!popup) return false;\n\n this.pos = computeMenuPosition({\n anchorRect: anchorEl.getBoundingClientRect(),\n popupWidth: popup.offsetWidth || this.popupFallbackWidthPx,\n popupHeight: popup.offsetHeight || this.popupFallbackHeightPx,\n side: this.side,\n align: this.align,\n sideOffsetPx: this.sideOffsetPx,\n alignOffsetPx: this.alignOffsetPx,\n viewportPadPx: this.viewportPadPx,\n viewportWidth: window.innerWidth,\n viewportHeight: window.innerHeight,\n });\n return true;\n }\n\n /** Focus the selected item when present, otherwise the first enabled item. */\n private focusInitialItem() {\n const flat = this.flatItems;\n const selectedIdx = flat.findIndex(it => it.isSelected && !it.isInactive);\n const firstEnabledIdx = flat.findIndex(it => !it.isInactive);\n this.focusedIndex = selectedIdx >= 0\n ? selectedIdx\n : (firstEnabledIdx >= 0 ? firstEnabledIdx : 0);\n\n requestAnimationFrame(() => {\n const btns = this.el.querySelectorAll<HTMLElement>('.menu-item');\n btns[this.focusedIndex]?.focus();\n });\n }\n\n private setupListeners() {\n this.clickOutsideHandler = (e: MouseEvent) => {\n const t = e.target as Node;\n const popup = this.el.querySelector('.menu-popup');\n const anchorEl = this.resolvedAnchor;\n if (popup?.contains(t) || anchorEl?.contains(t)) return;\n this.close();\n };\n\n this.scrollResizeHandler = () => {\n if (this.shouldRender && !this.closing) this.calculatePosition();\n };\n\n document.addEventListener('mousedown', this.clickOutsideHandler, true);\n window.addEventListener('scroll', this.scrollResizeHandler, true);\n window.addEventListener('resize', this.scrollResizeHandler);\n }\n\n private teardownListeners() {\n if (this.clickOutsideHandler) {\n document.removeEventListener('mousedown', this.clickOutsideHandler, true);\n this.clickOutsideHandler = null;\n }\n if (this.scrollResizeHandler) {\n window.removeEventListener('scroll', this.scrollResizeHandler, true);\n window.removeEventListener('resize', this.scrollResizeHandler);\n this.scrollResizeHandler = null;\n }\n if (this.closeTimer) {\n clearTimeout(this.closeTimer);\n this.closeTimer = null;\n }\n }\n\n private close() {\n this.resolvedAnchor?.focus();\n this.dsClose.emit();\n this.open = false;\n this.onOpenChange(false);\n }\n\n @Listen('keydown')\n handleKeyDown(e: KeyboardEvent) {\n if (!this.shouldRender || this.closing) return;\n const flat = this.flatItems;\n const enabled = flat.map((it, i) => ({ it, i })).filter(({ it }) => !it.isInactive).map(({ i }) => i);\n if (!enabled.length) return;\n\n const cur = enabled.indexOf(this.focusedIndex);\n const safe = cur < 0 ? 0 : cur;\n\n switch (e.key) {\n case 'ArrowDown':\n e.preventDefault(); e.stopPropagation();\n this.focusRingVisible = true;\n this.focusedIndex = enabled[(safe + 1) % enabled.length];\n this.focusItem(this.focusedIndex);\n break;\n case 'ArrowUp':\n e.preventDefault(); e.stopPropagation();\n this.focusRingVisible = true;\n this.focusedIndex = enabled[(safe - 1 + enabled.length) % enabled.length];\n this.focusItem(this.focusedIndex);\n break;\n case 'Home':\n e.preventDefault(); e.stopPropagation();\n this.focusRingVisible = true;\n this.focusedIndex = enabled[0];\n this.focusItem(this.focusedIndex);\n break;\n case 'End':\n e.preventDefault(); e.stopPropagation();\n this.focusRingVisible = true;\n this.focusedIndex = enabled[enabled.length - 1];\n this.focusItem(this.focusedIndex);\n break;\n case 'Escape':\n e.preventDefault();\n this.close();\n break;\n case 'Tab':\n e.preventDefault();\n this.close();\n break;\n }\n }\n\n private focusItem(idx: number) {\n const btns = this.el.querySelectorAll<HTMLElement>('.menu-item');\n btns[idx]?.focus();\n }\n\n private handleItemClick(item: MenuItemData) {\n if (item.isInactive) return;\n this.dsSelect.emit(item);\n }\n\n private handleGradientSelect(preset: ShellGradientPreset) {\n this.dsGradientSelect.emit(preset);\n }\n\n render() {\n if (!this.shouldRender) return <Host style={{ display: 'contents' }} />;\n\n const sections = this.activeSections;\n let flatIdx = 0;\n\n const popupStyle: Record<string, string> = {\n position: 'fixed',\n left: '0',\n top: '0',\n transform: `translate(${Math.round(this.pos.x)}px, ${Math.round(this.pos.y)}px)`,\n zIndex: '9998',\n visibility: this.positionReady ? 'visible' : 'hidden',\n };\n\n if (this.menuWidth) popupStyle['width'] = this.menuWidth;\n if (this.minWidth) popupStyle['min-width'] = this.minWidth;\n\n return (\n <Host style={{ display: 'contents' }}>\n <div\n class={{ 'menu-popup': true, 'menu-popup--closing': this.closing }}\n style={popupStyle}\n role=\"menu\"\n aria-label=\"Menu\"\n aria-orientation=\"vertical\"\n >\n {sections.map((section, si) => (\n <div\n key={si}\n class={{\n 'menu-section': true,\n 'menu-section--divided': si < sections.length - 1,\n 'menu-section--gradient-picker': isMenuGradientPickerSection(section),\n }}\n role={section.header ? 'group' : undefined}\n aria-label={section.header}\n >\n {section.header && (\n <div class=\"section-header ds-control--md\" aria-hidden=\"true\">\n <span class=\"text-body-small-emphasis section-label\">{section.header}</span>\n </div>\n )}\n {isMenuGradientPickerSection(section) ? (\n <ds-shell-gradient-picker\n value={section.value}\n onDsChange={(e: CustomEvent<ShellGradientPreset>) => {\n e.stopPropagation();\n this.handleGradientSelect(e.detail);\n }}\n />\n ) : (\n section.items.map(item => {\n const idx = flatIdx++;\n const isFocused = this.focusedIndex === idx;\n return (\n <button\n key={idx}\n type=\"button\"\n class={{\n 'menu-item': true,\n 'ds-control--md': true,\n 'ds-focus-ring-inset': true,\n 'ds-focus-ring--visible': isFocused && this.focusRingVisible,\n 'ds-interaction-fill': !item.isInactive,\n 'ds-interaction-fill--selected': !!item.isSelected && !item.isInactive,\n 'menu-item--selected': !!item.isSelected,\n 'ds-control-inactive': !!item.isInactive,\n 'menu-item--destructive': !!item.isDestructive,\n 'menu-item--focused': isFocused,\n }}\n role=\"menuitem\"\n aria-current={item.isSelected ? 'true' : undefined}\n disabled={item.isInactive}\n tabIndex={isFocused ? 0 : -1}\n onMouseDown={() => { this.focusRingVisible = false; }}\n onClick={() => this.handleItemClick(item)}\n onFocus={() => { this.focusedIndex = idx; }}\n >\n <div class=\"menu-item__content ds-interaction-fill__content\">\n <span class={item.isSelected ? 'text-body-medium-emphasis menu-item__label' : 'text-body-medium menu-item__label'}>\n {item.label}\n </span>\n {item.subtext && (\n <span class=\"text-body-small menu-item__subtext\">{item.subtext}</span>\n )}\n </div>\n {item.showToggle && (\n <div class={{ 'menu-item__toggle': true, 'menu-item__toggle--on': !!item.toggleValue, 'ds-interaction-fill__content': true }} aria-hidden=\"true\">\n <div class=\"menu-item__toggle-thumb\" />\n </div>\n )}\n </button>\n );\n }))}\n </div>\n ))}\n </div>\n </Host>\n );\n }\n}\n"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"names":["computeMenuPosition","input","anchorRect","a","popupWidth","pw","popupHeight","ph","side","align","sideOffsetPx","alignOffsetPx","viewportPadPx","vpPad","viewportWidth","viewportHeight","x","y","top","left","right","width","bottom","height","Math","min","max","isMenuGradientPickerSection","section","variant","menuCss","POSITION_RETRY_BUDGET","Menu","__stencil_proxyCustomElement","HTMLElement","constructor","registerHost","this","open","items","sections","sideOffset","TOKEN_CSS_LENGTHS","space050","alignOffset","initialFocusVisible","shouldRender","closing","pos","focusedIndex","positionReady","focusRingVisible","clickOutsideHandler","scrollResizeHandler","closeTimer","itemEls","positionRetryRaf","listenersReady","componentDidLoad","onOpenChange","disconnectedCallback","cancelPositionRetry","teardownListeners","isOpen","setupListeners","schedulePositionUpdate","focusInitialItem","setTimeout","closeAnimationMs","onAnchorChange","onPositionPropsChange","resolveCssLengthPx","TOKEN_DEFAULTS","popupFallbackWidthPx","menuWidthXs","popupFallbackHeightPx","menuFallbackHeight","resolveCssTimeMs","motionShort2","animationDurationShort3","resolvedAnchor","anchor","anchorId","document","getElementById","activeSections","length","flatItems","flatMap","cancelAnimationFrame","onReady","remaining","attempt","calculatePosition","requestAnimationFrame","anchorEl","popup","el","querySelector","getBoundingClientRect","offsetWidth","offsetHeight","window","innerWidth","innerHeight","flat","selectedIdx","findIndex","it","isSelected","isInactive","firstEnabledIdx","btns","querySelectorAll","focus","e","t","target","contains","close","addEventListener","removeEventListener","clearTimeout","dsClose","emit","handleKeyDown","enabled","map","i","filter","cur","indexOf","safe","key","preventDefault","stopPropagation","focusItem","idx","handleItemClick","item","dsSelect","handleGradientSelect","preset","dsGradientSelect","render","h","Host","style","display","flatIdx","popupStyle","position","transform","round","zIndex","visibility","menuWidth","minWidth","class","role","si","header","undefined","value","onDsChange","detail","isFocused","type","isDestructive","disabled","tabIndex","onMouseDown","onClick","onFocus","label","subtext","showToggle","toggleValue"],"sources":["src/wc/components/Menu/menu-position.ts","src/wc/components/Menu/menu-types.ts","src/wc/components/Menu/Menu.css?tag=ds-menu&encapsulation=scoped","src/wc/components/Menu/Menu.tsx"],"sourcesContent":["export type MenuSide = 'top' | 'right' | 'bottom' | 'left';\nexport type MenuAlign = 'start' | 'center' | 'end';\n\nexport interface MenuPositionInput {\n anchorRect: Pick<DOMRectReadOnly, 'top' | 'left' | 'right' | 'bottom' | 'width' | 'height'>;\n popupWidth: number;\n popupHeight: number;\n side: MenuSide;\n align: MenuAlign;\n sideOffsetPx: number;\n alignOffsetPx: number;\n viewportPadPx: number;\n viewportWidth: number;\n viewportHeight: number;\n}\n\n/** Pure layout math for ds-menu — anchor rect + placement props → viewport-fixed x/y. */\nexport function computeMenuPosition(input: MenuPositionInput): { x: number; y: number } {\n const {\n anchorRect: a,\n popupWidth: pw,\n popupHeight: ph,\n side,\n align,\n sideOffsetPx,\n alignOffsetPx,\n viewportPadPx: vpPad,\n viewportWidth,\n viewportHeight,\n } = input;\n\n let x = 0;\n let y = 0;\n\n switch (side) {\n case 'top':\n y = a.top - ph - sideOffsetPx;\n x =\n align === 'start'\n ? a.left + alignOffsetPx\n : align === 'end'\n ? a.right - pw + alignOffsetPx\n : a.left + a.width / 2 - pw / 2 + alignOffsetPx;\n break;\n case 'bottom':\n y = a.bottom + sideOffsetPx;\n x =\n align === 'start'\n ? a.left + alignOffsetPx\n : align === 'end'\n ? a.right - pw + alignOffsetPx\n : a.left + a.width / 2 - pw / 2 + alignOffsetPx;\n break;\n case 'left':\n x = a.left - pw - sideOffsetPx;\n y =\n align === 'start'\n ? a.top + alignOffsetPx\n : align === 'end'\n ? a.bottom - ph + alignOffsetPx\n : a.top + a.height / 2 - ph / 2 + alignOffsetPx;\n break;\n case 'right':\n x = a.right + sideOffsetPx;\n y =\n align === 'start'\n ? a.top + alignOffsetPx\n : align === 'end'\n ? a.bottom - ph + alignOffsetPx\n : a.top + a.height / 2 - ph / 2 + alignOffsetPx;\n break;\n }\n\n return {\n x: Math.min(Math.max(x, vpPad), viewportWidth - pw - vpPad),\n y: Math.min(Math.max(y, vpPad), viewportHeight - ph - vpPad),\n };\n}\n","import type { ShellGradientPreset } from '../../nav/shell-gradient-presets';\n\nexport type { MenuAlign, MenuSide } from './menu-position';\nexport {\n PANEL_NAV_USER_MENU_PLACEMENT,\n type MenuPlacement,\n} from './menu-placement';\n\nexport interface MenuItemData {\n label: string;\n value?: string;\n subtext?: string;\n isSelected?: boolean;\n isInactive?: boolean;\n isDestructive?: boolean;\n showToggle?: boolean;\n toggleValue?: boolean;\n}\n\nexport interface MenuItemsSection {\n header?: string;\n items: MenuItemData[];\n}\n\nexport interface MenuGradientPickerSection {\n header?: string;\n variant: 'gradient-picker';\n value: ShellGradientPreset;\n}\n\nexport type MenuSection = MenuItemsSection | MenuGradientPickerSection;\n\nexport function isMenuGradientPickerSection(\n section: MenuSection,\n): section is MenuGradientPickerSection {\n return 'variant' in section && section.variant === 'gradient-picker';\n}\n\nexport type { ShellGradientPreset };\n","@import '../../utils/focus-ring.css';\n@import '../../utils/interaction-fill.css';\n@import '../../utils/control-inactive.css';\n@import '../../utils/control-density.css';\n\n/* ── Popup shell ─────────────────────────────────────────────────────────── */\n\n.menu-popup {\n box-sizing: border-box;\n display: flex;\n flex-direction: column;\n min-width: var(--dimension-menu-width-xs);\n max-width: var(--dimension-panel-width-lg);\n overflow: hidden;\n background-color: var(--color-background-primary);\n border-radius: var(--dimension-radius-075);\n /* Split elevation: shadow on shell, inset highlight on ::after (combined token clips with overflow:hidden). */\n box-shadow: var(--effect-shadow-elevated-floating);\n animation: menuFadeIn var(--effect-motion-short-2) forwards;\n backface-visibility: hidden;\n will-change: transform;\n}\n\n.menu-popup::after {\n content: '';\n position: absolute;\n inset: 0;\n border-radius: inherit;\n pointer-events: none;\n box-shadow: var(--effect-highlight-elevated-floating);\n}\n\n.menu-popup--closing {\n animation: menuFadeOut var(--effect-motion-short-2) forwards;\n}\n\n@keyframes menuFadeIn {\n from { opacity: 0; transform: inherit scale(0.97); }\n to { opacity: 1; }\n}\n\n@keyframes menuFadeOut {\n from { opacity: 1; }\n to { opacity: 0; }\n}\n\n/* ── Sections ────────────────────────────────────────────────────────────── */\n\n.menu-section {\n display: flex;\n flex-direction: column;\n gap: var(--dimension-space-050);\n padding: var(--dimension-space-050);\n}\n\n.menu-section--divided {\n border-bottom: var(--dimension-stroke-width-015) solid var(--color-border-tertiary);\n}\n\n.section-header {\n display: flex;\n align-items: center;\n height: var(--ds-control-height, var(--dimension-size-400));\n padding: 0 var(--ds-control-padding-inline, var(--dimension-space-075));\n -webkit-user-select: none;\n user-select: none;\n}\n\n.section-label {\n display: inline-flex;\n align-items: center;\n padding: 0 var(--ds-control-label-inset, var(--dimension-space-025));\n color: var(--color-foreground-primary);\n}\n\n/* ── Items (md control-density + interaction-fill + inset focus) ─────────── */\n\n.menu-item {\n display: flex;\n align-items: center;\n gap: var(--ds-control-gap, var(--dimension-space-050));\n width: 100%;\n height: var(--ds-control-height, var(--dimension-size-400));\n padding: 0 var(--ds-control-padding-inline, var(--dimension-space-075));\n box-sizing: border-box;\n border: none;\n border-radius: var(--ds-control-radius, var(--dimension-radius-025));\n background: transparent;\n cursor: pointer;\n -webkit-user-select: none;\n user-select: none;\n text-align: left;\n color: inherit;\n /* Hover/press/selected: `.ds-interaction-fill` (+ `--selected`). */\n}\n\n/* Subtext rows grow past the density height. */\n.menu-item:has(.menu-item__subtext) {\n height: auto;\n min-height: var(--ds-control-height, var(--dimension-size-400));\n padding-block: var(--ds-control-padding-inline, var(--dimension-space-075));\n align-items: flex-start;\n}\n\n/* Inactive: `.ds-control-inactive` (25% opacity). */\n\n.menu-item--destructive .menu-item__label {\n color: var(--color-foreground-bold-negative) !important;\n}\n\n.menu-item__content {\n display: flex;\n flex: 1;\n flex-direction: column;\n gap: var(--dimension-space-025);\n min-width: 0;\n}\n\n.menu-item__label {\n padding: 0 var(--ds-control-label-inset, var(--dimension-space-025));\n color: var(--color-foreground-secondary);\n}\n\n.menu-item:has(.menu-item__subtext) .menu-item__label,\n.menu-item--selected .menu-item__label {\n color: var(--color-foreground-primary);\n}\n\n.menu-item__subtext {\n padding: 0 var(--ds-control-label-inset, var(--dimension-space-025));\n color: var(--color-foreground-secondary);\n line-height: 1.4;\n}\n\n/* ── Toggle suffix ───────────────────────────────────────────────────────── */\n\n.menu-item__toggle {\n position: relative;\n flex-shrink: 0;\n width: calc(var(--dimension-size-400) + var(--dimension-size-050));\n height: var(--dimension-size-250);\n border-radius: var(--dimension-radius-125);\n background-color: var(--color-background-translucent);\n transition: background-color var(--effect-motion-short-2);\n}\n\n.menu-item__toggle--on {\n background-color: var(--color-background-medium-brand);\n}\n\n.menu-item__toggle-thumb {\n position: absolute;\n top: var(--dimension-space-025);\n left: var(--dimension-space-025);\n width: var(--dimension-size-200);\n height: var(--dimension-size-200);\n border-radius: 50%;\n background-color: var(--color-foreground-on-bold-background-primary);\n transition: transform var(--effect-motion-short-2);\n}\n\n.menu-item__toggle--on .menu-item__toggle-thumb {\n transform: translateX(var(--dimension-offset-200, 16px));\n}\n","import { Component, Prop, State, Event, EventEmitter, Element, Watch, Listen, h, Host } from '@stencil/core';\nimport { resolveCssLengthPx, resolveCssTimeMs, TOKEN_CSS_LENGTHS, TOKEN_DEFAULTS } from '../../utils';\nimport { computeMenuPosition, type MenuAlign, type MenuSide } from './menu-position';\nimport type { MenuItemData, MenuSection } from './menu-types';\nimport { isMenuGradientPickerSection } from './menu-types';\nimport type { ShellGradientPreset } from '../../nav/shell-gradient-presets';\n\n/** rAF retries while the popup mounts or the anchor resolves. */\nconst POSITION_RETRY_BUDGET = 8;\n\n@Component({\n tag: 'ds-menu',\n styleUrl: 'Menu.css',\n scoped: true,\n})\nexport class Menu {\n @Element() el!: HTMLElement;\n\n @Prop({ mutable: true }) open: boolean = false;\n @Prop() items: MenuItemData[] = [];\n @Prop() sections: MenuSection[] = [];\n @Prop() side: MenuSide = 'bottom';\n @Prop() align: MenuAlign = 'start';\n /** Gap between anchor and menu — number (px) or TokoMo length (`var(--dimension-space-050)`, etc.). */\n @Prop() sideOffset: number | string = TOKEN_CSS_LENGTHS.space050;\n /** Cross-axis offset — number (px) or TokoMo length. */\n @Prop() alignOffset: number | string = 0;\n @Prop() menuWidth: string | undefined;\n @Prop() minWidth: string | undefined;\n /** External trigger element to position against. Set via JS: menuEl.anchor = buttonEl */\n @Prop() anchor: HTMLElement | undefined;\n /** ID of the external trigger element for positioning */\n @Prop() anchorId: string | undefined;\n /** Show a visible ring on the initially focused menu item. Use only when the opener was keyboard-driven. */\n @Prop() initialFocusVisible: boolean = false;\n\n @State() private shouldRender: boolean = false;\n @State() private closing: boolean = false;\n @State() private pos: { x: number; y: number } = { x: 0, y: 0 };\n @State() private focusedIndex: number = 0;\n @State() private positionReady: boolean = false;\n @State() private focusRingVisible: boolean = false;\n\n @Event() dsClose!: EventEmitter<void>;\n @Event() dsSelect!: EventEmitter<MenuItemData>;\n /** Emitted when a `gradient-picker` section swatch is chosen. */\n @Event() dsGradientSelect!: EventEmitter<ShellGradientPreset>;\n\n private clickOutsideHandler: ((e: MouseEvent) => void) | null = null;\n private scrollResizeHandler: (() => void) | null = null;\n private closeTimer: ReturnType<typeof setTimeout> | null = null;\n private itemEls: HTMLElement[] = [];\n private positionRetryRaf: number | null = null;\n private listenersReady = false;\n\n componentDidLoad() {\n if (this.open) this.onOpenChange(true);\n }\n\n disconnectedCallback() {\n this.cancelPositionRetry();\n this.teardownListeners();\n }\n\n @Watch('open')\n onOpenChange(isOpen: boolean) {\n if (isOpen) {\n this.teardownListeners();\n this.shouldRender = true;\n this.closing = false;\n this.positionReady = false;\n this.listenersReady = false;\n this.focusRingVisible = this.initialFocusVisible;\n this.listenersReady = true;\n this.setupListeners();\n this.schedulePositionUpdate(() => {\n this.focusInitialItem();\n });\n } else if (this.shouldRender) {\n this.cancelPositionRetry();\n this.closing = true;\n this.teardownListeners();\n this.listenersReady = false;\n this.closeTimer = setTimeout(() => {\n this.shouldRender = false;\n this.closing = false;\n this.closeTimer = null;\n }, this.closeAnimationMs);\n }\n }\n\n @Watch('anchor')\n @Watch('anchorId')\n onAnchorChange() {\n if (this.open) this.schedulePositionUpdate();\n }\n\n @Watch('side')\n @Watch('align')\n @Watch('sideOffset')\n @Watch('alignOffset')\n onPositionPropsChange() {\n if (this.open) this.schedulePositionUpdate();\n }\n\n private get viewportPadPx(): number {\n return resolveCssLengthPx(TOKEN_DEFAULTS.space050, TOKEN_DEFAULTS.space050);\n }\n\n private get sideOffsetPx(): number {\n return resolveCssLengthPx(this.sideOffset, TOKEN_DEFAULTS.space050);\n }\n\n private get alignOffsetPx(): number {\n return resolveCssLengthPx(this.alignOffset, 0);\n }\n\n private get popupFallbackWidthPx(): number {\n return resolveCssLengthPx(TOKEN_DEFAULTS.menuWidthXs, TOKEN_DEFAULTS.menuWidthXs);\n }\n\n private get popupFallbackHeightPx(): number {\n return resolveCssLengthPx(TOKEN_DEFAULTS.menuFallbackHeight, TOKEN_DEFAULTS.menuFallbackHeight);\n }\n\n private get closeAnimationMs(): number {\n return resolveCssTimeMs(TOKEN_DEFAULTS.motionShort2, TOKEN_DEFAULTS.animationDurationShort3);\n }\n\n private get resolvedAnchor(): HTMLElement | null {\n if (this.anchor) return this.anchor;\n if (this.anchorId) return document.getElementById(this.anchorId);\n return null;\n }\n\n private get activeSections(): MenuSection[] {\n if (this.sections.length > 0) return this.sections;\n if (this.items.length > 0) return [{ items: this.items }];\n return [];\n }\n\n private get flatItems(): MenuItemData[] {\n return this.activeSections.flatMap(section =>\n isMenuGradientPickerSection(section) ? [] : section.items,\n );\n }\n\n private cancelPositionRetry() {\n if (this.positionRetryRaf !== null) {\n cancelAnimationFrame(this.positionRetryRaf);\n this.positionRetryRaf = null;\n }\n }\n\n /** Retry until anchor + popup exist — do not reveal at 0,0 on a failed first pass. */\n private schedulePositionUpdate(onReady?: () => void) {\n if (!this.open) return;\n\n this.cancelPositionRetry();\n this.positionReady = false;\n\n let remaining = POSITION_RETRY_BUDGET;\n\n const attempt = () => {\n this.positionRetryRaf = null;\n if (!this.open) return;\n\n if (this.calculatePosition()) {\n this.positionReady = true;\n onReady?.();\n return;\n }\n\n if (remaining > 0) {\n remaining -= 1;\n this.positionRetryRaf = requestAnimationFrame(attempt);\n }\n };\n\n this.positionRetryRaf = requestAnimationFrame(attempt);\n }\n\n /** @returns `true` when anchor and popup were found and `pos` was updated. */\n private calculatePosition(): boolean {\n const anchorEl = this.resolvedAnchor;\n if (!anchorEl) return false;\n const popup = this.el.querySelector('.menu-popup') as HTMLElement | null;\n if (!popup) return false;\n\n this.pos = computeMenuPosition({\n anchorRect: anchorEl.getBoundingClientRect(),\n popupWidth: popup.offsetWidth || this.popupFallbackWidthPx,\n popupHeight: popup.offsetHeight || this.popupFallbackHeightPx,\n side: this.side,\n align: this.align,\n sideOffsetPx: this.sideOffsetPx,\n alignOffsetPx: this.alignOffsetPx,\n viewportPadPx: this.viewportPadPx,\n viewportWidth: window.innerWidth,\n viewportHeight: window.innerHeight,\n });\n return true;\n }\n\n /** Focus the selected item when present, otherwise the first enabled item. */\n private focusInitialItem() {\n const flat = this.flatItems;\n const selectedIdx = flat.findIndex(it => it.isSelected && !it.isInactive);\n const firstEnabledIdx = flat.findIndex(it => !it.isInactive);\n this.focusedIndex = selectedIdx >= 0\n ? selectedIdx\n : (firstEnabledIdx >= 0 ? firstEnabledIdx : 0);\n\n requestAnimationFrame(() => {\n const btns = this.el.querySelectorAll<HTMLElement>('.menu-item');\n btns[this.focusedIndex]?.focus();\n });\n }\n\n private setupListeners() {\n this.clickOutsideHandler = (e: MouseEvent) => {\n const t = e.target as Node;\n const popup = this.el.querySelector('.menu-popup');\n const anchorEl = this.resolvedAnchor;\n if (popup?.contains(t) || anchorEl?.contains(t)) return;\n this.close();\n };\n\n this.scrollResizeHandler = () => {\n if (this.shouldRender && !this.closing) this.calculatePosition();\n };\n\n document.addEventListener('mousedown', this.clickOutsideHandler, true);\n window.addEventListener('scroll', this.scrollResizeHandler, true);\n window.addEventListener('resize', this.scrollResizeHandler);\n }\n\n private teardownListeners() {\n if (this.clickOutsideHandler) {\n document.removeEventListener('mousedown', this.clickOutsideHandler, true);\n this.clickOutsideHandler = null;\n }\n if (this.scrollResizeHandler) {\n window.removeEventListener('scroll', this.scrollResizeHandler, true);\n window.removeEventListener('resize', this.scrollResizeHandler);\n this.scrollResizeHandler = null;\n }\n if (this.closeTimer) {\n clearTimeout(this.closeTimer);\n this.closeTimer = null;\n }\n }\n\n private close() {\n this.resolvedAnchor?.focus();\n this.dsClose.emit();\n this.open = false;\n this.onOpenChange(false);\n }\n\n @Listen('keydown')\n handleKeyDown(e: KeyboardEvent) {\n if (!this.shouldRender || this.closing) return;\n const flat = this.flatItems;\n const enabled = flat.map((it, i) => ({ it, i })).filter(({ it }) => !it.isInactive).map(({ i }) => i);\n if (!enabled.length) return;\n\n const cur = enabled.indexOf(this.focusedIndex);\n const safe = cur < 0 ? 0 : cur;\n\n switch (e.key) {\n case 'ArrowDown':\n e.preventDefault(); e.stopPropagation();\n this.focusRingVisible = true;\n this.focusedIndex = enabled[(safe + 1) % enabled.length];\n this.focusItem(this.focusedIndex);\n break;\n case 'ArrowUp':\n e.preventDefault(); e.stopPropagation();\n this.focusRingVisible = true;\n this.focusedIndex = enabled[(safe - 1 + enabled.length) % enabled.length];\n this.focusItem(this.focusedIndex);\n break;\n case 'Home':\n e.preventDefault(); e.stopPropagation();\n this.focusRingVisible = true;\n this.focusedIndex = enabled[0];\n this.focusItem(this.focusedIndex);\n break;\n case 'End':\n e.preventDefault(); e.stopPropagation();\n this.focusRingVisible = true;\n this.focusedIndex = enabled[enabled.length - 1];\n this.focusItem(this.focusedIndex);\n break;\n case 'Escape':\n e.preventDefault();\n this.close();\n break;\n case 'Tab':\n e.preventDefault();\n this.close();\n break;\n }\n }\n\n private focusItem(idx: number) {\n const btns = this.el.querySelectorAll<HTMLElement>('.menu-item');\n btns[idx]?.focus();\n }\n\n private handleItemClick(item: MenuItemData) {\n if (item.isInactive) return;\n this.dsSelect.emit(item);\n }\n\n private handleGradientSelect(preset: ShellGradientPreset) {\n this.dsGradientSelect.emit(preset);\n }\n\n render() {\n if (!this.shouldRender) return <Host style={{ display: 'contents' }} />;\n\n const sections = this.activeSections;\n let flatIdx = 0;\n\n const popupStyle: Record<string, string> = {\n position: 'fixed',\n left: '0',\n top: '0',\n transform: `translate(${Math.round(this.pos.x)}px, ${Math.round(this.pos.y)}px)`,\n zIndex: '9998',\n visibility: this.positionReady ? 'visible' : 'hidden',\n };\n\n if (this.menuWidth) popupStyle['width'] = this.menuWidth;\n if (this.minWidth) popupStyle['min-width'] = this.minWidth;\n\n return (\n <Host style={{ display: 'contents' }}>\n <div\n class={{ 'menu-popup': true, 'menu-popup--closing': this.closing }}\n style={popupStyle}\n role=\"menu\"\n aria-label=\"Menu\"\n aria-orientation=\"vertical\"\n >\n {sections.map((section, si) => (\n <div\n key={si}\n class={{\n 'menu-section': true,\n 'menu-section--divided': si < sections.length - 1,\n 'menu-section--gradient-picker': isMenuGradientPickerSection(section),\n }}\n role={section.header ? 'group' : undefined}\n aria-label={section.header}\n >\n {section.header && (\n <div class=\"section-header ds-control--md\" aria-hidden=\"true\">\n <span class=\"text-body-small-emphasis section-label\">{section.header}</span>\n </div>\n )}\n {isMenuGradientPickerSection(section) ? (\n <ds-shell-gradient-picker\n value={section.value}\n onDsChange={(e: CustomEvent<ShellGradientPreset>) => {\n e.stopPropagation();\n this.handleGradientSelect(e.detail);\n }}\n />\n ) : (\n section.items.map(item => {\n const idx = flatIdx++;\n const isFocused = this.focusedIndex === idx;\n return (\n <button\n key={idx}\n type=\"button\"\n class={{\n 'menu-item': true,\n 'ds-control--md': true,\n 'ds-focus-ring-inset': true,\n 'ds-focus-ring--visible': isFocused && this.focusRingVisible,\n 'ds-interaction-fill': !item.isInactive,\n 'ds-interaction-fill--selected': !!item.isSelected && !item.isInactive,\n 'menu-item--selected': !!item.isSelected,\n 'ds-control-inactive': !!item.isInactive,\n 'menu-item--destructive': !!item.isDestructive,\n 'menu-item--focused': isFocused,\n }}\n role=\"menuitem\"\n aria-current={item.isSelected ? 'true' : undefined}\n disabled={item.isInactive}\n tabIndex={isFocused ? 0 : -1}\n onMouseDown={() => { this.focusRingVisible = false; }}\n onClick={() => this.handleItemClick(item)}\n onFocus={() => { this.focusedIndex = idx; }}\n >\n <div class=\"menu-item__content ds-interaction-fill__content\">\n <span class={item.isSelected ? 'text-body-medium-emphasis menu-item__label' : 'text-body-medium menu-item__label'}>\n {item.label}\n </span>\n {item.subtext && (\n <span class=\"text-body-small menu-item__subtext\">{item.subtext}</span>\n )}\n </div>\n {item.showToggle && (\n <div class={{ 'menu-item__toggle': true, 'menu-item__toggle--on': !!item.toggleValue, 'ds-interaction-fill__content': true }} aria-hidden=\"true\">\n <div class=\"menu-item__toggle-thumb\" />\n </div>\n )}\n </button>\n );\n }))}\n </div>\n ))}\n </div>\n </Host>\n );\n }\n}\n"],"mappings":"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","ignoreList":[]}
@@ -1,2 +1,2 @@
1
- import{p as t,H as i,h as o,a as s,t as a}from"./index.js";import{d as e}from"./p-CtwNUCnp.js";import{T as n}from"./p-B_g8X0eh.js";import{r as d}from"./p-C1cBr-Ri.js";const r=()=>`.ds-control--md.sc-ds-tooltip-data-viz-h,.ds-control--md.sc-ds-tooltip-data-viz{--ds-control-height:var(--dimension-size-400);--ds-control-icon:var(--dimension-iconography-md);--ds-control-padding-inline:var(--dimension-space-075);--ds-control-label-inset:var(--dimension-space-025);--ds-control-gap:var(--dimension-space-050);--ds-control-radius:var(--dimension-radius-025)}.ds-control--sm.sc-ds-tooltip-data-viz-h,.ds-control--sm.sc-ds-tooltip-data-viz{--ds-control-height:var(--dimension-size-300);--ds-control-icon:var(--dimension-iconography-sm);--ds-control-padding-inline:var(--dimension-space-050);--ds-control-label-inset:var(--dimension-space-025);--ds-control-gap:var(--dimension-space-025);--ds-control-radius:var(--dimension-radius-025)}.ds-control--xs.sc-ds-tooltip-data-viz-h,.ds-control--xs.sc-ds-tooltip-data-viz{--ds-control-height:var(--dimension-size-200);--ds-control-icon:var(--dimension-iconography-xs);--ds-control-padding-inline:var(--dimension-space-025);--ds-control-label-inset:var(--dimension-space-025);--ds-control-gap:var(--dimension-space-025);--ds-control-radius:var(--dimension-radius-025)}.sc-ds-tooltip-data-viz-h{position:absolute;z-index:var(--dimension-z-index-tooltip);display:block;box-sizing:border-box;background-color:var(--color-background-translucent);backdrop-filter:blur(var(--effect-blur-md));-webkit-backdrop-filter:blur(var(--effect-blur-md));border-radius:var(--ds-control-radius, var(--dimension-radius-025));box-shadow:var(--effect-elevation-elevated-floating);white-space:nowrap;pointer-events:none;opacity:0}.tooltip-data-viz--visible.sc-ds-tooltip-data-viz-h{animation:tooltip-data-viz-fade-in var(--effect-motion-short-2) forwards}.tooltip-data-viz__item.sc-ds-tooltip-data-viz{box-sizing:border-box;height:var(--ds-control-height, var(--dimension-size-400));display:flex;align-items:center;gap:var(--dimension-space-025);padding:0 var(--ds-control-padding-inline, var(--dimension-space-075))}.tooltip-data-viz__label.sc-ds-tooltip-data-viz,.tooltip-data-viz__value.sc-ds-tooltip-data-viz{padding:0 var(--ds-control-label-inset, var(--dimension-space-025));line-height:var(--typography-lineheight-md)}@keyframes tooltip-data-viz-fade-in{from{opacity:0}to{opacity:1}}`;const c=12;const l=8;const p=t(class t extends i{constructor(t){super();if(t!==false){this.__registerHost()}this.value="";this.label="";this.x=0;this.y=0;this.delay=n.animationDelayInstant;this.side="right";this.align="bottom";this.visible=false;this.delayTimer=null}componentDidLoad(){this.scheduleShow();this.calculatePlacement()}disconnectedCallback(){this.clearDelayTimer()}onAnchorChange(){requestAnimationFrame((()=>this.calculatePlacement()))}onDelayChange(){if(!this.visible)this.scheduleShow()}get showDelayMs(){return d(this.delay,n.animationDelayInstant)}clearDelayTimer(){if(this.delayTimer){clearTimeout(this.delayTimer);this.delayTimer=null}}scheduleShow(){this.clearDelayTimer();this.visible=false;const t=this.showDelayMs;if(t<=0){this.visible=true;return}this.delayTimer=setTimeout((()=>{this.delayTimer=null;this.visible=true;requestAnimationFrame((()=>this.calculatePlacement()))}),t)}calculatePlacement(){const t=this.el.getBoundingClientRect();if(!t.width||!t.height)return;const i=t.right>window.innerWidth-l?"left":"right";const o=t.bottom>window.innerHeight-l?"top":"bottom";if(i!==this.side)this.side=i;if(o!==this.align)this.align=o}render(){const t=this.side==="right"?`${c}px`:`calc(-100% - ${c}px)`;const i=this.align==="bottom"?`${c}px`:`calc(-100% - ${c}px)`;return o(s,{key:"06233152937562e477fa04f4151683ab24f8c89b",class:{"tooltip-data-viz":true,"tooltip-data-viz--visible":this.visible},style:{left:`${this.x}px`,top:`${this.y}px`,transform:`translate(${t}, ${i})`}},o("div",{key:"d5c1ba43e1209e50c74ac45aaa34ac25d331e5e1",class:"tooltip-data-viz__item ds-control--md"},o("span",{key:"1561bfb05835ba6faf5501a1700acf56f7b2468b",class:"tooltip-data-viz__label"},o("ds-text",{key:"2ed44657c21814e4921679263cb1ba49d7357e7c",as:"span",variant:"text-body-medium",color:"var(--color-foreground-on-translucent-background-secondary)"},this.label)),o("span",{key:"deeefe07b3942991c9845dea61129a4cfc6e90e9",class:"tooltip-data-viz__value"},o("ds-text",{key:"d95181dbc54d1883f1f0687ca7204a67651a2ac9",as:"span",variant:"text-body-medium-emphasis",color:"var(--color-foreground-on-translucent-background-primary)"},this.value))))}get el(){return this}static get watchers(){return{x:[{onAnchorChange:0}],y:[{onAnchorChange:0}],delay:[{onDelayChange:0}]}}static get style(){return r()}},[2,"ds-tooltip-data-viz",{value:[8],label:[1],x:[2],y:[2],delay:[8],side:[32],align:[32],visible:[32]},undefined,{x:[{onAnchorChange:0}],y:[{onAnchorChange:0}],delay:[{onDelayChange:0}]}]);function h(){if(typeof customElements==="undefined"){return}const t=["ds-tooltip-data-viz","ds-text"];t.forEach((t=>{switch(t){case"ds-tooltip-data-viz":if(!customElements.get(a(t))){customElements.define(a(t),p)}break;case"ds-text":if(!customElements.get(a(t))){e()}break}}))}h();export{p as T,h as d};
2
- //# sourceMappingURL=p-3FLHei19.js.map
1
+ import{p as t,H as i,h as o,a as s,t as a}from"./index.js";import{d as e}from"./p-CtwNUCnp.js";import{T as n}from"./p-B_g8X0eh.js";import{r as d}from"./p-C1cBr-Ri.js";const r=()=>`.ds-control--md.sc-ds-tooltip-data-viz-h,.ds-control--md.sc-ds-tooltip-data-viz{--ds-control-height:var(--dimension-size-400);--ds-control-icon:var(--dimension-iconography-md);--ds-control-padding-inline:var(--dimension-space-075);--ds-control-label-inset:var(--dimension-space-025);--ds-control-gap:var(--dimension-space-050);--ds-control-radius:var(--dimension-radius-025)}.ds-control--sm.sc-ds-tooltip-data-viz-h,.ds-control--sm.sc-ds-tooltip-data-viz{--ds-control-height:var(--dimension-size-300);--ds-control-icon:var(--dimension-iconography-sm);--ds-control-padding-inline:var(--dimension-space-050);--ds-control-label-inset:var(--dimension-space-025);--ds-control-gap:var(--dimension-space-025);--ds-control-radius:var(--dimension-radius-025)}.ds-control--xs.sc-ds-tooltip-data-viz-h,.ds-control--xs.sc-ds-tooltip-data-viz{--ds-control-height:var(--dimension-size-200);--ds-control-icon:var(--dimension-iconography-xs);--ds-control-padding-inline:var(--dimension-space-025);--ds-control-label-inset:var(--dimension-space-025);--ds-control-gap:var(--dimension-space-025);--ds-control-radius:var(--dimension-radius-025)}.sc-ds-tooltip-data-viz-h{position:absolute;z-index:var(--dimension-z-index-tooltip);display:block;box-sizing:border-box;background-color:var(--color-background-translucent);backdrop-filter:blur(var(--effect-blur-md));-webkit-backdrop-filter:blur(var(--effect-blur-md));border-radius:var(--ds-control-radius, var(--dimension-radius-025));box-shadow:var(--effect-elevation-elevated-floating);white-space:nowrap;pointer-events:none;opacity:0}.tooltip-data-viz--visible.sc-ds-tooltip-data-viz-h{animation:tooltip-data-viz-fade-in var(--effect-motion-short-2) forwards}.tooltip-data-viz__item.sc-ds-tooltip-data-viz{box-sizing:border-box;height:var(--ds-control-height, var(--dimension-size-400));display:flex;align-items:center;gap:var(--dimension-space-025);padding:0 var(--ds-control-padding-inline, var(--dimension-space-075))}.tooltip-data-viz__label.sc-ds-tooltip-data-viz,.tooltip-data-viz__value.sc-ds-tooltip-data-viz{padding:0 var(--ds-control-label-inset, var(--dimension-space-025));line-height:var(--typography-lineheight-md)}@keyframes tooltip-data-viz-fade-in{from{opacity:0}to{opacity:1}}`;const l=12;const c=8;const p=t(class t extends i{constructor(t){super();if(t!==false){this.__registerHost()}this.value="";this.label="";this.x=0;this.y=0;this.delay=n.animationDelayInstant;this.side="right";this.align="bottom";this.visible=false;this.delayTimer=null}componentDidLoad(){this.scheduleShow();this.calculatePlacement()}disconnectedCallback(){this.clearDelayTimer()}onAnchorChange(){requestAnimationFrame((()=>this.calculatePlacement()))}onDelayChange(){if(!this.visible)this.scheduleShow()}get showDelayMs(){return d(this.delay,n.animationDelayInstant)}clearDelayTimer(){if(this.delayTimer){clearTimeout(this.delayTimer);this.delayTimer=null}}scheduleShow(){this.clearDelayTimer();this.visible=false;const t=this.showDelayMs;if(t<=0){this.visible=true;return}this.delayTimer=setTimeout((()=>{this.delayTimer=null;this.visible=true;requestAnimationFrame((()=>this.calculatePlacement()))}),t)}calculatePlacement(){const t=this.el.getBoundingClientRect();if(!t.width||!t.height)return;const i=t.right>window.innerWidth-c?"left":"right";const o=t.bottom>window.innerHeight-c?"top":"bottom";if(i!==this.side)this.side=i;if(o!==this.align)this.align=o}render(){const t=this.side==="right"?`${l}px`:`calc(-100% - ${l}px)`;const i=this.align==="bottom"?`${l}px`:`calc(-100% - ${l}px)`;return o(s,{key:"413424c88116e81fdb8a1ced4e418f0879f5df3f",class:{"tooltip-data-viz":true,"tooltip-data-viz--visible":this.visible},style:{left:`${this.x}px`,top:`${this.y}px`,transform:`translate(${t}, ${i})`}},o("div",{key:"e5fec7e435be1bdaf470d78275def92daf9e8e46",class:"tooltip-data-viz__item ds-control--md"},o("span",{key:"7f0b0dc4b11f173955718507ef241bd08c29c03d",class:"tooltip-data-viz__label"},o("ds-text",{key:"0b7393cce704ae6b7446419a75b86511f6d5483f",as:"span",variant:"text-body-medium",color:"var(--color-foreground-on-translucent-background-secondary)"},this.label)),o("span",{key:"b2e4ca42a348fda68ab33b6aaea712809adcd979",class:"tooltip-data-viz__value"},o("ds-text",{key:"5b89b7ee01693aa5f13958f5f9468029fb29e75d",as:"span",variant:"text-body-medium-emphasis",color:"var(--color-foreground-on-translucent-background-primary)"},this.value))))}get el(){return this}static get watchers(){return{x:[{onAnchorChange:0}],y:[{onAnchorChange:0}],delay:[{onDelayChange:0}]}}static get style(){return r()}},[2,"ds-tooltip-data-viz",{value:[8],label:[1],x:[2],y:[2],delay:[8],side:[32],align:[32],visible:[32]},undefined,{x:[{onAnchorChange:0}],y:[{onAnchorChange:0}],delay:[{onDelayChange:0}]}]);function h(){if(typeof customElements==="undefined"){return}const t=["ds-tooltip-data-viz","ds-text"];t.forEach((t=>{switch(t){case"ds-tooltip-data-viz":if(!customElements.get(a(t))){customElements.define(a(t),p)}break;case"ds-text":if(!customElements.get(a(t))){e()}break}}))}h();export{p as T,h as d};
2
+ //# sourceMappingURL=p-XNHSe8Mg.js.map
@@ -5,7 +5,7 @@ export declare class PanelTools {
5
5
  el: HTMLElement;
6
6
  /** When false, only the icon rail is shown. */
7
7
  open: boolean;
8
- /** Active tool view — `search`, `agents`, `messages`, `stacks`, or `activity`. */
8
+ /** Active tool view — `search`, `agents`, `messages`, `stacks`, `activity`, or `help`. */
9
9
  activeTool: PanelToolsToolId | '';
10
10
  /**
11
11
  * Rail items rendered in the right column.
@@ -1,3 +1,3 @@
1
1
  export type { PanelToolsItem, PanelToolsToolId } from './panel-tools-types';
2
- export { PANEL_TOOLS_LABELS, PANEL_TOOLS_PRIMARY_TOOL_ID, PANEL_TOOLS_TOOL_IDS, } from './panel-tools-types';
2
+ export { PANEL_TOOLS_FOOTER_TOOL_ID, PANEL_TOOLS_LABELS, PANEL_TOOLS_PRIMARY_TOOL_ID, PANEL_TOOLS_TOOL_IDS, } from './panel-tools-types';
3
3
  //# sourceMappingURL=index.d.ts.map
@@ -1,8 +1,10 @@
1
1
  /** Tool ids for the `ds-panel-tools` rail and drawer views. */
2
- export type PanelToolsToolId = 'search' | 'agents' | 'messages' | 'stacks' | 'activity';
2
+ export type PanelToolsToolId = 'search' | 'agents' | 'messages' | 'stacks' | 'activity' | 'help';
3
3
  export declare const PANEL_TOOLS_TOOL_IDS: PanelToolsToolId[];
4
4
  /** Rail header slot — mirrors panel-nav M logo row. */
5
5
  export declare const PANEL_TOOLS_PRIMARY_TOOL_ID: PanelToolsToolId;
6
+ /** Rail footer slot — flush to the bottom of the tools column. */
7
+ export declare const PANEL_TOOLS_FOOTER_TOOL_ID: PanelToolsToolId;
6
8
  export declare const PANEL_TOOLS_LABELS: Record<PanelToolsToolId, string>;
7
9
  export interface PanelToolsItem {
8
10
  id: PanelToolsToolId;
@@ -8,6 +8,10 @@ export type TooltipSize = 'md' | 'sm' | 'xs';
8
8
  */
9
9
  export declare class Tooltip {
10
10
  el: HTMLElement;
11
+ /**
12
+ * Tooltip text. Empty/whitespace skips show — useful when the wrapper must stay
13
+ * mounted for layout/animation stability (e.g. PanelNav expand/collapse).
14
+ */
11
15
  label: string;
12
16
  /** Control density (height, padding, type). */
13
17
  size: TooltipSize;
@@ -793,7 +793,7 @@ export namespace Components {
793
793
  */
794
794
  "activateTool": (id: PanelToolsToolId) => Promise<void>;
795
795
  /**
796
- * Active tool view — `search`, `agents`, `messages`, `stacks`, or `activity`.
796
+ * Active tool view — `search`, `agents`, `messages`, `stacks`, `activity`, or `help`.
797
797
  * @default ''
798
798
  */
799
799
  "activeTool": PanelToolsToolId | '';
@@ -1122,6 +1122,9 @@ export namespace Components {
1122
1122
  * @default TOKEN_DEFAULTS.animationDelayMedium3
1123
1123
  */
1124
1124
  "delay": number | string;
1125
+ /**
1126
+ * Tooltip text. Empty/whitespace skips show — useful when the wrapper must stay mounted for layout/animation stability (e.g. PanelNav expand/collapse).
1127
+ */
1125
1128
  "label": string;
1126
1129
  "shortcutKey": string | undefined;
1127
1130
  /**
@@ -2674,7 +2677,7 @@ declare namespace LocalJSX {
2674
2677
  }
2675
2678
  interface DsPanelTools {
2676
2679
  /**
2677
- * Active tool view — `search`, `agents`, `messages`, `stacks`, or `activity`.
2680
+ * Active tool view — `search`, `agents`, `messages`, `stacks`, `activity`, or `help`.
2678
2681
  * @default ''
2679
2682
  */
2680
2683
  "activeTool"?: PanelToolsToolId | '';
@@ -3026,6 +3029,9 @@ declare namespace LocalJSX {
3026
3029
  * @default TOKEN_DEFAULTS.animationDelayMedium3
3027
3030
  */
3028
3031
  "delay"?: number | string;
3032
+ /**
3033
+ * Tooltip text. Empty/whitespace skips show — useful when the wrapper must stay mounted for layout/animation stability (e.g. PanelNav expand/collapse).
3034
+ */
3029
3035
  "label": string;
3030
3036
  "shortcutKey"?: string | undefined;
3031
3037
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ds-mo/ui",
3
- "version": "2.13.0",
3
+ "version": "2.14.0",
4
4
  "description": "CompoMo — composable web components styled with TokoMo design tokens",
5
5
  "keywords": [
6
6
  "web-components",
@@ -481,7 +481,8 @@ export class PanelNav {
481
481
  }
482
482
 
483
483
  private renderFooterAction(isDashboardChrome: boolean) {
484
- const footerLabel = isDashboardChrome ? 'Open settings' : 'Go to dashboard';
484
+ // Short chrome labels same string for tip + aria (not "Open/Go to …").
485
+ const footerLabel = isDashboardChrome ? 'Settings' : 'Dashboard';
485
486
  return (
486
487
  <ds-tooltip label={footerLabel} side="right" size="sm">
487
488
  <ds-button-unfilled variant="icon"
@@ -500,14 +501,16 @@ export class PanelNav {
500
501
  }
501
502
 
502
503
  private renderFooterUser(collapsed: boolean, userName: string, userInitial: string) {
503
- const userLabel = collapsed ? `User: ${userName}` : `User menu for ${userName}`;
504
+ // Always wrap so collapse morph CSS can transition on a stable button node.
505
+ // Tip only when collapsed (empty label → ds-tooltip skips show).
506
+ const tipLabel = collapsed ? 'Account' : '';
504
507
  const userButton = (
505
508
  <button
506
509
  type="button"
507
510
  id={PANEL_NAV_USER_MENU_ANCHOR_ID}
508
511
  class="panel-nav__item panel-nav__footer-user ds-focus-ring-inset ds-interaction-fill"
509
512
  tabIndex={this.rovingIndex === this.getUserRovingIndex() ? 0 : -1}
510
- aria-label={userLabel}
513
+ aria-label="Account"
511
514
  onClick={(e) => this.handleUserAction(e)}
512
515
  onKeyDown={(e: KeyboardEvent) => this.handleRovingKeyDown(e, this.getUserRovingIndex())}
513
516
  onFocus={() => { this.rovingIndex = this.getUserRovingIndex(); }}
@@ -535,10 +538,8 @@ export class PanelNav {
535
538
  </button>
536
539
  );
537
540
 
538
- if (!collapsed) return userButton;
539
-
540
541
  return (
541
- <ds-tooltip label={userLabel} side="right" size="sm">
542
+ <ds-tooltip label={tipLabel} side="right" size="sm">
542
543
  {userButton}
543
544
  </ds-tooltip>
544
545
  );
@@ -593,10 +594,10 @@ export class PanelNav {
593
594
  ? <a {...sharedProps} href={item.href}>{itemContent}</a>
594
595
  : <button {...sharedProps} type="button">{itemContent}</button>;
595
596
 
596
- if (!collapsed) return control;
597
-
597
+ // Always wrap so label fade CSS can transition on a stable item node.
598
+ // Tip only when collapsed (empty label → ds-tooltip skips show).
598
599
  return (
599
- <ds-tooltip label={item.label} side="right" size="sm">
600
+ <ds-tooltip label={collapsed ? item.label : ''} side="right" size="sm">
600
601
  {control}
601
602
  </ds-tooltip>
602
603
  );
@@ -1,6 +1,7 @@
1
1
  import { Component, Prop, Event, EventEmitter, Element, State, Watch, Method, h, Host } from '@stencil/core';
2
2
  import type { ChromeTransitionDetail } from '../../nav/chrome-transition';
3
3
  import {
4
+ PANEL_TOOLS_FOOTER_TOOL_ID,
4
5
  PANEL_TOOLS_LABELS,
5
6
  PANEL_TOOLS_PRIMARY_TOOL_ID,
6
7
  type PanelToolsItem,
@@ -19,7 +20,7 @@ export class PanelTools {
19
20
  /** When false, only the icon rail is shown. */
20
21
  @Prop({ mutable: true, reflect: true }) open: boolean = false;
21
22
 
22
- /** Active tool view — `search`, `agents`, `messages`, `stacks`, or `activity`. */
23
+ /** Active tool view — `search`, `agents`, `messages`, `stacks`, `activity`, or `help`. */
23
24
  @Prop({ mutable: true, attribute: 'active-tool', reflect: true })
24
25
  activeTool: PanelToolsToolId | '' = '';
25
26
 
@@ -62,8 +63,15 @@ export class PanelTools {
62
63
  private get orderedRailItems(): PanelToolsItem[] {
63
64
  const railItems = this.railItems;
64
65
  const headerItem = railItems.find(item => item.id === PANEL_TOOLS_PRIMARY_TOOL_ID);
65
- const bodyItems = railItems.filter(item => item.id !== PANEL_TOOLS_PRIMARY_TOOL_ID);
66
- return headerItem ? [headerItem, ...bodyItems] : bodyItems;
66
+ const footerItem = railItems.find(item => item.id === PANEL_TOOLS_FOOTER_TOOL_ID);
67
+ const bodyItems = railItems.filter(
68
+ item => item.id !== PANEL_TOOLS_PRIMARY_TOOL_ID && item.id !== PANEL_TOOLS_FOOTER_TOOL_ID,
69
+ );
70
+ const ordered: PanelToolsItem[] = [];
71
+ if (headerItem) ordered.push(headerItem);
72
+ ordered.push(...bodyItems);
73
+ if (footerItem) ordered.push(footerItem);
74
+ return ordered;
67
75
  }
68
76
 
69
77
  disconnectedCallback() {
@@ -230,7 +238,13 @@ export class PanelTools {
230
238
  const headerLabel = this.headerLabel();
231
239
  const orderedRailItems = this.orderedRailItems;
232
240
  const headerItem = orderedRailItems.find(item => item.id === PANEL_TOOLS_PRIMARY_TOOL_ID);
233
- const bodyItems = orderedRailItems.filter(item => item.id !== PANEL_TOOLS_PRIMARY_TOOL_ID);
241
+ const footerItem = orderedRailItems.find(item => item.id === PANEL_TOOLS_FOOTER_TOOL_ID);
242
+ const bodyItems = orderedRailItems.filter(
243
+ item => item.id !== PANEL_TOOLS_PRIMARY_TOOL_ID && item.id !== PANEL_TOOLS_FOOTER_TOOL_ID,
244
+ );
245
+ const footerIndex = footerItem
246
+ ? (headerItem ? 1 : 0) + bodyItems.length
247
+ : -1;
234
248
  const showDrawerChrome = this.isDrawerPresent();
235
249
  const drawerResting = panelToolsDrawerResting(this.open, this.motion);
236
250
 
@@ -259,6 +273,11 @@ export class PanelTools {
259
273
  this.renderRailAction(item, headerItem ? bodyIdx + 1 : bodyIdx),
260
274
  )}
261
275
  </div>
276
+ {footerItem ? (
277
+ <div class="panel-tools__rail-footer">
278
+ {this.renderRailAction(footerItem, footerIndex)}
279
+ </div>
280
+ ) : null}
262
281
  </nav>
263
282
 
264
283
  <div
@@ -324,6 +343,16 @@ export class PanelTools {
324
343
  >
325
344
  <slot name="agents" />
326
345
  </div>
346
+ <div
347
+ class={{
348
+ 'panel-tools__view': true,
349
+ 'panel-tools__view--active': this.isViewActive('help'),
350
+ 'text-body-medium': true,
351
+ }}
352
+ hidden={!this.isViewActive('help')}
353
+ >
354
+ <slot name="help" />
355
+ </div>
327
356
  </div>
328
357
  </div>
329
358
  </div>
@@ -1,5 +1,6 @@
1
1
  export type { PanelToolsItem, PanelToolsToolId } from './panel-tools-types';
2
2
  export {
3
+ PANEL_TOOLS_FOOTER_TOOL_ID,
3
4
  PANEL_TOOLS_LABELS,
4
5
  PANEL_TOOLS_PRIMARY_TOOL_ID,
5
6
  PANEL_TOOLS_TOOL_IDS,
@@ -1,5 +1,11 @@
1
1
  /** Tool ids for the `ds-panel-tools` rail and drawer views. */
2
- export type PanelToolsToolId = 'search' | 'agents' | 'messages' | 'stacks' | 'activity';
2
+ export type PanelToolsToolId =
3
+ | 'search'
4
+ | 'agents'
5
+ | 'messages'
6
+ | 'stacks'
7
+ | 'activity'
8
+ | 'help';
3
9
 
4
10
  export const PANEL_TOOLS_TOOL_IDS: PanelToolsToolId[] = [
5
11
  'search',
@@ -7,17 +13,22 @@ export const PANEL_TOOLS_TOOL_IDS: PanelToolsToolId[] = [
7
13
  'messages',
8
14
  'stacks',
9
15
  'activity',
16
+ 'help',
10
17
  ];
11
18
 
12
19
  /** Rail header slot — mirrors panel-nav M logo row. */
13
20
  export const PANEL_TOOLS_PRIMARY_TOOL_ID: PanelToolsToolId = 'search';
14
21
 
22
+ /** Rail footer slot — flush to the bottom of the tools column. */
23
+ export const PANEL_TOOLS_FOOTER_TOOL_ID: PanelToolsToolId = 'help';
24
+
15
25
  export const PANEL_TOOLS_LABELS: Record<PanelToolsToolId, string> = {
16
26
  search: 'Search',
17
27
  messages: 'Messages',
18
28
  stacks: 'Stacks',
19
29
  activity: 'Activity',
20
30
  agents: 'Agents',
31
+ help: 'Help & Support',
21
32
  };
22
33
 
23
34
  export interface PanelToolsItem {
@@ -42,6 +42,10 @@ let activeTooltip: Tooltip | null = null;
42
42
  export class Tooltip {
43
43
  @Element() el!: HTMLElement;
44
44
 
45
+ /**
46
+ * Tooltip text. Empty/whitespace skips show — useful when the wrapper must stay
47
+ * mounted for layout/animation stability (e.g. PanelNav expand/collapse).
48
+ */
45
49
  @Prop() label!: string;
46
50
 
47
51
  /** Control density (height, padding, type). */
@@ -101,6 +105,10 @@ export class Tooltip {
101
105
  @Watch('shortcutKey')
102
106
  @Watch('shortcutKeyPosition')
103
107
  onContentOrPositionChange() {
108
+ if (!this.label?.trim()) {
109
+ if (this.isOpen || this.popupEl) this.hideInstant();
110
+ return;
111
+ }
104
112
  if (!this.popupEl || !this.isOpen) return;
105
113
  this.renderPopupContent();
106
114
  this.calculatePosition();
@@ -187,6 +195,9 @@ export class Tooltip {
187
195
  }
188
196
 
189
197
  private show() {
198
+ // Empty label = wrapper present for DOM stability (e.g. PanelNav expand/collapse)
199
+ // but no tip should appear.
200
+ if (!this.label?.trim()) return;
190
201
  this.clearTimers();
191
202
  let instant = Date.now() - lastDismissedAt < this.instantReopenMs;
192
203
  if (activeTooltip && activeTooltip !== this) {
@@ -201,6 +212,7 @@ export class Tooltip {
201
212
  }
202
213
  this.delayTimer = setTimeout(() => {
203
214
  this.delayTimer = null;
215
+ if (!this.label?.trim()) return;
204
216
  this.mountPopup();
205
217
  }, this.hoverDelayMs);
206
218
  }
@@ -793,7 +793,7 @@ export namespace Components {
793
793
  */
794
794
  "activateTool": (id: PanelToolsToolId) => Promise<void>;
795
795
  /**
796
- * Active tool view — `search`, `agents`, `messages`, `stacks`, or `activity`.
796
+ * Active tool view — `search`, `agents`, `messages`, `stacks`, `activity`, or `help`.
797
797
  * @default ''
798
798
  */
799
799
  "activeTool": PanelToolsToolId | '';
@@ -1122,6 +1122,9 @@ export namespace Components {
1122
1122
  * @default TOKEN_DEFAULTS.animationDelayMedium3
1123
1123
  */
1124
1124
  "delay": number | string;
1125
+ /**
1126
+ * Tooltip text. Empty/whitespace skips show — useful when the wrapper must stay mounted for layout/animation stability (e.g. PanelNav expand/collapse).
1127
+ */
1125
1128
  "label": string;
1126
1129
  "shortcutKey": string | undefined;
1127
1130
  /**
@@ -2674,7 +2677,7 @@ declare namespace LocalJSX {
2674
2677
  }
2675
2678
  interface DsPanelTools {
2676
2679
  /**
2677
- * Active tool view — `search`, `agents`, `messages`, `stacks`, or `activity`.
2680
+ * Active tool view — `search`, `agents`, `messages`, `stacks`, `activity`, or `help`.
2678
2681
  * @default ''
2679
2682
  */
2680
2683
  "activeTool"?: PanelToolsToolId | '';
@@ -3026,6 +3029,9 @@ declare namespace LocalJSX {
3026
3029
  * @default TOKEN_DEFAULTS.animationDelayMedium3
3027
3030
  */
3028
3031
  "delay"?: number | string;
3032
+ /**
3033
+ * Tooltip text. Empty/whitespace skips show — useful when the wrapper must stay mounted for layout/animation stability (e.g. PanelNav expand/collapse).
3034
+ */
3029
3035
  "label": string;
3030
3036
  "shortcutKey"?: string | undefined;
3031
3037
  /**
@@ -1,2 +0,0 @@
1
- import{p as t,H as i,h as s,a as o,t as n}from"./index.js";import{a as e,T as r}from"./p-B_g8X0eh.js";import{r as a}from"./p-BWUTr_XN.js";import{r as h}from"./p-C1cBr-Ri.js";const d=()=>`.ds-control--md.sc-ds-tooltip-h,.ds-control--md.sc-ds-tooltip{--ds-control-height:var(--dimension-size-400);--ds-control-icon:var(--dimension-iconography-md);--ds-control-padding-inline:var(--dimension-space-075);--ds-control-label-inset:var(--dimension-space-025);--ds-control-gap:var(--dimension-space-050);--ds-control-radius:var(--dimension-radius-025)}.ds-control--sm.sc-ds-tooltip-h,.ds-control--sm.sc-ds-tooltip{--ds-control-height:var(--dimension-size-300);--ds-control-icon:var(--dimension-iconography-sm);--ds-control-padding-inline:var(--dimension-space-050);--ds-control-label-inset:var(--dimension-space-025);--ds-control-gap:var(--dimension-space-025);--ds-control-radius:var(--dimension-radius-025)}.ds-control--xs.sc-ds-tooltip-h,.ds-control--xs.sc-ds-tooltip{--ds-control-height:var(--dimension-size-200);--ds-control-icon:var(--dimension-iconography-xs);--ds-control-padding-inline:var(--dimension-space-025);--ds-control-label-inset:var(--dimension-space-025);--ds-control-gap:var(--dimension-space-025);--ds-control-radius:var(--dimension-radius-025)}.tooltip-popup.sc-ds-tooltip{box-sizing:border-box;background-color:var(--color-background-primary);border-radius:var(--ds-control-radius, var(--dimension-radius-025));box-shadow:var(--effect-elevation-elevated-floating);white-space:nowrap;pointer-events:none;animation:tooltipFadeIn var(--effect-motion-short-2) forwards;will-change:transform;backface-visibility:hidden}.tooltip-popup--instant.sc-ds-tooltip{animation:none;opacity:1}.tooltip-popup--closing.sc-ds-tooltip{animation:tooltipFadeOut var(--effect-motion-short-3) forwards}.tooltip-inner.sc-ds-tooltip{box-sizing:border-box;height:var(--ds-control-height, var(--dimension-size-400));display:flex;align-items:center;gap:var(--ds-control-gap, var(--dimension-space-050));padding:0 var(--ds-control-padding-inline, var(--dimension-space-075))}.tooltip-label.sc-ds-tooltip{padding:0 var(--ds-control-label-inset, var(--dimension-space-025));color:var(--color-foreground-primary)}.key-hint.sc-ds-tooltip{width:var(--ds-control-icon, var(--dimension-size-200));height:var(--ds-control-icon, var(--dimension-size-200));flex-shrink:0;display:flex;align-items:center;justify-content:center;border:var(--dimension-stroke-width-015) solid var(--color-border-tertiary);border-bottom-width:var(--dimension-stroke-width-025);border-radius:var(--dimension-radius-025);color:var(--color-foreground-primary)}@keyframes tooltipFadeIn{from{opacity:0}to{opacity:1}}@keyframes tooltipFadeOut{from{opacity:1}to{opacity:0}}`;const l={md:"text-body-medium",sm:"text-body-small",xs:"text-caption"};const c={md:"var(--typography-lineheight-md)",sm:"var(--typography-lineheight-sm)",xs:"var(--typography-lineheight-xs)"};const p={md:r.size400,sm:r.size300,xs:r.size200};let u=0;let m=0;let f=null;const g=t(class t extends i{constructor(t){super();if(t!==false){this.__registerHost()}this.size="md";this.side="top";this.align="center";this.sideOffset=e.space050;this.alignOffset=0;this.delay=r.animationDelayMedium3;this.shortcutKeyPosition="end";this.tooltipId=`ds-tooltip-${++m}`;this.delayTimer=null;this.closeTimer=null;this.anchor=null;this.popupEl=null;this.skipEnterAnimation=false;this.isOpen=false;this.mouseEnterHandler=()=>this.show();this.mouseLeaveHandler=()=>this.hide();this.focusHandler=()=>this.show();this.blurHandler=t=>{const i=t.relatedTarget;if(i&&this.anchor?.contains(i))return;this.hide()}}componentDidLoad(){this.bindAnchor()}disconnectedCallback(){if(f===this)f=null;this.unbindAnchor();this.clearTimers();this.destroyPopup()}onContentOrPositionChange(){if(!this.popupEl||!this.isOpen)return;this.renderPopupContent();this.calculatePosition()}get viewportPadPx(){return a(r.space050,r.space050)}get sideOffsetPx(){return a(this.sideOffset,r.space050)}get alignOffsetPx(){return a(this.alignOffset,0)}get hoverDelayMs(){return h(this.delay,r.animationDelayMedium3)}get instantReopenMs(){return h(r.animationDurationMedium1,r.animationDurationMedium1)}get fadeOutMs(){return h(r.motionShort3,r.animationDurationShort3)}get tooltipFallbackWidthPx(){return a(r.tooltipFallbackWidth,r.tooltipFallbackWidth)}get tooltipFallbackHeightPx(){const t=p[this.size];return a(t,t)}bindAnchor(){const t=this.el.querySelector("slot");const i=t?.assignedElements?.()??[];const s=i[0]??this.el.firstElementChild;if(!s)return;this.anchor=s;s.setAttribute("aria-describedby",this.tooltipId);s.addEventListener("mouseenter",this.mouseEnterHandler);s.addEventListener("mouseleave",this.mouseLeaveHandler);s.addEventListener("focusin",this.focusHandler);s.addEventListener("focusout",this.blurHandler)}unbindAnchor(){if(!this.anchor)return;this.anchor.removeAttribute("aria-describedby");this.anchor.removeEventListener("mouseenter",this.mouseEnterHandler);this.anchor.removeEventListener("mouseleave",this.mouseLeaveHandler);this.anchor.removeEventListener("focusin",this.focusHandler);this.anchor.removeEventListener("focusout",this.blurHandler);this.anchor=null}clearTimers(){if(this.delayTimer){clearTimeout(this.delayTimer);this.delayTimer=null}if(this.closeTimer){clearTimeout(this.closeTimer);this.closeTimer=null}}destroyPopup(){if(!this.popupEl)return;this.popupEl.remove();this.popupEl=null}hideInstant(){this.clearTimers();u=Date.now();if(f===this)f=null;this.destroyPopup();this.isOpen=false;this.skipEnterAnimation=false}show(){this.clearTimers();let t=Date.now()-u<this.instantReopenMs;if(f&&f!==this){f.hideInstant();t=true}f=this;this.skipEnterAnimation=t;if(t){this.mountPopup();return}this.delayTimer=setTimeout((()=>{this.delayTimer=null;this.mountPopup()}),this.hoverDelayMs)}hide(){this.clearTimers();if(!this.popupEl&&!this.isOpen)return;u=Date.now();if(f===this)f=null;if(!this.popupEl){this.isOpen=false;return}this.popupEl.classList.remove("tooltip-popup--instant");this.popupEl.classList.add("tooltip-popup--closing");this.closeTimer=setTimeout((()=>{this.closeTimer=null;this.destroyPopup();this.isOpen=false;this.skipEnterAnimation=false}),this.fadeOutMs)}mountPopup(){if(!this.popupEl){const t=document.createElement("div");t.id=this.tooltipId;t.setAttribute("role","tooltip");t.className="tooltip-popup sc-ds-tooltip";Object.assign(t.style,{position:"fixed",left:"0",top:"0",zIndex:"var(--dimension-z-index-tooltip)",pointerEvents:"none"});document.body.appendChild(t);this.popupEl=t}this.popupEl.classList.toggle("tooltip-popup--instant",this.skipEnterAnimation);this.popupEl.classList.remove("tooltip-popup--closing");this.renderPopupContent();this.calculatePosition();this.isOpen=true}renderPopupContent(){if(!this.popupEl)return;const t=l[this.size];const i=c[this.size];const s=this.size==="md"?"ds-control--md":this.size==="sm"?"ds-control--sm":"ds-control--xs";const o="sc-ds-tooltip";const n=this.shortcutKey&&this.shortcutKeyPosition==="start"?`<div class="key-hint ${o}" aria-hidden="true"><span class="text-caption-emphasis ${o}">${v(this.shortcutKey)}</span></div>`:"";const e=this.shortcutKey&&this.shortcutKeyPosition==="end"?`<div class="key-hint ${o}" aria-hidden="true"><span class="text-caption-emphasis ${o}">${v(this.shortcutKey)}</span></div>`:"";this.popupEl.innerHTML=`<div class="tooltip-inner ${s} ${o}">`+n+`<span class="tooltip-label ${t} ${o}" style="line-height:${i}">${v(this.label)}</span>`+e+`</div>`}calculatePosition(){if(!this.anchor||!this.popupEl)return;const t=this.popupEl;const i=this.anchor.getBoundingClientRect();const s=t.offsetWidth||this.tooltipFallbackWidthPx;const o=t.offsetHeight||this.tooltipFallbackHeightPx;const n=this.sideOffsetPx;const e=this.alignOffsetPx;const r=this.viewportPadPx;let a=0,h=0;switch(this.side){case"top":h=i.top-o-n;a=this.align==="start"?i.left+e:this.align==="end"?i.right-s+e:i.left+i.width/2-s/2+e;break;case"bottom":h=i.bottom+n;a=this.align==="start"?i.left+e:this.align==="end"?i.right-s+e:i.left+i.width/2-s/2+e;break;case"left":a=i.left-s-n;h=this.align==="start"?i.top+e:this.align==="end"?i.bottom-o+e:i.top+i.height/2-o/2+e;break;case"right":a=i.right+n;h=this.align==="start"?i.top+e:this.align==="end"?i.bottom-o+e:i.top+i.height/2-o/2+e;break}const d=Math.min(Math.max(a,r),window.innerWidth-s-r);const l=Math.min(Math.max(h,r),window.innerHeight-o-r);t.style.transform=`translate(${Math.round(d)}px, ${Math.round(l)}px)`}render(){return s(o,{key:"95a50ec851eddaa0a12d446389f12b83890364dd",style:{display:"contents"}},s("slot",{key:"b9408aeca3e0109c21edfd2a876ceeecd3f0a706"}))}get el(){return this}static get watchers(){return{label:[{onContentOrPositionChange:0}],size:[{onContentOrPositionChange:0}],side:[{onContentOrPositionChange:0}],align:[{onContentOrPositionChange:0}],sideOffset:[{onContentOrPositionChange:0}],alignOffset:[{onContentOrPositionChange:0}],shortcutKey:[{onContentOrPositionChange:0}],shortcutKeyPosition:[{onContentOrPositionChange:0}]}}static get style(){return d()}},[262,"ds-tooltip",{label:[1],size:[1],side:[1],align:[1],sideOffset:[8,"side-offset"],alignOffset:[8,"align-offset"],delay:[8],shortcutKey:[1,"shortcut-key"],shortcutKeyPosition:[1,"shortcut-key-position"]},undefined,{label:[{onContentOrPositionChange:0}],size:[{onContentOrPositionChange:0}],side:[{onContentOrPositionChange:0}],align:[{onContentOrPositionChange:0}],sideOffset:[{onContentOrPositionChange:0}],alignOffset:[{onContentOrPositionChange:0}],shortcutKey:[{onContentOrPositionChange:0}],shortcutKeyPosition:[{onContentOrPositionChange:0}]}]);function v(t){return t.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/'/g,"&#39;")}function y(){if(typeof customElements==="undefined"){return}const t=["ds-tooltip"];t.forEach((t=>{switch(t){case"ds-tooltip":if(!customElements.get(n(t))){customElements.define(n(t),g)}break}}))}y();export{g as T,y as d};
2
- //# sourceMappingURL=p-DohXlNCJ.js.map