@vielzeug/refine 3.3.0 → 3.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. package/dist/alert.cjs +3 -3
  2. package/dist/alert.cjs.map +1 -1
  3. package/dist/alert.js +11 -4
  4. package/dist/alert.js.map +1 -1
  5. package/dist/core/positioner.cjs +1 -1
  6. package/dist/core/positioner.cjs.map +1 -1
  7. package/dist/core/positioner.d.ts +4 -1
  8. package/dist/core/positioner.d.ts.map +1 -1
  9. package/dist/core/positioner.js +10 -5
  10. package/dist/core/positioner.js.map +1 -1
  11. package/dist/custom-elements.json +4110 -4055
  12. package/dist/feedback/alert/alert.d.ts +8 -1
  13. package/dist/feedback/alert/alert.d.ts.map +1 -1
  14. package/dist/feedback/toast/toast.css.cjs +1 -1
  15. package/dist/feedback/toast/toast.css.cjs.map +1 -1
  16. package/dist/feedback/toast/toast.css.js +1 -1
  17. package/dist/feedback/toast/toast.css.js.map +1 -1
  18. package/dist/feedback/toast/toast.d.ts.map +1 -1
  19. package/dist/inputs/combobox/combobox.css.cjs +1 -1
  20. package/dist/inputs/combobox/combobox.css.cjs.map +1 -1
  21. package/dist/inputs/combobox/combobox.css.js +1 -1
  22. package/dist/inputs/combobox/combobox.css.js.map +1 -1
  23. package/dist/inputs/date-picker/date-picker.css.cjs +1 -1
  24. package/dist/inputs/date-picker/date-picker.css.cjs.map +1 -1
  25. package/dist/inputs/date-picker/date-picker.css.js +1 -1
  26. package/dist/inputs/date-picker/date-picker.css.js.map +1 -1
  27. package/dist/inputs/input/input.css.cjs +1 -1
  28. package/dist/inputs/input/input.css.cjs.map +1 -1
  29. package/dist/inputs/input/input.css.js +1 -1
  30. package/dist/inputs/input/input.css.js.map +1 -1
  31. package/dist/inputs/number-input/number-input.css.cjs +1 -1
  32. package/dist/inputs/number-input/number-input.css.cjs.map +1 -1
  33. package/dist/inputs/number-input/number-input.css.js +1 -1
  34. package/dist/inputs/number-input/number-input.css.js.map +1 -1
  35. package/dist/inputs/select/select.css.cjs +1 -1
  36. package/dist/inputs/select/select.css.cjs.map +1 -1
  37. package/dist/inputs/select/select.css.js +1 -1
  38. package/dist/inputs/select/select.css.js.map +1 -1
  39. package/dist/inputs/slider/slider.css.cjs +1 -1
  40. package/dist/inputs/slider/slider.css.cjs.map +1 -1
  41. package/dist/inputs/slider/slider.css.js +1 -1
  42. package/dist/inputs/slider/slider.css.js.map +1 -1
  43. package/dist/refine.cjs +103 -98
  44. package/dist/refine.cjs.map +1 -1
  45. package/dist/refine.iife.js +103 -98
  46. package/dist/refine.iife.js.map +1 -1
  47. package/dist/refine.js +610 -605
  48. package/dist/refine.js.map +1 -1
  49. package/dist/toast.cjs +44 -39
  50. package/dist/toast.cjs.map +1 -1
  51. package/dist/toast.js +132 -117
  52. package/dist/toast.js.map +1 -1
  53. package/package.json +2 -2
package/dist/alert.cjs CHANGED
@@ -1,10 +1,10 @@
1
- Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./styles/mixins/accessibility.css.cjs"),t=require("./styles/mixins/shape.css.cjs"),n=require("./styles/mixins/theme.css.cjs"),r=require("./shared/prop-bundles.cjs"),i=require("./overlay/shared/await-exit.cjs"),a=require("./feedback/alert/alert.css.cjs");let o=require("@vielzeug/ore");var s=`ore-alert`;(0,o.define)(s,{props:{...r.themableBundle,...r.sizableBundle,...r.roundableBundle,accented:o.prop.bool(!1),dismissible:o.prop.bool(!1),heading:o.prop.string(),horizontal:o.prop.bool(!1),variant:o.prop.string()},setup(e){let t=(0,o.getHost)(),n=(0,o.useEmit)(),r=(0,o.useSlots)(),a=null,s=e=>{a&&(a.textContent=``,requestAnimationFrame(()=>{a&&(a.textContent=e)}))};return o.html`
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./styles/mixins/accessibility.css.cjs"),t=require("./styles/mixins/shape.css.cjs"),n=require("./styles/mixins/theme.css.cjs"),r=require("./shared/prop-bundles.cjs"),i=require("./overlay/shared/await-exit.cjs"),a=require("./feedback/alert/alert.css.cjs");let o=require("@vielzeug/ore");var s=`ore-alert`;(0,o.define)(s,{props:{...r.themableBundle,...r.sizableBundle,...r.roundableBundle,accented:o.prop.bool(!1),dismissible:o.prop.bool(!1),embedded:o.prop.bool(!1),heading:o.prop.string(),horizontal:o.prop.bool(!1),variant:o.prop.string()},setup(e){let t=(0,o.getHost)(),n=(0,o.useEmit)(),r=(0,o.useSlots)(),a=null,s=e=>{a&&(a.textContent=``,requestAnimationFrame(()=>{a&&(a.textContent=e)}))};return o.html`
2
2
  <span
3
3
  class="sr-announce"
4
4
  aria-live="polite"
5
5
  aria-atomic="true"
6
6
  ref="${e=>{a=e}}"></span>
7
- <div class="alert" role="${()=>e.color.value===`error`?`alert`:`status`}" part="alert">
7
+ <div class="alert" role="${()=>e.embedded.value?`presentation`:e.color.value===`error`?`alert`:`status`}" part="alert">
8
8
  <span class="icon" part="icon" aria-hidden="true" ?hidden="${()=>!r.has(`icon`).value}">
9
9
  <slot name="icon"></slot>
10
10
  </span>
@@ -28,7 +28,7 @@ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=requi
28
28
  type="button"
29
29
  aria-label="Dismiss alert"
30
30
  ?hidden="${()=>!e.dismissible.value}"
31
- @click="${r=>{e.dismissible.value&&(t.setAttribute(`dismissing`,``),i.awaitExit(t,()=>{t.removeAttribute(`dismissing`),t.setAttribute(`dismissed`,``),s(`Alert dismissed`),n(`dismiss`,{originalEvent:r})}))}}">
31
+ @click="${r=>{if(e.dismissible.value){if(e.embedded.value){n(`dismiss`,{originalEvent:r});return}t.setAttribute(`dismissing`,``),i.awaitExit(t,()=>{t.removeAttribute(`dismissing`),t.setAttribute(`dismissed`,``),s(`Alert dismissed`),n(`dismiss`,{originalEvent:r})})}}}">
32
32
  <ore-icon name="x" size="16" stroke-width="2.5" aria-hidden="true"></ore-icon>
33
33
  </button>
34
34
  </div>
@@ -1 +1 @@
1
- {"version":3,"file":"alert.cjs","names":[],"sources":["../src/feedback/alert/alert.ts"],"sourcesContent":["import { define, getHost, html, prop, useEmit, useSlots } from '@vielzeug/ore';\n\nimport type { ComponentSize, RoundedSize, ThemeColor } from '../../types';\n\nimport '../../content/icon/icon';\nimport { awaitExit } from '../../overlay/shared/await-exit';\nimport { roundableBundle, sizableBundle, themableBundle } from '../../shared';\nimport {\n coarsePointerMixin,\n colorThemeMixin,\n forcedColorsMixin,\n reducedMotionMixin,\n roundedVariantMixin,\n sizeVariantMixin,\n} from '../../styles';\nimport componentStyles from './alert.css?inline';\n\nexport type OreAlertEvents = {\n dismiss: { originalEvent: MouseEvent };\n};\n\nexport type OreAlertProps = {\n /** Show a left accent border (for flat/bordered variants) */\n accented?: boolean;\n /** Theme color */\n color?: ThemeColor;\n /** Show a dismissible (×) button */\n dismissible?: boolean;\n /** Heading text shown above the content */\n heading?: string;\n /** Position action buttons to the right instead of below */\n horizontal?: boolean;\n /** Border radius */\n rounded?: RoundedSize | '';\n /** Alert size */\n size?: ComponentSize;\n /** Visual style variant */\n variant?: 'solid' | 'flat' | 'bordered';\n};\n\n/**\n * A status/feedback banner with optional heading, icon slot, and dismiss button.\n *\n * @element ore-alert\n *\n * @attr {string} color - Theme color: 'primary' | 'secondary' | 'info' | 'success' | 'warning' | 'error'\n * @attr {string} variant - Visual style: 'flat' | 'solid' | 'bordered'\n * @attr {string} size - Size: 'sm' | 'md' | 'lg'\n * @attr {string} rounded - Border radius: 'none' | 'sm' | 'md' | 'lg' | 'xl' | '2xl' | '3xl' | 'full'\n * @attr {string} heading - Bold heading text above the content\n * @attr {boolean} dismissible - Show a close (×) button\n * @attr {boolean} accented - Add a left accent border (flat/bordered only)\n * @attr {boolean} horizontal - Position action buttons to the right instead of below\n *\n * @fires dismiss - Fired when the alert is dismissed. detail: { originalEvent: MouseEvent }\n *\n * @slot - Default slot for the alert message content\n * @slot icon - Icon on the left side\n * @slot meta - Metadata displayed alongside the heading (lighter, right-aligned)\n * @slot actions - Action buttons shown below the message\n *\n * @cssprop --alert-bg - Background color\n * @cssprop --alert-color - Text/icon color\n * @cssprop --alert-border-color - Border color\n * @cssprop --alert-shadow - Box shadow\n * @cssprop --alert-radius - Border radius\n * @cssprop --alert-padding - Padding\n * @cssprop --alert-gap - Gap between icon, body, and close button\n * @cssprop --alert-font-size - Font size\n *\n * @part alert - Alert root container.\n * @part icon - Icon container.\n * @part header - Header container.\n * @part heading - Heading text element.\n * @part meta - Meta text container.\n * @part body - Body content container.\n * @part content - Content container.\n * @part actions - Actions slot container.\n * @part close - Close action control.\n * @example\n * ```html\n * <ore-alert color=\"success\">Your changes have been saved.</ore-alert>\n * <ore-alert color=\"error\" variant=\"solid\" dismissible heading=\"Something went wrong\">\n * Please try again later.\n * </ore-alert>\n * ```\n */\nexport const ALERT_TAG = 'ore-alert' as const;\ndefine<OreAlertProps>(ALERT_TAG, {\n props: {\n ...themableBundle,\n ...sizableBundle,\n ...roundableBundle,\n accented: prop.bool(false),\n dismissible: prop.bool(false),\n heading: prop.string(),\n horizontal: prop.bool(false),\n variant: prop.string<'solid' | 'flat' | 'bordered'>(),\n },\n setup(props) {\n const el = getHost();\n const emit = useEmit<OreAlertEvents>();\n const slots = useSlots();\n\n let announceEl: HTMLElement | null = null;\n\n const announce = (message: string) => {\n if (!announceEl) return;\n\n announceEl.textContent = '';\n requestAnimationFrame(() => {\n if (announceEl) announceEl.textContent = message;\n });\n };\n\n const handleDismiss = (e: MouseEvent) => {\n if (!props.dismissible.value) return;\n\n el.setAttribute('dismissing', '');\n awaitExit(el, () => {\n el.removeAttribute('dismissing');\n el.setAttribute('dismissed', '');\n announce('Alert dismissed');\n emit('dismiss', { originalEvent: e });\n });\n };\n\n const alertRole = () => (props.color.value === 'error' ? 'alert' : 'status');\n\n return html`\n <span\n class=\"sr-announce\"\n aria-live=\"polite\"\n aria-atomic=\"true\"\n ref=\"${(ref: HTMLElement) => {\n announceEl = ref;\n }}\"></span>\n <div class=\"alert\" role=\"${alertRole}\" part=\"alert\">\n <span class=\"icon\" part=\"icon\" aria-hidden=\"true\" ?hidden=\"${() => !slots.has('icon').value}\">\n <slot name=\"icon\"></slot>\n </span>\n <div class=\"header\" part=\"header\" ?hidden=\"${() => !props.heading.value}\">\n <span class=\"heading\" part=\"heading\">${props.heading}</span>\n <span class=\"meta\" part=\"meta\">\n <slot name=\"meta\"></slot>\n </span>\n </div>\n <div class=\"body\" part=\"body\">\n <div class=\"content\" part=\"content\">\n <slot></slot>\n </div>\n </div>\n <div class=\"actions\" part=\"actions\" ?hidden=\"${() => !slots.has('actions').value}\">\n <slot name=\"actions\"></slot>\n </div>\n <button\n class=\"close\"\n part=\"close\"\n type=\"button\"\n aria-label=\"Dismiss alert\"\n ?hidden=\"${() => !props.dismissible.value}\"\n @click=\"${handleDismiss}\">\n <ore-icon name=\"x\" size=\"16\" stroke-width=\"2.5\" aria-hidden=\"true\"></ore-icon>\n </button>\n </div>\n `;\n },\n styles: [\n colorThemeMixin,\n coarsePointerMixin,\n reducedMotionMixin,\n roundedVariantMixin,\n forcedColorsMixin,\n sizeVariantMixin({\n lg: { '--_padding': 'var(--size-4) var(--size-5)', fontSize: 'var(--text-base)', gap: 'var(--size-3-5)' },\n sm: { '--_padding': 'var(--size-2) var(--size-3)', fontSize: 'var(--text-xs)', gap: 'var(--size-2)' },\n }),\n componentStyles,\n ],\n});\n"],"mappings":"iXAuFA,IAAa,EAAY,aACzB,EAAA,EAAA,OAAA,CAAsB,EAAW,CAC/B,MAAO,CACL,GAAG,EAAA,eACH,GAAG,EAAA,cACH,GAAG,EAAA,gBACH,SAAU,EAAA,KAAK,KAAK,EAAK,EACzB,YAAa,EAAA,KAAK,KAAK,EAAK,EAC5B,QAAS,EAAA,KAAK,OAAO,EACrB,WAAY,EAAA,KAAK,KAAK,EAAK,EAC3B,QAAS,EAAA,KAAK,OAAsC,CACtD,EACA,MAAM,EAAO,CACX,IAAM,GAAA,EAAK,EAAA,QAAA,CAAQ,EACb,GAAA,EAAO,EAAA,QAAA,CAAwB,EAC/B,GAAA,EAAQ,EAAA,SAAA,CAAS,EAEnB,EAAiC,KAE/B,EAAY,GAAoB,CAC/B,IAEL,EAAW,YAAc,GACzB,0BAA4B,CACtB,IAAY,EAAW,YAAc,EAC3C,CAAC,EACH,EAgBA,MAAO,GAAA,IAAI;;;;;eAKC,GAAqB,CAC3B,EAAa,CACf,EAAE;qCATmB,EAAM,MAAM,QAAU,QAAU,QAAU,SAU5B;yEACgC,CAAC,EAAM,IAAI,MAAM,CAAC,CAAC,MAAM;;;yDAGzC,CAAC,EAAM,QAAQ,MAAM;iDAC/B,EAAM,QAAQ;;;;;;;;;;2DAUF,CAAC,EAAM,IAAI,SAAS,CAAC,CAAC,MAAM;;;;;;;;yBAQ9D,CAAC,EAAM,YAAY,MAAM;oBA7CzB,GAAkB,CAClC,EAAM,YAAY,QAEvB,EAAG,aAAa,aAAc,EAAE,EAChC,EAAA,UAAU,MAAU,CAClB,EAAG,gBAAgB,YAAY,EAC/B,EAAG,aAAa,YAAa,EAAE,EAC/B,EAAS,iBAAiB,EAC1B,EAAK,UAAW,CAAE,cAAe,CAAE,CAAC,CACtC,CAAC,EACH,EAoC8B;;;;KAKhC,EACA,OAAQ,CACN,EAAA,gBACA,EAAA,mBACA,EAAA,mBACA,EAAA,oBACA,EAAA,kBACA,EAAA,iBAAiB,CACf,GAAI,CAAE,aAAc,8BAA+B,SAAU,mBAAoB,IAAK,iBAAkB,EACxG,GAAI,CAAE,aAAc,8BAA+B,SAAU,iBAAkB,IAAK,eAAgB,CACtG,CAAC,EACD,EAAA,OACF,CACF,CAAC"}
1
+ {"version":3,"file":"alert.cjs","names":[],"sources":["../src/feedback/alert/alert.ts"],"sourcesContent":["import { define, getHost, html, prop, useEmit, useSlots } from '@vielzeug/ore';\n\nimport type { ComponentSize, RoundedSize, ThemeColor } from '../../types';\n\nimport '../../content/icon/icon';\nimport { awaitExit } from '../../overlay/shared/await-exit';\nimport { roundableBundle, sizableBundle, themableBundle } from '../../shared';\nimport {\n coarsePointerMixin,\n colorThemeMixin,\n forcedColorsMixin,\n reducedMotionMixin,\n roundedVariantMixin,\n sizeVariantMixin,\n} from '../../styles';\nimport componentStyles from './alert.css?inline';\n\nexport type OreAlertEvents = {\n dismiss: { originalEvent: MouseEvent };\n};\n\nexport type OreAlertProps = {\n /** Show a left accent border (for flat/bordered variants) */\n accented?: boolean;\n /** Theme color */\n color?: ThemeColor;\n /** Show a dismissible (×) button */\n dismissible?: boolean;\n /**\n * Render as a passive surface inside a host that owns lifecycle and\n * announcements (for example `ore-toast`): no live-region role, and the\n * close button only emits `dismiss` instead of hiding the alert.\n */\n embedded?: boolean;\n /** Heading text shown above the content */\n heading?: string;\n /** Position action buttons to the right instead of below */\n horizontal?: boolean;\n /** Border radius */\n rounded?: RoundedSize | '';\n /** Alert size */\n size?: ComponentSize;\n /** Visual style variant */\n variant?: 'solid' | 'flat' | 'bordered';\n};\n\n/**\n * A status/feedback banner with optional heading, icon slot, and dismiss button.\n *\n * @element ore-alert\n *\n * @attr {string} color - Theme color: 'primary' | 'secondary' | 'info' | 'success' | 'warning' | 'error'\n * @attr {string} variant - Visual style: 'flat' | 'solid' | 'bordered'\n * @attr {string} size - Size: 'sm' | 'md' | 'lg'\n * @attr {string} rounded - Border radius: 'none' | 'sm' | 'md' | 'lg' | 'xl' | '2xl' | '3xl' | 'full'\n * @attr {string} heading - Bold heading text above the content\n * @attr {boolean} dismissible - Show a close (×) button\n * @attr {boolean} embedded - Passive surface for a host that owns lifecycle and announcements; the close button only emits `dismiss`\n * @attr {boolean} accented - Add a left accent border (flat/bordered only)\n * @attr {boolean} horizontal - Position action buttons to the right instead of below\n *\n * @fires dismiss - Fired when the alert is dismissed (after the exit animation, or immediately when `embedded`). detail: { originalEvent: MouseEvent }\n *\n * @slot - Default slot for the alert message content\n * @slot icon - Icon on the left side\n * @slot meta - Metadata displayed alongside the heading (lighter, right-aligned)\n * @slot actions - Action buttons shown below the message\n *\n * @cssprop --alert-bg - Background color\n * @cssprop --alert-color - Text/icon color\n * @cssprop --alert-border-color - Border color\n * @cssprop --alert-shadow - Box shadow\n * @cssprop --alert-radius - Border radius\n * @cssprop --alert-padding - Padding\n * @cssprop --alert-gap - Gap between icon, body, and close button\n * @cssprop --alert-font-size - Font size\n *\n * @part alert - Alert root container.\n * @part icon - Icon container.\n * @part header - Header container.\n * @part heading - Heading text element.\n * @part meta - Meta text container.\n * @part body - Body content container.\n * @part content - Content container.\n * @part actions - Actions slot container.\n * @part close - Close action control.\n * @example\n * ```html\n * <ore-alert color=\"success\">Your changes have been saved.</ore-alert>\n * <ore-alert color=\"error\" variant=\"solid\" dismissible heading=\"Something went wrong\">\n * Please try again later.\n * </ore-alert>\n * ```\n */\nexport const ALERT_TAG = 'ore-alert' as const;\ndefine<OreAlertProps>(ALERT_TAG, {\n props: {\n ...themableBundle,\n ...sizableBundle,\n ...roundableBundle,\n accented: prop.bool(false),\n dismissible: prop.bool(false),\n embedded: prop.bool(false),\n heading: prop.string(),\n horizontal: prop.bool(false),\n variant: prop.string<'solid' | 'flat' | 'bordered'>(),\n },\n setup(props) {\n const el = getHost();\n const emit = useEmit<OreAlertEvents>();\n const slots = useSlots();\n\n let announceEl: HTMLElement | null = null;\n\n const announce = (message: string) => {\n if (!announceEl) return;\n\n announceEl.textContent = '';\n requestAnimationFrame(() => {\n if (announceEl) announceEl.textContent = message;\n });\n };\n\n const handleDismiss = (e: MouseEvent) => {\n if (!props.dismissible.value) return;\n\n if (props.embedded.value) {\n emit('dismiss', { originalEvent: e });\n\n return;\n }\n\n el.setAttribute('dismissing', '');\n awaitExit(el, () => {\n el.removeAttribute('dismissing');\n el.setAttribute('dismissed', '');\n announce('Alert dismissed');\n emit('dismiss', { originalEvent: e });\n });\n };\n\n const alertRole = () => {\n if (props.embedded.value) return 'presentation';\n\n return props.color.value === 'error' ? 'alert' : 'status';\n };\n\n return html`\n <span\n class=\"sr-announce\"\n aria-live=\"polite\"\n aria-atomic=\"true\"\n ref=\"${(ref: HTMLElement) => {\n announceEl = ref;\n }}\"></span>\n <div class=\"alert\" role=\"${alertRole}\" part=\"alert\">\n <span class=\"icon\" part=\"icon\" aria-hidden=\"true\" ?hidden=\"${() => !slots.has('icon').value}\">\n <slot name=\"icon\"></slot>\n </span>\n <div class=\"header\" part=\"header\" ?hidden=\"${() => !props.heading.value}\">\n <span class=\"heading\" part=\"heading\">${props.heading}</span>\n <span class=\"meta\" part=\"meta\">\n <slot name=\"meta\"></slot>\n </span>\n </div>\n <div class=\"body\" part=\"body\">\n <div class=\"content\" part=\"content\">\n <slot></slot>\n </div>\n </div>\n <div class=\"actions\" part=\"actions\" ?hidden=\"${() => !slots.has('actions').value}\">\n <slot name=\"actions\"></slot>\n </div>\n <button\n class=\"close\"\n part=\"close\"\n type=\"button\"\n aria-label=\"Dismiss alert\"\n ?hidden=\"${() => !props.dismissible.value}\"\n @click=\"${handleDismiss}\">\n <ore-icon name=\"x\" size=\"16\" stroke-width=\"2.5\" aria-hidden=\"true\"></ore-icon>\n </button>\n </div>\n `;\n },\n styles: [\n colorThemeMixin,\n coarsePointerMixin,\n reducedMotionMixin,\n roundedVariantMixin,\n forcedColorsMixin,\n sizeVariantMixin({\n lg: { '--_padding': 'var(--size-4) var(--size-5)', fontSize: 'var(--text-base)', gap: 'var(--size-3-5)' },\n sm: { '--_padding': 'var(--size-2) var(--size-3)', fontSize: 'var(--text-xs)', gap: 'var(--size-2)' },\n }),\n componentStyles,\n ],\n});\n"],"mappings":"iXA8FA,IAAa,EAAY,aACzB,EAAA,EAAA,OAAA,CAAsB,EAAW,CAC/B,MAAO,CACL,GAAG,EAAA,eACH,GAAG,EAAA,cACH,GAAG,EAAA,gBACH,SAAU,EAAA,KAAK,KAAK,EAAK,EACzB,YAAa,EAAA,KAAK,KAAK,EAAK,EAC5B,SAAU,EAAA,KAAK,KAAK,EAAK,EACzB,QAAS,EAAA,KAAK,OAAO,EACrB,WAAY,EAAA,KAAK,KAAK,EAAK,EAC3B,QAAS,EAAA,KAAK,OAAsC,CACtD,EACA,MAAM,EAAO,CACX,IAAM,GAAA,EAAK,EAAA,QAAA,CAAQ,EACb,GAAA,EAAO,EAAA,QAAA,CAAwB,EAC/B,GAAA,EAAQ,EAAA,SAAA,CAAS,EAEnB,EAAiC,KAE/B,EAAY,GAAoB,CAC/B,IAEL,EAAW,YAAc,GACzB,0BAA4B,CACtB,IAAY,EAAW,YAAc,EAC3C,CAAC,EACH,EA0BA,MAAO,GAAA,IAAI;;;;;eAKC,GAAqB,CAC3B,EAAa,CACf,EAAE;qCAZA,EAAM,SAAS,MAAc,eAE1B,EAAM,MAAM,QAAU,QAAU,QAAU,SAWZ;yEACgC,CAAC,EAAM,IAAI,MAAM,CAAC,CAAC,MAAM;;;yDAGzC,CAAC,EAAM,QAAQ,MAAM;iDAC/B,EAAM,QAAQ;;;;;;;;;;2DAUF,CAAC,EAAM,IAAI,SAAS,CAAC,CAAC,MAAM;;;;;;;;yBAQ9D,CAAC,EAAM,YAAY,MAAM;oBAvDzB,GAAkB,CAClC,KAAM,YAAY,MAEvB,IAAI,EAAM,SAAS,MAAO,CACxB,EAAK,UAAW,CAAE,cAAe,CAAE,CAAC,EAEpC,MACF,CAEA,EAAG,aAAa,aAAc,EAAE,EAChC,EAAA,UAAU,MAAU,CAClB,EAAG,gBAAgB,YAAY,EAC/B,EAAG,aAAa,YAAa,EAAE,EAC/B,EAAS,iBAAiB,EAC1B,EAAK,UAAW,CAAE,cAAe,CAAE,CAAC,CACtC,CAAC,CARD,CASF,EAwC8B;;;;KAKhC,EACA,OAAQ,CACN,EAAA,gBACA,EAAA,mBACA,EAAA,mBACA,EAAA,oBACA,EAAA,kBACA,EAAA,iBAAiB,CACf,GAAI,CAAE,aAAc,8BAA+B,SAAU,mBAAoB,IAAK,iBAAkB,EACxG,GAAI,CAAE,aAAc,8BAA+B,SAAU,iBAAkB,IAAK,eAAgB,CACtG,CAAC,EACD,EAAA,OACF,CACF,CAAC"}
package/dist/alert.js CHANGED
@@ -14,6 +14,7 @@ d(_, {
14
14
  ...o,
15
15
  accented: m.bool(!1),
16
16
  dismissible: m.bool(!1),
17
+ embedded: m.bool(!1),
17
18
  heading: m.string(),
18
19
  horizontal: m.bool(!1),
19
20
  variant: m.string()
@@ -32,7 +33,7 @@ d(_, {
32
33
  ref="${(e) => {
33
34
  i = e;
34
35
  }}"></span>
35
- <div class="alert" role="${() => e.color.value === "error" ? "alert" : "status"}" part="alert">
36
+ <div class="alert" role="${() => e.embedded.value ? "presentation" : e.color.value === "error" ? "alert" : "status"}" part="alert">
36
37
  <span class="icon" part="icon" aria-hidden="true" ?hidden="${() => !r.has("icon").value}">
37
38
  <slot name="icon"></slot>
38
39
  </span>
@@ -57,9 +58,15 @@ d(_, {
57
58
  aria-label="Dismiss alert"
58
59
  ?hidden="${() => !e.dismissible.value}"
59
60
  @click="${(r) => {
60
- e.dismissible.value && (t.setAttribute("dismissing", ""), l(t, () => {
61
- t.removeAttribute("dismissing"), t.setAttribute("dismissed", ""), a("Alert dismissed"), n("dismiss", { originalEvent: r });
62
- }));
61
+ if (e.dismissible.value) {
62
+ if (e.embedded.value) {
63
+ n("dismiss", { originalEvent: r });
64
+ return;
65
+ }
66
+ t.setAttribute("dismissing", ""), l(t, () => {
67
+ t.removeAttribute("dismissing"), t.setAttribute("dismissed", ""), a("Alert dismissed"), n("dismiss", { originalEvent: r });
68
+ });
69
+ }
63
70
  }}">
64
71
  <ore-icon name="x" size="16" stroke-width="2.5" aria-hidden="true"></ore-icon>
65
72
  </button>
package/dist/alert.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"alert.js","names":[],"sources":["../src/feedback/alert/alert.ts"],"sourcesContent":["import { define, getHost, html, prop, useEmit, useSlots } from '@vielzeug/ore';\n\nimport type { ComponentSize, RoundedSize, ThemeColor } from '../../types';\n\nimport '../../content/icon/icon';\nimport { awaitExit } from '../../overlay/shared/await-exit';\nimport { roundableBundle, sizableBundle, themableBundle } from '../../shared';\nimport {\n coarsePointerMixin,\n colorThemeMixin,\n forcedColorsMixin,\n reducedMotionMixin,\n roundedVariantMixin,\n sizeVariantMixin,\n} from '../../styles';\nimport componentStyles from './alert.css?inline';\n\nexport type OreAlertEvents = {\n dismiss: { originalEvent: MouseEvent };\n};\n\nexport type OreAlertProps = {\n /** Show a left accent border (for flat/bordered variants) */\n accented?: boolean;\n /** Theme color */\n color?: ThemeColor;\n /** Show a dismissible (×) button */\n dismissible?: boolean;\n /** Heading text shown above the content */\n heading?: string;\n /** Position action buttons to the right instead of below */\n horizontal?: boolean;\n /** Border radius */\n rounded?: RoundedSize | '';\n /** Alert size */\n size?: ComponentSize;\n /** Visual style variant */\n variant?: 'solid' | 'flat' | 'bordered';\n};\n\n/**\n * A status/feedback banner with optional heading, icon slot, and dismiss button.\n *\n * @element ore-alert\n *\n * @attr {string} color - Theme color: 'primary' | 'secondary' | 'info' | 'success' | 'warning' | 'error'\n * @attr {string} variant - Visual style: 'flat' | 'solid' | 'bordered'\n * @attr {string} size - Size: 'sm' | 'md' | 'lg'\n * @attr {string} rounded - Border radius: 'none' | 'sm' | 'md' | 'lg' | 'xl' | '2xl' | '3xl' | 'full'\n * @attr {string} heading - Bold heading text above the content\n * @attr {boolean} dismissible - Show a close (×) button\n * @attr {boolean} accented - Add a left accent border (flat/bordered only)\n * @attr {boolean} horizontal - Position action buttons to the right instead of below\n *\n * @fires dismiss - Fired when the alert is dismissed. detail: { originalEvent: MouseEvent }\n *\n * @slot - Default slot for the alert message content\n * @slot icon - Icon on the left side\n * @slot meta - Metadata displayed alongside the heading (lighter, right-aligned)\n * @slot actions - Action buttons shown below the message\n *\n * @cssprop --alert-bg - Background color\n * @cssprop --alert-color - Text/icon color\n * @cssprop --alert-border-color - Border color\n * @cssprop --alert-shadow - Box shadow\n * @cssprop --alert-radius - Border radius\n * @cssprop --alert-padding - Padding\n * @cssprop --alert-gap - Gap between icon, body, and close button\n * @cssprop --alert-font-size - Font size\n *\n * @part alert - Alert root container.\n * @part icon - Icon container.\n * @part header - Header container.\n * @part heading - Heading text element.\n * @part meta - Meta text container.\n * @part body - Body content container.\n * @part content - Content container.\n * @part actions - Actions slot container.\n * @part close - Close action control.\n * @example\n * ```html\n * <ore-alert color=\"success\">Your changes have been saved.</ore-alert>\n * <ore-alert color=\"error\" variant=\"solid\" dismissible heading=\"Something went wrong\">\n * Please try again later.\n * </ore-alert>\n * ```\n */\nexport const ALERT_TAG = 'ore-alert' as const;\ndefine<OreAlertProps>(ALERT_TAG, {\n props: {\n ...themableBundle,\n ...sizableBundle,\n ...roundableBundle,\n accented: prop.bool(false),\n dismissible: prop.bool(false),\n heading: prop.string(),\n horizontal: prop.bool(false),\n variant: prop.string<'solid' | 'flat' | 'bordered'>(),\n },\n setup(props) {\n const el = getHost();\n const emit = useEmit<OreAlertEvents>();\n const slots = useSlots();\n\n let announceEl: HTMLElement | null = null;\n\n const announce = (message: string) => {\n if (!announceEl) return;\n\n announceEl.textContent = '';\n requestAnimationFrame(() => {\n if (announceEl) announceEl.textContent = message;\n });\n };\n\n const handleDismiss = (e: MouseEvent) => {\n if (!props.dismissible.value) return;\n\n el.setAttribute('dismissing', '');\n awaitExit(el, () => {\n el.removeAttribute('dismissing');\n el.setAttribute('dismissed', '');\n announce('Alert dismissed');\n emit('dismiss', { originalEvent: e });\n });\n };\n\n const alertRole = () => (props.color.value === 'error' ? 'alert' : 'status');\n\n return html`\n <span\n class=\"sr-announce\"\n aria-live=\"polite\"\n aria-atomic=\"true\"\n ref=\"${(ref: HTMLElement) => {\n announceEl = ref;\n }}\"></span>\n <div class=\"alert\" role=\"${alertRole}\" part=\"alert\">\n <span class=\"icon\" part=\"icon\" aria-hidden=\"true\" ?hidden=\"${() => !slots.has('icon').value}\">\n <slot name=\"icon\"></slot>\n </span>\n <div class=\"header\" part=\"header\" ?hidden=\"${() => !props.heading.value}\">\n <span class=\"heading\" part=\"heading\">${props.heading}</span>\n <span class=\"meta\" part=\"meta\">\n <slot name=\"meta\"></slot>\n </span>\n </div>\n <div class=\"body\" part=\"body\">\n <div class=\"content\" part=\"content\">\n <slot></slot>\n </div>\n </div>\n <div class=\"actions\" part=\"actions\" ?hidden=\"${() => !slots.has('actions').value}\">\n <slot name=\"actions\"></slot>\n </div>\n <button\n class=\"close\"\n part=\"close\"\n type=\"button\"\n aria-label=\"Dismiss alert\"\n ?hidden=\"${() => !props.dismissible.value}\"\n @click=\"${handleDismiss}\">\n <ore-icon name=\"x\" size=\"16\" stroke-width=\"2.5\" aria-hidden=\"true\"></ore-icon>\n </button>\n </div>\n `;\n },\n styles: [\n colorThemeMixin,\n coarsePointerMixin,\n reducedMotionMixin,\n roundedVariantMixin,\n forcedColorsMixin,\n sizeVariantMixin({\n lg: { '--_padding': 'var(--size-4) var(--size-5)', fontSize: 'var(--text-base)', gap: 'var(--size-3-5)' },\n sm: { '--_padding': 'var(--size-2) var(--size-3)', fontSize: 'var(--text-xs)', gap: 'var(--size-2)' },\n }),\n componentStyles,\n ],\n});\n"],"mappings":";;;;;;;;AAuFA,IAAa,IAAY;AACzB,EAAsB,GAAW;CAC/B,OAAO;EACL,GAAG;EACH,GAAG;EACH,GAAG;EACH,UAAU,EAAK,KAAK,EAAK;EACzB,aAAa,EAAK,KAAK,EAAK;EAC5B,SAAS,EAAK,OAAO;EACrB,YAAY,EAAK,KAAK,EAAK;EAC3B,SAAS,EAAK,OAAsC;CACtD;CACA,MAAM,GAAO;EACX,IAAM,IAAK,EAAQ,GACb,IAAO,EAAwB,GAC/B,IAAQ,EAAS,GAEnB,IAAiC,MAE/B,KAAY,MAAoB;GAC/B,MAEL,EAAW,cAAc,IACzB,4BAA4B;IAC1B,AAAI,MAAY,EAAW,cAAc;GAC3C,CAAC;EACH;EAgBA,OAAO,CAAI;;;;;gBAKC,MAAqB;GAC3B,IAAa;EACf,EAAE;uCATmB,EAAM,MAAM,UAAU,UAAU,UAAU,SAU5B;2EACgC,CAAC,EAAM,IAAI,MAAM,CAAC,CAAC,MAAM;;;2DAGzC,CAAC,EAAM,QAAQ,MAAM;iDAC/B,EAAM,QAAQ;;;;;;;;;;6DAUF,CAAC,EAAM,IAAI,SAAS,CAAC,CAAC,MAAM;;;;;;;;2BAQ9D,CAAC,EAAM,YAAY,MAAM;qBA7CzB,MAAkB;GAClC,EAAM,YAAY,UAEvB,EAAG,aAAa,cAAc,EAAE,GAChC,EAAU,SAAU;IAIlB,AAHA,EAAG,gBAAgB,YAAY,GAC/B,EAAG,aAAa,aAAa,EAAE,GAC/B,EAAS,iBAAiB,GAC1B,EAAK,WAAW,EAAE,eAAe,EAAE,CAAC;GACtC,CAAC;EACH,EAoC8B;;;;;CAKhC;CACA,QAAQ;EACN;EACA;EACA;EACA;EACA;EACA,EAAiB;GACf,IAAI;IAAE,cAAc;IAA+B,UAAU;IAAoB,KAAK;GAAkB;GACxG,IAAI;IAAE,cAAc;IAA+B,UAAU;IAAkB,KAAK;GAAgB;EACtG,CAAC;EACD;CACF;AACF,CAAC"}
1
+ {"version":3,"file":"alert.js","names":[],"sources":["../src/feedback/alert/alert.ts"],"sourcesContent":["import { define, getHost, html, prop, useEmit, useSlots } from '@vielzeug/ore';\n\nimport type { ComponentSize, RoundedSize, ThemeColor } from '../../types';\n\nimport '../../content/icon/icon';\nimport { awaitExit } from '../../overlay/shared/await-exit';\nimport { roundableBundle, sizableBundle, themableBundle } from '../../shared';\nimport {\n coarsePointerMixin,\n colorThemeMixin,\n forcedColorsMixin,\n reducedMotionMixin,\n roundedVariantMixin,\n sizeVariantMixin,\n} from '../../styles';\nimport componentStyles from './alert.css?inline';\n\nexport type OreAlertEvents = {\n dismiss: { originalEvent: MouseEvent };\n};\n\nexport type OreAlertProps = {\n /** Show a left accent border (for flat/bordered variants) */\n accented?: boolean;\n /** Theme color */\n color?: ThemeColor;\n /** Show a dismissible (×) button */\n dismissible?: boolean;\n /**\n * Render as a passive surface inside a host that owns lifecycle and\n * announcements (for example `ore-toast`): no live-region role, and the\n * close button only emits `dismiss` instead of hiding the alert.\n */\n embedded?: boolean;\n /** Heading text shown above the content */\n heading?: string;\n /** Position action buttons to the right instead of below */\n horizontal?: boolean;\n /** Border radius */\n rounded?: RoundedSize | '';\n /** Alert size */\n size?: ComponentSize;\n /** Visual style variant */\n variant?: 'solid' | 'flat' | 'bordered';\n};\n\n/**\n * A status/feedback banner with optional heading, icon slot, and dismiss button.\n *\n * @element ore-alert\n *\n * @attr {string} color - Theme color: 'primary' | 'secondary' | 'info' | 'success' | 'warning' | 'error'\n * @attr {string} variant - Visual style: 'flat' | 'solid' | 'bordered'\n * @attr {string} size - Size: 'sm' | 'md' | 'lg'\n * @attr {string} rounded - Border radius: 'none' | 'sm' | 'md' | 'lg' | 'xl' | '2xl' | '3xl' | 'full'\n * @attr {string} heading - Bold heading text above the content\n * @attr {boolean} dismissible - Show a close (×) button\n * @attr {boolean} embedded - Passive surface for a host that owns lifecycle and announcements; the close button only emits `dismiss`\n * @attr {boolean} accented - Add a left accent border (flat/bordered only)\n * @attr {boolean} horizontal - Position action buttons to the right instead of below\n *\n * @fires dismiss - Fired when the alert is dismissed (after the exit animation, or immediately when `embedded`). detail: { originalEvent: MouseEvent }\n *\n * @slot - Default slot for the alert message content\n * @slot icon - Icon on the left side\n * @slot meta - Metadata displayed alongside the heading (lighter, right-aligned)\n * @slot actions - Action buttons shown below the message\n *\n * @cssprop --alert-bg - Background color\n * @cssprop --alert-color - Text/icon color\n * @cssprop --alert-border-color - Border color\n * @cssprop --alert-shadow - Box shadow\n * @cssprop --alert-radius - Border radius\n * @cssprop --alert-padding - Padding\n * @cssprop --alert-gap - Gap between icon, body, and close button\n * @cssprop --alert-font-size - Font size\n *\n * @part alert - Alert root container.\n * @part icon - Icon container.\n * @part header - Header container.\n * @part heading - Heading text element.\n * @part meta - Meta text container.\n * @part body - Body content container.\n * @part content - Content container.\n * @part actions - Actions slot container.\n * @part close - Close action control.\n * @example\n * ```html\n * <ore-alert color=\"success\">Your changes have been saved.</ore-alert>\n * <ore-alert color=\"error\" variant=\"solid\" dismissible heading=\"Something went wrong\">\n * Please try again later.\n * </ore-alert>\n * ```\n */\nexport const ALERT_TAG = 'ore-alert' as const;\ndefine<OreAlertProps>(ALERT_TAG, {\n props: {\n ...themableBundle,\n ...sizableBundle,\n ...roundableBundle,\n accented: prop.bool(false),\n dismissible: prop.bool(false),\n embedded: prop.bool(false),\n heading: prop.string(),\n horizontal: prop.bool(false),\n variant: prop.string<'solid' | 'flat' | 'bordered'>(),\n },\n setup(props) {\n const el = getHost();\n const emit = useEmit<OreAlertEvents>();\n const slots = useSlots();\n\n let announceEl: HTMLElement | null = null;\n\n const announce = (message: string) => {\n if (!announceEl) return;\n\n announceEl.textContent = '';\n requestAnimationFrame(() => {\n if (announceEl) announceEl.textContent = message;\n });\n };\n\n const handleDismiss = (e: MouseEvent) => {\n if (!props.dismissible.value) return;\n\n if (props.embedded.value) {\n emit('dismiss', { originalEvent: e });\n\n return;\n }\n\n el.setAttribute('dismissing', '');\n awaitExit(el, () => {\n el.removeAttribute('dismissing');\n el.setAttribute('dismissed', '');\n announce('Alert dismissed');\n emit('dismiss', { originalEvent: e });\n });\n };\n\n const alertRole = () => {\n if (props.embedded.value) return 'presentation';\n\n return props.color.value === 'error' ? 'alert' : 'status';\n };\n\n return html`\n <span\n class=\"sr-announce\"\n aria-live=\"polite\"\n aria-atomic=\"true\"\n ref=\"${(ref: HTMLElement) => {\n announceEl = ref;\n }}\"></span>\n <div class=\"alert\" role=\"${alertRole}\" part=\"alert\">\n <span class=\"icon\" part=\"icon\" aria-hidden=\"true\" ?hidden=\"${() => !slots.has('icon').value}\">\n <slot name=\"icon\"></slot>\n </span>\n <div class=\"header\" part=\"header\" ?hidden=\"${() => !props.heading.value}\">\n <span class=\"heading\" part=\"heading\">${props.heading}</span>\n <span class=\"meta\" part=\"meta\">\n <slot name=\"meta\"></slot>\n </span>\n </div>\n <div class=\"body\" part=\"body\">\n <div class=\"content\" part=\"content\">\n <slot></slot>\n </div>\n </div>\n <div class=\"actions\" part=\"actions\" ?hidden=\"${() => !slots.has('actions').value}\">\n <slot name=\"actions\"></slot>\n </div>\n <button\n class=\"close\"\n part=\"close\"\n type=\"button\"\n aria-label=\"Dismiss alert\"\n ?hidden=\"${() => !props.dismissible.value}\"\n @click=\"${handleDismiss}\">\n <ore-icon name=\"x\" size=\"16\" stroke-width=\"2.5\" aria-hidden=\"true\"></ore-icon>\n </button>\n </div>\n `;\n },\n styles: [\n colorThemeMixin,\n coarsePointerMixin,\n reducedMotionMixin,\n roundedVariantMixin,\n forcedColorsMixin,\n sizeVariantMixin({\n lg: { '--_padding': 'var(--size-4) var(--size-5)', fontSize: 'var(--text-base)', gap: 'var(--size-3-5)' },\n sm: { '--_padding': 'var(--size-2) var(--size-3)', fontSize: 'var(--text-xs)', gap: 'var(--size-2)' },\n }),\n componentStyles,\n ],\n});\n"],"mappings":";;;;;;;;AA8FA,IAAa,IAAY;AACzB,EAAsB,GAAW;CAC/B,OAAO;EACL,GAAG;EACH,GAAG;EACH,GAAG;EACH,UAAU,EAAK,KAAK,EAAK;EACzB,aAAa,EAAK,KAAK,EAAK;EAC5B,UAAU,EAAK,KAAK,EAAK;EACzB,SAAS,EAAK,OAAO;EACrB,YAAY,EAAK,KAAK,EAAK;EAC3B,SAAS,EAAK,OAAsC;CACtD;CACA,MAAM,GAAO;EACX,IAAM,IAAK,EAAQ,GACb,IAAO,EAAwB,GAC/B,IAAQ,EAAS,GAEnB,IAAiC,MAE/B,KAAY,MAAoB;GAC/B,MAEL,EAAW,cAAc,IACzB,4BAA4B;IAC1B,AAAI,MAAY,EAAW,cAAc;GAC3C,CAAC;EACH;EA0BA,OAAO,CAAI;;;;;gBAKC,MAAqB;GAC3B,IAAa;EACf,EAAE;uCAZA,EAAM,SAAS,QAAc,iBAE1B,EAAM,MAAM,UAAU,UAAU,UAAU,SAWZ;2EACgC,CAAC,EAAM,IAAI,MAAM,CAAC,CAAC,MAAM;;;2DAGzC,CAAC,EAAM,QAAQ,MAAM;iDAC/B,EAAM,QAAQ;;;;;;;;;;6DAUF,CAAC,EAAM,IAAI,SAAS,CAAC,CAAC,MAAM;;;;;;;;2BAQ9D,CAAC,EAAM,YAAY,MAAM;qBAvDzB,MAAkB;GAClC,MAAM,YAAY,OAEvB;QAAI,EAAM,SAAS,OAAO;KACxB,EAAK,WAAW,EAAE,eAAe,EAAE,CAAC;KAEpC;IACF;IAGA,AADA,EAAG,aAAa,cAAc,EAAE,GAChC,EAAU,SAAU;KAIlB,AAHA,EAAG,gBAAgB,YAAY,GAC/B,EAAG,aAAa,aAAa,EAAE,GAC/B,EAAS,iBAAiB,GAC1B,EAAK,WAAW,EAAE,eAAe,EAAE,CAAC;IACtC,CAAC;GARD;EASF,EAwC8B;;;;;CAKhC;CACA,QAAQ;EACN;EACA;EACA;EACA;EACA;EACA,EAAiB;GACf,IAAI;IAAE,cAAc;IAA+B,UAAU;IAAoB,KAAK;GAAkB;GACxG,IAAI;IAAE,cAAc;IAA+B,UAAU;IAAkB,KAAK;GAAgB;EACtG,CAAC;EACD;CACF;AACF,CAAC"}
@@ -1,2 +1,2 @@
1
- const e=require("./direction.cjs");let t=require("@vielzeug/orbit");var n=e=>e.endsWith(`-start`)?e.replace(`-start`,`-end`):e.endsWith(`-end`)?e.replace(`-end`,`-start`):e;function r({boundary:r,getDir:i,getFloating:a,getPlacement:o,getReference:s,matchWidth:c=!0,offsetPx:l=0,padding:u=6,useClippingAncestor:d=!0}){function f(){let t=o?.()??`bottom-start`;return(i?.()??e.elementDirection(s()))===`rtl`?n(t):t}function p(){let e=s(),n=a();if(!e||!n)return;let i=f(),o=r??(d?(0,t.getClippingAncestorRect)(n):void 0),p=(0,t.computePosition)(e,n,{boundary:o,middleware:[...l?[(0,t.offset)(l)]:[],(0,t.flip)({padding:u}),(0,t.shift)({padding:u}),...c?[(0,t.size)({padding:u})]:[]],placement:i});n.style.left=`${p.x}px`,n.style.top=`${p.y}px`;let m=n.style.transition,h=n.style.transform;n.style.transition=`none`,n.style.transform=`none`;let g=n.getBoundingClientRect(),_=p.x-g.left,v=p.y-g.top;n.style.transition=m,n.style.transform=h,(_!==0||v!==0)&&(n.style.left=`${p.x+_}px`,n.style.top=`${p.y+v}px`),c&&(n.style.width=`${e.getBoundingClientRect().width}px`)}function m(){let e=s(),n=a();return!e||!n?()=>{}:(0,t.autoUpdate)(e,n,p)}return{floating:a,reference:s,startAutoUpdate:m,update:p}}exports.createDropdownPositioner=r;
1
+ const e=require("./direction.cjs");let t=require("@vielzeug/orbit");var n=e=>e.endsWith(`-start`)?e.replace(`-start`,`-end`):e.endsWith(`-end`)?e.replace(`-end`,`-start`):e;function r({boundary:r,getDir:i,getFloating:a,getPlacement:o,getReference:s,matchWidth:c=!0,offsetPx:l=0,padding:u=6,useClippingAncestor:d=!0}){function f(){let t=o?.()??`bottom-start`;return(i?.()??e.elementDirection(s()))===`rtl`?n(t):t}function p(){let e=s(),n=a();if(!e||!n)return;let i=f(),o=r??(d?(0,t.getClippingAncestorRect)(n):void 0),p=e.getBoundingClientRect().width,m=0;c&&(n.style.left=`0px`,n.style.width=`auto`,n.style.minWidth=`${p}px`,m=n.getBoundingClientRect().width);let h=(0,t.computePosition)(e,n,{boundary:o,middleware:[...l?[(0,t.offset)(l)]:[],(0,t.flip)({padding:u}),(0,t.shift)({padding:u}),...c?[(0,t.size)({padding:u})]:[]],placement:i});n.style.left=`${h.x}px`,n.style.top=`${h.y}px`;let g=n.style.transition,_=n.style.transform;n.style.transition=`none`,n.style.transform=`none`;let v=n.getBoundingClientRect(),y=h.x-v.left,b=h.y-v.top;if(n.style.transition=g,n.style.transform=_,(y!==0||b!==0)&&(n.style.left=`${h.x+y}px`,n.style.top=`${h.y+b}px`),c){let e=h.middlewareData.size?.availableWidth??1/0;n.style.width=`${Math.min(Math.max(m,p),e)}px`}}function m(){let e=s(),n=a();return!e||!n?()=>{}:(0,t.autoUpdate)(e,n,p)}return{floating:a,reference:s,startAutoUpdate:m,update:p}}exports.createDropdownPositioner=r;
2
2
  //# sourceMappingURL=positioner.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"positioner.cjs","names":[],"sources":["../../src/core/positioner.ts"],"sourcesContent":["import {\n autoUpdate,\n computePosition,\n flip,\n getClippingAncestorRect,\n offset,\n type Placement,\n type Rect,\n shift,\n size,\n} from '@vielzeug/orbit';\n\nimport { elementDirection } from './direction';\n\n// ── Overlay positioner contract ──────────────────────────────────────────────\n\nexport type OverlayPositioner = {\n floating: () => HTMLElement | null;\n reference: () => HTMLElement | null;\n /** Start continuous position auto-updates. Returns a stop function. */\n startAutoUpdate?: () => () => void;\n update: () => void;\n};\n\n// ── Dropdown positioner ───────────────────────────────────────────────────────\n\nexport type DropdownPositionerOptions = {\n /**\n * Clipping boundary for flip/shift/size — the edges the dropdown must stay within.\n * Defaults to the nearest ancestor that actually clips content (`overflow:\n * hidden|auto|scroll|clip`, e.g. a dialog panel), auto-detected per update via\n * `getClippingAncestorRect()`, intersected with the viewport. Without this, flip/shift only\n * avoid the full page viewport — a dropdown can be \"in view\" by that measure while still\n * visibly overhanging a much smaller container it's actually nested in. Pass an explicit\n * boundary to override auto-detection.\n */\n boundary?: Element | Rect;\n /**\n * Returns the text direction used for placement mirroring. Defaults to the resolved\n * direction of the reference element (nearest `dir=\"ltr\"|\"rtl\"` ancestor, falling back to\n * computed style) — most callers never need to pass this explicitly.\n */\n getDir?: () => 'ltr' | 'rtl';\n /** Getter for the floating (dropdown panel) element. */\n getFloating: () => HTMLElement | null;\n /** Getter for the current placement. Defaults to 'bottom-start' (RTL-aware: flips to 'bottom-end'). */\n getPlacement?: () => Placement;\n /** Getter for the reference (trigger) element. */\n getReference: () => HTMLElement | null;\n /** Whether to match the floating element width to the reference. Default: true */\n matchWidth?: boolean;\n /** Additional offset in pixels between reference and floating element. Default: 0 */\n offsetPx?: number;\n /** Padding (px) used by flip, shift, and size middleware. Default: 6 */\n padding?: number;\n /**\n * Whether to constrain placement to a clipping ancestor when `boundary` is unset. Default:\n * true. Disable for native Popover API panels, which render in the top layer.\n */\n useClippingAncestor?: boolean;\n};\n\n/** Mirrors a start/end placement for RTL layouts. */\nconst rtlPlacement = (p: Placement): Placement => {\n if (p.endsWith('-start')) return p.replace('-start', '-end') as Placement;\n\n if (p.endsWith('-end')) return p.replace('-end', '-start') as Placement;\n\n return p;\n};\n\n/**\n * Creates an Orbit-powered positioner for dropdown overlays (select, combobox, menu).\n *\n * Uses flip + shift + optional width-matching middleware. Supports continuous\n * auto-update for repositioning on scroll or resize.\n *\n * RTL-aware: start/end placements are automatically mirrored so dropdowns open on the\n * correct side, based on `getDir` (or the reference element's resolved direction when\n * `getDir` is omitted).\n */\nexport function createDropdownPositioner({\n boundary,\n getDir,\n getFloating,\n getPlacement,\n getReference,\n matchWidth = true,\n offsetPx = 0,\n padding = 6,\n useClippingAncestor = true,\n}: DropdownPositionerOptions): OverlayPositioner {\n function resolvedPlacement(): Placement {\n const base = getPlacement?.() ?? 'bottom-start';\n const dir = getDir?.() ?? elementDirection(getReference());\n\n return dir === 'rtl' ? rtlPlacement(base) : base;\n }\n\n function updatePosition(): void {\n const ref = getReference();\n const floating = getFloating();\n\n if (!ref || !floating) return;\n\n const placement = resolvedPlacement();\n // Defaults to `undefined` (meaning \"auto-detect\"), not the detected value itself —\n // re-running the detection on every update (rather than once, at positioner-creation time)\n // matters because the dialog/scroll-container an instance is rendered into can change\n // across opens (e.g. the same `<ore-select>` reused in different dialogs), and the detected\n // ancestor's own rect can change between opens even when it's the same element (a resized\n // dialog).\n const resolvedBoundary = boundary ?? (useClippingAncestor ? getClippingAncestorRect(floating) : undefined);\n\n const result = computePosition(ref, floating, {\n boundary: resolvedBoundary,\n middleware: [\n ...(offsetPx ? [offset(offsetPx)] : []),\n flip({ padding }),\n shift({ padding }),\n ...(matchWidth ? [size({ padding })] : []),\n ],\n placement,\n });\n\n floating.style.left = `${result.x}px`;\n floating.style.top = `${result.y}px`;\n\n // `computePosition()` assumes `left`/`top` resolve relative to the viewport (correct for\n // `position: fixed`) — true almost always, but not when some ancestor establishes a\n // containing block for fixed descendants (a non-`none` `transform`, even a visually-identity\n // one left over from an entrance transition that never resets to `none` at rest is a real,\n // easy-to-hit case — a modal dialog's panel, say). Analytically detecting *that* ancestor via\n // `getContainingBlock()` and pre-subtracting its rect turned out to be unreliable in\n // practice: browsers don't apply the \"ancestor transform traps fixed descendants\" rule\n // uniformly across every element nested under it in every case we tested — some floating\n // elements in the exact same subtree needed the correction and some didn't, for reasons we\n // couldn't fully pin down. Measuring what actually happened after writing `left`/`top`\n // and correcting only the observed gap sidesteps that: it's a no-op when the browser already\n // put the element where asked, and it fixes the same class of bug the moment there's a real\n // mismatch, regardless of which specific ancestor (if any) turns out to be responsible.\n //\n // The measurement itself must not be fooled by the floating element's *own* transform,\n // though: dropdowns/popovers here all use a `transform: translateY(...)` entrance transition\n // driven by a `[data-open]` attribute + `@starting-style` — right at open, before the\n // transition has visibly progressed, `getBoundingClientRect()` can still reflect that\n // starting offset (a handful of px, matching `--overlay-enter-translate-y`), which this\n // correction would otherwise misread as a permanent ancestor-driven mismatch and bake in\n // forever. Temporarily neutralizing the floating element's own transform for the measurement\n // (and restoring it before anything paints, so there's no visible flash) isolates the\n // correction to ancestor-driven offsets only, which is the only thing it's meant to fix.\n const prevTransition = floating.style.transition;\n const prevTransform = floating.style.transform;\n\n floating.style.transition = 'none';\n floating.style.transform = 'none';\n\n const actual = floating.getBoundingClientRect();\n const deltaX = result.x - actual.left;\n const deltaY = result.y - actual.top;\n\n floating.style.transition = prevTransition;\n floating.style.transform = prevTransform;\n\n if (deltaX !== 0 || deltaY !== 0) {\n floating.style.left = `${result.x + deltaX}px`;\n floating.style.top = `${result.y + deltaY}px`;\n }\n\n if (matchWidth) {\n floating.style.width = `${ref.getBoundingClientRect().width}px`;\n }\n }\n\n function startAutoUpdate(): () => void {\n const ref = getReference();\n const floating = getFloating();\n\n if (!ref || !floating) return () => {};\n\n return autoUpdate(ref, floating, updatePosition);\n }\n\n return { floating: getFloating, reference: getReference, startAutoUpdate, update: updatePosition };\n}\n"],"mappings":"oEA+DA,IAAM,EAAgB,GAChB,EAAE,SAAS,QAAQ,EAAU,EAAE,QAAQ,SAAU,MAAM,EAEvD,EAAE,SAAS,MAAM,EAAU,EAAE,QAAQ,OAAQ,QAAQ,EAElD,EAaT,SAAgB,EAAyB,CACvC,WACA,SACA,cACA,eACA,eACA,aAAa,GACb,WAAW,EACX,UAAU,EACV,sBAAsB,IACyB,CAC/C,SAAS,GAA+B,CACtC,IAAM,EAAO,IAAe,GAAK,eAGjC,OAFY,IAAS,GAAK,EAAA,iBAAiB,EAAa,CAAC,KAE1C,MAAQ,EAAa,CAAI,EAAI,CAC9C,CAEA,SAAS,GAAuB,CAC9B,IAAM,EAAM,EAAa,EACnB,EAAW,EAAY,EAE7B,GAAI,CAAC,GAAO,CAAC,EAAU,OAEvB,IAAM,EAAY,EAAkB,EAO9B,EAAmB,IAAa,GAAA,EAAsB,EAAA,wBAAA,CAAwB,CAAQ,EAAI,IAAA,IAE1F,GAAA,EAAS,EAAA,gBAAA,CAAgB,EAAK,EAAU,CAC5C,SAAU,EACV,WAAY,CACV,GAAI,EAAW,EAAA,EAAC,EAAA,OAAA,CAAO,CAAQ,CAAC,EAAI,CAAC,GACrC,EAAA,EAAA,KAAA,CAAK,CAAE,SAAQ,CAAC,GAChB,EAAA,EAAA,MAAA,CAAM,CAAE,SAAQ,CAAC,EACjB,GAAI,EAAa,EAAA,EAAC,EAAA,KAAA,CAAK,CAAE,SAAQ,CAAC,CAAC,EAAI,CAAC,CAC1C,EACA,WACF,CAAC,EAED,EAAS,MAAM,KAAO,GAAG,EAAO,EAAE,IAClC,EAAS,MAAM,IAAM,GAAG,EAAO,EAAE,IAyBjC,IAAM,EAAiB,EAAS,MAAM,WAChC,EAAgB,EAAS,MAAM,UAErC,EAAS,MAAM,WAAa,OAC5B,EAAS,MAAM,UAAY,OAE3B,IAAM,EAAS,EAAS,sBAAsB,EACxC,EAAS,EAAO,EAAI,EAAO,KAC3B,EAAS,EAAO,EAAI,EAAO,IAEjC,EAAS,MAAM,WAAa,EAC5B,EAAS,MAAM,UAAY,GAEvB,IAAW,GAAK,IAAW,KAC7B,EAAS,MAAM,KAAO,GAAG,EAAO,EAAI,EAAO,IAC3C,EAAS,MAAM,IAAM,GAAG,EAAO,EAAI,EAAO,KAGxC,IACF,EAAS,MAAM,MAAQ,GAAG,EAAI,sBAAsB,CAAC,CAAC,MAAM,IAEhE,CAEA,SAAS,GAA8B,CACrC,IAAM,EAAM,EAAa,EACnB,EAAW,EAAY,EAI7B,MAFI,CAAC,GAAO,CAAC,MAAuB,CAAC,GAErC,EAAO,EAAA,WAAA,CAAW,EAAK,EAAU,CAAc,CACjD,CAEA,MAAO,CAAE,SAAU,EAAa,UAAW,EAAc,kBAAiB,OAAQ,CAAe,CACnG"}
1
+ {"version":3,"file":"positioner.cjs","names":[],"sources":["../../src/core/positioner.ts"],"sourcesContent":["import {\n autoUpdate,\n computePosition,\n flip,\n getClippingAncestorRect,\n offset,\n type Placement,\n type Rect,\n type SizeData,\n shift,\n size,\n} from '@vielzeug/orbit';\n\nimport { elementDirection } from './direction';\n\n// ── Overlay positioner contract ──────────────────────────────────────────────\n\nexport type OverlayPositioner = {\n floating: () => HTMLElement | null;\n reference: () => HTMLElement | null;\n /** Start continuous position auto-updates. Returns a stop function. */\n startAutoUpdate?: () => () => void;\n update: () => void;\n};\n\n// ── Dropdown positioner ───────────────────────────────────────────────────────\n\nexport type DropdownPositionerOptions = {\n /**\n * Clipping boundary for flip/shift/size — the edges the dropdown must stay within.\n * Defaults to the nearest ancestor that actually clips content (`overflow:\n * hidden|auto|scroll|clip`, e.g. a dialog panel), auto-detected per update via\n * `getClippingAncestorRect()`, intersected with the viewport. Without this, flip/shift only\n * avoid the full page viewport — a dropdown can be \"in view\" by that measure while still\n * visibly overhanging a much smaller container it's actually nested in. Pass an explicit\n * boundary to override auto-detection.\n */\n boundary?: Element | Rect;\n /**\n * Returns the text direction used for placement mirroring. Defaults to the resolved\n * direction of the reference element (nearest `dir=\"ltr\"|\"rtl\"` ancestor, falling back to\n * computed style) — most callers never need to pass this explicitly.\n */\n getDir?: () => 'ltr' | 'rtl';\n /** Getter for the floating (dropdown panel) element. */\n getFloating: () => HTMLElement | null;\n /** Getter for the current placement. Defaults to 'bottom-start' (RTL-aware: flips to 'bottom-end'). */\n getPlacement?: () => Placement;\n /** Getter for the reference (trigger) element. */\n getReference: () => HTMLElement | null;\n /**\n * Whether to size the floating element from the reference: at least as wide as the reference,\n * growing to fit its own content, capped by the boundary. Default: true\n */\n matchWidth?: boolean;\n /** Additional offset in pixels between reference and floating element. Default: 0 */\n offsetPx?: number;\n /** Padding (px) used by flip, shift, and size middleware. Default: 6 */\n padding?: number;\n /**\n * Whether to constrain placement to a clipping ancestor when `boundary` is unset. Default:\n * true. Disable for native Popover API panels, which render in the top layer.\n */\n useClippingAncestor?: boolean;\n};\n\n/** Mirrors a start/end placement for RTL layouts. */\nconst rtlPlacement = (p: Placement): Placement => {\n if (p.endsWith('-start')) return p.replace('-start', '-end') as Placement;\n\n if (p.endsWith('-end')) return p.replace('-end', '-start') as Placement;\n\n return p;\n};\n\n/**\n * Creates an Orbit-powered positioner for dropdown overlays (select, combobox, menu).\n *\n * Uses flip + shift + optional width-matching middleware. Supports continuous\n * auto-update for repositioning on scroll or resize.\n *\n * RTL-aware: start/end placements are automatically mirrored so dropdowns open on the\n * correct side, based on `getDir` (or the reference element's resolved direction when\n * `getDir` is omitted).\n */\nexport function createDropdownPositioner({\n boundary,\n getDir,\n getFloating,\n getPlacement,\n getReference,\n matchWidth = true,\n offsetPx = 0,\n padding = 6,\n useClippingAncestor = true,\n}: DropdownPositionerOptions): OverlayPositioner {\n function resolvedPlacement(): Placement {\n const base = getPlacement?.() ?? 'bottom-start';\n const dir = getDir?.() ?? elementDirection(getReference());\n\n return dir === 'rtl' ? rtlPlacement(base) : base;\n }\n\n function updatePosition(): void {\n const ref = getReference();\n const floating = getFloating();\n\n if (!ref || !floating) return;\n\n const placement = resolvedPlacement();\n // Defaults to `undefined` (meaning \"auto-detect\"), not the detected value itself —\n // re-running the detection on every update (rather than once, at positioner-creation time)\n // matters because the dialog/scroll-container an instance is rendered into can change\n // across opens (e.g. the same `<ore-select>` reused in different dialogs), and the detected\n // ancestor's own rect can change between opens even when it's the same element (a resized\n // dialog).\n const resolvedBoundary = boundary ?? (useClippingAncestor ? getClippingAncestorRect(floating) : undefined);\n\n const refWidth = ref.getBoundingClientRect().width;\n let naturalWidth = 0;\n\n if (matchWidth) {\n // Measure the panel at its natural content width, floored at the reference width, so\n // `computePosition()` places (and `shift` keeps inside the boundary) the box that will\n // actually render. `left: 0` frees the measurement from the shrink-to-fit clamp a previous\n // `left` would impose on a `position: fixed` element near the right edge.\n floating.style.left = '0px';\n floating.style.width = 'auto';\n floating.style.minWidth = `${refWidth}px`;\n naturalWidth = floating.getBoundingClientRect().width;\n }\n\n const result = computePosition(ref, floating, {\n boundary: resolvedBoundary,\n middleware: [\n ...(offsetPx ? [offset(offsetPx)] : []),\n flip({ padding }),\n shift({ padding }),\n ...(matchWidth ? [size({ padding })] : []),\n ],\n placement,\n });\n\n floating.style.left = `${result.x}px`;\n floating.style.top = `${result.y}px`;\n\n // `computePosition()` assumes `left`/`top` resolve relative to the viewport (correct for\n // `position: fixed`) — true almost always, but not when some ancestor establishes a\n // containing block for fixed descendants (a non-`none` `transform`, even a visually-identity\n // one left over from an entrance transition that never resets to `none` at rest is a real,\n // easy-to-hit case — a modal dialog's panel, say). Analytically detecting *that* ancestor via\n // `getContainingBlock()` and pre-subtracting its rect turned out to be unreliable in\n // practice: browsers don't apply the \"ancestor transform traps fixed descendants\" rule\n // uniformly across every element nested under it in every case we tested — some floating\n // elements in the exact same subtree needed the correction and some didn't, for reasons we\n // couldn't fully pin down. Measuring what actually happened after writing `left`/`top`\n // and correcting only the observed gap sidesteps that: it's a no-op when the browser already\n // put the element where asked, and it fixes the same class of bug the moment there's a real\n // mismatch, regardless of which specific ancestor (if any) turns out to be responsible.\n //\n // The measurement itself must not be fooled by the floating element's *own* transform,\n // though: dropdowns/popovers here all use a `transform: translateY(...)` entrance transition\n // driven by a `[data-open]` attribute + `@starting-style` — right at open, before the\n // transition has visibly progressed, `getBoundingClientRect()` can still reflect that\n // starting offset (a handful of px, matching `--overlay-enter-translate-y`), which this\n // correction would otherwise misread as a permanent ancestor-driven mismatch and bake in\n // forever. Temporarily neutralizing the floating element's own transform for the measurement\n // (and restoring it before anything paints, so there's no visible flash) isolates the\n // correction to ancestor-driven offsets only, which is the only thing it's meant to fix.\n const prevTransition = floating.style.transition;\n const prevTransform = floating.style.transform;\n\n floating.style.transition = 'none';\n floating.style.transform = 'none';\n\n const actual = floating.getBoundingClientRect();\n const deltaX = result.x - actual.left;\n const deltaY = result.y - actual.top;\n\n floating.style.transition = prevTransition;\n floating.style.transform = prevTransform;\n\n if (deltaX !== 0 || deltaY !== 0) {\n floating.style.left = `${result.x + deltaX}px`;\n floating.style.top = `${result.y + deltaY}px`;\n }\n\n if (matchWidth) {\n const available =\n (result.middlewareData.size as SizeData | undefined)?.availableWidth ?? Number.POSITIVE_INFINITY;\n // A panel measured while not yet rendered reports 0 — fall back to the reference width.\n floating.style.width = `${Math.min(Math.max(naturalWidth, refWidth), available)}px`;\n }\n }\n\n function startAutoUpdate(): () => void {\n const ref = getReference();\n const floating = getFloating();\n\n if (!ref || !floating) return () => {};\n\n return autoUpdate(ref, floating, updatePosition);\n }\n\n return { floating: getFloating, reference: getReference, startAutoUpdate, update: updatePosition };\n}\n"],"mappings":"oEAmEA,IAAM,EAAgB,GAChB,EAAE,SAAS,QAAQ,EAAU,EAAE,QAAQ,SAAU,MAAM,EAEvD,EAAE,SAAS,MAAM,EAAU,EAAE,QAAQ,OAAQ,QAAQ,EAElD,EAaT,SAAgB,EAAyB,CACvC,WACA,SACA,cACA,eACA,eACA,aAAa,GACb,WAAW,EACX,UAAU,EACV,sBAAsB,IACyB,CAC/C,SAAS,GAA+B,CACtC,IAAM,EAAO,IAAe,GAAK,eAGjC,OAFY,IAAS,GAAK,EAAA,iBAAiB,EAAa,CAAC,KAE1C,MAAQ,EAAa,CAAI,EAAI,CAC9C,CAEA,SAAS,GAAuB,CAC9B,IAAM,EAAM,EAAa,EACnB,EAAW,EAAY,EAE7B,GAAI,CAAC,GAAO,CAAC,EAAU,OAEvB,IAAM,EAAY,EAAkB,EAO9B,EAAmB,IAAa,GAAA,EAAsB,EAAA,wBAAA,CAAwB,CAAQ,EAAI,IAAA,IAE1F,EAAW,EAAI,sBAAsB,CAAC,CAAC,MACzC,EAAe,EAEf,IAKF,EAAS,MAAM,KAAO,MACtB,EAAS,MAAM,MAAQ,OACvB,EAAS,MAAM,SAAW,GAAG,EAAS,IACtC,EAAe,EAAS,sBAAsB,CAAC,CAAC,OAGlD,IAAM,GAAA,EAAS,EAAA,gBAAA,CAAgB,EAAK,EAAU,CAC5C,SAAU,EACV,WAAY,CACV,GAAI,EAAW,EAAA,EAAC,EAAA,OAAA,CAAO,CAAQ,CAAC,EAAI,CAAC,GACrC,EAAA,EAAA,KAAA,CAAK,CAAE,SAAQ,CAAC,GAChB,EAAA,EAAA,MAAA,CAAM,CAAE,SAAQ,CAAC,EACjB,GAAI,EAAa,EAAA,EAAC,EAAA,KAAA,CAAK,CAAE,SAAQ,CAAC,CAAC,EAAI,CAAC,CAC1C,EACA,WACF,CAAC,EAED,EAAS,MAAM,KAAO,GAAG,EAAO,EAAE,IAClC,EAAS,MAAM,IAAM,GAAG,EAAO,EAAE,IAyBjC,IAAM,EAAiB,EAAS,MAAM,WAChC,EAAgB,EAAS,MAAM,UAErC,EAAS,MAAM,WAAa,OAC5B,EAAS,MAAM,UAAY,OAE3B,IAAM,EAAS,EAAS,sBAAsB,EACxC,EAAS,EAAO,EAAI,EAAO,KAC3B,EAAS,EAAO,EAAI,EAAO,IAUjC,GARA,EAAS,MAAM,WAAa,EAC5B,EAAS,MAAM,UAAY,GAEvB,IAAW,GAAK,IAAW,KAC7B,EAAS,MAAM,KAAO,GAAG,EAAO,EAAI,EAAO,IAC3C,EAAS,MAAM,IAAM,GAAG,EAAO,EAAI,EAAO,KAGxC,EAAY,CACd,IAAM,EACH,EAAO,eAAe,MAA+B,gBAAkB,IAE1E,EAAS,MAAM,MAAQ,GAAG,KAAK,IAAI,KAAK,IAAI,EAAc,CAAQ,EAAG,CAAS,EAAE,GAClF,CACF,CAEA,SAAS,GAA8B,CACrC,IAAM,EAAM,EAAa,EACnB,EAAW,EAAY,EAI7B,MAFI,CAAC,GAAO,CAAC,MAAuB,CAAC,GAErC,EAAO,EAAA,WAAA,CAAW,EAAK,EAAU,CAAc,CACjD,CAEA,MAAO,CAAE,SAAU,EAAa,UAAW,EAAc,kBAAiB,OAAQ,CAAe,CACnG"}
@@ -29,7 +29,10 @@ export type DropdownPositionerOptions = {
29
29
  getPlacement?: () => Placement;
30
30
  /** Getter for the reference (trigger) element. */
31
31
  getReference: () => HTMLElement | null;
32
- /** Whether to match the floating element width to the reference. Default: true */
32
+ /**
33
+ * Whether to size the floating element from the reference: at least as wide as the reference,
34
+ * growing to fit its own content, capped by the boundary. Default: true
35
+ */
33
36
  matchWidth?: boolean;
34
37
  /** Additional offset in pixels between reference and floating element. Default: 0 */
35
38
  offsetPx?: number;
@@ -1 +1 @@
1
- {"version":3,"file":"positioner.d.ts","sourceRoot":"","sources":["../../src/core/positioner.ts"],"names":[],"mappings":"AAAA,OAAO,EAML,KAAK,SAAS,EACd,KAAK,IAAI,EAGV,MAAM,iBAAiB,CAAC;AAMzB,MAAM,MAAM,iBAAiB,GAAG;IAC9B,QAAQ,EAAE,MAAM,WAAW,GAAG,IAAI,CAAC;IACnC,SAAS,EAAE,MAAM,WAAW,GAAG,IAAI,CAAC;IACpC,uEAAuE;IACvE,eAAe,CAAC,EAAE,MAAM,MAAM,IAAI,CAAC;IACnC,MAAM,EAAE,MAAM,IAAI,CAAC;CACpB,CAAC;AAIF,MAAM,MAAM,yBAAyB,GAAG;IACtC;;;;;;;;OAQG;IACH,QAAQ,CAAC,EAAE,OAAO,GAAG,IAAI,CAAC;IAC1B;;;;OAIG;IACH,MAAM,CAAC,EAAE,MAAM,KAAK,GAAG,KAAK,CAAC;IAC7B,wDAAwD;IACxD,WAAW,EAAE,MAAM,WAAW,GAAG,IAAI,CAAC;IACtC,uGAAuG;IACvG,YAAY,CAAC,EAAE,MAAM,SAAS,CAAC;IAC/B,kDAAkD;IAClD,YAAY,EAAE,MAAM,WAAW,GAAG,IAAI,CAAC;IACvC,kFAAkF;IAClF,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,qFAAqF;IACrF,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,wEAAwE;IACxE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;OAGG;IACH,mBAAmB,CAAC,EAAE,OAAO,CAAC;CAC/B,CAAC;AAWF;;;;;;;;;GASG;AACH,wBAAgB,wBAAwB,CAAC,EACvC,QAAQ,EACR,MAAM,EACN,WAAW,EACX,YAAY,EACZ,YAAY,EACZ,UAAiB,EACjB,QAAY,EACZ,OAAW,EACX,mBAA0B,GAC3B,EAAE,yBAAyB,GAAG,iBAAiB,CA6F/C"}
1
+ {"version":3,"file":"positioner.d.ts","sourceRoot":"","sources":["../../src/core/positioner.ts"],"names":[],"mappings":"AAAA,OAAO,EAML,KAAK,SAAS,EACd,KAAK,IAAI,EAIV,MAAM,iBAAiB,CAAC;AAMzB,MAAM,MAAM,iBAAiB,GAAG;IAC9B,QAAQ,EAAE,MAAM,WAAW,GAAG,IAAI,CAAC;IACnC,SAAS,EAAE,MAAM,WAAW,GAAG,IAAI,CAAC;IACpC,uEAAuE;IACvE,eAAe,CAAC,EAAE,MAAM,MAAM,IAAI,CAAC;IACnC,MAAM,EAAE,MAAM,IAAI,CAAC;CACpB,CAAC;AAIF,MAAM,MAAM,yBAAyB,GAAG;IACtC;;;;;;;;OAQG;IACH,QAAQ,CAAC,EAAE,OAAO,GAAG,IAAI,CAAC;IAC1B;;;;OAIG;IACH,MAAM,CAAC,EAAE,MAAM,KAAK,GAAG,KAAK,CAAC;IAC7B,wDAAwD;IACxD,WAAW,EAAE,MAAM,WAAW,GAAG,IAAI,CAAC;IACtC,uGAAuG;IACvG,YAAY,CAAC,EAAE,MAAM,SAAS,CAAC;IAC/B,kDAAkD;IAClD,YAAY,EAAE,MAAM,WAAW,GAAG,IAAI,CAAC;IACvC;;;OAGG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,qFAAqF;IACrF,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,wEAAwE;IACxE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;OAGG;IACH,mBAAmB,CAAC,EAAE,OAAO,CAAC;CAC/B,CAAC;AAWF;;;;;;;;;GASG;AACH,wBAAgB,wBAAwB,CAAC,EACvC,QAAQ,EACR,MAAM,EACN,WAAW,EACX,YAAY,EACZ,YAAY,EACZ,UAAiB,EACjB,QAAY,EACZ,OAAW,EACX,mBAA0B,GAC3B,EAAE,yBAAyB,GAAG,iBAAiB,CA8G/C"}
@@ -10,7 +10,9 @@ function l({ boundary: l, getDir: u, getFloating: d, getPlacement: f, getReferen
10
10
  function y() {
11
11
  let e = p(), t = d();
12
12
  if (!e || !t) return;
13
- let c = v(), u = l ?? (_ ? i(t) : void 0), f = n(e, t, {
13
+ let c = v(), u = l ?? (_ ? i(t) : void 0), f = e.getBoundingClientRect().width, y = 0;
14
+ m && (t.style.left = "0px", t.style.width = "auto", t.style.minWidth = `${f}px`, y = t.getBoundingClientRect().width);
15
+ let b = n(e, t, {
14
16
  boundary: u,
15
17
  middleware: [
16
18
  ...h ? [a(h)] : [],
@@ -20,11 +22,14 @@ function l({ boundary: l, getDir: u, getFloating: d, getPlacement: f, getReferen
20
22
  ],
21
23
  placement: c
22
24
  });
23
- t.style.left = `${f.x}px`, t.style.top = `${f.y}px`;
24
- let y = t.style.transition, b = t.style.transform;
25
+ t.style.left = `${b.x}px`, t.style.top = `${b.y}px`;
26
+ let x = t.style.transition, S = t.style.transform;
25
27
  t.style.transition = "none", t.style.transform = "none";
26
- let x = t.getBoundingClientRect(), S = f.x - x.left, C = f.y - x.top;
27
- t.style.transition = y, t.style.transform = b, (S !== 0 || C !== 0) && (t.style.left = `${f.x + S}px`, t.style.top = `${f.y + C}px`), m && (t.style.width = `${e.getBoundingClientRect().width}px`);
28
+ let C = t.getBoundingClientRect(), w = b.x - C.left, T = b.y - C.top;
29
+ if (t.style.transition = x, t.style.transform = S, (w !== 0 || T !== 0) && (t.style.left = `${b.x + w}px`, t.style.top = `${b.y + T}px`), m) {
30
+ let e = b.middlewareData.size?.availableWidth ?? Infinity;
31
+ t.style.width = `${Math.min(Math.max(y, f), e)}px`;
32
+ }
28
33
  }
29
34
  function b() {
30
35
  let e = p(), n = d();
@@ -1 +1 @@
1
- {"version":3,"file":"positioner.js","names":[],"sources":["../../src/core/positioner.ts"],"sourcesContent":["import {\n autoUpdate,\n computePosition,\n flip,\n getClippingAncestorRect,\n offset,\n type Placement,\n type Rect,\n shift,\n size,\n} from '@vielzeug/orbit';\n\nimport { elementDirection } from './direction';\n\n// ── Overlay positioner contract ──────────────────────────────────────────────\n\nexport type OverlayPositioner = {\n floating: () => HTMLElement | null;\n reference: () => HTMLElement | null;\n /** Start continuous position auto-updates. Returns a stop function. */\n startAutoUpdate?: () => () => void;\n update: () => void;\n};\n\n// ── Dropdown positioner ───────────────────────────────────────────────────────\n\nexport type DropdownPositionerOptions = {\n /**\n * Clipping boundary for flip/shift/size — the edges the dropdown must stay within.\n * Defaults to the nearest ancestor that actually clips content (`overflow:\n * hidden|auto|scroll|clip`, e.g. a dialog panel), auto-detected per update via\n * `getClippingAncestorRect()`, intersected with the viewport. Without this, flip/shift only\n * avoid the full page viewport — a dropdown can be \"in view\" by that measure while still\n * visibly overhanging a much smaller container it's actually nested in. Pass an explicit\n * boundary to override auto-detection.\n */\n boundary?: Element | Rect;\n /**\n * Returns the text direction used for placement mirroring. Defaults to the resolved\n * direction of the reference element (nearest `dir=\"ltr\"|\"rtl\"` ancestor, falling back to\n * computed style) — most callers never need to pass this explicitly.\n */\n getDir?: () => 'ltr' | 'rtl';\n /** Getter for the floating (dropdown panel) element. */\n getFloating: () => HTMLElement | null;\n /** Getter for the current placement. Defaults to 'bottom-start' (RTL-aware: flips to 'bottom-end'). */\n getPlacement?: () => Placement;\n /** Getter for the reference (trigger) element. */\n getReference: () => HTMLElement | null;\n /** Whether to match the floating element width to the reference. Default: true */\n matchWidth?: boolean;\n /** Additional offset in pixels between reference and floating element. Default: 0 */\n offsetPx?: number;\n /** Padding (px) used by flip, shift, and size middleware. Default: 6 */\n padding?: number;\n /**\n * Whether to constrain placement to a clipping ancestor when `boundary` is unset. Default:\n * true. Disable for native Popover API panels, which render in the top layer.\n */\n useClippingAncestor?: boolean;\n};\n\n/** Mirrors a start/end placement for RTL layouts. */\nconst rtlPlacement = (p: Placement): Placement => {\n if (p.endsWith('-start')) return p.replace('-start', '-end') as Placement;\n\n if (p.endsWith('-end')) return p.replace('-end', '-start') as Placement;\n\n return p;\n};\n\n/**\n * Creates an Orbit-powered positioner for dropdown overlays (select, combobox, menu).\n *\n * Uses flip + shift + optional width-matching middleware. Supports continuous\n * auto-update for repositioning on scroll or resize.\n *\n * RTL-aware: start/end placements are automatically mirrored so dropdowns open on the\n * correct side, based on `getDir` (or the reference element's resolved direction when\n * `getDir` is omitted).\n */\nexport function createDropdownPositioner({\n boundary,\n getDir,\n getFloating,\n getPlacement,\n getReference,\n matchWidth = true,\n offsetPx = 0,\n padding = 6,\n useClippingAncestor = true,\n}: DropdownPositionerOptions): OverlayPositioner {\n function resolvedPlacement(): Placement {\n const base = getPlacement?.() ?? 'bottom-start';\n const dir = getDir?.() ?? elementDirection(getReference());\n\n return dir === 'rtl' ? rtlPlacement(base) : base;\n }\n\n function updatePosition(): void {\n const ref = getReference();\n const floating = getFloating();\n\n if (!ref || !floating) return;\n\n const placement = resolvedPlacement();\n // Defaults to `undefined` (meaning \"auto-detect\"), not the detected value itself —\n // re-running the detection on every update (rather than once, at positioner-creation time)\n // matters because the dialog/scroll-container an instance is rendered into can change\n // across opens (e.g. the same `<ore-select>` reused in different dialogs), and the detected\n // ancestor's own rect can change between opens even when it's the same element (a resized\n // dialog).\n const resolvedBoundary = boundary ?? (useClippingAncestor ? getClippingAncestorRect(floating) : undefined);\n\n const result = computePosition(ref, floating, {\n boundary: resolvedBoundary,\n middleware: [\n ...(offsetPx ? [offset(offsetPx)] : []),\n flip({ padding }),\n shift({ padding }),\n ...(matchWidth ? [size({ padding })] : []),\n ],\n placement,\n });\n\n floating.style.left = `${result.x}px`;\n floating.style.top = `${result.y}px`;\n\n // `computePosition()` assumes `left`/`top` resolve relative to the viewport (correct for\n // `position: fixed`) — true almost always, but not when some ancestor establishes a\n // containing block for fixed descendants (a non-`none` `transform`, even a visually-identity\n // one left over from an entrance transition that never resets to `none` at rest is a real,\n // easy-to-hit case — a modal dialog's panel, say). Analytically detecting *that* ancestor via\n // `getContainingBlock()` and pre-subtracting its rect turned out to be unreliable in\n // practice: browsers don't apply the \"ancestor transform traps fixed descendants\" rule\n // uniformly across every element nested under it in every case we tested — some floating\n // elements in the exact same subtree needed the correction and some didn't, for reasons we\n // couldn't fully pin down. Measuring what actually happened after writing `left`/`top`\n // and correcting only the observed gap sidesteps that: it's a no-op when the browser already\n // put the element where asked, and it fixes the same class of bug the moment there's a real\n // mismatch, regardless of which specific ancestor (if any) turns out to be responsible.\n //\n // The measurement itself must not be fooled by the floating element's *own* transform,\n // though: dropdowns/popovers here all use a `transform: translateY(...)` entrance transition\n // driven by a `[data-open]` attribute + `@starting-style` — right at open, before the\n // transition has visibly progressed, `getBoundingClientRect()` can still reflect that\n // starting offset (a handful of px, matching `--overlay-enter-translate-y`), which this\n // correction would otherwise misread as a permanent ancestor-driven mismatch and bake in\n // forever. Temporarily neutralizing the floating element's own transform for the measurement\n // (and restoring it before anything paints, so there's no visible flash) isolates the\n // correction to ancestor-driven offsets only, which is the only thing it's meant to fix.\n const prevTransition = floating.style.transition;\n const prevTransform = floating.style.transform;\n\n floating.style.transition = 'none';\n floating.style.transform = 'none';\n\n const actual = floating.getBoundingClientRect();\n const deltaX = result.x - actual.left;\n const deltaY = result.y - actual.top;\n\n floating.style.transition = prevTransition;\n floating.style.transform = prevTransform;\n\n if (deltaX !== 0 || deltaY !== 0) {\n floating.style.left = `${result.x + deltaX}px`;\n floating.style.top = `${result.y + deltaY}px`;\n }\n\n if (matchWidth) {\n floating.style.width = `${ref.getBoundingClientRect().width}px`;\n }\n }\n\n function startAutoUpdate(): () => void {\n const ref = getReference();\n const floating = getFloating();\n\n if (!ref || !floating) return () => {};\n\n return autoUpdate(ref, floating, updatePosition);\n }\n\n return { floating: getFloating, reference: getReference, startAutoUpdate, update: updatePosition };\n}\n"],"mappings":";;;AA+DA,IAAM,KAAgB,MAChB,EAAE,SAAS,QAAQ,IAAU,EAAE,QAAQ,UAAU,MAAM,IAEvD,EAAE,SAAS,MAAM,IAAU,EAAE,QAAQ,QAAQ,QAAQ,IAElD;AAaT,SAAgB,EAAyB,EACvC,aACA,WACA,gBACA,iBACA,iBACA,gBAAa,IACb,cAAW,GACX,aAAU,GACV,yBAAsB,MACyB;CAC/C,SAAS,IAA+B;EACtC,IAAM,IAAO,IAAe,KAAK;EAGjC,QAFY,IAAS,KAAK,EAAiB,EAAa,CAAC,OAE1C,QAAQ,EAAa,CAAI,IAAI;CAC9C;CAEA,SAAS,IAAuB;EAC9B,IAAM,IAAM,EAAa,GACnB,IAAW,EAAY;EAE7B,IAAI,CAAC,KAAO,CAAC,GAAU;EAEvB,IAAM,IAAY,EAAkB,GAO9B,IAAmB,MAAa,IAAsB,EAAwB,CAAQ,IAAI,KAAA,IAE1F,IAAS,EAAgB,GAAK,GAAU;GAC5C,UAAU;GACV,YAAY;IACV,GAAI,IAAW,CAAC,EAAO,CAAQ,CAAC,IAAI,CAAC;IACrC,EAAK,EAAE,WAAQ,CAAC;IAChB,EAAM,EAAE,WAAQ,CAAC;IACjB,GAAI,IAAa,CAAC,EAAK,EAAE,WAAQ,CAAC,CAAC,IAAI,CAAC;GAC1C;GACA;EACF,CAAC;EAGD,AADA,EAAS,MAAM,OAAO,GAAG,EAAO,EAAE,KAClC,EAAS,MAAM,MAAM,GAAG,EAAO,EAAE;EAyBjC,IAAM,IAAiB,EAAS,MAAM,YAChC,IAAgB,EAAS,MAAM;EAGrC,AADA,EAAS,MAAM,aAAa,QAC5B,EAAS,MAAM,YAAY;EAE3B,IAAM,IAAS,EAAS,sBAAsB,GACxC,IAAS,EAAO,IAAI,EAAO,MAC3B,IAAS,EAAO,IAAI,EAAO;EAUjC,AARA,EAAS,MAAM,aAAa,GAC5B,EAAS,MAAM,YAAY,IAEvB,MAAW,KAAK,MAAW,OAC7B,EAAS,MAAM,OAAO,GAAG,EAAO,IAAI,EAAO,KAC3C,EAAS,MAAM,MAAM,GAAG,EAAO,IAAI,EAAO,MAGxC,MACF,EAAS,MAAM,QAAQ,GAAG,EAAI,sBAAsB,CAAC,CAAC,MAAM;CAEhE;CAEA,SAAS,IAA8B;EACrC,IAAM,IAAM,EAAa,GACnB,IAAW,EAAY;EAI7B,OAFI,CAAC,KAAO,CAAC,UAAuB,CAAC,IAE9B,EAAW,GAAK,GAAU,CAAc;CACjD;CAEA,OAAO;EAAE,UAAU;EAAa,WAAW;EAAc;EAAiB,QAAQ;CAAe;AACnG"}
1
+ {"version":3,"file":"positioner.js","names":[],"sources":["../../src/core/positioner.ts"],"sourcesContent":["import {\n autoUpdate,\n computePosition,\n flip,\n getClippingAncestorRect,\n offset,\n type Placement,\n type Rect,\n type SizeData,\n shift,\n size,\n} from '@vielzeug/orbit';\n\nimport { elementDirection } from './direction';\n\n// ── Overlay positioner contract ──────────────────────────────────────────────\n\nexport type OverlayPositioner = {\n floating: () => HTMLElement | null;\n reference: () => HTMLElement | null;\n /** Start continuous position auto-updates. Returns a stop function. */\n startAutoUpdate?: () => () => void;\n update: () => void;\n};\n\n// ── Dropdown positioner ───────────────────────────────────────────────────────\n\nexport type DropdownPositionerOptions = {\n /**\n * Clipping boundary for flip/shift/size — the edges the dropdown must stay within.\n * Defaults to the nearest ancestor that actually clips content (`overflow:\n * hidden|auto|scroll|clip`, e.g. a dialog panel), auto-detected per update via\n * `getClippingAncestorRect()`, intersected with the viewport. Without this, flip/shift only\n * avoid the full page viewport — a dropdown can be \"in view\" by that measure while still\n * visibly overhanging a much smaller container it's actually nested in. Pass an explicit\n * boundary to override auto-detection.\n */\n boundary?: Element | Rect;\n /**\n * Returns the text direction used for placement mirroring. Defaults to the resolved\n * direction of the reference element (nearest `dir=\"ltr\"|\"rtl\"` ancestor, falling back to\n * computed style) — most callers never need to pass this explicitly.\n */\n getDir?: () => 'ltr' | 'rtl';\n /** Getter for the floating (dropdown panel) element. */\n getFloating: () => HTMLElement | null;\n /** Getter for the current placement. Defaults to 'bottom-start' (RTL-aware: flips to 'bottom-end'). */\n getPlacement?: () => Placement;\n /** Getter for the reference (trigger) element. */\n getReference: () => HTMLElement | null;\n /**\n * Whether to size the floating element from the reference: at least as wide as the reference,\n * growing to fit its own content, capped by the boundary. Default: true\n */\n matchWidth?: boolean;\n /** Additional offset in pixels between reference and floating element. Default: 0 */\n offsetPx?: number;\n /** Padding (px) used by flip, shift, and size middleware. Default: 6 */\n padding?: number;\n /**\n * Whether to constrain placement to a clipping ancestor when `boundary` is unset. Default:\n * true. Disable for native Popover API panels, which render in the top layer.\n */\n useClippingAncestor?: boolean;\n};\n\n/** Mirrors a start/end placement for RTL layouts. */\nconst rtlPlacement = (p: Placement): Placement => {\n if (p.endsWith('-start')) return p.replace('-start', '-end') as Placement;\n\n if (p.endsWith('-end')) return p.replace('-end', '-start') as Placement;\n\n return p;\n};\n\n/**\n * Creates an Orbit-powered positioner for dropdown overlays (select, combobox, menu).\n *\n * Uses flip + shift + optional width-matching middleware. Supports continuous\n * auto-update for repositioning on scroll or resize.\n *\n * RTL-aware: start/end placements are automatically mirrored so dropdowns open on the\n * correct side, based on `getDir` (or the reference element's resolved direction when\n * `getDir` is omitted).\n */\nexport function createDropdownPositioner({\n boundary,\n getDir,\n getFloating,\n getPlacement,\n getReference,\n matchWidth = true,\n offsetPx = 0,\n padding = 6,\n useClippingAncestor = true,\n}: DropdownPositionerOptions): OverlayPositioner {\n function resolvedPlacement(): Placement {\n const base = getPlacement?.() ?? 'bottom-start';\n const dir = getDir?.() ?? elementDirection(getReference());\n\n return dir === 'rtl' ? rtlPlacement(base) : base;\n }\n\n function updatePosition(): void {\n const ref = getReference();\n const floating = getFloating();\n\n if (!ref || !floating) return;\n\n const placement = resolvedPlacement();\n // Defaults to `undefined` (meaning \"auto-detect\"), not the detected value itself —\n // re-running the detection on every update (rather than once, at positioner-creation time)\n // matters because the dialog/scroll-container an instance is rendered into can change\n // across opens (e.g. the same `<ore-select>` reused in different dialogs), and the detected\n // ancestor's own rect can change between opens even when it's the same element (a resized\n // dialog).\n const resolvedBoundary = boundary ?? (useClippingAncestor ? getClippingAncestorRect(floating) : undefined);\n\n const refWidth = ref.getBoundingClientRect().width;\n let naturalWidth = 0;\n\n if (matchWidth) {\n // Measure the panel at its natural content width, floored at the reference width, so\n // `computePosition()` places (and `shift` keeps inside the boundary) the box that will\n // actually render. `left: 0` frees the measurement from the shrink-to-fit clamp a previous\n // `left` would impose on a `position: fixed` element near the right edge.\n floating.style.left = '0px';\n floating.style.width = 'auto';\n floating.style.minWidth = `${refWidth}px`;\n naturalWidth = floating.getBoundingClientRect().width;\n }\n\n const result = computePosition(ref, floating, {\n boundary: resolvedBoundary,\n middleware: [\n ...(offsetPx ? [offset(offsetPx)] : []),\n flip({ padding }),\n shift({ padding }),\n ...(matchWidth ? [size({ padding })] : []),\n ],\n placement,\n });\n\n floating.style.left = `${result.x}px`;\n floating.style.top = `${result.y}px`;\n\n // `computePosition()` assumes `left`/`top` resolve relative to the viewport (correct for\n // `position: fixed`) — true almost always, but not when some ancestor establishes a\n // containing block for fixed descendants (a non-`none` `transform`, even a visually-identity\n // one left over from an entrance transition that never resets to `none` at rest is a real,\n // easy-to-hit case — a modal dialog's panel, say). Analytically detecting *that* ancestor via\n // `getContainingBlock()` and pre-subtracting its rect turned out to be unreliable in\n // practice: browsers don't apply the \"ancestor transform traps fixed descendants\" rule\n // uniformly across every element nested under it in every case we tested — some floating\n // elements in the exact same subtree needed the correction and some didn't, for reasons we\n // couldn't fully pin down. Measuring what actually happened after writing `left`/`top`\n // and correcting only the observed gap sidesteps that: it's a no-op when the browser already\n // put the element where asked, and it fixes the same class of bug the moment there's a real\n // mismatch, regardless of which specific ancestor (if any) turns out to be responsible.\n //\n // The measurement itself must not be fooled by the floating element's *own* transform,\n // though: dropdowns/popovers here all use a `transform: translateY(...)` entrance transition\n // driven by a `[data-open]` attribute + `@starting-style` — right at open, before the\n // transition has visibly progressed, `getBoundingClientRect()` can still reflect that\n // starting offset (a handful of px, matching `--overlay-enter-translate-y`), which this\n // correction would otherwise misread as a permanent ancestor-driven mismatch and bake in\n // forever. Temporarily neutralizing the floating element's own transform for the measurement\n // (and restoring it before anything paints, so there's no visible flash) isolates the\n // correction to ancestor-driven offsets only, which is the only thing it's meant to fix.\n const prevTransition = floating.style.transition;\n const prevTransform = floating.style.transform;\n\n floating.style.transition = 'none';\n floating.style.transform = 'none';\n\n const actual = floating.getBoundingClientRect();\n const deltaX = result.x - actual.left;\n const deltaY = result.y - actual.top;\n\n floating.style.transition = prevTransition;\n floating.style.transform = prevTransform;\n\n if (deltaX !== 0 || deltaY !== 0) {\n floating.style.left = `${result.x + deltaX}px`;\n floating.style.top = `${result.y + deltaY}px`;\n }\n\n if (matchWidth) {\n const available =\n (result.middlewareData.size as SizeData | undefined)?.availableWidth ?? Number.POSITIVE_INFINITY;\n // A panel measured while not yet rendered reports 0 — fall back to the reference width.\n floating.style.width = `${Math.min(Math.max(naturalWidth, refWidth), available)}px`;\n }\n }\n\n function startAutoUpdate(): () => void {\n const ref = getReference();\n const floating = getFloating();\n\n if (!ref || !floating) return () => {};\n\n return autoUpdate(ref, floating, updatePosition);\n }\n\n return { floating: getFloating, reference: getReference, startAutoUpdate, update: updatePosition };\n}\n"],"mappings":";;;AAmEA,IAAM,KAAgB,MAChB,EAAE,SAAS,QAAQ,IAAU,EAAE,QAAQ,UAAU,MAAM,IAEvD,EAAE,SAAS,MAAM,IAAU,EAAE,QAAQ,QAAQ,QAAQ,IAElD;AAaT,SAAgB,EAAyB,EACvC,aACA,WACA,gBACA,iBACA,iBACA,gBAAa,IACb,cAAW,GACX,aAAU,GACV,yBAAsB,MACyB;CAC/C,SAAS,IAA+B;EACtC,IAAM,IAAO,IAAe,KAAK;EAGjC,QAFY,IAAS,KAAK,EAAiB,EAAa,CAAC,OAE1C,QAAQ,EAAa,CAAI,IAAI;CAC9C;CAEA,SAAS,IAAuB;EAC9B,IAAM,IAAM,EAAa,GACnB,IAAW,EAAY;EAE7B,IAAI,CAAC,KAAO,CAAC,GAAU;EAEvB,IAAM,IAAY,EAAkB,GAO9B,IAAmB,MAAa,IAAsB,EAAwB,CAAQ,IAAI,KAAA,IAE1F,IAAW,EAAI,sBAAsB,CAAC,CAAC,OACzC,IAAe;EAEnB,AAAI,MAKF,EAAS,MAAM,OAAO,OACtB,EAAS,MAAM,QAAQ,QACvB,EAAS,MAAM,WAAW,GAAG,EAAS,KACtC,IAAe,EAAS,sBAAsB,CAAC,CAAC;EAGlD,IAAM,IAAS,EAAgB,GAAK,GAAU;GAC5C,UAAU;GACV,YAAY;IACV,GAAI,IAAW,CAAC,EAAO,CAAQ,CAAC,IAAI,CAAC;IACrC,EAAK,EAAE,WAAQ,CAAC;IAChB,EAAM,EAAE,WAAQ,CAAC;IACjB,GAAI,IAAa,CAAC,EAAK,EAAE,WAAQ,CAAC,CAAC,IAAI,CAAC;GAC1C;GACA;EACF,CAAC;EAGD,AADA,EAAS,MAAM,OAAO,GAAG,EAAO,EAAE,KAClC,EAAS,MAAM,MAAM,GAAG,EAAO,EAAE;EAyBjC,IAAM,IAAiB,EAAS,MAAM,YAChC,IAAgB,EAAS,MAAM;EAGrC,AADA,EAAS,MAAM,aAAa,QAC5B,EAAS,MAAM,YAAY;EAE3B,IAAM,IAAS,EAAS,sBAAsB,GACxC,IAAS,EAAO,IAAI,EAAO,MAC3B,IAAS,EAAO,IAAI,EAAO;EAUjC,IARA,EAAS,MAAM,aAAa,GAC5B,EAAS,MAAM,YAAY,IAEvB,MAAW,KAAK,MAAW,OAC7B,EAAS,MAAM,OAAO,GAAG,EAAO,IAAI,EAAO,KAC3C,EAAS,MAAM,MAAM,GAAG,EAAO,IAAI,EAAO,MAGxC,GAAY;GACd,IAAM,IACH,EAAO,eAAe,MAA+B,kBAAkB;GAE1E,EAAS,MAAM,QAAQ,GAAG,KAAK,IAAI,KAAK,IAAI,GAAc,CAAQ,GAAG,CAAS,EAAE;EAClF;CACF;CAEA,SAAS,IAA8B;EACrC,IAAM,IAAM,EAAa,GACnB,IAAW,EAAY;EAI7B,OAFI,CAAC,KAAO,CAAC,UAAuB,CAAC,IAE9B,EAAW,GAAK,GAAU,CAAc;CACjD;CAEA,OAAO;EAAE,UAAU;EAAa,WAAW;EAAc;EAAiB,QAAQ;CAAe;AACnG"}