{"version":3,"file":"dialog.cjs","names":[],"sources":["../src/overlay/dialog/dialog.ts"],"sourcesContent":["import { define, getHost, html, onCleanup, onEvent, onMounted, prop, ref, useEmit, useSlots } from '@vielzeug/ore';\n\nimport type { OverlayOpenChangeDetail } from '../../core';\nimport { coarsePointerMixin, roundedVariantMixin } from '../../styles';\nimport type { PaddingSize, RoundedSize } from '../../types';\nimport { useDialogControl } from '../shared/use-dialog';\nimport '../../content/icon/icon';\nimport componentStyles from './dialog.css?inline';\n\ntype DialogSize = 'sm' | 'md' | 'lg' | 'xl' | 'full';\ntype DialogBackdrop = 'opaque' | 'blur' | 'transparent';\ntype DialogElevation = 'none' | 'sm' | 'md' | 'lg' | 'xl' | '2xl';\n\nconst parseOptionalBool = (value: string | null): boolean | undefined =>\n  value == null ? undefined : value === '' || value === 'true';\n\n/** Dialog component properties */\n\nexport type OreDialogEvents = {\n  'open-change': OverlayOpenChangeDetail;\n};\n\nexport type OreDialogProps = {\n  /** Backdrop style — 'blur' (default): dark overlay + blur; 'opaque': dark overlay only; 'transparent': no overlay */\n  backdrop?: DialogBackdrop;\n  /** Initial uncontrolled open state. Ignored when `open` is set. */\n  'default-open'?: boolean;\n  /** Show a close (×) button in the header */\n  dismissible?: boolean;\n  /** Panel shadow elevation — defaults to 'xl' */\n  elevation?: DialogElevation;\n  /**\n   * CSS selector for the element inside the dialog that should receive focus when the dialog opens.\n   * Defaults to the first focusable element (browser default).\n   * @example 'input[name=\"email\"]' | '#confirm-btn'\n   */\n  'initial-focus'?: string;\n  /** Dialog title shown in the header (used as aria-label when no header slot) */\n  label?: string;\n  /** Controls the open state of the dialog */\n  open?: boolean;\n  /** Internal padding size */\n  padding?: PaddingSize;\n  /** When true, clicking the backdrop does not close the dialog */\n  persistent?: boolean;\n  /**\n   * When true (default), the focus returns to the element that triggered the dialog after it closes.\n   * Set to false if you want to manage focus manually.\n   */\n  'return-focus'?: boolean;\n  /** Border radius */\n  rounded?: RoundedSize | '';\n  /** Dialog size */\n  size?: DialogSize;\n};\n\n/**\n * A modal dialog that traps focus, blocks page interaction, and dismisses on\n * `Escape`. Built on the native `<dialog>` element for correct top-layer stacking\n * and browser-managed accessibility.\n *\n * @element ore-dialog\n *\n * @attr {boolean} open - Controlled open state\n * @attr {boolean} default-open - Initial uncontrolled open state\n * @attr {string} label - Dialog title (also used as aria-label)\n * @attr {string} size - Size: 'sm' | 'md' | 'lg' | 'xl' | 'full'\n * @attr {boolean} dismissible - Show a close (×) button in the header\n * @attr {boolean} persistent - Prevent backdrop-click from closing\n * @attr {string} rounded - Border radius: 'none' | 'sm' | 'md' | 'lg' | 'xl' | '2xl' | '3xl' | 'full'\n * @attr {string} backdrop - Backdrop style: 'opaque' (default) | 'blur' | 'transparent'\n * @attr {string} elevation - Panel shadow: 'none' | 'sm' | 'md' | 'lg' | 'xl' | '2xl'\n * @attr {string} padding - Padding: 'none' | 'sm' | 'md' | 'lg' | 'xl'\n *\n * @fires open-change - Fired when the dialog state changes. detail: { open, reason }\n *\n * @slot - Dialog body content\n * @slot header - Custom header content (replaces the default title + close layout)\n * @slot footer - Action buttons or additional content at the bottom\n *\n * @cssprop --dialog-bg - Panel background color\n * @cssprop --dialog-border-color - Panel border color\n * @cssprop --dialog-radius - Panel border radius\n * @cssprop --dialog-shadow - Panel drop shadow\n * @cssprop --dialog-padding - Padding for header, body, and footer sections\n * @cssprop --dialog-gap - Gap between footer action buttons\n * @cssprop --dialog-backdrop - Backdrop overlay color\n * @cssprop --dialog-max-width - Maximum panel width (overridden by size prop)\n *\n * @part dialog - Dialog root container.\n * @part overlay - Overlay backdrop element.\n * @part panel - Panel container.\n * @part header - Header container.\n * @part title - Title text element.\n * @part close - Close action control.\n * @part body - Body content container.\n * @part footer - Footer container.\n * @example\n * ```html\n * <ore-dialog label=\"Confirm action\" dismissible>\n *   <p>Are you sure you want to delete this item?</p>\n *   <div slot=\"footer\">\n *     <ore-button variant=\"ghost\" id=\"cancel\">Cancel</ore-button>\n *     <ore-button color=\"error\" id=\"confirm\">Delete</ore-button>\n *   </div>\n * </ore-dialog>\n *\n * <script type=\"module\">\n *   import '@vielzeug/refine/dialog';\n *   const dialog = document.querySelector('ore-dialog');\n *   document.querySelector('#open-btn').addEventListener('click', () => {\n *     dialog.setAttribute('open', '');\n *   });\n *   document.querySelector('#cancel').addEventListener('click', () => {\n *     dialog.removeAttribute('open');\n *   });\n * </script>\n * ```\n */\n\nexport const DIALOG_TAG = 'ore-dialog' as const;\ndefine<OreDialogProps>(DIALOG_TAG, {\n  props: {\n    backdrop: prop.string<DialogBackdrop>(),\n    'default-open': prop.bool(false),\n    dismissible: prop.bool(false),\n    elevation: prop.string<DialogElevation>(),\n    'initial-focus': prop.string(),\n    label: prop.string(),\n    open: { default: undefined as boolean | undefined, parse: parseOptionalBool },\n    padding: prop.string<PaddingSize>(),\n    persistent: prop.bool(false),\n    'return-focus': prop.bool(true),\n    rounded: prop.string<RoundedSize | ''>(),\n    size: prop.oneOf(['sm', 'md', 'lg', 'xl', 'full'] as const, 'md'),\n  },\n  setup(props) {\n    const el = getHost();\n    const emit = useEmit<OreDialogEvents>();\n    const slots = useSlots();\n\n    const dialogRef = ref<HTMLDialogElement>();\n    const hasHeader = () => slots.has('header').value || !!props.label.value || props.dismissible.value;\n    const hasFooter = () => slots.has('footer').value;\n\n    const { closeWithAnimation, overlay, requestClose, setupNativeListeners } = useDialogControl({\n      defaultOpen: props['default-open'],\n      dialogRef,\n      getPanelEl: () => dialogRef.value?.querySelector<HTMLElement>('.panel'),\n      host: el,\n      initialFocus: props['initial-focus'],\n      isPersistent: () => Boolean(props.persistent.value),\n      onCleanup,\n      onEvent,\n      onNativeClose: (reason) => emit('open-change', { open: false, reason }),\n      onOpen: (reason) => emit('open-change', { open: true, reason }),\n      openProp: props.open,\n      performClose: () => {\n        closeWithAnimation();\n      },\n      returnFocus: props['return-focus'],\n    });\n\n    const handleDismiss = () => {\n      requestClose('trigger');\n    };\n\n    onMounted(() => {\n      const dialog = dialogRef.value;\n\n      if (!dialog) return;\n\n      setupNativeListeners();\n\n      return () => {\n        overlay.dispose();\n      };\n    });\n\n    return html`\n      <dialog ref=${dialogRef} class=\"dialog\" part=\"dialog\" aria-label=\"${props.label}\" aria-modal=\"true\">\n        <div class=\"overlay\" part=\"overlay\" aria-hidden=\"true\"></div>\n        <div class=\"panel\" part=\"panel\" data-size=\"${props.size}\">\n          <div class=\"header\" part=\"header\" ?hidden=${() => !hasHeader()}>\n            <slot name=\"header\">\n              <span class=\"title\" part=\"title\">${props.label}</span>\n            </slot>\n            <button\n              class=\"close\"\n              part=\"close\"\n              type=\"button\"\n              aria-label=\"Close dialog\"\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          <div class=\"body\" part=\"body\">\n            <slot></slot>\n          </div>\n          <div class=\"footer\" part=\"footer\" ?hidden=${() => !hasFooter()}>\n            <slot name=\"footer\"></slot>\n          </div>\n        </div>\n      </dialog>\n    `;\n  },\n  styles: [roundedVariantMixin, coarsePointerMixin, componentStyles],\n});\n"],"mappings":"gSAaA,IAAM,EAAqB,GACzB,GAAS,KAAO,IAAA,GAAY,IAAU,IAAM,IAAU,OA0G3C,EAAa,cAC1B,EAAA,EAAA,OAAA,CAAuB,EAAY,CACjC,MAAO,CACL,SAAU,EAAA,KAAK,OAAuB,EACtC,eAAgB,EAAA,KAAK,KAAK,EAAK,EAC/B,YAAa,EAAA,KAAK,KAAK,EAAK,EAC5B,UAAW,EAAA,KAAK,OAAwB,EACxC,gBAAiB,EAAA,KAAK,OAAO,EAC7B,MAAO,EAAA,KAAK,OAAO,EACnB,KAAM,CAAE,QAAS,IAAA,GAAkC,MAAO,CAAkB,EAC5E,QAAS,EAAA,KAAK,OAAoB,EAClC,WAAY,EAAA,KAAK,KAAK,EAAK,EAC3B,eAAgB,EAAA,KAAK,KAAK,EAAI,EAC9B,QAAS,EAAA,KAAK,OAAyB,EACvC,KAAM,EAAA,KAAK,MAAM,CAAC,KAAM,KAAM,KAAM,KAAM,MAAM,EAAY,IAAI,CAClE,EACA,MAAM,EAAO,CACX,IAAM,GAAA,EAAK,EAAA,QAAA,CAAQ,EACb,GAAA,EAAO,EAAA,QAAA,CAAyB,EAChC,GAAA,EAAQ,EAAA,SAAA,CAAS,EAEjB,GAAA,EAAY,EAAA,IAAA,CAAuB,EACnC,MAAkB,EAAM,IAAI,QAAQ,CAAC,CAAC,OAAS,CAAC,CAAC,EAAM,MAAM,OAAS,EAAM,YAAY,MACxF,MAAkB,EAAM,IAAI,QAAQ,CAAC,CAAC,MAEtC,CAAE,qBAAoB,UAAS,eAAc,wBAAyB,EAAA,iBAAiB,CAC3F,YAAa,EAAM,gBACnB,YACA,eAAkB,EAAU,OAAO,cAA2B,QAAQ,EACtE,KAAM,EACN,aAAc,EAAM,iBACpB,iBAAoB,EAAQ,EAAM,WAAW,MAC7C,UAAA,EAAA,UACA,QAAA,EAAA,QACA,cAAgB,GAAW,EAAK,cAAe,CAAE,KAAM,GAAO,QAAO,CAAC,EACtE,OAAS,GAAW,EAAK,cAAe,CAAE,KAAM,GAAM,QAAO,CAAC,EAC9D,SAAU,EAAM,KAChB,iBAAoB,CAClB,EAAmB,CACrB,EACA,YAAa,EAAM,eACrB,CAAC,EAkBD,OAZA,EAAA,EAAA,UAAA,KAAgB,CACC,KAAU,MAMzB,OAFA,EAAqB,MAER,CACX,EAAQ,QAAQ,CAClB,CACF,CAAC,EAEM,EAAA,IAAI;oBACK,EAAU,4CAA4C,EAAM,MAAM;;qDAEjC,EAAM,KAAK;0DACJ,CAAC,EAAU,EAAE;;iDAExB,EAAM,MAAM;;;;;;;4BAO/B,CAAC,EAAM,YAAY,MAAM;2BA7BvB,CAC1B,EAAa,SAAS,CACxB,EA4BiC;;;;;;;0DAOuB,CAAC,EAAU,EAAE;;;;;KAMvE,EACA,OAAQ,CAAC,EAAA,oBAAqB,EAAA,mBAAoB,EAAA,OAAe,CACnE,CAAC"}