@rcarls/rc-adaptive-menu 0.5.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.
- package/README.md +74 -0
- package/dist/custom-elements.json +906 -0
- package/dist/rc-adaptive-menu-BwvfkD_X.js +589 -0
- package/dist/rc-adaptive-menu-BwvfkD_X.js.map +1 -0
- package/dist/rc-adaptive-menu-define.js +6 -0
- package/dist/rc-adaptive-menu-define.js.map +1 -0
- package/dist/rc-adaptive-menu.js +5 -0
- package/dist/rc-adaptive-menu.js.map +1 -0
- package/dist/types/packages/rc-adaptive-menu/src/define.d.ts +1 -0
- package/dist/types/packages/rc-adaptive-menu/src/index.d.ts +1 -0
- package/dist/types/packages/rc-adaptive-menu/src/rc-adaptive-menu.d.ts +180 -0
- package/dist/types/packages/rc-adaptive-menu/src/rc-adaptive-menu.styles.d.ts +2 -0
- package/package.json +65 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rc-adaptive-menu-BwvfkD_X.js","sources":["../src/rc-adaptive-menu.styles.ts","../src/rc-adaptive-menu.ts"],"sourcesContent":["import { css } from 'lit';\n\nexport const adaptiveMenuStyles = css`\n :host {\n display: block;\n inline-size: fit-content;\n /*\n * Raised past 100% by exactly the overlap amount so the cap only ever\n * bounds the host's own visible content, not the content plus the\n * intentional negative-margin overhang below. Inside a shrink-to-fit\n * flex/grid ancestor (a toolbar's trailing group, for example) the\n * ancestor's own auto size already accounts for the negative margin,\n * so a plain 100% cap would re-clamp the host down to that\n * already-shrunk size and silently cancel the overlap out from under\n * it — confirmed live: with a plain 100% cap the host visibly shrank\n * instead of overlapping.\n */\n max-inline-size: calc(\n 100% + var(--rc-adaptive-menu-touch-target-overlap-inline-start, 0px) +\n var(--rc-adaptive-menu-touch-target-overlap-inline-end, 0px)\n );\n min-inline-size: 0;\n /*\n * Zero by default. The overflow trigger's own touch-target inflation\n * (see #overflow-trigger-target below) is what typically leaves dead\n * space at a real edge, such as a toolbar's trailing-most control — but\n * the margin has to live on the host, not on that internal wrapper:\n * #root just lays out flush with the host's own edges, so a margin\n * placed there has no visible effect on where the host itself renders\n * in an outer flex/grid container. The host's own box is\n * fit-content-sized and is what an outer container's own trailing\n * alignment actually positions, so this is the point that needs to\n * move. A theme or consumer sets one of these on a host that sits at a\n * real edge (no neighbor on that side) to let it overlap into whatever\n * sits just outside, such as a container's own edge padding, instead of\n * also reserving layout space there — the same pattern as rc-button's\n * and rc-menu-button's own touch-target overlap tokens.\n */\n margin-inline-start: calc(-1 * var(--rc-adaptive-menu-touch-target-overlap-inline-start, 0px));\n margin-inline-end: calc(-1 * var(--rc-adaptive-menu-touch-target-overlap-inline-end, 0px));\n }\n\n #root {\n display: flex;\n flex-direction: row;\n align-items: center;\n gap: var(--rc-adaptive-menu-gap, var(--rc-control-gap, 0.25em));\n min-inline-size: 0;\n max-inline-size: 100%;\n }\n\n :host([orientation='vertical']) {\n inline-size: 100%;\n }\n\n :host([orientation='vertical']) #root {\n flex-direction: column;\n align-items: stretch;\n min-block-size: 0;\n inline-size: 100%;\n block-size: 100%;\n }\n\n #actions,\n #overflow {\n display: contents;\n }\n\n #overflow-trigger-target {\n position: relative;\n flex: none;\n display: grid;\n place-items: center;\n box-sizing: border-box;\n min-inline-size: var(\n --rc-adaptive-menu-touch-target-inline-size,\n var(--rc-adaptive-menu-touch-target-block-size, 3rem)\n );\n min-block-size: var(--rc-adaptive-menu-touch-target-block-size, 3rem);\n }\n\n #overflow-trigger {\n position: relative;\n display: inline-grid;\n place-items: center;\n box-sizing: border-box;\n min-inline-size: var(--rc-adaptive-menu-trigger-inline-size, 2.5em);\n min-block-size: var(--rc-adaptive-menu-trigger-block-size, 2.5em);\n padding: var(--rc-adaptive-menu-trigger-padding, 0.5em);\n border: var(--rc-adaptive-menu-trigger-border, 1px solid ButtonBorder);\n border-radius: var(--rc-adaptive-menu-trigger-radius, var(--rc-control-radius, 0));\n background: var(--rc-adaptive-menu-trigger-background, ButtonFace);\n color: var(--rc-adaptive-menu-trigger-color, ButtonText);\n font: inherit;\n line-height: 1;\n cursor: pointer;\n }\n\n #overflow-trigger::before {\n content: '';\n position: absolute;\n inset-block: calc((var(--rc-adaptive-menu-touch-target-block-size, 3rem) - 100%) / -2);\n inset-inline: calc(\n (\n var(\n --rc-adaptive-menu-touch-target-inline-size,\n var(--rc-adaptive-menu-touch-target-block-size, 3rem)\n ) -\n 100%\n ) /\n -2\n );\n }\n\n :host(:not([data-overflow])) #overflow-trigger-target {\n position: fixed;\n visibility: hidden;\n pointer-events: none;\n }\n\n #overflow-trigger:focus-visible {\n outline: var(--rc-focus-ring, auto);\n outline-offset: var(--rc-focus-ring-offset, 0);\n }\n\n #overflow-icon {\n inline-size: var(--rc-adaptive-menu-icon-size, 1.5em);\n block-size: var(--rc-adaptive-menu-icon-size, 1.5em);\n fill: currentColor;\n }\n\n #popup {\n position: fixed;\n display: none;\n flex-direction: column;\n box-sizing: border-box;\n min-inline-size: var(--rc-adaptive-menu-popup-min-inline-size, 10em);\n max-inline-size: var(--rc-adaptive-menu-popup-max-inline-size, calc(100dvi - 0.5rem));\n max-block-size: var(--rc-adaptive-menu-popup-max-block-size, calc(100dvb - 0.5rem));\n margin: 0;\n padding-block: var(--rc-adaptive-menu-popup-padding-block, 0.25em);\n border: var(--rc-adaptive-menu-popup-border, var(--rc-border, 1px solid ButtonBorder));\n border-radius: var(--rc-adaptive-menu-popup-radius, var(--rc-control-radius, 0));\n background: var(--rc-adaptive-menu-popup-background, var(--rc-surface, Canvas));\n box-shadow: var(\n --rc-adaptive-menu-popup-shadow,\n var(--rc-shadow, 0 2px 8px color-mix(in srgb, CanvasText 15%, transparent))\n );\n color: var(--rc-adaptive-menu-popup-color, var(--rc-field-text, CanvasText));\n overflow: auto;\n overscroll-behavior: contain;\n scrollbar-gutter: stable;\n }\n\n #popup:is(:popover-open, [data-fallback-open]) {\n display: flex;\n }\n`;\n\nexport default adaptiveMenuStyles;\n","import { LitElement, html } from 'lit';\nimport { property, query } from 'lit/decorators.js';\n\nimport {\n AnchorController,\n isFocusable,\n keyNavigation,\n type KeyboardNavigationAction,\n} from '@rcarls/rc-common';\n\nimport adaptiveMenuStyles from './rc-adaptive-menu.styles.js';\n\n/** Detail payload for the `rc-adaptive-menu-toggle` event. */\nexport interface RCAdaptiveMenuToggleEvent {\n /** The open state requested by the user. */\n open: boolean;\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'rc-adaptive-menu': RCAdaptiveMenu;\n }\n\n interface HTMLElementEventMap {\n 'rc-adaptive-menu-toggle': CustomEvent<RCAdaptiveMenuToggleEvent>;\n }\n}\n\ntype AuthoredState = {\n slot: string;\n focusTarget: HTMLElement;\n orientation: string | null;\n role: string | null;\n tabindex: string | null;\n forcedOverflow: boolean;\n};\n\nconst ACTION_WRAPPERS = 'rc-button, rc-chip, rc-menu-button';\n\nconst LIGHT_DOM_CSS = `\n@layer rc-base {\n rc-adaptive-menu > [data-rc-action-promoted],\n rc-adaptive-menu > [data-rc-action-overflowed] {\n box-sizing: border-box;\n min-inline-size: 0;\n }\n\n rc-adaptive-menu > [data-rc-action-overflowed] {\n display: flex;\n inline-size: 100%;\n }\n\n rc-adaptive-menu > [data-rc-action-overflowed] > :is(button, a[href]),\n rc-adaptive-menu > :is(button, a[href])[data-rc-action-overflowed] {\n display: flex;\n align-items: center;\n gap: var(--rc-adaptive-menu-item-gap, var(--rc-item-gap, 0.5em));\n box-sizing: border-box;\n inline-size: 100%;\n min-block-size: var(--rc-adaptive-menu-item-min-block-size, 2.5em);\n padding: var(--rc-adaptive-menu-item-padding-block, var(--rc-item-padding-block, 0.3em))\n var(--rc-adaptive-menu-item-padding-inline, var(--rc-item-padding-inline, 0.75em));\n border: none;\n border-radius: 0;\n background: transparent;\n color: inherit;\n font: inherit;\n text-align: start;\n text-decoration: none;\n }\n\n rc-adaptive-menu [data-rc-menu-leading],\n rc-adaptive-menu [data-rc-menu-trailing] {\n flex: none;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n }\n\n rc-adaptive-menu [data-rc-menu-label] {\n flex: 1 1 auto;\n min-inline-size: 0;\n }\n\n rc-adaptive-menu [data-rc-menu-trailing],\n rc-adaptive-menu [data-rc-menu-shortcut],\n rc-adaptive-menu [data-menu-shortcut] {\n margin-inline-start: auto;\n }\n}\n`;\n\n/**\n * Priority-aware action toolbar that preserves authored light-DOM controls while moving\n * lower-priority actions into an overflow menu.\n *\n * Actions remain the same connected nodes in the same parent. The component\n * changes only their slot assignment and component-owned ARIA/focus state, so\n * framework listeners, native form behavior, dialog invokers, and references\n * continue to work after an action moves.\n *\n * Direct native buttons and links are supported. `rc-button`, `rc-chip`, and\n * `rc-menu-button` wrappers participate through their direct native trigger.\n * DOM order is the default priority; a numeric `data-priority` promotes larger\n * values first. Authoring `slot=\"overflow\"` keeps an action overflow-only. A\n * negative `data-priority` has the same effect: the action always overflows,\n * regardless of `max-shown`. The overflow menu itself renders in authored DOM\n * order (native slot distribution, not JS-ordered), so author negative-priority\n * actions after normal-priority ones to have them appear last.\n *\n * This is a purely declarative computation from authored state (`max-shown`,\n * `data-priority`, and authored `slot`) re-evaluated whenever that authored\n * state changes. The component performs no runtime size measurement: it does\n * not observe its own or its children's rendered size, and `max-shown`\n * actions are promoted regardless of whether they visually fit the available\n * space. A consumer that needs the promoted count to react to available\n * space — a responsive breakpoint, a container query, a window size class —\n * owns that decision itself and writes the resulting `max-shown` value down.\n *\n * @see {@link https://richardcarls.github.io/rc-webcomponents/components/rc-adaptive-menu rc-adaptive-menu docs}\n * @see {@link https://www.w3.org/WAI/ARIA/apg/patterns/toolbar/ WAI-ARIA Toolbar pattern}\n * @see {@link https://www.w3.org/WAI/ARIA/apg/patterns/menu-button/ WAI-ARIA Menu Button pattern}\n *\n * @slot default - Direct native actions or supported rc action wrappers.\n * @slot overflow - Overflow-only actions and actions re-slotted by the component.\n * @slot overflow-icon - Optional decorative icon for the overflow trigger.\n *\n * @fires rc-adaptive-menu-toggle - Fired when user interaction requests a change to `open`.\n *\n * @attr label - Accessible label for the toolbar. Defaults to `Actions`.\n * @attr max-shown - Maximum number of authored actions promoted ahead of the overflow trigger.\n * A static declarative cap, not a measurement of available space — actions beyond it move to\n * the overflow menu regardless of whether they'd visually fit. Defaults to 3.\n * @attr open - Whether the overflow menu is visible. Host writes are silent.\n * @attr default-open - Initial open state for uncontrolled usage.\n * @attr orientation - Toolbar layout and arrow-key axis: `horizontal` or `vertical`.\n * @attr overflow-label - Accessible label for the overflow trigger and popup. Defaults to `More actions`.\n *\n * @csspart root - Toolbar container.\n * @csspart overflow-trigger - Button that opens the overflow menu.\n * @csspart popup - Overflow menu popover.\n *\n * @cssprop [--rc-adaptive-menu-gap=var(--rc-control-gap)] - Gap between promoted actions and the overflow trigger.\n * @cssprop [--rc-adaptive-menu-trigger-inline-size=2.5em] - Overflow trigger minimum inline size.\n * @cssprop [--rc-adaptive-menu-trigger-block-size=2.5em] - Overflow trigger minimum block size.\n * @cssprop [--rc-adaptive-menu-touch-target-inline-size] - Minimum overflow trigger activation-area inline size. Defers to `--rc-adaptive-menu-touch-target-block-size` when unset.\n * @cssprop [--rc-adaptive-menu-touch-target-block-size=3rem] - Minimum overflow trigger activation-area block size.\n * @cssprop [--rc-adaptive-menu-touch-target-overlap-inline-start=0px] - Zero by default. A theme\n * or consumer sets this when the overflow trigger sits at a real leading edge (no neighbor on\n * that side) to let its touch-target inflation overlap into whatever sits just outside the\n * host, such as a container's own edge padding, instead of also reserving layout space there.\n * @cssprop [--rc-adaptive-menu-touch-target-overlap-inline-end=0px] - The trailing-edge\n * counterpart to `--rc-adaptive-menu-touch-target-overlap-inline-start` — the overflow trigger\n * is conventionally the trailing-most control in a toolbar, so this is the one commonly set.\n * @cssprop [--rc-adaptive-menu-trigger-padding=0.5em] - Overflow trigger padding.\n * @cssprop [--rc-adaptive-menu-trigger-border=1px solid ButtonBorder] - Overflow trigger border.\n * @cssprop [--rc-adaptive-menu-trigger-radius=var(--rc-control-radius)] - Overflow trigger radius.\n * @cssprop [--rc-adaptive-menu-trigger-background=ButtonFace] - Overflow trigger background.\n * @cssprop [--rc-adaptive-menu-trigger-color=ButtonText] - Overflow trigger color.\n * @cssprop [--rc-adaptive-menu-icon-size=1.5em] - Default overflow icon size.\n * @cssprop [--rc-adaptive-menu-popup-min-inline-size=10em] - Overflow popup minimum inline size.\n * @cssprop [--rc-adaptive-menu-popup-max-inline-size=calc(100dvi - 0.5rem)] - Overflow popup viewport-aware inline limit.\n * @cssprop [--rc-adaptive-menu-popup-max-block-size=calc(100dvb - 0.5rem)] - Overflow popup viewport-aware block limit.\n * @cssprop [--rc-adaptive-menu-popup-padding-block=0.25em] - Overflow popup block padding.\n * @cssprop [--rc-adaptive-menu-popup-border=var(--rc-border)] - Overflow popup border.\n * @cssprop [--rc-adaptive-menu-popup-radius=var(--rc-control-radius)] - Overflow popup radius.\n * @cssprop [--rc-adaptive-menu-popup-background=var(--rc-surface)] - Overflow popup background.\n * @cssprop [--rc-adaptive-menu-popup-shadow=var(--rc-shadow)] - Overflow popup shadow.\n * @cssprop [--rc-adaptive-menu-popup-color=var(--rc-field-text)] - Overflow popup foreground.\n * @cssprop [--rc-adaptive-menu-item-min-block-size=2.5em] - Overflow action minimum block size.\n * @cssprop [--rc-adaptive-menu-item-padding-block=var(--rc-item-padding-block)] - Overflow action block padding.\n * @cssprop [--rc-adaptive-menu-item-padding-inline=var(--rc-item-padding-inline)] - Overflow action inline padding.\n * @cssprop [--rc-adaptive-menu-item-gap=var(--rc-item-gap)] - Overflow action content gap.\n */\nexport class RCAdaptiveMenu extends LitElement {\n static override styles = adaptiveMenuStyles;\n\n private static readonly _styledRoots = new WeakSet<Document | ShadowRoot>();\n\n /** Accessible label for the toolbar. */\n @property({ type: String })\n label = 'Actions';\n\n /**\n * Maximum authored actions promoted ahead of the overflow trigger. A\n * static declarative cap, re-evaluated whenever it changes — not a\n * measurement of available space.\n */\n @property({ type: Number, attribute: 'max-shown' })\n maxShown = 3;\n\n private _open: boolean | undefined;\n private _defaultOpen = false;\n private _uncontrolledOpen: boolean | undefined;\n private _openInitialized = false;\n\n /** Whether the overflow menu is visible. Host writes are silent. */\n @property({ type: Boolean, reflect: true })\n get open(): boolean {\n return this._open ?? this._uncontrolledOpen ?? this._defaultOpen;\n }\n\n set open(value: boolean | undefined) {\n const oldValue = this.open;\n\n this._open = value;\n this._openInitialized = true;\n this.requestUpdate('open', oldValue);\n }\n\n /** Initial open state for uncontrolled usage. */\n @property({ type: Boolean, attribute: 'default-open' })\n get defaultOpen(): boolean {\n return this._defaultOpen;\n }\n\n set defaultOpen(value: boolean) {\n const oldValue = this._defaultOpen;\n\n this._defaultOpen = value;\n\n if (\n !this._openInitialized &&\n this._open === undefined &&\n this._uncontrolledOpen === undefined\n ) {\n this.requestUpdate('open', oldValue);\n }\n\n this.requestUpdate('defaultOpen', oldValue);\n }\n\n /** Toolbar layout and arrow-key navigation axis. */\n @property({ type: String, reflect: true })\n orientation: 'horizontal' | 'vertical' = 'horizontal';\n\n /** Accessible name for the overflow trigger and popup. */\n @property({ type: String, attribute: 'overflow-label' })\n overflowLabel = 'More actions';\n\n @query('#overflow-trigger')\n private _$trigger!: HTMLButtonElement;\n\n @query('#popup')\n private _$popup!: HTMLDivElement;\n\n private readonly _authored = new Map<HTMLElement, AuthoredState>();\n private _mutationObserver?: MutationObserver;\n private _applyingSlots = false;\n private _restoreFocusWhenClosed = false;\n private _focusWhenOpened: 'first' | 'last' | 'none' | null = null;\n\n private readonly _anchor = new AnchorController(this, {\n anchor: () => this._$trigger,\n floating: () => this._$popup,\n placement: 'bottom-end',\n offset: 4,\n shadowHost: () => this,\n });\n\n override connectedCallback(): void {\n super.connectedCallback();\n RCAdaptiveMenu._ensureBaseStyles(this.getRootNode() as Document | ShadowRoot);\n this.addEventListener('focusin', this._onFocusIn);\n this.ownerDocument.addEventListener('pointerdown', this._onDocumentPointerDown);\n this.ownerDocument.addEventListener('keydown', this._onDocumentKeydown);\n\n if (typeof MutationObserver === 'function') {\n this._mutationObserver = new MutationObserver(this._onMutation);\n\n this._mutationObserver.observe(this, {\n attributeFilter: ['aria-disabled', 'data-priority', 'disabled', 'slot'],\n childList: true,\n subtree: true,\n });\n }\n\n if (this.hasUpdated) {\n queueMicrotask(() => {\n if (!this.isConnected) {\n return;\n }\n\n this._syncActions();\n this._syncOpenState();\n });\n }\n }\n\n override disconnectedCallback(): void {\n this.removeEventListener('focusin', this._onFocusIn);\n this.ownerDocument.removeEventListener('pointerdown', this._onDocumentPointerDown);\n this.ownerDocument.removeEventListener('keydown', this._onDocumentKeydown);\n this._mutationObserver?.disconnect();\n this._mutationObserver = undefined;\n\n for (const [action, state] of this._authored) {\n this._restoreAction(action, state);\n }\n\n this._authored.clear();\n super.disconnectedCallback();\n }\n\n protected override firstUpdated(): void {\n this._syncActions();\n this._syncOpenState();\n\n if (import.meta.env.DEV && this.children.length === 0) {\n console.warn(\n '[rc-adaptive-menu] No direct actions found. Place native buttons, links, or supported rc action wrappers directly inside the component.',\n this,\n );\n }\n }\n\n protected override updated(changed: Map<PropertyKey, unknown>): void {\n if (changed.has('orientation')) {\n this._anchor.setOptions({\n placement: this.orientation === 'vertical' ? 'right-start' : 'bottom-end',\n });\n }\n\n if (changed.has('maxShown') || changed.has('orientation')) {\n this._applySlots();\n }\n\n if (changed.has('open')) {\n this._syncOpenState();\n }\n }\n\n /** Original authored action elements in DOM order. */\n get $actions(): HTMLElement[] {\n return this._entriesInDomOrder().map(([action]) => action);\n }\n\n /** Actions currently shown in the toolbar, in authored DOM order. */\n get $promotedActions(): HTMLElement[] {\n return this.$actions.filter((action) => action.hasAttribute('data-rc-action-promoted'));\n }\n\n /** Actions currently assigned to the overflow menu, in authored DOM order. */\n get $overflowedActions(): HTMLElement[] {\n return this.$actions.filter((action) => action.hasAttribute('data-rc-action-overflowed'));\n }\n\n /** Opens the overflow popup silently and optionally focuses an edge action. */\n openMenu(focus: 'first' | 'last' | 'none' = 'first'): void {\n if (!this.hasAttribute('data-overflow')) {\n return;\n }\n\n this._setProgrammaticOpen(true, false, focus);\n }\n\n /** Closes the overflow popup silently. */\n closeMenu(returnFocus = false): void {\n this._setProgrammaticOpen(false, returnFocus);\n }\n\n /** Toggles the overflow popup silently. */\n toggleMenu(focus: 'first' | 'last' | 'none' = 'first'): void {\n if (!this.open && !this.hasAttribute('data-overflow')) {\n return;\n }\n\n this._setProgrammaticOpen(!this.open, this.open, focus);\n }\n\n protected override render() {\n return html`\n <div\n id=\"root\"\n part=\"root\"\n role=\"toolbar\"\n aria-label=${this.label}\n aria-orientation=${this.orientation}\n ${keyNavigation(this._onToolbarNavigate, {\n navigationAxis: this.orientation,\n })}\n >\n <slot id=\"actions\" @slotchange=${this._onSlotChange}></slot>\n <span id=\"overflow-trigger-target\">\n <button\n id=\"overflow-trigger\"\n part=\"overflow-trigger\"\n type=\"button\"\n aria-haspopup=\"menu\"\n aria-expanded=\"false\"\n aria-controls=\"popup\"\n aria-label=${this.overflowLabel}\n title=${this.overflowLabel}\n ${keyNavigation(this._onTriggerNavigate, {\n navigationAxis: this.orientation,\n handleNavAxis: false,\n handleOpenAxis: true,\n handleEscape: this.open,\n })}\n @click=${this._onTriggerClick}\n >\n <slot name=\"overflow-icon\">\n <svg id=\"overflow-icon\" viewBox=\"0 0 24 24\" aria-hidden=\"true\">\n <circle cx=\"12\" cy=\"5\" r=\"2\"></circle>\n <circle cx=\"12\" cy=\"12\" r=\"2\"></circle>\n <circle cx=\"12\" cy=\"19\" r=\"2\"></circle>\n </svg>\n </slot>\n </button>\n </span>\n </div>\n <div\n id=\"popup\"\n part=\"popup\"\n popover=\"manual\"\n role=\"menu\"\n aria-label=${this.overflowLabel}\n ${keyNavigation(this._onOverflowNavigate, {\n navigationAxis: 'vertical',\n handleEscape: true,\n })}\n @click=${this._onPopupClick}\n >\n <slot id=\"overflow\" name=\"overflow\" @slotchange=${this._onSlotChange}></slot>\n </div>\n `;\n }\n\n private static _ensureBaseStyles(root: Document | ShadowRoot): void {\n if (RCAdaptiveMenu._styledRoots.has(root)) {\n return;\n }\n\n RCAdaptiveMenu._styledRoots.add(root);\n\n const $style = (root instanceof Document ? root : root.ownerDocument).createElement('style');\n\n $style.setAttribute('data-rc-light-dom-base', 'rc-adaptive-menu');\n $style.textContent = LIGHT_DOM_CSS;\n\n if (root instanceof Document) {\n root.head.append($style);\n } else {\n root.append($style);\n }\n }\n\n private _onSlotChange = (): void => {\n if (this._applyingSlots) {\n return;\n }\n\n queueMicrotask(() => {\n if (this.isConnected && !this._applyingSlots) {\n this._syncActions();\n }\n });\n };\n\n private _onMutation = (records: MutationRecord[]): void => {\n if (this._applyingSlots) {\n return;\n }\n\n if (records.some((record) => record.type === 'childList' || record.attributeName === 'slot')) {\n this._syncActions();\n\n return;\n }\n\n // A data-priority change reorders which actions are promoted; other\n // watched attributes (aria-disabled, disabled) don't affect slotting.\n if (records.some((record) => record.attributeName === 'data-priority')) {\n this._applySlots();\n\n return;\n }\n\n this._syncToolbarTabStops();\n };\n\n private _syncActions(): void {\n const current = new Set<HTMLElement>();\n\n for (const $child of Array.from(this.children)) {\n if (!($child instanceof HTMLElement) || $child.slot === 'overflow-icon') {\n continue;\n }\n\n const existing = this._authored.get($child);\n const authoredSlot = existing?.slot ?? $child.slot;\n\n if (authoredSlot && authoredSlot !== 'overflow') {\n if (import.meta.env.DEV && !existing) {\n console.warn(\n `[rc-adaptive-menu] Ignoring a child with the incompatible authored slot \"${authoredSlot}\". Actions may use only the default or overflow slot.`,\n $child,\n );\n }\n\n continue;\n }\n\n const $focusTarget = existing?.focusTarget ?? this._focusTarget($child);\n\n if (!$focusTarget) {\n if (import.meta.env.DEV && !existing) {\n console.warn(\n '[rc-adaptive-menu] Ignoring a direct child without a supported focusable action.',\n $child,\n );\n }\n\n continue;\n }\n\n current.add($child);\n\n if (!existing) {\n this._authored.set($child, {\n slot: authoredSlot,\n focusTarget: $focusTarget,\n orientation: $child.getAttribute('orientation'),\n role: $focusTarget.getAttribute('role'),\n tabindex: $focusTarget.getAttribute('tabindex'),\n forcedOverflow: authoredSlot === 'overflow',\n });\n }\n }\n\n for (const [$action, state] of this._authored) {\n if (!current.has($action)) {\n this._restoreAction($action, state);\n this._authored.delete($action);\n }\n }\n\n this._applySlots();\n }\n\n private _entriesInDomOrder(): [HTMLElement, AuthoredState][] {\n return Array.from(this.children).flatMap(($child) => {\n if (!($child instanceof HTMLElement)) {\n return [];\n }\n\n const state = this._authored.get($child);\n\n return state ? [[$child, state] as [HTMLElement, AuthoredState]] : [];\n });\n }\n\n /**\n * Assigns each authored action to the default (promoted) or overflow slot.\n * A purely declarative computation from `max-shown`, `data-priority`, and\n * authored `slot` — no size measurement of any kind.\n */\n private _applySlots(): void {\n const entries = this._entriesInDomOrder();\n\n const priorityOf = (action: HTMLElement): number => {\n const parsedPriority = Number(action.dataset.priority ?? 0);\n\n return Number.isFinite(parsedPriority) ? parsedPriority : 0;\n };\n\n // A negative data-priority forces overflow, the same as an authored\n // `slot=\"overflow\"`, regardless of max-shown.\n const isForcedOverflow = ([action, state]: [HTMLElement, AuthoredState]): boolean =>\n state.forcedOverflow || priorityOf(action) < 0;\n const candidates = entries.filter((entry) => !isForcedOverflow(entry));\n\n const ranked = candidates\n .map(([action], index) => ({\n action,\n index,\n priority: priorityOf(action),\n }))\n .sort((a, b) => b.priority - a.priority || a.index - b.index);\n\n const requestedCap = Number.isFinite(this.maxShown)\n ? Math.max(0, Math.trunc(this.maxShown))\n : candidates.length;\n const cap = Math.min(candidates.length, requestedCap);\n const promotedSet = new Set(ranked.slice(0, cap).map(({ action }) => action));\n const overflowed = entries\n .map(([action]) => action)\n .filter((action) => !promotedSet.has(action));\n const focusedAction = entries.find(\n ([, state]) => state.focusTarget === this.ownerDocument.activeElement,\n )?.[0];\n\n this.toggleAttribute('data-overflow', overflowed.length > 0);\n\n if (focusedAction && overflowed.includes(focusedAction)) {\n this._$trigger.focus();\n }\n\n this._applyingSlots = true;\n\n for (const [action, state] of entries) {\n if (promotedSet.has(action)) {\n action.slot = '';\n action.setAttribute('data-rc-action-promoted', '');\n action.removeAttribute('data-rc-action-overflowed');\n this._restoreOrientation(action, state);\n this._restoreRole(state);\n } else {\n action.slot = 'overflow';\n action.removeAttribute('data-rc-action-promoted');\n action.setAttribute('data-rc-action-overflowed', '');\n\n if (action.matches('rc-menu-button')) {\n action.setAttribute('orientation', 'vertical');\n }\n\n state.focusTarget.setAttribute('role', 'menuitem');\n state.focusTarget.setAttribute('tabindex', '-1');\n }\n }\n\n this._syncToolbarTabStops();\n\n queueMicrotask(() => {\n this._applyingSlots = false;\n });\n\n this._syncOpenState();\n }\n\n private _focusTarget($action: HTMLElement): HTMLElement | null {\n if ($action.matches(ACTION_WRAPPERS)) {\n return $action.querySelector<HTMLElement>(\n ':scope > button, :scope > a[href], :scope > [slot=\"trigger\"]',\n );\n }\n\n return $action.matches('button, a[href]') ? $action : null;\n }\n\n private _promotedFocusTargets(): HTMLElement[] {\n const targets = Array.from(this._authored.entries())\n .filter(([action]) => action.hasAttribute('data-rc-action-promoted'))\n .map(([, state]) => state.focusTarget)\n .filter(($target) => isFocusable($target));\n\n if (this.hasAttribute('data-overflow')) {\n targets.push(this._$trigger);\n }\n\n return targets;\n }\n\n private _overflowFocusTargets(): HTMLElement[] {\n return Array.from(this._authored.entries())\n .filter(([action]) => action.hasAttribute('data-rc-action-overflowed'))\n .map(([, state]) => state.focusTarget)\n .filter(($target) => isFocusable($target));\n }\n\n private _syncToolbarTabStops(active?: HTMLElement): void {\n const targets = this._promotedFocusTargets();\n const selected = active && targets.includes(active) ? active : targets[0];\n\n for (const target of targets) {\n target.setAttribute('tabindex', target === selected ? '0' : '-1');\n }\n }\n\n private _onFocusIn = (event: FocusEvent): void => {\n const $target = event.composedPath()[0];\n\n if ($target instanceof HTMLElement && this._promotedFocusTargets().includes($target)) {\n this._syncToolbarTabStops($target);\n }\n };\n\n private _onToolbarNavigate = (action: KeyboardNavigationAction): void => {\n this._focusByNavigationAction(action, this._promotedFocusTargets());\n };\n\n private _onTriggerNavigate = (action: KeyboardNavigationAction): void => {\n switch (action) {\n case 'open-to-first':\n this._requestUserOpen(true, false, 'first');\n break;\n case 'open-to-last':\n this._requestUserOpen(true, false, 'last');\n break;\n case 'escape':\n this._requestUserOpen(false, true);\n break;\n }\n };\n\n private _onOverflowNavigate = (action: KeyboardNavigationAction): void => {\n if (action === 'escape') {\n this._requestUserOpen(false, true);\n\n return;\n }\n\n this._focusByNavigationAction(action, this._overflowFocusTargets());\n };\n\n private _focusByNavigationAction(\n action: KeyboardNavigationAction,\n $targets: HTMLElement[],\n ): void {\n const $active = this.ownerDocument.activeElement;\n const index = $active instanceof HTMLElement ? $targets.indexOf($active) : -1;\n let $target: HTMLElement | undefined;\n\n switch (action) {\n case 'next':\n $target = $targets[(index + 1) % $targets.length];\n break;\n case 'prev':\n $target = $targets[(index - 1 + $targets.length) % $targets.length];\n break;\n case 'start':\n $target = $targets[0];\n break;\n case 'end':\n $target = $targets.at(-1);\n break;\n }\n\n $target?.focus();\n }\n\n private _onTriggerClick = (): void => {\n this._requestUserOpen(!this.open, false, 'none');\n };\n\n private _onDocumentPointerDown = (event: PointerEvent): void => {\n if (this.open && !event.composedPath().includes(this)) {\n this._requestUserOpen(false, false);\n }\n };\n\n private _onDocumentKeydown = (event: KeyboardEvent): void => {\n if (this.open && event.key === 'Escape' && !event.composedPath().includes(this)) {\n this._requestUserOpen(false, false);\n }\n };\n\n private _onPopupClick = (event: MouseEvent): void => {\n const path = event.composedPath();\n const entry = Array.from(this._authored.entries()).find(\n ([action, state]) =>\n action.hasAttribute('data-rc-action-overflowed') &&\n (path.includes(action) || path.includes(state.focusTarget)),\n );\n\n if (!entry || this._isDisabled(entry[1].focusTarget)) {\n return;\n }\n\n const $focusTarget = entry[1].focusTarget;\n\n // A menu-button trigger must keep the containing overflow menu open while\n // its submenu is shown. Leaf selections still dismiss the outer menu.\n if (path.includes($focusTarget) && $focusTarget.getAttribute('aria-haspopup') === 'menu') {\n return;\n }\n\n this._requestUserOpen(false, true);\n };\n\n private _setProgrammaticOpen(\n open: boolean,\n returnFocus: boolean,\n focus: 'first' | 'last' | 'none' = 'none',\n ): void {\n const oldValue = this.open;\n\n if (oldValue === open) {\n if (open) {\n this._focusOverflowEdge(focus);\n }\n\n return;\n }\n\n if (this._openInitialized) {\n this._open = open;\n } else {\n this._uncontrolledOpen = open;\n }\n\n this._restoreFocusWhenClosed = !open && returnFocus;\n this._focusWhenOpened = open ? focus : null;\n this.requestUpdate('open', oldValue);\n this._syncOpenState();\n }\n\n private _requestUserOpen(\n open: boolean,\n returnFocus: boolean,\n focus: 'first' | 'last' | 'none' = 'none',\n ): void {\n if (this.open === open) {\n return;\n }\n\n this._restoreFocusWhenClosed = !open && returnFocus;\n this._focusWhenOpened = open ? focus : null;\n\n if (this._open === undefined) {\n const oldValue = this.open;\n\n this._uncontrolledOpen = open;\n this.requestUpdate('open', oldValue);\n this._syncOpenState();\n }\n\n this.dispatchEvent(\n new CustomEvent<RCAdaptiveMenuToggleEvent>('rc-adaptive-menu-toggle', {\n bubbles: true,\n composed: true,\n detail: { open },\n }),\n );\n }\n\n private _syncOpenState(): void {\n const $popup = this._$popup as HTMLDivElement & {\n hidePopover?: () => void;\n showPopover?: () => void;\n };\n\n if (!$popup?.isConnected) {\n return;\n }\n\n if (this.open && this.hasAttribute('data-overflow')) {\n let shown = false;\n\n if (typeof $popup.showPopover === 'function') {\n try {\n $popup.showPopover();\n shown = true;\n } catch {\n // The fallback attribute provides the same visible state when the\n // Popover API exists only partially or rejects this element.\n }\n }\n\n $popup.toggleAttribute('data-fallback-open', !shown);\n this._$trigger.setAttribute('aria-expanded', 'true');\n this._anchor.update();\n\n if (this._focusWhenOpened !== null) {\n const focus = this._focusWhenOpened;\n\n this._focusWhenOpened = null;\n requestAnimationFrame(() => this._focusOverflowEdge(focus));\n }\n\n return;\n }\n\n if (typeof $popup.hidePopover === 'function') {\n try {\n $popup.hidePopover();\n } catch {\n // A closed or unsupported popover needs no additional native cleanup.\n }\n }\n\n $popup.removeAttribute('data-fallback-open');\n this._$trigger.setAttribute('aria-expanded', 'false');\n\n if (this._restoreFocusWhenClosed) {\n this._restoreFocusWhenClosed = false;\n this._$trigger.focus();\n }\n }\n\n private _focusOverflowEdge(focus: 'first' | 'last' | 'none'): void {\n if (!this.open || focus === 'none') {\n return;\n }\n\n const $items = this._overflowFocusTargets();\n const $target = focus === 'last' ? $items.at(-1) : $items[0];\n\n $target?.focus();\n }\n\n private _restoreAction(action: HTMLElement, state: AuthoredState): void {\n action.slot = state.slot;\n action.removeAttribute('data-rc-action-promoted');\n action.removeAttribute('data-rc-action-overflowed');\n this._restoreOrientation(action, state);\n this._restoreRole(state);\n\n if (state.tabindex === null) {\n state.focusTarget.removeAttribute('tabindex');\n } else {\n state.focusTarget.setAttribute('tabindex', state.tabindex);\n }\n }\n\n private _restoreOrientation(action: HTMLElement, state: AuthoredState): void {\n if (!action.matches('rc-menu-button')) {\n return;\n }\n\n if (state.orientation === null) {\n action.removeAttribute('orientation');\n } else {\n action.setAttribute('orientation', state.orientation);\n }\n }\n\n private _restoreRole(state: AuthoredState): void {\n if (state.role === null) {\n state.focusTarget.removeAttribute('role');\n } else {\n state.focusTarget.setAttribute('role', state.role);\n }\n }\n\n private _isDisabled(target: HTMLElement): boolean {\n return (\n (target instanceof HTMLButtonElement && target.disabled) ||\n target.getAttribute('aria-disabled') === 'true'\n );\n }\n}\n\nexport default RCAdaptiveMenu;\n"],"names":["adaptiveMenuStyles","css","ACTION_WRAPPERS","LIGHT_DOM_CSS","_RCAdaptiveMenu","_a","LitElement","AnchorController","records","record","event","$target","action","path","entry","state","$focusTarget","value","oldValue","changed","focus","returnFocus","html","keyNavigation","root","$style","current","$child","existing","authoredSlot","$action","entries","priorityOf","parsedPriority","isForcedOverflow","candidates","ranked","index","a","b","requestedCap","cap","promotedSet","overflowed","focusedAction","targets","isFocusable","active","selected","target","$targets","$active","open","$popup","shown","$items","__decorateClass","property","query","RCAdaptiveMenu"],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rc-adaptive-menu-define.js","sources":["../src/define.ts"],"sourcesContent":["import { RCAdaptiveMenu } from './rc-adaptive-menu.js';\n\nif (!customElements.get('rc-adaptive-menu')) {\n customElements.define('rc-adaptive-menu', RCAdaptiveMenu);\n}\n\nexport * from './index.js';\n"],"names":["RCAdaptiveMenu"],"mappings":";AAEK,eAAe,IAAI,kBAAkB,KACxC,eAAe,OAAO,oBAAoBA,CAAc;"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rc-adaptive-menu.js","sources":[],"sourcesContent":[],"names":[],"mappings":";"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './index.js';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './rc-adaptive-menu.js';
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
import { LitElement } from 'lit';
|
|
2
|
+
/** Detail payload for the `rc-adaptive-menu-toggle` event. */
|
|
3
|
+
export interface RCAdaptiveMenuToggleEvent {
|
|
4
|
+
/** The open state requested by the user. */
|
|
5
|
+
open: boolean;
|
|
6
|
+
}
|
|
7
|
+
declare global {
|
|
8
|
+
interface HTMLElementTagNameMap {
|
|
9
|
+
'rc-adaptive-menu': RCAdaptiveMenu;
|
|
10
|
+
}
|
|
11
|
+
interface HTMLElementEventMap {
|
|
12
|
+
'rc-adaptive-menu-toggle': CustomEvent<RCAdaptiveMenuToggleEvent>;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Priority-aware action toolbar that preserves authored light-DOM controls while moving
|
|
17
|
+
* lower-priority actions into an overflow menu.
|
|
18
|
+
*
|
|
19
|
+
* Actions remain the same connected nodes in the same parent. The component
|
|
20
|
+
* changes only their slot assignment and component-owned ARIA/focus state, so
|
|
21
|
+
* framework listeners, native form behavior, dialog invokers, and references
|
|
22
|
+
* continue to work after an action moves.
|
|
23
|
+
*
|
|
24
|
+
* Direct native buttons and links are supported. `rc-button`, `rc-chip`, and
|
|
25
|
+
* `rc-menu-button` wrappers participate through their direct native trigger.
|
|
26
|
+
* DOM order is the default priority; a numeric `data-priority` promotes larger
|
|
27
|
+
* values first. Authoring `slot="overflow"` keeps an action overflow-only. A
|
|
28
|
+
* negative `data-priority` has the same effect: the action always overflows,
|
|
29
|
+
* regardless of `max-shown`. The overflow menu itself renders in authored DOM
|
|
30
|
+
* order (native slot distribution, not JS-ordered), so author negative-priority
|
|
31
|
+
* actions after normal-priority ones to have them appear last.
|
|
32
|
+
*
|
|
33
|
+
* This is a purely declarative computation from authored state (`max-shown`,
|
|
34
|
+
* `data-priority`, and authored `slot`) re-evaluated whenever that authored
|
|
35
|
+
* state changes. The component performs no runtime size measurement: it does
|
|
36
|
+
* not observe its own or its children's rendered size, and `max-shown`
|
|
37
|
+
* actions are promoted regardless of whether they visually fit the available
|
|
38
|
+
* space. A consumer that needs the promoted count to react to available
|
|
39
|
+
* space — a responsive breakpoint, a container query, a window size class —
|
|
40
|
+
* owns that decision itself and writes the resulting `max-shown` value down.
|
|
41
|
+
*
|
|
42
|
+
* @see {@link https://richardcarls.github.io/rc-webcomponents/components/rc-adaptive-menu rc-adaptive-menu docs}
|
|
43
|
+
* @see {@link https://www.w3.org/WAI/ARIA/apg/patterns/toolbar/ WAI-ARIA Toolbar pattern}
|
|
44
|
+
* @see {@link https://www.w3.org/WAI/ARIA/apg/patterns/menu-button/ WAI-ARIA Menu Button pattern}
|
|
45
|
+
*
|
|
46
|
+
* @slot default - Direct native actions or supported rc action wrappers.
|
|
47
|
+
* @slot overflow - Overflow-only actions and actions re-slotted by the component.
|
|
48
|
+
* @slot overflow-icon - Optional decorative icon for the overflow trigger.
|
|
49
|
+
*
|
|
50
|
+
* @fires rc-adaptive-menu-toggle - Fired when user interaction requests a change to `open`.
|
|
51
|
+
*
|
|
52
|
+
* @attr label - Accessible label for the toolbar. Defaults to `Actions`.
|
|
53
|
+
* @attr max-shown - Maximum number of authored actions promoted ahead of the overflow trigger.
|
|
54
|
+
* A static declarative cap, not a measurement of available space — actions beyond it move to
|
|
55
|
+
* the overflow menu regardless of whether they'd visually fit. Defaults to 3.
|
|
56
|
+
* @attr open - Whether the overflow menu is visible. Host writes are silent.
|
|
57
|
+
* @attr default-open - Initial open state for uncontrolled usage.
|
|
58
|
+
* @attr orientation - Toolbar layout and arrow-key axis: `horizontal` or `vertical`.
|
|
59
|
+
* @attr overflow-label - Accessible label for the overflow trigger and popup. Defaults to `More actions`.
|
|
60
|
+
*
|
|
61
|
+
* @csspart root - Toolbar container.
|
|
62
|
+
* @csspart overflow-trigger - Button that opens the overflow menu.
|
|
63
|
+
* @csspart popup - Overflow menu popover.
|
|
64
|
+
*
|
|
65
|
+
* @cssprop [--rc-adaptive-menu-gap=var(--rc-control-gap)] - Gap between promoted actions and the overflow trigger.
|
|
66
|
+
* @cssprop [--rc-adaptive-menu-trigger-inline-size=2.5em] - Overflow trigger minimum inline size.
|
|
67
|
+
* @cssprop [--rc-adaptive-menu-trigger-block-size=2.5em] - Overflow trigger minimum block size.
|
|
68
|
+
* @cssprop [--rc-adaptive-menu-touch-target-inline-size] - Minimum overflow trigger activation-area inline size. Defers to `--rc-adaptive-menu-touch-target-block-size` when unset.
|
|
69
|
+
* @cssprop [--rc-adaptive-menu-touch-target-block-size=3rem] - Minimum overflow trigger activation-area block size.
|
|
70
|
+
* @cssprop [--rc-adaptive-menu-touch-target-overlap-inline-start=0px] - Zero by default. A theme
|
|
71
|
+
* or consumer sets this when the overflow trigger sits at a real leading edge (no neighbor on
|
|
72
|
+
* that side) to let its touch-target inflation overlap into whatever sits just outside the
|
|
73
|
+
* host, such as a container's own edge padding, instead of also reserving layout space there.
|
|
74
|
+
* @cssprop [--rc-adaptive-menu-touch-target-overlap-inline-end=0px] - The trailing-edge
|
|
75
|
+
* counterpart to `--rc-adaptive-menu-touch-target-overlap-inline-start` — the overflow trigger
|
|
76
|
+
* is conventionally the trailing-most control in a toolbar, so this is the one commonly set.
|
|
77
|
+
* @cssprop [--rc-adaptive-menu-trigger-padding=0.5em] - Overflow trigger padding.
|
|
78
|
+
* @cssprop [--rc-adaptive-menu-trigger-border=1px solid ButtonBorder] - Overflow trigger border.
|
|
79
|
+
* @cssprop [--rc-adaptive-menu-trigger-radius=var(--rc-control-radius)] - Overflow trigger radius.
|
|
80
|
+
* @cssprop [--rc-adaptive-menu-trigger-background=ButtonFace] - Overflow trigger background.
|
|
81
|
+
* @cssprop [--rc-adaptive-menu-trigger-color=ButtonText] - Overflow trigger color.
|
|
82
|
+
* @cssprop [--rc-adaptive-menu-icon-size=1.5em] - Default overflow icon size.
|
|
83
|
+
* @cssprop [--rc-adaptive-menu-popup-min-inline-size=10em] - Overflow popup minimum inline size.
|
|
84
|
+
* @cssprop [--rc-adaptive-menu-popup-max-inline-size=calc(100dvi - 0.5rem)] - Overflow popup viewport-aware inline limit.
|
|
85
|
+
* @cssprop [--rc-adaptive-menu-popup-max-block-size=calc(100dvb - 0.5rem)] - Overflow popup viewport-aware block limit.
|
|
86
|
+
* @cssprop [--rc-adaptive-menu-popup-padding-block=0.25em] - Overflow popup block padding.
|
|
87
|
+
* @cssprop [--rc-adaptive-menu-popup-border=var(--rc-border)] - Overflow popup border.
|
|
88
|
+
* @cssprop [--rc-adaptive-menu-popup-radius=var(--rc-control-radius)] - Overflow popup radius.
|
|
89
|
+
* @cssprop [--rc-adaptive-menu-popup-background=var(--rc-surface)] - Overflow popup background.
|
|
90
|
+
* @cssprop [--rc-adaptive-menu-popup-shadow=var(--rc-shadow)] - Overflow popup shadow.
|
|
91
|
+
* @cssprop [--rc-adaptive-menu-popup-color=var(--rc-field-text)] - Overflow popup foreground.
|
|
92
|
+
* @cssprop [--rc-adaptive-menu-item-min-block-size=2.5em] - Overflow action minimum block size.
|
|
93
|
+
* @cssprop [--rc-adaptive-menu-item-padding-block=var(--rc-item-padding-block)] - Overflow action block padding.
|
|
94
|
+
* @cssprop [--rc-adaptive-menu-item-padding-inline=var(--rc-item-padding-inline)] - Overflow action inline padding.
|
|
95
|
+
* @cssprop [--rc-adaptive-menu-item-gap=var(--rc-item-gap)] - Overflow action content gap.
|
|
96
|
+
*/
|
|
97
|
+
export declare class RCAdaptiveMenu extends LitElement {
|
|
98
|
+
static styles: import('lit').CSSResult;
|
|
99
|
+
private static readonly _styledRoots;
|
|
100
|
+
/** Accessible label for the toolbar. */
|
|
101
|
+
label: string;
|
|
102
|
+
/**
|
|
103
|
+
* Maximum authored actions promoted ahead of the overflow trigger. A
|
|
104
|
+
* static declarative cap, re-evaluated whenever it changes — not a
|
|
105
|
+
* measurement of available space.
|
|
106
|
+
*/
|
|
107
|
+
maxShown: number;
|
|
108
|
+
private _open;
|
|
109
|
+
private _defaultOpen;
|
|
110
|
+
private _uncontrolledOpen;
|
|
111
|
+
private _openInitialized;
|
|
112
|
+
/** Whether the overflow menu is visible. Host writes are silent. */
|
|
113
|
+
get open(): boolean;
|
|
114
|
+
set open(value: boolean | undefined);
|
|
115
|
+
/** Initial open state for uncontrolled usage. */
|
|
116
|
+
get defaultOpen(): boolean;
|
|
117
|
+
set defaultOpen(value: boolean);
|
|
118
|
+
/** Toolbar layout and arrow-key navigation axis. */
|
|
119
|
+
orientation: 'horizontal' | 'vertical';
|
|
120
|
+
/** Accessible name for the overflow trigger and popup. */
|
|
121
|
+
overflowLabel: string;
|
|
122
|
+
private _$trigger;
|
|
123
|
+
private _$popup;
|
|
124
|
+
private readonly _authored;
|
|
125
|
+
private _mutationObserver?;
|
|
126
|
+
private _applyingSlots;
|
|
127
|
+
private _restoreFocusWhenClosed;
|
|
128
|
+
private _focusWhenOpened;
|
|
129
|
+
private readonly _anchor;
|
|
130
|
+
connectedCallback(): void;
|
|
131
|
+
disconnectedCallback(): void;
|
|
132
|
+
protected firstUpdated(): void;
|
|
133
|
+
protected updated(changed: Map<PropertyKey, unknown>): void;
|
|
134
|
+
/** Original authored action elements in DOM order. */
|
|
135
|
+
get $actions(): HTMLElement[];
|
|
136
|
+
/** Actions currently shown in the toolbar, in authored DOM order. */
|
|
137
|
+
get $promotedActions(): HTMLElement[];
|
|
138
|
+
/** Actions currently assigned to the overflow menu, in authored DOM order. */
|
|
139
|
+
get $overflowedActions(): HTMLElement[];
|
|
140
|
+
/** Opens the overflow popup silently and optionally focuses an edge action. */
|
|
141
|
+
openMenu(focus?: 'first' | 'last' | 'none'): void;
|
|
142
|
+
/** Closes the overflow popup silently. */
|
|
143
|
+
closeMenu(returnFocus?: boolean): void;
|
|
144
|
+
/** Toggles the overflow popup silently. */
|
|
145
|
+
toggleMenu(focus?: 'first' | 'last' | 'none'): void;
|
|
146
|
+
protected render(): import('lit').TemplateResult<1>;
|
|
147
|
+
private static _ensureBaseStyles;
|
|
148
|
+
private _onSlotChange;
|
|
149
|
+
private _onMutation;
|
|
150
|
+
private _syncActions;
|
|
151
|
+
private _entriesInDomOrder;
|
|
152
|
+
/**
|
|
153
|
+
* Assigns each authored action to the default (promoted) or overflow slot.
|
|
154
|
+
* A purely declarative computation from `max-shown`, `data-priority`, and
|
|
155
|
+
* authored `slot` — no size measurement of any kind.
|
|
156
|
+
*/
|
|
157
|
+
private _applySlots;
|
|
158
|
+
private _focusTarget;
|
|
159
|
+
private _promotedFocusTargets;
|
|
160
|
+
private _overflowFocusTargets;
|
|
161
|
+
private _syncToolbarTabStops;
|
|
162
|
+
private _onFocusIn;
|
|
163
|
+
private _onToolbarNavigate;
|
|
164
|
+
private _onTriggerNavigate;
|
|
165
|
+
private _onOverflowNavigate;
|
|
166
|
+
private _focusByNavigationAction;
|
|
167
|
+
private _onTriggerClick;
|
|
168
|
+
private _onDocumentPointerDown;
|
|
169
|
+
private _onDocumentKeydown;
|
|
170
|
+
private _onPopupClick;
|
|
171
|
+
private _setProgrammaticOpen;
|
|
172
|
+
private _requestUserOpen;
|
|
173
|
+
private _syncOpenState;
|
|
174
|
+
private _focusOverflowEdge;
|
|
175
|
+
private _restoreAction;
|
|
176
|
+
private _restoreOrientation;
|
|
177
|
+
private _restoreRole;
|
|
178
|
+
private _isDisabled;
|
|
179
|
+
}
|
|
180
|
+
export default RCAdaptiveMenu;
|
package/package.json
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@rcarls/rc-adaptive-menu",
|
|
3
|
+
"publishConfig": {
|
|
4
|
+
"access": "public"
|
|
5
|
+
},
|
|
6
|
+
"version": "0.5.0",
|
|
7
|
+
"description": "Priority-aware action toolbar that preserves authored controls while moving lower-priority actions into an overflow menu.",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "git+https://github.com/richardcarls/rc-webcomponents.git",
|
|
11
|
+
"directory": "packages/rc-adaptive-menu"
|
|
12
|
+
},
|
|
13
|
+
"homepage": "https://richardcarls.github.io/rc-webcomponents/components/rc-adaptive-menu",
|
|
14
|
+
"license": "MIT",
|
|
15
|
+
"type": "module",
|
|
16
|
+
"files": [
|
|
17
|
+
"dist"
|
|
18
|
+
],
|
|
19
|
+
"module": "./dist/rc-adaptive-menu.js",
|
|
20
|
+
"exports": {
|
|
21
|
+
".": {
|
|
22
|
+
"import": {
|
|
23
|
+
"types": "./dist/types/packages/rc-adaptive-menu/src/index.d.ts",
|
|
24
|
+
"default": "./dist/rc-adaptive-menu.js"
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
"./define": {
|
|
28
|
+
"import": {
|
|
29
|
+
"types": "./dist/types/packages/rc-adaptive-menu/src/define.d.ts",
|
|
30
|
+
"default": "./dist/rc-adaptive-menu-define.js"
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
"types": "./dist/types/packages/rc-adaptive-menu/src/index.d.ts",
|
|
35
|
+
"sideEffects": [
|
|
36
|
+
"./dist/rc-adaptive-menu-define.js"
|
|
37
|
+
],
|
|
38
|
+
"customElements": "dist/custom-elements.json",
|
|
39
|
+
"scripts": {
|
|
40
|
+
"build": "tsc && vite build && cem analyze",
|
|
41
|
+
"cem:analyze": "cem analyze",
|
|
42
|
+
"preview": "vite preview",
|
|
43
|
+
"test:browser": "vitest --run",
|
|
44
|
+
"test:browser:chrome": "vitest --run --project=chromium",
|
|
45
|
+
"test:browser:firefox": "vitest --run --project=firefox"
|
|
46
|
+
},
|
|
47
|
+
"dependencies": {
|
|
48
|
+
"@rcarls/rc-common": "0.5.0"
|
|
49
|
+
},
|
|
50
|
+
"devDependencies": {
|
|
51
|
+
"@custom-elements-manifest/analyzer": "0.11.0",
|
|
52
|
+
"@vitest/browser-playwright": "4.1.5",
|
|
53
|
+
"lit": "^3.0.0",
|
|
54
|
+
"playwright": "^1.56.0",
|
|
55
|
+
"rollup": "^4.60.2",
|
|
56
|
+
"typescript": "~5.9.3",
|
|
57
|
+
"vite": "^7.1.7",
|
|
58
|
+
"vite-plugin-dts": "^4.5.4",
|
|
59
|
+
"vitest": "^4.0.6",
|
|
60
|
+
"vitest-browser-lit": "^1.0.1"
|
|
61
|
+
},
|
|
62
|
+
"peerDependencies": {
|
|
63
|
+
"lit": "^3.0.0"
|
|
64
|
+
}
|
|
65
|
+
}
|