@allxsmith/bestax-bulma 5.28.0 → 5.29.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/dist/esm/components/Dialog.js +8 -4
  2. package/dist/esm/components/Dialog.js.map +1 -1
  3. package/dist/esm/components/HoverCard.js.map +1 -1
  4. package/dist/esm/components/Modal.js +19 -11
  5. package/dist/esm/components/Modal.js.map +1 -1
  6. package/dist/esm/components/Popover.js.map +1 -1
  7. package/dist/esm/components/Sidebar.js +12 -3
  8. package/dist/esm/components/Sidebar.js.map +1 -1
  9. package/dist/esm/components/Toast.js +5 -3
  10. package/dist/esm/components/Toast.js.map +1 -1
  11. package/dist/esm/elements/Notification.js +3 -1
  12. package/dist/esm/elements/Notification.js.map +1 -1
  13. package/dist/esm/form/DateInputBase.js.map +1 -1
  14. package/dist/esm/form/DateRangeInputBase.js.map +1 -1
  15. package/dist/esm/form/DateTimeInputBase.js.map +1 -1
  16. package/dist/esm/form/TimeInputBase.js.map +1 -1
  17. package/dist/esm/form/_pickerInternals/PickerPopover.js +3 -1
  18. package/dist/esm/form/_pickerInternals/PickerPopover.js.map +1 -1
  19. package/dist/esm/helpers/Config.js +5 -2
  20. package/dist/esm/helpers/Config.js.map +1 -1
  21. package/dist/esm/helpers/portal.js +30 -5
  22. package/dist/esm/helpers/portal.js.map +1 -1
  23. package/dist/index.cjs +76 -27
  24. package/dist/index.cjs.map +1 -1
  25. package/dist/types/components/HoverCard.d.ts +14 -12
  26. package/dist/types/components/Modal.d.ts +2 -1
  27. package/dist/types/components/Popover.d.ts +8 -7
  28. package/dist/types/form/DateInputBase.d.ts +4 -1
  29. package/dist/types/form/DateRangeInputBase.d.ts +4 -1
  30. package/dist/types/form/DateTimeInputBase.d.ts +4 -1
  31. package/dist/types/form/TimeInputBase.d.ts +2 -1
  32. package/dist/types/helpers/Config.d.ts +34 -3
  33. package/dist/types/helpers/portal.d.ts +27 -5
  34. package/dist/types-cjs/components/HoverCard.d.ts +14 -12
  35. package/dist/types-cjs/components/Modal.d.ts +2 -1
  36. package/dist/types-cjs/components/Popover.d.ts +8 -7
  37. package/dist/types-cjs/form/DateInputBase.d.ts +4 -1
  38. package/dist/types-cjs/form/DateRangeInputBase.d.ts +4 -1
  39. package/dist/types-cjs/form/DateTimeInputBase.d.ts +4 -1
  40. package/dist/types-cjs/form/TimeInputBase.d.ts +2 -1
  41. package/dist/types-cjs/helpers/Config.d.ts +34 -3
  42. package/dist/types-cjs/helpers/portal.d.ts +27 -5
  43. package/package.json +1 -1
@@ -4,6 +4,7 @@ import { forwardRef, useRef, useCallback, useEffect, useSyncExternalStore } from
4
4
  import { usePrefixedClassNames, classNames } from '../helpers/classNames.js';
5
5
  import { useBulmaClasses } from '../helpers/useBulmaClasses.js';
6
6
  import { useIsHydrated } from '../helpers/useIsHydrated.js';
7
+ import { useConfig } from '../helpers/Config.js';
7
8
  import { Modal } from './Modal.js';
8
9
 
9
10
  /**
@@ -60,11 +61,14 @@ const Dialog = forwardRef(({ isOpen, title, message, type = 'default', confirmTe
60
61
  handleCancel();
61
62
  }
62
63
  }, [canCancel, handleCancel]);
63
- // Moving into the portal remounts the Modal's subtree — these buttons
64
- // included — so the focus effect below has to re-run against the new
65
- // nodes, exactly as the Modal's own focus effect does.
64
+ // Moving into the portal, or from one portal target to another, remounts
65
+ // the Modal's subtree, these buttons included, so the focus effect below
66
+ // has to re-run against the new nodes, exactly as the Modal's own focus
67
+ // effect does, on the same inputs.
66
68
  const isHydrated = useIsHydrated();
67
69
  const isPortaled = Boolean(portal) && isHydrated;
70
+ const { portalContainer } = useConfig();
71
+ const defaultTarget = isPortaled && portal === true ? portalContainer : undefined;
68
72
  // Focus management — Dialog picks a specific button rather than the
69
73
  // Modal's default (first focusable/root); this effect runs after the
70
74
  // inner Modal's own focus-on-open effect, so it wins.
@@ -73,7 +77,7 @@ const Dialog = forwardRef(({ isOpen, title, message, type = 'default', confirmTe
73
77
  const buttonToFocus = focusCancel && showCancel ? cancelRef.current : confirmRef.current;
74
78
  buttonToFocus?.focus();
75
79
  }
76
- }, [isOpen, focusCancel, showCancel, isPortaled]);
80
+ }, [isOpen, focusCancel, showCancel, isPortaled, portal, defaultTarget]);
77
81
  // Use combined ref
78
82
  const combinedRef = useCallback((node) => {
79
83
  dialogRef.current =
@@ -1 +1 @@
1
- {"version":3,"file":"Dialog.js","sources":["../../../src/components/Dialog.tsx"],"sourcesContent":["import React, {\n forwardRef,\n useCallback,\n useEffect,\n useRef,\n useSyncExternalStore,\n} from 'react';\nimport { classNames, usePrefixedClassNames } from '../helpers/classNames';\nimport { useBulmaClasses, BulmaClassesProps } from '../helpers/useBulmaClasses';\nimport { useIsHydrated } from '../helpers/useIsHydrated';\nimport { Modal } from './Modal';\n\n/** Valid dialog type/color values. */\nexport type DialogType = 'default' | 'success' | 'danger' | 'warning' | 'info';\n\n/**\n * Props for the Dialog component.\n * @extraProp {string} [className] - Additional CSS classes.\n * @extraProp {React.Ref<HTMLElement>} [ref] - Ref forwarded to the dialog element.\n */\nexport interface DialogProps\n extends\n Omit<React.HTMLAttributes<HTMLDivElement>, 'color'>,\n Omit<BulmaClassesProps, 'color'> {\n /** Whether the dialog is open (required). */\n isOpen: boolean;\n /** Dialog title. */\n title?: string;\n /** Dialog message/content (required). */\n message: string | React.ReactNode;\n /** The type/color of the dialog. Default: 'default'. */\n type?: DialogType;\n /** Text for confirm button. Default: 'OK'. */\n confirmText?: string;\n /** Text for cancel button. Default: 'Cancel'. */\n cancelText?: string;\n /** Callback when confirm button is clicked. */\n onConfirm?: () => void;\n /** Callback when cancel button is clicked or dismissed. */\n onCancel?: () => void;\n /** Whether to show cancel button. Default: true for confirm dialogs. */\n showCancel?: boolean;\n /** Whether the dialog can be dismissed. Default: true. */\n canCancel?: boolean;\n /** Focus cancel button instead of confirm. Default: false. */\n focusCancel?: boolean;\n /** Custom icon to display. */\n icon?: React.ReactNode;\n /**\n * Renders the dialog into a portal target instead of inline. Forwarded to\n * the underlying `Modal`; see its `portal` prop for the accepted values.\n * @defaultValue false\n */\n portal?: boolean | string | HTMLElement;\n}\n\n/**\n * The `Dialog` component provides ready-made confirm and alert dialogs, so a destructive action stays one `await dialog.confirm()` call away.\n *\n * @function\n * @param {DialogProps} props - Props for the Dialog component.\n * @param {React.Ref<HTMLDivElement>} ref - Forwarded ref to the dialog element.\n * @returns {JSX.Element | null} The rendered dialog component.\n *\n * @example\n * // Alert dialog\n * <Dialog\n * isOpen={showAlert}\n * title=\"Success\"\n * message=\"Operation completed successfully!\"\n * type=\"success\"\n * onConfirm={() => setShowAlert(false)}\n * showCancel={false}\n * />\n *\n * @example\n * // Confirm dialog\n * <Dialog\n * isOpen={showConfirm}\n * title=\"Delete Item?\"\n * message=\"This action cannot be undone.\"\n * type=\"danger\"\n * confirmText=\"Delete\"\n * onConfirm={handleDelete}\n * onCancel={() => setShowConfirm(false)}\n * />\n */\nexport const Dialog = forwardRef<HTMLDivElement, DialogProps>(\n (\n {\n isOpen,\n title,\n message,\n type = 'default',\n confirmText = 'OK',\n cancelText = 'Cancel',\n onConfirm,\n onCancel,\n showCancel = true,\n canCancel = true,\n focusCancel = false,\n icon,\n portal,\n className,\n ...props\n },\n ref\n ) => {\n const { bulmaHelperClasses, rest } = useBulmaClasses(props);\n const dialogRef = useRef<HTMLDivElement>(null);\n // Cleanup returned by a consumer's callback ref on attach, held until detach.\n const consumerCleanupRef = useRef<(() => void) | null>(null);\n const confirmRef = useRef<HTMLButtonElement>(null);\n const cancelRef = useRef<HTMLButtonElement>(null);\n\n // Handle cancel\n const handleCancel = useCallback(() => {\n if (canCancel) {\n onCancel?.();\n }\n }, [canCancel, onCancel]);\n\n // Handle confirm\n const handleConfirm = useCallback(() => {\n onConfirm?.();\n }, [onConfirm]);\n\n // Handle background click\n const handleBackgroundClick = useCallback(() => {\n if (canCancel) {\n handleCancel();\n }\n }, [canCancel, handleCancel]);\n\n // Moving into the portal remounts the Modal's subtree — these buttons\n // included — so the focus effect below has to re-run against the new\n // nodes, exactly as the Modal's own focus effect does.\n const isHydrated = useIsHydrated();\n const isPortaled = Boolean(portal) && isHydrated;\n\n // Focus management — Dialog picks a specific button rather than the\n // Modal's default (first focusable/root); this effect runs after the\n // inner Modal's own focus-on-open effect, so it wins.\n useEffect(() => {\n if (isOpen) {\n const buttonToFocus =\n focusCancel && showCancel ? cancelRef.current : confirmRef.current;\n buttonToFocus?.focus();\n }\n }, [isOpen, focusCancel, showCancel, isPortaled]);\n\n // Use combined ref\n const combinedRef = useCallback(\n (node: HTMLDivElement | null) => {\n (dialogRef as React.MutableRefObject<HTMLDivElement | null>).current =\n node;\n if (typeof ref === 'function') {\n // React 19 lets a callback ref return a cleanup function and detaches by\n // running it instead of calling the ref with `null`; React 18 discards the\n // return value entirely. Returning it from here would be a React-19-only\n // contract, so we hold the cleanup and run it ourselves on detach — the\n // same shape as `Dropdown`, `Modal`, `Sidebar`, `Toast` and `Carousel`.\n // The form controls that merge a forwarded ref still discard the cleanup\n // and detach with `ref(null)`, so this is not yet library-wide — grep\n // `typeof ref === 'function'` for the current split.\n if (node === null) {\n const consumerCleanup = consumerCleanupRef.current;\n consumerCleanupRef.current = null;\n if (consumerCleanup) {\n consumerCleanup();\n } else {\n ref(null);\n }\n return;\n }\n const cleanup: unknown = ref(node);\n consumerCleanupRef.current =\n typeof cleanup === 'function' ? (cleanup as () => void) : null;\n } else if (ref) {\n (ref as React.MutableRefObject<HTMLDivElement | null>).current = node;\n }\n },\n [ref]\n );\n\n // Generate classes\n const dialogClasses = usePrefixedClassNames('dialog', {\n [`is-${type}`]: type !== 'default',\n });\n const headerClass = usePrefixedClassNames('dialog-header');\n const iconClass = usePrefixedClassNames('dialog-icon');\n const titleClass = usePrefixedClassNames('dialog-title');\n const bodyClass = usePrefixedClassNames('dialog-body');\n const footerClass = usePrefixedClassNames('dialog-footer');\n const cancelButtonClass = usePrefixedClassNames('button');\n const confirmButtonClass = usePrefixedClassNames('button', {\n 'is-success': type === 'success',\n 'is-danger': type === 'danger',\n 'is-warning': type === 'warning',\n 'is-info': type === 'info',\n 'is-primary': type === 'default',\n });\n\n const combinedClasses = classNames(\n dialogClasses,\n bulmaHelperClasses,\n className\n );\n\n // Get default icon based on type\n const getDefaultIcon = () => {\n switch (type) {\n case 'success':\n return (\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n >\n <path d=\"M20 6L9 17l-5-5\" />\n </svg>\n );\n case 'danger':\n return (\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n >\n <circle cx=\"12\" cy=\"12\" r=\"10\" />\n <line x1=\"15\" y1=\"9\" x2=\"9\" y2=\"15\" />\n <line x1=\"9\" y1=\"9\" x2=\"15\" y2=\"15\" />\n </svg>\n );\n case 'warning':\n return (\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"currentColor\"\n stroke=\"none\"\n >\n <path d=\"M12 2L1 21h22L12 2zm0 3.99L19.53 19H4.47L12 5.99zM11 16h2v2h-2zm0-6h2v4h-2z\" />\n </svg>\n );\n case 'info':\n return (\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"currentColor\"\n stroke=\"none\"\n >\n <path d=\"M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8zm-1-9h2v6h-2zm0-4h2v2h-2z\" />\n </svg>\n );\n default:\n return null;\n }\n };\n\n const displayIcon = icon !== undefined ? icon : getDefaultIcon();\n\n if (!isOpen) {\n return null;\n }\n\n return (\n <Modal\n isActive={isOpen}\n onClose={handleCancel}\n role=\"presentation\"\n portal={portal}\n >\n <Modal.Background onClick={handleBackgroundClick} />\n <div\n ref={combinedRef}\n className={combinedClasses}\n role=\"alertdialog\"\n aria-modal=\"true\"\n aria-labelledby={title ? 'dialog-title' : undefined}\n aria-describedby=\"dialog-message\"\n {...rest}\n >\n {title && (\n <div className={headerClass}>\n {displayIcon && <span className={iconClass}>{displayIcon}</span>}\n <h3 id=\"dialog-title\" className={titleClass}>\n {title}\n </h3>\n </div>\n )}\n <div id=\"dialog-message\" className={bodyClass}>\n {message}\n </div>\n <div className={footerClass}>\n {showCancel && (\n <button\n ref={cancelRef}\n type=\"button\"\n className={cancelButtonClass}\n onClick={handleCancel}\n >\n {cancelText}\n </button>\n )}\n <button\n ref={confirmRef}\n type=\"button\"\n className={confirmButtonClass}\n onClick={handleConfirm}\n >\n {confirmText}\n </button>\n </div>\n </div>\n </Modal>\n );\n }\n);\n\nDialog.displayName = 'Dialog';\n\n// Dialog Manager for programmatic dialogs\n\n/**\n * Options for showing a programmatic alert dialog.\n */\nexport interface AlertOptions {\n /** Dialog title. */\n title?: string;\n /** Dialog message/content. */\n message: string | React.ReactNode;\n /** Dialog type/color. */\n type?: DialogType;\n /** Text for the confirm button. */\n confirmText?: string;\n /** Custom icon to display. */\n icon?: React.ReactNode;\n}\n\n/**\n * Options for showing a programmatic confirm dialog.\n */\nexport interface ConfirmOptions extends AlertOptions {\n /** Text for the cancel button. */\n cancelText?: string;\n /** Focus cancel button instead of confirm. */\n focusCancel?: boolean;\n}\n\n/**\n * Options for showing a programmatic prompt dialog.\n */\nexport interface PromptOptions extends ConfirmOptions {\n /** Input placeholder text. */\n placeholder?: string;\n /** Default input value. */\n defaultValue?: string;\n}\n\ntype DialogResolve = (value?: void | boolean | string) => void;\n\ntype DialogState = {\n type: 'alert' | 'confirm' | 'prompt';\n options: PromptOptions;\n resolve: DialogResolve;\n};\n\nlet dialogListeners: Set<(dialog: DialogState | null) => void> = new Set();\nlet currentDialog: DialogState | null = null;\n\n// DialogContainer renders from the open dialog rather than from updates alone,\n// so a dialog raised before it mounted still opens once it does.\nconst getCurrentDialog = () => currentDialog;\n\n// The server renders no dialog: its copy of this module is shared by every\n// request, so the dialog it holds may belong to another page. Hydration reads\n// the same empty value, which keeps the first client render matching the\n// server's, and the dialog opens right after.\nconst getServerDialog = (): DialogState | null => null;\n\nconst notifyDialogListeners = () => {\n dialogListeners.forEach(listener => listener(currentDialog));\n};\n\n/**\n * Programmatic dialog API for showing dialogs from anywhere.\n */\nexport const dialog = {\n /**\n * Show an alert dialog. Returns a promise that resolves when closed.\n */\n alert: (options: AlertOptions | string): Promise<void> => {\n return new Promise<void>(resolve => {\n const opts = typeof options === 'string' ? { message: options } : options;\n currentDialog = {\n type: 'alert',\n options: opts,\n resolve: () => resolve(),\n };\n notifyDialogListeners();\n });\n },\n\n /**\n * Show a confirm dialog. Returns a promise that resolves to true/false.\n */\n confirm: (options: ConfirmOptions | string): Promise<boolean> => {\n return new Promise<boolean>(resolve => {\n const opts = typeof options === 'string' ? { message: options } : options;\n currentDialog = {\n type: 'confirm',\n options: opts,\n resolve: value => resolve(Boolean(value)),\n };\n notifyDialogListeners();\n });\n },\n\n /**\n * Close the current dialog.\n */\n close: (value?: void | boolean | string): void => {\n if (currentDialog) {\n currentDialog.resolve(value);\n currentDialog = null;\n notifyDialogListeners();\n }\n },\n\n /**\n * Subscribe to dialog changes.\n */\n subscribe: (listener: (dialog: DialogState | null) => void): (() => void) => {\n dialogListeners.add(listener);\n return () => dialogListeners.delete(listener);\n },\n};\n\n/**\n * DialogContainer component to render programmatic dialogs.\n * Place this once at the root of your app.\n *\n * @function\n * @returns {JSX.Element | null} The rendered dialog, or null if none is active.\n */\nexport const DialogContainer: React.FC = () => {\n // Starts from the dialog already open instead of none, then follows changes.\n const current = useSyncExternalStore(\n dialog.subscribe,\n getCurrentDialog,\n getServerDialog\n );\n\n if (!current) {\n return null;\n }\n\n const { type, options } = current;\n\n return (\n <Dialog\n isOpen\n title={options.title}\n message={options.message}\n type={options.type}\n confirmText={options.confirmText}\n cancelText={options.cancelText}\n icon={options.icon}\n showCancel={type === 'confirm'}\n focusCancel={options.focusCancel}\n onConfirm={() => dialog.close(type === 'confirm' ? true : undefined)}\n onCancel={() => dialog.close(type === 'confirm' ? false : undefined)}\n />\n );\n};\n\nexport default Dialog;\n"],"names":["_jsx","_jsxs"],"mappings":"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"}
1
+ {"version":3,"file":"Dialog.js","sources":["../../../src/components/Dialog.tsx"],"sourcesContent":["import React, {\n forwardRef,\n useCallback,\n useEffect,\n useRef,\n useSyncExternalStore,\n} from 'react';\nimport { classNames, usePrefixedClassNames } from '../helpers/classNames';\nimport { useBulmaClasses, BulmaClassesProps } from '../helpers/useBulmaClasses';\nimport { useIsHydrated } from '../helpers/useIsHydrated';\nimport { useConfig } from '../helpers/Config';\nimport { Modal } from './Modal';\n\n/** Valid dialog type/color values. */\nexport type DialogType = 'default' | 'success' | 'danger' | 'warning' | 'info';\n\n/**\n * Props for the Dialog component.\n * @extraProp {string} [className] - Additional CSS classes.\n * @extraProp {React.Ref<HTMLElement>} [ref] - Ref forwarded to the dialog element.\n */\nexport interface DialogProps\n extends\n Omit<React.HTMLAttributes<HTMLDivElement>, 'color'>,\n Omit<BulmaClassesProps, 'color'> {\n /** Whether the dialog is open (required). */\n isOpen: boolean;\n /** Dialog title. */\n title?: string;\n /** Dialog message/content (required). */\n message: string | React.ReactNode;\n /** The type/color of the dialog. Default: 'default'. */\n type?: DialogType;\n /** Text for confirm button. Default: 'OK'. */\n confirmText?: string;\n /** Text for cancel button. Default: 'Cancel'. */\n cancelText?: string;\n /** Callback when confirm button is clicked. */\n onConfirm?: () => void;\n /** Callback when cancel button is clicked or dismissed. */\n onCancel?: () => void;\n /** Whether to show cancel button. Default: true for confirm dialogs. */\n showCancel?: boolean;\n /** Whether the dialog can be dismissed. Default: true. */\n canCancel?: boolean;\n /** Focus cancel button instead of confirm. Default: false. */\n focusCancel?: boolean;\n /** Custom icon to display. */\n icon?: React.ReactNode;\n /**\n * Renders the dialog into a portal target instead of inline. Forwarded to\n * the underlying `Modal`; see its `portal` prop for the accepted values.\n * @defaultValue false\n */\n portal?: boolean | string | HTMLElement;\n}\n\n/**\n * The `Dialog` component provides ready-made confirm and alert dialogs, so a destructive action stays one `await dialog.confirm()` call away.\n *\n * @function\n * @param {DialogProps} props - Props for the Dialog component.\n * @param {React.Ref<HTMLDivElement>} ref - Forwarded ref to the dialog element.\n * @returns {JSX.Element | null} The rendered dialog component.\n *\n * @example\n * // Alert dialog\n * <Dialog\n * isOpen={showAlert}\n * title=\"Success\"\n * message=\"Operation completed successfully!\"\n * type=\"success\"\n * onConfirm={() => setShowAlert(false)}\n * showCancel={false}\n * />\n *\n * @example\n * // Confirm dialog\n * <Dialog\n * isOpen={showConfirm}\n * title=\"Delete Item?\"\n * message=\"This action cannot be undone.\"\n * type=\"danger\"\n * confirmText=\"Delete\"\n * onConfirm={handleDelete}\n * onCancel={() => setShowConfirm(false)}\n * />\n */\nexport const Dialog = forwardRef<HTMLDivElement, DialogProps>(\n (\n {\n isOpen,\n title,\n message,\n type = 'default',\n confirmText = 'OK',\n cancelText = 'Cancel',\n onConfirm,\n onCancel,\n showCancel = true,\n canCancel = true,\n focusCancel = false,\n icon,\n portal,\n className,\n ...props\n },\n ref\n ) => {\n const { bulmaHelperClasses, rest } = useBulmaClasses(props);\n const dialogRef = useRef<HTMLDivElement>(null);\n // Cleanup returned by a consumer's callback ref on attach, held until detach.\n const consumerCleanupRef = useRef<(() => void) | null>(null);\n const confirmRef = useRef<HTMLButtonElement>(null);\n const cancelRef = useRef<HTMLButtonElement>(null);\n\n // Handle cancel\n const handleCancel = useCallback(() => {\n if (canCancel) {\n onCancel?.();\n }\n }, [canCancel, onCancel]);\n\n // Handle confirm\n const handleConfirm = useCallback(() => {\n onConfirm?.();\n }, [onConfirm]);\n\n // Handle background click\n const handleBackgroundClick = useCallback(() => {\n if (canCancel) {\n handleCancel();\n }\n }, [canCancel, handleCancel]);\n\n // Moving into the portal, or from one portal target to another, remounts\n // the Modal's subtree, these buttons included, so the focus effect below\n // has to re-run against the new nodes, exactly as the Modal's own focus\n // effect does, on the same inputs.\n const isHydrated = useIsHydrated();\n const isPortaled = Boolean(portal) && isHydrated;\n const { portalContainer } = useConfig();\n const defaultTarget =\n isPortaled && portal === true ? portalContainer : undefined;\n\n // Focus management — Dialog picks a specific button rather than the\n // Modal's default (first focusable/root); this effect runs after the\n // inner Modal's own focus-on-open effect, so it wins.\n useEffect(() => {\n if (isOpen) {\n const buttonToFocus =\n focusCancel && showCancel ? cancelRef.current : confirmRef.current;\n buttonToFocus?.focus();\n }\n }, [isOpen, focusCancel, showCancel, isPortaled, portal, defaultTarget]);\n\n // Use combined ref\n const combinedRef = useCallback(\n (node: HTMLDivElement | null) => {\n (dialogRef as React.MutableRefObject<HTMLDivElement | null>).current =\n node;\n if (typeof ref === 'function') {\n // React 19 lets a callback ref return a cleanup function and detaches by\n // running it instead of calling the ref with `null`; React 18 discards the\n // return value entirely. Returning it from here would be a React-19-only\n // contract, so we hold the cleanup and run it ourselves on detach — the\n // same shape as `Dropdown`, `Modal`, `Sidebar`, `Toast` and `Carousel`.\n // The form controls that merge a forwarded ref still discard the cleanup\n // and detach with `ref(null)`, so this is not yet library-wide — grep\n // `typeof ref === 'function'` for the current split.\n if (node === null) {\n const consumerCleanup = consumerCleanupRef.current;\n consumerCleanupRef.current = null;\n if (consumerCleanup) {\n consumerCleanup();\n } else {\n ref(null);\n }\n return;\n }\n const cleanup: unknown = ref(node);\n consumerCleanupRef.current =\n typeof cleanup === 'function' ? (cleanup as () => void) : null;\n } else if (ref) {\n (ref as React.MutableRefObject<HTMLDivElement | null>).current = node;\n }\n },\n [ref]\n );\n\n // Generate classes\n const dialogClasses = usePrefixedClassNames('dialog', {\n [`is-${type}`]: type !== 'default',\n });\n const headerClass = usePrefixedClassNames('dialog-header');\n const iconClass = usePrefixedClassNames('dialog-icon');\n const titleClass = usePrefixedClassNames('dialog-title');\n const bodyClass = usePrefixedClassNames('dialog-body');\n const footerClass = usePrefixedClassNames('dialog-footer');\n const cancelButtonClass = usePrefixedClassNames('button');\n const confirmButtonClass = usePrefixedClassNames('button', {\n 'is-success': type === 'success',\n 'is-danger': type === 'danger',\n 'is-warning': type === 'warning',\n 'is-info': type === 'info',\n 'is-primary': type === 'default',\n });\n\n const combinedClasses = classNames(\n dialogClasses,\n bulmaHelperClasses,\n className\n );\n\n // Get default icon based on type\n const getDefaultIcon = () => {\n switch (type) {\n case 'success':\n return (\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n >\n <path d=\"M20 6L9 17l-5-5\" />\n </svg>\n );\n case 'danger':\n return (\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n >\n <circle cx=\"12\" cy=\"12\" r=\"10\" />\n <line x1=\"15\" y1=\"9\" x2=\"9\" y2=\"15\" />\n <line x1=\"9\" y1=\"9\" x2=\"15\" y2=\"15\" />\n </svg>\n );\n case 'warning':\n return (\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"currentColor\"\n stroke=\"none\"\n >\n <path d=\"M12 2L1 21h22L12 2zm0 3.99L19.53 19H4.47L12 5.99zM11 16h2v2h-2zm0-6h2v4h-2z\" />\n </svg>\n );\n case 'info':\n return (\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"currentColor\"\n stroke=\"none\"\n >\n <path d=\"M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8zm-1-9h2v6h-2zm0-4h2v2h-2z\" />\n </svg>\n );\n default:\n return null;\n }\n };\n\n const displayIcon = icon !== undefined ? icon : getDefaultIcon();\n\n if (!isOpen) {\n return null;\n }\n\n return (\n <Modal\n isActive={isOpen}\n onClose={handleCancel}\n role=\"presentation\"\n portal={portal}\n >\n <Modal.Background onClick={handleBackgroundClick} />\n <div\n ref={combinedRef}\n className={combinedClasses}\n role=\"alertdialog\"\n aria-modal=\"true\"\n aria-labelledby={title ? 'dialog-title' : undefined}\n aria-describedby=\"dialog-message\"\n {...rest}\n >\n {title && (\n <div className={headerClass}>\n {displayIcon && <span className={iconClass}>{displayIcon}</span>}\n <h3 id=\"dialog-title\" className={titleClass}>\n {title}\n </h3>\n </div>\n )}\n <div id=\"dialog-message\" className={bodyClass}>\n {message}\n </div>\n <div className={footerClass}>\n {showCancel && (\n <button\n ref={cancelRef}\n type=\"button\"\n className={cancelButtonClass}\n onClick={handleCancel}\n >\n {cancelText}\n </button>\n )}\n <button\n ref={confirmRef}\n type=\"button\"\n className={confirmButtonClass}\n onClick={handleConfirm}\n >\n {confirmText}\n </button>\n </div>\n </div>\n </Modal>\n );\n }\n);\n\nDialog.displayName = 'Dialog';\n\n// Dialog Manager for programmatic dialogs\n\n/**\n * Options for showing a programmatic alert dialog.\n */\nexport interface AlertOptions {\n /** Dialog title. */\n title?: string;\n /** Dialog message/content. */\n message: string | React.ReactNode;\n /** Dialog type/color. */\n type?: DialogType;\n /** Text for the confirm button. */\n confirmText?: string;\n /** Custom icon to display. */\n icon?: React.ReactNode;\n}\n\n/**\n * Options for showing a programmatic confirm dialog.\n */\nexport interface ConfirmOptions extends AlertOptions {\n /** Text for the cancel button. */\n cancelText?: string;\n /** Focus cancel button instead of confirm. */\n focusCancel?: boolean;\n}\n\n/**\n * Options for showing a programmatic prompt dialog.\n */\nexport interface PromptOptions extends ConfirmOptions {\n /** Input placeholder text. */\n placeholder?: string;\n /** Default input value. */\n defaultValue?: string;\n}\n\ntype DialogResolve = (value?: void | boolean | string) => void;\n\ntype DialogState = {\n type: 'alert' | 'confirm' | 'prompt';\n options: PromptOptions;\n resolve: DialogResolve;\n};\n\nlet dialogListeners: Set<(dialog: DialogState | null) => void> = new Set();\nlet currentDialog: DialogState | null = null;\n\n// DialogContainer renders from the open dialog rather than from updates alone,\n// so a dialog raised before it mounted still opens once it does.\nconst getCurrentDialog = () => currentDialog;\n\n// The server renders no dialog: its copy of this module is shared by every\n// request, so the dialog it holds may belong to another page. Hydration reads\n// the same empty value, which keeps the first client render matching the\n// server's, and the dialog opens right after.\nconst getServerDialog = (): DialogState | null => null;\n\nconst notifyDialogListeners = () => {\n dialogListeners.forEach(listener => listener(currentDialog));\n};\n\n/**\n * Programmatic dialog API for showing dialogs from anywhere.\n */\nexport const dialog = {\n /**\n * Show an alert dialog. Returns a promise that resolves when closed.\n */\n alert: (options: AlertOptions | string): Promise<void> => {\n return new Promise<void>(resolve => {\n const opts = typeof options === 'string' ? { message: options } : options;\n currentDialog = {\n type: 'alert',\n options: opts,\n resolve: () => resolve(),\n };\n notifyDialogListeners();\n });\n },\n\n /**\n * Show a confirm dialog. Returns a promise that resolves to true/false.\n */\n confirm: (options: ConfirmOptions | string): Promise<boolean> => {\n return new Promise<boolean>(resolve => {\n const opts = typeof options === 'string' ? { message: options } : options;\n currentDialog = {\n type: 'confirm',\n options: opts,\n resolve: value => resolve(Boolean(value)),\n };\n notifyDialogListeners();\n });\n },\n\n /**\n * Close the current dialog.\n */\n close: (value?: void | boolean | string): void => {\n if (currentDialog) {\n currentDialog.resolve(value);\n currentDialog = null;\n notifyDialogListeners();\n }\n },\n\n /**\n * Subscribe to dialog changes.\n */\n subscribe: (listener: (dialog: DialogState | null) => void): (() => void) => {\n dialogListeners.add(listener);\n return () => dialogListeners.delete(listener);\n },\n};\n\n/**\n * DialogContainer component to render programmatic dialogs.\n * Place this once at the root of your app.\n *\n * @function\n * @returns {JSX.Element | null} The rendered dialog, or null if none is active.\n */\nexport const DialogContainer: React.FC = () => {\n // Starts from the dialog already open instead of none, then follows changes.\n const current = useSyncExternalStore(\n dialog.subscribe,\n getCurrentDialog,\n getServerDialog\n );\n\n if (!current) {\n return null;\n }\n\n const { type, options } = current;\n\n return (\n <Dialog\n isOpen\n title={options.title}\n message={options.message}\n type={options.type}\n confirmText={options.confirmText}\n cancelText={options.cancelText}\n icon={options.icon}\n showCancel={type === 'confirm'}\n focusCancel={options.focusCancel}\n onConfirm={() => dialog.close(type === 'confirm' ? true : undefined)}\n onCancel={() => dialog.close(type === 'confirm' ? false : undefined)}\n />\n );\n};\n\nexport default Dialog;\n"],"names":["_jsx","_jsxs"],"mappings":";;;;;;;;;AAyDA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA8BG;AACI,MAAM,MAAM,GAAG,UAAU,CAC9B,CACE,EACE,MAAM,EACN,KAAK,EACL,OAAO,EACP,IAAI,GAAG,SAAS,EAChB,WAAW,GAAG,IAAI,EAClB,UAAU,GAAG,QAAQ,EACrB,SAAS,EACT,QAAQ,EACR,UAAU,GAAG,IAAI,EACjB,SAAS,GAAG,IAAI,EAChB,WAAW,GAAG,KAAK,EACnB,IAAI,EACJ,MAAM,EACN,SAAS,EACT,GAAG,KAAK,EACT,EACD,GAAG,KACD;IACF,MAAM,EAAE,kBAAkB,EAAE,IAAI,EAAE,GAAG,eAAe,CAAC,KAAK,CAAC;AAC3D,IAAA,MAAM,SAAS,GAAG,MAAM,CAAiB,IAAI,CAAC;;AAE9C,IAAA,MAAM,kBAAkB,GAAG,MAAM,CAAsB,IAAI,CAAC;AAC5D,IAAA,MAAM,UAAU,GAAG,MAAM,CAAoB,IAAI,CAAC;AAClD,IAAA,MAAM,SAAS,GAAG,MAAM,CAAoB,IAAI,CAAC;;AAGjD,IAAA,MAAM,YAAY,GAAG,WAAW,CAAC,MAAK;QACpC,IAAI,SAAS,EAAE;YACb,QAAQ,IAAI;QACd;AACF,IAAA,CAAC,EAAE,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC;;AAGzB,IAAA,MAAM,aAAa,GAAG,WAAW,CAAC,MAAK;QACrC,SAAS,IAAI;AACf,IAAA,CAAC,EAAE,CAAC,SAAS,CAAC,CAAC;;AAGf,IAAA,MAAM,qBAAqB,GAAG,WAAW,CAAC,MAAK;QAC7C,IAAI,SAAS,EAAE;AACb,YAAA,YAAY,EAAE;QAChB;AACF,IAAA,CAAC,EAAE,CAAC,SAAS,EAAE,YAAY,CAAC,CAAC;;;;;AAM7B,IAAA,MAAM,UAAU,GAAG,aAAa,EAAE;IAClC,MAAM,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC,IAAI,UAAU;AAChD,IAAA,MAAM,EAAE,eAAe,EAAE,GAAG,SAAS,EAAE;AACvC,IAAA,MAAM,aAAa,GACjB,UAAU,IAAI,MAAM,KAAK,IAAI,GAAG,eAAe,GAAG,SAAS;;;;IAK7D,SAAS,CAAC,MAAK;QACb,IAAI,MAAM,EAAE;AACV,YAAA,MAAM,aAAa,GACjB,WAAW,IAAI,UAAU,GAAG,SAAS,CAAC,OAAO,GAAG,UAAU,CAAC,OAAO;YACpE,aAAa,EAAE,KAAK,EAAE;QACxB;AACF,IAAA,CAAC,EAAE,CAAC,MAAM,EAAE,WAAW,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,EAAE,aAAa,CAAC,CAAC;;AAGxE,IAAA,MAAM,WAAW,GAAG,WAAW,CAC7B,CAAC,IAA2B,KAAI;AAC7B,QAAA,SAA2D,CAAC,OAAO;AAClE,YAAA,IAAI;AACN,QAAA,IAAI,OAAO,GAAG,KAAK,UAAU,EAAE;;;;;;;;;AAS7B,YAAA,IAAI,IAAI,KAAK,IAAI,EAAE;AACjB,gBAAA,MAAM,eAAe,GAAG,kBAAkB,CAAC,OAAO;AAClD,gBAAA,kBAAkB,CAAC,OAAO,GAAG,IAAI;gBACjC,IAAI,eAAe,EAAE;AACnB,oBAAA,eAAe,EAAE;gBACnB;qBAAO;oBACL,GAAG,CAAC,IAAI,CAAC;gBACX;gBACA;YACF;AACA,YAAA,MAAM,OAAO,GAAY,GAAG,CAAC,IAAI,CAAC;AAClC,YAAA,kBAAkB,CAAC,OAAO;gBACxB,OAAO,OAAO,KAAK,UAAU,GAAI,OAAsB,GAAG,IAAI;QAClE;aAAO,IAAI,GAAG,EAAE;AACb,YAAA,GAAqD,CAAC,OAAO,GAAG,IAAI;QACvE;AACF,IAAA,CAAC,EACD,CAAC,GAAG,CAAC,CACN;;AAGD,IAAA,MAAM,aAAa,GAAG,qBAAqB,CAAC,QAAQ,EAAE;AACpD,QAAA,CAAC,MAAM,IAAI,CAAA,CAAE,GAAG,IAAI,KAAK,SAAS;AACnC,KAAA,CAAC;AACF,IAAA,MAAM,WAAW,GAAG,qBAAqB,CAAC,eAAe,CAAC;AAC1D,IAAA,MAAM,SAAS,GAAG,qBAAqB,CAAC,aAAa,CAAC;AACtD,IAAA,MAAM,UAAU,GAAG,qBAAqB,CAAC,cAAc,CAAC;AACxD,IAAA,MAAM,SAAS,GAAG,qBAAqB,CAAC,aAAa,CAAC;AACtD,IAAA,MAAM,WAAW,GAAG,qBAAqB,CAAC,eAAe,CAAC;AAC1D,IAAA,MAAM,iBAAiB,GAAG,qBAAqB,CAAC,QAAQ,CAAC;AACzD,IAAA,MAAM,kBAAkB,GAAG,qBAAqB,CAAC,QAAQ,EAAE;QACzD,YAAY,EAAE,IAAI,KAAK,SAAS;QAChC,WAAW,EAAE,IAAI,KAAK,QAAQ;QAC9B,YAAY,EAAE,IAAI,KAAK,SAAS;QAChC,SAAS,EAAE,IAAI,KAAK,MAAM;QAC1B,YAAY,EAAE,IAAI,KAAK,SAAS;AACjC,KAAA,CAAC;IAEF,MAAM,eAAe,GAAG,UAAU,CAChC,aAAa,EACb,kBAAkB,EAClB,SAAS,CACV;;IAGD,MAAM,cAAc,GAAG,MAAK;QAC1B,QAAQ,IAAI;AACV,YAAA,KAAK,SAAS;AACZ,gBAAA,QACEA,GAAA,CAAA,KAAA,EAAA,EACE,KAAK,EAAC,4BAA4B,EAClC,OAAO,EAAC,WAAW,EACnB,IAAI,EAAC,MAAM,EACX,MAAM,EAAC,cAAc,EACrB,WAAW,EAAC,GAAG,EAAA,QAAA,EAEfA,GAAA,CAAA,MAAA,EAAA,EAAM,CAAC,EAAC,iBAAiB,EAAA,CAAG,EAAA,CACxB;AAEV,YAAA,KAAK,QAAQ;AACX,gBAAA,QACEC,IAAA,CAAA,KAAA,EAAA,EACE,KAAK,EAAC,4BAA4B,EAClC,OAAO,EAAC,WAAW,EACnB,IAAI,EAAC,MAAM,EACX,MAAM,EAAC,cAAc,EACrB,WAAW,EAAC,GAAG,EAAA,QAAA,EAAA,CAEfD,GAAA,CAAA,QAAA,EAAA,EAAQ,EAAE,EAAC,IAAI,EAAC,EAAE,EAAC,IAAI,EAAC,CAAC,EAAC,IAAI,EAAA,CAAG,EACjCA,GAAA,CAAA,MAAA,EAAA,EAAM,EAAE,EAAC,IAAI,EAAC,EAAE,EAAC,GAAG,EAAC,EAAE,EAAC,GAAG,EAAC,EAAE,EAAC,IAAI,GAAG,EACtCA,GAAA,CAAA,MAAA,EAAA,EAAM,EAAE,EAAC,GAAG,EAAC,EAAE,EAAC,GAAG,EAAC,EAAE,EAAC,IAAI,EAAC,EAAE,EAAC,IAAI,EAAA,CAAG,CAAA,EAAA,CAClC;AAEV,YAAA,KAAK,SAAS;gBACZ,QACEA,GAAA,CAAA,KAAA,EAAA,EACE,KAAK,EAAC,4BAA4B,EAClC,OAAO,EAAC,WAAW,EACnB,IAAI,EAAC,cAAc,EACnB,MAAM,EAAC,MAAM,EAAA,QAAA,EAEbA,GAAA,CAAA,MAAA,EAAA,EAAM,CAAC,EAAC,6EAA6E,EAAA,CAAG,EAAA,CACpF;AAEV,YAAA,KAAK,MAAM;gBACT,QACEA,GAAA,CAAA,KAAA,EAAA,EACE,KAAK,EAAC,4BAA4B,EAClC,OAAO,EAAC,WAAW,EACnB,IAAI,EAAC,cAAc,EACnB,MAAM,EAAC,MAAM,EAAA,QAAA,EAEbA,GAAA,CAAA,MAAA,EAAA,EAAM,CAAC,EAAC,uJAAuJ,EAAA,CAAG,EAAA,CAC9J;AAEV,YAAA;AACE,gBAAA,OAAO,IAAI;;AAEjB,IAAA,CAAC;AAED,IAAA,MAAM,WAAW,GAAG,IAAI,KAAK,SAAS,GAAG,IAAI,GAAG,cAAc,EAAE;IAEhE,IAAI,CAAC,MAAM,EAAE;AACX,QAAA,OAAO,IAAI;IACb;AAEA,IAAA,QACEC,IAAA,CAAC,KAAK,EAAA,EACJ,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,YAAY,EACrB,IAAI,EAAC,cAAc,EACnB,MAAM,EAAE,MAAM,aAEdD,GAAA,CAAC,KAAK,CAAC,UAAU,IAAC,OAAO,EAAE,qBAAqB,EAAA,CAAI,EACpDC,IAAA,CAAA,KAAA,EAAA,EACE,GAAG,EAAE,WAAW,EAChB,SAAS,EAAE,eAAe,EAC1B,IAAI,EAAC,aAAa,EAAA,YAAA,EACP,MAAM,qBACA,KAAK,GAAG,cAAc,GAAG,SAAS,sBAClC,gBAAgB,EAAA,GAC7B,IAAI,EAAA,QAAA,EAAA,CAEP,KAAK,KACJA,IAAA,CAAA,KAAA,EAAA,EAAK,SAAS,EAAE,WAAW,EAAA,QAAA,EAAA,CACxB,WAAW,IAAID,cAAM,SAAS,EAAE,SAAS,EAAA,QAAA,EAAG,WAAW,EAAA,CAAQ,EAChEA,GAAA,CAAA,IAAA,EAAA,EAAI,EAAE,EAAC,cAAc,EAAC,SAAS,EAAE,UAAU,EAAA,QAAA,EACxC,KAAK,EAAA,CACH,CAAA,EAAA,CACD,CACP,EACDA,GAAA,CAAA,KAAA,EAAA,EAAK,EAAE,EAAC,gBAAgB,EAAC,SAAS,EAAE,SAAS,YAC1C,OAAO,EAAA,CACJ,EACNC,IAAA,CAAA,KAAA,EAAA,EAAK,SAAS,EAAE,WAAW,EAAA,QAAA,EAAA,CACxB,UAAU,KACTD,GAAA,CAAA,QAAA,EAAA,EACE,GAAG,EAAE,SAAS,EACd,IAAI,EAAC,QAAQ,EACb,SAAS,EAAE,iBAAiB,EAC5B,OAAO,EAAE,YAAY,EAAA,QAAA,EAEpB,UAAU,GACJ,CACV,EACDA,GAAA,CAAA,QAAA,EAAA,EACE,GAAG,EAAE,UAAU,EACf,IAAI,EAAC,QAAQ,EACb,SAAS,EAAE,kBAAkB,EAC7B,OAAO,EAAE,aAAa,EAAA,QAAA,EAErB,WAAW,EAAA,CACL,CAAA,EAAA,CACL,CAAA,EAAA,CACF,CAAA,EAAA,CACA;AAEZ,CAAC;AAGH,MAAM,CAAC,WAAW,GAAG,QAAQ;AAgD7B,IAAI,eAAe,GAA8C,IAAI,GAAG,EAAE;AAC1E,IAAI,aAAa,GAAuB,IAAI;AAE5C;AACA;AACA,MAAM,gBAAgB,GAAG,MAAM,aAAa;AAE5C;AACA;AACA;AACA;AACA,MAAM,eAAe,GAAG,MAA0B,IAAI;AAEtD,MAAM,qBAAqB,GAAG,MAAK;AACjC,IAAA,eAAe,CAAC,OAAO,CAAC,QAAQ,IAAI,QAAQ,CAAC,aAAa,CAAC,CAAC;AAC9D,CAAC;AAED;;AAEG;AACI,MAAM,MAAM,GAAG;AACpB;;AAEG;AACH,IAAA,KAAK,EAAE,CAAC,OAA8B,KAAmB;AACvD,QAAA,OAAO,IAAI,OAAO,CAAO,OAAO,IAAG;AACjC,YAAA,MAAM,IAAI,GAAG,OAAO,OAAO,KAAK,QAAQ,GAAG,EAAE,OAAO,EAAE,OAAO,EAAE,GAAG,OAAO;AACzE,YAAA,aAAa,GAAG;AACd,gBAAA,IAAI,EAAE,OAAO;AACb,gBAAA,OAAO,EAAE,IAAI;AACb,gBAAA,OAAO,EAAE,MAAM,OAAO,EAAE;aACzB;AACD,YAAA,qBAAqB,EAAE;AACzB,QAAA,CAAC,CAAC;IACJ,CAAC;AAED;;AAEG;AACH,IAAA,OAAO,EAAE,CAAC,OAAgC,KAAsB;AAC9D,QAAA,OAAO,IAAI,OAAO,CAAU,OAAO,IAAG;AACpC,YAAA,MAAM,IAAI,GAAG,OAAO,OAAO,KAAK,QAAQ,GAAG,EAAE,OAAO,EAAE,OAAO,EAAE,GAAG,OAAO;AACzE,YAAA,aAAa,GAAG;AACd,gBAAA,IAAI,EAAE,SAAS;AACf,gBAAA,OAAO,EAAE,IAAI;gBACb,OAAO,EAAE,KAAK,IAAI,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;aAC1C;AACD,YAAA,qBAAqB,EAAE;AACzB,QAAA,CAAC,CAAC;IACJ,CAAC;AAED;;AAEG;AACH,IAAA,KAAK,EAAE,CAAC,KAA+B,KAAU;QAC/C,IAAI,aAAa,EAAE;AACjB,YAAA,aAAa,CAAC,OAAO,CAAC,KAAK,CAAC;YAC5B,aAAa,GAAG,IAAI;AACpB,YAAA,qBAAqB,EAAE;QACzB;IACF,CAAC;AAED;;AAEG;AACH,IAAA,SAAS,EAAE,CAAC,QAA8C,KAAkB;AAC1E,QAAA,eAAe,CAAC,GAAG,CAAC,QAAQ,CAAC;QAC7B,OAAO,MAAM,eAAe,CAAC,MAAM,CAAC,QAAQ,CAAC;IAC/C,CAAC;;AAGH;;;;;;AAMG;AACI,MAAM,eAAe,GAAa,MAAK;;AAE5C,IAAA,MAAM,OAAO,GAAG,oBAAoB,CAClC,MAAM,CAAC,SAAS,EAChB,gBAAgB,EAChB,eAAe,CAChB;IAED,IAAI,CAAC,OAAO,EAAE;AACZ,QAAA,OAAO,IAAI;IACb;AAEA,IAAA,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,OAAO;IAEjC,QACEA,IAAC,MAAM,EAAA,EACL,MAAM,EAAA,IAAA,EACN,KAAK,EAAE,OAAO,CAAC,KAAK,EACpB,OAAO,EAAE,OAAO,CAAC,OAAO,EACxB,IAAI,EAAE,OAAO,CAAC,IAAI,EAClB,WAAW,EAAE,OAAO,CAAC,WAAW,EAChC,UAAU,EAAE,OAAO,CAAC,UAAU,EAC9B,IAAI,EAAE,OAAO,CAAC,IAAI,EAClB,UAAU,EAAE,IAAI,KAAK,SAAS,EAC9B,WAAW,EAAE,OAAO,CAAC,WAAW,EAChC,SAAS,EAAE,MAAM,MAAM,CAAC,KAAK,CAAC,IAAI,KAAK,SAAS,GAAG,IAAI,GAAG,SAAS,CAAC,EACpE,QAAQ,EAAE,MAAM,MAAM,CAAC,KAAK,CAAC,IAAI,KAAK,SAAS,GAAG,KAAK,GAAG,SAAS,CAAC,EAAA,CACpE;AAEN;;"}
@@ -1 +1 @@
1
- {"version":3,"file":"HoverCard.js","sources":["../../../src/components/HoverCard.tsx"],"sourcesContent":["import React, {\n cloneElement,\n useCallback,\n useEffect,\n useId,\n useMemo,\n useRef,\n useState,\n} from 'react';\nimport { classNames, usePrefixedClassNames } from '../helpers/classNames';\nimport { useBulmaClasses, BulmaClassesProps } from '../helpers/useBulmaClasses';\nimport { Portal } from '../helpers/portal';\nimport { useIsHydrated } from '../helpers/useIsHydrated';\nimport { marginGap, useAnchoredPosition } from '../helpers/useAnchoredPosition';\nimport { useHoverOpen } from '../helpers/useHoverOpen';\nimport { isTabbableKind } from '../helpers/useFocusTrap';\nimport { isDev, warnOnce } from '../helpers/devWarnings';\nimport type { PickerPosition } from '../form/_pickerInternals/pickerTypes';\n\n/**\n * Where the card opens against its trigger: below (`bottom-*`) or above\n * (`top-*`), lined up with the trigger's left or right edge. `auto` picks the\n * one that keeps the card in the viewport, preferring below and lined up on\n * the left. The same union `Popover` and the date and time pickers take.\n */\nexport type HoverCardPosition = PickerPosition;\n\n/**\n * Props for the HoverCard component. `className`, `id`, the other HTML\n * attributes and the helper props land on the `span` around the trigger, not\n * on the card; `contentClassName` reaches the card.\n * @extraProp {string} [className] - Additional classes for the `span` around the trigger. The card takes `contentClassName`.\n */\nexport interface HoverCardProps\n extends\n Omit<React.HTMLAttributes<HTMLSpanElement>, 'color' | 'children'>,\n Omit<BulmaClassesProps, 'color' | 'backgroundColor'> {\n /**\n * The element the card previews, such as a link or a `Button`. The element\n * itself has to take focus, so keyboard users can open the card too: a\n * link inside a `<span>` doesn't do, since focus comes back to the trigger\n * and its ARIA is on it. HoverCard gives it no role and no tab stop, since\n * it can't know what the element is, and a development build warns when\n * it can't take focus: use a link or a button, or give the element\n * `tabIndex={0}`. A disabled button can't take focus either. HoverCard adds\n * `aria-expanded` and `aria-controls`, both naming the card only while it\n * is on the page, so it has to be a single element that passes those to\n * its DOM node. Its own click and keys stay its own, and the card doesn't\n * toggle on them, except Escape while the card is open, which closes the\n * card before the trigger sees it.\n */\n trigger: React.ReactElement;\n /**\n * The card's content: a preview whose links and buttons are fine, but not a\n * form, which belongs in a `Popover`. The card pads itself, so content goes\n * straight in.\n */\n children?: React.ReactNode;\n /**\n * Controlled open state. When set, hover, focus and Escape only report\n * through `onOpenChange`, so an action inside the card can close it. While\n * the card is open, Escape is reported, and when focus is in the card, on\n * its trigger or nowhere, the key stops there even if you keep the card\n * open, so a `Modal` around it doesn't close from the keyboard until the\n * card does.\n */\n open?: boolean;\n /**\n * Initial open state when uncontrolled. A card that starts open stays open\n * until the pointer or focus has been and gone, or Escape closes it.\n * @defaultValue false\n */\n defaultOpen?: boolean;\n /**\n * Called with the state asked for: when `openDelay` or `closeDelay` runs\n * out, and right away on Escape. Fires in both controlled and uncontrolled\n * use. An Escape it reports stops at the card when focus is in the card,\n * on its trigger or nowhere, so nothing around it sees that key. With\n * focus anywhere else, the card still closes and the key goes on to\n * whatever has focus.\n */\n onOpenChange?: (open: boolean) => void;\n /**\n * How long the pointer or keyboard focus waits on the trigger before the\n * card opens, in milliseconds.\n * @defaultValue 600\n */\n openDelay?: number;\n /**\n * How long the card stays after the pointer and focus have left the trigger\n * and the card, in milliseconds. It is the time the pointer has to cross\n * the gap between the trigger and the card: with `0` the card closes as the\n * pointer leaves the trigger, before the pointer can reach it.\n * @defaultValue 300\n */\n closeDelay?: number;\n /**\n * Where the card opens against the trigger.\n * @defaultValue 'bottom-left'\n */\n position?: HoverCardPosition;\n /**\n * Render the card at the end of `document.body` through `Portal`, fixed to\n * the viewport, so an ancestor's `overflow`, `transform` or stacking\n * context can't clip it. A trigger in running text needs it too, since the\n * card's block content can't sit inside a `<p>`. The card then shows from\n * the commit after hydration, never in server markup, and it sits last in\n * the Tab order, at the end of `<body>`. Tab reaches it only after the rest\n * of the page, and focus leaving the trigger on the way closes it, so\n * keyboard users can open it, read it and close it with Escape, and\n * whatever it links to has to be reachable from the page as well. It is\n * measured against the trigger when it opens and on resize and scroll, so\n * it doesn't follow content that changes size while it is open. Theming\n * variables set on an ancestor of the trigger don't reach it there.\n * @defaultValue false\n */\n appendToBody?: boolean;\n /**\n * Open the card with a tap on a touch screen. Touch has no hover, so\n * without this a phone or tablet never opens the card. With it, a tap on\n * the trigger opens the card at once, and it stays until a second tap on\n * the trigger, a tap outside the trigger and the card, or Escape. The tap\n * still does whatever the trigger does, so turn this on only for a trigger\n * with no tap action of its own, such as a `Button` with no `onClick`: on\n * a link, the first tap would follow the link rather than show the card.\n * Mouse and pen work the same either way.\n * @defaultValue false\n */\n openOnTap?: boolean;\n /** Additional classes for the card (`.hover-card-content`). */\n contentClassName?: string;\n}\n\n/** Runs the caller's handler on the wrapper, then the hover card's. */\nfunction chain<E>(\n own: ((event: E) => void) | undefined,\n ours: (event: E) => void\n): (event: E) => void {\n return event => {\n own?.(event);\n ours(event);\n };\n}\n\n/**\n * The `HoverCard` component shows a card previewing what's behind a link or a button while the pointer rests on it or keyboard focus is on it.\n *\n * Nothing moves focus when the card opens, and nothing traps it. In place,\n * the card comes right after the trigger, so Tab goes from the trigger into\n * its links and tabbing past them closes it. The pointer can move onto the\n * card, Escape closes it from anywhere, and by default touch doesn't open it,\n * so tapping the trigger just does what the trigger does. `openOnTap` opens\n * it with a tap, for a trigger with no tap action of its own. For hints with nothing to\n * click use `Tooltip`, and for content opened on purpose, such as a form,\n * use `Popover`.\n *\n * @function\n * @param {HoverCardProps} props - Props for the HoverCard component.\n * @returns {JSX.Element} The trigger in its wrapper, and the card while open.\n *\n * @example\n * <HoverCard trigger={<a href=\"/people/ada\">Ada Lovelace</a>}>\n * <Title as=\"p\" size=\"6\">Ada Lovelace</Title>\n * <SubTitle as=\"p\" size=\"6\">Analytical Engine team</SubTitle>\n * </HoverCard>\n */\nexport const HoverCard: React.FC<HoverCardProps> = ({\n trigger,\n children,\n open: controlledOpen,\n defaultOpen = false,\n onOpenChange,\n openDelay = 600,\n closeDelay = 300,\n position = 'bottom-left',\n appendToBody = false,\n openOnTap = false,\n className,\n contentClassName,\n ...props\n}) => {\n const { bulmaHelperClasses, rest } = useBulmaClasses(props);\n const {\n onPointerEnter,\n onPointerLeave,\n onPointerDown,\n onPointerUp,\n onFocus,\n onBlur,\n ...attrs\n } = rest;\n const rootRef = useRef<HTMLSpanElement>(null);\n const cardRef = useRef<HTMLDivElement>(null);\n const cardId = `${useId()}-card`;\n\n const isControlled = controlledOpen !== undefined;\n const [internalOpen, setInternalOpen] = useState(defaultOpen);\n const isOpen = controlledOpen ?? internalOpen;\n\n const requestOpen = useCallback(\n (next: boolean) => {\n onOpenChange?.(next);\n if (!isControlled) setInternalOpen(next);\n },\n [isControlled, onOpenChange]\n );\n\n // A portaled card has no server markup, so it only exists from the commit\n // after hydration, and nothing measures it before then.\n const hydrated = useIsHydrated();\n const cardMounted = isOpen && (hydrated || !appendToBody);\n\n const resolved = useAnchoredPosition(rootRef, cardRef, {\n active: cardMounted,\n position,\n fixed: appendToBody,\n // The gap comes from `--bulma-hover-card-offset` in the stylesheet, so\n // the coordinates leave it out and `auto` reads it off the card.\n offset: 0,\n styledGap: marginGap,\n });\n\n // The trigger renders first inside the wrapper. Read when needed rather\n // than held, so a trigger that remounted is still the one found.\n const triggerRef = useMemo(\n () => ({\n get current() {\n const first = (rootRef.current as HTMLSpanElement).firstElementChild;\n return first && first !== cardRef.current\n ? (first as HTMLElement)\n : null;\n },\n }),\n []\n );\n\n const hover = useHoverOpen({\n open: isOpen,\n onOpenChange: requestOpen,\n openDelay,\n closeDelay,\n triggerRef,\n floatingRef: cardRef,\n openOnTap,\n });\n\n // A trigger that can't take focus leaves keyboard users no way to open the\n // card. It has to be the trigger element itself, not something inside it,\n // since focus comes back to it and its ARIA is on it. Judged by what the\n // element is rather than by its styles, which can hide it for now. After\n // every commit, since the trigger can change, and only in development.\n useEffect(() => {\n if (!isDev()) return;\n const triggerNode = triggerRef.current;\n if (!triggerNode) return;\n if (!triggerNode.matches(':disabled') && isTabbableKind(triggerNode)) {\n return;\n }\n warnOnce(\n 'HoverCard:unfocusable-trigger',\n \"[bestax-bulma] <HoverCard> has a trigger that can't take focus, so \" +\n \"keyboard users can't open the card. Use a link or a button as the \" +\n 'trigger, or give it tabIndex={0}.'\n );\n });\n\n // Expanded only while the card is on the page, so a portaled card that\n // waits for hydration isn't announced before it exists.\n const clonedTrigger = cloneElement(\n trigger as React.ReactElement<Record<string, unknown>>,\n {\n 'aria-expanded': cardMounted,\n 'aria-controls': cardMounted ? cardId : undefined,\n }\n );\n\n const rootClasses = classNames(\n usePrefixedClassNames('hover-card', { 'is-active': cardMounted }),\n bulmaHelperClasses,\n className\n );\n const cardClasses = classNames(\n usePrefixedClassNames('hover-card-content', {\n [`is-${resolved.position}`]: true,\n 'is-portal': appendToBody,\n }),\n contentClassName\n );\n\n return (\n // The ref after the spread: React 19 hands a function component a `ref`\n // prop like any other, and the wrapper has to stay ours. The card is in\n // the wrapper's React tree even when portaled, so the hover and focus\n // handlers here hear from both.\n <span\n className={rootClasses}\n {...attrs}\n onPointerEnter={chain(onPointerEnter, hover.onPointerEnter)}\n onPointerLeave={chain(onPointerLeave, hover.onPointerLeave)}\n onFocus={chain(onFocus, hover.onFocus)}\n onBlur={chain(onBlur, hover.onBlur)}\n onPointerDown={chain(onPointerDown, hover.onPointerDown)}\n onPointerUp={chain(onPointerUp, hover.onPointerUp)}\n ref={rootRef}\n >\n {clonedTrigger}\n {isOpen && (\n <Portal disabled={!appendToBody}>\n <div\n ref={cardRef}\n id={cardId}\n className={cardClasses}\n style={\n appendToBody\n ? {\n position: 'fixed',\n top: resolved.top,\n left: resolved.left,\n }\n : undefined\n }\n >\n {children}\n </div>\n </Portal>\n )}\n </span>\n );\n};\n\nexport default HoverCard;\n"],"names":["_jsxs","_jsx"],"mappings":"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"}
1
+ {"version":3,"file":"HoverCard.js","sources":["../../../src/components/HoverCard.tsx"],"sourcesContent":["import React, {\n cloneElement,\n useCallback,\n useEffect,\n useId,\n useMemo,\n useRef,\n useState,\n} from 'react';\nimport { classNames, usePrefixedClassNames } from '../helpers/classNames';\nimport { useBulmaClasses, BulmaClassesProps } from '../helpers/useBulmaClasses';\nimport { Portal } from '../helpers/portal';\nimport { useIsHydrated } from '../helpers/useIsHydrated';\nimport { marginGap, useAnchoredPosition } from '../helpers/useAnchoredPosition';\nimport { useHoverOpen } from '../helpers/useHoverOpen';\nimport { isTabbableKind } from '../helpers/useFocusTrap';\nimport { isDev, warnOnce } from '../helpers/devWarnings';\nimport type { PickerPosition } from '../form/_pickerInternals/pickerTypes';\n\n/**\n * Where the card opens against its trigger: below (`bottom-*`) or above\n * (`top-*`), lined up with the trigger's left or right edge. `auto` picks the\n * one that keeps the card in the viewport, preferring below and lined up on\n * the left. The same union `Popover` and the date and time pickers take.\n */\nexport type HoverCardPosition = PickerPosition;\n\n/**\n * Props for the HoverCard component. `className`, `id`, the other HTML\n * attributes and the helper props land on the `span` around the trigger, not\n * on the card; `contentClassName` reaches the card.\n * @extraProp {string} [className] - Additional classes for the `span` around the trigger. The card takes `contentClassName`.\n */\nexport interface HoverCardProps\n extends\n Omit<React.HTMLAttributes<HTMLSpanElement>, 'color' | 'children'>,\n Omit<BulmaClassesProps, 'color' | 'backgroundColor'> {\n /**\n * The element the card previews, such as a link or a `Button`. The element\n * itself has to take focus, so keyboard users can open the card too: a\n * link inside a `<span>` doesn't do, since focus comes back to the trigger\n * and its ARIA is on it. HoverCard gives it no role and no tab stop, since\n * it can't know what the element is, and a development build warns when\n * it can't take focus: use a link or a button, or give the element\n * `tabIndex={0}`. A disabled button can't take focus either. HoverCard adds\n * `aria-expanded` and `aria-controls`, both naming the card only while it\n * is on the page, so it has to be a single element that passes those to\n * its DOM node. Its own click and keys stay its own, and the card doesn't\n * toggle on them, except Escape while the card is open, which closes the\n * card before the trigger sees it.\n */\n trigger: React.ReactElement;\n /**\n * The card's content: a preview whose links and buttons are fine, but not a\n * form, which belongs in a `Popover`. The card pads itself, so content goes\n * straight in.\n */\n children?: React.ReactNode;\n /**\n * Controlled open state. When set, hover, focus and Escape only report\n * through `onOpenChange`, so an action inside the card can close it. While\n * the card is open, Escape is reported, and when focus is in the card, on\n * its trigger or nowhere, the key stops there even if you keep the card\n * open, so a `Modal` around it doesn't close from the keyboard until the\n * card does.\n */\n open?: boolean;\n /**\n * Initial open state when uncontrolled. A card that starts open stays open\n * until the pointer or focus has been and gone, or Escape closes it.\n * @defaultValue false\n */\n defaultOpen?: boolean;\n /**\n * Called with the state asked for: when `openDelay` or `closeDelay` runs\n * out, and right away on Escape. Fires in both controlled and uncontrolled\n * use. An Escape it reports stops at the card when focus is in the card,\n * on its trigger or nowhere, so nothing around it sees that key. With\n * focus anywhere else, the card still closes and the key goes on to\n * whatever has focus.\n */\n onOpenChange?: (open: boolean) => void;\n /**\n * How long the pointer or keyboard focus waits on the trigger before the\n * card opens, in milliseconds.\n * @defaultValue 600\n */\n openDelay?: number;\n /**\n * How long the card stays after the pointer and focus have left the trigger\n * and the card, in milliseconds. It is the time the pointer has to cross\n * the gap between the trigger and the card: with `0` the card closes as the\n * pointer leaves the trigger, before the pointer can reach it.\n * @defaultValue 300\n */\n closeDelay?: number;\n /**\n * Where the card opens against the trigger.\n * @defaultValue 'bottom-left'\n */\n position?: HoverCardPosition;\n /**\n * Render the card through `Portal`, into the `portalContainer` a\n * `ConfigProvider` sets, `document.body` by default, fixed to the viewport,\n * so an ancestor's `overflow`, `transform` or stacking context can't clip\n * it. A trigger in running text needs it too, since the card's block\n * content can't sit inside a `<p>`. The card then shows from the commit\n * after hydration, never in server markup, and it sits in the Tab order\n * where its container is. At the end of `<body>`, the default, Tab reaches\n * it only after the rest of the page, and focus leaving the trigger on the\n * way closes it, so keyboard users can open it, read it and close it with\n * Escape, and whatever it links to has to be reachable from the page as\n * well. It is measured against the trigger when it opens and on resize and\n * scroll, so it doesn't follow content that changes size while it is open.\n * Theming variables set on an ancestor of the trigger reach it only when its\n * container is inside that ancestor.\n * @defaultValue false\n */\n appendToBody?: boolean;\n /**\n * Open the card with a tap on a touch screen. Touch has no hover, so\n * without this a phone or tablet never opens the card. With it, a tap on\n * the trigger opens the card at once, and it stays until a second tap on\n * the trigger, a tap outside the trigger and the card, or Escape. The tap\n * still does whatever the trigger does, so turn this on only for a trigger\n * with no tap action of its own, such as a `Button` with no `onClick`: on\n * a link, the first tap would follow the link rather than show the card.\n * Mouse and pen work the same either way.\n * @defaultValue false\n */\n openOnTap?: boolean;\n /** Additional classes for the card (`.hover-card-content`). */\n contentClassName?: string;\n}\n\n/** Runs the caller's handler on the wrapper, then the hover card's. */\nfunction chain<E>(\n own: ((event: E) => void) | undefined,\n ours: (event: E) => void\n): (event: E) => void {\n return event => {\n own?.(event);\n ours(event);\n };\n}\n\n/**\n * The `HoverCard` component shows a card previewing what's behind a link or a button while the pointer rests on it or keyboard focus is on it.\n *\n * Nothing moves focus when the card opens, and nothing traps it. In place,\n * the card comes right after the trigger, so Tab goes from the trigger into\n * its links and tabbing past them closes it. The pointer can move onto the\n * card, Escape closes it from anywhere, and by default touch doesn't open it,\n * so tapping the trigger just does what the trigger does. `openOnTap` opens\n * it with a tap, for a trigger with no tap action of its own. For hints with nothing to\n * click use `Tooltip`, and for content opened on purpose, such as a form,\n * use `Popover`.\n *\n * @function\n * @param {HoverCardProps} props - Props for the HoverCard component.\n * @returns {JSX.Element} The trigger in its wrapper, and the card while open.\n *\n * @example\n * <HoverCard trigger={<a href=\"/people/ada\">Ada Lovelace</a>}>\n * <Title as=\"p\" size=\"6\">Ada Lovelace</Title>\n * <SubTitle as=\"p\" size=\"6\">Analytical Engine team</SubTitle>\n * </HoverCard>\n */\nexport const HoverCard: React.FC<HoverCardProps> = ({\n trigger,\n children,\n open: controlledOpen,\n defaultOpen = false,\n onOpenChange,\n openDelay = 600,\n closeDelay = 300,\n position = 'bottom-left',\n appendToBody = false,\n openOnTap = false,\n className,\n contentClassName,\n ...props\n}) => {\n const { bulmaHelperClasses, rest } = useBulmaClasses(props);\n const {\n onPointerEnter,\n onPointerLeave,\n onPointerDown,\n onPointerUp,\n onFocus,\n onBlur,\n ...attrs\n } = rest;\n const rootRef = useRef<HTMLSpanElement>(null);\n const cardRef = useRef<HTMLDivElement>(null);\n const cardId = `${useId()}-card`;\n\n const isControlled = controlledOpen !== undefined;\n const [internalOpen, setInternalOpen] = useState(defaultOpen);\n const isOpen = controlledOpen ?? internalOpen;\n\n const requestOpen = useCallback(\n (next: boolean) => {\n onOpenChange?.(next);\n if (!isControlled) setInternalOpen(next);\n },\n [isControlled, onOpenChange]\n );\n\n // A portaled card has no server markup, so it only exists from the commit\n // after hydration, and nothing measures it before then.\n const hydrated = useIsHydrated();\n const cardMounted = isOpen && (hydrated || !appendToBody);\n\n const resolved = useAnchoredPosition(rootRef, cardRef, {\n active: cardMounted,\n position,\n fixed: appendToBody,\n // The gap comes from `--bulma-hover-card-offset` in the stylesheet, so\n // the coordinates leave it out and `auto` reads it off the card.\n offset: 0,\n styledGap: marginGap,\n });\n\n // The trigger renders first inside the wrapper. Read when needed rather\n // than held, so a trigger that remounted is still the one found.\n const triggerRef = useMemo(\n () => ({\n get current() {\n const first = (rootRef.current as HTMLSpanElement).firstElementChild;\n return first && first !== cardRef.current\n ? (first as HTMLElement)\n : null;\n },\n }),\n []\n );\n\n const hover = useHoverOpen({\n open: isOpen,\n onOpenChange: requestOpen,\n openDelay,\n closeDelay,\n triggerRef,\n floatingRef: cardRef,\n openOnTap,\n });\n\n // A trigger that can't take focus leaves keyboard users no way to open the\n // card. It has to be the trigger element itself, not something inside it,\n // since focus comes back to it and its ARIA is on it. Judged by what the\n // element is rather than by its styles, which can hide it for now. After\n // every commit, since the trigger can change, and only in development.\n useEffect(() => {\n if (!isDev()) return;\n const triggerNode = triggerRef.current;\n if (!triggerNode) return;\n if (!triggerNode.matches(':disabled') && isTabbableKind(triggerNode)) {\n return;\n }\n warnOnce(\n 'HoverCard:unfocusable-trigger',\n \"[bestax-bulma] <HoverCard> has a trigger that can't take focus, so \" +\n \"keyboard users can't open the card. Use a link or a button as the \" +\n 'trigger, or give it tabIndex={0}.'\n );\n });\n\n // Expanded only while the card is on the page, so a portaled card that\n // waits for hydration isn't announced before it exists.\n const clonedTrigger = cloneElement(\n trigger as React.ReactElement<Record<string, unknown>>,\n {\n 'aria-expanded': cardMounted,\n 'aria-controls': cardMounted ? cardId : undefined,\n }\n );\n\n const rootClasses = classNames(\n usePrefixedClassNames('hover-card', { 'is-active': cardMounted }),\n bulmaHelperClasses,\n className\n );\n const cardClasses = classNames(\n usePrefixedClassNames('hover-card-content', {\n [`is-${resolved.position}`]: true,\n 'is-portal': appendToBody,\n }),\n contentClassName\n );\n\n return (\n // The ref after the spread: React 19 hands a function component a `ref`\n // prop like any other, and the wrapper has to stay ours. The card is in\n // the wrapper's React tree even when portaled, so the hover and focus\n // handlers here hear from both.\n <span\n className={rootClasses}\n {...attrs}\n onPointerEnter={chain(onPointerEnter, hover.onPointerEnter)}\n onPointerLeave={chain(onPointerLeave, hover.onPointerLeave)}\n onFocus={chain(onFocus, hover.onFocus)}\n onBlur={chain(onBlur, hover.onBlur)}\n onPointerDown={chain(onPointerDown, hover.onPointerDown)}\n onPointerUp={chain(onPointerUp, hover.onPointerUp)}\n ref={rootRef}\n >\n {clonedTrigger}\n {isOpen && (\n <Portal disabled={!appendToBody}>\n <div\n ref={cardRef}\n id={cardId}\n className={cardClasses}\n style={\n appendToBody\n ? {\n position: 'fixed',\n top: resolved.top,\n left: resolved.left,\n }\n : undefined\n }\n >\n {children}\n </div>\n </Portal>\n )}\n </span>\n );\n};\n\nexport default HoverCard;\n"],"names":["_jsxs","_jsx"],"mappings":"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"}
@@ -9,7 +9,7 @@ import { useBulmaClasses } from '../helpers/useBulmaClasses.js';
9
9
  import { useConfig } from '../helpers/Config.js';
10
10
  import { useScrollLock } from '../helpers/scrollLock.js';
11
11
  import { getDeepestActiveElement, getActiveElementInTree } from '../helpers/shadowDom.js';
12
- import { resolvePortalContainer } from '../helpers/portal.js';
12
+ import { usePortalContainer } from '../helpers/portal.js';
13
13
  import { useIsHydrated } from '../helpers/useIsHydrated.js';
14
14
 
15
15
  /**
@@ -164,7 +164,7 @@ const ModalClose = ({ className, size = 'large', variant = 'delete', ...props })
164
164
  * @see {@link https://bulma.io/documentation/components/modal/ | Bulma Modal documentation}
165
165
  */
166
166
  const ModalRoot = forwardRef(function ModalRoot({ active, isActive, onClose, className, textColor, bgColor, modalCardTitle, modalCardFoot, type, children, closeOnEscape = true, lockScroll = true, portal = false, role, 'aria-modal': ariaModalProp, ...props }, ref) {
167
- const { classPrefix } = useConfig();
167
+ const { classPrefix, portalContainer } = useConfig();
168
168
  const { bulmaHelperClasses, rest } = useBulmaClasses({
169
169
  color: textColor,
170
170
  backgroundColor: bgColor,
@@ -214,10 +214,17 @@ const ModalRoot = forwardRef(function ModalRoot({ active, isActive, onClose, cla
214
214
  // hydrating render have to stay inline and only move into the portal once
215
215
  // we're past hydration.
216
216
  const onClient = useIsHydrated();
217
- // Moving into the portal remounts the modal's subtree, so any effect holding
218
- // a DOM node from before the move is holding a detached one. Effects that
219
- // touch the modal's nodes key on this so they re-run against the new tree.
217
+ const resolveContainer = usePortalContainer();
218
+ // Moving into the portal, or from one portal target to another, remounts
219
+ // the modal's subtree, so any effect holding a DOM node from before the move
220
+ // is holding a detached one. Effects that touch the modal's nodes key on
221
+ // this, on `portal` and on `defaultTarget`, so they re-run against the new
222
+ // tree.
220
223
  const isPortaled = Boolean(portal) && onClient;
224
+ // The configured `portalContainer` only places a modal portaled with
225
+ // `portal` set to `true`. In place, or with a target of its own, a change
226
+ // to it moves nothing, so it mustn't re-run the effects either.
227
+ const defaultTarget = isPortaled && portal === true ? portalContainer : undefined;
221
228
  useScrollLock(isModalActive && lockScroll);
222
229
  // Record open order so keyboard handling only applies to the topmost modal.
223
230
  useEffect(() => {
@@ -297,11 +304,12 @@ const ModalRoot = forwardRef(function ModalRoot({ active, isActive, onClose, cla
297
304
  previouslyFocusedRef.current?.focus?.();
298
305
  };
299
306
  // `isPortaled` flips once when a hydrated portal modal moves out of the
300
- // inline tree; re-running rebinds focus onto the remounted nodes. The
301
- // cleanup above restores focus to the pre-open element first (the detached
302
- // subtree has left focus on <body>), so the re-run re-records the same
303
- // element and restore-on-close still lands in the right place.
304
- }, [isModalActive, isPortaled]);
307
+ // inline tree, and `portal` or `defaultTarget` changing moves a portaled
308
+ // one to another target; re-running rebinds focus onto the remounted
309
+ // nodes. The cleanup above restores focus to the pre-open element first
310
+ // (the detached subtree has left focus on <body>), so the re-run re-records
311
+ // the same element and restore-on-close still lands in the right place.
312
+ }, [isModalActive, isPortaled, portal, defaultTarget]);
305
313
  // Check if children contain compound components
306
314
  const hasCompoundComponents = React.Children.toArray(children).some(child => React.isValidElement(child) &&
307
315
  (child.type === ModalBackground ||
@@ -338,7 +346,7 @@ const ModalRoot = forwardRef(function ModalRoot({ active, isActive, onClose, cla
338
346
  modalElement = (jsxs("div", { className: modalClasses, ref: combinedRef, role: resolvedRole, "aria-modal": resolvedAriaModal, "aria-labelledby": titleId, tabIndex: -1, ...rest, "data-testid": "modal", children: [jsx("div", { className: prefixedClassNames(classPrefix, 'modal-background'), onClick: onClose, "data-testid": "modal-background" }), isModalCard ? (jsxs("div", { className: prefixedClassNames(classPrefix, 'modal-card'), children: [modalCardTitle && (jsxs("header", { className: prefixedClassNames(classPrefix, 'modal-card-head'), children: [jsx("p", { id: titleId, className: prefixedClassNames(classPrefix, 'modal-card-title'), children: modalCardTitle }), onClose && (jsx("button", { className: deleteClass, "aria-label": "close", onClick: onClose, type: "button", "data-testid": "modal-close" }))] })), jsx("section", { className: prefixedClassNames(classPrefix, 'modal-card-body'), "data-testid": "modal-body", children: children }), modalCardFoot && (jsx("footer", { className: prefixedClassNames(classPrefix, 'modal-card-foot'), children: modalCardFoot }))] })) : (jsx("div", { className: prefixedClassNames(classPrefix, 'modal-content'), "data-testid": "modal-content", children: children })), (!isModalCard || (!modalCardTitle && onClose)) && onClose && (jsx("button", { className: prefixedClassNames(classPrefix, 'modal-close', 'is-large'), "aria-label": "close", onClick: onClose, type: "button", "data-testid": "modal-close-float" }))] }));
339
347
  }
340
348
  if (isPortaled) {
341
- const target = resolvePortalContainer(typeof portal === 'boolean' ? undefined : portal);
349
+ const target = resolveContainer(typeof portal === 'boolean' ? undefined : portal);
342
350
  return createPortal(modalElement, target);
343
351
  }
344
352
  return modalElement;
@@ -1 +1 @@
1
- {"version":3,"file":"Modal.js","sources":["../../../src/components/Modal.tsx"],"sourcesContent":["import React, {\n forwardRef,\n useCallback,\n useEffect,\n useId,\n useRef,\n} from 'react';\nimport { createPortal } from 'react-dom';\nimport {\n classNames,\n usePrefixedClassNames,\n prefixedClassNames,\n} from '../helpers/classNames';\nimport { withSubComponents } from '../helpers/withSubComponents';\nimport { buttonType } from '../helpers/buttonType';\nimport {\n useBulmaClasses,\n BulmaClassesProps,\n validColors,\n} from '../helpers/useBulmaClasses';\nimport { useConfig } from '../helpers/Config';\nimport { useScrollLock } from '../helpers/scrollLock';\nimport {\n getActiveElementInTree,\n getDeepestActiveElement,\n} from '../helpers/shadowDom';\nimport { resolvePortalContainer } from '../helpers/portal';\nimport { useIsHydrated } from '../helpers/useIsHydrated';\n\n/**\n * Every currently active modal, in the order they opened. Only the last entry\n * — the topmost modal — reacts to Escape and Tab, so one keypress can't\n * dismiss a whole stack (e.g. a `Dialog` opened on top of a `Modal`).\n */\nconst activeModalStack: object[] = [];\n\n/**\n * Controls the modal can hand focus to. Disabled and hidden controls are\n * excluded because `focus()` on them is a no-op, which would leave focus\n * outside the modal.\n */\nconst FOCUSABLE_SELECTOR =\n 'button:not(:disabled), [href], input:not(:disabled):not([type=\"hidden\"]), select:not(:disabled), textarea:not(:disabled), [tabindex]';\n\n/**\n * The modal's tab stops, in document order. The selector alone is not the\n * tabbable set — `[href]` and `button` match regardless of `tabindex`, so an\n * `<a href tabIndex={-1}>` would otherwise be treated as a tab stop and let\n * Tab escape the modal when it sorts last. `el.tabIndex` is the browser's own\n * resolved value, so filtering on it drops every negative index (not just\n * `-1`) without a second selector to keep in sync.\n */\nconst getTabbable = (node: HTMLElement): HTMLElement[] =>\n Array.from(node.querySelectorAll<HTMLElement>(FOCUSABLE_SELECTOR)).filter(\n el => el.tabIndex >= 0\n );\n\n/**\n * Props for the Modal component.\n * @extraProp {React.Ref<HTMLDivElement>} [ref] - Ref forwarded to the root `.modal` element.\n */\nexport interface ModalProps\n extends\n Omit<React.HTMLAttributes<HTMLDivElement>, 'color' | 'title'>,\n Omit<BulmaClassesProps, 'color' | 'backgroundColor'> {\n /** Whether the modal is open/visible. */\n active?: boolean;\n /** Alias for `active`. Whether the modal is open/visible. */\n isActive?: boolean; // Alias for active\n /** Callback invoked to request modal close (background or close button). */\n onClose?: () => void;\n /** Additional CSS classes for the modal. */\n className?: string;\n /** Text color for modal content. */\n textColor?: (typeof validColors)[number] | 'inherit' | 'current';\n /** Background color for modal content. */\n bgColor?: (typeof validColors)[number] | 'inherit' | 'current';\n /** Title/header for modal-card variant. (Legacy API only) */\n modalCardTitle?: React.ReactNode;\n /** Footer for modal-card variant. (Legacy API only) */\n modalCardFoot?: React.ReactNode;\n /**\n * Modal style: `'card'` for modal-card, `'content'` for modal-content. (Legacy API only)\n * @defaultValue auto\n */\n type?: 'card' | 'content';\n /** Modal body/content or compound components. */\n children?: React.ReactNode;\n /**\n * Close the modal when the Escape key is pressed (calls `onClose`). Only the\n * topmost open modal responds, so Escape closes one layer at a time.\n * @defaultValue true\n */\n closeOnEscape?: boolean;\n /**\n * Lock body scroll while the modal is active. Ref-counted and shared with\n * `Dialog`, `Sidebar` and `Loading`, so whichever overlay closes first does\n * not unlock the page underneath one that is still open.\n * @defaultValue true\n */\n lockScroll?: boolean;\n /**\n * Renders the modal into a portal target instead of inline, so it isn't\n * clipped by an ancestor with `overflow: hidden`, `filter` or `transform`.\n * `true` portals to `document.body`; a string is used as a\n * `document.querySelector` selector; an element is used directly. Renders\n * inline on the server and while hydrating, moving into the portal once the\n * client takes over, so hydration matches.\n * @defaultValue false\n */\n portal?: boolean | string | HTMLElement;\n}\n\n/**\n * Props for Modal.Background component.\n */\nexport interface ModalBackgroundProps extends React.HTMLAttributes<HTMLDivElement> {\n /** Additional CSS classes. */\n className?: string;\n}\n\n/**\n * Props for Modal.Content component.\n */\nexport interface ModalContentProps extends React.HTMLAttributes<HTMLDivElement> {\n /** Additional CSS classes. */\n className?: string;\n}\n\n/**\n * Props for Modal.Card component.\n */\nexport interface ModalCardProps extends React.HTMLAttributes<HTMLDivElement> {\n /** Additional CSS classes. */\n className?: string;\n}\n\n/**\n * Props for Modal.Card.Head component.\n */\nexport interface ModalCardHeadProps extends React.HTMLAttributes<HTMLElement> {\n /** Additional CSS classes. */\n className?: string;\n}\n\n/**\n * Props for Modal.Card.Title component.\n */\nexport interface ModalCardTitleProps extends React.HTMLAttributes<HTMLParagraphElement> {\n /** Additional CSS classes. */\n className?: string;\n}\n\n/**\n * Props for Modal.Card.Body component.\n */\nexport interface ModalCardBodyProps extends React.HTMLAttributes<HTMLElement> {\n /** Additional CSS classes. */\n className?: string;\n}\n\n/**\n * Props for Modal.Card.Foot component.\n */\nexport interface ModalCardFootProps extends React.HTMLAttributes<HTMLElement> {\n /** Additional CSS classes. */\n className?: string;\n}\n\n/**\n * Props for Modal.Close component.\n * @extraProp {'button' | 'submit' | 'reset'} [type='button'] - Button type. Defaults to `'button'`, so a close button inside a form does not submit it. Pass `'submit'` or `'reset'` and yours is used; any other value, or a spread carrying `type: undefined`, renders `'button'`.\n * @extraProp {string} [aria-label='close'] - Accessible name. Pass your own to replace it. An empty one, or a spread carrying `'aria-label': undefined`, keeps the default rather than leaving the button unnamed.\n */\nexport interface ModalCloseProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {\n /** Additional CSS classes. */\n className?: string;\n /** Size of the close button (only applies to 'floating' variant). */\n size?: 'small' | 'medium' | 'large';\n /** Button variant. 'delete' (default) for modal card headers, 'floating' for overlay close button. */\n variant?: 'delete' | 'floating';\n}\n\n/**\n * Modal.Background - Renders the modal background overlay.\n *\n * @function\n * @param {ModalBackgroundProps} props - Component props.\n * @returns {JSX.Element} Modal background element.\n */\nconst ModalBackground: React.FC<ModalBackgroundProps> = ({\n className,\n ...props\n}) => {\n const classes = classNames(\n usePrefixedClassNames('modal-background'),\n className\n );\n return <div className={classes} {...props} />;\n};\n\n/**\n * Modal.Content - Renders modal content wrapper for custom content.\n *\n * @function\n * @param {ModalContentProps} props - Component props.\n * @returns {JSX.Element} Modal content element.\n */\nconst ModalContent: React.FC<ModalContentProps> = ({ className, ...props }) => {\n const classes = classNames(usePrefixedClassNames('modal-content'), className);\n return <div className={classes} {...props} />;\n};\n\n/**\n * Modal.Card.Head - Renders modal card header section.\n *\n * @function\n * @param {ModalCardHeadProps} props - Component props.\n * @returns {JSX.Element} Modal card header element.\n */\nconst ModalCardHead: React.FC<ModalCardHeadProps> = ({\n className,\n ...props\n}) => {\n const classes = classNames(\n usePrefixedClassNames('modal-card-head'),\n className\n );\n return <header className={classes} {...props} />;\n};\n\n/**\n * Modal.Card.Title - Renders modal card title.\n *\n * @function\n * @param {ModalCardTitleProps} props - Component props.\n * @returns {JSX.Element} Modal card title element.\n */\nconst ModalCardTitle: React.FC<ModalCardTitleProps> = ({\n className,\n ...props\n}) => {\n const classes = classNames(\n usePrefixedClassNames('modal-card-title'),\n className\n );\n return <p className={classes} {...props} />;\n};\n\n/**\n * Modal.Card.Body - Renders modal card body section.\n *\n * @function\n * @param {ModalCardBodyProps} props - Component props.\n * @returns {JSX.Element} Modal card body element.\n */\nconst ModalCardBody: React.FC<ModalCardBodyProps> = ({\n className,\n ...props\n}) => {\n const classes = classNames(\n usePrefixedClassNames('modal-card-body'),\n className\n );\n return <section className={classes} {...props} />;\n};\n\n/**\n * Modal.Card.Foot - Renders modal card footer section.\n *\n * @function\n * @param {ModalCardFootProps} props - Component props.\n * @returns {JSX.Element} Modal card footer element.\n */\nconst ModalCardFoot: React.FC<ModalCardFootProps> = ({\n className,\n ...props\n}) => {\n const classes = classNames(\n usePrefixedClassNames('modal-card-foot'),\n className\n );\n return <footer className={classes} {...props} />;\n};\n\n/**\n * Modal.Card - Renders modal card wrapper with compound components.\n * Use with Modal.Card.Head, Modal.Card.Title, Modal.Card.Body, and Modal.Card.Foot.\n *\n * @function\n * @param {ModalCardProps} props - Component props.\n * @returns {JSX.Element} Modal card element.\n */\nconst ModalCardComponent: React.FC<ModalCardProps> = ({\n className,\n ...props\n}) => {\n const classes = classNames(usePrefixedClassNames('modal-card'), className);\n return <div className={classes} {...props} />;\n};\n\nconst ModalCard = withSubComponents(ModalCardComponent, {\n Head: ModalCardHead,\n Title: ModalCardTitle,\n Body: ModalCardBody,\n Foot: ModalCardFoot,\n});\n\n/**\n * Modal.Close - Renders modal close button with two variant styles.\n *\n * @function\n * @param {ModalCloseProps} props - Component props.\n * @returns {JSX.Element} Close button element.\n *\n * @remarks\n * Supports two variants:\n * - 'delete' (default): For use in modal card headers, renders with 'delete' class\n * - 'floating': For floating overlay close button, renders with 'modal-close' class\n *\n * The size prop only applies to the 'floating' variant.\n */\nconst ModalClose: React.FC<ModalCloseProps> = ({\n className,\n size = 'large',\n variant = 'delete',\n ...props\n}) => {\n const classes = classNames(\n usePrefixedClassNames(\n variant === 'delete' ? 'delete' : 'modal-close',\n variant === 'floating' && size && { [`is-${size}`]: true }\n ),\n className\n );\n return (\n <button\n className={classes}\n {...props}\n // After the spread, reading through it: a key the spread carries with no\n // value would otherwise erase the default, leaving a nameless button\n // that submits the form around it. An empty label falls back too: the\n // button is drawn as an icon, so `''` would leave it unnamed. A caller's\n // own value still wins; `buttonType` says more.\n aria-label={props['aria-label'] || 'close'}\n type={buttonType(props.type)}\n />\n );\n};\n\n/**\n * The `Modal` component provides an empty, accessible overlay for arbitrary content — for a ready-made confirm or alert, reach for `Dialog` instead.\n *\n * @function\n * @param {ModalProps} props - Props for the Modal component.\n * @returns {JSX.Element} The rendered modal.\n *\n * @example\n * // Legacy API\n * <Modal active={isOpen} onClose={handleClose} modalCardTitle=\"Title\">\n * Content\n * </Modal>\n *\n * @example\n * // Compound Component API\n * <Modal isActive={isOpen}>\n * <Modal.Background onClick={handleClose} />\n * <Modal.Card>\n * <Modal.Card.Head>\n * <Modal.Card.Title>Title</Modal.Card.Title>\n * <Modal.Close onClick={handleClose} />\n * </Modal.Card.Head>\n * <Modal.Card.Body>Content</Modal.Card.Body>\n * </Modal.Card>\n * </Modal>\n *\n * @see {@link https://bulma.io/documentation/components/modal/ | Bulma Modal documentation}\n */\nconst ModalRoot = forwardRef<HTMLDivElement, ModalProps>(function ModalRoot(\n {\n active,\n isActive,\n onClose,\n className,\n textColor,\n bgColor,\n modalCardTitle,\n modalCardFoot,\n type,\n children,\n closeOnEscape = true,\n lockScroll = true,\n portal = false,\n role,\n 'aria-modal': ariaModalProp,\n ...props\n },\n ref\n) {\n const { classPrefix } = useConfig();\n const { bulmaHelperClasses, rest } = useBulmaClasses({\n color: textColor,\n backgroundColor: bgColor,\n ...props,\n });\n\n // Support both active and isActive props\n const isModalActive = active ?? isActive ?? false;\n\n const modalRootRef = useRef<HTMLDivElement>(null);\n\n // Cleanup returned by a consumer's callback ref on attach, held until detach.\n const consumerCleanupRef = useRef<(() => void) | null>(null);\n\n // The forwarded ref and the internal one must both see the node: focus\n // management, the scroll lock and the topmost-modal check all read\n // `modalRootRef`, while consumers expect their own ref to resolve.\n const combinedRef = useCallback(\n (node: HTMLDivElement | null) => {\n (modalRootRef as React.MutableRefObject<HTMLDivElement | null>).current =\n node;\n if (typeof ref === 'function') {\n // React 19 lets a callback ref return a cleanup function and detaches\n // by running it instead of calling the ref with `null`; React 18\n // discards the return value entirely. Returning it from here would be\n // a React-19-only contract, so instead we hold the cleanup and run it\n // ourselves on detach — a consumer's cleanup ref then behaves the same\n // on both majors of the CI matrix. Same shape as `Dropdown`.\n if (node === null) {\n const consumerCleanup = consumerCleanupRef.current;\n consumerCleanupRef.current = null;\n if (consumerCleanup) {\n consumerCleanup();\n } else {\n ref(null);\n }\n return;\n }\n const cleanup: unknown = ref(node);\n consumerCleanupRef.current =\n typeof cleanup === 'function' ? (cleanup as () => void) : null;\n } else if (ref) {\n (ref as React.MutableRefObject<HTMLDivElement | null>).current = node;\n }\n },\n [ref]\n );\n const previouslyFocusedRef = useRef<HTMLElement | null>(null);\n const stackTokenRef = useRef<object>({});\n const generatedTitleId = useId();\n // A portal has no server-rendered counterpart, so the server render and the\n // hydrating render have to stay inline and only move into the portal once\n // we're past hydration.\n const onClient = useIsHydrated();\n // Moving into the portal remounts the modal's subtree, so any effect holding\n // a DOM node from before the move is holding a detached one. Effects that\n // touch the modal's nodes key on this so they re-run against the new tree.\n const isPortaled = Boolean(portal) && onClient;\n\n useScrollLock(isModalActive && lockScroll);\n\n // Record open order so keyboard handling only applies to the topmost modal.\n useEffect(() => {\n if (!isModalActive) return undefined;\n\n const token = stackTokenRef.current;\n activeModalStack.push(token);\n return () => {\n const index = activeModalStack.indexOf(token);\n if (index !== -1) activeModalStack.splice(index, 1);\n };\n }, [isModalActive]);\n\n // Escape closes; Tab cycles inside the modal. Both only for the topmost one.\n useEffect(() => {\n if (!isModalActive) return undefined;\n\n const handleKeyDown = (e: KeyboardEvent) => {\n const node = modalRootRef.current;\n if (\n activeModalStack[activeModalStack.length - 1] !== stackTokenRef.current\n ) {\n return;\n }\n\n if (e.key === 'Escape') {\n if (closeOnEscape) onClose?.();\n return;\n }\n if (e.key !== 'Tab' || !node) return;\n\n // Keep Tab within the modal — `aria-modal` hides the rest of the page\n // from assistive technology, so the keyboard order has to agree.\n const focusable = getTabbable(node);\n const activeElement = getActiveElementInTree(node);\n if (focusable.length === 0) {\n e.preventDefault();\n node.focus();\n return;\n }\n\n const first = focusable[0];\n const last = focusable[focusable.length - 1];\n const outside = !node.contains(activeElement);\n if (e.shiftKey) {\n if (outside || activeElement === first) {\n e.preventDefault();\n last.focus();\n }\n } else if (outside || activeElement === last) {\n e.preventDefault();\n first.focus();\n }\n };\n\n document.addEventListener('keydown', handleKeyDown);\n return () => document.removeEventListener('keydown', handleKeyDown);\n }, [isModalActive, closeOnEscape, onClose]);\n\n // Move focus into the modal on open, restore it on close\n useEffect(() => {\n if (!isModalActive) return undefined;\n\n // The element that had focus, wherever it sits: a portaled modal renders\n // under `document.body` while its opener can be inside a shadow root.\n previouslyFocusedRef.current =\n getDeepestActiveElement() as HTMLElement | null;\n const node = modalRootRef.current;\n const focusable = node ? getTabbable(node)[0] : undefined;\n (focusable ?? node)?.focus();\n\n return () => {\n // Only hand focus back if this modal still owns it: closing a background\n // modal must not pull focus out of one that is still open on top. A\n // removed subtree leaves focus on <body>, which still counts as ours.\n const activeElement = getActiveElementInTree(node);\n if (\n activeElement &&\n activeElement !== document.body &&\n !node?.contains(activeElement)\n ) {\n return;\n }\n previouslyFocusedRef.current?.focus?.();\n };\n // `isPortaled` flips once when a hydrated portal modal moves out of the\n // inline tree; re-running rebinds focus onto the remounted nodes. The\n // cleanup above restores focus to the pre-open element first (the detached\n // subtree has left focus on <body>), so the re-run re-records the same\n // element and restore-on-close still lands in the right place.\n }, [isModalActive, isPortaled]);\n\n // Check if children contain compound components\n const hasCompoundComponents = React.Children.toArray(children).some(\n child =>\n React.isValidElement(child) &&\n (child.type === ModalBackground ||\n child.type === ModalContent ||\n child.type === ModalCard ||\n child.type === ModalClose)\n );\n\n // Generate Bulma classes with prefix\n const bulmaClasses = usePrefixedClassNames('modal', {\n 'is-active': isModalActive,\n });\n const deleteClass = usePrefixedClassNames('delete');\n\n const modalClasses = classNames(bulmaClasses, bulmaHelperClasses, className);\n\n const resolvedRole = role ?? (isModalActive ? 'dialog' : undefined);\n const resolvedAriaModal =\n ariaModalProp ??\n (isModalActive && resolvedRole !== 'presentation' ? 'true' : undefined);\n // A dialog needs an accessible name: the legacy card title is wired up\n // automatically, compound users pass their own aria-label/aria-labelledby\n // (both arrive in `rest`, which is spread last and so wins).\n const titleId = modalCardTitle ? generatedTitleId : undefined;\n\n let modalElement: React.ReactElement;\n\n // If using compound components, render children as-is\n if (hasCompoundComponents) {\n modalElement = (\n <div\n className={modalClasses}\n ref={combinedRef}\n role={resolvedRole}\n aria-modal={resolvedAriaModal}\n tabIndex={-1}\n {...rest}\n data-testid=\"modal\"\n >\n {children}\n </div>\n );\n } else {\n // Legacy API: EXPLICIT type wins; fallback to auto detection if not provided\n let isModalCard: boolean;\n if (type === 'card') isModalCard = true;\n else if (type === 'content') isModalCard = false;\n else isModalCard = !!modalCardTitle || !!modalCardFoot;\n\n modalElement = (\n <div\n className={modalClasses}\n ref={combinedRef}\n role={resolvedRole}\n aria-modal={resolvedAriaModal}\n aria-labelledby={titleId}\n tabIndex={-1}\n {...rest}\n data-testid=\"modal\"\n >\n <div\n className={prefixedClassNames(classPrefix, 'modal-background')}\n onClick={onClose}\n data-testid=\"modal-background\"\n />\n {isModalCard ? (\n <div className={prefixedClassNames(classPrefix, 'modal-card')}>\n {modalCardTitle && (\n <header\n className={prefixedClassNames(classPrefix, 'modal-card-head')}\n >\n <p\n id={titleId}\n className={prefixedClassNames(\n classPrefix,\n 'modal-card-title'\n )}\n >\n {modalCardTitle}\n </p>\n {onClose && (\n <button\n className={deleteClass}\n aria-label=\"close\"\n onClick={onClose}\n type=\"button\"\n data-testid=\"modal-close\"\n />\n )}\n </header>\n )}\n <section\n className={prefixedClassNames(classPrefix, 'modal-card-body')}\n data-testid=\"modal-body\"\n >\n {children}\n </section>\n {modalCardFoot && (\n <footer\n className={prefixedClassNames(classPrefix, 'modal-card-foot')}\n >\n {modalCardFoot}\n </footer>\n )}\n </div>\n ) : (\n <div\n className={prefixedClassNames(classPrefix, 'modal-content')}\n data-testid=\"modal-content\"\n >\n {children}\n </div>\n )}\n {/* Show floating close button for modal-content, or for modal-card when no header */}\n {(!isModalCard || (!modalCardTitle && onClose)) && onClose && (\n <button\n className={prefixedClassNames(\n classPrefix,\n 'modal-close',\n 'is-large'\n )}\n aria-label=\"close\"\n onClick={onClose}\n type=\"button\"\n data-testid=\"modal-close-float\"\n />\n )}\n </div>\n );\n }\n\n if (isPortaled) {\n const target = resolvePortalContainer(\n typeof portal === 'boolean' ? undefined : portal\n );\n return createPortal(modalElement, target);\n }\n\n return modalElement;\n});\n\nexport const Modal = withSubComponents(\n ModalRoot,\n {\n Background: ModalBackground,\n Content: ModalContent,\n Card: ModalCard,\n Close: ModalClose,\n },\n 'Modal'\n);\nexport default Modal;\n"],"names":["_jsx","_jsxs"],"mappings":"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"}
1
+ {"version":3,"file":"Modal.js","sources":["../../../src/components/Modal.tsx"],"sourcesContent":["import React, {\n forwardRef,\n useCallback,\n useEffect,\n useId,\n useRef,\n} from 'react';\nimport { createPortal } from 'react-dom';\nimport {\n classNames,\n usePrefixedClassNames,\n prefixedClassNames,\n} from '../helpers/classNames';\nimport { withSubComponents } from '../helpers/withSubComponents';\nimport { buttonType } from '../helpers/buttonType';\nimport {\n useBulmaClasses,\n BulmaClassesProps,\n validColors,\n} from '../helpers/useBulmaClasses';\nimport { useConfig } from '../helpers/Config';\nimport { useScrollLock } from '../helpers/scrollLock';\nimport {\n getActiveElementInTree,\n getDeepestActiveElement,\n} from '../helpers/shadowDom';\nimport { usePortalContainer } from '../helpers/portal';\nimport { useIsHydrated } from '../helpers/useIsHydrated';\n\n/**\n * Every currently active modal, in the order they opened. Only the last entry\n * — the topmost modal — reacts to Escape and Tab, so one keypress can't\n * dismiss a whole stack (e.g. a `Dialog` opened on top of a `Modal`).\n */\nconst activeModalStack: object[] = [];\n\n/**\n * Controls the modal can hand focus to. Disabled and hidden controls are\n * excluded because `focus()` on them is a no-op, which would leave focus\n * outside the modal.\n */\nconst FOCUSABLE_SELECTOR =\n 'button:not(:disabled), [href], input:not(:disabled):not([type=\"hidden\"]), select:not(:disabled), textarea:not(:disabled), [tabindex]';\n\n/**\n * The modal's tab stops, in document order. The selector alone is not the\n * tabbable set — `[href]` and `button` match regardless of `tabindex`, so an\n * `<a href tabIndex={-1}>` would otherwise be treated as a tab stop and let\n * Tab escape the modal when it sorts last. `el.tabIndex` is the browser's own\n * resolved value, so filtering on it drops every negative index (not just\n * `-1`) without a second selector to keep in sync.\n */\nconst getTabbable = (node: HTMLElement): HTMLElement[] =>\n Array.from(node.querySelectorAll<HTMLElement>(FOCUSABLE_SELECTOR)).filter(\n el => el.tabIndex >= 0\n );\n\n/**\n * Props for the Modal component.\n * @extraProp {React.Ref<HTMLDivElement>} [ref] - Ref forwarded to the root `.modal` element.\n */\nexport interface ModalProps\n extends\n Omit<React.HTMLAttributes<HTMLDivElement>, 'color' | 'title'>,\n Omit<BulmaClassesProps, 'color' | 'backgroundColor'> {\n /** Whether the modal is open/visible. */\n active?: boolean;\n /** Alias for `active`. Whether the modal is open/visible. */\n isActive?: boolean; // Alias for active\n /** Callback invoked to request modal close (background or close button). */\n onClose?: () => void;\n /** Additional CSS classes for the modal. */\n className?: string;\n /** Text color for modal content. */\n textColor?: (typeof validColors)[number] | 'inherit' | 'current';\n /** Background color for modal content. */\n bgColor?: (typeof validColors)[number] | 'inherit' | 'current';\n /** Title/header for modal-card variant. (Legacy API only) */\n modalCardTitle?: React.ReactNode;\n /** Footer for modal-card variant. (Legacy API only) */\n modalCardFoot?: React.ReactNode;\n /**\n * Modal style: `'card'` for modal-card, `'content'` for modal-content. (Legacy API only)\n * @defaultValue auto\n */\n type?: 'card' | 'content';\n /** Modal body/content or compound components. */\n children?: React.ReactNode;\n /**\n * Close the modal when the Escape key is pressed (calls `onClose`). Only the\n * topmost open modal responds, so Escape closes one layer at a time.\n * @defaultValue true\n */\n closeOnEscape?: boolean;\n /**\n * Lock body scroll while the modal is active. Ref-counted and shared with\n * `Dialog`, `Sidebar` and `Loading`, so whichever overlay closes first does\n * not unlock the page underneath one that is still open.\n * @defaultValue true\n */\n lockScroll?: boolean;\n /**\n * Renders the modal into a portal target instead of inline, so it isn't\n * clipped by an ancestor with `overflow: hidden`, `filter` or `transform`.\n * `true` portals to the `portalContainer` a `ConfigProvider` sets,\n * `document.body` by default; a string is used as a\n * `document.querySelector` selector; an element is used directly. Renders\n * inline on the server and while hydrating, moving into the portal once the\n * client takes over, so hydration matches.\n * @defaultValue false\n */\n portal?: boolean | string | HTMLElement;\n}\n\n/**\n * Props for Modal.Background component.\n */\nexport interface ModalBackgroundProps extends React.HTMLAttributes<HTMLDivElement> {\n /** Additional CSS classes. */\n className?: string;\n}\n\n/**\n * Props for Modal.Content component.\n */\nexport interface ModalContentProps extends React.HTMLAttributes<HTMLDivElement> {\n /** Additional CSS classes. */\n className?: string;\n}\n\n/**\n * Props for Modal.Card component.\n */\nexport interface ModalCardProps extends React.HTMLAttributes<HTMLDivElement> {\n /** Additional CSS classes. */\n className?: string;\n}\n\n/**\n * Props for Modal.Card.Head component.\n */\nexport interface ModalCardHeadProps extends React.HTMLAttributes<HTMLElement> {\n /** Additional CSS classes. */\n className?: string;\n}\n\n/**\n * Props for Modal.Card.Title component.\n */\nexport interface ModalCardTitleProps extends React.HTMLAttributes<HTMLParagraphElement> {\n /** Additional CSS classes. */\n className?: string;\n}\n\n/**\n * Props for Modal.Card.Body component.\n */\nexport interface ModalCardBodyProps extends React.HTMLAttributes<HTMLElement> {\n /** Additional CSS classes. */\n className?: string;\n}\n\n/**\n * Props for Modal.Card.Foot component.\n */\nexport interface ModalCardFootProps extends React.HTMLAttributes<HTMLElement> {\n /** Additional CSS classes. */\n className?: string;\n}\n\n/**\n * Props for Modal.Close component.\n * @extraProp {'button' | 'submit' | 'reset'} [type='button'] - Button type. Defaults to `'button'`, so a close button inside a form does not submit it. Pass `'submit'` or `'reset'` and yours is used; any other value, or a spread carrying `type: undefined`, renders `'button'`.\n * @extraProp {string} [aria-label='close'] - Accessible name. Pass your own to replace it. An empty one, or a spread carrying `'aria-label': undefined`, keeps the default rather than leaving the button unnamed.\n */\nexport interface ModalCloseProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {\n /** Additional CSS classes. */\n className?: string;\n /** Size of the close button (only applies to 'floating' variant). */\n size?: 'small' | 'medium' | 'large';\n /** Button variant. 'delete' (default) for modal card headers, 'floating' for overlay close button. */\n variant?: 'delete' | 'floating';\n}\n\n/**\n * Modal.Background - Renders the modal background overlay.\n *\n * @function\n * @param {ModalBackgroundProps} props - Component props.\n * @returns {JSX.Element} Modal background element.\n */\nconst ModalBackground: React.FC<ModalBackgroundProps> = ({\n className,\n ...props\n}) => {\n const classes = classNames(\n usePrefixedClassNames('modal-background'),\n className\n );\n return <div className={classes} {...props} />;\n};\n\n/**\n * Modal.Content - Renders modal content wrapper for custom content.\n *\n * @function\n * @param {ModalContentProps} props - Component props.\n * @returns {JSX.Element} Modal content element.\n */\nconst ModalContent: React.FC<ModalContentProps> = ({ className, ...props }) => {\n const classes = classNames(usePrefixedClassNames('modal-content'), className);\n return <div className={classes} {...props} />;\n};\n\n/**\n * Modal.Card.Head - Renders modal card header section.\n *\n * @function\n * @param {ModalCardHeadProps} props - Component props.\n * @returns {JSX.Element} Modal card header element.\n */\nconst ModalCardHead: React.FC<ModalCardHeadProps> = ({\n className,\n ...props\n}) => {\n const classes = classNames(\n usePrefixedClassNames('modal-card-head'),\n className\n );\n return <header className={classes} {...props} />;\n};\n\n/**\n * Modal.Card.Title - Renders modal card title.\n *\n * @function\n * @param {ModalCardTitleProps} props - Component props.\n * @returns {JSX.Element} Modal card title element.\n */\nconst ModalCardTitle: React.FC<ModalCardTitleProps> = ({\n className,\n ...props\n}) => {\n const classes = classNames(\n usePrefixedClassNames('modal-card-title'),\n className\n );\n return <p className={classes} {...props} />;\n};\n\n/**\n * Modal.Card.Body - Renders modal card body section.\n *\n * @function\n * @param {ModalCardBodyProps} props - Component props.\n * @returns {JSX.Element} Modal card body element.\n */\nconst ModalCardBody: React.FC<ModalCardBodyProps> = ({\n className,\n ...props\n}) => {\n const classes = classNames(\n usePrefixedClassNames('modal-card-body'),\n className\n );\n return <section className={classes} {...props} />;\n};\n\n/**\n * Modal.Card.Foot - Renders modal card footer section.\n *\n * @function\n * @param {ModalCardFootProps} props - Component props.\n * @returns {JSX.Element} Modal card footer element.\n */\nconst ModalCardFoot: React.FC<ModalCardFootProps> = ({\n className,\n ...props\n}) => {\n const classes = classNames(\n usePrefixedClassNames('modal-card-foot'),\n className\n );\n return <footer className={classes} {...props} />;\n};\n\n/**\n * Modal.Card - Renders modal card wrapper with compound components.\n * Use with Modal.Card.Head, Modal.Card.Title, Modal.Card.Body, and Modal.Card.Foot.\n *\n * @function\n * @param {ModalCardProps} props - Component props.\n * @returns {JSX.Element} Modal card element.\n */\nconst ModalCardComponent: React.FC<ModalCardProps> = ({\n className,\n ...props\n}) => {\n const classes = classNames(usePrefixedClassNames('modal-card'), className);\n return <div className={classes} {...props} />;\n};\n\nconst ModalCard = withSubComponents(ModalCardComponent, {\n Head: ModalCardHead,\n Title: ModalCardTitle,\n Body: ModalCardBody,\n Foot: ModalCardFoot,\n});\n\n/**\n * Modal.Close - Renders modal close button with two variant styles.\n *\n * @function\n * @param {ModalCloseProps} props - Component props.\n * @returns {JSX.Element} Close button element.\n *\n * @remarks\n * Supports two variants:\n * - 'delete' (default): For use in modal card headers, renders with 'delete' class\n * - 'floating': For floating overlay close button, renders with 'modal-close' class\n *\n * The size prop only applies to the 'floating' variant.\n */\nconst ModalClose: React.FC<ModalCloseProps> = ({\n className,\n size = 'large',\n variant = 'delete',\n ...props\n}) => {\n const classes = classNames(\n usePrefixedClassNames(\n variant === 'delete' ? 'delete' : 'modal-close',\n variant === 'floating' && size && { [`is-${size}`]: true }\n ),\n className\n );\n return (\n <button\n className={classes}\n {...props}\n // After the spread, reading through it: a key the spread carries with no\n // value would otherwise erase the default, leaving a nameless button\n // that submits the form around it. An empty label falls back too: the\n // button is drawn as an icon, so `''` would leave it unnamed. A caller's\n // own value still wins; `buttonType` says more.\n aria-label={props['aria-label'] || 'close'}\n type={buttonType(props.type)}\n />\n );\n};\n\n/**\n * The `Modal` component provides an empty, accessible overlay for arbitrary content — for a ready-made confirm or alert, reach for `Dialog` instead.\n *\n * @function\n * @param {ModalProps} props - Props for the Modal component.\n * @returns {JSX.Element} The rendered modal.\n *\n * @example\n * // Legacy API\n * <Modal active={isOpen} onClose={handleClose} modalCardTitle=\"Title\">\n * Content\n * </Modal>\n *\n * @example\n * // Compound Component API\n * <Modal isActive={isOpen}>\n * <Modal.Background onClick={handleClose} />\n * <Modal.Card>\n * <Modal.Card.Head>\n * <Modal.Card.Title>Title</Modal.Card.Title>\n * <Modal.Close onClick={handleClose} />\n * </Modal.Card.Head>\n * <Modal.Card.Body>Content</Modal.Card.Body>\n * </Modal.Card>\n * </Modal>\n *\n * @see {@link https://bulma.io/documentation/components/modal/ | Bulma Modal documentation}\n */\nconst ModalRoot = forwardRef<HTMLDivElement, ModalProps>(function ModalRoot(\n {\n active,\n isActive,\n onClose,\n className,\n textColor,\n bgColor,\n modalCardTitle,\n modalCardFoot,\n type,\n children,\n closeOnEscape = true,\n lockScroll = true,\n portal = false,\n role,\n 'aria-modal': ariaModalProp,\n ...props\n },\n ref\n) {\n const { classPrefix, portalContainer } = useConfig();\n const { bulmaHelperClasses, rest } = useBulmaClasses({\n color: textColor,\n backgroundColor: bgColor,\n ...props,\n });\n\n // Support both active and isActive props\n const isModalActive = active ?? isActive ?? false;\n\n const modalRootRef = useRef<HTMLDivElement>(null);\n\n // Cleanup returned by a consumer's callback ref on attach, held until detach.\n const consumerCleanupRef = useRef<(() => void) | null>(null);\n\n // The forwarded ref and the internal one must both see the node: focus\n // management, the scroll lock and the topmost-modal check all read\n // `modalRootRef`, while consumers expect their own ref to resolve.\n const combinedRef = useCallback(\n (node: HTMLDivElement | null) => {\n (modalRootRef as React.MutableRefObject<HTMLDivElement | null>).current =\n node;\n if (typeof ref === 'function') {\n // React 19 lets a callback ref return a cleanup function and detaches\n // by running it instead of calling the ref with `null`; React 18\n // discards the return value entirely. Returning it from here would be\n // a React-19-only contract, so instead we hold the cleanup and run it\n // ourselves on detach — a consumer's cleanup ref then behaves the same\n // on both majors of the CI matrix. Same shape as `Dropdown`.\n if (node === null) {\n const consumerCleanup = consumerCleanupRef.current;\n consumerCleanupRef.current = null;\n if (consumerCleanup) {\n consumerCleanup();\n } else {\n ref(null);\n }\n return;\n }\n const cleanup: unknown = ref(node);\n consumerCleanupRef.current =\n typeof cleanup === 'function' ? (cleanup as () => void) : null;\n } else if (ref) {\n (ref as React.MutableRefObject<HTMLDivElement | null>).current = node;\n }\n },\n [ref]\n );\n const previouslyFocusedRef = useRef<HTMLElement | null>(null);\n const stackTokenRef = useRef<object>({});\n const generatedTitleId = useId();\n // A portal has no server-rendered counterpart, so the server render and the\n // hydrating render have to stay inline and only move into the portal once\n // we're past hydration.\n const onClient = useIsHydrated();\n const resolveContainer = usePortalContainer();\n // Moving into the portal, or from one portal target to another, remounts\n // the modal's subtree, so any effect holding a DOM node from before the move\n // is holding a detached one. Effects that touch the modal's nodes key on\n // this, on `portal` and on `defaultTarget`, so they re-run against the new\n // tree.\n const isPortaled = Boolean(portal) && onClient;\n // The configured `portalContainer` only places a modal portaled with\n // `portal` set to `true`. In place, or with a target of its own, a change\n // to it moves nothing, so it mustn't re-run the effects either.\n const defaultTarget =\n isPortaled && portal === true ? portalContainer : undefined;\n\n useScrollLock(isModalActive && lockScroll);\n\n // Record open order so keyboard handling only applies to the topmost modal.\n useEffect(() => {\n if (!isModalActive) return undefined;\n\n const token = stackTokenRef.current;\n activeModalStack.push(token);\n return () => {\n const index = activeModalStack.indexOf(token);\n if (index !== -1) activeModalStack.splice(index, 1);\n };\n }, [isModalActive]);\n\n // Escape closes; Tab cycles inside the modal. Both only for the topmost one.\n useEffect(() => {\n if (!isModalActive) return undefined;\n\n const handleKeyDown = (e: KeyboardEvent) => {\n const node = modalRootRef.current;\n if (\n activeModalStack[activeModalStack.length - 1] !== stackTokenRef.current\n ) {\n return;\n }\n\n if (e.key === 'Escape') {\n if (closeOnEscape) onClose?.();\n return;\n }\n if (e.key !== 'Tab' || !node) return;\n\n // Keep Tab within the modal — `aria-modal` hides the rest of the page\n // from assistive technology, so the keyboard order has to agree.\n const focusable = getTabbable(node);\n const activeElement = getActiveElementInTree(node);\n if (focusable.length === 0) {\n e.preventDefault();\n node.focus();\n return;\n }\n\n const first = focusable[0];\n const last = focusable[focusable.length - 1];\n const outside = !node.contains(activeElement);\n if (e.shiftKey) {\n if (outside || activeElement === first) {\n e.preventDefault();\n last.focus();\n }\n } else if (outside || activeElement === last) {\n e.preventDefault();\n first.focus();\n }\n };\n\n document.addEventListener('keydown', handleKeyDown);\n return () => document.removeEventListener('keydown', handleKeyDown);\n }, [isModalActive, closeOnEscape, onClose]);\n\n // Move focus into the modal on open, restore it on close\n useEffect(() => {\n if (!isModalActive) return undefined;\n\n // The element that had focus, wherever it sits: a portaled modal renders\n // under `document.body` while its opener can be inside a shadow root.\n previouslyFocusedRef.current =\n getDeepestActiveElement() as HTMLElement | null;\n const node = modalRootRef.current;\n const focusable = node ? getTabbable(node)[0] : undefined;\n (focusable ?? node)?.focus();\n\n return () => {\n // Only hand focus back if this modal still owns it: closing a background\n // modal must not pull focus out of one that is still open on top. A\n // removed subtree leaves focus on <body>, which still counts as ours.\n const activeElement = getActiveElementInTree(node);\n if (\n activeElement &&\n activeElement !== document.body &&\n !node?.contains(activeElement)\n ) {\n return;\n }\n previouslyFocusedRef.current?.focus?.();\n };\n // `isPortaled` flips once when a hydrated portal modal moves out of the\n // inline tree, and `portal` or `defaultTarget` changing moves a portaled\n // one to another target; re-running rebinds focus onto the remounted\n // nodes. The cleanup above restores focus to the pre-open element first\n // (the detached subtree has left focus on <body>), so the re-run re-records\n // the same element and restore-on-close still lands in the right place.\n }, [isModalActive, isPortaled, portal, defaultTarget]);\n\n // Check if children contain compound components\n const hasCompoundComponents = React.Children.toArray(children).some(\n child =>\n React.isValidElement(child) &&\n (child.type === ModalBackground ||\n child.type === ModalContent ||\n child.type === ModalCard ||\n child.type === ModalClose)\n );\n\n // Generate Bulma classes with prefix\n const bulmaClasses = usePrefixedClassNames('modal', {\n 'is-active': isModalActive,\n });\n const deleteClass = usePrefixedClassNames('delete');\n\n const modalClasses = classNames(bulmaClasses, bulmaHelperClasses, className);\n\n const resolvedRole = role ?? (isModalActive ? 'dialog' : undefined);\n const resolvedAriaModal =\n ariaModalProp ??\n (isModalActive && resolvedRole !== 'presentation' ? 'true' : undefined);\n // A dialog needs an accessible name: the legacy card title is wired up\n // automatically, compound users pass their own aria-label/aria-labelledby\n // (both arrive in `rest`, which is spread last and so wins).\n const titleId = modalCardTitle ? generatedTitleId : undefined;\n\n let modalElement: React.ReactElement;\n\n // If using compound components, render children as-is\n if (hasCompoundComponents) {\n modalElement = (\n <div\n className={modalClasses}\n ref={combinedRef}\n role={resolvedRole}\n aria-modal={resolvedAriaModal}\n tabIndex={-1}\n {...rest}\n data-testid=\"modal\"\n >\n {children}\n </div>\n );\n } else {\n // Legacy API: EXPLICIT type wins; fallback to auto detection if not provided\n let isModalCard: boolean;\n if (type === 'card') isModalCard = true;\n else if (type === 'content') isModalCard = false;\n else isModalCard = !!modalCardTitle || !!modalCardFoot;\n\n modalElement = (\n <div\n className={modalClasses}\n ref={combinedRef}\n role={resolvedRole}\n aria-modal={resolvedAriaModal}\n aria-labelledby={titleId}\n tabIndex={-1}\n {...rest}\n data-testid=\"modal\"\n >\n <div\n className={prefixedClassNames(classPrefix, 'modal-background')}\n onClick={onClose}\n data-testid=\"modal-background\"\n />\n {isModalCard ? (\n <div className={prefixedClassNames(classPrefix, 'modal-card')}>\n {modalCardTitle && (\n <header\n className={prefixedClassNames(classPrefix, 'modal-card-head')}\n >\n <p\n id={titleId}\n className={prefixedClassNames(\n classPrefix,\n 'modal-card-title'\n )}\n >\n {modalCardTitle}\n </p>\n {onClose && (\n <button\n className={deleteClass}\n aria-label=\"close\"\n onClick={onClose}\n type=\"button\"\n data-testid=\"modal-close\"\n />\n )}\n </header>\n )}\n <section\n className={prefixedClassNames(classPrefix, 'modal-card-body')}\n data-testid=\"modal-body\"\n >\n {children}\n </section>\n {modalCardFoot && (\n <footer\n className={prefixedClassNames(classPrefix, 'modal-card-foot')}\n >\n {modalCardFoot}\n </footer>\n )}\n </div>\n ) : (\n <div\n className={prefixedClassNames(classPrefix, 'modal-content')}\n data-testid=\"modal-content\"\n >\n {children}\n </div>\n )}\n {/* Show floating close button for modal-content, or for modal-card when no header */}\n {(!isModalCard || (!modalCardTitle && onClose)) && onClose && (\n <button\n className={prefixedClassNames(\n classPrefix,\n 'modal-close',\n 'is-large'\n )}\n aria-label=\"close\"\n onClick={onClose}\n type=\"button\"\n data-testid=\"modal-close-float\"\n />\n )}\n </div>\n );\n }\n\n if (isPortaled) {\n const target = resolveContainer(\n typeof portal === 'boolean' ? undefined : portal\n );\n return createPortal(modalElement, target);\n }\n\n return modalElement;\n});\n\nexport const Modal = withSubComponents(\n ModalRoot,\n {\n Background: ModalBackground,\n Content: ModalContent,\n Card: ModalCard,\n Close: ModalClose,\n },\n 'Modal'\n);\nexport default Modal;\n"],"names":["_jsx","_jsxs"],"mappings":"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"}