@vielzeug/refine 2.2.5 → 2.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (223) hide show
  1. package/dist/accordion-item.cjs +1 -1
  2. package/dist/accordion-item.cjs.map +1 -1
  3. package/dist/accordion-item.js +1 -1
  4. package/dist/accordion-item.js.map +1 -1
  5. package/dist/card.cjs +1 -1
  6. package/dist/card.cjs.map +1 -1
  7. package/dist/card.js +1 -1
  8. package/dist/card.js.map +1 -1
  9. package/dist/carousel.cjs +1 -1
  10. package/dist/carousel.cjs.map +1 -1
  11. package/dist/carousel.js +1 -1
  12. package/dist/carousel.js.map +1 -1
  13. package/dist/chat-message.cjs +9 -9
  14. package/dist/chat-message.cjs.map +1 -1
  15. package/dist/chat-message.js +41 -38
  16. package/dist/chat-message.js.map +1 -1
  17. package/dist/combobox.cjs +9 -7
  18. package/dist/combobox.cjs.map +1 -1
  19. package/dist/combobox.js +55 -48
  20. package/dist/combobox.js.map +1 -1
  21. package/dist/command-palette.cjs +1 -1
  22. package/dist/command-palette.cjs.map +1 -1
  23. package/dist/command-palette.js +1 -1
  24. package/dist/command-palette.js.map +1 -1
  25. package/dist/content/index.d.ts +2 -0
  26. package/dist/content/index.d.ts.map +1 -1
  27. package/dist/content/list/list-item.css.cjs +1 -1
  28. package/dist/content/list/list-item.css.cjs.map +1 -1
  29. package/dist/content/list/list-item.css.js +1 -1
  30. package/dist/content/list/list-item.css.js.map +1 -1
  31. package/dist/content/list/list-item.d.ts +9 -0
  32. package/dist/content/list/list-item.d.ts.map +1 -1
  33. package/dist/content/stats/stats.css.cjs +2 -0
  34. package/dist/content/stats/stats.css.cjs.map +1 -0
  35. package/dist/content/stats/stats.css.js +6 -0
  36. package/dist/content/stats/stats.css.js.map +1 -0
  37. package/dist/content/stats/stats.d.ts +72 -0
  38. package/dist/content/stats/stats.d.ts.map +1 -0
  39. package/dist/core/announcer.cjs +1 -1
  40. package/dist/core/announcer.cjs.map +1 -1
  41. package/dist/core/announcer.d.ts +13 -3
  42. package/dist/core/announcer.d.ts.map +1 -1
  43. package/dist/core/announcer.js +16 -9
  44. package/dist/core/announcer.js.map +1 -1
  45. package/dist/core/auto-resize.cjs +1 -1
  46. package/dist/core/auto-resize.cjs.map +1 -1
  47. package/dist/core/auto-resize.js +1 -1
  48. package/dist/core/auto-resize.js.map +1 -1
  49. package/dist/custom-elements.json +4457 -4161
  50. package/dist/datagrid.cjs +199 -179
  51. package/dist/datagrid.cjs.map +1 -1
  52. package/dist/datagrid.js +435 -321
  53. package/dist/datagrid.js.map +1 -1
  54. package/dist/disclosure/tab-item/tab-item.css.cjs +1 -1
  55. package/dist/disclosure/tab-item/tab-item.css.cjs.map +1 -1
  56. package/dist/disclosure/tab-item/tab-item.css.js +1 -1
  57. package/dist/disclosure/tab-item/tab-item.css.js.map +1 -1
  58. package/dist/disclosure/tab-item/tab-item.d.ts +3 -0
  59. package/dist/disclosure/tab-item/tab-item.d.ts.map +1 -1
  60. package/dist/disclosure/tabs/tabs.css.cjs +1 -1
  61. package/dist/disclosure/tabs/tabs.css.cjs.map +1 -1
  62. package/dist/disclosure/tabs/tabs.css.js +1 -1
  63. package/dist/disclosure/tabs/tabs.css.js.map +1 -1
  64. package/dist/disclosure/tabs/tabs.d.ts +4 -0
  65. package/dist/disclosure/tabs/tabs.d.ts.map +1 -1
  66. package/dist/feedback/typing-indicator/typing-indicator.d.ts +2 -2
  67. package/dist/file-input.cjs +49 -49
  68. package/dist/file-input.cjs.map +1 -1
  69. package/dist/file-input.js +128 -127
  70. package/dist/file-input.js.map +1 -1
  71. package/dist/frameworks/elements.d.ts +2 -1
  72. package/dist/icon.cjs +1 -1
  73. package/dist/icon.cjs.map +1 -1
  74. package/dist/icon.js +1 -1
  75. package/dist/icon.js.map +1 -1
  76. package/dist/input.cjs +5 -4
  77. package/dist/input.cjs.map +1 -1
  78. package/dist/input.js +14 -8
  79. package/dist/input.js.map +1 -1
  80. package/dist/inputs/combobox/combobox-options.cjs +1 -1
  81. package/dist/inputs/combobox/combobox-options.cjs.map +1 -1
  82. package/dist/inputs/combobox/combobox-options.d.ts.map +1 -1
  83. package/dist/inputs/combobox/combobox-options.js +1 -0
  84. package/dist/inputs/combobox/combobox-options.js.map +1 -1
  85. package/dist/inputs/combobox/combobox.css.cjs +1 -1
  86. package/dist/inputs/combobox/combobox.css.cjs.map +1 -1
  87. package/dist/inputs/combobox/combobox.css.js +1 -1
  88. package/dist/inputs/combobox/combobox.css.js.map +1 -1
  89. package/dist/inputs/combobox/combobox.d.ts +1 -0
  90. package/dist/inputs/combobox/combobox.d.ts.map +1 -1
  91. package/dist/inputs/combobox/combobox.types.d.ts +7 -0
  92. package/dist/inputs/combobox/combobox.types.d.ts.map +1 -1
  93. package/dist/inputs/datagrid/datagrid-model.cjs +1 -1
  94. package/dist/inputs/datagrid/datagrid-model.cjs.map +1 -1
  95. package/dist/inputs/datagrid/datagrid-model.d.ts +17 -3
  96. package/dist/inputs/datagrid/datagrid-model.d.ts.map +1 -1
  97. package/dist/inputs/datagrid/datagrid-model.js +93 -70
  98. package/dist/inputs/datagrid/datagrid-model.js.map +1 -1
  99. package/dist/inputs/datagrid/datagrid.css.cjs +1 -1
  100. package/dist/inputs/datagrid/datagrid.css.cjs.map +1 -1
  101. package/dist/inputs/datagrid/datagrid.css.js +1 -1
  102. package/dist/inputs/datagrid/datagrid.css.js.map +1 -1
  103. package/dist/inputs/datagrid/datagrid.d.ts +54 -4
  104. package/dist/inputs/datagrid/datagrid.d.ts.map +1 -1
  105. package/dist/inputs/file-input/file-input-upload.cjs +1 -1
  106. package/dist/inputs/file-input/file-input-upload.cjs.map +1 -1
  107. package/dist/inputs/file-input/file-input-upload.d.ts +2 -0
  108. package/dist/inputs/file-input/file-input-upload.d.ts.map +1 -1
  109. package/dist/inputs/file-input/file-input-upload.js +5 -2
  110. package/dist/inputs/file-input/file-input-upload.js.map +1 -1
  111. package/dist/inputs/file-input/file-input.d.ts.map +1 -1
  112. package/dist/inputs/index.d.ts +1 -1
  113. package/dist/inputs/index.d.ts.map +1 -1
  114. package/dist/inputs/input/input.css.cjs +1 -1
  115. package/dist/inputs/input/input.css.cjs.map +1 -1
  116. package/dist/inputs/input/input.css.js +1 -1
  117. package/dist/inputs/input/input.css.js.map +1 -1
  118. package/dist/inputs/input/input.d.ts +3 -0
  119. package/dist/inputs/input/input.d.ts.map +1 -1
  120. package/dist/inputs/select/select.css.cjs +1 -1
  121. package/dist/inputs/select/select.css.cjs.map +1 -1
  122. package/dist/inputs/select/select.css.js +1 -1
  123. package/dist/inputs/select/select.css.js.map +1 -1
  124. package/dist/inputs/select/select.d.ts +5 -0
  125. package/dist/inputs/select/select.d.ts.map +1 -1
  126. package/dist/layout/navbar/navbar.d.ts +2 -0
  127. package/dist/layout/navbar/navbar.d.ts.map +1 -1
  128. package/dist/layout/sidebar/sidebar.css.cjs +1 -1
  129. package/dist/layout/sidebar/sidebar.css.cjs.map +1 -1
  130. package/dist/layout/sidebar/sidebar.css.js +1 -1
  131. package/dist/layout/sidebar/sidebar.css.js.map +1 -1
  132. package/dist/layout/sidebar/sidebar.d.ts +8 -0
  133. package/dist/layout/sidebar/sidebar.d.ts.map +1 -1
  134. package/dist/list-item.cjs +6 -5
  135. package/dist/list-item.cjs.map +1 -1
  136. package/dist/list-item.js +23 -17
  137. package/dist/list-item.js.map +1 -1
  138. package/dist/list.cjs +1 -1
  139. package/dist/list.cjs.map +1 -1
  140. package/dist/list.js +1 -1
  141. package/dist/list.js.map +1 -1
  142. package/dist/menu.cjs +1 -1
  143. package/dist/menu.cjs.map +1 -1
  144. package/dist/menu.js +1 -1
  145. package/dist/menu.js.map +1 -1
  146. package/dist/navbar.cjs +2 -2
  147. package/dist/navbar.cjs.map +1 -1
  148. package/dist/navbar.js +7 -5
  149. package/dist/navbar.js.map +1 -1
  150. package/dist/navigation-menu.cjs +1 -1
  151. package/dist/navigation-menu.cjs.map +1 -1
  152. package/dist/navigation-menu.js +1 -1
  153. package/dist/navigation-menu.js.map +1 -1
  154. package/dist/overlay/popover/popover.css.cjs +1 -1
  155. package/dist/overlay/popover/popover.css.cjs.map +1 -1
  156. package/dist/overlay/popover/popover.css.js +1 -1
  157. package/dist/overlay/popover/popover.css.js.map +1 -1
  158. package/dist/overlay/popover/popover.d.ts +1 -0
  159. package/dist/overlay/popover/popover.d.ts.map +1 -1
  160. package/dist/overlay/shared/use-dialog.cjs +1 -1
  161. package/dist/overlay/shared/use-dialog.cjs.map +1 -1
  162. package/dist/overlay/shared/use-dialog.js +1 -1
  163. package/dist/overlay/shared/use-dialog.js.map +1 -1
  164. package/dist/overlay/shared/use-floating-trigger.cjs +1 -1
  165. package/dist/overlay/shared/use-floating-trigger.cjs.map +1 -1
  166. package/dist/overlay/shared/use-floating-trigger.js +1 -1
  167. package/dist/overlay/shared/use-floating-trigger.js.map +1 -1
  168. package/dist/popover.cjs.map +1 -1
  169. package/dist/popover.js.map +1 -1
  170. package/dist/radio.cjs +1 -1
  171. package/dist/radio.cjs.map +1 -1
  172. package/dist/radio.js +10 -8
  173. package/dist/radio.js.map +1 -1
  174. package/dist/rating.cjs +1 -1
  175. package/dist/rating.cjs.map +1 -1
  176. package/dist/rating.js +1 -1
  177. package/dist/rating.js.map +1 -1
  178. package/dist/refine.cjs +428 -379
  179. package/dist/refine.cjs.map +1 -1
  180. package/dist/refine.iife.js +428 -379
  181. package/dist/refine.iife.js.map +1 -1
  182. package/dist/refine.js +458 -409
  183. package/dist/refine.js.map +1 -1
  184. package/dist/select.cjs +16 -14
  185. package/dist/select.cjs.map +1 -1
  186. package/dist/select.js +118 -112
  187. package/dist/select.js.map +1 -1
  188. package/dist/sidebar.cjs +28 -23
  189. package/dist/sidebar.cjs.map +1 -1
  190. package/dist/sidebar.js +112 -77
  191. package/dist/sidebar.js.map +1 -1
  192. package/dist/slider.cjs +1 -1
  193. package/dist/slider.cjs.map +1 -1
  194. package/dist/slider.js +1 -1
  195. package/dist/slider.js.map +1 -1
  196. package/dist/stats.cjs +18 -0
  197. package/dist/stats.cjs.map +1 -0
  198. package/dist/stats.js +88 -0
  199. package/dist/stats.js.map +1 -0
  200. package/dist/stepper.cjs +1 -1
  201. package/dist/stepper.cjs.map +1 -1
  202. package/dist/stepper.js +1 -1
  203. package/dist/stepper.js.map +1 -1
  204. package/dist/styles/mixins/animation.css.cjs +2 -0
  205. package/dist/styles/mixins/animation.css.cjs.map +1 -1
  206. package/dist/styles/mixins/animation.css.d.ts.map +1 -1
  207. package/dist/styles/mixins/animation.css.js +2 -0
  208. package/dist/styles/mixins/animation.css.js.map +1 -1
  209. package/dist/tab-item.cjs +1 -1
  210. package/dist/tab-item.cjs.map +1 -1
  211. package/dist/tab-item.js +3 -2
  212. package/dist/tab-item.js.map +1 -1
  213. package/dist/tabs.cjs +1 -1
  214. package/dist/tabs.cjs.map +1 -1
  215. package/dist/tabs.js +3 -1
  216. package/dist/tabs.js.map +1 -1
  217. package/dist/toast.cjs.map +1 -1
  218. package/dist/toast.js.map +1 -1
  219. package/dist/typing-indicator.cjs +1 -1
  220. package/dist/typing-indicator.cjs.map +1 -1
  221. package/dist/typing-indicator.js +8 -7
  222. package/dist/typing-indicator.js.map +1 -1
  223. package/package.json +66 -58
@@ -1 +1 @@
1
- {"version":3,"file":"navigation-menu.js","names":[],"sources":["../src/overlay/navigation-menu/navigation-menu.ts"],"sourcesContent":["import { createStableId, define, getHost, html, onCleanup, onMounted, prop, useEmit, useSlots } from '@vielzeug/ore';\nimport { computed, signal, watch } from '@vielzeug/ripple';\n\nimport type { OverlayOpenChangeDetail } from '../../core';\n\nimport { createDropdownPositioner, createOutsidePointerDismissal, lifecycleSignal } from '../../core';\nimport { disablableBundle } from '../../shared';\nimport { reducedMotionMixin } from '../../styles';\nimport hostStyles from './navigation-menu.css?inline';\nimport itemStyles from './navigation-menu-item.css?inline';\nimport panelStyles from './navigation-menu-panel.css?inline';\n\nexport type OreNavigationMenuEvents = {\n 'open-change': OverlayOpenChangeDetail & { value: string | null };\n select: NavigationMenuSelectDetail;\n};\n\nexport type NavigationMenuSelectDetail = {\n href: string;\n value: string;\n};\n\nexport type OreNavigationMenuItemProps = {\n disabled?: boolean;\n value: string;\n};\n\nexport type OreNavigationMenuProps = {\n /** Close after activating a panel link. */\n 'close-on-select'?: boolean;\n columns?: number;\n 'default-open'?: string;\n disabled?: boolean;\n /** Accessible label for the trigger region. */\n label?: string;\n open?: string;\n placement?: 'bottom-end' | 'bottom-start';\n};\n\nconst parseOptionalString = (value: string | null): string | undefined => value ?? undefined;\n\n/**\n * A trigger for an associated navigation menu panel.\n *\n * @element ore-navigation-menu-item\n *\n * @attr {boolean} disabled - Disables panel activation\n * @attr {string} value - Unique value matched by a panel's `for` attribute\n *\n * @slot - Trigger label and optional icon\n */\nexport const NAVIGATION_MENU_ITEM_TAG = 'ore-navigation-menu-item' as const;\ndefine<OreNavigationMenuItemProps>(NAVIGATION_MENU_ITEM_TAG, {\n props: { disabled: prop.bool(false), value: prop.string('') },\n setup(props) {\n return html`\n <button class=\"trigger\" type=\"button\" ?disabled=${props.disabled}><slot></slot></button>\n `;\n },\n styles: [itemStyles],\n});\n\n/**\n * An anchored navigation menu panel associated with an item.\n *\n * @element ore-navigation-menu-panel\n *\n * @attr {string} for - Value of the associated navigation menu item\n *\n * @slot - Panel content\n * @slot footer - Optional panel footer\n *\n * @part content - Grid container for panel content\n * @part footer - Container for optional footer content\n */\nexport const NAVIGATION_MENU_PANEL_TAG = 'ore-navigation-menu-panel' as const;\ndefine<{ for?: string }>(NAVIGATION_MENU_PANEL_TAG, {\n props: { for: prop.string() },\n setup() {\n const slots = useSlots();\n\n return html`\n <div class=\"content\" part=\"content\"><slot></slot></div>\n <div class=\"footer\" part=\"footer\" ?hidden=${() => !slots.has('footer').value}><slot name=\"footer\"></slot></div>\n `;\n },\n styles: [panelStyles],\n});\n\n/**\n * A horizontal set of triggers with anchored, non-modal navigation panels.\n *\n * @element ore-navigation-menu\n *\n * @attr {string} open - Controlled active trigger value\n * @attr {string} default-open - Initial uncontrolled active trigger value\n * @attr {boolean} close-on-select - Closes after activating a panel link\n * @attr {number} columns - Number of panel content columns\n * @attr {boolean} disabled - Disables all triggers\n * @attr {string} label - Accessible navigation region label\n * @attr {'bottom-start'|'bottom-end'} placement - Panel placement relative to its trigger\n *\n * @slot - Navigation menu items and panels\n *\n * @event open-change - Requested active panel change\n * @event select - A panel link was activated\n *\n * @cssprop --navigation-menu-panel-width - Maximum panel width\n * @cssprop --navigation-menu-panel-columns - Fallback number of panel content columns\n * @cssprop --navigation-menu-backdrop-blur - Frosted panel backdrop blur\n */\nexport const NAVIGATION_MENU_TAG = 'ore-navigation-menu' as const;\ndefine<OreNavigationMenuProps>(NAVIGATION_MENU_TAG, {\n props: {\n ...disablableBundle,\n 'close-on-select': prop.bool(true),\n columns: prop.number(),\n 'default-open': prop.string(),\n label: prop.string('Navigation menu'),\n open: { default: undefined as string | undefined, parse: parseOptionalString },\n placement: prop.oneOf(['bottom-end', 'bottom-start'] as const, 'bottom-start'),\n },\n setup(props) {\n const host = getHost();\n const emit = useEmit<OreNavigationMenuEvents>();\n const abortSignal = lifecycleSignal(onCleanup);\n const panelId = createStableId('navigation-menu');\n const isDisabled = computed(() => props.disabled.value);\n const activeValue = signal<string | null>(null);\n let activeTrigger: HTMLElement | null = null;\n let activePanel: HTMLElement | null = null;\n let stopPositioning: (() => void) | null = null;\n\n const items = () => Array.from(host.querySelectorAll<HTMLElement>(`${NAVIGATION_MENU_ITEM_TAG}:not([disabled])`));\n const valueOf = (item: HTMLElement) => item.getAttribute('value') ?? '';\n const panels = () => Array.from(host.querySelectorAll<HTMLElement>(NAVIGATION_MENU_PANEL_TAG));\n const panelFor = (value: string) => panels().find((panel) => panel.getAttribute('for') === value) ?? null;\n const isControlled = () => props.open.value !== undefined;\n const panelIdFor = (panel: HTMLElement) => (panel.id ||= createStableId('navigation-menu-panel'));\n\n const positioner = createDropdownPositioner({\n getFloating: () => activePanel,\n getPlacement: () => props.placement.value ?? 'bottom-start',\n getReference: () => activeTrigger?.shadowRoot?.querySelector<HTMLElement>('.trigger') ?? activeTrigger,\n matchWidth: false,\n offsetPx: 8,\n useClippingAncestor: false,\n });\n\n const panelColumns = (): number | undefined => {\n const columns = props.columns.value;\n\n return columns === undefined ? undefined : Math.max(1, Math.trunc(columns));\n };\n\n const showPanel = (panel: HTMLElement): void => {\n panel.hidden = false;\n panel.dataset.open = '';\n\n if ('showPopover' in panel) {\n try {\n if (!panel.matches(':popover-open')) panel.showPopover();\n } catch {\n // Sandboxed documents can reject the Popover API; data-open remains the fixed-position fallback.\n }\n }\n };\n\n const hidePanel = (panel: HTMLElement): void => {\n delete panel.dataset.open;\n\n if ('hidePopover' in panel) {\n try {\n if (panel.matches(':popover-open')) panel.hidePopover();\n } catch {\n // The fallback visibility state is still cleared below.\n }\n }\n\n panel.hidden = true;\n };\n\n const applyActive = (value: string | null): void => {\n const nextPanel = value === null ? null : panelFor(value);\n\n stopPositioning?.();\n stopPositioning = null;\n activeValue.value = value;\n activeTrigger = value ? (items().find((item) => valueOf(item) === value) ?? null) : null;\n activePanel = nextPanel;\n\n for (const item of items()) {\n const expanded = valueOf(item) === value;\n const trigger = item.shadowRoot?.querySelector<HTMLElement>('.trigger');\n\n item.setAttribute('aria-expanded', String(expanded));\n trigger?.setAttribute('aria-expanded', String(expanded));\n }\n for (const panel of panels()) {\n if (panel !== nextPanel) hidePanel(panel);\n }\n\n if (nextPanel) {\n const columns = panelColumns();\n\n if (columns !== undefined) nextPanel.style.setProperty('--navigation-menu-panel-columns', String(columns));\n\n nextPanel.setAttribute('aria-labelledby', activeTrigger?.id ?? '');\n showPanel(nextPanel);\n positioner.update();\n stopPositioning = positioner.startAutoUpdate?.() ?? null;\n }\n };\n\n const requestActive = (value: string | null, reason: OverlayOpenChangeDetail['reason']): void => {\n if (isDisabled.value || activeValue.value === value) return;\n\n if (value !== null && !panelFor(value)) return;\n\n if (!isControlled()) applyActive(value);\n\n emit('open-change', { open: value !== null, reason, value });\n };\n\n onCleanup(() => {\n stopPositioning?.();\n\n for (const panel of panels()) hidePanel(panel);\n });\n\n createOutsidePointerDismissal({\n getTargets: () => [host, activePanel],\n isActive: () => activeValue.value !== null,\n onDismiss: () => requestActive(null, 'outsideClick'),\n signal: abortSignal,\n });\n\n onMounted(() => {\n const values = new Set<string>();\n\n for (const panel of panels()) {\n panel.setAttribute('popover', 'manual');\n panel.setAttribute('role', 'region');\n panelIdFor(panel);\n hidePanel(panel);\n\n panel.addEventListener(\n 'click',\n (event) => {\n const link = event.target instanceof Element ? event.target.closest<HTMLAnchorElement>('a[href]') : null;\n\n if (!link || !panel.contains(link)) return;\n\n const value = activeValue.value;\n\n if (!value) return;\n\n emit('select', { href: link.href, value });\n\n if (props['close-on-select'].value && !link.closest('[data-navigation-menu-keep-open]')) {\n requestActive(null, 'select');\n }\n },\n { signal: abortSignal },\n );\n }\n\n document.addEventListener(\n 'keydown',\n (event) => {\n if (event.key !== 'Escape' || activeValue.value === null) return;\n\n event.preventDefault();\n requestActive(null, 'escape');\n activeTrigger?.shadowRoot?.querySelector<HTMLElement>('.trigger')?.focus();\n },\n { signal: abortSignal },\n );\n\n for (const item of items()) {\n const value = valueOf(item);\n const panel = panelFor(value);\n\n if (!value || values.has(value) || !panel) continue;\n\n values.add(value);\n item.id ||= createStableId('navigation-menu-trigger');\n\n const trigger = item.shadowRoot?.querySelector<HTMLButtonElement>('.trigger');\n\n if (!trigger) continue;\n\n trigger.setAttribute('aria-controls', panelIdFor(panel));\n trigger.setAttribute('aria-haspopup', 'true');\n trigger.setAttribute('aria-expanded', 'false');\n item.setAttribute('aria-expanded', 'false');\n trigger.addEventListener('click', () => requestActive(activeValue.value === value ? null : value, 'click'), {\n signal: abortSignal,\n });\n trigger.addEventListener(\n 'keydown',\n (event) => {\n const all = items();\n const index = all.indexOf(item);\n\n if (\n event.key === 'ArrowLeft' ||\n event.key === 'ArrowRight' ||\n event.key === 'Home' ||\n event.key === 'End'\n ) {\n event.preventDefault();\n\n const next =\n event.key === 'Home'\n ? all[0]\n : event.key === 'End'\n ? all.at(-1)\n : all[(index + (event.key === 'ArrowRight' ? 1 : all.length - 1)) % all.length];\n\n next?.shadowRoot?.querySelector<HTMLElement>('.trigger')?.focus();\n } else if (event.key === 'ArrowDown') {\n event.preventDefault();\n requestActive(value, 'keyboard');\n requestAnimationFrame(() =>\n activePanel?.querySelector<HTMLElement>('a, button, input, [tabindex]:not([tabindex=\"-1\"])')?.focus(),\n );\n }\n },\n { signal: abortSignal },\n );\n }\n\n if (props.open.value !== undefined) applyActive(props.open.value);\n else if (props['default-open'].value) applyActive(props['default-open'].value);\n });\n\n watch(props.open, (value) => {\n if (value !== undefined) applyActive(value);\n });\n\n watch(props.disabled, (disabled) => {\n if (disabled) applyActive(null);\n });\n\n return html`\n <nav class=\"triggers\" aria-label=\"${props.label}\" id=\"${panelId}\"><slot></slot></nav>\n <slot name=\"panel\"></slot>\n <slot name=\"footer\"></slot>\n `;\n },\n styles: [reducedMotionMixin, hostStyles],\n});\n"],"mappings":"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"}
1
+ {"version":3,"file":"navigation-menu.js","names":[],"sources":["../src/overlay/navigation-menu/navigation-menu.ts"],"sourcesContent":["import { createStableId, define, getHost, html, onCleanup, onMounted, prop, useEmit, useSlots } from '@vielzeug/ore';\nimport { computed, signal, watch } from '@vielzeug/ripple';\n\nimport type { OverlayOpenChangeDetail } from '../../core';\n\nimport { createDropdownPositioner, createOutsidePointerDismissal, lifecycleSignal } from '../../core';\nimport { disablableBundle } from '../../shared';\nimport { reducedMotionMixin } from '../../styles';\nimport hostStyles from './navigation-menu.css?inline';\nimport itemStyles from './navigation-menu-item.css?inline';\nimport panelStyles from './navigation-menu-panel.css?inline';\n\nexport type OreNavigationMenuEvents = {\n 'open-change': OverlayOpenChangeDetail & { value: string | null };\n select: NavigationMenuSelectDetail;\n};\n\nexport type NavigationMenuSelectDetail = {\n href: string;\n value: string;\n};\n\nexport type OreNavigationMenuItemProps = {\n disabled?: boolean;\n value: string;\n};\n\nexport type OreNavigationMenuProps = {\n /** Close after activating a panel link. */\n 'close-on-select'?: boolean;\n columns?: number;\n 'default-open'?: string;\n disabled?: boolean;\n /** Accessible label for the trigger region. */\n label?: string;\n open?: string;\n placement?: 'bottom-end' | 'bottom-start';\n};\n\nconst parseOptionalString = (value: string | null): string | undefined => value ?? undefined;\n\n/**\n * A trigger for an associated navigation menu panel.\n *\n * @element ore-navigation-menu-item\n *\n * @attr {boolean} disabled - Disables panel activation\n * @attr {string} value - Unique value matched by a panel's `for` attribute\n *\n * @slot - Trigger label and optional icon\n */\nexport const NAVIGATION_MENU_ITEM_TAG = 'ore-navigation-menu-item' as const;\ndefine<OreNavigationMenuItemProps>(NAVIGATION_MENU_ITEM_TAG, {\n props: { disabled: prop.bool(false), value: prop.string('') },\n setup(props) {\n return html`\n <button class=\"trigger\" type=\"button\" ?disabled=${props.disabled}><slot></slot></button>\n `;\n },\n styles: [itemStyles],\n});\n\n/**\n * An anchored navigation menu panel associated with an item.\n *\n * @element ore-navigation-menu-panel\n *\n * @attr {string} for - Value of the associated navigation menu item\n *\n * @slot - Panel content\n * @slot footer - Optional panel footer\n *\n * @part content - Grid container for panel content\n * @part footer - Container for optional footer content\n */\nexport const NAVIGATION_MENU_PANEL_TAG = 'ore-navigation-menu-panel' as const;\ndefine<{ for?: string }>(NAVIGATION_MENU_PANEL_TAG, {\n props: { for: prop.string() },\n setup() {\n const slots = useSlots();\n\n return html`\n <div class=\"content\" part=\"content\"><slot></slot></div>\n <div class=\"footer\" part=\"footer\" ?hidden=${() => !slots.has('footer').value}><slot name=\"footer\"></slot></div>\n `;\n },\n styles: [panelStyles],\n});\n\n/**\n * A horizontal set of triggers with anchored, non-modal navigation panels.\n *\n * @element ore-navigation-menu\n *\n * @attr {string} open - Controlled active trigger value\n * @attr {string} default-open - Initial uncontrolled active trigger value\n * @attr {boolean} close-on-select - Closes after activating a panel link\n * @attr {number} columns - Number of panel content columns\n * @attr {boolean} disabled - Disables all triggers\n * @attr {string} label - Accessible navigation region label\n * @attr {'bottom-start'|'bottom-end'} placement - Panel placement relative to its trigger\n *\n * @slot - Navigation menu items and panels\n *\n * @event open-change - Requested active panel change\n * @event select - A panel link was activated\n *\n * @cssprop --navigation-menu-panel-width - Maximum panel width\n * @cssprop --navigation-menu-panel-columns - Fallback number of panel content columns\n * @cssprop --navigation-menu-backdrop-blur - Frosted panel backdrop blur\n */\nexport const NAVIGATION_MENU_TAG = 'ore-navigation-menu' as const;\ndefine<OreNavigationMenuProps>(NAVIGATION_MENU_TAG, {\n props: {\n ...disablableBundle,\n 'close-on-select': prop.bool(true),\n columns: prop.number(),\n 'default-open': prop.string(),\n label: prop.string('Navigation menu'),\n open: { default: undefined as string | undefined, parse: parseOptionalString },\n placement: prop.oneOf(['bottom-end', 'bottom-start'] as const, 'bottom-start'),\n },\n setup(props) {\n const host = getHost();\n const emit = useEmit<OreNavigationMenuEvents>();\n const abortSignal = lifecycleSignal(onCleanup);\n const panelId = createStableId('navigation-menu');\n const isDisabled = computed(() => props.disabled.value);\n const activeValue = signal<string | null>(null);\n let activeTrigger: HTMLElement | null = null;\n let activePanel: HTMLElement | null = null;\n let stopPositioning: (() => void) | null = null;\n\n const items = () => Array.from(host.querySelectorAll<HTMLElement>(`${NAVIGATION_MENU_ITEM_TAG}:not([disabled])`));\n const valueOf = (item: HTMLElement) => item.getAttribute('value') ?? '';\n const panels = () => Array.from(host.querySelectorAll<HTMLElement>(NAVIGATION_MENU_PANEL_TAG));\n const panelFor = (value: string) => panels().find((panel) => panel.getAttribute('for') === value) ?? null;\n const isControlled = () => props.open.value !== undefined;\n const panelIdFor = (panel: HTMLElement) => (panel.id ||= createStableId('navigation-menu-panel'));\n\n const positioner = createDropdownPositioner({\n getFloating: () => activePanel,\n getPlacement: () => props.placement.value ?? 'bottom-start',\n getReference: () => activeTrigger?.shadowRoot?.querySelector<HTMLElement>('.trigger') ?? activeTrigger,\n matchWidth: false,\n offsetPx: 8,\n useClippingAncestor: false,\n });\n\n const panelColumns = (): number | undefined => {\n const columns = props.columns.value;\n\n return columns === undefined ? undefined : Math.max(1, Math.trunc(columns));\n };\n\n const showPanel = (panel: HTMLElement): void => {\n panel.hidden = false;\n panel.dataset.open = '';\n\n if ('showPopover' in panel) {\n try {\n if (!panel.matches(':popover-open')) panel.showPopover();\n } catch {\n // Sandboxed documents can reject the Popover API; data-open remains the fixed-position fallback.\n }\n }\n };\n\n const hidePanel = (panel: HTMLElement): void => {\n delete panel.dataset.open;\n\n if ('hidePopover' in panel) {\n try {\n if (panel.matches(':popover-open')) panel.hidePopover();\n } catch {\n // The fallback visibility state is still cleared below.\n }\n }\n\n panel.hidden = true;\n };\n\n const applyActive = (value: string | null): void => {\n const nextPanel = value === null ? null : panelFor(value);\n\n stopPositioning?.();\n stopPositioning = null;\n activeValue.value = value;\n activeTrigger = value ? (items().find((item) => valueOf(item) === value) ?? null) : null;\n activePanel = nextPanel;\n\n for (const item of items()) {\n const expanded = valueOf(item) === value;\n const trigger = item.shadowRoot?.querySelector<HTMLElement>('.trigger');\n\n item.setAttribute('aria-expanded', String(expanded));\n trigger?.setAttribute('aria-expanded', String(expanded));\n }\n for (const panel of panels()) {\n if (panel !== nextPanel) hidePanel(panel);\n }\n\n if (nextPanel) {\n const columns = panelColumns();\n\n if (columns !== undefined) nextPanel.style.setProperty('--navigation-menu-panel-columns', String(columns));\n\n nextPanel.setAttribute('aria-labelledby', activeTrigger?.id ?? '');\n showPanel(nextPanel);\n positioner.update();\n stopPositioning = positioner.startAutoUpdate?.() ?? null;\n }\n };\n\n const requestActive = (value: string | null, reason: OverlayOpenChangeDetail['reason']): void => {\n if (isDisabled.value || activeValue.value === value) return;\n\n if (value !== null && !panelFor(value)) return;\n\n if (!isControlled()) applyActive(value);\n\n emit('open-change', { open: value !== null, reason, value });\n };\n\n onCleanup(() => {\n stopPositioning?.();\n\n for (const panel of panels()) hidePanel(panel);\n });\n\n createOutsidePointerDismissal({\n getTargets: () => [host, activePanel],\n isActive: () => activeValue.value !== null,\n onDismiss: () => requestActive(null, 'outsideClick'),\n signal: abortSignal,\n });\n\n onMounted(() => {\n const values = new Set<string>();\n\n for (const panel of panels()) {\n panel.setAttribute('popover', 'manual');\n panel.setAttribute('role', 'region');\n panelIdFor(panel);\n hidePanel(panel);\n\n panel.addEventListener(\n 'click',\n (event) => {\n const link = event.target instanceof Element ? event.target.closest<HTMLAnchorElement>('a[href]') : null;\n\n if (!link || !panel.contains(link)) return;\n\n const value = activeValue.value;\n\n if (!value) return;\n\n emit('select', { href: link.href, value });\n\n if (props['close-on-select'].value && !link.closest('[data-navigation-menu-keep-open]')) {\n requestActive(null, 'select');\n }\n },\n { signal: abortSignal },\n );\n }\n\n document.addEventListener(\n 'keydown',\n (event) => {\n if (event.key !== 'Escape' || activeValue.value === null) return;\n\n event.preventDefault();\n requestActive(null, 'escape');\n activeTrigger?.shadowRoot?.querySelector<HTMLElement>('.trigger')?.focus();\n },\n { signal: abortSignal },\n );\n\n for (const item of items()) {\n const value = valueOf(item);\n const panel = panelFor(value);\n\n if (!value || values.has(value) || !panel) continue;\n\n values.add(value);\n item.id ||= createStableId('navigation-menu-trigger');\n\n const trigger = item.shadowRoot?.querySelector<HTMLButtonElement>('.trigger');\n\n if (!trigger) continue;\n\n trigger.setAttribute('aria-controls', panelIdFor(panel));\n trigger.setAttribute('aria-haspopup', 'true');\n trigger.setAttribute('aria-expanded', 'false');\n item.setAttribute('aria-expanded', 'false');\n trigger.addEventListener('click', () => requestActive(activeValue.value === value ? null : value, 'click'), {\n signal: abortSignal,\n });\n trigger.addEventListener(\n 'keydown',\n (event) => {\n const all = items();\n const index = all.indexOf(item);\n\n if (\n event.key === 'ArrowLeft' ||\n event.key === 'ArrowRight' ||\n event.key === 'Home' ||\n event.key === 'End'\n ) {\n event.preventDefault();\n\n const next =\n event.key === 'Home'\n ? all[0]\n : event.key === 'End'\n ? all.at(-1)\n : all[(index + (event.key === 'ArrowRight' ? 1 : all.length - 1)) % all.length];\n\n next?.shadowRoot?.querySelector<HTMLElement>('.trigger')?.focus();\n } else if (event.key === 'ArrowDown') {\n event.preventDefault();\n requestActive(value, 'keyboard');\n requestAnimationFrame(() =>\n activePanel?.querySelector<HTMLElement>('a, button, input, [tabindex]:not([tabindex=\"-1\"])')?.focus(),\n );\n }\n },\n { signal: abortSignal },\n );\n }\n\n if (props.open.value !== undefined) applyActive(props.open.value);\n else if (props['default-open'].value) applyActive(props['default-open'].value);\n });\n\n watch(props.open, (value) => {\n if (value !== undefined) applyActive(value);\n });\n\n watch(props.disabled, (disabled) => {\n if (disabled) applyActive(null);\n });\n\n return html`\n <nav class=\"triggers\" aria-label=\"${props.label}\" id=\"${panelId}\"><slot></slot></nav>\n <slot name=\"panel\"></slot>\n <slot name=\"footer\"></slot>\n `;\n },\n styles: [reducedMotionMixin, hostStyles],\n});\n"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- var e=`@layer refine.base{:host{--_panel-bg:var(--popover-bg,var(--color-canvas));--_panel-blur:var(--popover-panel-blur,var(--blur-md));--_panel-border-color:var(--popover-border-color,var(--color-divider));--_panel-shadow:var(--popover-shadow,var(--shadow-xl));display:inline-block;position:relative}.panel{inset:unset;box-sizing:border-box;min-width:var(--popover-min-width,12rem);max-width:var(--popover-max-width,24rem);max-height:var(--popover-max-height,min(70vh, 32rem));word-break:auto-phrase;overflow-wrap:anywhere;background:var(--_panel-bg);border:var(--border) solid var(--_panel-border-color);border-radius:var(--rounded-lg);box-shadow:var(--_panel-shadow);opacity:0;-webkit-backdrop-filter:blur(var(--_panel-blur));backdrop-filter:blur(var(--_panel-blur));transform:scale(var(--overlay-enter-scale-dialog)) translateY(4px);transition:opacity var(--transition-fast), transform .2s var(--ease-spring), display var(--transition-fast) allow-discrete, overlay var(--transition-fast) allow-discrete;margin:0;padding:0;position:fixed;overflow:auto}.panel[data-open]{opacity:1;display:block;transform:scale(1)translateY(0)}.panel:popover-open{opacity:1;transform:scale(1)translateY(0)}@starting-style{.panel:popover-open{opacity:0;transform:scale(var(--overlay-enter-scale-dialog)) translateY(4px)}}@starting-style{.panel[data-open]{opacity:0;transform:scale(var(--overlay-enter-scale-dialog)) translateY(4px)}}}`;exports.default=e;
1
+ var e=`@layer refine.base{:host{--_panel-bg:var(--popover-bg,var(--color-canvas));--_panel-blur:var(--popover-panel-blur,var(--blur-md));--_panel-border-color:var(--popover-border-color,var(--color-divider));--_panel-shadow:var(--popover-shadow,var(--shadow-xl));display:inline-block;position:relative}.panel{inset:unset;box-sizing:border-box;min-width:var(--popover-min-width,12rem);max-width:var(--popover-max-width,24rem);max-height:var(--popover-max-height,min(70vh, 32rem));overflow:var(--popover-overflow,auto);word-break:auto-phrase;overflow-wrap:anywhere;background:var(--_panel-bg);border:var(--border) solid var(--_panel-border-color);border-radius:var(--rounded-lg);box-shadow:var(--_panel-shadow);opacity:0;-webkit-backdrop-filter:blur(var(--_panel-blur));backdrop-filter:blur(var(--_panel-blur));transform:scale(var(--overlay-enter-scale-dialog)) translateY(4px);transition:opacity var(--transition-fast), transform .2s var(--ease-spring), display var(--transition-fast) allow-discrete, overlay var(--transition-fast) allow-discrete;margin:0;padding:0;position:fixed}.panel[data-open]{opacity:1;display:block;transform:scale(1)translateY(0)}.panel:popover-open{opacity:1;transform:scale(1)translateY(0)}@starting-style{.panel:popover-open{opacity:0;transform:scale(var(--overlay-enter-scale-dialog)) translateY(4px)}}@starting-style{.panel[data-open]{opacity:0;transform:scale(var(--overlay-enter-scale-dialog)) translateY(4px)}}}`;exports.default=e;
2
2
  //# sourceMappingURL=popover.css.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"popover.css.cjs","names":[],"sources":["../../../src/overlay/popover/popover.css?inline"],"sourcesContent":["@layer refine.base {\n :host {\n --_panel-bg: var(--popover-bg, var(--color-canvas));\n --_panel-blur: var(--popover-panel-blur, var(--blur-md));\n --_panel-border-color: var(--popover-border-color, var(--color-divider));\n --_panel-shadow: var(--popover-shadow, var(--shadow-xl));\n\n position: relative;\n display: inline-block;\n }\n\n .panel {\n position: fixed;\n\n /* Popover UA resets */\n inset: unset;\n box-sizing: border-box;\n min-width: var(--popover-min-width, 12rem);\n max-width: var(--popover-max-width, 24rem);\n max-height: var(--popover-max-height, min(70vh, 32rem));\n padding: 0;\n margin: 0;\n overflow: auto;\n word-break: auto-phrase;\n overflow-wrap: anywhere;\n background: var(--_panel-bg);\n border: var(--border) solid var(--_panel-border-color);\n border-radius: var(--rounded-lg);\n box-shadow: var(--_panel-shadow);\n\n /* Hidden by default */\n opacity: 0;\n backdrop-filter: blur(var(--_panel-blur));\n transform: scale(var(--overlay-enter-scale-dialog)) translateY(4px);\n transition:\n opacity var(--transition-fast),\n transform 200ms var(--ease-spring),\n display var(--transition-fast) allow-discrete,\n overlay var(--transition-fast) allow-discrete;\n }\n\n .panel[data-open] {\n display: block;\n }\n\n .panel:popover-open,\n .panel[data-open] {\n opacity: 1;\n transform: scale(1) translateY(0);\n\n @starting-style {\n opacity: 0;\n transform: scale(var(--overlay-enter-scale-dialog)) translateY(4px);\n }\n }\n}\n"],"mappings":""}
1
+ {"version":3,"file":"popover.css.cjs","names":[],"sources":["../../../src/overlay/popover/popover.css?inline"],"sourcesContent":["@layer refine.base {\n :host {\n --_panel-bg: var(--popover-bg, var(--color-canvas));\n --_panel-blur: var(--popover-panel-blur, var(--blur-md));\n --_panel-border-color: var(--popover-border-color, var(--color-divider));\n --_panel-shadow: var(--popover-shadow, var(--shadow-xl));\n\n position: relative;\n display: inline-block;\n }\n\n .panel {\n position: fixed;\n\n /* Popover UA resets */\n inset: unset;\n box-sizing: border-box;\n min-width: var(--popover-min-width, 12rem);\n max-width: var(--popover-max-width, 24rem);\n max-height: var(--popover-max-height, min(70vh, 32rem));\n padding: 0;\n margin: 0;\n overflow: var(--popover-overflow, auto);\n word-break: auto-phrase;\n overflow-wrap: anywhere;\n background: var(--_panel-bg);\n border: var(--border) solid var(--_panel-border-color);\n border-radius: var(--rounded-lg);\n box-shadow: var(--_panel-shadow);\n\n /* Hidden by default */\n opacity: 0;\n backdrop-filter: blur(var(--_panel-blur));\n transform: scale(var(--overlay-enter-scale-dialog)) translateY(4px);\n transition:\n opacity var(--transition-fast),\n transform 200ms var(--ease-spring),\n display var(--transition-fast) allow-discrete,\n overlay var(--transition-fast) allow-discrete;\n }\n\n .panel[data-open] {\n display: block;\n }\n\n .panel:popover-open,\n .panel[data-open] {\n opacity: 1;\n transform: scale(1) translateY(0);\n\n @starting-style {\n opacity: 0;\n transform: scale(var(--overlay-enter-scale-dialog)) translateY(4px);\n }\n }\n}\n"],"mappings":""}
@@ -1,5 +1,5 @@
1
1
  //#region src/overlay/popover/popover.css?inline
2
- var e = "@layer refine.base{:host{--_panel-bg:var(--popover-bg,var(--color-canvas));--_panel-blur:var(--popover-panel-blur,var(--blur-md));--_panel-border-color:var(--popover-border-color,var(--color-divider));--_panel-shadow:var(--popover-shadow,var(--shadow-xl));display:inline-block;position:relative}.panel{inset:unset;box-sizing:border-box;min-width:var(--popover-min-width,12rem);max-width:var(--popover-max-width,24rem);max-height:var(--popover-max-height,min(70vh, 32rem));word-break:auto-phrase;overflow-wrap:anywhere;background:var(--_panel-bg);border:var(--border) solid var(--_panel-border-color);border-radius:var(--rounded-lg);box-shadow:var(--_panel-shadow);opacity:0;-webkit-backdrop-filter:blur(var(--_panel-blur));backdrop-filter:blur(var(--_panel-blur));transform:scale(var(--overlay-enter-scale-dialog)) translateY(4px);transition:opacity var(--transition-fast), transform .2s var(--ease-spring), display var(--transition-fast) allow-discrete, overlay var(--transition-fast) allow-discrete;margin:0;padding:0;position:fixed;overflow:auto}.panel[data-open]{opacity:1;display:block;transform:scale(1)translateY(0)}.panel:popover-open{opacity:1;transform:scale(1)translateY(0)}@starting-style{.panel:popover-open{opacity:0;transform:scale(var(--overlay-enter-scale-dialog)) translateY(4px)}}@starting-style{.panel[data-open]{opacity:0;transform:scale(var(--overlay-enter-scale-dialog)) translateY(4px)}}}";
2
+ var e = "@layer refine.base{:host{--_panel-bg:var(--popover-bg,var(--color-canvas));--_panel-blur:var(--popover-panel-blur,var(--blur-md));--_panel-border-color:var(--popover-border-color,var(--color-divider));--_panel-shadow:var(--popover-shadow,var(--shadow-xl));display:inline-block;position:relative}.panel{inset:unset;box-sizing:border-box;min-width:var(--popover-min-width,12rem);max-width:var(--popover-max-width,24rem);max-height:var(--popover-max-height,min(70vh, 32rem));overflow:var(--popover-overflow,auto);word-break:auto-phrase;overflow-wrap:anywhere;background:var(--_panel-bg);border:var(--border) solid var(--_panel-border-color);border-radius:var(--rounded-lg);box-shadow:var(--_panel-shadow);opacity:0;-webkit-backdrop-filter:blur(var(--_panel-blur));backdrop-filter:blur(var(--_panel-blur));transform:scale(var(--overlay-enter-scale-dialog)) translateY(4px);transition:opacity var(--transition-fast), transform .2s var(--ease-spring), display var(--transition-fast) allow-discrete, overlay var(--transition-fast) allow-discrete;margin:0;padding:0;position:fixed}.panel[data-open]{opacity:1;display:block;transform:scale(1)translateY(0)}.panel:popover-open{opacity:1;transform:scale(1)translateY(0)}@starting-style{.panel:popover-open{opacity:0;transform:scale(var(--overlay-enter-scale-dialog)) translateY(4px)}}@starting-style{.panel[data-open]{opacity:0;transform:scale(var(--overlay-enter-scale-dialog)) translateY(4px)}}}";
3
3
  //#endregion
4
4
  export { e as default };
5
5
 
@@ -1 +1 @@
1
- {"version":3,"file":"popover.css.js","names":[],"sources":["../../../src/overlay/popover/popover.css?inline"],"sourcesContent":["@layer refine.base {\n :host {\n --_panel-bg: var(--popover-bg, var(--color-canvas));\n --_panel-blur: var(--popover-panel-blur, var(--blur-md));\n --_panel-border-color: var(--popover-border-color, var(--color-divider));\n --_panel-shadow: var(--popover-shadow, var(--shadow-xl));\n\n position: relative;\n display: inline-block;\n }\n\n .panel {\n position: fixed;\n\n /* Popover UA resets */\n inset: unset;\n box-sizing: border-box;\n min-width: var(--popover-min-width, 12rem);\n max-width: var(--popover-max-width, 24rem);\n max-height: var(--popover-max-height, min(70vh, 32rem));\n padding: 0;\n margin: 0;\n overflow: auto;\n word-break: auto-phrase;\n overflow-wrap: anywhere;\n background: var(--_panel-bg);\n border: var(--border) solid var(--_panel-border-color);\n border-radius: var(--rounded-lg);\n box-shadow: var(--_panel-shadow);\n\n /* Hidden by default */\n opacity: 0;\n backdrop-filter: blur(var(--_panel-blur));\n transform: scale(var(--overlay-enter-scale-dialog)) translateY(4px);\n transition:\n opacity var(--transition-fast),\n transform 200ms var(--ease-spring),\n display var(--transition-fast) allow-discrete,\n overlay var(--transition-fast) allow-discrete;\n }\n\n .panel[data-open] {\n display: block;\n }\n\n .panel:popover-open,\n .panel[data-open] {\n opacity: 1;\n transform: scale(1) translateY(0);\n\n @starting-style {\n opacity: 0;\n transform: scale(var(--overlay-enter-scale-dialog)) translateY(4px);\n }\n }\n}\n"],"mappings":""}
1
+ {"version":3,"file":"popover.css.js","names":[],"sources":["../../../src/overlay/popover/popover.css?inline"],"sourcesContent":["@layer refine.base {\n :host {\n --_panel-bg: var(--popover-bg, var(--color-canvas));\n --_panel-blur: var(--popover-panel-blur, var(--blur-md));\n --_panel-border-color: var(--popover-border-color, var(--color-divider));\n --_panel-shadow: var(--popover-shadow, var(--shadow-xl));\n\n position: relative;\n display: inline-block;\n }\n\n .panel {\n position: fixed;\n\n /* Popover UA resets */\n inset: unset;\n box-sizing: border-box;\n min-width: var(--popover-min-width, 12rem);\n max-width: var(--popover-max-width, 24rem);\n max-height: var(--popover-max-height, min(70vh, 32rem));\n padding: 0;\n margin: 0;\n overflow: var(--popover-overflow, auto);\n word-break: auto-phrase;\n overflow-wrap: anywhere;\n background: var(--_panel-bg);\n border: var(--border) solid var(--_panel-border-color);\n border-radius: var(--rounded-lg);\n box-shadow: var(--_panel-shadow);\n\n /* Hidden by default */\n opacity: 0;\n backdrop-filter: blur(var(--_panel-blur));\n transform: scale(var(--overlay-enter-scale-dialog)) translateY(4px);\n transition:\n opacity var(--transition-fast),\n transform 200ms var(--ease-spring),\n display var(--transition-fast) allow-discrete,\n overlay var(--transition-fast) allow-discrete;\n }\n\n .panel[data-open] {\n display: block;\n }\n\n .panel:popover-open,\n .panel[data-open] {\n opacity: 1;\n transform: scale(1) translateY(0);\n\n @starting-style {\n opacity: 0;\n transform: scale(var(--overlay-enter-scale-dialog)) translateY(4px);\n }\n }\n}\n"],"mappings":""}
@@ -35,6 +35,7 @@ export type OrePopoverProps = {
35
35
  * @fires open-change - When the panel state changes. detail: { open, reason }
36
36
  * @slot - The trigger element
37
37
  * @slot content - Panel content
38
+ * @cssprop --popover-overflow - Panel overflow behavior (default: auto)
38
39
  * @part panel - Panel container.
39
40
  *
40
41
  * @example
@@ -1 +1 @@
1
- {"version":3,"file":"popover.d.ts","sourceRoot":"","sources":["../../../src/overlay/popover/popover.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAejD,OAAO,EAAE,KAAK,uBAAuB,EAAuB,MAAM,YAAY,CAAC;AAM/E,MAAM,MAAM,cAAc,GAAG,OAAO,GAAG,OAAO,GAAG,OAAO,CAAC;AAYzD,MAAM,MAAM,gBAAgB,GAAG;IAC7B,aAAa,EAAE,uBAAuB,CAAC;CACxC,CAAC;AAEF,mCAAmC;AACnC,MAAM,MAAM,eAAe,GAAG;IAC5B,mEAAmE;IACnE,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,0BAA0B;IAC1B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,qCAAqC;IACrC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,0CAA0C;IAC1C,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,4BAA4B;IAC5B,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,kDAAkD;IAClD,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB,8CAA8C;IAC9C,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,eAAO,MAAM,WAAW,EAAG,aAAsB,CAAC"}
1
+ {"version":3,"file":"popover.d.ts","sourceRoot":"","sources":["../../../src/overlay/popover/popover.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAejD,OAAO,EAAE,KAAK,uBAAuB,EAAuB,MAAM,YAAY,CAAC;AAM/E,MAAM,MAAM,cAAc,GAAG,OAAO,GAAG,OAAO,GAAG,OAAO,CAAC;AAYzD,MAAM,MAAM,gBAAgB,GAAG;IAC7B,aAAa,EAAE,uBAAuB,CAAC;CACxC,CAAC;AAEF,mCAAmC;AACnC,MAAM,MAAM,eAAe,GAAG;IAC5B,mEAAmE;IACnE,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,0BAA0B;IAC1B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,qCAAqC;IACrC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,0CAA0C;IAC1C,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,4BAA4B;IAC5B,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,kDAAkD;IAClD,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB,8CAA8C;IAC9C,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,eAAO,MAAM,WAAW,EAAG,aAAsB,CAAC"}
@@ -1,2 +1,2 @@
1
- const e=require("../../core/focus.cjs"),t=require("../../core/ore.cjs"),n=require("./await-exit.cjs"),r=require("./background-lock.cjs");let i=require("@vielzeug/ripple");function a(a){let o=t.lifecycleSignal(a.onCleanup),s=(0,i.signal)(!1),c=`programmatic`,l=r.createBackgroundLock(),u=!1,d=e.createFocusManager({getInitialFocusSelector:()=>a.initialFocus.value,getReturnFocus:()=>a.returnFocus.value,host:a.host,signal:o}),f=()=>{let e=a.dialogRef.value;if(!e?.open||u)return;u=!0,e.classList.add(`closing`);let t=()=>{e.close(),u=!1},r=a.getPanelEl();r?n.awaitExit(r,t,`transition`):t()},p=e=>a.host.dispatchEvent(new CustomEvent(`close-request`,{bubbles:!0,cancelable:!0,composed:!0,detail:{reason:e,...a.closeRequestDetail?.(e)}})),m={close(e=`programmatic`){if(!s.value)return;let t=a.dialogRef.value;t?.open?(c=e,(a.performClose??((e,t)=>e.close()))(t,e)):s.value=!1},dispose(){m.close(`programmatic`)},open(e=`programmatic`){if(s.value)return;let t=a.dialogRef.value;!t||t.open||(t.classList.remove(`closing`),d.captureReturnFocus(),a.beforeOpen?.(t),t.showModal(),l.lock(a.host),d.applyInitialFocus(),s.value=!0,a.onOpen?.(e))},[Symbol.dispose](){m.dispose()},toggle(e=`click`,t=`trigger`){s.value?m.close(t):m.open(e)}};o.addEventListener(`abort`,m.dispose,{once:!0});let h=e=>{p(e)&&m.close(e)};a.host.addEventListener(`command`,e=>{let t=e.command;t===`show-modal`?m.open(`trigger`):(t===`close`||t===`request-close`)&&h(`trigger`)},{signal:o});let g=e=>{e.target===a.dialogRef.value&&(e.preventDefault(),!a.isPersistent()&&h(`escape`))},_=e=>{a.isPersistent()||e.target===a.dialogRef.value&&h(`outsideClick`)},v=e=>{if(e&&e.target!==a.dialogRef.value)return;l.unlock(),a.host.removeAttribute(`open`),s.value=!1,d.restoreFocus();let t=c;c=`programmatic`,a.onNativeClose?.(t)},y=()=>{let e=a.dialogRef.value;e&&a.onEvent(e,`close`,v);let t=!1;(0,i.watch)(a.openProp,e=>{if(e===void 0){if(!t){a.defaultOpen.value&&m.open(`programmatic`),t=!0;return}m.close(`programmatic`);return}t=!0,e?m.open(`programmatic`):m.close(`programmatic`)},{immediate:!0})};return{closeWithAnimation:f,dispatchCloseRequest:p,handleBackdropClick:_,handleCancel:g,isOpen:s,overlay:m,requestClose:h,setupNativeListeners:()=>{let e=a.dialogRef.value;e&&(y(),a.onEvent(e,`click`,_),a.onEvent(e,`cancel`,g))},watchOpenProp:y}}exports.useDialogControl=a;
1
+ const e=require("../../core/focus.cjs"),t=require("../../core/ore.cjs"),n=require("./await-exit.cjs"),r=require("./background-lock.cjs");let i=require("@vielzeug/ripple");function a(a){let o=t.lifecycleSignal(a.onCleanup),s=(0,i.signal)(!1),c=`programmatic`,l=r.createBackgroundLock(),u=!1,d=e.createFocusManager({getInitialFocusSelector:()=>a.initialFocus.value,getReturnFocus:()=>a.returnFocus.value,host:a.host,signal:o}),f=()=>{let e=a.dialogRef.value;if(!e?.open||u)return;u=!0,e.classList.add(`closing`);let t=()=>{e.close(),u=!1},r=a.getPanelEl();r?n.awaitExit(r,t,`transition`):t()},p=e=>a.host.dispatchEvent(new CustomEvent(`close-request`,{bubbles:!0,cancelable:!0,composed:!0,detail:{reason:e,...a.closeRequestDetail?.(e)}})),m={close(e=`programmatic`){if(!s.value)return;let t=a.dialogRef.value;t?.open?(c=e,(a.performClose??((e,t)=>e.close()))(t,e)):s.value=!1},dispose(){m.close(`programmatic`)},open(e=`programmatic`){if(s.value)return;let t=a.dialogRef.value;t&&!t.open&&(t.classList.remove(`closing`),d.captureReturnFocus(),a.beforeOpen?.(t),t.showModal(),l.lock(a.host),d.applyInitialFocus(),s.value=!0,a.onOpen?.(e))},[Symbol.dispose](){m.dispose()},toggle(e=`click`,t=`trigger`){s.value?m.close(t):m.open(e)}};o.addEventListener(`abort`,m.dispose,{once:!0});let h=e=>{p(e)&&m.close(e)};a.host.addEventListener(`command`,e=>{let t=e.command;t===`show-modal`?m.open(`trigger`):(t===`close`||t===`request-close`)&&h(`trigger`)},{signal:o});let g=e=>{e.target===a.dialogRef.value&&(e.preventDefault(),!a.isPersistent()&&h(`escape`))},_=e=>{a.isPersistent()||e.target===a.dialogRef.value&&h(`outsideClick`)},v=e=>{if(e&&e.target!==a.dialogRef.value)return;l.unlock(),a.host.removeAttribute(`open`),s.value=!1,d.restoreFocus();let t=c;c=`programmatic`,a.onNativeClose?.(t)},y=()=>{let e=a.dialogRef.value;e&&a.onEvent(e,`close`,v);let t=!1;(0,i.watch)(a.openProp,e=>{if(e===void 0){if(!t){a.defaultOpen.value&&m.open(`programmatic`),t=!0;return}m.close(`programmatic`);return}t=!0,e?m.open(`programmatic`):m.close(`programmatic`)},{immediate:!0})};return{closeWithAnimation:f,dispatchCloseRequest:p,handleBackdropClick:_,handleCancel:g,isOpen:s,overlay:m,requestClose:h,setupNativeListeners:()=>{let e=a.dialogRef.value;e&&(y(),a.onEvent(e,`click`,_),a.onEvent(e,`cancel`,g))},watchOpenProp:y}}exports.useDialogControl=a;
2
2
  //# sourceMappingURL=use-dialog.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"use-dialog.cjs","names":[],"sources":["../../../src/overlay/shared/use-dialog.ts"],"sourcesContent":["import { type Readable, type Signal, signal, watch } from '@vielzeug/ripple';\n\nimport { createFocusManager, type DialogCloseReason, lifecycleSignal, type OverlayOpenReason } from '../../core';\nimport { awaitExit } from './await-exit';\nimport { createBackgroundLock } from './background-lock';\n\n// Invoker Commands API — not yet in lib.dom.d.ts\ninterface CommandEvent extends Event {\n command: string;\n}\n\n// ── Types ─────────────────────────────────────────────────────────────────────\n\nexport type UseDialogOptions = {\n /** Called after showModal() but before isOpen = true. Use to clear drag styles etc. */\n beforeOpen?: (dialog: HTMLDialogElement) => void;\n /** Extra properties merged into the `close-request` custom event detail. */\n closeRequestDetail?: (reason: Exclude<DialogCloseReason, 'programmatic'>) => Record<string, unknown>;\n /** Initial uncontrolled open state. Ignored when `openProp` is defined. */\n defaultOpen: Readable<boolean | undefined>;\n dialogRef: { value: HTMLDialogElement | null | undefined };\n getPanelEl: () => HTMLElement | null | undefined;\n host: HTMLElement;\n initialFocus: Readable<string | undefined>;\n isPersistent: () => boolean;\n /** Cleanup registrar from the component setup ctx. Automatically called on disconnect. */\n onCleanup: (fn: () => void) => void;\n /**\n * Scoped event listener registrar from the component setup ctx.\n * Automatically removed on component disconnect.\n */\n onEvent: <K extends keyof HTMLElementEventMap>(\n target: EventTarget | null | undefined,\n event: K,\n listener: (e: HTMLElementEventMap[K]) => void,\n options?: AddEventListenerOptions,\n ) => void;\n /**\n * Called after the native `close` event fires — after background unlock,\n * isOpen reset, and focus restore. Use to emit component events.\n */\n onNativeClose?: (reason: DialogCloseReason) => void;\n /** Called by the overlay on open. Use to emit events. */\n onOpen?: (reason: OverlayOpenReason) => void;\n /** Open prop from the component — drives programmatic open/close via watch. */\n openProp: Readable<boolean | undefined>;\n /**\n * Called by the overlay when closing. Orchestrates the close animation.\n * Default: `dialog.close()`. Override in dialog.ts to add the CSS exit animation.\n */\n performClose?: (dialog: HTMLDialogElement, reason: DialogCloseReason) => void;\n returnFocus: Readable<boolean | undefined>;\n};\n\nexport type UseDialogHandle = {\n closeWithAnimation: () => void;\n /**\n * Dispatch a cancelable `close-request` event on the host. Returns true when\n * the close is allowed (event not prevented), false when it was cancelled.\n */\n dispatchCloseRequest: (reason: Exclude<DialogCloseReason, 'programmatic'>) => boolean;\n handleBackdropClick: (e: MouseEvent) => void;\n handleCancel: (e: Event) => void;\n isOpen: Signal<boolean>;\n /** The modal dialog controller — open/close/toggle/dispose. */\n overlay: ModalDialogController;\n /**\n * Dispatch `close-request`; if allowed, call `overlay.close()`.\n */\n requestClose: (reason: Exclude<DialogCloseReason, 'programmatic'>) => void;\n /**\n * Registers the open-prop watcher AND standard click/cancel event listeners on\n * the dialog element (backdrop click-to-close, Escape via the native `cancel`\n * event). Use for components that need standard dialog behavior.\n * Call inside `onMounted()`.\n */\n setupNativeListeners: () => void;\n /**\n * Registers only the open-prop watcher (without adding click/cancel listeners).\n * Use for components like drawer that register their own event handlers.\n * Call inside `onMounted()`.\n */\n watchOpenProp: () => void;\n};\n\ntype ModalDialogController = {\n [Symbol.dispose](): void;\n close(reason?: DialogCloseReason): void;\n dispose(): void;\n open(reason?: OverlayOpenReason): void;\n toggle(openReason?: OverlayOpenReason, closeReason?: DialogCloseReason): void;\n};\n\n// ── Factory ───────────────────────────────────────────────────────────────────\n\n/**\n * Shared dialog-control composable for `ore-dialog` and `ore-drawer`.\n *\n * Owns: `isOpen`, close-reason tracking, focus management, `overlay`,\n * `requestClose`, background lock/unlock, and the `close` event handler.\n * Components supply only their unique behavior via callbacks and `onNativeClose`.\n */\nexport function useDialogControl(options: UseDialogOptions): UseDialogHandle {\n const abortSignal = lifecycleSignal(options.onCleanup);\n const isOpen = signal(false);\n // Internal close-reason: set atomically before dialog.close() fires.\n let pendingCloseReason: DialogCloseReason = 'programmatic';\n const bgLock = createBackgroundLock();\n let isClosing = false;\n\n // ── Focus management ──────────────────────────────────────────────────────\n const focus = createFocusManager({\n getInitialFocusSelector: () => options.initialFocus.value,\n getReturnFocus: () => options.returnFocus.value,\n host: options.host,\n signal: abortSignal,\n });\n // ─────────────────────────────────────────────────────────────────────────\n\n const closeWithAnimation = (): void => {\n const dialog = options.dialogRef.value;\n\n if (!dialog?.open || isClosing) return;\n\n isClosing = true;\n dialog.classList.add('closing');\n\n const finish = () => {\n dialog.close();\n // Do NOT remove 'closing' here — removing it while the native ::backdrop is\n // still held in the top layer by the 'overlay allow-discrete' transition\n // would revert its opacity to 1 and cause a visible flash. The class is\n // cleaned up at the start of the next open cycle (see setOpen below).\n isClosing = false;\n };\n\n const panel = options.getPanelEl();\n\n if (panel) {\n awaitExit(panel, finish, 'transition');\n } else {\n finish();\n }\n };\n\n const dispatchCloseRequest = (reason: Exclude<DialogCloseReason, 'programmatic'>): boolean => {\n return options.host.dispatchEvent(\n new CustomEvent('close-request', {\n bubbles: true,\n cancelable: true,\n composed: true,\n detail: { reason, ...options.closeRequestDetail?.(reason) },\n }),\n );\n };\n\n const overlay: ModalDialogController = {\n close(reason: DialogCloseReason = 'programmatic'): void {\n if (!isOpen.value) return;\n\n const dialog = options.dialogRef.value;\n\n if (dialog?.open) {\n pendingCloseReason = reason;\n (options.performClose ?? ((element, _reason) => element.close()))(dialog, reason);\n } else {\n isOpen.value = false;\n }\n },\n dispose(): void {\n overlay.close('programmatic');\n },\n open(reason: OverlayOpenReason = 'programmatic'): void {\n if (isOpen.value) return;\n\n const dialog = options.dialogRef.value;\n\n if (!dialog || dialog.open) return;\n\n // Clean up any leftover 'closing' class from the previous close animation\n // before opening so the entry @starting-style and transition work correctly.\n dialog.classList.remove('closing');\n focus.captureReturnFocus();\n options.beforeOpen?.(dialog);\n dialog.showModal();\n bgLock.lock(options.host);\n focus.applyInitialFocus();\n isOpen.value = true;\n options.onOpen?.(reason);\n },\n [Symbol.dispose](): void {\n overlay.dispose();\n },\n toggle(openReason: OverlayOpenReason = 'click', closeReason: DialogCloseReason = 'trigger'): void {\n if (isOpen.value) overlay.close(closeReason);\n else overlay.open(openReason);\n },\n };\n\n abortSignal.addEventListener('abort', overlay.dispose, { once: true });\n\n const requestClose = (reason: Exclude<DialogCloseReason, 'programmatic'>): void => {\n const allowed = dispatchCloseRequest(reason);\n\n if (!allowed) return;\n\n overlay.close(reason);\n };\n\n // Invoker Commands API — allows declarative open/close from outside the component:\n // <button commandfor=\"ore-dialog-id\" command=\"show-modal\">Open</button>\n // <button commandfor=\"ore-dialog-id\" command=\"close\">Close</button>\n options.host.addEventListener(\n 'command',\n (e: Event) => {\n const cmd = (e as CommandEvent).command;\n\n if (cmd === 'show-modal') overlay.open('trigger');\n else if (cmd === 'close' || cmd === 'request-close') requestClose('trigger');\n },\n { signal: abortSignal },\n );\n\n // The native `<dialog>` element auto-closes on Escape via its own close-watcher, which\n // dispatches a cancelable `cancel` event before `close`. Suppressing/redirecting that is the\n // spec-correct hook — relying on `keydown`'s `preventDefault()` to stop the close-watcher is\n // implementation-specific, not guaranteed across browsers.\n const handleCancel = (e: Event): void => {\n // See `handleNativeClose`'s comment: guard against a same-named bubbling event from a\n // descendant field being misread as this dialog's own native event.\n if (e.target !== options.dialogRef.value) return;\n\n e.preventDefault();\n\n if (options.isPersistent()) return;\n\n requestClose('escape');\n };\n\n const handleBackdropClick = (e: MouseEvent): void => {\n if (options.isPersistent()) return;\n\n if (e.target === options.dialogRef.value) requestClose('outsideClick');\n };\n\n // ── Internal native close handler ─────────────────────────────────────────\n // `e.target` must be checked against the native `<dialog>` itself, the same way\n // `handleBackdropClick` already does: a bubbling `close`-named event fired by *any* descendant\n // component reaches this listener too — regardless of that event's own `composed` flag, since\n // slot-assignment-based event-path computation (a slotted descendant's \"parent\" is the `<slot>`\n // projecting it) isn't gated by `composed` the way crossing back out of a shadow root is. Some\n // fields nested here (`ore-select`, `ore-combobox`, …) emit their own public `close` event when\n // *their* dropdown closes — without this check, selecting an option in one of those closes this\n // dialog too, misreading the field's own event as this dialog's native close.\n const handleNativeClose = (e?: Event): void => {\n if (e && e.target !== options.dialogRef.value) return;\n\n bgLock.unlock();\n options.host.removeAttribute('open');\n isOpen.value = false;\n focus.restoreFocus();\n\n const reason = pendingCloseReason;\n\n pendingCloseReason = 'programmatic';\n options.onNativeClose?.(reason);\n };\n\n const watchOpenProp = (): void => {\n const dialog = options.dialogRef.value;\n\n if (dialog) options.onEvent(dialog, 'close', handleNativeClose);\n\n let initialized = false;\n\n watch(\n options.openProp,\n (open) => {\n if (open === undefined) {\n if (!initialized) {\n if (options.defaultOpen.value) overlay.open('programmatic');\n\n initialized = true;\n\n return;\n }\n\n overlay.close('programmatic');\n\n return;\n }\n\n initialized = true;\n\n if (open) {\n overlay.open('programmatic');\n } else {\n overlay.close('programmatic');\n }\n },\n { immediate: true },\n );\n };\n\n const setupNativeListeners = (): void => {\n const dialog = options.dialogRef.value;\n\n if (!dialog) return;\n\n watchOpenProp();\n options.onEvent(dialog, 'click', handleBackdropClick);\n options.onEvent(dialog, 'cancel', handleCancel);\n };\n\n return {\n closeWithAnimation,\n dispatchCloseRequest,\n handleBackdropClick,\n handleCancel,\n isOpen,\n overlay,\n requestClose,\n setupNativeListeners,\n watchOpenProp,\n };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"use-dialog.cjs","names":[],"sources":["../../../src/overlay/shared/use-dialog.ts"],"sourcesContent":["import { type Readable, type Signal, signal, watch } from '@vielzeug/ripple';\n\nimport { createFocusManager, type DialogCloseReason, lifecycleSignal, type OverlayOpenReason } from '../../core';\nimport { awaitExit } from './await-exit';\nimport { createBackgroundLock } from './background-lock';\n\n// Invoker Commands API — not yet in lib.dom.d.ts\ninterface CommandEvent extends Event {\n command: string;\n}\n\n// ── Types ─────────────────────────────────────────────────────────────────────\n\nexport type UseDialogOptions = {\n /** Called after showModal() but before isOpen = true. Use to clear drag styles etc. */\n beforeOpen?: (dialog: HTMLDialogElement) => void;\n /** Extra properties merged into the `close-request` custom event detail. */\n closeRequestDetail?: (reason: Exclude<DialogCloseReason, 'programmatic'>) => Record<string, unknown>;\n /** Initial uncontrolled open state. Ignored when `openProp` is defined. */\n defaultOpen: Readable<boolean | undefined>;\n dialogRef: { value: HTMLDialogElement | null | undefined };\n getPanelEl: () => HTMLElement | null | undefined;\n host: HTMLElement;\n initialFocus: Readable<string | undefined>;\n isPersistent: () => boolean;\n /** Cleanup registrar from the component setup ctx. Automatically called on disconnect. */\n onCleanup: (fn: () => void) => void;\n /**\n * Scoped event listener registrar from the component setup ctx.\n * Automatically removed on component disconnect.\n */\n onEvent: <K extends keyof HTMLElementEventMap>(\n target: EventTarget | null | undefined,\n event: K,\n listener: (e: HTMLElementEventMap[K]) => void,\n options?: AddEventListenerOptions,\n ) => void;\n /**\n * Called after the native `close` event fires — after background unlock,\n * isOpen reset, and focus restore. Use to emit component events.\n */\n onNativeClose?: (reason: DialogCloseReason) => void;\n /** Called by the overlay on open. Use to emit events. */\n onOpen?: (reason: OverlayOpenReason) => void;\n /** Open prop from the component — drives programmatic open/close via watch. */\n openProp: Readable<boolean | undefined>;\n /**\n * Called by the overlay when closing. Orchestrates the close animation.\n * Default: `dialog.close()`. Override in dialog.ts to add the CSS exit animation.\n */\n performClose?: (dialog: HTMLDialogElement, reason: DialogCloseReason) => void;\n returnFocus: Readable<boolean | undefined>;\n};\n\nexport type UseDialogHandle = {\n closeWithAnimation: () => void;\n /**\n * Dispatch a cancelable `close-request` event on the host. Returns true when\n * the close is allowed (event not prevented), false when it was cancelled.\n */\n dispatchCloseRequest: (reason: Exclude<DialogCloseReason, 'programmatic'>) => boolean;\n handleBackdropClick: (e: MouseEvent) => void;\n handleCancel: (e: Event) => void;\n isOpen: Signal<boolean>;\n /** The modal dialog controller — open/close/toggle/dispose. */\n overlay: ModalDialogController;\n /**\n * Dispatch `close-request`; if allowed, call `overlay.close()`.\n */\n requestClose: (reason: Exclude<DialogCloseReason, 'programmatic'>) => void;\n /**\n * Registers the open-prop watcher AND standard click/cancel event listeners on\n * the dialog element (backdrop click-to-close, Escape via the native `cancel`\n * event). Use for components that need standard dialog behavior.\n * Call inside `onMounted()`.\n */\n setupNativeListeners: () => void;\n /**\n * Registers only the open-prop watcher (without adding click/cancel listeners).\n * Use for components like drawer that register their own event handlers.\n * Call inside `onMounted()`.\n */\n watchOpenProp: () => void;\n};\n\ntype ModalDialogController = {\n [Symbol.dispose](): void;\n close(reason?: DialogCloseReason): void;\n dispose(): void;\n open(reason?: OverlayOpenReason): void;\n toggle(openReason?: OverlayOpenReason, closeReason?: DialogCloseReason): void;\n};\n\n// ── Factory ───────────────────────────────────────────────────────────────────\n\n/**\n * Shared dialog-control composable for `ore-dialog` and `ore-drawer`.\n *\n * Owns: `isOpen`, close-reason tracking, focus management, `overlay`,\n * `requestClose`, background lock/unlock, and the `close` event handler.\n * Components supply only their unique behavior via callbacks and `onNativeClose`.\n */\nexport function useDialogControl(options: UseDialogOptions): UseDialogHandle {\n const abortSignal = lifecycleSignal(options.onCleanup);\n const isOpen = signal(false);\n // Internal close-reason: set atomically before dialog.close() fires.\n let pendingCloseReason: DialogCloseReason = 'programmatic';\n const bgLock = createBackgroundLock();\n let isClosing = false;\n\n // ── Focus management ──────────────────────────────────────────────────────\n const focus = createFocusManager({\n getInitialFocusSelector: () => options.initialFocus.value,\n getReturnFocus: () => options.returnFocus.value,\n host: options.host,\n signal: abortSignal,\n });\n // ─────────────────────────────────────────────────────────────────────────\n\n const closeWithAnimation = (): void => {\n const dialog = options.dialogRef.value;\n\n if (!dialog?.open || isClosing) return;\n\n isClosing = true;\n dialog.classList.add('closing');\n\n const finish = () => {\n dialog.close();\n // Do NOT remove 'closing' here — removing it while the native ::backdrop is\n // still held in the top layer by the 'overlay allow-discrete' transition\n // would revert its opacity to 1 and cause a visible flash. The class is\n // cleaned up at the start of the next open cycle (see setOpen below).\n isClosing = false;\n };\n\n const panel = options.getPanelEl();\n\n if (panel) {\n awaitExit(panel, finish, 'transition');\n } else {\n finish();\n }\n };\n\n const dispatchCloseRequest = (reason: Exclude<DialogCloseReason, 'programmatic'>): boolean => {\n return options.host.dispatchEvent(\n new CustomEvent('close-request', {\n bubbles: true,\n cancelable: true,\n composed: true,\n detail: { reason, ...options.closeRequestDetail?.(reason) },\n }),\n );\n };\n\n const overlay: ModalDialogController = {\n close(reason: DialogCloseReason = 'programmatic'): void {\n if (!isOpen.value) return;\n\n const dialog = options.dialogRef.value;\n\n if (dialog?.open) {\n pendingCloseReason = reason;\n (options.performClose ?? ((element, _reason) => element.close()))(dialog, reason);\n } else {\n isOpen.value = false;\n }\n },\n dispose(): void {\n overlay.close('programmatic');\n },\n open(reason: OverlayOpenReason = 'programmatic'): void {\n if (isOpen.value) return;\n\n const dialog = options.dialogRef.value;\n\n if (!dialog || dialog.open) return;\n\n // Clean up any leftover 'closing' class from the previous close animation\n // before opening so the entry @starting-style and transition work correctly.\n dialog.classList.remove('closing');\n focus.captureReturnFocus();\n options.beforeOpen?.(dialog);\n dialog.showModal();\n bgLock.lock(options.host);\n focus.applyInitialFocus();\n isOpen.value = true;\n options.onOpen?.(reason);\n },\n [Symbol.dispose](): void {\n overlay.dispose();\n },\n toggle(openReason: OverlayOpenReason = 'click', closeReason: DialogCloseReason = 'trigger'): void {\n if (isOpen.value) overlay.close(closeReason);\n else overlay.open(openReason);\n },\n };\n\n abortSignal.addEventListener('abort', overlay.dispose, { once: true });\n\n const requestClose = (reason: Exclude<DialogCloseReason, 'programmatic'>): void => {\n const allowed = dispatchCloseRequest(reason);\n\n if (!allowed) return;\n\n overlay.close(reason);\n };\n\n // Invoker Commands API — allows declarative open/close from outside the component:\n // <button commandfor=\"ore-dialog-id\" command=\"show-modal\">Open</button>\n // <button commandfor=\"ore-dialog-id\" command=\"close\">Close</button>\n options.host.addEventListener(\n 'command',\n (e: Event) => {\n const cmd = (e as CommandEvent).command;\n\n if (cmd === 'show-modal') overlay.open('trigger');\n else if (cmd === 'close' || cmd === 'request-close') requestClose('trigger');\n },\n { signal: abortSignal },\n );\n\n // The native `<dialog>` element auto-closes on Escape via its own close-watcher, which\n // dispatches a cancelable `cancel` event before `close`. Suppressing/redirecting that is the\n // spec-correct hook — relying on `keydown`'s `preventDefault()` to stop the close-watcher is\n // implementation-specific, not guaranteed across browsers.\n const handleCancel = (e: Event): void => {\n // See `handleNativeClose`'s comment: guard against a same-named bubbling event from a\n // descendant field being misread as this dialog's own native event.\n if (e.target !== options.dialogRef.value) return;\n\n e.preventDefault();\n\n if (options.isPersistent()) return;\n\n requestClose('escape');\n };\n\n const handleBackdropClick = (e: MouseEvent): void => {\n if (options.isPersistent()) return;\n\n if (e.target === options.dialogRef.value) requestClose('outsideClick');\n };\n\n // ── Internal native close handler ─────────────────────────────────────────\n // `e.target` must be checked against the native `<dialog>` itself, the same way\n // `handleBackdropClick` already does: a bubbling `close`-named event fired by *any* descendant\n // component reaches this listener too — regardless of that event's own `composed` flag, since\n // slot-assignment-based event-path computation (a slotted descendant's \"parent\" is the `<slot>`\n // projecting it) isn't gated by `composed` the way crossing back out of a shadow root is. Some\n // fields nested here (`ore-select`, `ore-combobox`, …) emit their own public `close` event when\n // *their* dropdown closes — without this check, selecting an option in one of those closes this\n // dialog too, misreading the field's own event as this dialog's native close.\n const handleNativeClose = (e?: Event): void => {\n if (e && e.target !== options.dialogRef.value) return;\n\n bgLock.unlock();\n options.host.removeAttribute('open');\n isOpen.value = false;\n focus.restoreFocus();\n\n const reason = pendingCloseReason;\n\n pendingCloseReason = 'programmatic';\n options.onNativeClose?.(reason);\n };\n\n const watchOpenProp = (): void => {\n const dialog = options.dialogRef.value;\n\n if (dialog) options.onEvent(dialog, 'close', handleNativeClose);\n\n let initialized = false;\n\n watch(\n options.openProp,\n (open) => {\n if (open === undefined) {\n if (!initialized) {\n if (options.defaultOpen.value) overlay.open('programmatic');\n\n initialized = true;\n\n return;\n }\n\n overlay.close('programmatic');\n\n return;\n }\n\n initialized = true;\n\n if (open) {\n overlay.open('programmatic');\n } else {\n overlay.close('programmatic');\n }\n },\n { immediate: true },\n );\n };\n\n const setupNativeListeners = (): void => {\n const dialog = options.dialogRef.value;\n\n if (!dialog) return;\n\n watchOpenProp();\n options.onEvent(dialog, 'click', handleBackdropClick);\n options.onEvent(dialog, 'cancel', handleCancel);\n };\n\n return {\n closeWithAnimation,\n dispatchCloseRequest,\n handleBackdropClick,\n handleCancel,\n isOpen,\n overlay,\n requestClose,\n setupNativeListeners,\n watchOpenProp,\n };\n}\n"],"mappings":"2KAsGA,SAAgB,EAAiB,EAA4C,CAC3E,IAAM,EAAc,EAAA,gBAAgB,EAAQ,SAAS,EAC/C,GAAA,EAAS,EAAA,OAAA,CAAO,EAAK,EAEvB,EAAwC,eACtC,EAAS,EAAA,qBAAqB,EAChC,EAAY,GAGV,EAAQ,EAAA,mBAAmB,CAC/B,4BAA+B,EAAQ,aAAa,MACpD,mBAAsB,EAAQ,YAAY,MAC1C,KAAM,EAAQ,KACd,OAAQ,CACV,CAAC,EAGK,MAAiC,CACrC,IAAM,EAAS,EAAQ,UAAU,MAEjC,GAAI,CAAC,GAAQ,MAAQ,EAAW,OAEhC,EAAY,GACZ,EAAO,UAAU,IAAI,SAAS,EAE9B,IAAM,MAAe,CACnB,EAAO,MAAM,EAKb,EAAY,EACd,EAEM,EAAQ,EAAQ,WAAW,EAE7B,EACF,EAAA,UAAU,EAAO,EAAQ,YAAY,EAErC,EAAO,CAEX,EAEM,EAAwB,GACrB,EAAQ,KAAK,cAClB,IAAI,YAAY,gBAAiB,CAC/B,QAAS,GACT,WAAY,GACZ,SAAU,GACV,OAAQ,CAAE,SAAQ,GAAG,EAAQ,qBAAqB,CAAM,CAAE,CAC5D,CAAC,CACH,EAGI,EAAiC,CACrC,MAAM,EAA4B,eAAsB,CACtD,GAAI,CAAC,EAAO,MAAO,OAEnB,IAAM,EAAS,EAAQ,UAAU,MAE7B,GAAQ,MACV,EAAqB,GACpB,EAAQ,gBAAkB,EAAS,IAAY,EAAQ,MAAM,GAAA,CAAI,EAAQ,CAAM,GAEhF,EAAO,MAAQ,EAEnB,EACA,SAAgB,CACd,EAAQ,MAAM,cAAc,CAC9B,EACA,KAAK,EAA4B,eAAsB,CACrD,GAAI,EAAO,MAAO,OAElB,IAAM,EAAS,EAAQ,UAAU,MAE5B,GAAU,GAAO,OAItB,EAAO,UAAU,OAAO,SAAS,EACjC,EAAM,mBAAmB,EACzB,EAAQ,aAAa,CAAM,EAC3B,EAAO,UAAU,EACjB,EAAO,KAAK,EAAQ,IAAI,EACxB,EAAM,kBAAkB,EACxB,EAAO,MAAQ,GACf,EAAQ,SAAS,CAAM,EACzB,EACA,CAAC,OAAO,UAAiB,CACvB,EAAQ,QAAQ,CAClB,EACA,OAAO,EAAgC,QAAS,EAAiC,UAAiB,CAC5F,EAAO,MAAO,EAAQ,MAAM,CAAW,EACtC,EAAQ,KAAK,CAAU,CAC9B,CACF,EAEA,EAAY,iBAAiB,QAAS,EAAQ,QAAS,CAAE,KAAM,EAAK,CAAC,EAErE,IAAM,EAAgB,GAA6D,CACjE,EAAqB,CAEhC,GAEL,EAAQ,MAAM,CAAM,CACtB,EAKA,EAAQ,KAAK,iBACX,UACC,GAAa,CACZ,IAAM,EAAO,EAAmB,QAE5B,IAAQ,aAAc,EAAQ,KAAK,SAAS,GACvC,IAAQ,SAAW,IAAQ,kBAAiB,EAAa,SAAS,CAC7E,EACA,CAAE,OAAQ,CAAY,CACxB,EAMA,IAAM,EAAgB,GAAmB,CAGnC,EAAE,SAAW,EAAQ,UAAU,QAEnC,EAAE,eAAe,EAEb,GAAQ,aAAa,GAEzB,EAAa,QAAQ,EACvB,EAEM,EAAuB,GAAwB,CAC/C,EAAQ,aAAa,GAErB,EAAE,SAAW,EAAQ,UAAU,OAAO,EAAa,cAAc,CACvE,EAWM,EAAqB,GAAoB,CAC7C,GAAI,GAAK,EAAE,SAAW,EAAQ,UAAU,MAAO,OAE/C,EAAO,OAAO,EACd,EAAQ,KAAK,gBAAgB,MAAM,EACnC,EAAO,MAAQ,GACf,EAAM,aAAa,EAEnB,IAAM,EAAS,EAEf,EAAqB,eACrB,EAAQ,gBAAgB,CAAM,CAChC,EAEM,MAA4B,CAChC,IAAM,EAAS,EAAQ,UAAU,MAE7B,GAAQ,EAAQ,QAAQ,EAAQ,QAAS,CAAiB,EAE9D,IAAI,EAAc,IAElB,EAAA,EAAA,MAAA,CACE,EAAQ,SACP,GAAS,CACR,GAAI,IAAS,IAAA,GAAW,CACtB,GAAI,CAAC,EAAa,CACZ,EAAQ,YAAY,OAAO,EAAQ,KAAK,cAAc,EAE1D,EAAc,GAEd,MACF,CAEA,EAAQ,MAAM,cAAc,EAE5B,MACF,CAEA,EAAc,GAEV,EACF,EAAQ,KAAK,cAAc,EAE3B,EAAQ,MAAM,cAAc,CAEhC,EACA,CAAE,UAAW,EAAK,CACpB,CACF,EAYA,MAAO,CACL,qBACA,uBACA,sBACA,eACA,SACA,UACA,eACA,yBAlBuC,CACvC,IAAM,EAAS,EAAQ,UAAU,MAE5B,IAEL,EAAc,EACd,EAAQ,QAAQ,EAAQ,QAAS,CAAmB,EACpD,EAAQ,QAAQ,EAAQ,SAAU,CAAY,EAChD,EAWE,eACF,CACF"}
@@ -38,7 +38,7 @@ function o(o) {
38
38
  open(e = "programmatic") {
39
39
  if (c.value) return;
40
40
  let t = o.dialogRef.value;
41
- !t || t.open || (t.classList.remove("closing"), f.captureReturnFocus(), o.beforeOpen?.(t), t.showModal(), u.lock(o.host), f.applyInitialFocus(), c.value = !0, o.onOpen?.(e));
41
+ t && !t.open && (t.classList.remove("closing"), f.captureReturnFocus(), o.beforeOpen?.(t), t.showModal(), u.lock(o.host), f.applyInitialFocus(), c.value = !0, o.onOpen?.(e));
42
42
  },
43
43
  [Symbol.dispose]() {
44
44
  h.dispose();
@@ -1 +1 @@
1
- {"version":3,"file":"use-dialog.js","names":[],"sources":["../../../src/overlay/shared/use-dialog.ts"],"sourcesContent":["import { type Readable, type Signal, signal, watch } from '@vielzeug/ripple';\n\nimport { createFocusManager, type DialogCloseReason, lifecycleSignal, type OverlayOpenReason } from '../../core';\nimport { awaitExit } from './await-exit';\nimport { createBackgroundLock } from './background-lock';\n\n// Invoker Commands API — not yet in lib.dom.d.ts\ninterface CommandEvent extends Event {\n command: string;\n}\n\n// ── Types ─────────────────────────────────────────────────────────────────────\n\nexport type UseDialogOptions = {\n /** Called after showModal() but before isOpen = true. Use to clear drag styles etc. */\n beforeOpen?: (dialog: HTMLDialogElement) => void;\n /** Extra properties merged into the `close-request` custom event detail. */\n closeRequestDetail?: (reason: Exclude<DialogCloseReason, 'programmatic'>) => Record<string, unknown>;\n /** Initial uncontrolled open state. Ignored when `openProp` is defined. */\n defaultOpen: Readable<boolean | undefined>;\n dialogRef: { value: HTMLDialogElement | null | undefined };\n getPanelEl: () => HTMLElement | null | undefined;\n host: HTMLElement;\n initialFocus: Readable<string | undefined>;\n isPersistent: () => boolean;\n /** Cleanup registrar from the component setup ctx. Automatically called on disconnect. */\n onCleanup: (fn: () => void) => void;\n /**\n * Scoped event listener registrar from the component setup ctx.\n * Automatically removed on component disconnect.\n */\n onEvent: <K extends keyof HTMLElementEventMap>(\n target: EventTarget | null | undefined,\n event: K,\n listener: (e: HTMLElementEventMap[K]) => void,\n options?: AddEventListenerOptions,\n ) => void;\n /**\n * Called after the native `close` event fires — after background unlock,\n * isOpen reset, and focus restore. Use to emit component events.\n */\n onNativeClose?: (reason: DialogCloseReason) => void;\n /** Called by the overlay on open. Use to emit events. */\n onOpen?: (reason: OverlayOpenReason) => void;\n /** Open prop from the component — drives programmatic open/close via watch. */\n openProp: Readable<boolean | undefined>;\n /**\n * Called by the overlay when closing. Orchestrates the close animation.\n * Default: `dialog.close()`. Override in dialog.ts to add the CSS exit animation.\n */\n performClose?: (dialog: HTMLDialogElement, reason: DialogCloseReason) => void;\n returnFocus: Readable<boolean | undefined>;\n};\n\nexport type UseDialogHandle = {\n closeWithAnimation: () => void;\n /**\n * Dispatch a cancelable `close-request` event on the host. Returns true when\n * the close is allowed (event not prevented), false when it was cancelled.\n */\n dispatchCloseRequest: (reason: Exclude<DialogCloseReason, 'programmatic'>) => boolean;\n handleBackdropClick: (e: MouseEvent) => void;\n handleCancel: (e: Event) => void;\n isOpen: Signal<boolean>;\n /** The modal dialog controller — open/close/toggle/dispose. */\n overlay: ModalDialogController;\n /**\n * Dispatch `close-request`; if allowed, call `overlay.close()`.\n */\n requestClose: (reason: Exclude<DialogCloseReason, 'programmatic'>) => void;\n /**\n * Registers the open-prop watcher AND standard click/cancel event listeners on\n * the dialog element (backdrop click-to-close, Escape via the native `cancel`\n * event). Use for components that need standard dialog behavior.\n * Call inside `onMounted()`.\n */\n setupNativeListeners: () => void;\n /**\n * Registers only the open-prop watcher (without adding click/cancel listeners).\n * Use for components like drawer that register their own event handlers.\n * Call inside `onMounted()`.\n */\n watchOpenProp: () => void;\n};\n\ntype ModalDialogController = {\n [Symbol.dispose](): void;\n close(reason?: DialogCloseReason): void;\n dispose(): void;\n open(reason?: OverlayOpenReason): void;\n toggle(openReason?: OverlayOpenReason, closeReason?: DialogCloseReason): void;\n};\n\n// ── Factory ───────────────────────────────────────────────────────────────────\n\n/**\n * Shared dialog-control composable for `ore-dialog` and `ore-drawer`.\n *\n * Owns: `isOpen`, close-reason tracking, focus management, `overlay`,\n * `requestClose`, background lock/unlock, and the `close` event handler.\n * Components supply only their unique behavior via callbacks and `onNativeClose`.\n */\nexport function useDialogControl(options: UseDialogOptions): UseDialogHandle {\n const abortSignal = lifecycleSignal(options.onCleanup);\n const isOpen = signal(false);\n // Internal close-reason: set atomically before dialog.close() fires.\n let pendingCloseReason: DialogCloseReason = 'programmatic';\n const bgLock = createBackgroundLock();\n let isClosing = false;\n\n // ── Focus management ──────────────────────────────────────────────────────\n const focus = createFocusManager({\n getInitialFocusSelector: () => options.initialFocus.value,\n getReturnFocus: () => options.returnFocus.value,\n host: options.host,\n signal: abortSignal,\n });\n // ─────────────────────────────────────────────────────────────────────────\n\n const closeWithAnimation = (): void => {\n const dialog = options.dialogRef.value;\n\n if (!dialog?.open || isClosing) return;\n\n isClosing = true;\n dialog.classList.add('closing');\n\n const finish = () => {\n dialog.close();\n // Do NOT remove 'closing' here — removing it while the native ::backdrop is\n // still held in the top layer by the 'overlay allow-discrete' transition\n // would revert its opacity to 1 and cause a visible flash. The class is\n // cleaned up at the start of the next open cycle (see setOpen below).\n isClosing = false;\n };\n\n const panel = options.getPanelEl();\n\n if (panel) {\n awaitExit(panel, finish, 'transition');\n } else {\n finish();\n }\n };\n\n const dispatchCloseRequest = (reason: Exclude<DialogCloseReason, 'programmatic'>): boolean => {\n return options.host.dispatchEvent(\n new CustomEvent('close-request', {\n bubbles: true,\n cancelable: true,\n composed: true,\n detail: { reason, ...options.closeRequestDetail?.(reason) },\n }),\n );\n };\n\n const overlay: ModalDialogController = {\n close(reason: DialogCloseReason = 'programmatic'): void {\n if (!isOpen.value) return;\n\n const dialog = options.dialogRef.value;\n\n if (dialog?.open) {\n pendingCloseReason = reason;\n (options.performClose ?? ((element, _reason) => element.close()))(dialog, reason);\n } else {\n isOpen.value = false;\n }\n },\n dispose(): void {\n overlay.close('programmatic');\n },\n open(reason: OverlayOpenReason = 'programmatic'): void {\n if (isOpen.value) return;\n\n const dialog = options.dialogRef.value;\n\n if (!dialog || dialog.open) return;\n\n // Clean up any leftover 'closing' class from the previous close animation\n // before opening so the entry @starting-style and transition work correctly.\n dialog.classList.remove('closing');\n focus.captureReturnFocus();\n options.beforeOpen?.(dialog);\n dialog.showModal();\n bgLock.lock(options.host);\n focus.applyInitialFocus();\n isOpen.value = true;\n options.onOpen?.(reason);\n },\n [Symbol.dispose](): void {\n overlay.dispose();\n },\n toggle(openReason: OverlayOpenReason = 'click', closeReason: DialogCloseReason = 'trigger'): void {\n if (isOpen.value) overlay.close(closeReason);\n else overlay.open(openReason);\n },\n };\n\n abortSignal.addEventListener('abort', overlay.dispose, { once: true });\n\n const requestClose = (reason: Exclude<DialogCloseReason, 'programmatic'>): void => {\n const allowed = dispatchCloseRequest(reason);\n\n if (!allowed) return;\n\n overlay.close(reason);\n };\n\n // Invoker Commands API — allows declarative open/close from outside the component:\n // <button commandfor=\"ore-dialog-id\" command=\"show-modal\">Open</button>\n // <button commandfor=\"ore-dialog-id\" command=\"close\">Close</button>\n options.host.addEventListener(\n 'command',\n (e: Event) => {\n const cmd = (e as CommandEvent).command;\n\n if (cmd === 'show-modal') overlay.open('trigger');\n else if (cmd === 'close' || cmd === 'request-close') requestClose('trigger');\n },\n { signal: abortSignal },\n );\n\n // The native `<dialog>` element auto-closes on Escape via its own close-watcher, which\n // dispatches a cancelable `cancel` event before `close`. Suppressing/redirecting that is the\n // spec-correct hook — relying on `keydown`'s `preventDefault()` to stop the close-watcher is\n // implementation-specific, not guaranteed across browsers.\n const handleCancel = (e: Event): void => {\n // See `handleNativeClose`'s comment: guard against a same-named bubbling event from a\n // descendant field being misread as this dialog's own native event.\n if (e.target !== options.dialogRef.value) return;\n\n e.preventDefault();\n\n if (options.isPersistent()) return;\n\n requestClose('escape');\n };\n\n const handleBackdropClick = (e: MouseEvent): void => {\n if (options.isPersistent()) return;\n\n if (e.target === options.dialogRef.value) requestClose('outsideClick');\n };\n\n // ── Internal native close handler ─────────────────────────────────────────\n // `e.target` must be checked against the native `<dialog>` itself, the same way\n // `handleBackdropClick` already does: a bubbling `close`-named event fired by *any* descendant\n // component reaches this listener too — regardless of that event's own `composed` flag, since\n // slot-assignment-based event-path computation (a slotted descendant's \"parent\" is the `<slot>`\n // projecting it) isn't gated by `composed` the way crossing back out of a shadow root is. Some\n // fields nested here (`ore-select`, `ore-combobox`, …) emit their own public `close` event when\n // *their* dropdown closes — without this check, selecting an option in one of those closes this\n // dialog too, misreading the field's own event as this dialog's native close.\n const handleNativeClose = (e?: Event): void => {\n if (e && e.target !== options.dialogRef.value) return;\n\n bgLock.unlock();\n options.host.removeAttribute('open');\n isOpen.value = false;\n focus.restoreFocus();\n\n const reason = pendingCloseReason;\n\n pendingCloseReason = 'programmatic';\n options.onNativeClose?.(reason);\n };\n\n const watchOpenProp = (): void => {\n const dialog = options.dialogRef.value;\n\n if (dialog) options.onEvent(dialog, 'close', handleNativeClose);\n\n let initialized = false;\n\n watch(\n options.openProp,\n (open) => {\n if (open === undefined) {\n if (!initialized) {\n if (options.defaultOpen.value) overlay.open('programmatic');\n\n initialized = true;\n\n return;\n }\n\n overlay.close('programmatic');\n\n return;\n }\n\n initialized = true;\n\n if (open) {\n overlay.open('programmatic');\n } else {\n overlay.close('programmatic');\n }\n },\n { immediate: true },\n );\n };\n\n const setupNativeListeners = (): void => {\n const dialog = options.dialogRef.value;\n\n if (!dialog) return;\n\n watchOpenProp();\n options.onEvent(dialog, 'click', handleBackdropClick);\n options.onEvent(dialog, 'cancel', handleCancel);\n };\n\n return {\n closeWithAnimation,\n dispatchCloseRequest,\n handleBackdropClick,\n handleCancel,\n isOpen,\n overlay,\n requestClose,\n setupNativeListeners,\n watchOpenProp,\n };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"use-dialog.js","names":[],"sources":["../../../src/overlay/shared/use-dialog.ts"],"sourcesContent":["import { type Readable, type Signal, signal, watch } from '@vielzeug/ripple';\n\nimport { createFocusManager, type DialogCloseReason, lifecycleSignal, type OverlayOpenReason } from '../../core';\nimport { awaitExit } from './await-exit';\nimport { createBackgroundLock } from './background-lock';\n\n// Invoker Commands API — not yet in lib.dom.d.ts\ninterface CommandEvent extends Event {\n command: string;\n}\n\n// ── Types ─────────────────────────────────────────────────────────────────────\n\nexport type UseDialogOptions = {\n /** Called after showModal() but before isOpen = true. Use to clear drag styles etc. */\n beforeOpen?: (dialog: HTMLDialogElement) => void;\n /** Extra properties merged into the `close-request` custom event detail. */\n closeRequestDetail?: (reason: Exclude<DialogCloseReason, 'programmatic'>) => Record<string, unknown>;\n /** Initial uncontrolled open state. Ignored when `openProp` is defined. */\n defaultOpen: Readable<boolean | undefined>;\n dialogRef: { value: HTMLDialogElement | null | undefined };\n getPanelEl: () => HTMLElement | null | undefined;\n host: HTMLElement;\n initialFocus: Readable<string | undefined>;\n isPersistent: () => boolean;\n /** Cleanup registrar from the component setup ctx. Automatically called on disconnect. */\n onCleanup: (fn: () => void) => void;\n /**\n * Scoped event listener registrar from the component setup ctx.\n * Automatically removed on component disconnect.\n */\n onEvent: <K extends keyof HTMLElementEventMap>(\n target: EventTarget | null | undefined,\n event: K,\n listener: (e: HTMLElementEventMap[K]) => void,\n options?: AddEventListenerOptions,\n ) => void;\n /**\n * Called after the native `close` event fires — after background unlock,\n * isOpen reset, and focus restore. Use to emit component events.\n */\n onNativeClose?: (reason: DialogCloseReason) => void;\n /** Called by the overlay on open. Use to emit events. */\n onOpen?: (reason: OverlayOpenReason) => void;\n /** Open prop from the component — drives programmatic open/close via watch. */\n openProp: Readable<boolean | undefined>;\n /**\n * Called by the overlay when closing. Orchestrates the close animation.\n * Default: `dialog.close()`. Override in dialog.ts to add the CSS exit animation.\n */\n performClose?: (dialog: HTMLDialogElement, reason: DialogCloseReason) => void;\n returnFocus: Readable<boolean | undefined>;\n};\n\nexport type UseDialogHandle = {\n closeWithAnimation: () => void;\n /**\n * Dispatch a cancelable `close-request` event on the host. Returns true when\n * the close is allowed (event not prevented), false when it was cancelled.\n */\n dispatchCloseRequest: (reason: Exclude<DialogCloseReason, 'programmatic'>) => boolean;\n handleBackdropClick: (e: MouseEvent) => void;\n handleCancel: (e: Event) => void;\n isOpen: Signal<boolean>;\n /** The modal dialog controller — open/close/toggle/dispose. */\n overlay: ModalDialogController;\n /**\n * Dispatch `close-request`; if allowed, call `overlay.close()`.\n */\n requestClose: (reason: Exclude<DialogCloseReason, 'programmatic'>) => void;\n /**\n * Registers the open-prop watcher AND standard click/cancel event listeners on\n * the dialog element (backdrop click-to-close, Escape via the native `cancel`\n * event). Use for components that need standard dialog behavior.\n * Call inside `onMounted()`.\n */\n setupNativeListeners: () => void;\n /**\n * Registers only the open-prop watcher (without adding click/cancel listeners).\n * Use for components like drawer that register their own event handlers.\n * Call inside `onMounted()`.\n */\n watchOpenProp: () => void;\n};\n\ntype ModalDialogController = {\n [Symbol.dispose](): void;\n close(reason?: DialogCloseReason): void;\n dispose(): void;\n open(reason?: OverlayOpenReason): void;\n toggle(openReason?: OverlayOpenReason, closeReason?: DialogCloseReason): void;\n};\n\n// ── Factory ───────────────────────────────────────────────────────────────────\n\n/**\n * Shared dialog-control composable for `ore-dialog` and `ore-drawer`.\n *\n * Owns: `isOpen`, close-reason tracking, focus management, `overlay`,\n * `requestClose`, background lock/unlock, and the `close` event handler.\n * Components supply only their unique behavior via callbacks and `onNativeClose`.\n */\nexport function useDialogControl(options: UseDialogOptions): UseDialogHandle {\n const abortSignal = lifecycleSignal(options.onCleanup);\n const isOpen = signal(false);\n // Internal close-reason: set atomically before dialog.close() fires.\n let pendingCloseReason: DialogCloseReason = 'programmatic';\n const bgLock = createBackgroundLock();\n let isClosing = false;\n\n // ── Focus management ──────────────────────────────────────────────────────\n const focus = createFocusManager({\n getInitialFocusSelector: () => options.initialFocus.value,\n getReturnFocus: () => options.returnFocus.value,\n host: options.host,\n signal: abortSignal,\n });\n // ─────────────────────────────────────────────────────────────────────────\n\n const closeWithAnimation = (): void => {\n const dialog = options.dialogRef.value;\n\n if (!dialog?.open || isClosing) return;\n\n isClosing = true;\n dialog.classList.add('closing');\n\n const finish = () => {\n dialog.close();\n // Do NOT remove 'closing' here — removing it while the native ::backdrop is\n // still held in the top layer by the 'overlay allow-discrete' transition\n // would revert its opacity to 1 and cause a visible flash. The class is\n // cleaned up at the start of the next open cycle (see setOpen below).\n isClosing = false;\n };\n\n const panel = options.getPanelEl();\n\n if (panel) {\n awaitExit(panel, finish, 'transition');\n } else {\n finish();\n }\n };\n\n const dispatchCloseRequest = (reason: Exclude<DialogCloseReason, 'programmatic'>): boolean => {\n return options.host.dispatchEvent(\n new CustomEvent('close-request', {\n bubbles: true,\n cancelable: true,\n composed: true,\n detail: { reason, ...options.closeRequestDetail?.(reason) },\n }),\n );\n };\n\n const overlay: ModalDialogController = {\n close(reason: DialogCloseReason = 'programmatic'): void {\n if (!isOpen.value) return;\n\n const dialog = options.dialogRef.value;\n\n if (dialog?.open) {\n pendingCloseReason = reason;\n (options.performClose ?? ((element, _reason) => element.close()))(dialog, reason);\n } else {\n isOpen.value = false;\n }\n },\n dispose(): void {\n overlay.close('programmatic');\n },\n open(reason: OverlayOpenReason = 'programmatic'): void {\n if (isOpen.value) return;\n\n const dialog = options.dialogRef.value;\n\n if (!dialog || dialog.open) return;\n\n // Clean up any leftover 'closing' class from the previous close animation\n // before opening so the entry @starting-style and transition work correctly.\n dialog.classList.remove('closing');\n focus.captureReturnFocus();\n options.beforeOpen?.(dialog);\n dialog.showModal();\n bgLock.lock(options.host);\n focus.applyInitialFocus();\n isOpen.value = true;\n options.onOpen?.(reason);\n },\n [Symbol.dispose](): void {\n overlay.dispose();\n },\n toggle(openReason: OverlayOpenReason = 'click', closeReason: DialogCloseReason = 'trigger'): void {\n if (isOpen.value) overlay.close(closeReason);\n else overlay.open(openReason);\n },\n };\n\n abortSignal.addEventListener('abort', overlay.dispose, { once: true });\n\n const requestClose = (reason: Exclude<DialogCloseReason, 'programmatic'>): void => {\n const allowed = dispatchCloseRequest(reason);\n\n if (!allowed) return;\n\n overlay.close(reason);\n };\n\n // Invoker Commands API — allows declarative open/close from outside the component:\n // <button commandfor=\"ore-dialog-id\" command=\"show-modal\">Open</button>\n // <button commandfor=\"ore-dialog-id\" command=\"close\">Close</button>\n options.host.addEventListener(\n 'command',\n (e: Event) => {\n const cmd = (e as CommandEvent).command;\n\n if (cmd === 'show-modal') overlay.open('trigger');\n else if (cmd === 'close' || cmd === 'request-close') requestClose('trigger');\n },\n { signal: abortSignal },\n );\n\n // The native `<dialog>` element auto-closes on Escape via its own close-watcher, which\n // dispatches a cancelable `cancel` event before `close`. Suppressing/redirecting that is the\n // spec-correct hook — relying on `keydown`'s `preventDefault()` to stop the close-watcher is\n // implementation-specific, not guaranteed across browsers.\n const handleCancel = (e: Event): void => {\n // See `handleNativeClose`'s comment: guard against a same-named bubbling event from a\n // descendant field being misread as this dialog's own native event.\n if (e.target !== options.dialogRef.value) return;\n\n e.preventDefault();\n\n if (options.isPersistent()) return;\n\n requestClose('escape');\n };\n\n const handleBackdropClick = (e: MouseEvent): void => {\n if (options.isPersistent()) return;\n\n if (e.target === options.dialogRef.value) requestClose('outsideClick');\n };\n\n // ── Internal native close handler ─────────────────────────────────────────\n // `e.target` must be checked against the native `<dialog>` itself, the same way\n // `handleBackdropClick` already does: a bubbling `close`-named event fired by *any* descendant\n // component reaches this listener too — regardless of that event's own `composed` flag, since\n // slot-assignment-based event-path computation (a slotted descendant's \"parent\" is the `<slot>`\n // projecting it) isn't gated by `composed` the way crossing back out of a shadow root is. Some\n // fields nested here (`ore-select`, `ore-combobox`, …) emit their own public `close` event when\n // *their* dropdown closes — without this check, selecting an option in one of those closes this\n // dialog too, misreading the field's own event as this dialog's native close.\n const handleNativeClose = (e?: Event): void => {\n if (e && e.target !== options.dialogRef.value) return;\n\n bgLock.unlock();\n options.host.removeAttribute('open');\n isOpen.value = false;\n focus.restoreFocus();\n\n const reason = pendingCloseReason;\n\n pendingCloseReason = 'programmatic';\n options.onNativeClose?.(reason);\n };\n\n const watchOpenProp = (): void => {\n const dialog = options.dialogRef.value;\n\n if (dialog) options.onEvent(dialog, 'close', handleNativeClose);\n\n let initialized = false;\n\n watch(\n options.openProp,\n (open) => {\n if (open === undefined) {\n if (!initialized) {\n if (options.defaultOpen.value) overlay.open('programmatic');\n\n initialized = true;\n\n return;\n }\n\n overlay.close('programmatic');\n\n return;\n }\n\n initialized = true;\n\n if (open) {\n overlay.open('programmatic');\n } else {\n overlay.close('programmatic');\n }\n },\n { immediate: true },\n );\n };\n\n const setupNativeListeners = (): void => {\n const dialog = options.dialogRef.value;\n\n if (!dialog) return;\n\n watchOpenProp();\n options.onEvent(dialog, 'click', handleBackdropClick);\n options.onEvent(dialog, 'cancel', handleCancel);\n };\n\n return {\n closeWithAnimation,\n dispatchCloseRequest,\n handleBackdropClick,\n handleCancel,\n isOpen,\n overlay,\n requestClose,\n setupNativeListeners,\n watchOpenProp,\n };\n}\n"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- const e=require("../../core/ore.cjs"),t=require("../../core/overlay.cjs");let n=require("@vielzeug/ripple"),r=require("@vielzeug/orbit");var i=i=>{let{bindTriggerAria:a,defaultOpen:o,disabled:s,getPanel:c,onClose:l,onOpen:u,onPlacementChange:d,openProp:f,placement:p,slot:m,slotElements:h,triggers:g}=i,_=()=>typeof m==`function`?m():m,v=()=>{let e=i.offset;return e==null?8:(typeof e==`object`?e.value:e)??8},y=e.lifecycleSignal(i.onCleanup),b=(0,n.signal)(!1),x=()=>f.value!==void 0,S=null,C=null;function w(){let e=c(),t=S??_()?.assignedElements({flatten:!0})[0];if(!e||!t)return;let n=e.style.transition,i=e.style.transform;e.style.transition=`none`,e.style.transform=`none`;let a=(0,r.computePosition)(t,e,{middleware:[(0,r.offset)(v()),(0,r.flip)(),(0,r.shift)({padding:8})],placement:p.value});e.style.left=`${a.x}px`,e.style.top=`${a.y}px`,e.style.transition=n,e.style.transform=i,e.dataset.placement=a.placement,d?.(a.placement)}function T(){b.value=!0;let e=c();if(e&&(e.dataset.open=``,`showPopover`in e))try{e.matches(`:popover-open`)||e.showPopover()}catch{}w()}function E(){b.value=!1;let e=c();if(e&&(delete e.dataset.open,`hidePopover`in e))try{e.matches(`:popover-open`)&&e.hidePopover()}catch{}}function D(e=`programmatic`){x()||s.value||b.value||(T(),u?.(e))}function O(e=`trigger`){x()||!b.value||(E(),l?.(e))}function k(){x()||(b.value?O():D(`click`))}function A(e){e.key===`Escape`&&O(`escape`)}function j(e){let t=e.relatedTarget,n=c();t&&n?.contains(t)||t&&S?.contains(t)||O(`trigger`)}let M=()=>{C?.(),C=null;let e=_()?.assignedElements({flatten:!0})[0];if(!e){S=null;return}S=e;let t=a(e),n=[],r=(e,t,r,i)=>{e.addEventListener(t,r,i),n.push(()=>e.removeEventListener(t,r,i))},i=g.value;if(i.includes(`click`)&&r(e,`click`,k),i.includes(`hover`)){r(e,`pointerenter`,()=>D(`hover`)),r(e,`pointerleave`,()=>O(`trigger`));let t=c();t&&(r(t,`pointerenter`,()=>D(`hover`)),r(t,`pointerleave`,()=>O(`trigger`)))}if(i.includes(`focus`)){r(e,`focusin`,()=>D(`focus`)),r(e,`focusout`,j);let t=c();t&&r(t,`focusout`,j)}r(document,`keydown`,A),C=()=>{t();for(let e of n)e();S=null}},N=t.createOutsidePointerDismissal({getTargets:()=>[S,c(),i.getHost?.()],isActive:()=>b.value&&g.value.includes(`click`),onDismiss:()=>O(`outsideClick`),signal:y});return{close:O,mount:()=>{let e=!1,t=_(),r=()=>{queueMicrotask(()=>{y.aborted||M()})};return t?.addEventListener(`slotchange`,r),r(),(0,n.watch)(h,M,{immediate:!0}),(0,n.watch)(f,t=>{if(t==null){e&&b.value&&(E(),l?.(`programmatic`)),e=!0;return}e=!0,t?(T(),u?.(`programmatic`)):b.value&&(E(),l?.(`programmatic`))},{immediate:!0}),f.value===void 0&&o.value&&(T(),u?.(`programmatic`)),(0,n.watch)(s,e=>{e&&O(`programmatic`)}),()=>{t?.removeEventListener(`slotchange`,r),C?.(),C=null;let e=c();if(e&&(delete e.dataset.open,`hidePopover`in e))try{e.matches(`:popover-open`)&&e.hidePopover()}catch{}N()}},open:D,toggle:k,updatePosition:w,visible:b}};exports.useFloatingTrigger=i;
1
+ const e=require("../../core/ore.cjs"),t=require("../../core/overlay.cjs");let n=require("@vielzeug/ripple"),r=require("@vielzeug/orbit");var i=i=>{let{bindTriggerAria:a,defaultOpen:o,disabled:s,getPanel:c,onClose:l,onOpen:u,onPlacementChange:d,openProp:f,placement:p,slot:m,slotElements:h,triggers:g}=i,_=()=>typeof m==`function`?m():m,v=()=>{let e=i.offset;return e==null?8:(typeof e==`object`?e.value:e)??8},y=e.lifecycleSignal(i.onCleanup),b=(0,n.signal)(!1),x=()=>f.value!==void 0,S=null,C=null;function w(){let e=c(),t=S??_()?.assignedElements({flatten:!0})[0];if(!e||!t)return;let n=e.style.transition,i=e.style.transform;e.style.transition=`none`,e.style.transform=`none`;let a=(0,r.computePosition)(t,e,{middleware:[(0,r.offset)(v()),(0,r.flip)(),(0,r.shift)({padding:8})],placement:p.value});e.style.left=`${a.x}px`,e.style.top=`${a.y}px`,e.style.transition=n,e.style.transform=i,e.dataset.placement=a.placement,d?.(a.placement)}function T(){b.value=!0;let e=c();if(e&&(e.dataset.open=``,`showPopover`in e))try{e.matches(`:popover-open`)||e.showPopover()}catch{}w()}function E(){b.value=!1;let e=c();if(e&&(delete e.dataset.open,`hidePopover`in e))try{e.matches(`:popover-open`)&&e.hidePopover()}catch{}}function D(e=`programmatic`){x()||s.value||b.value||(T(),u?.(e))}function O(e=`trigger`){!x()&&b.value&&(E(),l?.(e))}function k(){x()||(b.value?O():D(`click`))}function A(e){e.key===`Escape`&&O(`escape`)}function j(e){let t=e.relatedTarget,n=c();t&&n?.contains(t)||t&&S?.contains(t)||O(`trigger`)}let M=()=>{C?.(),C=null;let e=_()?.assignedElements({flatten:!0})[0];if(!e){S=null;return}S=e;let t=a(e),n=[],r=(e,t,r,i)=>{e.addEventListener(t,r,i),n.push(()=>e.removeEventListener(t,r,i))},i=g.value;if(i.includes(`click`)&&r(e,`click`,k),i.includes(`hover`)){r(e,`pointerenter`,()=>D(`hover`)),r(e,`pointerleave`,()=>O(`trigger`));let t=c();t&&(r(t,`pointerenter`,()=>D(`hover`)),r(t,`pointerleave`,()=>O(`trigger`)))}if(i.includes(`focus`)){r(e,`focusin`,()=>D(`focus`)),r(e,`focusout`,j);let t=c();t&&r(t,`focusout`,j)}r(document,`keydown`,A),C=()=>{t();for(let e of n)e();S=null}},N=t.createOutsidePointerDismissal({getTargets:()=>[S,c(),i.getHost?.()],isActive:()=>b.value&&g.value.includes(`click`),onDismiss:()=>O(`outsideClick`),signal:y});return{close:O,mount:()=>{let e=!1,t=_(),r=()=>{queueMicrotask(()=>{y.aborted||M()})};return t?.addEventListener(`slotchange`,r),r(),(0,n.watch)(h,M,{immediate:!0}),(0,n.watch)(f,t=>{if(t==null){e&&b.value&&(E(),l?.(`programmatic`)),e=!0;return}e=!0,t?(T(),u?.(`programmatic`)):b.value&&(E(),l?.(`programmatic`))},{immediate:!0}),f.value===void 0&&o.value&&(T(),u?.(`programmatic`)),(0,n.watch)(s,e=>{e&&O(`programmatic`)}),()=>{t?.removeEventListener(`slotchange`,r),C?.(),C=null;let e=c();if(e&&(delete e.dataset.open,`hidePopover`in e))try{e.matches(`:popover-open`)&&e.hidePopover()}catch{}N()}},open:D,toggle:k,updatePosition:w,visible:b}};exports.useFloatingTrigger=i;
2
2
  //# sourceMappingURL=use-floating-trigger.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"use-floating-trigger.cjs","names":[],"sources":["../../../src/overlay/shared/use-floating-trigger.ts"],"sourcesContent":["import type { Placement } from '@vielzeug/orbit';\n\nimport { computePosition, flip, offset, shift } from '@vielzeug/orbit';\nimport { type Readable, type Signal, signal, watch } from '@vielzeug/ripple';\n\nimport {\n createOutsidePointerDismissal,\n type DialogCloseReason,\n lifecycleSignal,\n type OverlayOpenReason,\n} from '../../core';\n\n// ── Types ─────────────────────────────────────────────────────────────────────\n\nexport type FloatingTriggerType = 'click' | 'focus' | 'hover';\n\nexport type AriaBindFn = (triggerEl: HTMLElement) => () => void;\n\nexport type FloatingTriggerOptions = {\n /** ARIA binding factory: called with the trigger element, returns a cleanup. */\n bindTriggerAria: AriaBindFn;\n /** Initial uncontrolled visibility. Ignored when `openProp` is defined. */\n defaultOpen: Readable<boolean | undefined>;\n /** If true, disable all trigger interactions and close if open. */\n disabled: Readable<boolean>;\n /** Returns the host element (e.g. ore-popover) so slotted content clicks aren't treated as outside clicks. */\n getHost?: () => Element | null;\n /** Returns the panel element, if mounted. */\n getPanel: () => HTMLElement | null;\n /** Gap from reference to floating panel in px. Default: 8. Accepts a signal for runtime reactivity. */\n offset?: Readable<number | undefined> | number;\n /** Cleanup registrar from the component setup ctx. Automatically called on disconnect. */\n onCleanup: (fn: () => void) => void;\n /** Called when the popover closes. */\n onClose?: (reason: DialogCloseReason) => void;\n /** Called when the popover opens. */\n onOpen?: (reason: OverlayOpenReason) => void;\n /** Callback when resolved placement changes (useful for CSS attribute). */\n onPlacementChange?: (placement: Placement) => void;\n /** Controlled open prop. When defined, disables uncontrolled logic. */\n openProp: Readable<boolean | undefined>;\n /** Preferred placement. Default: 'bottom'. */\n placement: Readable<Placement>;\n /** Slot element or a factory to lazily find it. Resolved each time bindEvents runs. */\n slot: HTMLSlotElement | null | (() => HTMLSlotElement | null);\n /** Slot elements signal — used to rebind when slotted elements change. */\n slotElements: Readable<Element[]>;\n /** Which triggers are active. Set to empty array or omit if handling events manually. */\n triggers: Readable<FloatingTriggerType[]>;\n};\n\nexport type FloatingTriggerHandle = {\n /** Closes the panel. No-op if controlled. */\n close: (reason?: DialogCloseReason) => void;\n /**\n * Call inside `onMounted`. Sets up slot + trigger event watchers\n * and returns a cleanup function to pass back to the framework.\n */\n mount: () => () => void;\n /** Opens the panel. No-op if controlled. */\n open: (reason?: OverlayOpenReason) => void;\n /** Toggles the panel. No-op if controlled. */\n toggle: () => void;\n /** Manually trigger a position recalculation. */\n updatePosition: () => void;\n /** Whether the panel is currently visible. */\n visible: Signal<boolean>;\n};\n\n// ── Implementation ─────────────────────────────────────────────────────────────\n\n/**\n * Shared logic for floating-panel triggers (tooltip, popover).\n *\n * Handles:\n * - Slot-based trigger element detection and rebinding\n * - Event binding per trigger type (hover / focus / click)\n * - Floating-element positioning via `@vielzeug/orbit`\n * - Controlled mode via `openProp` watcher\n * - Keyboard escape dismissal\n *\n * ARIA binding is intentionally delegated to the caller via `bindTriggerAria`\n * so that tooltip (`describedby`) and popover (`expanded`/`controls`/…) can diverge.\n */\nexport const useFloatingTrigger = (options: FloatingTriggerOptions): FloatingTriggerHandle => {\n const {\n bindTriggerAria,\n defaultOpen,\n disabled,\n getPanel,\n onClose,\n onOpen,\n onPlacementChange,\n openProp,\n placement,\n slot,\n slotElements,\n triggers,\n } = options;\n\n const resolveSlot = (): HTMLSlotElement | null => (typeof slot === 'function' ? slot() : slot);\n const getOffset = (): number => {\n const o = options.offset;\n\n if (o == null) return 8;\n\n const val = typeof o === 'object' ? (o as Readable<number | undefined>).value : o;\n\n return val ?? 8;\n };\n const abortSignal = lifecycleSignal(options.onCleanup);\n const visible = signal(false);\n const isControlled = () => openProp.value !== undefined;\n let currentTrigger: HTMLElement | null = null;\n let triggerBinding: (() => void) | null = null;\n\n function updatePosition(): void {\n const panel = getPanel();\n const trigger =\n currentTrigger ?? (resolveSlot()?.assignedElements({ flatten: true })[0] as HTMLElement | undefined);\n\n if (!panel || !trigger) return;\n\n const previousTransition = panel.style.transition;\n const previousTransform = panel.style.transform;\n\n panel.style.transition = 'none';\n panel.style.transform = 'none';\n\n const result = computePosition(trigger, panel, {\n middleware: [offset(getOffset()), flip(), shift({ padding: 8 })],\n placement: placement.value,\n });\n\n panel.style.left = `${result.x}px`;\n panel.style.top = `${result.y}px`;\n panel.style.transition = previousTransition;\n panel.style.transform = previousTransform;\n panel.dataset.placement = result.placement;\n\n onPlacementChange?.(result.placement as Placement);\n }\n\n function showFloat(): void {\n visible.value = true;\n\n const panel = getPanel();\n\n if (panel) {\n panel.dataset.open = '';\n\n if ('showPopover' in panel) {\n try {\n if (!panel.matches(':popover-open')) panel.showPopover();\n } catch {\n // Popover API restricted (e.g. sandboxed iframe); data-open CSS fallback handles visibility.\n }\n }\n }\n\n updatePosition();\n }\n\n function hideFloat(): void {\n visible.value = false;\n\n const panel = getPanel();\n\n if (panel) {\n delete panel.dataset.open;\n\n if ('hidePopover' in panel) {\n try {\n if (panel.matches(':popover-open')) panel.hidePopover();\n } catch {\n // Popover API restricted; data-open removal handles visibility.\n }\n }\n }\n }\n\n function open(reason: OverlayOpenReason = 'programmatic'): void {\n if (isControlled() || disabled.value || visible.value) return;\n\n showFloat();\n onOpen?.(reason);\n }\n\n function close(reason: DialogCloseReason = 'trigger'): void {\n if (isControlled() || !visible.value) return;\n\n hideFloat();\n onClose?.(reason);\n }\n\n function toggle(): void {\n if (isControlled()) return;\n\n if (visible.value) close();\n else open('click');\n }\n\n function handleKeydown(e: KeyboardEvent): void {\n if (e.key === 'Escape') close('escape');\n }\n\n function handleFocusOut(e: FocusEvent): void {\n const next = e.relatedTarget as Element | null;\n const panel = getPanel();\n\n if (next && panel?.contains(next)) return;\n\n if (next && currentTrigger?.contains(next)) return;\n\n close('trigger');\n }\n\n const bindEvents = (): void => {\n triggerBinding?.();\n triggerBinding = null;\n\n const triggerEl = resolveSlot()?.assignedElements({ flatten: true })[0] as HTMLElement | undefined;\n\n if (!triggerEl) {\n currentTrigger = null;\n\n return;\n }\n\n currentTrigger = triggerEl;\n\n const removeAria = bindTriggerAria(triggerEl);\n const cleanups: Array<() => void> = [];\n\n const add = (\n target: EventTarget,\n event: string,\n listener: EventListener,\n eventOptions?: AddEventListenerOptions,\n ): void => {\n target.addEventListener(event, listener, eventOptions);\n cleanups.push(() => target.removeEventListener(event, listener, eventOptions));\n };\n\n const t = triggers.value;\n\n if (t.includes('click')) {\n add(triggerEl, 'click', toggle as EventListener);\n }\n\n if (t.includes('hover')) {\n add(triggerEl, 'pointerenter', () => open('hover'));\n add(triggerEl, 'pointerleave', () => close('trigger'));\n\n const panelEl = getPanel();\n\n if (panelEl) {\n add(panelEl, 'pointerenter', () => open('hover'));\n add(panelEl, 'pointerleave', () => close('trigger'));\n }\n }\n\n if (t.includes('focus')) {\n add(triggerEl, 'focusin', () => open('focus'));\n add(triggerEl, 'focusout', handleFocusOut as EventListener);\n\n const panelEl = getPanel();\n\n if (panelEl) add(panelEl, 'focusout', handleFocusOut as EventListener);\n }\n\n add(document, 'keydown', handleKeydown as EventListener);\n\n triggerBinding = () => {\n removeAria();\n\n for (const cleanup of cleanups) cleanup();\n\n currentTrigger = null;\n };\n };\n\n const stopOutsidePointerDismissal = createOutsidePointerDismissal({\n getTargets: () => [currentTrigger, getPanel(), options.getHost?.()],\n isActive: () => visible.value && triggers.value.includes('click'),\n onDismiss: () => close('outsideClick'),\n signal: abortSignal,\n });\n\n const mount = (): (() => void) => {\n let initializedOpenProp = false;\n const triggerSlot = resolveSlot();\n const rebind = (): void => {\n queueMicrotask(() => {\n if (!abortSignal.aborted) bindEvents();\n });\n };\n\n triggerSlot?.addEventListener('slotchange', rebind);\n rebind();\n watch(slotElements, bindEvents, { immediate: true });\n\n watch(\n openProp,\n (openVal) => {\n if (openVal === undefined || openVal === null) {\n if (initializedOpenProp && visible.value) {\n hideFloat();\n onClose?.('programmatic');\n }\n\n initializedOpenProp = true;\n\n return;\n }\n\n initializedOpenProp = true;\n\n if (openVal) {\n showFloat();\n onOpen?.('programmatic');\n } else if (visible.value) {\n hideFloat();\n onClose?.('programmatic');\n }\n },\n { immediate: true },\n );\n\n if (openProp.value === undefined && defaultOpen.value) {\n showFloat();\n onOpen?.('programmatic');\n }\n\n watch(disabled, (isNowDisabled) => {\n if (isNowDisabled) close('programmatic');\n });\n\n return () => {\n triggerSlot?.removeEventListener('slotchange', rebind);\n triggerBinding?.();\n triggerBinding = null;\n\n const panel = getPanel();\n\n if (panel) {\n delete panel.dataset.open;\n\n if ('hidePopover' in panel) {\n try {\n if (panel.matches(':popover-open')) panel.hidePopover();\n } catch {\n // Popover API restricted; data-open removal handles visibility.\n }\n }\n }\n\n stopOutsidePointerDismissal();\n };\n };\n\n return { close, mount, open, toggle, updatePosition, visible };\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"use-floating-trigger.cjs","names":[],"sources":["../../../src/overlay/shared/use-floating-trigger.ts"],"sourcesContent":["import type { Placement } from '@vielzeug/orbit';\n\nimport { computePosition, flip, offset, shift } from '@vielzeug/orbit';\nimport { type Readable, type Signal, signal, watch } from '@vielzeug/ripple';\n\nimport {\n createOutsidePointerDismissal,\n type DialogCloseReason,\n lifecycleSignal,\n type OverlayOpenReason,\n} from '../../core';\n\n// ── Types ─────────────────────────────────────────────────────────────────────\n\nexport type FloatingTriggerType = 'click' | 'focus' | 'hover';\n\nexport type AriaBindFn = (triggerEl: HTMLElement) => () => void;\n\nexport type FloatingTriggerOptions = {\n /** ARIA binding factory: called with the trigger element, returns a cleanup. */\n bindTriggerAria: AriaBindFn;\n /** Initial uncontrolled visibility. Ignored when `openProp` is defined. */\n defaultOpen: Readable<boolean | undefined>;\n /** If true, disable all trigger interactions and close if open. */\n disabled: Readable<boolean>;\n /** Returns the host element (e.g. ore-popover) so slotted content clicks aren't treated as outside clicks. */\n getHost?: () => Element | null;\n /** Returns the panel element, if mounted. */\n getPanel: () => HTMLElement | null;\n /** Gap from reference to floating panel in px. Default: 8. Accepts a signal for runtime reactivity. */\n offset?: Readable<number | undefined> | number;\n /** Cleanup registrar from the component setup ctx. Automatically called on disconnect. */\n onCleanup: (fn: () => void) => void;\n /** Called when the popover closes. */\n onClose?: (reason: DialogCloseReason) => void;\n /** Called when the popover opens. */\n onOpen?: (reason: OverlayOpenReason) => void;\n /** Callback when resolved placement changes (useful for CSS attribute). */\n onPlacementChange?: (placement: Placement) => void;\n /** Controlled open prop. When defined, disables uncontrolled logic. */\n openProp: Readable<boolean | undefined>;\n /** Preferred placement. Default: 'bottom'. */\n placement: Readable<Placement>;\n /** Slot element or a factory to lazily find it. Resolved each time bindEvents runs. */\n slot: HTMLSlotElement | null | (() => HTMLSlotElement | null);\n /** Slot elements signal — used to rebind when slotted elements change. */\n slotElements: Readable<Element[]>;\n /** Which triggers are active. Set to empty array or omit if handling events manually. */\n triggers: Readable<FloatingTriggerType[]>;\n};\n\nexport type FloatingTriggerHandle = {\n /** Closes the panel. No-op if controlled. */\n close: (reason?: DialogCloseReason) => void;\n /**\n * Call inside `onMounted`. Sets up slot + trigger event watchers\n * and returns a cleanup function to pass back to the framework.\n */\n mount: () => () => void;\n /** Opens the panel. No-op if controlled. */\n open: (reason?: OverlayOpenReason) => void;\n /** Toggles the panel. No-op if controlled. */\n toggle: () => void;\n /** Manually trigger a position recalculation. */\n updatePosition: () => void;\n /** Whether the panel is currently visible. */\n visible: Signal<boolean>;\n};\n\n// ── Implementation ─────────────────────────────────────────────────────────────\n\n/**\n * Shared logic for floating-panel triggers (tooltip, popover).\n *\n * Handles:\n * - Slot-based trigger element detection and rebinding\n * - Event binding per trigger type (hover / focus / click)\n * - Floating-element positioning via `@vielzeug/orbit`\n * - Controlled mode via `openProp` watcher\n * - Keyboard escape dismissal\n *\n * ARIA binding is intentionally delegated to the caller via `bindTriggerAria`\n * so that tooltip (`describedby`) and popover (`expanded`/`controls`/…) can diverge.\n */\nexport const useFloatingTrigger = (options: FloatingTriggerOptions): FloatingTriggerHandle => {\n const {\n bindTriggerAria,\n defaultOpen,\n disabled,\n getPanel,\n onClose,\n onOpen,\n onPlacementChange,\n openProp,\n placement,\n slot,\n slotElements,\n triggers,\n } = options;\n\n const resolveSlot = (): HTMLSlotElement | null => (typeof slot === 'function' ? slot() : slot);\n const getOffset = (): number => {\n const o = options.offset;\n\n if (o == null) return 8;\n\n const val = typeof o === 'object' ? (o as Readable<number | undefined>).value : o;\n\n return val ?? 8;\n };\n const abortSignal = lifecycleSignal(options.onCleanup);\n const visible = signal(false);\n const isControlled = () => openProp.value !== undefined;\n let currentTrigger: HTMLElement | null = null;\n let triggerBinding: (() => void) | null = null;\n\n function updatePosition(): void {\n const panel = getPanel();\n const trigger =\n currentTrigger ?? (resolveSlot()?.assignedElements({ flatten: true })[0] as HTMLElement | undefined);\n\n if (!panel || !trigger) return;\n\n const previousTransition = panel.style.transition;\n const previousTransform = panel.style.transform;\n\n panel.style.transition = 'none';\n panel.style.transform = 'none';\n\n const result = computePosition(trigger, panel, {\n middleware: [offset(getOffset()), flip(), shift({ padding: 8 })],\n placement: placement.value,\n });\n\n panel.style.left = `${result.x}px`;\n panel.style.top = `${result.y}px`;\n panel.style.transition = previousTransition;\n panel.style.transform = previousTransform;\n panel.dataset.placement = result.placement;\n\n onPlacementChange?.(result.placement as Placement);\n }\n\n function showFloat(): void {\n visible.value = true;\n\n const panel = getPanel();\n\n if (panel) {\n panel.dataset.open = '';\n\n if ('showPopover' in panel) {\n try {\n if (!panel.matches(':popover-open')) panel.showPopover();\n } catch {\n // Popover API restricted (e.g. sandboxed iframe); data-open CSS fallback handles visibility.\n }\n }\n }\n\n updatePosition();\n }\n\n function hideFloat(): void {\n visible.value = false;\n\n const panel = getPanel();\n\n if (panel) {\n delete panel.dataset.open;\n\n if ('hidePopover' in panel) {\n try {\n if (panel.matches(':popover-open')) panel.hidePopover();\n } catch {\n // Popover API restricted; data-open removal handles visibility.\n }\n }\n }\n }\n\n function open(reason: OverlayOpenReason = 'programmatic'): void {\n if (isControlled() || disabled.value || visible.value) return;\n\n showFloat();\n onOpen?.(reason);\n }\n\n function close(reason: DialogCloseReason = 'trigger'): void {\n if (isControlled() || !visible.value) return;\n\n hideFloat();\n onClose?.(reason);\n }\n\n function toggle(): void {\n if (isControlled()) return;\n\n if (visible.value) close();\n else open('click');\n }\n\n function handleKeydown(e: KeyboardEvent): void {\n if (e.key === 'Escape') close('escape');\n }\n\n function handleFocusOut(e: FocusEvent): void {\n const next = e.relatedTarget as Element | null;\n const panel = getPanel();\n\n if (next && panel?.contains(next)) return;\n\n if (next && currentTrigger?.contains(next)) return;\n\n close('trigger');\n }\n\n const bindEvents = (): void => {\n triggerBinding?.();\n triggerBinding = null;\n\n const triggerEl = resolveSlot()?.assignedElements({ flatten: true })[0] as HTMLElement | undefined;\n\n if (!triggerEl) {\n currentTrigger = null;\n\n return;\n }\n\n currentTrigger = triggerEl;\n\n const removeAria = bindTriggerAria(triggerEl);\n const cleanups: Array<() => void> = [];\n\n const add = (\n target: EventTarget,\n event: string,\n listener: EventListener,\n eventOptions?: AddEventListenerOptions,\n ): void => {\n target.addEventListener(event, listener, eventOptions);\n cleanups.push(() => target.removeEventListener(event, listener, eventOptions));\n };\n\n const t = triggers.value;\n\n if (t.includes('click')) {\n add(triggerEl, 'click', toggle as EventListener);\n }\n\n if (t.includes('hover')) {\n add(triggerEl, 'pointerenter', () => open('hover'));\n add(triggerEl, 'pointerleave', () => close('trigger'));\n\n const panelEl = getPanel();\n\n if (panelEl) {\n add(panelEl, 'pointerenter', () => open('hover'));\n add(panelEl, 'pointerleave', () => close('trigger'));\n }\n }\n\n if (t.includes('focus')) {\n add(triggerEl, 'focusin', () => open('focus'));\n add(triggerEl, 'focusout', handleFocusOut as EventListener);\n\n const panelEl = getPanel();\n\n if (panelEl) add(panelEl, 'focusout', handleFocusOut as EventListener);\n }\n\n add(document, 'keydown', handleKeydown as EventListener);\n\n triggerBinding = () => {\n removeAria();\n\n for (const cleanup of cleanups) cleanup();\n\n currentTrigger = null;\n };\n };\n\n const stopOutsidePointerDismissal = createOutsidePointerDismissal({\n getTargets: () => [currentTrigger, getPanel(), options.getHost?.()],\n isActive: () => visible.value && triggers.value.includes('click'),\n onDismiss: () => close('outsideClick'),\n signal: abortSignal,\n });\n\n const mount = (): (() => void) => {\n let initializedOpenProp = false;\n const triggerSlot = resolveSlot();\n const rebind = (): void => {\n queueMicrotask(() => {\n if (!abortSignal.aborted) bindEvents();\n });\n };\n\n triggerSlot?.addEventListener('slotchange', rebind);\n rebind();\n watch(slotElements, bindEvents, { immediate: true });\n\n watch(\n openProp,\n (openVal) => {\n if (openVal === undefined || openVal === null) {\n if (initializedOpenProp && visible.value) {\n hideFloat();\n onClose?.('programmatic');\n }\n\n initializedOpenProp = true;\n\n return;\n }\n\n initializedOpenProp = true;\n\n if (openVal) {\n showFloat();\n onOpen?.('programmatic');\n } else if (visible.value) {\n hideFloat();\n onClose?.('programmatic');\n }\n },\n { immediate: true },\n );\n\n if (openProp.value === undefined && defaultOpen.value) {\n showFloat();\n onOpen?.('programmatic');\n }\n\n watch(disabled, (isNowDisabled) => {\n if (isNowDisabled) close('programmatic');\n });\n\n return () => {\n triggerSlot?.removeEventListener('slotchange', rebind);\n triggerBinding?.();\n triggerBinding = null;\n\n const panel = getPanel();\n\n if (panel) {\n delete panel.dataset.open;\n\n if ('hidePopover' in panel) {\n try {\n if (panel.matches(':popover-open')) panel.hidePopover();\n } catch {\n // Popover API restricted; data-open removal handles visibility.\n }\n }\n }\n\n stopOutsidePointerDismissal();\n };\n };\n\n return { close, mount, open, toggle, updatePosition, visible };\n};\n"],"mappings":"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"}
@@ -42,7 +42,7 @@ var c = (c) => {
42
42
  T() || d.value || w.value || (k(), m?.(e));
43
43
  }
44
44
  function M(e = "trigger") {
45
- T() || !w.value || (A(), p?.(e));
45
+ !T() && w.value && (A(), p?.(e));
46
46
  }
47
47
  function N() {
48
48
  T() || (w.value ? M() : j("click"));
@@ -1 +1 @@
1
- {"version":3,"file":"use-floating-trigger.js","names":[],"sources":["../../../src/overlay/shared/use-floating-trigger.ts"],"sourcesContent":["import type { Placement } from '@vielzeug/orbit';\n\nimport { computePosition, flip, offset, shift } from '@vielzeug/orbit';\nimport { type Readable, type Signal, signal, watch } from '@vielzeug/ripple';\n\nimport {\n createOutsidePointerDismissal,\n type DialogCloseReason,\n lifecycleSignal,\n type OverlayOpenReason,\n} from '../../core';\n\n// ── Types ─────────────────────────────────────────────────────────────────────\n\nexport type FloatingTriggerType = 'click' | 'focus' | 'hover';\n\nexport type AriaBindFn = (triggerEl: HTMLElement) => () => void;\n\nexport type FloatingTriggerOptions = {\n /** ARIA binding factory: called with the trigger element, returns a cleanup. */\n bindTriggerAria: AriaBindFn;\n /** Initial uncontrolled visibility. Ignored when `openProp` is defined. */\n defaultOpen: Readable<boolean | undefined>;\n /** If true, disable all trigger interactions and close if open. */\n disabled: Readable<boolean>;\n /** Returns the host element (e.g. ore-popover) so slotted content clicks aren't treated as outside clicks. */\n getHost?: () => Element | null;\n /** Returns the panel element, if mounted. */\n getPanel: () => HTMLElement | null;\n /** Gap from reference to floating panel in px. Default: 8. Accepts a signal for runtime reactivity. */\n offset?: Readable<number | undefined> | number;\n /** Cleanup registrar from the component setup ctx. Automatically called on disconnect. */\n onCleanup: (fn: () => void) => void;\n /** Called when the popover closes. */\n onClose?: (reason: DialogCloseReason) => void;\n /** Called when the popover opens. */\n onOpen?: (reason: OverlayOpenReason) => void;\n /** Callback when resolved placement changes (useful for CSS attribute). */\n onPlacementChange?: (placement: Placement) => void;\n /** Controlled open prop. When defined, disables uncontrolled logic. */\n openProp: Readable<boolean | undefined>;\n /** Preferred placement. Default: 'bottom'. */\n placement: Readable<Placement>;\n /** Slot element or a factory to lazily find it. Resolved each time bindEvents runs. */\n slot: HTMLSlotElement | null | (() => HTMLSlotElement | null);\n /** Slot elements signal — used to rebind when slotted elements change. */\n slotElements: Readable<Element[]>;\n /** Which triggers are active. Set to empty array or omit if handling events manually. */\n triggers: Readable<FloatingTriggerType[]>;\n};\n\nexport type FloatingTriggerHandle = {\n /** Closes the panel. No-op if controlled. */\n close: (reason?: DialogCloseReason) => void;\n /**\n * Call inside `onMounted`. Sets up slot + trigger event watchers\n * and returns a cleanup function to pass back to the framework.\n */\n mount: () => () => void;\n /** Opens the panel. No-op if controlled. */\n open: (reason?: OverlayOpenReason) => void;\n /** Toggles the panel. No-op if controlled. */\n toggle: () => void;\n /** Manually trigger a position recalculation. */\n updatePosition: () => void;\n /** Whether the panel is currently visible. */\n visible: Signal<boolean>;\n};\n\n// ── Implementation ─────────────────────────────────────────────────────────────\n\n/**\n * Shared logic for floating-panel triggers (tooltip, popover).\n *\n * Handles:\n * - Slot-based trigger element detection and rebinding\n * - Event binding per trigger type (hover / focus / click)\n * - Floating-element positioning via `@vielzeug/orbit`\n * - Controlled mode via `openProp` watcher\n * - Keyboard escape dismissal\n *\n * ARIA binding is intentionally delegated to the caller via `bindTriggerAria`\n * so that tooltip (`describedby`) and popover (`expanded`/`controls`/…) can diverge.\n */\nexport const useFloatingTrigger = (options: FloatingTriggerOptions): FloatingTriggerHandle => {\n const {\n bindTriggerAria,\n defaultOpen,\n disabled,\n getPanel,\n onClose,\n onOpen,\n onPlacementChange,\n openProp,\n placement,\n slot,\n slotElements,\n triggers,\n } = options;\n\n const resolveSlot = (): HTMLSlotElement | null => (typeof slot === 'function' ? slot() : slot);\n const getOffset = (): number => {\n const o = options.offset;\n\n if (o == null) return 8;\n\n const val = typeof o === 'object' ? (o as Readable<number | undefined>).value : o;\n\n return val ?? 8;\n };\n const abortSignal = lifecycleSignal(options.onCleanup);\n const visible = signal(false);\n const isControlled = () => openProp.value !== undefined;\n let currentTrigger: HTMLElement | null = null;\n let triggerBinding: (() => void) | null = null;\n\n function updatePosition(): void {\n const panel = getPanel();\n const trigger =\n currentTrigger ?? (resolveSlot()?.assignedElements({ flatten: true })[0] as HTMLElement | undefined);\n\n if (!panel || !trigger) return;\n\n const previousTransition = panel.style.transition;\n const previousTransform = panel.style.transform;\n\n panel.style.transition = 'none';\n panel.style.transform = 'none';\n\n const result = computePosition(trigger, panel, {\n middleware: [offset(getOffset()), flip(), shift({ padding: 8 })],\n placement: placement.value,\n });\n\n panel.style.left = `${result.x}px`;\n panel.style.top = `${result.y}px`;\n panel.style.transition = previousTransition;\n panel.style.transform = previousTransform;\n panel.dataset.placement = result.placement;\n\n onPlacementChange?.(result.placement as Placement);\n }\n\n function showFloat(): void {\n visible.value = true;\n\n const panel = getPanel();\n\n if (panel) {\n panel.dataset.open = '';\n\n if ('showPopover' in panel) {\n try {\n if (!panel.matches(':popover-open')) panel.showPopover();\n } catch {\n // Popover API restricted (e.g. sandboxed iframe); data-open CSS fallback handles visibility.\n }\n }\n }\n\n updatePosition();\n }\n\n function hideFloat(): void {\n visible.value = false;\n\n const panel = getPanel();\n\n if (panel) {\n delete panel.dataset.open;\n\n if ('hidePopover' in panel) {\n try {\n if (panel.matches(':popover-open')) panel.hidePopover();\n } catch {\n // Popover API restricted; data-open removal handles visibility.\n }\n }\n }\n }\n\n function open(reason: OverlayOpenReason = 'programmatic'): void {\n if (isControlled() || disabled.value || visible.value) return;\n\n showFloat();\n onOpen?.(reason);\n }\n\n function close(reason: DialogCloseReason = 'trigger'): void {\n if (isControlled() || !visible.value) return;\n\n hideFloat();\n onClose?.(reason);\n }\n\n function toggle(): void {\n if (isControlled()) return;\n\n if (visible.value) close();\n else open('click');\n }\n\n function handleKeydown(e: KeyboardEvent): void {\n if (e.key === 'Escape') close('escape');\n }\n\n function handleFocusOut(e: FocusEvent): void {\n const next = e.relatedTarget as Element | null;\n const panel = getPanel();\n\n if (next && panel?.contains(next)) return;\n\n if (next && currentTrigger?.contains(next)) return;\n\n close('trigger');\n }\n\n const bindEvents = (): void => {\n triggerBinding?.();\n triggerBinding = null;\n\n const triggerEl = resolveSlot()?.assignedElements({ flatten: true })[0] as HTMLElement | undefined;\n\n if (!triggerEl) {\n currentTrigger = null;\n\n return;\n }\n\n currentTrigger = triggerEl;\n\n const removeAria = bindTriggerAria(triggerEl);\n const cleanups: Array<() => void> = [];\n\n const add = (\n target: EventTarget,\n event: string,\n listener: EventListener,\n eventOptions?: AddEventListenerOptions,\n ): void => {\n target.addEventListener(event, listener, eventOptions);\n cleanups.push(() => target.removeEventListener(event, listener, eventOptions));\n };\n\n const t = triggers.value;\n\n if (t.includes('click')) {\n add(triggerEl, 'click', toggle as EventListener);\n }\n\n if (t.includes('hover')) {\n add(triggerEl, 'pointerenter', () => open('hover'));\n add(triggerEl, 'pointerleave', () => close('trigger'));\n\n const panelEl = getPanel();\n\n if (panelEl) {\n add(panelEl, 'pointerenter', () => open('hover'));\n add(panelEl, 'pointerleave', () => close('trigger'));\n }\n }\n\n if (t.includes('focus')) {\n add(triggerEl, 'focusin', () => open('focus'));\n add(triggerEl, 'focusout', handleFocusOut as EventListener);\n\n const panelEl = getPanel();\n\n if (panelEl) add(panelEl, 'focusout', handleFocusOut as EventListener);\n }\n\n add(document, 'keydown', handleKeydown as EventListener);\n\n triggerBinding = () => {\n removeAria();\n\n for (const cleanup of cleanups) cleanup();\n\n currentTrigger = null;\n };\n };\n\n const stopOutsidePointerDismissal = createOutsidePointerDismissal({\n getTargets: () => [currentTrigger, getPanel(), options.getHost?.()],\n isActive: () => visible.value && triggers.value.includes('click'),\n onDismiss: () => close('outsideClick'),\n signal: abortSignal,\n });\n\n const mount = (): (() => void) => {\n let initializedOpenProp = false;\n const triggerSlot = resolveSlot();\n const rebind = (): void => {\n queueMicrotask(() => {\n if (!abortSignal.aborted) bindEvents();\n });\n };\n\n triggerSlot?.addEventListener('slotchange', rebind);\n rebind();\n watch(slotElements, bindEvents, { immediate: true });\n\n watch(\n openProp,\n (openVal) => {\n if (openVal === undefined || openVal === null) {\n if (initializedOpenProp && visible.value) {\n hideFloat();\n onClose?.('programmatic');\n }\n\n initializedOpenProp = true;\n\n return;\n }\n\n initializedOpenProp = true;\n\n if (openVal) {\n showFloat();\n onOpen?.('programmatic');\n } else if (visible.value) {\n hideFloat();\n onClose?.('programmatic');\n }\n },\n { immediate: true },\n );\n\n if (openProp.value === undefined && defaultOpen.value) {\n showFloat();\n onOpen?.('programmatic');\n }\n\n watch(disabled, (isNowDisabled) => {\n if (isNowDisabled) close('programmatic');\n });\n\n return () => {\n triggerSlot?.removeEventListener('slotchange', rebind);\n triggerBinding?.();\n triggerBinding = null;\n\n const panel = getPanel();\n\n if (panel) {\n delete panel.dataset.open;\n\n if ('hidePopover' in panel) {\n try {\n if (panel.matches(':popover-open')) panel.hidePopover();\n } catch {\n // Popover API restricted; data-open removal handles visibility.\n }\n }\n }\n\n stopOutsidePointerDismissal();\n };\n };\n\n return { close, mount, open, toggle, updatePosition, visible };\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"use-floating-trigger.js","names":[],"sources":["../../../src/overlay/shared/use-floating-trigger.ts"],"sourcesContent":["import type { Placement } from '@vielzeug/orbit';\n\nimport { computePosition, flip, offset, shift } from '@vielzeug/orbit';\nimport { type Readable, type Signal, signal, watch } from '@vielzeug/ripple';\n\nimport {\n createOutsidePointerDismissal,\n type DialogCloseReason,\n lifecycleSignal,\n type OverlayOpenReason,\n} from '../../core';\n\n// ── Types ─────────────────────────────────────────────────────────────────────\n\nexport type FloatingTriggerType = 'click' | 'focus' | 'hover';\n\nexport type AriaBindFn = (triggerEl: HTMLElement) => () => void;\n\nexport type FloatingTriggerOptions = {\n /** ARIA binding factory: called with the trigger element, returns a cleanup. */\n bindTriggerAria: AriaBindFn;\n /** Initial uncontrolled visibility. Ignored when `openProp` is defined. */\n defaultOpen: Readable<boolean | undefined>;\n /** If true, disable all trigger interactions and close if open. */\n disabled: Readable<boolean>;\n /** Returns the host element (e.g. ore-popover) so slotted content clicks aren't treated as outside clicks. */\n getHost?: () => Element | null;\n /** Returns the panel element, if mounted. */\n getPanel: () => HTMLElement | null;\n /** Gap from reference to floating panel in px. Default: 8. Accepts a signal for runtime reactivity. */\n offset?: Readable<number | undefined> | number;\n /** Cleanup registrar from the component setup ctx. Automatically called on disconnect. */\n onCleanup: (fn: () => void) => void;\n /** Called when the popover closes. */\n onClose?: (reason: DialogCloseReason) => void;\n /** Called when the popover opens. */\n onOpen?: (reason: OverlayOpenReason) => void;\n /** Callback when resolved placement changes (useful for CSS attribute). */\n onPlacementChange?: (placement: Placement) => void;\n /** Controlled open prop. When defined, disables uncontrolled logic. */\n openProp: Readable<boolean | undefined>;\n /** Preferred placement. Default: 'bottom'. */\n placement: Readable<Placement>;\n /** Slot element or a factory to lazily find it. Resolved each time bindEvents runs. */\n slot: HTMLSlotElement | null | (() => HTMLSlotElement | null);\n /** Slot elements signal — used to rebind when slotted elements change. */\n slotElements: Readable<Element[]>;\n /** Which triggers are active. Set to empty array or omit if handling events manually. */\n triggers: Readable<FloatingTriggerType[]>;\n};\n\nexport type FloatingTriggerHandle = {\n /** Closes the panel. No-op if controlled. */\n close: (reason?: DialogCloseReason) => void;\n /**\n * Call inside `onMounted`. Sets up slot + trigger event watchers\n * and returns a cleanup function to pass back to the framework.\n */\n mount: () => () => void;\n /** Opens the panel. No-op if controlled. */\n open: (reason?: OverlayOpenReason) => void;\n /** Toggles the panel. No-op if controlled. */\n toggle: () => void;\n /** Manually trigger a position recalculation. */\n updatePosition: () => void;\n /** Whether the panel is currently visible. */\n visible: Signal<boolean>;\n};\n\n// ── Implementation ─────────────────────────────────────────────────────────────\n\n/**\n * Shared logic for floating-panel triggers (tooltip, popover).\n *\n * Handles:\n * - Slot-based trigger element detection and rebinding\n * - Event binding per trigger type (hover / focus / click)\n * - Floating-element positioning via `@vielzeug/orbit`\n * - Controlled mode via `openProp` watcher\n * - Keyboard escape dismissal\n *\n * ARIA binding is intentionally delegated to the caller via `bindTriggerAria`\n * so that tooltip (`describedby`) and popover (`expanded`/`controls`/…) can diverge.\n */\nexport const useFloatingTrigger = (options: FloatingTriggerOptions): FloatingTriggerHandle => {\n const {\n bindTriggerAria,\n defaultOpen,\n disabled,\n getPanel,\n onClose,\n onOpen,\n onPlacementChange,\n openProp,\n placement,\n slot,\n slotElements,\n triggers,\n } = options;\n\n const resolveSlot = (): HTMLSlotElement | null => (typeof slot === 'function' ? slot() : slot);\n const getOffset = (): number => {\n const o = options.offset;\n\n if (o == null) return 8;\n\n const val = typeof o === 'object' ? (o as Readable<number | undefined>).value : o;\n\n return val ?? 8;\n };\n const abortSignal = lifecycleSignal(options.onCleanup);\n const visible = signal(false);\n const isControlled = () => openProp.value !== undefined;\n let currentTrigger: HTMLElement | null = null;\n let triggerBinding: (() => void) | null = null;\n\n function updatePosition(): void {\n const panel = getPanel();\n const trigger =\n currentTrigger ?? (resolveSlot()?.assignedElements({ flatten: true })[0] as HTMLElement | undefined);\n\n if (!panel || !trigger) return;\n\n const previousTransition = panel.style.transition;\n const previousTransform = panel.style.transform;\n\n panel.style.transition = 'none';\n panel.style.transform = 'none';\n\n const result = computePosition(trigger, panel, {\n middleware: [offset(getOffset()), flip(), shift({ padding: 8 })],\n placement: placement.value,\n });\n\n panel.style.left = `${result.x}px`;\n panel.style.top = `${result.y}px`;\n panel.style.transition = previousTransition;\n panel.style.transform = previousTransform;\n panel.dataset.placement = result.placement;\n\n onPlacementChange?.(result.placement as Placement);\n }\n\n function showFloat(): void {\n visible.value = true;\n\n const panel = getPanel();\n\n if (panel) {\n panel.dataset.open = '';\n\n if ('showPopover' in panel) {\n try {\n if (!panel.matches(':popover-open')) panel.showPopover();\n } catch {\n // Popover API restricted (e.g. sandboxed iframe); data-open CSS fallback handles visibility.\n }\n }\n }\n\n updatePosition();\n }\n\n function hideFloat(): void {\n visible.value = false;\n\n const panel = getPanel();\n\n if (panel) {\n delete panel.dataset.open;\n\n if ('hidePopover' in panel) {\n try {\n if (panel.matches(':popover-open')) panel.hidePopover();\n } catch {\n // Popover API restricted; data-open removal handles visibility.\n }\n }\n }\n }\n\n function open(reason: OverlayOpenReason = 'programmatic'): void {\n if (isControlled() || disabled.value || visible.value) return;\n\n showFloat();\n onOpen?.(reason);\n }\n\n function close(reason: DialogCloseReason = 'trigger'): void {\n if (isControlled() || !visible.value) return;\n\n hideFloat();\n onClose?.(reason);\n }\n\n function toggle(): void {\n if (isControlled()) return;\n\n if (visible.value) close();\n else open('click');\n }\n\n function handleKeydown(e: KeyboardEvent): void {\n if (e.key === 'Escape') close('escape');\n }\n\n function handleFocusOut(e: FocusEvent): void {\n const next = e.relatedTarget as Element | null;\n const panel = getPanel();\n\n if (next && panel?.contains(next)) return;\n\n if (next && currentTrigger?.contains(next)) return;\n\n close('trigger');\n }\n\n const bindEvents = (): void => {\n triggerBinding?.();\n triggerBinding = null;\n\n const triggerEl = resolveSlot()?.assignedElements({ flatten: true })[0] as HTMLElement | undefined;\n\n if (!triggerEl) {\n currentTrigger = null;\n\n return;\n }\n\n currentTrigger = triggerEl;\n\n const removeAria = bindTriggerAria(triggerEl);\n const cleanups: Array<() => void> = [];\n\n const add = (\n target: EventTarget,\n event: string,\n listener: EventListener,\n eventOptions?: AddEventListenerOptions,\n ): void => {\n target.addEventListener(event, listener, eventOptions);\n cleanups.push(() => target.removeEventListener(event, listener, eventOptions));\n };\n\n const t = triggers.value;\n\n if (t.includes('click')) {\n add(triggerEl, 'click', toggle as EventListener);\n }\n\n if (t.includes('hover')) {\n add(triggerEl, 'pointerenter', () => open('hover'));\n add(triggerEl, 'pointerleave', () => close('trigger'));\n\n const panelEl = getPanel();\n\n if (panelEl) {\n add(panelEl, 'pointerenter', () => open('hover'));\n add(panelEl, 'pointerleave', () => close('trigger'));\n }\n }\n\n if (t.includes('focus')) {\n add(triggerEl, 'focusin', () => open('focus'));\n add(triggerEl, 'focusout', handleFocusOut as EventListener);\n\n const panelEl = getPanel();\n\n if (panelEl) add(panelEl, 'focusout', handleFocusOut as EventListener);\n }\n\n add(document, 'keydown', handleKeydown as EventListener);\n\n triggerBinding = () => {\n removeAria();\n\n for (const cleanup of cleanups) cleanup();\n\n currentTrigger = null;\n };\n };\n\n const stopOutsidePointerDismissal = createOutsidePointerDismissal({\n getTargets: () => [currentTrigger, getPanel(), options.getHost?.()],\n isActive: () => visible.value && triggers.value.includes('click'),\n onDismiss: () => close('outsideClick'),\n signal: abortSignal,\n });\n\n const mount = (): (() => void) => {\n let initializedOpenProp = false;\n const triggerSlot = resolveSlot();\n const rebind = (): void => {\n queueMicrotask(() => {\n if (!abortSignal.aborted) bindEvents();\n });\n };\n\n triggerSlot?.addEventListener('slotchange', rebind);\n rebind();\n watch(slotElements, bindEvents, { immediate: true });\n\n watch(\n openProp,\n (openVal) => {\n if (openVal === undefined || openVal === null) {\n if (initializedOpenProp && visible.value) {\n hideFloat();\n onClose?.('programmatic');\n }\n\n initializedOpenProp = true;\n\n return;\n }\n\n initializedOpenProp = true;\n\n if (openVal) {\n showFloat();\n onOpen?.('programmatic');\n } else if (visible.value) {\n hideFloat();\n onClose?.('programmatic');\n }\n },\n { immediate: true },\n );\n\n if (openProp.value === undefined && defaultOpen.value) {\n showFloat();\n onOpen?.('programmatic');\n }\n\n watch(disabled, (isNowDisabled) => {\n if (isNowDisabled) close('programmatic');\n });\n\n return () => {\n triggerSlot?.removeEventListener('slotchange', rebind);\n triggerBinding?.();\n triggerBinding = null;\n\n const panel = getPanel();\n\n if (panel) {\n delete panel.dataset.open;\n\n if ('hidePopover' in panel) {\n try {\n if (panel.matches(':popover-open')) panel.hidePopover();\n } catch {\n // Popover API restricted; data-open removal handles visibility.\n }\n }\n }\n\n stopOutsidePointerDismissal();\n };\n };\n\n return { close, mount, open, toggle, updatePosition, visible };\n};\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"popover.cjs","names":[],"sources":["../src/overlay/popover/popover.ts"],"sourcesContent":["import type { Placement } from '@vielzeug/orbit';\nimport {\n bind,\n createStableId,\n define,\n getHost,\n html,\n onCleanup,\n onMounted,\n prop,\n useEmit,\n useSlots,\n} from '@vielzeug/ore';\nimport { computed } from '@vielzeug/ripple';\n\nimport { type OverlayOpenChangeDetail, parseStringTriggers } from '../../core';\nimport { disablableBundle } from '../../shared';\nimport { reducedMotionMixin } from '../../styles';\nimport { useFloatingTrigger } from '../shared/use-floating-trigger';\nimport styles from './popover.css?inline';\n\nexport type PopoverTrigger = 'click' | 'focus' | 'hover';\n\nconst PANEL_OFFSET = 8;\nconst VALID_TRIGGERS = new Set<PopoverTrigger>(['click', 'focus', 'hover']);\nconst DEFAULT_POPOVER_TRIGGERS: PopoverTrigger[] = ['click'];\n\nconst parseOptionalBool = (value: string | null): boolean | undefined =>\n value == null ? undefined : value === '' || value === 'true';\n\nconst normalizeTriggers = (value: unknown): PopoverTrigger[] =>\n parseStringTriggers(String(value ?? ''), VALID_TRIGGERS, DEFAULT_POPOVER_TRIGGERS);\n\nexport type OrePopoverEvents = {\n 'open-change': OverlayOpenChangeDetail;\n};\n\n/** Popover component properties */\nexport type OrePopoverProps = {\n /** Initial uncontrolled open state. Ignored when `open` is set. */\n 'default-open'?: boolean;\n /** Disable the popover */\n disabled?: boolean;\n /** Accessible label for the panel */\n label?: string;\n /** Gap between trigger and panel in px */\n offset?: number;\n /** Controlled open state */\n open?: boolean;\n /** Preferred placement relative to the trigger */\n placement?: Placement;\n /** Which trigger(s) open/close the popover */\n trigger?: string;\n};\n\n/**\n * A floating panel anchored to a trigger element.\n *\n * @element ore-popover\n * @attr {string} placement - Preferred placement (default: 'bottom')\n * @attr {string} trigger - 'click' | 'hover' | 'focus' (default: 'click')\n * @attr {boolean} open - Controlled open state\n * @attr {boolean} default-open - Initial uncontrolled open state\n * @attr {number} offset - Gap in px (default: 8)\n * @attr {boolean} disabled - Disables the popover\n * @attr {string} label - aria-label on the panel\n * @fires open-change - When the panel state changes. detail: { open, reason }\n * @slot - The trigger element\n * @slot content - Panel content\n * @part panel - Panel container.\n *\n * @example\n * ```html\n * <ore-popover placement=\"bottom\" trigger=\"click\">\n * <ore-button>Open info</ore-button>\n * <div slot=\"content\">\n * <p>Popover body content goes here.</p>\n * </div>\n * </ore-popover>\n * ```\n */\nexport const POPOVER_TAG = 'ore-popover' as const;\ndefine<OrePopoverProps>(POPOVER_TAG, {\n props: {\n ...disablableBundle,\n 'default-open': prop.bool(false),\n label: prop.string(),\n offset: prop.number(PANEL_OFFSET),\n open: { default: undefined as boolean | undefined, parse: parseOptionalBool },\n placement: prop.oneOf(\n [\n 'bottom',\n 'bottom-end',\n 'bottom-start',\n 'left',\n 'left-end',\n 'left-start',\n 'right',\n 'right-end',\n 'right-start',\n 'top',\n 'top-end',\n 'top-start',\n ] as const,\n 'bottom',\n ),\n trigger: prop.string('click'),\n },\n setup(props) {\n const el = getHost();\n const emit = useEmit<OrePopoverEvents>();\n const slots = useSlots();\n\n const shadowRoot = el.shadowRoot;\n const isDisabled = computed(() => Boolean(props.disabled.value));\n const panelId = createStableId('popover');\n const triggers = computed<PopoverTrigger[]>(() => normalizeTriggers(props.trigger.value));\n let panelEl: HTMLElement | null = null;\n\n const floating = useFloatingTrigger({\n bindTriggerAria: (triggerEl) =>\n bind(\n {\n aria: {\n controls: () => panelId,\n disabled: () => String(isDisabled.value),\n expanded: () => String(floating.visible.value),\n haspopup: 'dialog',\n },\n },\n { target: triggerEl },\n ),\n defaultOpen: props['default-open'],\n disabled: isDisabled,\n getHost: () => el,\n getPanel: () => panelEl,\n offset: props.offset,\n onCleanup,\n onClose: (reason) => emit('open-change', { open: false, reason }),\n onOpen: (reason) => emit('open-change', { open: true, reason }),\n openProp: props.open as typeof props.open & { value: boolean | undefined },\n placement: computed(() => props.placement.value as Placement),\n slot: () => shadowRoot?.querySelector<HTMLSlotElement>('slot:not([name])') ?? null,\n slotElements: slots.elements(),\n triggers,\n });\n\n onMounted(() => floating.mount());\n\n return html`\n <slot></slot>\n <div\n class=\"panel\"\n part=\"panel\"\n id=\"${panelId}\"\n role=\"dialog\"\n aria-modal=\"false\"\n popover=\"manual\"\n aria-label=\"${props.label}\"\n aria-hidden=\"${() => String(!floating.visible.value)}\"\n ref=${(ref: HTMLElement) => {\n panelEl = ref;\n }}>\n <slot name=\"content\"></slot>\n </div>\n `;\n },\n styles: [reducedMotionMixin, styles],\n});\n"],"mappings":"oWAuBA,IAAM,EAAe,EACf,EAAiB,IAAI,IAAoB,CAAC,QAAS,QAAS,OAAO,CAAC,EACpE,EAA6C,CAAC,OAAO,EAErD,EAAqB,GACzB,GAAS,KAAO,IAAA,GAAY,IAAU,IAAM,IAAU,OAElD,EAAqB,GACzB,EAAA,oBAAoB,OAAO,GAAS,EAAE,EAAG,EAAgB,CAAwB,EAkDtE,EAAc,eAC3B,EAAA,EAAA,OAAA,CAAwB,EAAa,CACnC,MAAO,CACL,GAAG,EAAA,iBACH,eAAgB,EAAA,KAAK,KAAK,EAAK,EAC/B,MAAO,EAAA,KAAK,OAAO,EACnB,OAAQ,EAAA,KAAK,OAAO,CAAY,EAChC,KAAM,CAAE,QAAS,IAAA,GAAkC,MAAO,CAAkB,EAC5E,UAAW,EAAA,KAAK,MACd,CACE,SACA,aACA,eACA,OACA,WACA,aACA,QACA,YACA,cACA,MACA,UACA,WACF,EACA,QACF,EACA,QAAS,EAAA,KAAK,OAAO,OAAO,CAC9B,EACA,MAAM,EAAO,CACX,IAAM,GAAA,EAAK,EAAA,QAAA,CAAQ,EACb,GAAA,EAAO,EAAA,QAAA,CAA0B,EACjC,GAAA,EAAQ,EAAA,SAAA,CAAS,EAEjB,EAAa,EAAG,WAChB,GAAA,EAAa,EAAA,SAAA,KAAe,EAAQ,EAAM,SAAS,KAAM,EACzD,GAAA,EAAU,EAAA,eAAA,CAAe,SAAS,EAClC,GAAA,EAAW,EAAA,SAAA,KAAiC,EAAkB,EAAM,QAAQ,KAAK,CAAC,EACpF,EAA8B,KAE5B,EAAW,EAAA,mBAAmB,CAClC,gBAAkB,IAAA,EAChB,EAAA,KAAA,CACE,CACE,KAAM,CACJ,aAAgB,EAChB,aAAgB,OAAO,EAAW,KAAK,EACvC,aAAgB,OAAO,EAAS,QAAQ,KAAK,EAC7C,SAAU,QACZ,CACF,EACA,CAAE,OAAQ,CAAU,CACtB,EACF,YAAa,EAAM,gBACnB,SAAU,EACV,YAAe,EACf,aAAgB,EAChB,OAAQ,EAAM,OACd,UAAA,EAAA,UACA,QAAU,GAAW,EAAK,cAAe,CAAE,KAAM,GAAO,QAAO,CAAC,EAChE,OAAS,GAAW,EAAK,cAAe,CAAE,KAAM,GAAM,QAAO,CAAC,EAC9D,SAAU,EAAM,KAChB,WAAA,EAAW,EAAA,SAAA,KAAe,EAAM,UAAU,KAAkB,EAC5D,SAAY,GAAY,cAA+B,kBAAkB,GAAK,KAC9E,aAAc,EAAM,SAAS,EAC7B,UACF,CAAC,EAID,OAFA,EAAA,EAAA,UAAA,KAAgB,EAAS,MAAM,CAAC,EAEzB,EAAA,IAAI;;;;;cAKD,EAAQ;;;;sBAIA,EAAM,MAAM;2BACL,OAAO,CAAC,EAAS,QAAQ,KAAK,EAAE;cAC9C,GAAqB,CAC1B,EAAU,CACZ,EAAE;;;KAIR,EACA,OAAQ,CAAC,EAAA,mBAAoB,EAAA,OAAM,CACrC,CAAC"}
1
+ {"version":3,"file":"popover.cjs","names":[],"sources":["../src/overlay/popover/popover.ts"],"sourcesContent":["import type { Placement } from '@vielzeug/orbit';\nimport {\n bind,\n createStableId,\n define,\n getHost,\n html,\n onCleanup,\n onMounted,\n prop,\n useEmit,\n useSlots,\n} from '@vielzeug/ore';\nimport { computed } from '@vielzeug/ripple';\n\nimport { type OverlayOpenChangeDetail, parseStringTriggers } from '../../core';\nimport { disablableBundle } from '../../shared';\nimport { reducedMotionMixin } from '../../styles';\nimport { useFloatingTrigger } from '../shared/use-floating-trigger';\nimport styles from './popover.css?inline';\n\nexport type PopoverTrigger = 'click' | 'focus' | 'hover';\n\nconst PANEL_OFFSET = 8;\nconst VALID_TRIGGERS = new Set<PopoverTrigger>(['click', 'focus', 'hover']);\nconst DEFAULT_POPOVER_TRIGGERS: PopoverTrigger[] = ['click'];\n\nconst parseOptionalBool = (value: string | null): boolean | undefined =>\n value == null ? undefined : value === '' || value === 'true';\n\nconst normalizeTriggers = (value: unknown): PopoverTrigger[] =>\n parseStringTriggers(String(value ?? ''), VALID_TRIGGERS, DEFAULT_POPOVER_TRIGGERS);\n\nexport type OrePopoverEvents = {\n 'open-change': OverlayOpenChangeDetail;\n};\n\n/** Popover component properties */\nexport type OrePopoverProps = {\n /** Initial uncontrolled open state. Ignored when `open` is set. */\n 'default-open'?: boolean;\n /** Disable the popover */\n disabled?: boolean;\n /** Accessible label for the panel */\n label?: string;\n /** Gap between trigger and panel in px */\n offset?: number;\n /** Controlled open state */\n open?: boolean;\n /** Preferred placement relative to the trigger */\n placement?: Placement;\n /** Which trigger(s) open/close the popover */\n trigger?: string;\n};\n\n/**\n * A floating panel anchored to a trigger element.\n *\n * @element ore-popover\n * @attr {string} placement - Preferred placement (default: 'bottom')\n * @attr {string} trigger - 'click' | 'hover' | 'focus' (default: 'click')\n * @attr {boolean} open - Controlled open state\n * @attr {boolean} default-open - Initial uncontrolled open state\n * @attr {number} offset - Gap in px (default: 8)\n * @attr {boolean} disabled - Disables the popover\n * @attr {string} label - aria-label on the panel\n * @fires open-change - When the panel state changes. detail: { open, reason }\n * @slot - The trigger element\n * @slot content - Panel content\n * @cssprop --popover-overflow - Panel overflow behavior (default: auto)\n * @part panel - Panel container.\n *\n * @example\n * ```html\n * <ore-popover placement=\"bottom\" trigger=\"click\">\n * <ore-button>Open info</ore-button>\n * <div slot=\"content\">\n * <p>Popover body content goes here.</p>\n * </div>\n * </ore-popover>\n * ```\n */\nexport const POPOVER_TAG = 'ore-popover' as const;\ndefine<OrePopoverProps>(POPOVER_TAG, {\n props: {\n ...disablableBundle,\n 'default-open': prop.bool(false),\n label: prop.string(),\n offset: prop.number(PANEL_OFFSET),\n open: { default: undefined as boolean | undefined, parse: parseOptionalBool },\n placement: prop.oneOf(\n [\n 'bottom',\n 'bottom-end',\n 'bottom-start',\n 'left',\n 'left-end',\n 'left-start',\n 'right',\n 'right-end',\n 'right-start',\n 'top',\n 'top-end',\n 'top-start',\n ] as const,\n 'bottom',\n ),\n trigger: prop.string('click'),\n },\n setup(props) {\n const el = getHost();\n const emit = useEmit<OrePopoverEvents>();\n const slots = useSlots();\n\n const shadowRoot = el.shadowRoot;\n const isDisabled = computed(() => Boolean(props.disabled.value));\n const panelId = createStableId('popover');\n const triggers = computed<PopoverTrigger[]>(() => normalizeTriggers(props.trigger.value));\n let panelEl: HTMLElement | null = null;\n\n const floating = useFloatingTrigger({\n bindTriggerAria: (triggerEl) =>\n bind(\n {\n aria: {\n controls: () => panelId,\n disabled: () => String(isDisabled.value),\n expanded: () => String(floating.visible.value),\n haspopup: 'dialog',\n },\n },\n { target: triggerEl },\n ),\n defaultOpen: props['default-open'],\n disabled: isDisabled,\n getHost: () => el,\n getPanel: () => panelEl,\n offset: props.offset,\n onCleanup,\n onClose: (reason) => emit('open-change', { open: false, reason }),\n onOpen: (reason) => emit('open-change', { open: true, reason }),\n openProp: props.open as typeof props.open & { value: boolean | undefined },\n placement: computed(() => props.placement.value as Placement),\n slot: () => shadowRoot?.querySelector<HTMLSlotElement>('slot:not([name])') ?? null,\n slotElements: slots.elements(),\n triggers,\n });\n\n onMounted(() => floating.mount());\n\n return html`\n <slot></slot>\n <div\n class=\"panel\"\n part=\"panel\"\n id=\"${panelId}\"\n role=\"dialog\"\n aria-modal=\"false\"\n popover=\"manual\"\n aria-label=\"${props.label}\"\n aria-hidden=\"${() => String(!floating.visible.value)}\"\n ref=${(ref: HTMLElement) => {\n panelEl = ref;\n }}>\n <slot name=\"content\"></slot>\n </div>\n `;\n },\n styles: [reducedMotionMixin, styles],\n});\n"],"mappings":"oWAuBA,IAAM,EAAe,EACf,EAAiB,IAAI,IAAoB,CAAC,QAAS,QAAS,OAAO,CAAC,EACpE,EAA6C,CAAC,OAAO,EAErD,EAAqB,GACzB,GAAS,KAAO,IAAA,GAAY,IAAU,IAAM,IAAU,OAElD,EAAqB,GACzB,EAAA,oBAAoB,OAAO,GAAS,EAAE,EAAG,EAAgB,CAAwB,EAmDtE,EAAc,eAC3B,EAAA,EAAA,OAAA,CAAwB,EAAa,CACnC,MAAO,CACL,GAAG,EAAA,iBACH,eAAgB,EAAA,KAAK,KAAK,EAAK,EAC/B,MAAO,EAAA,KAAK,OAAO,EACnB,OAAQ,EAAA,KAAK,OAAO,CAAY,EAChC,KAAM,CAAE,QAAS,IAAA,GAAkC,MAAO,CAAkB,EAC5E,UAAW,EAAA,KAAK,MACd,CACE,SACA,aACA,eACA,OACA,WACA,aACA,QACA,YACA,cACA,MACA,UACA,WACF,EACA,QACF,EACA,QAAS,EAAA,KAAK,OAAO,OAAO,CAC9B,EACA,MAAM,EAAO,CACX,IAAM,GAAA,EAAK,EAAA,QAAA,CAAQ,EACb,GAAA,EAAO,EAAA,QAAA,CAA0B,EACjC,GAAA,EAAQ,EAAA,SAAA,CAAS,EAEjB,EAAa,EAAG,WAChB,GAAA,EAAa,EAAA,SAAA,KAAe,EAAQ,EAAM,SAAS,KAAM,EACzD,GAAA,EAAU,EAAA,eAAA,CAAe,SAAS,EAClC,GAAA,EAAW,EAAA,SAAA,KAAiC,EAAkB,EAAM,QAAQ,KAAK,CAAC,EACpF,EAA8B,KAE5B,EAAW,EAAA,mBAAmB,CAClC,gBAAkB,IAAA,EAChB,EAAA,KAAA,CACE,CACE,KAAM,CACJ,aAAgB,EAChB,aAAgB,OAAO,EAAW,KAAK,EACvC,aAAgB,OAAO,EAAS,QAAQ,KAAK,EAC7C,SAAU,QACZ,CACF,EACA,CAAE,OAAQ,CAAU,CACtB,EACF,YAAa,EAAM,gBACnB,SAAU,EACV,YAAe,EACf,aAAgB,EAChB,OAAQ,EAAM,OACd,UAAA,EAAA,UACA,QAAU,GAAW,EAAK,cAAe,CAAE,KAAM,GAAO,QAAO,CAAC,EAChE,OAAS,GAAW,EAAK,cAAe,CAAE,KAAM,GAAM,QAAO,CAAC,EAC9D,SAAU,EAAM,KAChB,WAAA,EAAW,EAAA,SAAA,KAAe,EAAM,UAAU,KAAkB,EAC5D,SAAY,GAAY,cAA+B,kBAAkB,GAAK,KAC9E,aAAc,EAAM,SAAS,EAC7B,UACF,CAAC,EAID,OAFA,EAAA,EAAA,UAAA,KAAgB,EAAS,MAAM,CAAC,EAEzB,EAAA,IAAI;;;;;cAKD,EAAQ;;;;sBAIA,EAAM,MAAM;2BACL,OAAO,CAAC,EAAS,QAAQ,KAAK,EAAE;cAC9C,GAAqB,CAC1B,EAAU,CACZ,EAAE;;;KAIR,EACA,OAAQ,CAAC,EAAA,mBAAoB,EAAA,OAAM,CACrC,CAAC"}