@ds-mo/ui 2.5.0 → 2.6.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-menu.js +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-select.js +1 -1
- package/dist/components/ds-shell-gradient-picker.js +1 -1
- package/dist/components/ds-shell-gradient-swatch.js +1 -1
- package/dist/components/p-Bknnbbb7.js +2 -0
- package/dist/components/p-Bknnbbb7.js.map +1 -0
- package/dist/components/{p-BgiK3sHs.js → p-DOMIPWaB.js} +2 -2
- package/dist/components/p-DuI6g-ke.js +2 -0
- package/dist/components/p-DuI6g-ke.js.map +1 -0
- package/dist/components/{p-CLmK4lRE.js → p-Rn0--Bsl.js} +2 -2
- package/dist/components/p-Rn0--Bsl.js.map +1 -0
- package/dist/types/components/Menu/menu-placement.d.ts +22 -0
- package/dist/types/components/Menu/menu-types.d.ts +1 -0
- package/dist/types/components/PanelNav/index.d.ts +1 -1
- package/dist/types/components/PanelNav/panel-nav-types.d.ts +5 -0
- package/dist/types/components/ShellGradientSwatch/ShellGradientSwatch.d.ts +1 -1
- package/dist/types/components.d.ts +2 -2
- package/dist/types/nav/index.d.ts +2 -0
- package/package.json +3 -3
- package/src/wc/components/Menu/menu-placement.ts +24 -0
- package/src/wc/components/Menu/menu-types.ts +4 -0
- package/src/wc/components/PanelNav/PanelNav.tsx +3 -2
- package/src/wc/components/PanelNav/index.ts +1 -1
- package/src/wc/components/PanelNav/panel-nav-types.ts +6 -0
- package/src/wc/components/ShellGradientSwatch/ShellGradientSwatch.tsx +6 -2
- package/src/wc/components.d.ts +5 -2
- package/src/wc/nav/index.ts +4 -0
- package/dist/components/p-BU5fKLdO.js +0 -2
- package/dist/components/p-BU5fKLdO.js.map +0 -1
- package/dist/components/p-CLmK4lRE.js.map +0 -1
- package/dist/components/p-DKG6W4pb.js +0 -2
- package/dist/components/p-DKG6W4pb.js.map +0 -1
- /package/dist/components/{p-BgiK3sHs.js.map → p-DOMIPWaB.js.map} +0 -0
|
@@ -0,0 +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_DEFAULTS","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","popupFallbackWidthPx","menuWidthXs","popupFallbackHeightPx","menuFallbackHeight","resolveCssTimeMs","motionShort3","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\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-3) 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(--dimension-size-400);\n padding: var(--dimension-space-050) var(--dimension-space-075);\n -webkit-user-select: none;\n user-select: none;\n}\n\n.section-label {\n color: var(--color-foreground-secondary);\n}\n\n.menu-section--gradient-picker {\n padding-bottom: var(--dimension-space-050);\n}\n\n/* ── Items ───────────────────────────────────────────────────────────────── */\n\n.menu-item {\n display: flex;\n align-items: center;\n gap: var(--dimension-space-050);\n position: relative;\n width: 100%;\n min-height: var(--dimension-size-400);\n padding: var(--dimension-space-075);\n box-sizing: border-box;\n border: none;\n border-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-bg: var(--color-interaction-hover);\n --_pressed-bg: var(--color-interaction-pressed);\n --_selected-bg: var(--color-interaction-active);\n}\n\n.menu-item:has(.menu-item__subtext) {\n align-items: flex-start;\n}\n\n/* Interaction layer — same ::after pattern as tabs / BarNav trigger */\n.menu-item::after {\n content: '';\n position: absolute;\n inset: 0;\n border-radius: inherit;\n background: transparent;\n pointer-events: none;\n transition: none;\n}\n\n.menu-item--selected::after {\n background: var(--_selected-bg);\n transition: none;\n}\n\n.menu-item:hover:not(:disabled):not(.menu-item--selected)::after {\n background: var(--_hover-bg);\n transition: none;\n}\n\n.menu-item:active:not(:disabled)::after {\n background: var(--_pressed-bg);\n transition: none;\n}\n\n.menu-item--inactive {\n opacity: 0.5;\n cursor: not-allowed;\n pointer-events: none;\n}\n\n.menu-item--destructive .menu-item__label {\n color: var(--color-foreground-bold-negative) !important;\n}\n\n.menu-item__content {\n position: relative;\n z-index: 1;\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 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 color: var(--color-foreground-secondary);\n line-height: 1.4;\n}\n\n/* ── Toggle suffix ───────────────────────────────────────────────────────── */\n\n.menu-item__toggle {\n position: relative;\n z-index: 1;\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_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_DEFAULTS.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.motionShort3, 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 this.close();\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\" 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-focus-ring-inset': true,\n 'ds-focus-ring--visible': isFocused && this.focusRingVisible,\n 'menu-item--selected': !!item.isSelected,\n 'menu-item--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\">\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 }} 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":[]}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { MenuAlign, MenuSide } from './menu-position';
|
|
2
|
+
/** Placement props bindable on `ds-menu` (side, align, offsets). */
|
|
3
|
+
export interface MenuPlacement {
|
|
4
|
+
side: MenuSide;
|
|
5
|
+
align: MenuAlign;
|
|
6
|
+
sideOffset: number | string;
|
|
7
|
+
alignOffset: number | string;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Canonical `ds-menu` placement for the **panel-nav footer user menu**.
|
|
11
|
+
* Anchor from `dsNavUserAction` detail `{ anchor }` (id `ds-panel-nav-user-menu-anchor`).
|
|
12
|
+
*
|
|
13
|
+
* Not for BarNav overflow menus — those use `side="bottom"` / `align="end"` internally.
|
|
14
|
+
* Use `align: 'end'` so tall footer menus bottom-align on the anchor and grow upward.
|
|
15
|
+
*/
|
|
16
|
+
export declare const PANEL_NAV_USER_MENU_PLACEMENT: {
|
|
17
|
+
readonly side: "right";
|
|
18
|
+
readonly align: "end";
|
|
19
|
+
readonly sideOffset: "--dimension-space-050";
|
|
20
|
+
readonly alignOffset: 0;
|
|
21
|
+
};
|
|
22
|
+
//# sourceMappingURL=menu-placement.d.ts.map
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { ShellGradientPreset } from '../../nav/shell-gradient-presets';
|
|
2
2
|
export type { MenuAlign, MenuSide } from './menu-position';
|
|
3
|
+
export { PANEL_NAV_USER_MENU_PLACEMENT, type MenuPlacement, } from './menu-placement';
|
|
3
4
|
export interface MenuItemData {
|
|
4
5
|
label: string;
|
|
5
6
|
value?: string;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export type { PanelNavRouterMode, PanelNavItem, PanelNavGroup, PanelNavUserActionDetail, } from './panel-nav-types';
|
|
2
|
-
export { PANEL_NAV_USER_MENU_ANCHOR_ID } from './panel-nav-types';
|
|
2
|
+
export { PANEL_NAV_USER_MENU_ANCHOR_ID, PANEL_NAV_USER_MENU_PLACEMENT } from './panel-nav-types';
|
|
3
3
|
export type { NavChromeStyle } from '../../nav/nav-chrome';
|
|
4
4
|
export { NAV_STYLE_HINT_ATTR, setNavStyleHint, clearNavStyleHint, resolvePanelNavStyle, shouldResyncPanelNavStyle, deriveActiveIdFromUrl, parsePanelNavGroups, } from './panel-nav-utils';
|
|
5
5
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import type { MenuPlacement } from '../Menu/menu-placement';
|
|
2
|
+
import { PANEL_NAV_USER_MENU_PLACEMENT } from '../Menu/menu-placement';
|
|
1
3
|
import type { NavChromeStyle } from '../../nav/nav-chrome';
|
|
2
4
|
export type { NavChromeStyle };
|
|
3
5
|
export type PanelNavRouterMode = 'anchor' | 'event';
|
|
@@ -22,5 +24,8 @@ export declare const PANEL_NAV_USER_MENU_ANCHOR_ID = "ds-panel-nav-user-menu-anc
|
|
|
22
24
|
/** Detail for `dsNavUserAction` — anchor element for an external `ds-menu`. */
|
|
23
25
|
export interface PanelNavUserActionDetail {
|
|
24
26
|
anchor: HTMLElement;
|
|
27
|
+
/** Recommended `ds-menu` placement — spread onto external user menu instance. */
|
|
28
|
+
menuPlacement: MenuPlacement;
|
|
25
29
|
}
|
|
30
|
+
export { PANEL_NAV_USER_MENU_PLACEMENT };
|
|
26
31
|
//# sourceMappingURL=panel-nav-types.d.ts.map
|
|
@@ -3,7 +3,7 @@ import { type ShellGradientPreset } from './shell-gradient-swatch-types';
|
|
|
3
3
|
export declare class ShellGradientSwatch {
|
|
4
4
|
/** Wash preset this orb previews. */
|
|
5
5
|
preset: ShellGradientPreset;
|
|
6
|
-
/** Selected state —
|
|
6
|
+
/** Selected state — brand ring with background offset (notification-dot pattern). */
|
|
7
7
|
selected: boolean;
|
|
8
8
|
inactive: boolean;
|
|
9
9
|
ariaLabel: string | undefined;
|
|
@@ -830,7 +830,7 @@ export namespace Components {
|
|
|
830
830
|
*/
|
|
831
831
|
"preset": ShellGradientPreset1;
|
|
832
832
|
/**
|
|
833
|
-
* Selected state —
|
|
833
|
+
* Selected state — brand ring with background offset (notification-dot pattern).
|
|
834
834
|
* @default false
|
|
835
835
|
*/
|
|
836
836
|
"selected": boolean;
|
|
@@ -2705,7 +2705,7 @@ declare namespace LocalJSX {
|
|
|
2705
2705
|
*/
|
|
2706
2706
|
"preset"?: ShellGradientPreset1;
|
|
2707
2707
|
/**
|
|
2708
|
-
* Selected state —
|
|
2708
|
+
* Selected state — brand ring with background offset (notification-dot pattern).
|
|
2709
2709
|
* @default false
|
|
2710
2710
|
*/
|
|
2711
2711
|
"selected"?: boolean;
|
|
@@ -4,6 +4,8 @@ export { SHELL_BAR_NAV_VT_NAME, SHELL_NAV_REVEAL_DURATION_VAR, SHELL_NAV_REVEAL_
|
|
|
4
4
|
export type { ShellNavRevealOrigin } from './shell-view-transition';
|
|
5
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, readShellViewportDimensions, } from './shell-gradient';
|
|
6
6
|
export type { ShellGradientLayout, ShellViewportDimensions } from './shell-gradient';
|
|
7
|
+
export type { MenuPlacement } from '../components/Menu/menu-placement';
|
|
8
|
+
export { PANEL_NAV_USER_MENU_PLACEMENT, } from '../components/Menu/menu-placement';
|
|
7
9
|
export type { ShellGradientPreset } from './shell-gradient-presets';
|
|
8
10
|
export { DEFAULT_SHELL_GRADIENT_PRESET, SHELL_GRADIENT_PRESETS, SHELL_GRADIENT_PRESET_LABELS, buildShellRadialGradientForPreset, isShellGradientPreset, shellGradientPresetStopToken, } from './shell-gradient-presets';
|
|
9
11
|
export type { ChromeTransitionDetail, ChromeTransitionSource } from './chrome-transition';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ds-mo/ui",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.6.1",
|
|
4
4
|
"description": "CompoMo — composable web components styled with TokoMo design tokens",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"web-components",
|
|
@@ -67,9 +67,9 @@
|
|
|
67
67
|
"prebuild": "node scripts/generate-icon-catalog.mjs",
|
|
68
68
|
"pretypecheck": "node scripts/generate-icon-catalog.mjs",
|
|
69
69
|
"pretest": "node scripts/generate-icon-catalog.mjs",
|
|
70
|
-
"build": "stencil build && node scripts/patch-index-types.mjs && node scripts/verify-icons-externalized.mjs && node scripts/write-build-stamp.mjs",
|
|
70
|
+
"build": "stencil build && node scripts/patch-index-types.mjs && node scripts/patch-components-d-exports.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-shortcuts.test.ts tests/shell-gradient-presets.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",
|
|
72
|
+
"test": "node --import tsx/esm --test tests/panel-nav-utils.test.ts tests/panel-tools.test.ts tests/shell-shortcuts.test.ts tests/shell-gradient-presets.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/menu-placement.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",
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { TOKEN_DEFAULTS } from '../../utils';
|
|
2
|
+
import type { MenuAlign, MenuSide } from './menu-position';
|
|
3
|
+
|
|
4
|
+
/** Placement props bindable on `ds-menu` (side, align, offsets). */
|
|
5
|
+
export interface MenuPlacement {
|
|
6
|
+
side: MenuSide;
|
|
7
|
+
align: MenuAlign;
|
|
8
|
+
sideOffset: number | string;
|
|
9
|
+
alignOffset: number | string;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Canonical `ds-menu` placement for the **panel-nav footer user menu**.
|
|
14
|
+
* Anchor from `dsNavUserAction` detail `{ anchor }` (id `ds-panel-nav-user-menu-anchor`).
|
|
15
|
+
*
|
|
16
|
+
* Not for BarNav overflow menus — those use `side="bottom"` / `align="end"` internally.
|
|
17
|
+
* Use `align: 'end'` so tall footer menus bottom-align on the anchor and grow upward.
|
|
18
|
+
*/
|
|
19
|
+
export const PANEL_NAV_USER_MENU_PLACEMENT = {
|
|
20
|
+
side: 'right',
|
|
21
|
+
align: 'end',
|
|
22
|
+
sideOffset: TOKEN_DEFAULTS.space050,
|
|
23
|
+
alignOffset: 0,
|
|
24
|
+
} as const satisfies MenuPlacement;
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import type { ShellGradientPreset } from '../../nav/shell-gradient-presets';
|
|
2
2
|
|
|
3
3
|
export type { MenuAlign, MenuSide } from './menu-position';
|
|
4
|
+
export {
|
|
5
|
+
PANEL_NAV_USER_MENU_PLACEMENT,
|
|
6
|
+
type MenuPlacement,
|
|
7
|
+
} from './menu-placement';
|
|
4
8
|
|
|
5
9
|
export interface MenuItemData {
|
|
6
10
|
label: string;
|
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
} from './panel-nav-utils';
|
|
12
12
|
import {
|
|
13
13
|
PANEL_NAV_USER_MENU_ANCHOR_ID,
|
|
14
|
+
PANEL_NAV_USER_MENU_PLACEMENT,
|
|
14
15
|
type PanelNavGroup,
|
|
15
16
|
type PanelNavItem,
|
|
16
17
|
type PanelNavRouterMode,
|
|
@@ -336,7 +337,7 @@ export class PanelNav {
|
|
|
336
337
|
}
|
|
337
338
|
if (index === this.getUserRovingIndex()) {
|
|
338
339
|
const anchor = this.el.querySelector(`#${PANEL_NAV_USER_MENU_ANCHOR_ID}`) as HTMLElement | null;
|
|
339
|
-
if (anchor) this.dsNavUserAction.emit({ anchor });
|
|
340
|
+
if (anchor) this.dsNavUserAction.emit({ anchor, menuPlacement: PANEL_NAV_USER_MENU_PLACEMENT });
|
|
340
341
|
}
|
|
341
342
|
}
|
|
342
343
|
|
|
@@ -421,7 +422,7 @@ export class PanelNav {
|
|
|
421
422
|
|
|
422
423
|
private handleUserAction(e: MouseEvent) {
|
|
423
424
|
const anchor = e.currentTarget as HTMLElement;
|
|
424
|
-
this.dsNavUserAction.emit({ anchor });
|
|
425
|
+
this.dsNavUserAction.emit({ anchor, menuPlacement: PANEL_NAV_USER_MENU_PLACEMENT });
|
|
425
426
|
}
|
|
426
427
|
|
|
427
428
|
private clearEdgeOverlayTimer() {
|
|
@@ -4,7 +4,7 @@ export type {
|
|
|
4
4
|
PanelNavGroup,
|
|
5
5
|
PanelNavUserActionDetail,
|
|
6
6
|
} from './panel-nav-types';
|
|
7
|
-
export { PANEL_NAV_USER_MENU_ANCHOR_ID } from './panel-nav-types';
|
|
7
|
+
export { PANEL_NAV_USER_MENU_ANCHOR_ID, PANEL_NAV_USER_MENU_PLACEMENT } from './panel-nav-types';
|
|
8
8
|
export type { NavChromeStyle } from '../../nav/nav-chrome';
|
|
9
9
|
export {
|
|
10
10
|
NAV_STYLE_HINT_ATTR,
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import type { MenuPlacement } from '../Menu/menu-placement';
|
|
2
|
+
import { PANEL_NAV_USER_MENU_PLACEMENT } from '../Menu/menu-placement';
|
|
1
3
|
import type { NavChromeStyle } from '../../nav/nav-chrome';
|
|
2
4
|
|
|
3
5
|
export type { NavChromeStyle };
|
|
@@ -27,4 +29,8 @@ export const PANEL_NAV_USER_MENU_ANCHOR_ID = 'ds-panel-nav-user-menu-anchor';
|
|
|
27
29
|
/** Detail for `dsNavUserAction` — anchor element for an external `ds-menu`. */
|
|
28
30
|
export interface PanelNavUserActionDetail {
|
|
29
31
|
anchor: HTMLElement;
|
|
32
|
+
/** Recommended `ds-menu` placement — spread onto external user menu instance. */
|
|
33
|
+
menuPlacement: MenuPlacement;
|
|
30
34
|
}
|
|
35
|
+
|
|
36
|
+
export { PANEL_NAV_USER_MENU_PLACEMENT };
|
|
@@ -5,6 +5,7 @@ import {
|
|
|
5
5
|
DEFAULT_SHELL_GRADIENT_PRESET,
|
|
6
6
|
type ShellGradientPreset,
|
|
7
7
|
} from './shell-gradient-swatch-types';
|
|
8
|
+
import { SHELL_GRADIENT_OPACITY } from '../../nav/shell-gradient';
|
|
8
9
|
|
|
9
10
|
@Component({
|
|
10
11
|
tag: 'ds-shell-gradient-swatch',
|
|
@@ -15,7 +16,7 @@ export class ShellGradientSwatch {
|
|
|
15
16
|
/** Wash preset this orb previews. */
|
|
16
17
|
@Prop({ reflect: true }) preset: ShellGradientPreset = DEFAULT_SHELL_GRADIENT_PRESET;
|
|
17
18
|
|
|
18
|
-
/** Selected state —
|
|
19
|
+
/** Selected state — brand ring with background offset (notification-dot pattern). */
|
|
19
20
|
@Prop({ reflect: true }) selected = false;
|
|
20
21
|
|
|
21
22
|
@Prop() inactive = false;
|
|
@@ -41,7 +42,10 @@ export class ShellGradientSwatch {
|
|
|
41
42
|
'ds-focus-ring-inset': true,
|
|
42
43
|
'shell-gradient-swatch--selected': this.selected,
|
|
43
44
|
}}
|
|
44
|
-
style={{
|
|
45
|
+
style={{
|
|
46
|
+
'--_swatch-gradient-image': buildShellRadialGradientForPreset(this.preset),
|
|
47
|
+
'--ds-shell-gradient-opacity': SHELL_GRADIENT_OPACITY,
|
|
48
|
+
}}
|
|
45
49
|
aria-label={label}
|
|
46
50
|
aria-pressed={this.selected ? 'true' : 'false'}
|
|
47
51
|
disabled={this.inactive}
|
package/src/wc/components.d.ts
CHANGED
|
@@ -830,7 +830,7 @@ export namespace Components {
|
|
|
830
830
|
*/
|
|
831
831
|
"preset": ShellGradientPreset1;
|
|
832
832
|
/**
|
|
833
|
-
* Selected state —
|
|
833
|
+
* Selected state — brand ring with background offset (notification-dot pattern).
|
|
834
834
|
* @default false
|
|
835
835
|
*/
|
|
836
836
|
"selected": boolean;
|
|
@@ -2705,7 +2705,7 @@ declare namespace LocalJSX {
|
|
|
2705
2705
|
*/
|
|
2706
2706
|
"preset"?: ShellGradientPreset1;
|
|
2707
2707
|
/**
|
|
2708
|
-
* Selected state —
|
|
2708
|
+
* Selected state — brand ring with background offset (notification-dot pattern).
|
|
2709
2709
|
* @default false
|
|
2710
2710
|
*/
|
|
2711
2711
|
"selected"?: boolean;
|
|
@@ -3499,3 +3499,6 @@ declare module "@stencil/core" {
|
|
|
3499
3499
|
}
|
|
3500
3500
|
}
|
|
3501
3501
|
}
|
|
3502
|
+
// --- menu placement re-exports (patch-components-d-exports.mjs) ---
|
|
3503
|
+
export { PANEL_NAV_USER_MENU_PLACEMENT, type MenuPlacement } from "./components/Menu/menu-types";
|
|
3504
|
+
|
package/src/wc/nav/index.ts
CHANGED
|
@@ -37,6 +37,10 @@ export {
|
|
|
37
37
|
readShellViewportDimensions,
|
|
38
38
|
} from './shell-gradient';
|
|
39
39
|
export type { ShellGradientLayout, ShellViewportDimensions } from './shell-gradient';
|
|
40
|
+
export type { MenuPlacement } from '../components/Menu/menu-placement';
|
|
41
|
+
export {
|
|
42
|
+
PANEL_NAV_USER_MENU_PLACEMENT,
|
|
43
|
+
} from '../components/Menu/menu-placement';
|
|
40
44
|
export type { ShellGradientPreset } from './shell-gradient-presets';
|
|
41
45
|
export {
|
|
42
46
|
DEFAULT_SHELL_GRADIENT_PRESET,
|
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
const o=["cool","neutral","warm"];const r="neutral";const n={cool:"Cool",neutral:"Neutral",warm:"Warm"};const t={cool:"var(--color-color-intent-blue-strong-background)",neutral:"var(--color-color-intent-grey-strong-background)",warm:"var(--color-color-intent-yellow-strong-background)"};const a="100% 100% at 0% 0%";function c(o){return t[o]}function l(o){const r=c(o);return`radial-gradient(${a}, var(--color-background-transparent) 0%, ${r} 100%)`}export{r as D,o as S,n as a,l as b};
|
|
2
|
-
//# sourceMappingURL=p-BU5fKLdO.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"names":["SHELL_GRADIENT_PRESETS","DEFAULT_SHELL_GRADIENT_PRESET","SHELL_GRADIENT_PRESET_LABELS","cool","neutral","warm","SHELL_GRADIENT_PRESET_STOP","GRADIENT_GEOMETRY","shellGradientPresetStopToken","preset","buildShellRadialGradientForPreset","stop"],"sources":["src/wc/nav/shell-gradient-presets.ts"],"sourcesContent":["/** Built-in shell chrome wash presets — cool (blue), neutral (grey), warm (yellow). */\nexport type ShellGradientPreset = 'cool' | 'neutral' | 'warm';\n\nexport const SHELL_GRADIENT_PRESETS: ShellGradientPreset[] = ['cool', 'neutral', 'warm'];\n\n/** Default wash when `gradient-preset` is omitted. */\nexport const DEFAULT_SHELL_GRADIENT_PRESET: ShellGradientPreset = 'neutral';\n\nexport const SHELL_GRADIENT_PRESET_LABELS: Record<ShellGradientPreset, string> = {\n cool: 'Cool',\n neutral: 'Neutral',\n warm: 'Warm',\n};\n\nconst SHELL_GRADIENT_PRESET_STOP: Record<ShellGradientPreset, string> = {\n cool: 'var(--color-color-intent-blue-strong-background)',\n neutral: 'var(--color-color-intent-grey-strong-background)',\n warm: 'var(--color-color-intent-yellow-strong-background)',\n};\n\nconst GRADIENT_GEOMETRY = '100% 100% at 0% 0%';\n\nexport function shellGradientPresetStopToken(preset: ShellGradientPreset): string {\n return SHELL_GRADIENT_PRESET_STOP[preset];\n}\n\nexport function isShellGradientPreset(value: string): value is ShellGradientPreset {\n return (SHELL_GRADIENT_PRESETS as string[]).includes(value);\n}\n\n/** Radial wash for a preset — transparent at top-left into the intent stop. */\nexport function buildShellRadialGradientForPreset(preset: ShellGradientPreset): string {\n const stop = shellGradientPresetStopToken(preset);\n return `radial-gradient(${GRADIENT_GEOMETRY}, var(--color-background-transparent) 0%, ${stop} 100%)`;\n}\n"],"mappings":"AAGO,MAAMA,EAAgD,CAAC,OAAQ,UAAW,QAG1E,MAAMC,EAAqD,UAE3D,MAAMC,EAAoE,CAC/EC,KAAM,OACNC,QAAS,UACTC,KAAM,QAGR,MAAMC,EAAkE,CACtEH,KAAM,mDACNC,QAAS,mDACTC,KAAM,sDAGR,MAAME,EAAoB,qBAEpB,SAAUC,EAA6BC,GAC3C,OAAOH,EAA2BG,EACpC,CAOM,SAAUC,EAAkCD,GAChD,MAAME,EAAOH,EAA6BC,GAC1C,MAAO,mBAAmBF,8CAA8DI,SAC1F,Q","ignoreList":[]}
|
|
@@ -1 +0,0 @@
|
|
|
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_DEFAULTS","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","popupFallbackWidthPx","menuWidthXs","popupFallbackHeightPx","menuFallbackHeight","resolveCssTimeMs","motionShort3","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';\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\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-3) 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(--dimension-size-400);\n padding: var(--dimension-space-050) var(--dimension-space-075);\n -webkit-user-select: none;\n user-select: none;\n}\n\n.section-label {\n color: var(--color-foreground-secondary);\n}\n\n.menu-section--gradient-picker {\n padding-bottom: var(--dimension-space-050);\n}\n\n/* ── Items ───────────────────────────────────────────────────────────────── */\n\n.menu-item {\n display: flex;\n align-items: center;\n gap: var(--dimension-space-050);\n position: relative;\n width: 100%;\n min-height: var(--dimension-size-400);\n padding: var(--dimension-space-075);\n box-sizing: border-box;\n border: none;\n border-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-bg: var(--color-interaction-hover);\n --_pressed-bg: var(--color-interaction-pressed);\n --_selected-bg: var(--color-interaction-active);\n}\n\n.menu-item:has(.menu-item__subtext) {\n align-items: flex-start;\n}\n\n/* Interaction layer — same ::after pattern as tabs / BarNav trigger */\n.menu-item::after {\n content: '';\n position: absolute;\n inset: 0;\n border-radius: inherit;\n background: transparent;\n pointer-events: none;\n transition: none;\n}\n\n.menu-item--selected::after {\n background: var(--_selected-bg);\n transition: none;\n}\n\n.menu-item:hover:not(:disabled):not(.menu-item--selected)::after {\n background: var(--_hover-bg);\n transition: none;\n}\n\n.menu-item:active:not(:disabled)::after {\n background: var(--_pressed-bg);\n transition: none;\n}\n\n.menu-item--inactive {\n opacity: 0.5;\n cursor: not-allowed;\n pointer-events: none;\n}\n\n.menu-item--destructive .menu-item__label {\n color: var(--color-foreground-bold-negative) !important;\n}\n\n.menu-item__content {\n position: relative;\n z-index: 1;\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 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 color: var(--color-foreground-secondary);\n line-height: 1.4;\n}\n\n/* ── Toggle suffix ───────────────────────────────────────────────────────── */\n\n.menu-item__toggle {\n position: relative;\n z-index: 1;\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_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_DEFAULTS.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.motionShort3, 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 this.close();\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\" 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-focus-ring-inset': true,\n 'ds-focus-ring--visible': isFocused && this.focusRingVisible,\n 'menu-item--selected': !!item.isSelected,\n 'menu-item--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\">\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 }} 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 +0,0 @@
|
|
|
1
|
-
import{p as s,H as e,c as i,h as t,a as r,t as o}from"./index.js";import{D as n,a,b as d}from"./p-BU5fKLdO.js";const c=()=>`.sc-ds-shell-gradient-swatch-h{--ds-focus-ring-width:var(--dimension-stroke-width-025, 2px);--ds-focus-ring-offset:var(--dimension-space-025, 2px);--ds-focus-ring-color:var(--color-interaction-focus)}.ds-focus-ring.sc-ds-shell-gradient-swatch:focus-visible{outline:var(--ds-focus-ring-width) solid var(--ds-focus-ring-color);outline-offset:var(--ds-focus-ring-offset)}.ds-focus-ring.ds-focus-ring--visible.sc-ds-shell-gradient-swatch{outline:var(--ds-focus-ring-width) solid var(--ds-focus-ring-color);outline-offset:var(--ds-focus-ring-offset)}.ds-focus-ring-inset.sc-ds-shell-gradient-swatch:focus-visible{outline:none}.ds-focus-ring-inset.sc-ds-shell-gradient-swatch:focus-visible::after{outline:var(--ds-focus-ring-width) solid var(--ds-focus-ring-color);outline-offset:calc(-1 * var(--ds-focus-ring-width))}.ds-focus-ring-inset.ds-focus-ring--visible.sc-ds-shell-gradient-swatch{outline:none}.ds-focus-ring-inset.ds-focus-ring--visible.sc-ds-shell-gradient-swatch::after{outline:var(--ds-focus-ring-width) solid var(--ds-focus-ring-color);outline-offset:calc(-1 * var(--ds-focus-ring-width))}.sc-ds-shell-gradient-swatch-h{display:inline-flex}.shell-gradient-swatch.sc-ds-shell-gradient-swatch{position:relative;box-sizing:border-box;width:var(--dimension-size-400);height:var(--dimension-size-400);padding:0;border:none;border-radius:var(--dimension-radius-half);background-color:var(--color-background-secondary);background-repeat:no-repeat;background-size:cover;background-position:0 0;cursor:pointer;flex-shrink:0}.shell-gradient-swatch.sc-ds-shell-gradient-swatch:disabled{opacity:0.5;cursor:not-allowed}.shell-gradient-swatch--selected.sc-ds-shell-gradient-swatch::after{content:'';position:absolute;inset:0;border-radius:inherit;box-shadow:inset 0 0 0 var(--dimension-stroke-width-025) var(--color-border-bold-brand);pointer-events:none}.shell-gradient-swatch.sc-ds-shell-gradient-swatch:not(.shell-gradient-swatch--selected):hover:not(:disabled)::after{content:'';position:absolute;inset:0;border-radius:inherit;background:var(--color-interaction-hover);pointer-events:none}`;const l=s(class s extends e{constructor(s){super();if(s!==false){this.__registerHost()}this.dsSelect=i(this,"dsSelect");this.preset=n;this.selected=false;this.inactive=false;this.handleClick=()=>{if(this.inactive)return;this.dsSelect.emit(this.preset)}}render(){const s=this.ariaLabel??a[this.preset];return t(r,{key:"6486c517e186071b6f756f3561e073367107274b"},t("button",{key:"8eb09042ad6d120c8717a206fa8dc8aaf289360f",type:"button",class:{"shell-gradient-swatch":true,"ds-focus-ring-inset":true,"shell-gradient-swatch--selected":this.selected},style:{backgroundImage:d(this.preset)},"aria-label":s,"aria-pressed":this.selected?"true":"false",disabled:this.inactive,onClick:this.handleClick}))}static get style(){return c()}},[2,"ds-shell-gradient-swatch",{preset:[513],selected:[516],inactive:[4],ariaLabel:[1,"aria-label"]}]);function h(){if(typeof customElements==="undefined"){return}const s=["ds-shell-gradient-swatch"];s.forEach((s=>{switch(s){case"ds-shell-gradient-swatch":if(!customElements.get(o(s))){customElements.define(o(s),l)}break}}))}h();export{l as S,h as d};
|
|
2
|
-
//# sourceMappingURL=p-DKG6W4pb.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"names":["shellGradientSwatchCss","ShellGradientSwatch","__stencil_proxyCustomElement","HTMLElement","constructor","registerHost","this","preset","DEFAULT_SHELL_GRADIENT_PRESET","selected","inactive","handleClick","dsSelect","emit","render","label","ariaLabel","SHELL_GRADIENT_PRESET_LABELS","h","Host","key","type","class","style","backgroundImage","buildShellRadialGradientForPreset","disabled","onClick"],"sources":["src/wc/components/ShellGradientSwatch/ShellGradientSwatch.css?tag=ds-shell-gradient-swatch&encapsulation=scoped","src/wc/components/ShellGradientSwatch/ShellGradientSwatch.tsx"],"sourcesContent":["@import '../../utils/focus-ring.css';\n\n:host {\n display: inline-flex;\n}\n\n.shell-gradient-swatch {\n position: relative;\n box-sizing: border-box;\n width: var(--dimension-size-400);\n height: var(--dimension-size-400);\n padding: 0;\n border: none;\n border-radius: var(--dimension-radius-half);\n background-color: var(--color-background-secondary);\n background-repeat: no-repeat;\n background-size: cover;\n background-position: 0 0;\n cursor: pointer;\n flex-shrink: 0;\n}\n\n.shell-gradient-swatch:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n}\n\n.shell-gradient-swatch--selected::after {\n content: '';\n position: absolute;\n inset: 0;\n border-radius: inherit;\n box-shadow: inset 0 0 0 var(--dimension-stroke-width-025) var(--color-border-bold-brand);\n pointer-events: none;\n}\n\n.shell-gradient-swatch:not(.shell-gradient-swatch--selected):hover:not(:disabled)::after {\n content: '';\n position: absolute;\n inset: 0;\n border-radius: inherit;\n background: var(--color-interaction-hover);\n pointer-events: none;\n}\n","import { Component, Prop, Event, EventEmitter, h, Host } from '@stencil/core';\nimport {\n SHELL_GRADIENT_PRESET_LABELS,\n buildShellRadialGradientForPreset,\n DEFAULT_SHELL_GRADIENT_PRESET,\n type ShellGradientPreset,\n} from './shell-gradient-swatch-types';\n\n@Component({\n tag: 'ds-shell-gradient-swatch',\n styleUrl: 'ShellGradientSwatch.css',\n scoped: true,\n})\nexport class ShellGradientSwatch {\n /** Wash preset this orb previews. */\n @Prop({ reflect: true }) preset: ShellGradientPreset = DEFAULT_SHELL_GRADIENT_PRESET;\n\n /** Selected state — shows an inset brand ring. */\n @Prop({ reflect: true }) selected = false;\n\n @Prop() inactive = false;\n\n @Prop({ attribute: 'aria-label' }) ariaLabel: string | undefined;\n\n @Event() dsSelect!: EventEmitter<ShellGradientPreset>;\n\n private handleClick = () => {\n if (this.inactive) return;\n this.dsSelect.emit(this.preset);\n };\n\n render() {\n const label = this.ariaLabel ?? SHELL_GRADIENT_PRESET_LABELS[this.preset];\n\n return (\n <Host>\n <button\n type=\"button\"\n class={{\n 'shell-gradient-swatch': true,\n 'ds-focus-ring-inset': true,\n 'shell-gradient-swatch--selected': this.selected,\n }}\n style={{ backgroundImage: buildShellRadialGradientForPreset(this.preset) }}\n aria-label={label}\n aria-pressed={this.selected ? 'true' : 'false'}\n disabled={this.inactive}\n onClick={this.handleClick}\n />\n </Host>\n );\n }\n}\n"],"mappings":"+GAAA,MAAMA,EAAyB,IAAM,0iE,MCaxBC,EAAmBC,EAAA,MAAAD,UAAAE,EALhC,WAAAC,CAAAC,G,6EAO2BC,KAAAC,OAA8BC,EAG9BF,KAAAG,SAAW,MAE5BH,KAAAI,SAAW,MAMXJ,KAAAK,YAAc,KACpB,GAAIL,KAAKI,SAAU,OACnBJ,KAAKM,SAASC,KAAKP,KAAKC,OAAO,CAwBlC,CArBC,MAAAO,GACE,MAAMC,EAAQT,KAAKU,WAAaC,EAA6BX,KAAKC,QAElE,OACEW,EAACC,EAAI,CAAAC,IAAA,4CACHF,EAAA,UAAAE,IAAA,2CACEC,KAAK,SACLC,MAAO,CACL,wBAAyB,KACzB,sBAAuB,KACvB,kCAAmChB,KAAKG,UAE1Cc,MAAO,CAAEC,gBAAiBC,EAAkCnB,KAAKC,SAAS,aAC9DQ,EAAK,eACHT,KAAKG,SAAW,OAAS,QACvCiB,SAAUpB,KAAKI,SACfiB,QAASrB,KAAKK,c","ignoreList":[]}
|
|
File without changes
|