{"version":3,"sources":["../src/components/dialog/dialog.tsx","../src/components/dialog/views/dialog-header.tsx","../src/components/buttons/icon-button.tsx","../src/components/dialog/views/dialog-footer.tsx","../src/hooks/useDialogClickHandler.ts"],"names":["React","useRef","useEffect","useCallback","useId","IconButton","icon","label","variant","type","props","Button","DialogHeader","dialogTitle","onClick","id","closeIconSize","handleClose","ui_default","heading_default","icon_default","dialog_header_default","DialogFooter","onClose","onConfirm","confirmLabel","cancelLabel","handleCancel","handleConfirm","button_default","dialog_footer_default","useDialogClickHandler","dialogRef","e","dialogDimensions","Dialog","isOpen","onOpenChange","isAlertDialog","dialogLabel","children","className","hideFooter","styles","size","position","titleId","dialog","handleClickOutside","contentId","dialog_default"],"mappings":"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","sourcesContent":["import React, { useRef, useEffect, useCallback, useId } from \"react\";\nimport UI from \"#components/ui\";\nimport DialogHeader from \"#components/dialog/views/dialog-header\";\nimport DialogFooter from \"#components/dialog/views/dialog-footer\";\nimport { useDialogClickHandler } from \"#hooks/useDialogClickHandler.js\";\nimport type { DialogProps } from \"./dialog.types\";\n\n/**\n * A controlled dialog component that supports both modal and non-modal (inline alert) modes.\n *\n * **Modal Dialog** (default): Uses native `<dialog>` element with `.showModal()` which provides:\n * - Automatic focus trap (Tab cycles within dialog)\n * - Escape key closes dialog (native behavior)\n * - Backdrop overlay with click-to-close\n * - Inert background (page content becomes non-interactive)\n *\n * **Inline Alert Dialog** (`isAlertDialog={true}`): Uses `.show()` for non-modal inline alerts:\n * - No focus trap (page remains interactive)\n * - No escape key behavior\n * - Positioned inline in page flow\n * - User must explicitly close with button\n *\n * @component\n * @example\n * ```tsx\n * // Controlled usage\n * const [open, setOpen] = useState(false);\n * <Dialog\n *   isOpen={open}\n *   onOpenChange={setOpen}\n *   dialogTitle=\"Confirm Delete\"\n * >\n *   Are you sure you want to delete this item?\n * </Dialog>\n * ```\n *\n * @param {DialogProps} props - Component props\n * @param {boolean} props.isOpen - Controls whether the dialog is currently open\n * @param {(open: boolean) => void} props.onOpenChange - Callback fired when dialog open state changes\n * @param {string} props.dialogTitle - The title displayed in the dialog header\n * @param {boolean} [props.isAlertDialog=false] - If true, renders as non-modal inline alert\n * @param {() => void} [props.onClose] - Deprecated: Use onOpenChange. Called when dialog closes.\n * @param {ReactNode} props.children - Content to display inside the dialog body\n * @param {() => void | Promise<void>} [props.onConfirm] - Callback fired when confirm button is clicked\n * @param {string} [props.confirmLabel=\"Confirm\"] - Text label for confirm button\n * @param {string} [props.cancelLabel=\"Cancel\"] - Text label for cancel button\n * @param {boolean} [props.hideFooter=false] - If true, hides the footer with action buttons\n * @param {string} [props.className] - Additional CSS classes to apply\n * @param {string} [props.dialogLabel] - Optional aria-label for the dialog\n * @param {CSSProperties} [props.styles] - Inline styles to apply to dialog element\n * @param {number} [props.closeIconSize=24] - Size of the close icon in pixels\n * @returns {JSX.Element} A controlled dialog component\n */\nexport const Dialog: React.FC<DialogProps> = ({\n  isOpen,\n  onOpenChange,\n  isAlertDialog = false,\n  onClose,\n  dialogTitle,\n  dialogLabel,\n  children,\n  onConfirm,\n  confirmLabel = \"Confirm\",\n  cancelLabel = \"Cancel\",\n  className = \"\",\n  hideFooter = false,\n  styles,\n  size,\n  position = \"center\",\n  closeIconSize,\n}) => {\n  const dialogRef = useRef<HTMLDialogElement>(null);\n  const titleId = useId();\n\n  // Handle native dialog open/close based on isOpen prop\n  useEffect(() => {\n    const dialog = dialogRef.current;\n    if (!dialog) return;\n\n    if (isOpen) {\n      if (isAlertDialog) {\n        // Non-modal inline alert - no focus trap, no backdrop\n        dialog.show();\n      } else {\n        // Modal dialog - native focus trap, escape key, backdrop\n        dialog.showModal();\n      }\n    } else {\n      dialog.close();\n    }\n  }, [isOpen, isAlertDialog]);\n\n  // Handle close event - notify parent via onOpenChange\n  const handleClose = useCallback(() => {\n    onOpenChange(false);\n    // Support deprecated onClose prop for backward compatibility\n    if (onClose) onClose();\n  }, [onOpenChange, onClose]);\n\n  // Handle backdrop clicks (only for modal dialogs)\n  const handleClickOutside = useDialogClickHandler(dialogRef, handleClose);\n\n  const contentId = useId();\n\n  return (\n    <UI\n      as=\"dialog\"\n      role={isAlertDialog ? \"alertdialog\" : \"dialog\"}\n      ref={dialogRef}\n      onClose={handleClose}\n      onClick={handleClickOutside}\n      aria-modal={isOpen && !isAlertDialog ? \"true\" : undefined}\n      aria-labelledby={titleId}\n      aria-describedby={contentId}\n      aria-label={dialogLabel}\n      className={`dialog-modal ${className}`.trim()}\n      style={styles}\n      {...(size && { \"data-size\": size })}\n      {...(position && { \"data-position\": position })}\n    >\n      <DialogHeader dialogTitle={dialogTitle} onClick={handleClose} id={titleId} closeIconSize={closeIconSize} />\n\n      <UI\n        as=\"section\"\n        id={contentId}\n        className=\"dialog-content\"\n        onClick={(e: React.MouseEvent) => e.stopPropagation()}\n      >\n        {children}\n        {!hideFooter && (\n          <DialogFooter\n            onClose={handleClose}\n            onConfirm={onConfirm}\n            confirmLabel={confirmLabel}\n            cancelLabel={cancelLabel}\n          />\n        )}\n      </UI>\n    </UI>\n  );\n};\nDialog.displayName = \"Dialog\";\n\nexport default React.memo(Dialog);\n","import React, { useCallback } from \"react\";\nimport UI from \"#components/ui\";\nimport Heading from \"#components/heading/heading\";\nimport { IconButton } from \"#components/buttons/icon-button\";\nimport Icon from \"#components/icons/icon\";\nimport type { DialogHeaderProps } from \"../dialog.types\";\n\n/**\n * DialogHeader component displays the header section of a dialog with a title and close button.\n *\n * This component is optimized for accessibility with:\n * - Unique ID for `aria-labelledby` linking to parent dialog\n * - Semantic heading structure for screen readers\n * - Clear close button with accessible label\n * - Memoized to prevent unnecessary re-renders\n *\n * @component\n * @param {DialogHeaderProps} props - Component props\n * @param {string} props.dialogTitle - The title text to display in the dialog header\n * @param {() => void} props.onClick - Callback function triggered when close button is clicked\n * @param {string} [props.id] - Optional ID for aria-labelledby linking. Auto-generated if not provided.\n * @param {\"h1\" | \"h2\" | \"h3\" | \"h4\" | \"h5\" | \"h6\"} [props.type=\"h3\"] - Heading level for semantic structure\n * @param {number} [props.closeIconSize=24] - Size of the close icon in pixels\n * @returns {JSX.Element} A dialog header with title and close button\n *\n * @example\n * ```tsx\n * <DialogHeader\n *   id=\"dialog-title-1\"\n *   dialogTitle=\"Confirm Action\"\n *   onClick={() => setIsOpen(false)}\n *   type=\"h2\"\n * />\n * ```\n */\nconst DialogHeader: React.FC<DialogHeaderProps> = ({\n  dialogTitle,\n  onClick,\n  id,\n  type = \"h3\",\n  closeIconSize = 24,\n}) => {\n  const handleClose = useCallback(() => {\n    onClick();\n  }, [onClick]);\n\n  return (\n    <UI as=\"div\" classes=\"dialog-header\">\n      <Heading type={type} className=\"dialog-title\" id={id}>\n        {dialogTitle || \"Dialog\"}\n      </Heading>\n      <IconButton\n        type=\"button\"\n        onClick={handleClose}\n        className=\"dialog-close\"\n        aria-label=\"Close dialog\"\n        icon={\n          <Icon>\n            <Icon.Remove size={closeIconSize} />\n          </Icon>\n        }\n      />\n    </UI>\n  );\n};\n\nexport default React.memo(DialogHeader);\nDialogHeader.displayName = \"DialogHeader\";\n","import React from \"react\";\nimport { Button, type ButtonProps } from \"./button\";\n\n/**\n * XOR constraint: exactly one of aria-label or aria-labelledby is required.\n * Passing both or neither is a TypeScript compile-time error.\n * Satisfies WCAG 2.1 SC 1.1.1 (Non-text Content).\n */\ntype WithAriaLabel = { \"aria-label\": string; \"aria-labelledby\"?: never };\ntype WithAriaLabelledBy = { \"aria-labelledby\": string; \"aria-label\"?: never };\n\nexport type IconButtonProps = Omit<ButtonProps, \"children\"> &\n  (WithAriaLabel | WithAriaLabelledBy) & {\n    /** The icon element rendered inside the button. */\n    icon: React.ReactNode;\n    /**\n     * Optional text shown alongside the icon at desktop widths.\n     * Visually hidden below the `$icon-label-bp` SCSS breakpoint (default 48rem / 768px)\n     * via a media query on `[data-icon-label]`, but always present in the accessibility\n     * tree — screen readers announce it at every viewport size.\n     *\n     * NOTE: When `label` is provided, the default `variant=\"icon\"` removes padding.\n     * Use `variant=\"outline\"` (or another padded variant) to restore layout padding\n     * alongside the label.\n     */\n    label?: string;\n    /** Button type: button, submit, or reset. Required. */\n    type: \"button\" | \"submit\" | \"reset\";\n  };\n\n/**\n * Accessible icon button component. Wraps `Button` with:\n * - Required accessible label via `aria-label` or `aria-labelledby` (XOR enforced)\n * - Optional `label` text hidden on mobile (< 48rem), visible on desktop — always in a11y tree\n * - `variant=\"icon\"` default (square, no padding)\n * - Fixed `3rem × 3rem` tap target (48px at default root font size — WCAG 2.5.5 AAA)\n *\n * @example\n * // Icon only\n * <IconButton type=\"button\" aria-label=\"Close menu\" icon={<CloseIcon />} />\n *\n * @example\n * // Icon + label (label hides on mobile, visible at >= 48rem / 768px)\n * <IconButton\n *   type=\"button\"\n *   aria-label=\"Settings\"\n *   icon={<SettingsIcon />}\n *   label=\"Settings\"\n *   variant=\"outline\"\n * />\n *\n * @example\n * // Labelled by external element\n * <span id=\"btn-label\">Delete item</span>\n * <IconButton type=\"button\" aria-labelledby=\"btn-label\" icon={<TrashIcon />} />\n */\nexport const IconButton = ({\n  icon,\n  label,\n  variant = \"icon\",\n  type = \"button\",\n  ...props\n}: IconButtonProps) => (\n  <Button\n    variant={variant}\n    data-icon-btn={label ? \"has-label\" : \"icon\"}\n    {...props}\n    type={type}\n  >\n    {icon}\n    {label && <span data-icon-label>{label}</span>}\n  </Button>\n);\n\nIconButton.displayName = \"IconButton\";\n","import React, { useCallback } from \"react\";\nimport UI from \"#components/ui\";\nimport Button from \"#components/buttons/button\";\nimport type { DialogFooterProps } from \"../dialog.types\";\n\n/**\n * DialogFooter component renders action buttons for dialog confirmation/cancellation.\n *\n * This component provides a consistent footer layout with:\n * - Cancel button (secondary style) - Always shown if cancelLabel provided\n * - Confirm button (primary style) - Only shown if onConfirm callback provided\n * - Proper semantic button types\n * - Accessible button sizing and spacing\n * - Memoized to prevent unnecessary re-renders\n *\n * @component\n * @param {DialogFooterProps} props - Component props\n * @param {() => void} props.onClose - Callback fired when cancel button is clicked\n * @param {() => void | Promise<void>} [props.onConfirm] - Optional callback for confirm action. If omitted, confirm button is hidden.\n * @param {string} props.confirmLabel - Text label for the confirm button\n * @param {string} props.cancelLabel - Text label for the cancel button\n * @returns {JSX.Element} A footer section with action buttons\n *\n * @example\n * ```tsx\n * // Simple close-only footer\n * <DialogFooter\n *   onClose={() => setOpen(false)}\n *   cancelLabel=\"Close\"\n *   confirmLabel=\"OK\"\n * />\n *\n * // Confirmation dialog with both actions\n * <DialogFooter\n *   onClose={() => setOpen(false)}\n *   onConfirm={async () => await deleteItem()}\n *   confirmLabel=\"Delete\"\n *   cancelLabel=\"Cancel\"\n * />\n * ```\n */\nconst DialogFooter: React.FC<DialogFooterProps> = ({\n  onClose,\n  onConfirm,\n  confirmLabel,\n  cancelLabel,\n}) => {\n  // Memoize handlers to prevent unnecessary re-renders\n  const handleCancel = useCallback(() => {\n    onClose();\n  }, [onClose]);\n\n  const handleConfirm = useCallback(() => {\n    if (onConfirm) {\n      onConfirm();\n    }\n  }, [onConfirm]);\n\n  return (\n    <UI as=\"section\" className=\"dialog-footer\">\n      {cancelLabel && (\n        <Button\n          type=\"button\"\n          onClick={handleCancel}\n          className=\"dialog-button button-secondary\"\n          data-btn=\"sm\"\n        >\n          {cancelLabel}\n        </Button>\n      )}\n\n      {onConfirm && (\n        <Button\n          type=\"button\"\n          onClick={handleConfirm}\n          className=\"dialog-button button-primary\"\n          data-btn=\"sm\"\n        >\n          {confirmLabel}\n        </Button>\n      )}\n    </UI>\n  );\n};\n\nDialogFooter.displayName = \"DialogFooter\";\n\nexport default React.memo(DialogFooter);\n","import { useCallback, RefObject } from \"react\";\n\nexport const useDialogClickHandler = (\n  dialogRef: RefObject<HTMLDialogElement>,\n  handleClose: () => void\n) => {\n  const handleClick = useCallback(\n    (e: React.MouseEvent<HTMLDialogElement>) => {\n      const dialogDimensions = dialogRef.current?.getBoundingClientRect();\n      if (dialogDimensions) {\n        const isClickOutside =\n          e.clientY < dialogDimensions.top ||\n          e.clientY > dialogDimensions.bottom ||\n          e.clientX < dialogDimensions.left ||\n          e.clientX > dialogDimensions.right;\n\n        if (isClickOutside) {\n          handleClose();\n        }\n      }\n    },\n    [dialogRef, handleClose]\n  );\n\n  return handleClick;\n};\n"]}