@ecopages/radiant-ui 0.1.0-beta.0 → 0.1.0-beta.2
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 +4 -2
- package/dist/components/ui/alert/alert.css +56 -23
- package/dist/components/ui/breadcrumb/breadcrumb.css +85 -36
- package/dist/components/ui/carousel/carousel.css +465 -166
- package/dist/components/ui/checkbox/checkbox.css +196 -49
- package/dist/components/ui/combobox/combobox.css +213 -60
- package/dist/components/ui/dialog/dialog.css +176 -49
- package/dist/components/ui/disclosure/disclosure.css +228 -100
- package/dist/components/ui/feed/feed.css +30 -10
- package/dist/components/ui/field/field.css +18 -22
- package/dist/components/ui/form/form.css +7 -8
- package/dist/components/ui/grid/grid.css +172 -43
- package/dist/components/ui/input/input.css +126 -30
- package/dist/components/ui/label/label.css +20 -6
- package/dist/components/ui/listbox/listbox.css +137 -19
- package/dist/components/ui/menu-button/index.js +1 -1
- package/dist/components/ui/menu-button/index.js.map +4 -4
- package/dist/components/ui/menu-button/menu-button.css +158 -38
- package/dist/components/ui/menu-button/menu-button.script.d.ts +7 -7
- package/dist/components/ui/menubar/index.js +1 -1
- package/dist/components/ui/menubar/index.js.map +2 -2
- package/dist/components/ui/menubar/menubar.css +207 -39
- package/dist/components/ui/meter/meter.css +50 -13
- package/dist/components/ui/navigation-menu/navigation-menu.css +281 -85
- package/dist/components/ui/radio-group/radio-group.css +179 -40
- package/dist/components/ui/shared/floating-position.d.ts +37 -0
- package/dist/components/ui/shared/floating-position.test.d.ts +1 -0
- package/dist/components/ui/shared/placement.d.ts +2 -0
- package/dist/components/ui/sidebar/sidebar.css +407 -189
- package/dist/components/ui/slider/slider.css +220 -47
- package/dist/components/ui/spinbutton/spinbutton.css +65 -15
- package/dist/components/ui/switch/switch.css +164 -43
- package/dist/components/ui/tabs/tabs.css +145 -24
- package/dist/components/ui/textarea/textarea.css +128 -31
- package/dist/components/ui/toast/toast.css +388 -255
- package/dist/components/ui/toolbar/toolbar.css +118 -11
- package/dist/components/ui/tooltip/index.js +1 -1
- package/dist/components/ui/tooltip/index.js.map +4 -4
- package/dist/components/ui/tooltip/tooltip.css +119 -21
- package/dist/components/ui/tooltip/tooltip.script.d.ts +6 -6
- package/dist/components/ui/tree/tree.css +180 -49
- package/dist/components/ui/treegrid/treegrid.css +190 -72
- package/dist/components/ui/window-splitter/window-splitter.css +144 -31
- package/dist/index.js +1 -1
- package/dist/index.js.map +4 -4
- package/dist/styles/components/button.css +299 -113
- package/dist/styles/radiant-ui-core.css +828 -7
- package/dist/styles/radiant-ui.css +1497 -8
- package/dist/styles/styles.css +5830 -36
- package/dist/styles/tailwind.css +1607 -27
- package/dist/styles/themes/aurora.css +637 -15
- package/dist/styles/themes/default-compact-soft.css +580 -15
- package/dist/styles/themes/default.css +641 -2
- package/dist/styles/themes/glacier.css +637 -15
- package/dist/styles/tokens/colors/aurora.css +1 -73
- package/dist/styles/tokens/colors/basalt.css +1 -36
- package/dist/styles/tokens/colors/glacier.css +1 -45
- package/dist/styles/tokens/colors/palettes.css +1 -3
- package/dist/styles/tokens/elevation/default.css +11 -19
- package/dist/styles/tokens/elevation/flat.css +11 -19
- package/dist/styles/tokens/motion/default.css +6 -17
- package/dist/styles/tokens/motion/snappy.css +6 -17
- package/dist/styles/tokens/presets/colors/index.css +446 -4
- package/dist/styles/tokens/radius/default.css +17 -31
- package/dist/styles/tokens/radius/edgy.css +17 -32
- package/dist/styles/tokens/radius/sharp.css +20 -2
- package/dist/styles/tokens/radius/soft.css +17 -31
- package/dist/styles/tokens/semantic.css +445 -79
- package/dist/styles/tokens/spacing/compact.css +16 -21
- package/dist/styles/tokens/spacing/default.css +16 -21
- package/dist/styles/tokens/spacing/scale-bridge.css +1 -17
- package/dist/styles/tokens/spacing/wide.css +16 -21
- package/dist/styles/tokens/system.css +21 -33
- package/dist/styles/tokens/typography/default.css +23 -33
- package/package.json +5 -6
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
|
-
"sources": ["../../../../src/components/ui/menu-button/menu-button.script.tsx", "../../../../src/lib/radiant-view.ts", "../../../../src/components/ui/menu-button/menu-button.tsx"],
|
|
4
|
-
"sourcesContent": ["import { RadiantElement, bound, customElement, event, onEvent, onUpdated, prop, query } from '@ecopages/radiant';\nimport type { EventEmitter } from '@ecopages/radiant/tools/event-emitter';\nimport { type Placement, autoUpdate, computePosition, flip, offset, shift } from '@floating-ui/dom';\n\nexport type RuiMenuButtonProps = {\n\t/** Whether the menu starts open. Default: `false`. */\n\topen?: boolean;\n\t/** Floating-ui placement for the menu. Default: `bottom-start`. */\n\tplacement?: Placement;\n};\n\nexport type RuiMenuButtonSelectDetail = {\n\tvalue: string;\n};\n\n/**\n * `<rui-menu-button>` \u2014 a button that opens a menu of actions.\n *\n * Implements the WAI-ARIA APG Menu Button pattern together with the Menu\n * keyboard model: the trigger exposes `aria-haspopup=\"menu\"` and\n * `aria-expanded`, and the popup has `role=\"menu\"` with `role=\"menuitem\"`\n * children.\n *\n * @see https://www.w3.org/WAI/ARIA/apg/patterns/menu-button/\n * @see https://www.w3.org/WAI/ARIA/apg/patterns/menubar/\n *\n * Keyboard interaction (trigger):\n * - `Enter` / `Space` / `ArrowDown`: open and focus the first item\n * - `ArrowUp`: open and focus the last item\n *\n * Keyboard interaction (open menu):\n * - `ArrowDown` / `ArrowUp`: move between items (wraps)\n * - `Home` / `End`: first / last item\n * - `Enter` / `Space`: activate the focused item and close\n * - `Escape`: close and return focus to the trigger\n *\n * @element rui-menu-button\n * @slot trigger - Label for the menu button.\n * @slot - Menu items (`role=\"menuitem\"`), typically buttons or anchors.\n * @fires rui-change - Emitted when a menu item is activated; `detail.value` is the item's `data-value` or text.\n * @fires rui-close - Emitted when the menu closes.\n */\n@customElement('rui-menu-button')\nexport class RuiMenuButton extends RadiantElement {\n\t@prop({ type: Boolean, reflect: true, defaultValue: false }) open: boolean;\n\t@prop({ type: String, defaultValue: 'bottom-start' }) placement: Placement;\n\n\t@query({ ref: 'trigger' }) triggerTarget: HTMLButtonElement;\n\t@query({ ref: 'menu' }) menuTarget: HTMLElement;\n\n\t@event({ name: 'rui-change', bubbles: true, composed: true })\n\tchangeEvent: EventEmitter<RuiMenuButtonSelectDetail>;\n\n\t@event({ name: 'rui-close', bubbles: true, composed: true })\n\tcloseEvent: EventEmitter<void>;\n\n\tprivate cleanup: ReturnType<typeof autoUpdate> | null = null;\n\tprivate menuId = `rui-menu-${Math.random().toString(36).slice(2, 9)}`;\n\n\toverride connectedCallback(): void {\n\t\tsuper.connectedCallback();\n\t\tqueueMicrotask(() => this.syncOpenState());\n\t}\n\n\toverride disconnectedCallback(): void {\n\t\tthis.teardownFloating();\n\t\tsuper.disconnectedCallback();\n\t}\n\n\tprivate getItems(): HTMLElement[] {\n\t\treturn Array.from(this.menuTarget?.querySelectorAll<HTMLElement>('[role=\"menuitem\"]') ?? []).filter(\n\t\t\t(item) => item.getAttribute('aria-disabled') !== 'true',\n\t\t);\n\t}\n\n\tprivate teardownFloating(): void {\n\t\tthis.cleanup?.();\n\t\tthis.cleanup = null;\n\t}\n\n\t@bound\n\tupdatePosition(): void {\n\t\tif (!this.triggerTarget || !this.menuTarget || !this.open) return;\n\t\tcomputePosition(this.triggerTarget, this.menuTarget, {\n\t\t\tplacement: this.placement,\n\t\t\tmiddleware: [offset(6), flip(), shift({ padding: 8 })],\n\t\t}).then(({ x, y }) => {\n\t\t\tObject.assign(this.menuTarget.style, {\n\t\t\t\tleft: `${x}px`,\n\t\t\t\ttop: `${y}px`,\n\t\t\t});\n\t\t});\n\t}\n\n\tprivate pendingFocus: 'first' | 'last' | 'trigger' | null = null;\n\n\t@bound\n\t@onUpdated(['open', 'placement'])\n\tsyncOpenState(): void {\n\t\tif (!this.triggerTarget || !this.menuTarget) return;\n\n\t\tthis.menuTarget.id = this.menuId;\n\t\tthis.triggerTarget.setAttribute('aria-controls', this.menuId);\n\t\tthis.triggerTarget.setAttribute('aria-haspopup', 'menu');\n\t\tthis.triggerTarget.setAttribute('aria-expanded', String(this.open));\n\t\tthis.menuTarget.hidden = !this.open;\n\n\t\tif (this.open) {\n\t\t\tthis.teardownFloating();\n\t\t\tthis.cleanup = autoUpdate(this.triggerTarget, this.menuTarget, this.updatePosition);\n\t\t\tthis.updatePosition();\n\t\t} else {\n\t\t\tthis.teardownFloating();\n\t\t}\n\n\t\tconst focus = this.pendingFocus;\n\t\tthis.pendingFocus = null;\n\t\tif (!focus) return;\n\n\t\tif (focus === 'first') this.getItems()[0]?.focus();\n\t\tif (focus === 'last') {\n\t\t\tconst items = this.getItems();\n\t\t\titems[items.length - 1]?.focus();\n\t\t}\n\t\tif (focus === 'trigger') this.triggerTarget?.focus();\n\t}\n\n\tprivate setOpen(next: boolean, focus: 'first' | 'last' | 'trigger' | null = null): void {\n\t\tconst wasOpen = this.open;\n\t\tthis.pendingFocus = focus;\n\t\tthis.open = next;\n\t\tif (wasOpen && !next) this.closeEvent.emit();\n\t\tif (wasOpen === next) this.syncOpenState();\n\t}\n\n\t@onEvent({ document: true, type: 'click' })\n\tonDocumentClick(event: MouseEvent): void {\n\t\tif (!this.open) return;\n\t\tconst target = event.target as Node;\n\t\tif (this.triggerTarget?.contains(target) || this.menuTarget?.contains(target)) return;\n\t\tthis.setOpen(false);\n\t}\n\n\t@onEvent({ ref: 'trigger', type: 'click' })\n\tonTriggerClick(): void {\n\t\tthis.setOpen(!this.open, this.open ? null : 'first');\n\t}\n\n\t@onEvent({ ref: 'trigger', type: 'keydown' })\n\tonTriggerKeydown(event: KeyboardEvent): void {\n\t\tswitch (event.key) {\n\t\t\tcase 'ArrowDown':\n\t\t\tcase 'Enter':\n\t\t\tcase ' ':\n\t\t\t\tevent.preventDefault();\n\t\t\t\tthis.setOpen(true, 'first');\n\t\t\t\tbreak;\n\t\t\tcase 'ArrowUp':\n\t\t\t\tevent.preventDefault();\n\t\t\t\tthis.setOpen(true, 'last');\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tbreak;\n\t\t}\n\t}\n\n\t@onEvent({ ref: 'menu', type: 'keydown' })\n\tonMenuKeydown(event: KeyboardEvent): void {\n\t\tconst items = this.getItems();\n\t\tconst current = document.activeElement as HTMLElement | null;\n\t\tconst index = current ? items.indexOf(current) : -1;\n\n\t\tswitch (event.key) {\n\t\t\tcase 'ArrowDown': {\n\t\t\t\tevent.preventDefault();\n\t\t\t\tconst next = items[(index + 1) % items.length];\n\t\t\t\tnext?.focus();\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase 'ArrowUp': {\n\t\t\t\tevent.preventDefault();\n\t\t\t\tconst prev = items[(index - 1 + items.length) % items.length];\n\t\t\t\tprev?.focus();\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase 'Home':\n\t\t\t\tevent.preventDefault();\n\t\t\t\titems[0]?.focus();\n\t\t\t\tbreak;\n\t\t\tcase 'End':\n\t\t\t\tevent.preventDefault();\n\t\t\t\titems[items.length - 1]?.focus();\n\t\t\t\tbreak;\n\t\t\tcase 'Escape':\n\t\t\t\tevent.preventDefault();\n\t\t\t\tthis.setOpen(false, 'trigger');\n\t\t\t\tbreak;\n\t\t\tcase 'Tab':\n\t\t\t\tthis.setOpen(false);\n\t\t\t\tbreak;\n\t\t\tcase 'Enter':\n\t\t\tcase ' ':\n\t\t\t\tif (current && items.includes(current)) {\n\t\t\t\t\tevent.preventDefault();\n\t\t\t\t\tthis.activateItem(current);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tbreak;\n\t\t}\n\t}\n\n\t@onEvent({ selector: '[role=\"menuitem\"]', type: 'click' })\n\tonItemClick(event: Event): void {\n\t\tconst item = (event.target as HTMLElement).closest('[role=\"menuitem\"]') as HTMLElement | null;\n\t\tif (!item || !this.contains(item)) return;\n\t\tthis.activateItem(item);\n\t}\n\n\tprivate activateItem(item: HTMLElement): void {\n\t\tconst value = item.getAttribute('data-value') || item.textContent?.trim() || '';\n\t\tthis.changeEvent.emit({ value });\n\t\tthis.setOpen(false, 'trigger');\n\t}\n\n\toverride render() {\n\t\treturn (\n\t\t\t<div class=\"rui-menu-button\">\n\t\t\t\t<button\n\t\t\t\t\ttype=\"button\"\n\t\t\t\t\tdata-ref=\"trigger\"\n\t\t\t\t\tclass=\"rui-button rui-button--primary rui-button--md rui-menu-button__trigger\"\n\t\t\t\t\taria-haspopup=\"menu\"\n\t\t\t\t\taria-expanded=\"false\"\n\t\t\t\t>\n\t\t\t\t\t<slot name=\"trigger\"></slot>\n\t\t\t\t\t<span class=\"rui-menu-button__chevron\" aria-hidden=\"true\"></span>\n\t\t\t\t</button>\n\t\t\t\t<div data-ref=\"menu\" class=\"rui-menu-button__menu\" role=\"menu\" hidden>\n\t\t\t\t\t<slot></slot>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t);\n\t}\n}\n", "import type { JsxRenderable } from '@ecopages/jsx';\n\nconst RADIANT_VIEW_ELEMENT = Symbol.for('@ecopages/storybook-radiant.viewElement');\nconst RADIANT_SCRIPT_MODULE = Symbol.for('@ecopages/storybook-radiant.scriptModule');\nconst RADIANT_SCRIPT_EXPORT = Symbol.for('@ecopages/storybook-radiant.scriptExport');\n\nexport type RadiantViewComponent<TArgs = unknown> = ((args: TArgs) => JsxRenderable) & {\n\t[RADIANT_VIEW_ELEMENT]?: CustomElementConstructor;\n\t[RADIANT_SCRIPT_MODULE]?: string;\n\t[RADIANT_SCRIPT_EXPORT]?: string;\n};\n\nfunction linkViewToElement<TArgs>(\n\tview: RadiantViewComponent<TArgs>,\n\telement: CustomElementConstructor & {\n\t\t[RADIANT_SCRIPT_MODULE]?: string;\n\t\t[RADIANT_SCRIPT_EXPORT]?: string;\n\t},\n): void {\n\tview[RADIANT_VIEW_ELEMENT] = element;\n\n\tconst ssrModule = element[RADIANT_SCRIPT_MODULE];\n\tif (ssrModule) {\n\t\tview[RADIANT_SCRIPT_MODULE] = ssrModule;\n\t\tview[RADIANT_SCRIPT_EXPORT] = element[RADIANT_SCRIPT_EXPORT] ?? element.name;\n\t}\n}\n\n/** Link a JSX view export to its RadiantElement class (same symbols as the Storybook framework). */\nexport function defineRadiantView<TArgs>(\n\telement: CustomElementConstructor & {\n\t\t[RADIANT_SCRIPT_MODULE]?: string;\n\t\t[RADIANT_SCRIPT_EXPORT]?: string;\n\t},\n\trender: (args: TArgs) => JsxRenderable,\n): RadiantViewComponent<TArgs> {\n\tconst view = ((args: TArgs) => render(args)) as RadiantViewComponent<TArgs>;\n\tlinkViewToElement(view, element);\n\treturn view;\n}\n\n/** Re-copy SSR metadata from the linked element (e.g. after HMR). */\nexport function syncRadiantViewMetadata(view: RadiantViewComponent): void {\n\tconst element = view[RADIANT_VIEW_ELEMENT];\n\tif (element) {\n\t\tlinkViewToElement(view, element);\n\t}\n}\n", "import type { JsxRenderable } from '@ecopages/jsx';\nimport type { RadiantSlotProps } from '../../../types';\nimport { defineRadiantView } from '../../../lib/radiant-view';\nimport type { RuiMenuButtonProps } from './menu-button.script';\nimport { RuiMenuButton as RuiMenuButtonElement } from './menu-button.script';\nimport './menu-button.css';\n\nexport type RuiMenuItem = {\n\tvalue: string;\n\tlabel: JsxRenderable;\n\tdisabled?: boolean;\n};\n\nexport const RuiMenuButton = defineRadiantView(\n\tRuiMenuButtonElement,\n\t({\n\t\tslot,\n\t\topen,\n\t\tplacement,\n\t\ttrigger,\n\t\titems,\n\t}: RuiMenuButtonProps &\n\t\tRadiantSlotProps & {\n\t\t\ttrigger: JsxRenderable;\n\t\t\titems: RuiMenuItem[];\n\t\t}) => (\n\t\t<rui-menu-button slot={slot} open={open} placement={placement}>\n\t\t\t<span slot=\"trigger\">{trigger}</span>\n\t\t\t{items.map((item) => (\n\t\t\t\t<button\n\t\t\t\t\ttype=\"button\"\n\t\t\t\t\tclass=\"rui-menu-button__item\"\n\t\t\t\t\trole=\"menuitem\"\n\t\t\t\t\tdata-value={item.value}\n\t\t\t\t\taria-disabled={item.disabled ? 'true' : undefined}\n\t\t\t\t\ttabindex={-1}\n\t\t\t\t>\n\t\t\t\t\t{item.label}\n\t\t\t\t</button>\n\t\t\t))}\n\t\t</rui-menu-button>\n\t),\n);\n"],
|
|
5
|
-
"mappings": "AAAA,OAAS,kBAAAA,EAAgB,SAAAC,EAAO,iBAAAC,EAAe,SAAAC,EAAO,WAAAC,EAAS,aAAAC,EAAW,QAAAC,EAAM,SAAAC,MAAa,
|
|
6
|
-
"names": ["RadiantElement", "bound", "customElement", "event", "onEvent", "onUpdated", "prop", "query", "
|
|
3
|
+
"sources": ["../../../../src/components/ui/menu-button/menu-button.script.tsx", "../../../../src/components/ui/shared/floating-position.ts", "../../../../src/lib/radiant-view.ts", "../../../../src/components/ui/menu-button/menu-button.tsx"],
|
|
4
|
+
"sourcesContent": ["import { RadiantElement, bound, customElement, event, onEvent, onUpdated, prop, query } from '@ecopages/radiant';\nimport type { EventEmitter } from '@ecopages/radiant/tools/event-emitter';\nimport { attachFloating } from '../shared/floating-position';\nimport type { RuiPlacement } from '../shared/placement';\n\nexport type RuiMenuButtonProps = {\n\t/** Whether the menu starts open. Default: `false`. */\n\topen?: boolean;\n\t/** Placement of the menu surface relative to its trigger. Default: `bottom-start`. */\n\tplacement?: RuiPlacement;\n};\n\nexport type RuiMenuButtonSelectDetail = {\n\tvalue: string;\n};\n\nconst MENU_GAP = 6;\n\n/**\n * `<rui-menu-button>` \u2014 a button that opens a menu of actions.\n *\n * Implements the WAI-ARIA APG Menu Button pattern together with the Menu\n * keyboard model: the trigger exposes `aria-haspopup=\"menu\"` and\n * `aria-expanded`, and the popup has `role=\"menu\"` with `role=\"menuitem\"`\n * children.\n *\n * @see https://www.w3.org/WAI/ARIA/apg/patterns/menu-button/\n * @see https://www.w3.org/WAI/ARIA/apg/patterns/menubar/\n *\n * Keyboard interaction (trigger):\n * - `Enter` / `Space` / `ArrowDown`: open and focus the first item\n * - `ArrowUp`: open and focus the last item\n *\n * Keyboard interaction (open menu):\n * - `ArrowDown` / `ArrowUp`: move between items (wraps)\n * - `Home` / `End`: first / last item\n * - `Enter` / `Space`: activate the focused item and close\n * - `Escape`: close and return focus to the trigger\n *\n * @element rui-menu-button\n * @slot trigger - Label for the menu button.\n * @slot - Menu items (`role=\"menuitem\"`), typically buttons or anchors.\n * @fires rui-change - Emitted when a menu item is activated; `detail.value` is the item's `data-value` or text.\n * @fires rui-close - Emitted when the menu closes.\n */\n@customElement('rui-menu-button')\nexport class RuiMenuButton extends RadiantElement {\n\t@prop({ type: Boolean, reflect: true, defaultValue: false }) open: boolean;\n\t@prop({ type: String, defaultValue: 'bottom-start' }) placement: RuiPlacement;\n\n\t@query({ ref: 'trigger' }) triggerTarget: HTMLButtonElement;\n\t@query({ ref: 'menu' }) menuTarget: HTMLElement;\n\n\t@event({ name: 'rui-change', bubbles: true, composed: true })\n\tchangeEvent: EventEmitter<RuiMenuButtonSelectDetail>;\n\n\t@event({ name: 'rui-close', bubbles: true, composed: true })\n\tcloseEvent: EventEmitter<void>;\n\n\tprivate menuId = `rui-menu-${Math.random().toString(36).slice(2, 9)}`;\n\tprivate cleanupFloating: (() => void) | null = null;\n\tprivate pendingFocus: 'first' | 'last' | 'trigger' | null = null;\n\n\toverride connectedCallback(): void {\n\t\tsuper.connectedCallback();\n\t\tqueueMicrotask(() => this.syncOpenState());\n\t}\n\n\toverride disconnectedCallback(): void {\n\t\tthis.teardownFloating();\n\t\tsuper.disconnectedCallback();\n\t}\n\n\tprivate getItems(): HTMLElement[] {\n\t\treturn Array.from(this.menuTarget?.querySelectorAll<HTMLElement>('[role=\"menuitem\"]') ?? []).filter(\n\t\t\t(item) => item.getAttribute('aria-disabled') !== 'true',\n\t\t);\n\t}\n\n\tprivate teardownFloating(): void {\n\t\tthis.cleanupFloating?.();\n\t\tthis.cleanupFloating = null;\n\t}\n\n\tprivate attachFloating(): void {\n\t\tif (!this.triggerTarget || !this.menuTarget) return;\n\t\tthis.teardownFloating();\n\t\tthis.cleanupFloating = attachFloating({\n\t\t\tanchor: this.triggerTarget,\n\t\t\tfloating: this.menuTarget,\n\t\t\tgetPlacement: () => this.placement,\n\t\t\tgap: MENU_GAP,\n\t\t});\n\t}\n\n\t@bound\n\t@onUpdated(['open', 'placement'])\n\tsyncOpenState(): void {\n\t\tif (!this.triggerTarget || !this.menuTarget) return;\n\n\t\tthis.menuTarget.id = this.menuId;\n\t\tthis.triggerTarget.setAttribute('aria-controls', this.menuId);\n\t\tthis.triggerTarget.setAttribute('aria-haspopup', 'menu');\n\t\tthis.triggerTarget.setAttribute('aria-expanded', String(this.open));\n\t\tthis.menuTarget.hidden = !this.open;\n\n\t\tif (this.open) this.attachFloating();\n\t\telse this.teardownFloating();\n\n\t\tconst focus = this.pendingFocus;\n\t\tthis.pendingFocus = null;\n\t\tif (!focus) return;\n\n\t\tif (focus === 'first') this.getItems()[0]?.focus();\n\t\tif (focus === 'last') {\n\t\t\tconst items = this.getItems();\n\t\t\titems[items.length - 1]?.focus();\n\t\t}\n\t\tif (focus === 'trigger') this.triggerTarget?.focus();\n\t}\n\n\tprivate setOpen(next: boolean, focus: 'first' | 'last' | 'trigger' | null = null): void {\n\t\tconst wasOpen = this.open;\n\t\tthis.pendingFocus = focus;\n\t\tthis.open = next;\n\t\tif (wasOpen && !next) this.closeEvent.emit();\n\t\tif (wasOpen === next) this.syncOpenState();\n\t}\n\n\t@onEvent({ document: true, type: 'click' })\n\tonDocumentClick(event: MouseEvent): void {\n\t\tif (!this.open) return;\n\t\tconst target = event.target as Node;\n\t\tif (this.triggerTarget?.contains(target) || this.menuTarget?.contains(target)) return;\n\t\tthis.setOpen(false);\n\t}\n\n\t@onEvent({ ref: 'trigger', type: 'click' })\n\tonTriggerClick(): void {\n\t\tthis.setOpen(!this.open, this.open ? null : 'first');\n\t}\n\n\t@onEvent({ ref: 'trigger', type: 'keydown' })\n\tonTriggerKeydown(event: KeyboardEvent): void {\n\t\tswitch (event.key) {\n\t\t\tcase 'ArrowDown':\n\t\t\tcase 'Enter':\n\t\t\tcase ' ':\n\t\t\t\tevent.preventDefault();\n\t\t\t\tthis.setOpen(true, 'first');\n\t\t\t\tbreak;\n\t\t\tcase 'ArrowUp':\n\t\t\t\tevent.preventDefault();\n\t\t\t\tthis.setOpen(true, 'last');\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tbreak;\n\t\t}\n\t}\n\n\t@onEvent({ ref: 'menu', type: 'keydown' })\n\tonMenuKeydown(event: KeyboardEvent): void {\n\t\tconst items = this.getItems();\n\t\tconst current = document.activeElement as HTMLElement | null;\n\t\tconst index = current ? items.indexOf(current) : -1;\n\n\t\tswitch (event.key) {\n\t\t\tcase 'ArrowDown': {\n\t\t\t\tevent.preventDefault();\n\t\t\t\tconst next = items[(index + 1) % items.length];\n\t\t\t\tnext?.focus();\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase 'ArrowUp': {\n\t\t\t\tevent.preventDefault();\n\t\t\t\tconst prev = items[(index - 1 + items.length) % items.length];\n\t\t\t\tprev?.focus();\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase 'Home':\n\t\t\t\tevent.preventDefault();\n\t\t\t\titems[0]?.focus();\n\t\t\t\tbreak;\n\t\t\tcase 'End':\n\t\t\t\tevent.preventDefault();\n\t\t\t\titems[items.length - 1]?.focus();\n\t\t\t\tbreak;\n\t\t\tcase 'Escape':\n\t\t\t\tevent.preventDefault();\n\t\t\t\tthis.setOpen(false, 'trigger');\n\t\t\t\tbreak;\n\t\t\tcase 'Tab':\n\t\t\t\tthis.setOpen(false);\n\t\t\t\tbreak;\n\t\t\tcase 'Enter':\n\t\t\tcase ' ':\n\t\t\t\tif (current && items.includes(current)) {\n\t\t\t\t\tevent.preventDefault();\n\t\t\t\t\tthis.activateItem(current);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tbreak;\n\t\t}\n\t}\n\n\t@onEvent({ selector: '[role=\"menuitem\"]', type: 'click' })\n\tonItemClick(event: Event): void {\n\t\tconst item = (event.target as HTMLElement).closest('[role=\"menuitem\"]') as HTMLElement | null;\n\t\tif (!item || !this.contains(item)) return;\n\t\tthis.activateItem(item);\n\t}\n\n\tprivate activateItem(item: HTMLElement): void {\n\t\tconst value = item.getAttribute('data-value') || item.textContent?.trim() || '';\n\t\tthis.changeEvent.emit({ value });\n\t\tthis.setOpen(false, 'trigger');\n\t}\n\n\toverride render() {\n\t\treturn (\n\t\t\t<div class=\"rui-menu-button\">\n\t\t\t\t<button\n\t\t\t\t\ttype=\"button\"\n\t\t\t\t\tdata-ref=\"trigger\"\n\t\t\t\t\tclass=\"rui-button rui-button--primary rui-button--md rui-menu-button__trigger\"\n\t\t\t\t\taria-haspopup=\"menu\"\n\t\t\t\t\taria-expanded=\"false\"\n\t\t\t\t>\n\t\t\t\t\t<slot name=\"trigger\"></slot>\n\t\t\t\t\t<span class=\"rui-menu-button__chevron\" aria-hidden=\"true\"></span>\n\t\t\t\t</button>\n\t\t\t\t<div data-ref=\"menu\" class=\"rui-menu-button__menu rui-floating\" role=\"menu\" hidden>\n\t\t\t\t\t<slot></slot>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t);\n\t}\n}\n", "import type { RuiPlacement } from './placement';\n\ntype Side = 'top' | 'right' | 'bottom' | 'left';\ntype Align = 'start' | 'center' | 'end';\n\nexport type FloatingSize = { width: number; height: number };\nexport type FloatingCoords = { x: number; y: number };\nexport type FloatingViewport = { width: number; height: number; padding: number };\n\nconst OPPOSITE: Record<Side, Side> = {\n\ttop: 'bottom',\n\tbottom: 'top',\n\tleft: 'right',\n\tright: 'left',\n};\n\nconst DEFAULT_VIEWPORT_PADDING = 8;\n\nfunction parsePlacement(placement: RuiPlacement): { side: Side; align: Align } {\n\tconst dash = placement.indexOf('-');\n\tif (dash === -1) return { side: placement as Side, align: 'center' };\n\tconst side = placement.slice(0, dash) as Side;\n\tconst align = placement.slice(dash + 1);\n\treturn { side, align: align === 'start' || align === 'end' ? align : 'center' };\n}\n\nfunction primaryAxis(side: Side): 'x' | 'y' {\n\treturn side === 'top' || side === 'bottom' ? 'y' : 'x';\n}\n\nfunction coordsFor(anchor: DOMRect, size: FloatingSize, side: Side, align: Align, gap: number): FloatingCoords {\n\tconst { width, height } = size;\n\tlet x = 0;\n\tlet y = 0;\n\n\tswitch (side) {\n\t\tcase 'top':\n\t\t\ty = anchor.top - height - gap;\n\t\t\tbreak;\n\t\tcase 'bottom':\n\t\t\ty = anchor.bottom + gap;\n\t\t\tbreak;\n\t\tcase 'left':\n\t\t\tx = anchor.left - width - gap;\n\t\t\tbreak;\n\t\tcase 'right':\n\t\t\tx = anchor.right + gap;\n\t\t\tbreak;\n\t}\n\n\tif (primaryAxis(side) === 'y') {\n\t\tif (align === 'start') x = anchor.left;\n\t\telse if (align === 'end') x = anchor.right - width;\n\t\telse x = anchor.left + (anchor.width - width) / 2;\n\t} else {\n\t\tif (align === 'start') y = anchor.top;\n\t\telse if (align === 'end') y = anchor.bottom - height;\n\t\telse y = anchor.top + (anchor.height - height) / 2;\n\t}\n\n\treturn { x, y };\n}\n\nfunction overflowOnPrimary(coords: FloatingCoords, size: FloatingSize, side: Side, viewport: FloatingViewport): number {\n\tconst { padding, width: vw, height: vh } = viewport;\n\tif (side === 'top') return Math.max(0, padding - coords.y);\n\tif (side === 'bottom') return Math.max(0, coords.y + size.height - (vh - padding));\n\tif (side === 'left') return Math.max(0, padding - coords.x);\n\treturn Math.max(0, coords.x + size.width - (vw - padding));\n}\n\nfunction freeSpaceOnSide(anchor: DOMRect, size: FloatingSize, side: Side, viewport: FloatingViewport): number {\n\tconst { padding, width: vw, height: vh } = viewport;\n\tif (side === 'top') return anchor.top - padding - size.height;\n\tif (side === 'bottom') return vh - padding - size.height - anchor.bottom;\n\tif (side === 'left') return anchor.left - padding - size.width;\n\treturn vw - padding - size.width - anchor.right;\n}\n\nfunction clampCrossAxis(\n\tcoords: FloatingCoords,\n\tsize: FloatingSize,\n\tside: Side,\n\tviewport: FloatingViewport,\n): FloatingCoords {\n\tconst { padding, width: vw, height: vh } = viewport;\n\tif (primaryAxis(side) === 'y') {\n\t\tconst maxX = Math.max(padding, vw - padding - size.width);\n\t\treturn { x: Math.min(Math.max(coords.x, padding), maxX), y: coords.y };\n\t}\n\tconst maxY = Math.max(padding, vh - padding - size.height);\n\treturn { x: coords.x, y: Math.min(Math.max(coords.y, padding), maxY) };\n}\n\n/**\n * Computes fixed-position coords for a floating surface.\n *\n * @remarks\n * Prefers `placement`, then flips on the primary axis when the opposite side\n * has more free space. Clamps only the cross-axis so the floating surface\n * never slides onto its anchor.\n */\nexport function computeFloatingCoords(\n\tanchor: DOMRect,\n\tsize: FloatingSize,\n\tplacement: RuiPlacement,\n\tgap: number,\n\tviewport: FloatingViewport = {\n\t\twidth: typeof window === 'undefined' ? 0 : window.innerWidth,\n\t\theight: typeof window === 'undefined' ? 0 : window.innerHeight,\n\t\tpadding: DEFAULT_VIEWPORT_PADDING,\n\t},\n): FloatingCoords {\n\tconst { side: preferred, align } = parsePlacement(placement);\n\tconst preferredCoords = coordsFor(anchor, size, preferred, align, gap);\n\tconst preferredOverflow = overflowOnPrimary(preferredCoords, size, preferred, viewport);\n\n\tlet side = preferred;\n\tlet coords = preferredCoords;\n\n\tif (preferredOverflow > 0) {\n\t\tconst flipped = OPPOSITE[preferred];\n\t\tconst flippedCoords = coordsFor(anchor, size, flipped, align, gap);\n\t\tconst preferredFree = freeSpaceOnSide(anchor, size, preferred, viewport);\n\t\tconst flippedFree = freeSpaceOnSide(anchor, size, flipped, viewport);\n\t\tif (flippedFree > preferredFree) {\n\t\t\tside = flipped;\n\t\t\tcoords = flippedCoords;\n\t\t}\n\t}\n\n\treturn clampCrossAxis(coords, size, side, viewport);\n}\n\n/** Applies fixed coords to a floating element. */\nexport function applyFloatingPosition(\n\tanchor: HTMLElement,\n\tfloating: HTMLElement,\n\tplacement: RuiPlacement,\n\tgap: number,\n): void {\n\tconst coords = computeFloatingCoords(\n\t\tanchor.getBoundingClientRect(),\n\t\t{ width: floating.offsetWidth, height: floating.offsetHeight },\n\t\tplacement,\n\t\tgap,\n\t);\n\tObject.assign(floating.style, {\n\t\tposition: 'fixed',\n\t\tleft: `${coords.x}px`,\n\t\ttop: `${coords.y}px`,\n\t\tright: 'auto',\n\t\tbottom: 'auto',\n\t\tvisibility: 'visible',\n\t});\n}\n\nexport type AttachFloatingOptions = {\n\tanchor: HTMLElement;\n\tfloating: HTMLElement;\n\tgetPlacement: () => RuiPlacement;\n\tgap: number;\n};\n\n/**\n * Positions on attach and keeps the floating element updated on scroll/resize.\n *\n * @returns Cleanup that removes listeners and observers.\n */\nexport function attachFloating({ anchor, floating, getPlacement, gap }: AttachFloatingOptions): () => void {\n\tconst update = (): void => {\n\t\tif (floating.hidden) return;\n\t\tapplyFloatingPosition(anchor, floating, getPlacement(), gap);\n\t};\n\n\tupdate();\n\n\twindow.addEventListener('resize', update);\n\twindow.addEventListener('scroll', update, true);\n\n\tconst observer = new ResizeObserver(update);\n\tobserver.observe(anchor);\n\tobserver.observe(floating);\n\n\treturn () => {\n\t\twindow.removeEventListener('resize', update);\n\t\twindow.removeEventListener('scroll', update, true);\n\t\tobserver.disconnect();\n\t};\n}\n", "import type { JsxRenderable } from '@ecopages/jsx';\n\nconst RADIANT_VIEW_ELEMENT = Symbol.for('@ecopages/storybook-radiant.viewElement');\nconst RADIANT_SCRIPT_MODULE = Symbol.for('@ecopages/storybook-radiant.scriptModule');\nconst RADIANT_SCRIPT_EXPORT = Symbol.for('@ecopages/storybook-radiant.scriptExport');\n\nexport type RadiantViewComponent<TArgs = unknown> = ((args: TArgs) => JsxRenderable) & {\n\t[RADIANT_VIEW_ELEMENT]?: CustomElementConstructor;\n\t[RADIANT_SCRIPT_MODULE]?: string;\n\t[RADIANT_SCRIPT_EXPORT]?: string;\n};\n\nfunction linkViewToElement<TArgs>(\n\tview: RadiantViewComponent<TArgs>,\n\telement: CustomElementConstructor & {\n\t\t[RADIANT_SCRIPT_MODULE]?: string;\n\t\t[RADIANT_SCRIPT_EXPORT]?: string;\n\t},\n): void {\n\tview[RADIANT_VIEW_ELEMENT] = element;\n\n\tconst ssrModule = element[RADIANT_SCRIPT_MODULE];\n\tif (ssrModule) {\n\t\tview[RADIANT_SCRIPT_MODULE] = ssrModule;\n\t\tview[RADIANT_SCRIPT_EXPORT] = element[RADIANT_SCRIPT_EXPORT] ?? element.name;\n\t}\n}\n\n/** Link a JSX view export to its RadiantElement class (same symbols as the Storybook framework). */\nexport function defineRadiantView<TArgs>(\n\telement: CustomElementConstructor & {\n\t\t[RADIANT_SCRIPT_MODULE]?: string;\n\t\t[RADIANT_SCRIPT_EXPORT]?: string;\n\t},\n\trender: (args: TArgs) => JsxRenderable,\n): RadiantViewComponent<TArgs> {\n\tconst view = ((args: TArgs) => render(args)) as RadiantViewComponent<TArgs>;\n\tlinkViewToElement(view, element);\n\treturn view;\n}\n\n/** Re-copy SSR metadata from the linked element (e.g. after HMR). */\nexport function syncRadiantViewMetadata(view: RadiantViewComponent): void {\n\tconst element = view[RADIANT_VIEW_ELEMENT];\n\tif (element) {\n\t\tlinkViewToElement(view, element);\n\t}\n}\n", "import type { JsxRenderable } from '@ecopages/jsx';\nimport type { RadiantSlotProps } from '../../../types';\nimport { defineRadiantView } from '../../../lib/radiant-view';\nimport type { RuiMenuButtonProps } from './menu-button.script';\nimport { RuiMenuButton as RuiMenuButtonElement } from './menu-button.script';\nimport './menu-button.css';\n\nexport type RuiMenuItem = {\n\tvalue: string;\n\tlabel: JsxRenderable;\n\tdisabled?: boolean;\n};\n\nexport const RuiMenuButton = defineRadiantView(\n\tRuiMenuButtonElement,\n\t({\n\t\tslot,\n\t\topen,\n\t\tplacement,\n\t\ttrigger,\n\t\titems,\n\t}: RuiMenuButtonProps &\n\t\tRadiantSlotProps & {\n\t\t\ttrigger: JsxRenderable;\n\t\t\titems: RuiMenuItem[];\n\t\t}) => (\n\t\t<rui-menu-button slot={slot} open={open} placement={placement}>\n\t\t\t<span slot=\"trigger\">{trigger}</span>\n\t\t\t{items.map((item) => (\n\t\t\t\t<button\n\t\t\t\t\ttype=\"button\"\n\t\t\t\t\tclass=\"rui-menu-button__item\"\n\t\t\t\t\trole=\"menuitem\"\n\t\t\t\t\tdata-value={item.value}\n\t\t\t\t\taria-disabled={item.disabled ? 'true' : undefined}\n\t\t\t\t\ttabindex={-1}\n\t\t\t\t>\n\t\t\t\t\t{item.label}\n\t\t\t\t</button>\n\t\t\t))}\n\t\t</rui-menu-button>\n\t),\n);\n"],
|
|
5
|
+
"mappings": "AAAA,OAAS,kBAAAA,EAAgB,SAAAC,EAAO,iBAAAC,EAAe,SAAAC,EAAO,WAAAC,EAAS,aAAAC,EAAW,QAAAC,EAAM,SAAAC,MAAa,oBCS7F,IAAMC,EAA+B,CACpC,IAAK,SACL,OAAQ,MACR,KAAM,QACN,MAAO,MACR,EAEMC,EAA2B,EAEjC,SAASC,EAAeC,EAAuD,CAC9E,IAAMC,EAAOD,EAAU,QAAQ,GAAG,EAClC,GAAIC,IAAS,GAAI,MAAO,CAAE,KAAMD,EAAmB,MAAO,QAAS,EACnE,IAAME,EAAOF,EAAU,MAAM,EAAGC,CAAI,EAC9BE,EAAQH,EAAU,MAAMC,EAAO,CAAC,EACtC,MAAO,CAAE,KAAAC,EAAM,MAAOC,IAAU,SAAWA,IAAU,MAAQA,EAAQ,QAAS,CAC/E,CAEA,SAASC,EAAYF,EAAuB,CAC3C,OAAOA,IAAS,OAASA,IAAS,SAAW,IAAM,GACpD,CAEA,SAASG,EAAUC,EAAiBC,EAAoBL,EAAYC,EAAcK,EAA6B,CAC9G,GAAM,CAAE,MAAAC,EAAO,OAAAC,CAAO,EAAIH,EACtBI,EAAI,EACJC,EAAI,EAER,OAAQV,EAAM,CACb,IAAK,MACJU,EAAIN,EAAO,IAAMI,EAASF,EAC1B,MACD,IAAK,SACJI,EAAIN,EAAO,OAASE,EACpB,MACD,IAAK,OACJG,EAAIL,EAAO,KAAOG,EAAQD,EAC1B,MACD,IAAK,QACJG,EAAIL,EAAO,MAAQE,EACnB,KACF,CAEA,OAAIJ,EAAYF,CAAI,IAAM,IACrBC,IAAU,QAASQ,EAAIL,EAAO,KACzBH,IAAU,MAAOQ,EAAIL,EAAO,MAAQG,EACxCE,EAAIL,EAAO,MAAQA,EAAO,MAAQG,GAAS,EAE5CN,IAAU,QAASS,EAAIN,EAAO,IACzBH,IAAU,MAAOS,EAAIN,EAAO,OAASI,EACzCE,EAAIN,EAAO,KAAOA,EAAO,OAASI,GAAU,EAG3C,CAAE,EAAAC,EAAG,EAAAC,CAAE,CACf,CAEA,SAASC,EAAkBC,EAAwBP,EAAoBL,EAAYa,EAAoC,CACtH,GAAM,CAAE,QAAAC,EAAS,MAAOC,EAAI,OAAQC,CAAG,EAAIH,EAC3C,OAAIb,IAAS,MAAc,KAAK,IAAI,EAAGc,EAAUF,EAAO,CAAC,EACrDZ,IAAS,SAAiB,KAAK,IAAI,EAAGY,EAAO,EAAIP,EAAK,QAAUW,EAAKF,EAAQ,EAC7Ed,IAAS,OAAe,KAAK,IAAI,EAAGc,EAAUF,EAAO,CAAC,EACnD,KAAK,IAAI,EAAGA,EAAO,EAAIP,EAAK,OAASU,EAAKD,EAAQ,CAC1D,CAEA,SAASG,EAAgBb,EAAiBC,EAAoBL,EAAYa,EAAoC,CAC7G,GAAM,CAAE,QAAAC,EAAS,MAAOC,EAAI,OAAQC,CAAG,EAAIH,EAC3C,OAAIb,IAAS,MAAcI,EAAO,IAAMU,EAAUT,EAAK,OACnDL,IAAS,SAAiBgB,EAAKF,EAAUT,EAAK,OAASD,EAAO,OAC9DJ,IAAS,OAAeI,EAAO,KAAOU,EAAUT,EAAK,MAClDU,EAAKD,EAAUT,EAAK,MAAQD,EAAO,KAC3C,CAEA,SAASc,EACRN,EACAP,EACAL,EACAa,EACiB,CACjB,GAAM,CAAE,QAAAC,EAAS,MAAOC,EAAI,OAAQC,CAAG,EAAIH,EAC3C,GAAIX,EAAYF,CAAI,IAAM,IAAK,CAC9B,IAAMmB,EAAO,KAAK,IAAIL,EAASC,EAAKD,EAAUT,EAAK,KAAK,EACxD,MAAO,CAAE,EAAG,KAAK,IAAI,KAAK,IAAIO,EAAO,EAAGE,CAAO,EAAGK,CAAI,EAAG,EAAGP,EAAO,CAAE,CACtE,CACA,IAAMQ,EAAO,KAAK,IAAIN,EAASE,EAAKF,EAAUT,EAAK,MAAM,EACzD,MAAO,CAAE,EAAGO,EAAO,EAAG,EAAG,KAAK,IAAI,KAAK,IAAIA,EAAO,EAAGE,CAAO,EAAGM,CAAI,CAAE,CACtE,CAUO,SAASC,EACfjB,EACAC,EACAP,EACAQ,EACAO,EAA6B,CAC5B,MAAO,OAAO,OAAW,IAAc,EAAI,OAAO,WAClD,OAAQ,OAAO,OAAW,IAAc,EAAI,OAAO,YACnD,QAASjB,CACV,EACiB,CACjB,GAAM,CAAE,KAAM0B,EAAW,MAAArB,CAAM,EAAIJ,EAAeC,CAAS,EACrDyB,EAAkBpB,EAAUC,EAAQC,EAAMiB,EAAWrB,EAAOK,CAAG,EAC/DkB,EAAoBb,EAAkBY,EAAiBlB,EAAMiB,EAAWT,CAAQ,EAElFb,EAAOsB,EACPV,EAASW,EAEb,GAAIC,EAAoB,EAAG,CAC1B,IAAMC,EAAU9B,EAAS2B,CAAS,EAC5BI,EAAgBvB,EAAUC,EAAQC,EAAMoB,EAASxB,EAAOK,CAAG,EAC3DqB,EAAgBV,EAAgBb,EAAQC,EAAMiB,EAAWT,CAAQ,EACnDI,EAAgBb,EAAQC,EAAMoB,EAASZ,CAAQ,EACjDc,IACjB3B,EAAOyB,EACPb,EAASc,EAEX,CAEA,OAAOR,EAAeN,EAAQP,EAAML,EAAMa,CAAQ,CACnD,CAGO,SAASe,EACfxB,EACAyB,EACA/B,EACAQ,EACO,CACP,IAAMM,EAASS,EACdjB,EAAO,sBAAsB,EAC7B,CAAE,MAAOyB,EAAS,YAAa,OAAQA,EAAS,YAAa,EAC7D/B,EACAQ,CACD,EACA,OAAO,OAAOuB,EAAS,MAAO,CAC7B,SAAU,QACV,KAAM,GAAGjB,EAAO,CAAC,KACjB,IAAK,GAAGA,EAAO,CAAC,KAChB,MAAO,OACP,OAAQ,OACR,WAAY,SACb,CAAC,CACF,CAcO,SAASkB,EAAe,CAAE,OAAA1B,EAAQ,SAAAyB,EAAU,aAAAE,EAAc,IAAAzB,CAAI,EAAsC,CAC1G,IAAM0B,EAAS,IAAY,CACtBH,EAAS,QACbD,EAAsBxB,EAAQyB,EAAUE,EAAa,EAAGzB,CAAG,CAC5D,EAEA0B,EAAO,EAEP,OAAO,iBAAiB,SAAUA,CAAM,EACxC,OAAO,iBAAiB,SAAUA,EAAQ,EAAI,EAE9C,IAAMC,EAAW,IAAI,eAAeD,CAAM,EAC1C,OAAAC,EAAS,QAAQ7B,CAAM,EACvB6B,EAAS,QAAQJ,CAAQ,EAElB,IAAM,CACZ,OAAO,oBAAoB,SAAUG,CAAM,EAC3C,OAAO,oBAAoB,SAAUA,EAAQ,EAAI,EACjDC,EAAS,WAAW,CACrB,CACD,CD7KA,IAAMC,EAAW,EA8BJC,EADb,CAACC,EAAc,iBAAiB,CACzB,cAA4BC,CAAe,CACjD,CAACC,EAAK,CAAE,KAAM,QAAS,QAAS,GAAM,aAAc,EAAM,CAAC,CAAE,KAC7D,CAACA,EAAK,CAAE,KAAM,OAAQ,aAAc,cAAe,CAAC,CAAE,UAEtD,CAACC,EAAM,CAAE,IAAK,SAAU,CAAC,CAAE,cAC3B,CAACA,EAAM,CAAE,IAAK,MAAO,CAAC,CAAE,WAExB,CAACC,EAAM,CAAE,KAAM,aAAc,QAAS,GAAM,SAAU,EAAK,CAAC,CAC5D,YAEA,CAACA,EAAM,CAAE,KAAM,YAAa,QAAS,GAAM,SAAU,EAAK,CAAC,CAC3D,WAEQ,OAAS,YAAY,KAAK,OAAO,EAAE,SAAS,EAAE,EAAE,MAAM,EAAG,CAAC,CAAC,GAC3D,gBAAuC,KACvC,aAAoD,KAEnD,mBAA0B,CAClC,MAAM,kBAAkB,EACxB,eAAe,IAAM,KAAK,cAAc,CAAC,CAC1C,CAES,sBAA6B,CACrC,KAAK,iBAAiB,EACtB,MAAM,qBAAqB,CAC5B,CAEQ,UAA0B,CACjC,OAAO,MAAM,KAAK,KAAK,YAAY,iBAA8B,mBAAmB,GAAK,CAAC,CAAC,EAAE,OAC3FC,GAASA,EAAK,aAAa,eAAe,IAAM,MAClD,CACD,CAEQ,kBAAyB,CAChC,KAAK,kBAAkB,EACvB,KAAK,gBAAkB,IACxB,CAEQ,gBAAuB,CAC1B,CAAC,KAAK,eAAiB,CAAC,KAAK,aACjC,KAAK,iBAAiB,EACtB,KAAK,gBAAkBC,EAAe,CACrC,OAAQ,KAAK,cACb,SAAU,KAAK,WACf,aAAc,IAAM,KAAK,UACzB,IAAKR,CACN,CAAC,EACF,CAEA,CAACS,CACD,CAACC,EAAU,CAAC,OAAQ,WAAW,CAAC,CAChC,eAAsB,CACrB,GAAI,CAAC,KAAK,eAAiB,CAAC,KAAK,WAAY,OAE7C,KAAK,WAAW,GAAK,KAAK,OAC1B,KAAK,cAAc,aAAa,gBAAiB,KAAK,MAAM,EAC5D,KAAK,cAAc,aAAa,gBAAiB,MAAM,EACvD,KAAK,cAAc,aAAa,gBAAiB,OAAO,KAAK,IAAI,CAAC,EAClE,KAAK,WAAW,OAAS,CAAC,KAAK,KAE3B,KAAK,KAAM,KAAK,eAAe,EAC9B,KAAK,iBAAiB,EAE3B,IAAMC,EAAQ,KAAK,aAEnB,GADA,KAAK,aAAe,KAChB,EAACA,EAGL,IADIA,IAAU,SAAS,KAAK,SAAS,EAAE,CAAC,GAAG,MAAM,EAC7CA,IAAU,OAAQ,CACrB,IAAMC,EAAQ,KAAK,SAAS,EAC5BA,EAAMA,EAAM,OAAS,CAAC,GAAG,MAAM,CAChC,CACID,IAAU,WAAW,KAAK,eAAe,MAAM,EACpD,CAEQ,QAAQE,EAAeF,EAA6C,KAAY,CACvF,IAAMG,EAAU,KAAK,KACrB,KAAK,aAAeH,EACpB,KAAK,KAAOE,EACRC,GAAW,CAACD,GAAM,KAAK,WAAW,KAAK,EACvCC,IAAYD,GAAM,KAAK,cAAc,CAC1C,CAEA,CAACE,EAAQ,CAAE,SAAU,GAAM,KAAM,OAAQ,CAAC,CAC1C,gBAAgBT,EAAyB,CACxC,GAAI,CAAC,KAAK,KAAM,OAChB,IAAMU,EAASV,EAAM,OACjB,KAAK,eAAe,SAASU,CAAM,GAAK,KAAK,YAAY,SAASA,CAAM,GAC5E,KAAK,QAAQ,EAAK,CACnB,CAEA,CAACD,EAAQ,CAAE,IAAK,UAAW,KAAM,OAAQ,CAAC,CAC1C,gBAAuB,CACtB,KAAK,QAAQ,CAAC,KAAK,KAAM,KAAK,KAAO,KAAO,OAAO,CACpD,CAEA,CAACA,EAAQ,CAAE,IAAK,UAAW,KAAM,SAAU,CAAC,CAC5C,iBAAiBT,EAA4B,CAC5C,OAAQA,EAAM,IAAK,CAClB,IAAK,YACL,IAAK,QACL,IAAK,IACJA,EAAM,eAAe,EACrB,KAAK,QAAQ,GAAM,OAAO,EAC1B,MACD,IAAK,UACJA,EAAM,eAAe,EACrB,KAAK,QAAQ,GAAM,MAAM,EACzB,MACD,QACC,KACF,CACD,CAEA,CAACS,EAAQ,CAAE,IAAK,OAAQ,KAAM,SAAU,CAAC,CACzC,cAAcT,EAA4B,CACzC,IAAMM,EAAQ,KAAK,SAAS,EACtBK,EAAU,SAAS,cACnBC,EAAQD,EAAUL,EAAM,QAAQK,CAAO,EAAI,GAEjD,OAAQX,EAAM,IAAK,CAClB,IAAK,YAAa,CACjBA,EAAM,eAAe,EACRM,GAAOM,EAAQ,GAAKN,EAAM,MAAM,GACvC,MAAM,EACZ,KACD,CACA,IAAK,UAAW,CACfN,EAAM,eAAe,EACRM,GAAOM,EAAQ,EAAIN,EAAM,QAAUA,EAAM,MAAM,GACtD,MAAM,EACZ,KACD,CACA,IAAK,OACJN,EAAM,eAAe,EACrBM,EAAM,CAAC,GAAG,MAAM,EAChB,MACD,IAAK,MACJN,EAAM,eAAe,EACrBM,EAAMA,EAAM,OAAS,CAAC,GAAG,MAAM,EAC/B,MACD,IAAK,SACJN,EAAM,eAAe,EACrB,KAAK,QAAQ,GAAO,SAAS,EAC7B,MACD,IAAK,MACJ,KAAK,QAAQ,EAAK,EAClB,MACD,IAAK,QACL,IAAK,IACAW,GAAWL,EAAM,SAASK,CAAO,IACpCX,EAAM,eAAe,EACrB,KAAK,aAAaW,CAAO,GAE1B,MACD,QACC,KACF,CACD,CAEA,CAACF,EAAQ,CAAE,SAAU,oBAAqB,KAAM,OAAQ,CAAC,CACzD,YAAYT,EAAoB,CAC/B,IAAMC,EAAQD,EAAM,OAAuB,QAAQ,mBAAmB,EAClE,CAACC,GAAQ,CAAC,KAAK,SAASA,CAAI,GAChC,KAAK,aAAaA,CAAI,CACvB,CAEQ,aAAaA,EAAyB,CAC7C,IAAMY,EAAQZ,EAAK,aAAa,YAAY,GAAKA,EAAK,aAAa,KAAK,GAAK,GAC7E,KAAK,YAAY,KAAK,CAAE,MAAAY,CAAM,CAAC,EAC/B,KAAK,QAAQ,GAAO,SAAS,CAC9B,CAES,QAAS,CACjB,OACC,oBAAC,OAAI,MAAM,mBACV,oBAAC,UACA,KAAK,SACL,WAAS,UACT,MAAM,yEACN,gBAAc,OACd,gBAAc,SAEd,oBAAC,QAAK,KAAK,UAAU,EACrB,oBAAC,QAAK,MAAM,2BAA2B,cAAY,OAAO,CAC3D,EACA,oBAAC,OAAI,WAAS,OAAO,MAAM,qCAAqC,KAAK,OAAO,OAAM,IACjF,oBAAC,WAAK,CACP,CACD,CAEF,CACD,EE5OA,IAAMC,EAAuB,OAAO,IAAI,yCAAyC,EAC3EC,EAAwB,OAAO,IAAI,0CAA0C,EAC7EC,EAAwB,OAAO,IAAI,0CAA0C,EAQnF,SAASC,EACRC,EACAC,EAIO,CACPD,EAAKJ,CAAoB,EAAIK,EAE7B,IAAMC,EAAYD,EAAQJ,CAAqB,EAC3CK,IACHF,EAAKH,CAAqB,EAAIK,EAC9BF,EAAKF,CAAqB,EAAIG,EAAQH,CAAqB,GAAKG,EAAQ,KAE1E,CAGO,SAASE,EACfF,EAIAG,EAC8B,CAC9B,IAAMJ,GAASK,GAAgBD,EAAOC,CAAI,GAC1C,OAAAN,EAAkBC,EAAMC,CAAO,EACxBD,CACR,CClCA,MAAO,oBAQA,IAAMM,EAAgBC,EAC5BD,EACA,CAAC,CACA,KAAAE,EACA,KAAAC,EACA,UAAAC,EACA,QAAAC,EACA,MAAAC,CACD,IAKC,oBAAC,mBAAgB,KAAMJ,EAAM,KAAMC,EAAM,UAAWC,GACnD,oBAAC,QAAK,KAAK,WAAWC,CAAQ,EAC7BC,EAAM,IAAKC,GACX,oBAAC,UACA,KAAK,SACL,MAAM,wBACN,KAAK,WACL,aAAYA,EAAK,MACjB,gBAAeA,EAAK,SAAW,OAAS,OACxC,SAAU,IAETA,EAAK,KACP,CACA,CACF,CAEF",
|
|
6
|
+
"names": ["RadiantElement", "bound", "customElement", "event", "onEvent", "onUpdated", "prop", "query", "OPPOSITE", "DEFAULT_VIEWPORT_PADDING", "parsePlacement", "placement", "dash", "side", "align", "primaryAxis", "coordsFor", "anchor", "size", "gap", "width", "height", "x", "y", "overflowOnPrimary", "coords", "viewport", "padding", "vw", "vh", "freeSpaceOnSide", "clampCrossAxis", "maxX", "maxY", "computeFloatingCoords", "preferred", "preferredCoords", "preferredOverflow", "flipped", "flippedCoords", "preferredFree", "applyFloatingPosition", "floating", "attachFloating", "getPlacement", "update", "observer", "MENU_GAP", "RuiMenuButton", "customElement", "RadiantElement", "prop", "query", "event", "item", "attachFloating", "bound", "onUpdated", "focus", "items", "next", "wasOpen", "onEvent", "target", "current", "index", "value", "RADIANT_VIEW_ELEMENT", "RADIANT_SCRIPT_MODULE", "RADIANT_SCRIPT_EXPORT", "linkViewToElement", "view", "element", "ssrModule", "defineRadiantView", "render", "args", "RuiMenuButton", "defineRadiantView", "slot", "open", "placement", "trigger", "items", "item"]
|
|
7
7
|
}
|
|
@@ -1,43 +1,163 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
/*! tailwindcss v4.3.3 | MIT License | https://tailwindcss.com */
|
|
2
|
+
@layer properties;
|
|
3
|
+
@layer components {
|
|
4
|
+
.rui-floating {
|
|
5
|
+
position: fixed;
|
|
6
|
+
visibility: hidden;
|
|
7
|
+
}
|
|
8
|
+
}
|
|
3
9
|
rui-menu-button {
|
|
4
|
-
|
|
10
|
+
display: inline-flex;
|
|
5
11
|
}
|
|
6
|
-
|
|
7
12
|
@layer components {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
13
|
+
.rui-menu-button {
|
|
14
|
+
position: relative;
|
|
15
|
+
display: inline-flex;
|
|
16
|
+
}
|
|
17
|
+
.rui-menu-button__trigger {
|
|
18
|
+
gap: var(--spacing-inline, var(--space-inline));
|
|
19
|
+
}
|
|
20
|
+
.rui-menu-button__chevron {
|
|
21
|
+
display: inline-block;
|
|
22
|
+
border-inline-style: var(--tw-border-style);
|
|
23
|
+
border-inline-width: 4px;
|
|
24
|
+
border-top-style: var(--tw-border-style);
|
|
25
|
+
border-top-width: 5px;
|
|
26
|
+
border-inline-color: transparent;
|
|
27
|
+
border-top-color: currentcolor;
|
|
28
|
+
width: 0;
|
|
29
|
+
height: 0;
|
|
30
|
+
}
|
|
31
|
+
.rui-menu-button__menu {
|
|
32
|
+
z-index: var(--z-index-popover, var(--z-popover));
|
|
33
|
+
min-width: calc(var(--spacing, 0.25rem) * 40);
|
|
34
|
+
border-radius: var(--radius-container, var(--radius-container));
|
|
35
|
+
border-style: var(--tw-border-style);
|
|
36
|
+
border-width: 1px;
|
|
37
|
+
border-color: var(--color-border, var(--border));
|
|
38
|
+
background-color: var(--color-background, var(--background));
|
|
39
|
+
padding: var(--spacing-1, var(--space-1));
|
|
40
|
+
--tw-shadow: var(--shadow-overlay);
|
|
41
|
+
box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
|
|
42
|
+
}
|
|
43
|
+
.rui-menu-button__menu[hidden] {
|
|
44
|
+
display: none;
|
|
45
|
+
}
|
|
46
|
+
.rui-menu-button__item {
|
|
47
|
+
display: flex;
|
|
48
|
+
width: 100%;
|
|
49
|
+
cursor: pointer;
|
|
50
|
+
align-items: center;
|
|
51
|
+
border-radius: var(--radius-control, var(--radius-control));
|
|
52
|
+
border-style: var(--tw-border-style);
|
|
53
|
+
border-width: 0px;
|
|
54
|
+
background-color: transparent;
|
|
55
|
+
padding-inline: var(--spacing-3, var(--space-3));
|
|
56
|
+
padding-block: var(--spacing-2, var(--space-2));
|
|
57
|
+
text-align: left;
|
|
58
|
+
font-size: var(--text-sm, var(--text-sm));
|
|
59
|
+
line-height: var(--tw-leading, var(--text-sm--line-height, calc(1.25 / 0.875)));
|
|
60
|
+
color: var(--color-on-background, var(--on-background));
|
|
61
|
+
}
|
|
62
|
+
.rui-menu-button__item:hover,
|
|
36
63
|
.rui-menu-button__item:focus-visible {
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
64
|
+
background-color: var(--color-surface, var(--surface));
|
|
65
|
+
--tw-outline-style: none;
|
|
66
|
+
outline-style: none;
|
|
67
|
+
}
|
|
68
|
+
.rui-menu-button__item[aria-disabled='true'] {
|
|
69
|
+
cursor: not-allowed;
|
|
70
|
+
opacity: var(--opacity-disabled, var(--opacity-disabled));
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
@property --tw-border-style {
|
|
74
|
+
syntax: "*";
|
|
75
|
+
inherits: false;
|
|
76
|
+
initial-value: solid;
|
|
77
|
+
}
|
|
78
|
+
@property --tw-shadow {
|
|
79
|
+
syntax: "*";
|
|
80
|
+
inherits: false;
|
|
81
|
+
initial-value: 0 0 #0000;
|
|
82
|
+
}
|
|
83
|
+
@property --tw-shadow-color {
|
|
84
|
+
syntax: "*";
|
|
85
|
+
inherits: false;
|
|
86
|
+
}
|
|
87
|
+
@property --tw-shadow-alpha {
|
|
88
|
+
syntax: "<percentage>";
|
|
89
|
+
inherits: false;
|
|
90
|
+
initial-value: 100%;
|
|
91
|
+
}
|
|
92
|
+
@property --tw-inset-shadow {
|
|
93
|
+
syntax: "*";
|
|
94
|
+
inherits: false;
|
|
95
|
+
initial-value: 0 0 #0000;
|
|
96
|
+
}
|
|
97
|
+
@property --tw-inset-shadow-color {
|
|
98
|
+
syntax: "*";
|
|
99
|
+
inherits: false;
|
|
100
|
+
}
|
|
101
|
+
@property --tw-inset-shadow-alpha {
|
|
102
|
+
syntax: "<percentage>";
|
|
103
|
+
inherits: false;
|
|
104
|
+
initial-value: 100%;
|
|
105
|
+
}
|
|
106
|
+
@property --tw-ring-color {
|
|
107
|
+
syntax: "*";
|
|
108
|
+
inherits: false;
|
|
109
|
+
}
|
|
110
|
+
@property --tw-ring-shadow {
|
|
111
|
+
syntax: "*";
|
|
112
|
+
inherits: false;
|
|
113
|
+
initial-value: 0 0 #0000;
|
|
114
|
+
}
|
|
115
|
+
@property --tw-inset-ring-color {
|
|
116
|
+
syntax: "*";
|
|
117
|
+
inherits: false;
|
|
118
|
+
}
|
|
119
|
+
@property --tw-inset-ring-shadow {
|
|
120
|
+
syntax: "*";
|
|
121
|
+
inherits: false;
|
|
122
|
+
initial-value: 0 0 #0000;
|
|
123
|
+
}
|
|
124
|
+
@property --tw-ring-inset {
|
|
125
|
+
syntax: "*";
|
|
126
|
+
inherits: false;
|
|
127
|
+
}
|
|
128
|
+
@property --tw-ring-offset-width {
|
|
129
|
+
syntax: "<length>";
|
|
130
|
+
inherits: false;
|
|
131
|
+
initial-value: 0px;
|
|
132
|
+
}
|
|
133
|
+
@property --tw-ring-offset-color {
|
|
134
|
+
syntax: "*";
|
|
135
|
+
inherits: false;
|
|
136
|
+
initial-value: #fff;
|
|
137
|
+
}
|
|
138
|
+
@property --tw-ring-offset-shadow {
|
|
139
|
+
syntax: "*";
|
|
140
|
+
inherits: false;
|
|
141
|
+
initial-value: 0 0 #0000;
|
|
142
|
+
}
|
|
143
|
+
@layer properties {
|
|
144
|
+
@supports ((-webkit-hyphens: none) and (not (margin-trim: inline))) or ((-moz-orient: inline) and (not (color:rgb(from red r g b)))) {
|
|
145
|
+
*, ::before, ::after, ::backdrop {
|
|
146
|
+
--tw-border-style: solid;
|
|
147
|
+
--tw-shadow: 0 0 #0000;
|
|
148
|
+
--tw-shadow-color: initial;
|
|
149
|
+
--tw-shadow-alpha: 100%;
|
|
150
|
+
--tw-inset-shadow: 0 0 #0000;
|
|
151
|
+
--tw-inset-shadow-color: initial;
|
|
152
|
+
--tw-inset-shadow-alpha: 100%;
|
|
153
|
+
--tw-ring-color: initial;
|
|
154
|
+
--tw-ring-shadow: 0 0 #0000;
|
|
155
|
+
--tw-inset-ring-color: initial;
|
|
156
|
+
--tw-inset-ring-shadow: 0 0 #0000;
|
|
157
|
+
--tw-ring-inset: initial;
|
|
158
|
+
--tw-ring-offset-width: 0px;
|
|
159
|
+
--tw-ring-offset-color: #fff;
|
|
160
|
+
--tw-ring-offset-shadow: 0 0 #0000;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
43
163
|
}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { RadiantElement } from '@ecopages/radiant';
|
|
2
2
|
import type { EventEmitter } from '@ecopages/radiant/tools/event-emitter';
|
|
3
|
-
import {
|
|
3
|
+
import type { RuiPlacement } from '../shared/placement';
|
|
4
4
|
export type RuiMenuButtonProps = {
|
|
5
5
|
/** Whether the menu starts open. Default: `false`. */
|
|
6
6
|
open?: boolean;
|
|
7
|
-
/**
|
|
8
|
-
placement?:
|
|
7
|
+
/** Placement of the menu surface relative to its trigger. Default: `bottom-start`. */
|
|
8
|
+
placement?: RuiPlacement;
|
|
9
9
|
};
|
|
10
10
|
export type RuiMenuButtonSelectDetail = {
|
|
11
11
|
value: string;
|
|
@@ -39,19 +39,19 @@ export type RuiMenuButtonSelectDetail = {
|
|
|
39
39
|
*/
|
|
40
40
|
export declare class RuiMenuButton extends RadiantElement {
|
|
41
41
|
open: boolean;
|
|
42
|
-
placement:
|
|
42
|
+
placement: RuiPlacement;
|
|
43
43
|
triggerTarget: HTMLButtonElement;
|
|
44
44
|
menuTarget: HTMLElement;
|
|
45
45
|
changeEvent: EventEmitter<RuiMenuButtonSelectDetail>;
|
|
46
46
|
closeEvent: EventEmitter<void>;
|
|
47
|
-
private cleanup;
|
|
48
47
|
private menuId;
|
|
48
|
+
private cleanupFloating;
|
|
49
|
+
private pendingFocus;
|
|
49
50
|
connectedCallback(): void;
|
|
50
51
|
disconnectedCallback(): void;
|
|
51
52
|
private getItems;
|
|
52
53
|
private teardownFloating;
|
|
53
|
-
|
|
54
|
-
private pendingFocus;
|
|
54
|
+
private attachFloating;
|
|
55
55
|
syncOpenState(): void;
|
|
56
56
|
private setOpen;
|
|
57
57
|
onDocumentClick(event: MouseEvent): void;
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{RadiantElement as R,customElement as E,event as T,onEvent as u,prop as y}from"@ecopages/radiant";function p(r,e){r.forEach((t,n)=>{t.tabIndex=n===e?0:-1})}function M(r,e){if(!r.length)return null;let t=Math.max(0,Math.min(r.length-1,e));return p(r,t),r[t]?.focus(),r[t]??null}function h(r,e){return r==="Home"?"first":r==="End"?"last":e==="horizontal"?r==="ArrowRight"?"next":r==="ArrowLeft"?"prev":null:r==="ArrowDown"?"next":r==="ArrowUp"?"prev":null}function d(r){let{items:e,current:t,key:n,orientation:o="horizontal",wrap:l=!0}=r;if(!e.length||!t)return{handled:!1};let a=e.indexOf(t);if(a<0)return{handled:!1};let s=h(n,o);if(!s)return{handled:!1};let i=a;s==="first"?i=0:s==="last"?i=e.length-1:s==="next"?i=l?(a+1)%e.length:Math.min(e.length-1,a+1):i=l?(a-1+e.length)%e.length:Math.max(0,a-1);let c=M(e,i);return c?{handled:!0,index:i,item:c}:{handled:!1}}var m=@E("rui-menubar")class extends R{@y({type:String,defaultValue:""})label;@T({name:"rui-change",bubbles:!0,composed:!0})changeEvent;resolvedAriaLabel=this.$.label.map(e=>e||void 0);openRoot=null;getTopItems(){return Array.from(this.querySelectorAll('[data-ref="menubar"] > [data-ref="menubar-root"] > [role="menuitem"]'))}getMenuFor(e){return e.closest('[data-ref="menubar-root"]')?.querySelector(':scope > [role="menu"]')??null}getMenuItems(e){return Array.from(e.querySelectorAll(':scope > [role="menuitem"]')).filter(t=>t.getAttribute("aria-disabled")!=="true")}connectedCallback(){super.connectedCallback(),queueMicrotask(()=>p(this.getTopItems(),0))}closeOpenMenu(e=!1){if(!this.openRoot)return;let t=this.openRoot.querySelector(':scope > [role="menuitem"]'),n=this.openRoot.querySelector(':scope > [role="menu"]');t&&t.setAttribute("aria-expanded","false"),n&&(n.hidden=!0),this.openRoot=null,e&&t&&t.focus()}openMenu(e,t="first"){let n=e.closest('[data-ref="menubar-root"]'),o=this.getMenuFor(e);if(!(!n||!o)&&(this.openRoot&&this.openRoot!==n&&this.closeOpenMenu(),e.setAttribute("aria-expanded","true"),o.hidden=!1,this.openRoot=n,t==="first")){let l=this.getMenuItems(o);p(l,0),l[0]?.focus()}}@u({document:!0,type:"click"})onDocumentClick(e){this.openRoot&&(this.contains(e.target)||this.closeOpenMenu())}@u({selector:'[data-ref="menubar"] > [data-ref="menubar-root"] > [role="menuitem"]',type:"keydown"})onTopKeydown(e){let t=this.getTopItems(),n=e.target.closest('[role="menuitem"]');if(!n||!t.includes(n))return;if(e.key==="ArrowDown"||e.key==="Enter"||e.key===" "){this.getMenuFor(n)&&(e.preventDefault(),this.openMenu(n,"first"));return}if(e.key==="Escape"){e.preventDefault(),this.closeOpenMenu(!0);return}let o=d({items:t,current:n,key:e.key,orientation:"horizontal"});o.handled&&(e.preventDefault(),this.openRoot&&(this.getMenuFor(o.item)?this.openMenu(o.item,"first"):this.closeOpenMenu()))}@u({selector:'[data-ref="menubar"] > [data-ref="menubar-root"] > [role="menuitem"]',type:"click"})onTopClick(e){let t=e.target.closest('[role="menuitem"]');if(!t||!this.getTopItems().includes(t)
|
|
1
|
+
import{RadiantElement as R,customElement as E,event as T,onEvent as u,prop as y}from"@ecopages/radiant";function p(r,e){r.forEach((t,n)=>{t.tabIndex=n===e?0:-1})}function M(r,e){if(!r.length)return null;let t=Math.max(0,Math.min(r.length-1,e));return p(r,t),r[t]?.focus(),r[t]??null}function h(r,e){return r==="Home"?"first":r==="End"?"last":e==="horizontal"?r==="ArrowRight"?"next":r==="ArrowLeft"?"prev":null:r==="ArrowDown"?"next":r==="ArrowUp"?"prev":null}function d(r){let{items:e,current:t,key:n,orientation:o="horizontal",wrap:l=!0}=r;if(!e.length||!t)return{handled:!1};let a=e.indexOf(t);if(a<0)return{handled:!1};let s=h(n,o);if(!s)return{handled:!1};let i=a;s==="first"?i=0:s==="last"?i=e.length-1:s==="next"?i=l?(a+1)%e.length:Math.min(e.length-1,a+1):i=l?(a-1+e.length)%e.length:Math.max(0,a-1);let c=M(e,i);return c?{handled:!0,index:i,item:c}:{handled:!1}}var m=@E("rui-menubar")class extends R{@y({type:String,defaultValue:""})label;@T({name:"rui-change",bubbles:!0,composed:!0})changeEvent;resolvedAriaLabel=this.$.label.map(e=>e||void 0);openRoot=null;getTopItems(){return Array.from(this.querySelectorAll('[data-ref="menubar"] > [data-ref="menubar-root"] > [role="menuitem"]'))}getMenuFor(e){return e.closest('[data-ref="menubar-root"]')?.querySelector(':scope > [role="menu"]')??null}getMenuItems(e){return Array.from(e.querySelectorAll(':scope > [role="menuitem"]')).filter(t=>t.getAttribute("aria-disabled")!=="true")}connectedCallback(){super.connectedCallback(),queueMicrotask(()=>p(this.getTopItems(),0))}closeOpenMenu(e=!1){if(!this.openRoot)return;let t=this.openRoot.querySelector(':scope > [role="menuitem"]'),n=this.openRoot.querySelector(':scope > [role="menu"]');t&&t.setAttribute("aria-expanded","false"),n&&(n.hidden=!0),this.openRoot=null,e&&t&&t.focus()}openMenu(e,t="first"){let n=e.closest('[data-ref="menubar-root"]'),o=this.getMenuFor(e);if(!(!n||!o)&&(this.openRoot&&this.openRoot!==n&&this.closeOpenMenu(),e.setAttribute("aria-expanded","true"),o.hidden=!1,this.openRoot=n,t==="first")){let l=this.getMenuItems(o);p(l,0),l[0]?.focus()}}@u({document:!0,type:"click"})onDocumentClick(e){this.openRoot&&(this.contains(e.target)||this.closeOpenMenu())}@u({selector:'[data-ref="menubar"] > [data-ref="menubar-root"] > [role="menuitem"]',type:"keydown"})onTopKeydown(e){let t=this.getTopItems(),n=e.target.closest('[role="menuitem"]');if(!n||!t.includes(n))return;if(e.key==="ArrowDown"||e.key==="Enter"||e.key===" "){this.getMenuFor(n)&&(e.preventDefault(),this.openMenu(n,"first"));return}if(e.key==="Escape"){e.preventDefault(),this.closeOpenMenu(!0);return}let o=d({items:t,current:n,key:e.key,orientation:"horizontal"});o.handled&&(e.preventDefault(),this.openRoot&&(this.getMenuFor(o.item)?this.openMenu(o.item,"first"):this.closeOpenMenu()))}@u({selector:'[data-ref="menubar"] > [data-ref="menubar-root"] > [role="menuitem"]',type:"click"})onTopClick(e){let t=e.target.closest('[role="menuitem"]');if(!t||!this.getTopItems().includes(t))return;if(!this.getMenuFor(t)){this.closeOpenMenu();return}t.getAttribute("aria-expanded")==="true"?this.closeOpenMenu():this.openMenu(t,"first")}@u({selector:'[role="menu"] > [role="menuitem"]',type:"keydown"})onMenuKeydown(e){let t=e.target.closest('[role="menu"]'),n=e.target.closest('[role="menuitem"]');if(!t||!n||!this.contains(t))return;let o=this.getMenuItems(t);if(e.key==="Escape"){e.preventDefault(),this.closeOpenMenu(!0);return}if(e.key==="Enter"||e.key===" "){e.preventDefault(),this.activateItem(n);return}if(e.key==="ArrowLeft"||e.key==="ArrowRight"){let a=this.getTopItems(),s=t.parentElement?.querySelector(':scope > [role="menuitem"]');if(!s)return;e.preventDefault();let i=d({items:a,current:s,key:e.key,orientation:"horizontal"});i.handled&&this.getMenuFor(i.item)&&this.openMenu(i.item,"first");return}d({items:o,current:n,key:e.key,orientation:"vertical"}).handled&&e.preventDefault()}@u({selector:'[role="menu"] > [role="menuitem"]',type:"click"})onMenuItemClick(e){let t=e.target.closest('[role="menuitem"]');!t||!this.contains(t)||t.parentElement?.getAttribute("role")==="menu"&&this.activateItem(t)}activateItem(e){let t=e.getAttribute("data-value")||e.textContent?.trim()||"";this.changeEvent.emit({value:t}),this.closeOpenMenu(!0)}render(){return React.createElement("div",{class:"rui-menubar","data-ref":"menubar",role:"menubar","aria-label":this.resolvedAriaLabel},React.createElement("slot",null))}};var v=Symbol.for("@ecopages/storybook-radiant.viewElement"),f=Symbol.for("@ecopages/storybook-radiant.scriptModule"),b=Symbol.for("@ecopages/storybook-radiant.scriptExport");function x(r,e){r[v]=e;let t=e[f];t&&(r[f]=t,r[b]=e[b]??e.name)}function g(r,e){let t=(n=>e(n));return x(t,r),t}import"./menubar.css";var A=g(m,({slot:r,label:e,items:t})=>React.createElement("rui-menubar",{slot:r,label:e},t.map(n=>React.createElement("div",{class:"rui-menubar__root","data-ref":"menubar-root"},React.createElement("button",{type:"button",class:"rui-menubar__item",role:"menuitem",tabindex:-1,"aria-haspopup":n.items?.length?"true":void 0,"aria-expanded":n.items?.length?"false":void 0},n.label),n.items?.length?React.createElement("div",{class:"rui-menubar__menu",role:"menu",hidden:!0},n.items.map(o=>React.createElement("button",{type:"button",class:"rui-menubar__menu-item",role:"menuitem","data-value":o.id,tabindex:-1},o.label))):null))));export{A as RuiMenubar,m as RuiMenubarElement};
|
|
2
2
|
//# sourceMappingURL=index.js.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/components/ui/menubar/menubar.script.tsx", "../../../../src/lib/roving-tabindex.ts", "../../../../src/lib/radiant-view.ts", "../../../../src/components/ui/menubar/menubar.tsx"],
|
|
4
|
-
"sourcesContent": ["import { RadiantElement, customElement, event, onEvent, prop } from '@ecopages/radiant';\nimport type { EventEmitter } from '@ecopages/radiant/tools/event-emitter';\nimport { applyRovingTabindex, navigateRovingTabindex } from '../../../lib/roving-tabindex';\n\nexport type RuiMenubarProps = {\n\tlabel?: string;\n};\n\nexport type RuiMenubarChangeDetail = { value: string };\n\ntype RuiMenubarBindings = {\n\tlabel: string;\n};\n\n/**\n * `<rui-menubar>` \u2014 a horizontal menubar of menus.\n *\n * Implements the APG Menubar pattern: top-level items use `role=\"menuitem\"` with\n * `aria-haspopup` / `aria-expanded` when they own a nested `[role=\"menu\"]`. Arrow\n * keys move across the bar; Enter/Space/ArrowDown open the popup; Escape closes it.\n *\n * Expected markup (also produced by the JSX view helper):\n * ```html\n * <div class=\"rui-menubar__root\" data-ref=\"menubar-root\">\n * <button role=\"menuitem\" aria-haspopup=\"true\" aria-expanded=\"false\">File</button>\n * <div role=\"menu\" hidden>\n * <button role=\"menuitem\" data-value=\"new\">New</button>\n * </div>\n * </div>\n * ```\n *\n * @see https://www.w3.org/WAI/ARIA/apg/patterns/menubar/\n * @element rui-menubar\n * @fires rui-change\n */\n@customElement('rui-menubar')\nexport class RuiMenubar extends RadiantElement<RuiMenubarBindings> {\n\t@prop({ type: String, defaultValue: '' }) label: string;\n\n\t@event({ name: 'rui-change', bubbles: true, composed: true })\n\tchangeEvent: EventEmitter<RuiMenubarChangeDetail>;\n\n\tprivate readonly resolvedAriaLabel = this.$.label.map((label) => label || undefined);\n\n\tprivate openRoot: HTMLElement | null = null;\n\n\tprivate getTopItems(): HTMLElement[] {\n\t\treturn Array.from(\n\t\t\tthis.querySelectorAll<HTMLElement>('[data-ref=\"menubar\"] > [data-ref=\"menubar-root\"] > [role=\"menuitem\"]'),\n\t\t);\n\t}\n\n\tprivate getMenuFor(topItem: HTMLElement): HTMLElement | null {\n\t\tconst root = topItem.closest('[data-ref=\"menubar-root\"]');\n\t\treturn root?.querySelector<HTMLElement>(':scope > [role=\"menu\"]') ?? null;\n\t}\n\n\tprivate getMenuItems(menu: HTMLElement): HTMLElement[] {\n\t\treturn Array.from(menu.querySelectorAll<HTMLElement>(':scope > [role=\"menuitem\"]')).filter(\n\t\t\t(item) => item.getAttribute('aria-disabled') !== 'true',\n\t\t);\n\t}\n\n\toverride connectedCallback(): void {\n\t\tsuper.connectedCallback();\n\t\tqueueMicrotask(() => applyRovingTabindex(this.getTopItems(), 0));\n\t}\n\n\tprivate closeOpenMenu(returnFocus = false): void {\n\t\tif (!this.openRoot) return;\n\t\tconst top = this.openRoot.querySelector<HTMLElement>(':scope > [role=\"menuitem\"]');\n\t\tconst menu = this.openRoot.querySelector<HTMLElement>(':scope > [role=\"menu\"]');\n\t\tif (top) top.setAttribute('aria-expanded', 'false');\n\t\tif (menu) menu.hidden = true;\n\t\tthis.openRoot = null;\n\t\tif (returnFocus && top) top.focus();\n\t}\n\n\tprivate openMenu(topItem: HTMLElement, focus: 'first' | null = 'first'): void {\n\t\tconst root = topItem.closest('[data-ref=\"menubar-root\"]') as HTMLElement | null;\n\t\tconst menu = this.getMenuFor(topItem);\n\t\tif (!root || !menu) return;\n\n\t\tif (this.openRoot && this.openRoot !== root) this.closeOpenMenu();\n\n\t\ttopItem.setAttribute('aria-expanded', 'true');\n\t\tmenu.hidden = false;\n\t\tthis.openRoot = root;\n\n\t\tif (focus === 'first') {\n\t\t\tconst items = this.getMenuItems(menu);\n\t\t\tapplyRovingTabindex(items, 0);\n\t\t\titems[0]?.focus();\n\t\t}\n\t}\n\n\t@onEvent({ document: true, type: 'click' })\n\tonDocumentClick(event: MouseEvent): void {\n\t\tif (!this.openRoot) return;\n\t\tif (!this.contains(event.target as Node)) this.closeOpenMenu();\n\t}\n\n\t@onEvent({ selector: '[data-ref=\"menubar\"] > [data-ref=\"menubar-root\"] > [role=\"menuitem\"]', type: 'keydown' })\n\tonTopKeydown(event: KeyboardEvent): void {\n\t\tconst items = this.getTopItems();\n\t\tconst current = (event.target as HTMLElement).closest('[role=\"menuitem\"]') as HTMLElement | null;\n\t\tif (!current || !items.includes(current)) return;\n\n\t\tif (event.key === 'ArrowDown' || event.key === 'Enter' || event.key === ' ') {\n\t\t\tif (this.getMenuFor(current)) {\n\t\t\t\tevent.preventDefault();\n\t\t\t\tthis.openMenu(current, 'first');\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\tif (event.key === 'Escape') {\n\t\t\tevent.preventDefault();\n\t\t\tthis.closeOpenMenu(true);\n\t\t\treturn;\n\t\t}\n\n\t\tconst result = navigateRovingTabindex({\n\t\t\titems,\n\t\t\tcurrent,\n\t\t\tkey: event.key,\n\t\t\torientation: 'horizontal',\n\t\t});\n\t\tif (!result.handled) return;\n\t\tevent.preventDefault();\n\n\t\t// APG: when a menu is open, moving across the bar opens the newly focused menu.\n\t\tif (this.openRoot) {\n\t\t\tif (this.getMenuFor(result.item)) this.openMenu(result.item, 'first');\n\t\t\telse this.closeOpenMenu();\n\t\t}\n\t}\n\n\t@onEvent({ selector: '[data-ref=\"menubar\"] > [data-ref=\"menubar-root\"] > [role=\"menuitem\"]', type: 'click' })\n\tonTopClick(event: Event): void {\n\t\tconst current = (event.target as HTMLElement).closest('[role=\"menuitem\"]') as HTMLElement | null;\n\t\tif (!current || !this.getTopItems().includes(current)) return;\n\t\tif (!this.getMenuFor(current)) return;\n\n\t\tconst expanded = current.getAttribute('aria-expanded') === 'true';\n\t\tif (expanded) this.closeOpenMenu();\n\t\telse this.openMenu(current, 'first');\n\t}\n\n\t@onEvent({ selector: '[role=\"menu\"] > [role=\"menuitem\"]', type: 'keydown' })\n\tonMenuKeydown(event: KeyboardEvent): void {\n\t\tconst menu = (event.target as HTMLElement).closest('[role=\"menu\"]') as HTMLElement | null;\n\t\tconst current = (event.target as HTMLElement).closest('[role=\"menuitem\"]') as HTMLElement | null;\n\t\tif (!menu || !current || !this.contains(menu)) return;\n\n\t\tconst items = this.getMenuItems(menu);\n\n\t\tif (event.key === 'Escape') {\n\t\t\tevent.preventDefault();\n\t\t\tthis.closeOpenMenu(true);\n\t\t\treturn;\n\t\t}\n\n\t\tif (event.key === 'Enter' || event.key === ' ') {\n\t\t\tevent.preventDefault();\n\t\t\tthis.activateItem(current);\n\t\t\treturn;\n\t\t}\n\n\t\tif (event.key === 'ArrowLeft' || event.key === 'ArrowRight') {\n\t\t\tconst tops = this.getTopItems();\n\t\t\tconst top = menu.parentElement?.querySelector<HTMLElement>(':scope > [role=\"menuitem\"]');\n\t\t\tif (!top) return;\n\t\t\tevent.preventDefault();\n\t\t\tconst result = navigateRovingTabindex({\n\t\t\t\titems: tops,\n\t\t\t\tcurrent: top,\n\t\t\t\tkey: event.key,\n\t\t\t\torientation: 'horizontal',\n\t\t\t});\n\t\t\tif (result.handled && this.getMenuFor(result.item)) this.openMenu(result.item, 'first');\n\t\t\treturn;\n\t\t}\n\n\t\tconst result = navigateRovingTabindex({\n\t\t\titems,\n\t\t\tcurrent,\n\t\t\tkey: event.key,\n\t\t\torientation: 'vertical',\n\t\t});\n\t\tif (result.handled) event.preventDefault();\n\t}\n\n\t@onEvent({ selector: '[role=\"menu\"] > [role=\"menuitem\"]', type: 'click' })\n\tonMenuItemClick(event: Event): void {\n\t\tconst item = (event.target as HTMLElement).closest('[role=\"menuitem\"]') as HTMLElement | null;\n\t\tif (!item || !this.contains(item)) return;\n\t\t// Ignore top-level items (they are not direct children of role=menu in our markup).\n\t\tif (item.parentElement?.getAttribute('role') !== 'menu') return;\n\t\tthis.activateItem(item);\n\t}\n\n\tprivate activateItem(item: HTMLElement): void {\n\t\tconst value = item.getAttribute('data-value') || item.textContent?.trim() || '';\n\t\tthis.changeEvent.emit({ value });\n\t\tthis.closeOpenMenu(true);\n\t}\n\n\toverride render() {\n\t\treturn (\n\t\t\t<div class=\"rui-menubar\" data-ref=\"menubar\" role=\"menubar\" aria-label={this.resolvedAriaLabel}>\n\t\t\t\t<slot></slot>\n\t\t\t</div>\n\t\t);\n\t}\n}\n", "export type RovingOrientation = 'horizontal' | 'vertical';\n\nexport type RovingNavResult = { handled: false } | { handled: true; index: number; item: HTMLElement };\n\n/**\n * Apply the APG roving-tabindex pattern: one item is tabbable, the rest are `-1`.\n */\nexport function applyRovingTabindex(items: HTMLElement[], activeIndex: number): void {\n\titems.forEach((item, index) => {\n\t\titem.tabIndex = index === activeIndex ? 0 : -1;\n\t});\n}\n\n/**\n * Focus `items[index]` and update tabindexes. Returns the focused item, or null.\n */\nexport function focusRovingItem(items: HTMLElement[], index: number): HTMLElement | null {\n\tif (!items.length) return null;\n\tconst next = Math.max(0, Math.min(items.length - 1, index));\n\tapplyRovingTabindex(items, next);\n\titems[next]?.focus();\n\treturn items[next] ?? null;\n}\n\nfunction deltaForKey(key: string, orientation: RovingOrientation): 'prev' | 'next' | 'first' | 'last' | null {\n\tif (key === 'Home') return 'first';\n\tif (key === 'End') return 'last';\n\n\tif (orientation === 'horizontal') {\n\t\tif (key === 'ArrowRight') return 'next';\n\t\tif (key === 'ArrowLeft') return 'prev';\n\t\treturn null;\n\t}\n\n\tif (key === 'ArrowDown') return 'next';\n\tif (key === 'ArrowUp') return 'prev';\n\treturn null;\n}\n\n/**\n * Handle arrow / Home / End navigation for a flat roving-tabindex collection.\n * Does not activate the item \u2014 callers decide whether to select on move.\n */\nexport function navigateRovingTabindex(options: {\n\titems: HTMLElement[];\n\tcurrent: HTMLElement | null;\n\tkey: string;\n\torientation?: RovingOrientation;\n\twrap?: boolean;\n}): RovingNavResult {\n\tconst { items, current, key, orientation = 'horizontal', wrap = true } = options;\n\tif (!items.length || !current) return { handled: false };\n\n\tconst index = items.indexOf(current);\n\tif (index < 0) return { handled: false };\n\n\tconst delta = deltaForKey(key, orientation);\n\tif (!delta) return { handled: false };\n\n\tlet next = index;\n\tif (delta === 'first') next = 0;\n\telse if (delta === 'last') next = items.length - 1;\n\telse if (delta === 'next') next = wrap ? (index + 1) % items.length : Math.min(items.length - 1, index + 1);\n\telse next = wrap ? (index - 1 + items.length) % items.length : Math.max(0, index - 1);\n\n\tconst item = focusRovingItem(items, next);\n\tif (!item) return { handled: false };\n\treturn { handled: true, index: next, item };\n}\n", "import type { JsxRenderable } from '@ecopages/jsx';\n\nconst RADIANT_VIEW_ELEMENT = Symbol.for('@ecopages/storybook-radiant.viewElement');\nconst RADIANT_SCRIPT_MODULE = Symbol.for('@ecopages/storybook-radiant.scriptModule');\nconst RADIANT_SCRIPT_EXPORT = Symbol.for('@ecopages/storybook-radiant.scriptExport');\n\nexport type RadiantViewComponent<TArgs = unknown> = ((args: TArgs) => JsxRenderable) & {\n\t[RADIANT_VIEW_ELEMENT]?: CustomElementConstructor;\n\t[RADIANT_SCRIPT_MODULE]?: string;\n\t[RADIANT_SCRIPT_EXPORT]?: string;\n};\n\nfunction linkViewToElement<TArgs>(\n\tview: RadiantViewComponent<TArgs>,\n\telement: CustomElementConstructor & {\n\t\t[RADIANT_SCRIPT_MODULE]?: string;\n\t\t[RADIANT_SCRIPT_EXPORT]?: string;\n\t},\n): void {\n\tview[RADIANT_VIEW_ELEMENT] = element;\n\n\tconst ssrModule = element[RADIANT_SCRIPT_MODULE];\n\tif (ssrModule) {\n\t\tview[RADIANT_SCRIPT_MODULE] = ssrModule;\n\t\tview[RADIANT_SCRIPT_EXPORT] = element[RADIANT_SCRIPT_EXPORT] ?? element.name;\n\t}\n}\n\n/** Link a JSX view export to its RadiantElement class (same symbols as the Storybook framework). */\nexport function defineRadiantView<TArgs>(\n\telement: CustomElementConstructor & {\n\t\t[RADIANT_SCRIPT_MODULE]?: string;\n\t\t[RADIANT_SCRIPT_EXPORT]?: string;\n\t},\n\trender: (args: TArgs) => JsxRenderable,\n): RadiantViewComponent<TArgs> {\n\tconst view = ((args: TArgs) => render(args)) as RadiantViewComponent<TArgs>;\n\tlinkViewToElement(view, element);\n\treturn view;\n}\n\n/** Re-copy SSR metadata from the linked element (e.g. after HMR). */\nexport function syncRadiantViewMetadata(view: RadiantViewComponent): void {\n\tconst element = view[RADIANT_VIEW_ELEMENT];\n\tif (element) {\n\t\tlinkViewToElement(view, element);\n\t}\n}\n", "import type { JsxRenderable } from '@ecopages/jsx';\nimport type { RadiantSlotProps } from '../../../types';\nimport { defineRadiantView } from '../../../lib/radiant-view';\nimport type { RuiMenubarProps } from './menubar.script';\nimport { RuiMenubar as RuiMenubarElement } from './menubar.script';\nimport './menubar.css';\n\nexport type RuiMenubarItem = {\n\tid: string;\n\tlabel: JsxRenderable;\n\t/** Popup actions. When present, the top item opens a `role=\"menu\"`. */\n\titems?: Array<{ id: string; label: JsxRenderable }>;\n};\n\nexport const RuiMenubar = defineRadiantView(\n\tRuiMenubarElement,\n\t({ slot, label, items }: RuiMenubarProps & RadiantSlotProps & { items: RuiMenubarItem[] }) => (\n\t\t<rui-menubar slot={slot} label={label}>\n\t\t\t{items.map((item) => (\n\t\t\t\t<div class=\"rui-menubar__root\" data-ref=\"menubar-root\">\n\t\t\t\t\t<button\n\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\tclass=\"rui-menubar__item\"\n\t\t\t\t\t\trole=\"menuitem\"\n\t\t\t\t\t\ttabindex={-1}\n\t\t\t\t\t\taria-haspopup={item.items?.length ? 'true' : undefined}\n\t\t\t\t\t\taria-expanded={item.items?.length ? 'false' : undefined}\n\t\t\t\t\t>\n\t\t\t\t\t\t{item.label}\n\t\t\t\t\t</button>\n\t\t\t\t\t{item.items?.length ? (\n\t\t\t\t\t\t<div class=\"rui-menubar__menu\" role=\"menu\" hidden>\n\t\t\t\t\t\t\t{item.items.map((child) => (\n\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\tclass=\"rui-menubar__menu-item\"\n\t\t\t\t\t\t\t\t\trole=\"menuitem\"\n\t\t\t\t\t\t\t\t\tdata-value={child.id}\n\t\t\t\t\t\t\t\t\ttabindex={-1}\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t{child.label}\n\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t))}\n\t\t\t\t\t\t</div>\n\t\t\t\t\t) : null}\n\t\t\t\t</div>\n\t\t\t))}\n\t\t</rui-menubar>\n\t),\n);\n"],
|
|
5
|
-
"mappings": "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
|
|
4
|
+
"sourcesContent": ["import { RadiantElement, customElement, event, onEvent, prop } from '@ecopages/radiant';\nimport type { EventEmitter } from '@ecopages/radiant/tools/event-emitter';\nimport { applyRovingTabindex, navigateRovingTabindex } from '../../../lib/roving-tabindex';\n\nexport type RuiMenubarProps = {\n\tlabel?: string;\n};\n\nexport type RuiMenubarChangeDetail = { value: string };\n\ntype RuiMenubarBindings = {\n\tlabel: string;\n};\n\n/**\n * `<rui-menubar>` \u2014 a horizontal menubar of menus.\n *\n * Implements the APG Menubar pattern: top-level items use `role=\"menuitem\"` with\n * `aria-haspopup` / `aria-expanded` when they own a nested `[role=\"menu\"]`. Arrow\n * keys move across the bar; Enter/Space/ArrowDown open the popup; Escape closes it.\n *\n * Expected markup (also produced by the JSX view helper):\n * ```html\n * <div class=\"rui-menubar__root\" data-ref=\"menubar-root\">\n * <button role=\"menuitem\" aria-haspopup=\"true\" aria-expanded=\"false\">File</button>\n * <div role=\"menu\" hidden>\n * <button role=\"menuitem\" data-value=\"new\">New</button>\n * </div>\n * </div>\n * ```\n *\n * @see https://www.w3.org/WAI/ARIA/apg/patterns/menubar/\n * @element rui-menubar\n * @fires rui-change\n */\n@customElement('rui-menubar')\nexport class RuiMenubar extends RadiantElement<RuiMenubarBindings> {\n\t@prop({ type: String, defaultValue: '' }) label: string;\n\n\t@event({ name: 'rui-change', bubbles: true, composed: true })\n\tchangeEvent: EventEmitter<RuiMenubarChangeDetail>;\n\n\tprivate readonly resolvedAriaLabel = this.$.label.map((label) => label || undefined);\n\n\tprivate openRoot: HTMLElement | null = null;\n\n\tprivate getTopItems(): HTMLElement[] {\n\t\treturn Array.from(\n\t\t\tthis.querySelectorAll<HTMLElement>('[data-ref=\"menubar\"] > [data-ref=\"menubar-root\"] > [role=\"menuitem\"]'),\n\t\t);\n\t}\n\n\tprivate getMenuFor(topItem: HTMLElement): HTMLElement | null {\n\t\tconst root = topItem.closest('[data-ref=\"menubar-root\"]');\n\t\treturn root?.querySelector<HTMLElement>(':scope > [role=\"menu\"]') ?? null;\n\t}\n\n\tprivate getMenuItems(menu: HTMLElement): HTMLElement[] {\n\t\treturn Array.from(menu.querySelectorAll<HTMLElement>(':scope > [role=\"menuitem\"]')).filter(\n\t\t\t(item) => item.getAttribute('aria-disabled') !== 'true',\n\t\t);\n\t}\n\n\toverride connectedCallback(): void {\n\t\tsuper.connectedCallback();\n\t\tqueueMicrotask(() => applyRovingTabindex(this.getTopItems(), 0));\n\t}\n\n\tprivate closeOpenMenu(returnFocus = false): void {\n\t\tif (!this.openRoot) return;\n\t\tconst top = this.openRoot.querySelector<HTMLElement>(':scope > [role=\"menuitem\"]');\n\t\tconst menu = this.openRoot.querySelector<HTMLElement>(':scope > [role=\"menu\"]');\n\t\tif (top) top.setAttribute('aria-expanded', 'false');\n\t\tif (menu) menu.hidden = true;\n\t\tthis.openRoot = null;\n\t\tif (returnFocus && top) top.focus();\n\t}\n\n\tprivate openMenu(topItem: HTMLElement, focus: 'first' | null = 'first'): void {\n\t\tconst root = topItem.closest('[data-ref=\"menubar-root\"]') as HTMLElement | null;\n\t\tconst menu = this.getMenuFor(topItem);\n\t\tif (!root || !menu) return;\n\n\t\tif (this.openRoot && this.openRoot !== root) this.closeOpenMenu();\n\n\t\ttopItem.setAttribute('aria-expanded', 'true');\n\t\tmenu.hidden = false;\n\t\tthis.openRoot = root;\n\n\t\tif (focus === 'first') {\n\t\t\tconst items = this.getMenuItems(menu);\n\t\t\tapplyRovingTabindex(items, 0);\n\t\t\titems[0]?.focus();\n\t\t}\n\t}\n\n\t@onEvent({ document: true, type: 'click' })\n\tonDocumentClick(event: MouseEvent): void {\n\t\tif (!this.openRoot) return;\n\t\tif (!this.contains(event.target as Node)) this.closeOpenMenu();\n\t}\n\n\t@onEvent({ selector: '[data-ref=\"menubar\"] > [data-ref=\"menubar-root\"] > [role=\"menuitem\"]', type: 'keydown' })\n\tonTopKeydown(event: KeyboardEvent): void {\n\t\tconst items = this.getTopItems();\n\t\tconst current = (event.target as HTMLElement).closest('[role=\"menuitem\"]') as HTMLElement | null;\n\t\tif (!current || !items.includes(current)) return;\n\n\t\tif (event.key === 'ArrowDown' || event.key === 'Enter' || event.key === ' ') {\n\t\t\tif (this.getMenuFor(current)) {\n\t\t\t\tevent.preventDefault();\n\t\t\t\tthis.openMenu(current, 'first');\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\tif (event.key === 'Escape') {\n\t\t\tevent.preventDefault();\n\t\t\tthis.closeOpenMenu(true);\n\t\t\treturn;\n\t\t}\n\n\t\tconst result = navigateRovingTabindex({\n\t\t\titems,\n\t\t\tcurrent,\n\t\t\tkey: event.key,\n\t\t\torientation: 'horizontal',\n\t\t});\n\t\tif (!result.handled) return;\n\t\tevent.preventDefault();\n\n\t\t// APG: when a menu is open, moving across the bar opens the newly focused menu.\n\t\tif (this.openRoot) {\n\t\t\tif (this.getMenuFor(result.item)) this.openMenu(result.item, 'first');\n\t\t\telse this.closeOpenMenu();\n\t\t}\n\t}\n\n\t@onEvent({ selector: '[data-ref=\"menubar\"] > [data-ref=\"menubar-root\"] > [role=\"menuitem\"]', type: 'click' })\n\tonTopClick(event: Event): void {\n\t\tconst current = (event.target as HTMLElement).closest('[role=\"menuitem\"]') as HTMLElement | null;\n\t\tif (!current || !this.getTopItems().includes(current)) return;\n\n\t\tif (!this.getMenuFor(current)) {\n\t\t\tthis.closeOpenMenu();\n\t\t\treturn;\n\t\t}\n\n\t\tconst expanded = current.getAttribute('aria-expanded') === 'true';\n\t\tif (expanded) this.closeOpenMenu();\n\t\telse this.openMenu(current, 'first');\n\t}\n\n\t@onEvent({ selector: '[role=\"menu\"] > [role=\"menuitem\"]', type: 'keydown' })\n\tonMenuKeydown(event: KeyboardEvent): void {\n\t\tconst menu = (event.target as HTMLElement).closest('[role=\"menu\"]') as HTMLElement | null;\n\t\tconst current = (event.target as HTMLElement).closest('[role=\"menuitem\"]') as HTMLElement | null;\n\t\tif (!menu || !current || !this.contains(menu)) return;\n\n\t\tconst items = this.getMenuItems(menu);\n\n\t\tif (event.key === 'Escape') {\n\t\t\tevent.preventDefault();\n\t\t\tthis.closeOpenMenu(true);\n\t\t\treturn;\n\t\t}\n\n\t\tif (event.key === 'Enter' || event.key === ' ') {\n\t\t\tevent.preventDefault();\n\t\t\tthis.activateItem(current);\n\t\t\treturn;\n\t\t}\n\n\t\tif (event.key === 'ArrowLeft' || event.key === 'ArrowRight') {\n\t\t\tconst tops = this.getTopItems();\n\t\t\tconst top = menu.parentElement?.querySelector<HTMLElement>(':scope > [role=\"menuitem\"]');\n\t\t\tif (!top) return;\n\t\t\tevent.preventDefault();\n\t\t\tconst result = navigateRovingTabindex({\n\t\t\t\titems: tops,\n\t\t\t\tcurrent: top,\n\t\t\t\tkey: event.key,\n\t\t\t\torientation: 'horizontal',\n\t\t\t});\n\t\t\tif (result.handled && this.getMenuFor(result.item)) this.openMenu(result.item, 'first');\n\t\t\treturn;\n\t\t}\n\n\t\tconst result = navigateRovingTabindex({\n\t\t\titems,\n\t\t\tcurrent,\n\t\t\tkey: event.key,\n\t\t\torientation: 'vertical',\n\t\t});\n\t\tif (result.handled) event.preventDefault();\n\t}\n\n\t@onEvent({ selector: '[role=\"menu\"] > [role=\"menuitem\"]', type: 'click' })\n\tonMenuItemClick(event: Event): void {\n\t\tconst item = (event.target as HTMLElement).closest('[role=\"menuitem\"]') as HTMLElement | null;\n\t\tif (!item || !this.contains(item)) return;\n\t\t// Ignore top-level items (they are not direct children of role=menu in our markup).\n\t\tif (item.parentElement?.getAttribute('role') !== 'menu') return;\n\t\tthis.activateItem(item);\n\t}\n\n\tprivate activateItem(item: HTMLElement): void {\n\t\tconst value = item.getAttribute('data-value') || item.textContent?.trim() || '';\n\t\tthis.changeEvent.emit({ value });\n\t\tthis.closeOpenMenu(true);\n\t}\n\n\toverride render() {\n\t\treturn (\n\t\t\t<div class=\"rui-menubar\" data-ref=\"menubar\" role=\"menubar\" aria-label={this.resolvedAriaLabel}>\n\t\t\t\t<slot></slot>\n\t\t\t</div>\n\t\t);\n\t}\n}\n", "export type RovingOrientation = 'horizontal' | 'vertical';\n\nexport type RovingNavResult = { handled: false } | { handled: true; index: number; item: HTMLElement };\n\n/**\n * Apply the APG roving-tabindex pattern: one item is tabbable, the rest are `-1`.\n */\nexport function applyRovingTabindex(items: HTMLElement[], activeIndex: number): void {\n\titems.forEach((item, index) => {\n\t\titem.tabIndex = index === activeIndex ? 0 : -1;\n\t});\n}\n\n/**\n * Focus `items[index]` and update tabindexes. Returns the focused item, or null.\n */\nexport function focusRovingItem(items: HTMLElement[], index: number): HTMLElement | null {\n\tif (!items.length) return null;\n\tconst next = Math.max(0, Math.min(items.length - 1, index));\n\tapplyRovingTabindex(items, next);\n\titems[next]?.focus();\n\treturn items[next] ?? null;\n}\n\nfunction deltaForKey(key: string, orientation: RovingOrientation): 'prev' | 'next' | 'first' | 'last' | null {\n\tif (key === 'Home') return 'first';\n\tif (key === 'End') return 'last';\n\n\tif (orientation === 'horizontal') {\n\t\tif (key === 'ArrowRight') return 'next';\n\t\tif (key === 'ArrowLeft') return 'prev';\n\t\treturn null;\n\t}\n\n\tif (key === 'ArrowDown') return 'next';\n\tif (key === 'ArrowUp') return 'prev';\n\treturn null;\n}\n\n/**\n * Handle arrow / Home / End navigation for a flat roving-tabindex collection.\n * Does not activate the item \u2014 callers decide whether to select on move.\n */\nexport function navigateRovingTabindex(options: {\n\titems: HTMLElement[];\n\tcurrent: HTMLElement | null;\n\tkey: string;\n\torientation?: RovingOrientation;\n\twrap?: boolean;\n}): RovingNavResult {\n\tconst { items, current, key, orientation = 'horizontal', wrap = true } = options;\n\tif (!items.length || !current) return { handled: false };\n\n\tconst index = items.indexOf(current);\n\tif (index < 0) return { handled: false };\n\n\tconst delta = deltaForKey(key, orientation);\n\tif (!delta) return { handled: false };\n\n\tlet next = index;\n\tif (delta === 'first') next = 0;\n\telse if (delta === 'last') next = items.length - 1;\n\telse if (delta === 'next') next = wrap ? (index + 1) % items.length : Math.min(items.length - 1, index + 1);\n\telse next = wrap ? (index - 1 + items.length) % items.length : Math.max(0, index - 1);\n\n\tconst item = focusRovingItem(items, next);\n\tif (!item) return { handled: false };\n\treturn { handled: true, index: next, item };\n}\n", "import type { JsxRenderable } from '@ecopages/jsx';\n\nconst RADIANT_VIEW_ELEMENT = Symbol.for('@ecopages/storybook-radiant.viewElement');\nconst RADIANT_SCRIPT_MODULE = Symbol.for('@ecopages/storybook-radiant.scriptModule');\nconst RADIANT_SCRIPT_EXPORT = Symbol.for('@ecopages/storybook-radiant.scriptExport');\n\nexport type RadiantViewComponent<TArgs = unknown> = ((args: TArgs) => JsxRenderable) & {\n\t[RADIANT_VIEW_ELEMENT]?: CustomElementConstructor;\n\t[RADIANT_SCRIPT_MODULE]?: string;\n\t[RADIANT_SCRIPT_EXPORT]?: string;\n};\n\nfunction linkViewToElement<TArgs>(\n\tview: RadiantViewComponent<TArgs>,\n\telement: CustomElementConstructor & {\n\t\t[RADIANT_SCRIPT_MODULE]?: string;\n\t\t[RADIANT_SCRIPT_EXPORT]?: string;\n\t},\n): void {\n\tview[RADIANT_VIEW_ELEMENT] = element;\n\n\tconst ssrModule = element[RADIANT_SCRIPT_MODULE];\n\tif (ssrModule) {\n\t\tview[RADIANT_SCRIPT_MODULE] = ssrModule;\n\t\tview[RADIANT_SCRIPT_EXPORT] = element[RADIANT_SCRIPT_EXPORT] ?? element.name;\n\t}\n}\n\n/** Link a JSX view export to its RadiantElement class (same symbols as the Storybook framework). */\nexport function defineRadiantView<TArgs>(\n\telement: CustomElementConstructor & {\n\t\t[RADIANT_SCRIPT_MODULE]?: string;\n\t\t[RADIANT_SCRIPT_EXPORT]?: string;\n\t},\n\trender: (args: TArgs) => JsxRenderable,\n): RadiantViewComponent<TArgs> {\n\tconst view = ((args: TArgs) => render(args)) as RadiantViewComponent<TArgs>;\n\tlinkViewToElement(view, element);\n\treturn view;\n}\n\n/** Re-copy SSR metadata from the linked element (e.g. after HMR). */\nexport function syncRadiantViewMetadata(view: RadiantViewComponent): void {\n\tconst element = view[RADIANT_VIEW_ELEMENT];\n\tif (element) {\n\t\tlinkViewToElement(view, element);\n\t}\n}\n", "import type { JsxRenderable } from '@ecopages/jsx';\nimport type { RadiantSlotProps } from '../../../types';\nimport { defineRadiantView } from '../../../lib/radiant-view';\nimport type { RuiMenubarProps } from './menubar.script';\nimport { RuiMenubar as RuiMenubarElement } from './menubar.script';\nimport './menubar.css';\n\nexport type RuiMenubarItem = {\n\tid: string;\n\tlabel: JsxRenderable;\n\t/** Popup actions. When present, the top item opens a `role=\"menu\"`. */\n\titems?: Array<{ id: string; label: JsxRenderable }>;\n};\n\nexport const RuiMenubar = defineRadiantView(\n\tRuiMenubarElement,\n\t({ slot, label, items }: RuiMenubarProps & RadiantSlotProps & { items: RuiMenubarItem[] }) => (\n\t\t<rui-menubar slot={slot} label={label}>\n\t\t\t{items.map((item) => (\n\t\t\t\t<div class=\"rui-menubar__root\" data-ref=\"menubar-root\">\n\t\t\t\t\t<button\n\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\tclass=\"rui-menubar__item\"\n\t\t\t\t\t\trole=\"menuitem\"\n\t\t\t\t\t\ttabindex={-1}\n\t\t\t\t\t\taria-haspopup={item.items?.length ? 'true' : undefined}\n\t\t\t\t\t\taria-expanded={item.items?.length ? 'false' : undefined}\n\t\t\t\t\t>\n\t\t\t\t\t\t{item.label}\n\t\t\t\t\t</button>\n\t\t\t\t\t{item.items?.length ? (\n\t\t\t\t\t\t<div class=\"rui-menubar__menu\" role=\"menu\" hidden>\n\t\t\t\t\t\t\t{item.items.map((child) => (\n\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\tclass=\"rui-menubar__menu-item\"\n\t\t\t\t\t\t\t\t\trole=\"menuitem\"\n\t\t\t\t\t\t\t\t\tdata-value={child.id}\n\t\t\t\t\t\t\t\t\ttabindex={-1}\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t{child.label}\n\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t))}\n\t\t\t\t\t\t</div>\n\t\t\t\t\t) : null}\n\t\t\t\t</div>\n\t\t\t))}\n\t\t</rui-menubar>\n\t),\n);\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["RadiantElement", "customElement", "event", "onEvent", "prop", "applyRovingTabindex", "items", "activeIndex", "item", "index", "focusRovingItem", "next", "deltaForKey", "key", "orientation", "navigateRovingTabindex", "options", "current", "wrap", "delta", "RuiMenubar", "customElement", "RadiantElement", "prop", "event", "label", "topItem", "menu", "item", "applyRovingTabindex", "returnFocus", "top", "focus", "root", "items", "onEvent", "current", "result", "navigateRovingTabindex", "tops", "value", "RADIANT_VIEW_ELEMENT", "RADIANT_SCRIPT_MODULE", "RADIANT_SCRIPT_EXPORT", "linkViewToElement", "view", "element", "ssrModule", "defineRadiantView", "render", "args", "RuiMenubar", "defineRadiantView", "slot", "label", "items", "item", "child"]
|
|
7
7
|
}
|