@mattermost/compass-ui 0.1.0-alpha.21 → 0.1.0-alpha.23
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.
- package/dist/components/combobox/Combobox.cjs +1 -1
- package/dist/components/combobox/Combobox.cjs.map +1 -1
- package/dist/components/combobox/Combobox.css +1 -1
- package/dist/components/combobox/Combobox.d.ts +43 -3
- package/dist/components/combobox/Combobox.d.ts.map +1 -1
- package/dist/components/combobox/Combobox.js +298 -283
- package/dist/components/combobox/Combobox.js.map +1 -1
- package/dist/components/combobox/Combobox.module.scss.cjs +1 -1
- package/dist/components/combobox/Combobox.module.scss.js +25 -25
- package/dist/components/combobox/index.d.ts +1 -1
- package/dist/components/combobox/index.d.ts.map +1 -1
- package/dist/components/dropdown/Dropdown.css +1 -1
- package/dist/components/dropdown/Dropdown.module.scss.cjs +1 -1
- package/dist/components/dropdown/Dropdown.module.scss.js +14 -14
- package/dist/components/global-banner/GlobalBanner.css +1 -1
- package/dist/components/global-banner/GlobalBanner.module.scss.cjs +1 -1
- package/dist/components/global-banner/GlobalBanner.module.scss.js +10 -10
- package/dist/components/modal/Modal.cjs +1 -1
- package/dist/components/modal/Modal.cjs.map +1 -1
- package/dist/components/modal/Modal.d.ts.map +1 -1
- package/dist/components/modal/Modal.js +91 -93
- package/dist/components/modal/Modal.js.map +1 -1
- package/dist/components/modal/ModalOverlay.cjs +2 -0
- package/dist/components/modal/ModalOverlay.cjs.map +1 -0
- package/dist/components/modal/ModalOverlay.css +1 -1
- package/dist/components/modal/ModalOverlay.d.ts +43 -0
- package/dist/components/modal/ModalOverlay.d.ts.map +1 -0
- package/dist/components/modal/ModalOverlay.js +59 -0
- package/dist/components/modal/ModalOverlay.js.map +1 -0
- package/dist/components/modal/ModalOverlay.module.scss.cjs +1 -1
- package/dist/components/modal/ModalOverlay.module.scss.js +3 -3
- package/dist/components/modal/index.cjs +1 -1
- package/dist/components/modal/index.d.ts +2 -0
- package/dist/components/modal/index.d.ts.map +1 -1
- package/dist/components/modal/index.js +4 -2
- package/dist/components/modal/index.js.map +1 -1
- package/dist/components/password-input/PasswordInput.css +1 -1
- package/dist/components/password-input/PasswordInput.module.scss.cjs +1 -1
- package/dist/components/password-input/PasswordInput.module.scss.js +12 -12
- package/dist/components/progress-bar/ProgressBar.cjs +1 -1
- package/dist/components/progress-bar/ProgressBar.cjs.map +1 -1
- package/dist/components/progress-bar/ProgressBar.d.ts +10 -2
- package/dist/components/progress-bar/ProgressBar.d.ts.map +1 -1
- package/dist/components/progress-bar/ProgressBar.js +31 -21
- package/dist/components/progress-bar/ProgressBar.js.map +1 -1
- package/dist/components/search-input/SearchInput.css +1 -1
- package/dist/components/search-input/SearchInput.module.scss.cjs +1 -1
- package/dist/components/search-input/SearchInput.module.scss.js +15 -15
- package/dist/components/text-area/TextArea.css +1 -1
- package/dist/components/text-area/TextArea.module.scss.cjs +1 -1
- package/dist/components/text-area/TextArea.module.scss.js +12 -12
- package/dist/components/text-input/TextInput.css +1 -1
- package/dist/components/text-input/TextInput.module.scss.cjs +1 -1
- package/dist/components/text-input/TextInput.module.scss.js +19 -19
- package/dist/components/thread-footer/ThreadFooter.css +1 -1
- package/dist/components/thread-footer/ThreadFooter.module.scss.cjs +1 -1
- package/dist/components/thread-footer/ThreadFooter.module.scss.js +18 -18
- package/dist/components/toast/Toast.css +1 -1
- package/dist/components/toast/Toast.module.scss.cjs +1 -1
- package/dist/components/toast/Toast.module.scss.js +10 -10
- package/dist/components/tour-point/TourPoint.css +1 -1
- package/dist/components/tour-point/TourPoint.module.scss.cjs +1 -1
- package/dist/components/tour-point/TourPoint.module.scss.js +28 -28
- package/dist/index.cjs +1 -1
- package/dist/index.js +97 -95
- package/dist/index.js.map +1 -1
- package/dist/utils/scrollLock.cjs +2 -0
- package/dist/utils/scrollLock.cjs.map +1 -0
- package/dist/utils/scrollLock.d.ts +9 -0
- package/dist/utils/scrollLock.d.ts.map +1 -0
- package/dist/utils/scrollLock.js +38 -0
- package/dist/utils/scrollLock.js.map +1 -0
- package/dist/utils/useOnExited.cjs +2 -0
- package/dist/utils/useOnExited.cjs.map +1 -0
- package/dist/utils/useOnExited.d.ts +7 -0
- package/dist/utils/useOnExited.d.ts.map +1 -0
- package/dist/utils/useOnExited.js +14 -0
- package/dist/utils/useOnExited.js.map +1 -0
- package/package.json +2 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Modal.cjs","sources":["../../../src/components/modal/Modal.tsx"],"sourcesContent":["import type {\n CSSProperties,\n HTMLAttributes,\n ReactNode,\n RefObject,\n} from 'react';\nimport { useEffect, useId, useRef } from 'react';\nimport { useModalFocus } from '@/hooks/use-modal-focus';\nimport { usePopoverTransition } from '@/hooks/use-popover-transition';\nimport Scrollbar from '@/components/scrollbar/Scrollbar';\nimport ModalFooter from '@/components/modal-footer/ModalFooter';\nimport type { ModalFooterType } from '@/components/modal-footer/ModalFooter';\nimport ModalHeader from '@/components/modal-header/ModalHeader';\nimport type { ModalSubtitlePlacement } from '@/components/modal-header/ModalHeader';\nimport type { BuiltInButtonProps } from '@/utils/props';\nimport { toKebab } from '@/utils/string';\nimport styles from './Modal.module.scss';\n\nexport type ModalSize = 'small' | 'medium' | 'large';\n\n/**\n * Body inset. `menu` is for MenuItem lists — 8px vertical / 16px horizontal so\n * row labels align with the 32px header/footer margins. `none` removes inset\n * for host-owned full-bleed layouts (e.g. settings sidebars).\n */\nexport type ModalBodyPadding = 'default' | 'menu' | 'none';\n\nexport type { ModalFooterType, ModalSubtitlePlacement };\n\nexport interface ModalProps extends Omit<\n HTMLAttributes<HTMLDivElement>,\n 'title' | 'children' | 'style'\n> {\n /** Extra class on the dialog root (size + chrome classes still apply). */\n className?: string;\n /** Inline style on the dialog root — prefer `className` for layout overrides. */\n style?: CSSProperties;\n /** Width variant. Figma: Size — Small 600px, Medium 704px, Large 832px. */\n size?: ModalSize;\n /** Modal heading text. Required for a11y; use `hideTitle` for close-only chrome. */\n title: ReactNode;\n /** Optional secondary line — placement via `subtitlePlacement`. */\n subtitle?: ReactNode;\n /**\n * Where the subtitle sits. Figma Modal Header: Below | Beside.\n * Default: `below`.\n */\n subtitlePlacement?: ModalSubtitlePlacement;\n /** Visually hide the title (close-only header); title remains for `aria-labelledby`. */\n hideTitle?: boolean;\n /** When true, shows a back-arrow button before the title. */\n showBackButton?: boolean;\n /** Called when the back button is clicked. */\n onBack?: () => void;\n /** Accessible name for the back button. Default: \"Go back\". */\n backLabel?: string;\n /** Extra attributes for the back button (e.g. `data-testid`). */\n backButtonProps?: BuiltInButtonProps;\n /** Called when the × close button is clicked. */\n onClose?: () => void;\n /** Accessible name for the close button. Default: \"Close\". */\n closeLabel?: string;\n /** Extra attributes for the close button (e.g. `data-testid`). */\n closeButtonProps?: BuiltInButtonProps;\n /** Show divider between header and body. Default: true. */\n headerDivider?: boolean;\n /** Optional control before close (Button, search field, …). */\n headerAction?: ReactNode;\n /**\n * Full-width slot rendered below the title row and above the header divider.\n * Accepts any ReactNode — SearchInput, Tabs, a custom toolbar, etc.\n */\n headerSlot?: ReactNode;\n /**\n * Body inset. Default is 32px horizontal / 28px vertical. Use `menu` for\n * MenuItem lists. Use `none` when the host owns padding.\n */\n bodyPadding?: ModalBodyPadding;\n /**\n * When false, the body fills the dialog height so host panes can manage\n * their own scroll regions (settings-style layouts). Default true sizes the\n * body to its content. Overflow always uses Scrollbar (default track width).\n */\n scrollable?: boolean;\n /** Body content. */\n children: ReactNode;\n /** Footer slot — typically Buttons. Wrapped in `ModalFooter`. */\n footer?: ReactNode;\n /** Left footer slot (pagination status, PaginationDots, …). */\n footerLeading?: ReactNode;\n /** Footer layout. Figma Modal Footer Type. Default: `2-actions`. */\n footerType?: ModalFooterType;\n /** Show divider between body and footer. Default: true. */\n footerDivider?: boolean;\n /**\n * Close on Escape via a document listener while mounted. Escapes already\n * handled by an inner widget (`defaultPrevented`) are ignored. Default: true.\n */\n closeOnEscape?: boolean;\n /**\n * Initial focus, Tab trap and focus restore. Set false when the host owns\n * focus (e.g. a floating-ui `FloatingFocusManager`); pair with\n * `closeOnEscape={false}` when the host also handles Escape. Initial focus\n * and restore use the value at mount; the Tab trap follows later updates.\n * Default: true.\n */\n manageFocus?: boolean;\n /**\n * Element to focus when the modal mounts (e.g. the first form field).\n * Default: the dialog root.\n */\n initialFocusRef?: RefObject<HTMLElement | null>;\n /**\n * Controlled visibility with the Compass enter/exit transition (fade + drop from the top + subtle scale 98%→100%,\n * reverse on close, `--duration-quick`); the modal unmounts after the exit,\n * and focus is restored then. Omit to keep the mount-controlled behavior\n * (always rendered, no transition). The host still owns the state.\n */\n open?: boolean;\n /** Called after the exit transition finishes and the modal unmounts (only when `open` is controlled). */\n onExited?: () => void;\n}\n\ninterface ModalPanelState {\n active: boolean;\n transition: boolean;\n visible: boolean;\n}\n\nfunction ModalPanel({\n className = '',\n style,\n size = 'small',\n title,\n subtitle,\n subtitlePlacement = 'below',\n hideTitle = false,\n showBackButton = false,\n onBack,\n backLabel,\n backButtonProps,\n onClose,\n closeLabel,\n closeButtonProps,\n headerDivider = true,\n headerAction,\n headerSlot,\n bodyPadding = 'default',\n scrollable = true,\n children,\n footer,\n footerLeading,\n footerType = '2-actions',\n footerDivider = true,\n closeOnEscape = true,\n manageFocus = true,\n initialFocusRef,\n active,\n transition,\n visible,\n ...rest\n}: ModalProps & ModalPanelState) {\n const rootRef = useRef<HTMLDivElement>(null);\n useModalFocus(rootRef, {\n initialFocusRef,\n closeOnEscape,\n manageFocus,\n onClose,\n active,\n });\n const titleId = useId();\n const sizeClass = styles[`modal--size-${toKebab(size)}`];\n const bodyInnerClass = [\n styles['modal__body-inner'],\n bodyPadding === 'menu' && styles['modal__body-inner--menu'],\n bodyPadding === 'none' && styles['modal__body-inner--none'],\n !scrollable && styles['modal__body-inner--fill'],\n ]\n .filter(Boolean)\n .join(' ');\n\n const bodyInner = <div className={bodyInnerClass}>{children}</div>;\n const scrollClass = [\n styles['modal__scroll'],\n !scrollable && styles['modal__scroll--fill'],\n ]\n .filter(Boolean)\n .join(' ');\n const showFooter =\n footer != null ||\n footerLeading != null ||\n footerType === 'spacer-small' ||\n footerType === 'spacer-large';\n\n return (\n <div\n {...rest}\n ref={rootRef}\n tabIndex={-1}\n className={[\n styles.modal,\n sizeClass,\n transition && styles['modal--transition'],\n transition && visible && styles['modal--visible'],\n className,\n ]\n .filter(Boolean)\n .join(' ')}\n style={style}\n role=\"dialog\"\n aria-modal=\"true\"\n aria-labelledby={titleId}\n >\n <ModalHeader\n title={title}\n titleId={titleId}\n subtitle={subtitle}\n subtitlePlacement={subtitlePlacement}\n hideTitle={hideTitle}\n showBackButton={showBackButton}\n onBack={onBack}\n backLabel={backLabel}\n backButtonProps={backButtonProps}\n onClose={onClose}\n closeLabel={closeLabel}\n closeButtonProps={closeButtonProps}\n divider={headerDivider}\n headerAction={headerAction}\n headerSlot={headerSlot}\n />\n\n <div className={styles['modal__body']}>\n <Scrollbar className={scrollClass}>{bodyInner}</Scrollbar>\n </div>\n\n {showFooter && (\n <ModalFooter\n type={footerType}\n divider={footerDivider}\n leading={footerLeading}\n >\n {footer}\n </ModalFooter>\n )}\n </div>\n );\n}\n\n/**\n * Modal dialog shell — composes ModalHeader, scrollable body, and ModalFooter.\n * Owns dialog-level focus and keyboard behavior: initial focus, Tab trap,\n * Escape, and focus restore on unmount. Host owns portal, overlay/backdrop,\n * scroll lock, stacking, and open state. Mount-controlled by default; pass\n * `open` for the enter/exit transition.\n */\nexport default function Modal({ open, onExited, ...props }: ModalProps) {\n const controlled = open !== undefined;\n const { mounted, visible } = usePopoverTransition(open ?? true);\n const wasMounted = useRef(mounted);\n const onExitedRef = useRef(onExited);\n onExitedRef.current = onExited;\n\n useEffect(() => {\n if (wasMounted.current && !mounted && controlled) {\n onExitedRef.current?.();\n }\n wasMounted.current = mounted;\n }, [mounted, controlled]);\n\n if (controlled && !mounted) return null;\n\n return (\n <ModalPanel\n {...props}\n active={open ?? true}\n transition={controlled}\n visible={visible}\n />\n );\n}\n"],"names":["ModalPanel","className","style","size","title","subtitle","subtitlePlacement","hideTitle","showBackButton","onBack","backLabel","backButtonProps","onClose","closeLabel","closeButtonProps","headerDivider","headerAction","headerSlot","bodyPadding","scrollable","children","footer","footerLeading","footerType","footerDivider","closeOnEscape","manageFocus","initialFocusRef","active","transition","visible","rest","rootRef","useRef","useModalFocus","titleId","useId","sizeClass","styles","toKebab","bodyInnerClass","bodyInner","jsx","scrollClass","showFooter","jsxs","ModalHeader","Scrollbar","ModalFooter","Modal","open","onExited","props","controlled","mounted","usePopoverTransition","wasMounted","onExitedRef","useEffect","_a"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"Modal.cjs","sources":["../../../src/components/modal/Modal.tsx"],"sourcesContent":["import type {\n CSSProperties,\n HTMLAttributes,\n ReactNode,\n RefObject,\n} from 'react';\nimport { useId, useRef } from 'react';\nimport { useModalFocus } from '@/hooks/use-modal-focus';\nimport { usePopoverTransition } from '@/hooks/use-popover-transition';\nimport Scrollbar from '@/components/scrollbar/Scrollbar';\nimport ModalFooter from '@/components/modal-footer/ModalFooter';\nimport type { ModalFooterType } from '@/components/modal-footer/ModalFooter';\nimport ModalHeader from '@/components/modal-header/ModalHeader';\nimport type { ModalSubtitlePlacement } from '@/components/modal-header/ModalHeader';\nimport type { BuiltInButtonProps } from '@/utils/props';\nimport { toKebab } from '@/utils/string';\nimport { useOnExited } from '@/utils/useOnExited';\nimport styles from './Modal.module.scss';\n\nexport type ModalSize = 'small' | 'medium' | 'large';\n\n/**\n * Body inset. `menu` is for MenuItem lists — 8px vertical / 16px horizontal so\n * row labels align with the 32px header/footer margins. `none` removes inset\n * for host-owned full-bleed layouts (e.g. settings sidebars).\n */\nexport type ModalBodyPadding = 'default' | 'menu' | 'none';\n\nexport type { ModalFooterType, ModalSubtitlePlacement };\n\nexport interface ModalProps extends Omit<\n HTMLAttributes<HTMLDivElement>,\n 'title' | 'children' | 'style'\n> {\n /** Extra class on the dialog root (size + chrome classes still apply). */\n className?: string;\n /** Inline style on the dialog root — prefer `className` for layout overrides. */\n style?: CSSProperties;\n /** Width variant. Figma: Size — Small 600px, Medium 704px, Large 832px. */\n size?: ModalSize;\n /** Modal heading text. Required for a11y; use `hideTitle` for close-only chrome. */\n title: ReactNode;\n /** Optional secondary line — placement via `subtitlePlacement`. */\n subtitle?: ReactNode;\n /**\n * Where the subtitle sits. Figma Modal Header: Below | Beside.\n * Default: `below`.\n */\n subtitlePlacement?: ModalSubtitlePlacement;\n /** Visually hide the title (close-only header); title remains for `aria-labelledby`. */\n hideTitle?: boolean;\n /** When true, shows a back-arrow button before the title. */\n showBackButton?: boolean;\n /** Called when the back button is clicked. */\n onBack?: () => void;\n /** Accessible name for the back button. Default: \"Go back\". */\n backLabel?: string;\n /** Extra attributes for the back button (e.g. `data-testid`). */\n backButtonProps?: BuiltInButtonProps;\n /** Called when the × close button is clicked. */\n onClose?: () => void;\n /** Accessible name for the close button. Default: \"Close\". */\n closeLabel?: string;\n /** Extra attributes for the close button (e.g. `data-testid`). */\n closeButtonProps?: BuiltInButtonProps;\n /** Show divider between header and body. Default: true. */\n headerDivider?: boolean;\n /** Optional control before close (Button, search field, …). */\n headerAction?: ReactNode;\n /**\n * Full-width slot rendered below the title row and above the header divider.\n * Accepts any ReactNode — SearchInput, Tabs, a custom toolbar, etc.\n */\n headerSlot?: ReactNode;\n /**\n * Body inset. Default is 32px horizontal / 28px vertical. Use `menu` for\n * MenuItem lists. Use `none` when the host owns padding.\n */\n bodyPadding?: ModalBodyPadding;\n /**\n * When false, the body fills the dialog height so host panes can manage\n * their own scroll regions (settings-style layouts). Default true sizes the\n * body to its content. Overflow always uses Scrollbar (default track width).\n */\n scrollable?: boolean;\n /** Body content. */\n children: ReactNode;\n /** Footer slot — typically Buttons. Wrapped in `ModalFooter`. */\n footer?: ReactNode;\n /** Left footer slot (pagination status, PaginationDots, …). */\n footerLeading?: ReactNode;\n /** Footer layout. Figma Modal Footer Type. Default: `2-actions`. */\n footerType?: ModalFooterType;\n /** Show divider between body and footer. Default: true. */\n footerDivider?: boolean;\n /**\n * Close on Escape via a document listener while mounted. Escapes already\n * handled by an inner widget (`defaultPrevented`) are ignored. Default: true.\n */\n closeOnEscape?: boolean;\n /**\n * Initial focus, Tab trap and focus restore. Set false when the host owns\n * focus (e.g. a floating-ui `FloatingFocusManager`); pair with\n * `closeOnEscape={false}` when the host also handles Escape. Initial focus\n * and restore use the value at mount; the Tab trap follows later updates.\n * Default: true.\n */\n manageFocus?: boolean;\n /**\n * Element to focus when the modal mounts (e.g. the first form field).\n * Default: the dialog root.\n */\n initialFocusRef?: RefObject<HTMLElement | null>;\n /**\n * Controlled visibility with the Compass enter/exit transition (fade + drop from the top + subtle scale 98%→100%,\n * reverse on close, `--duration-quick`); the modal unmounts after the exit,\n * and focus is restored then. Omit to keep the mount-controlled behavior\n * (always rendered, no transition). The host still owns the state.\n */\n open?: boolean;\n /** Called after the exit transition finishes and the modal unmounts (only when `open` is controlled). */\n onExited?: () => void;\n}\n\ninterface ModalPanelState {\n active: boolean;\n transition: boolean;\n visible: boolean;\n}\n\nfunction ModalPanel({\n className = '',\n style,\n size = 'small',\n title,\n subtitle,\n subtitlePlacement = 'below',\n hideTitle = false,\n showBackButton = false,\n onBack,\n backLabel,\n backButtonProps,\n onClose,\n closeLabel,\n closeButtonProps,\n headerDivider = true,\n headerAction,\n headerSlot,\n bodyPadding = 'default',\n scrollable = true,\n children,\n footer,\n footerLeading,\n footerType = '2-actions',\n footerDivider = true,\n closeOnEscape = true,\n manageFocus = true,\n initialFocusRef,\n active,\n transition,\n visible,\n ...rest\n}: ModalProps & ModalPanelState) {\n const rootRef = useRef<HTMLDivElement>(null);\n useModalFocus(rootRef, {\n initialFocusRef,\n closeOnEscape,\n manageFocus,\n onClose,\n active,\n });\n const titleId = useId();\n const sizeClass = styles[`modal--size-${toKebab(size)}`];\n const bodyInnerClass = [\n styles['modal__body-inner'],\n bodyPadding === 'menu' && styles['modal__body-inner--menu'],\n bodyPadding === 'none' && styles['modal__body-inner--none'],\n !scrollable && styles['modal__body-inner--fill'],\n ]\n .filter(Boolean)\n .join(' ');\n\n const bodyInner = <div className={bodyInnerClass}>{children}</div>;\n const scrollClass = [\n styles['modal__scroll'],\n !scrollable && styles['modal__scroll--fill'],\n ]\n .filter(Boolean)\n .join(' ');\n const showFooter =\n footer != null ||\n footerLeading != null ||\n footerType === 'spacer-small' ||\n footerType === 'spacer-large';\n\n return (\n <div\n {...rest}\n ref={rootRef}\n tabIndex={-1}\n className={[\n styles.modal,\n sizeClass,\n transition && styles['modal--transition'],\n transition && visible && styles['modal--visible'],\n className,\n ]\n .filter(Boolean)\n .join(' ')}\n style={style}\n role=\"dialog\"\n aria-modal=\"true\"\n aria-labelledby={titleId}\n >\n <ModalHeader\n title={title}\n titleId={titleId}\n subtitle={subtitle}\n subtitlePlacement={subtitlePlacement}\n hideTitle={hideTitle}\n showBackButton={showBackButton}\n onBack={onBack}\n backLabel={backLabel}\n backButtonProps={backButtonProps}\n onClose={onClose}\n closeLabel={closeLabel}\n closeButtonProps={closeButtonProps}\n divider={headerDivider}\n headerAction={headerAction}\n headerSlot={headerSlot}\n />\n\n <div className={styles['modal__body']}>\n <Scrollbar className={scrollClass}>{bodyInner}</Scrollbar>\n </div>\n\n {showFooter && (\n <ModalFooter\n type={footerType}\n divider={footerDivider}\n leading={footerLeading}\n >\n {footer}\n </ModalFooter>\n )}\n </div>\n );\n}\n\n/**\n * Modal dialog shell — composes ModalHeader, scrollable body, and ModalFooter.\n * Owns dialog-level focus and keyboard behavior: initial focus, Tab trap,\n * Escape, and focus restore on unmount. Host owns portal, overlay/backdrop,\n * scroll lock, stacking, and open state. Mount-controlled by default; pass\n * `open` for the enter/exit transition.\n */\nexport default function Modal({ open, onExited, ...props }: ModalProps) {\n const controlled = open !== undefined;\n const { mounted, visible } = usePopoverTransition(open ?? true);\n useOnExited(mounted, onExited, controlled);\n\n if (controlled && !mounted) return null;\n\n return (\n <ModalPanel\n {...props}\n active={open ?? true}\n transition={controlled}\n visible={visible}\n />\n );\n}\n"],"names":["ModalPanel","className","style","size","title","subtitle","subtitlePlacement","hideTitle","showBackButton","onBack","backLabel","backButtonProps","onClose","closeLabel","closeButtonProps","headerDivider","headerAction","headerSlot","bodyPadding","scrollable","children","footer","footerLeading","footerType","footerDivider","closeOnEscape","manageFocus","initialFocusRef","active","transition","visible","rest","rootRef","useRef","useModalFocus","titleId","useId","sizeClass","styles","toKebab","bodyInnerClass","bodyInner","jsx","scrollClass","showFooter","jsxs","ModalHeader","Scrollbar","ModalFooter","Modal","open","onExited","props","controlled","mounted","usePopoverTransition","useOnExited"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Modal.d.ts","sourceRoot":"","sources":["../../../src/components/modal/Modal.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,aAAa,EACb,cAAc,EACd,SAAS,EACT,SAAS,EACV,MAAM,OAAO,CAAC;AAMf,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,sCAAsC,CAAC;AAE5E,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,sCAAsC,CAAC;AACnF,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"Modal.d.ts","sourceRoot":"","sources":["../../../src/components/modal/Modal.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,aAAa,EACb,cAAc,EACd,SAAS,EACT,SAAS,EACV,MAAM,OAAO,CAAC;AAMf,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,sCAAsC,CAAC;AAE5E,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,sCAAsC,CAAC;AACnF,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AAKxD,MAAM,MAAM,SAAS,GAAG,OAAO,GAAG,QAAQ,GAAG,OAAO,CAAC;AAErD;;;;GAIG;AACH,MAAM,MAAM,gBAAgB,GAAG,SAAS,GAAG,MAAM,GAAG,MAAM,CAAC;AAE3D,YAAY,EAAE,eAAe,EAAE,sBAAsB,EAAE,CAAC;AAExD,MAAM,WAAW,UAAW,SAAQ,IAAI,CACtC,cAAc,CAAC,cAAc,CAAC,EAC9B,OAAO,GAAG,UAAU,GAAG,OAAO,CAC/B;IACC,0EAA0E;IAC1E,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,iFAAiF;IACjF,KAAK,CAAC,EAAE,aAAa,CAAC;IACtB,2EAA2E;IAC3E,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB,oFAAoF;IACpF,KAAK,EAAE,SAAS,CAAC;IACjB,mEAAmE;IACnE,QAAQ,CAAC,EAAE,SAAS,CAAC;IACrB;;;OAGG;IACH,iBAAiB,CAAC,EAAE,sBAAsB,CAAC;IAC3C,wFAAwF;IACxF,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,6DAA6D;IAC7D,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,8CAA8C;IAC9C,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC;IACpB,+DAA+D;IAC/D,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,iEAAiE;IACjE,eAAe,CAAC,EAAE,kBAAkB,CAAC;IACrC,iDAAiD;IACjD,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,8DAA8D;IAC9D,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,kEAAkE;IAClE,gBAAgB,CAAC,EAAE,kBAAkB,CAAC;IACtC,2DAA2D;IAC3D,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,+DAA+D;IAC/D,YAAY,CAAC,EAAE,SAAS,CAAC;IACzB;;;OAGG;IACH,UAAU,CAAC,EAAE,SAAS,CAAC;IACvB;;;OAGG;IACH,WAAW,CAAC,EAAE,gBAAgB,CAAC;IAC/B;;;;OAIG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,oBAAoB;IACpB,QAAQ,EAAE,SAAS,CAAC;IACpB,iEAAiE;IACjE,MAAM,CAAC,EAAE,SAAS,CAAC;IACnB,+DAA+D;IAC/D,aAAa,CAAC,EAAE,SAAS,CAAC;IAC1B,oEAAoE;IACpE,UAAU,CAAC,EAAE,eAAe,CAAC;IAC7B,2DAA2D;IAC3D,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB;;;OAGG;IACH,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB;;;;;;OAMG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB;;;OAGG;IACH,eAAe,CAAC,EAAE,SAAS,CAAC,WAAW,GAAG,IAAI,CAAC,CAAC;IAChD;;;;;OAKG;IACH,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,yGAAyG;IACzG,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAC;CACvB;AA+HD;;;;;;GAMG;AACH,MAAM,CAAC,OAAO,UAAU,KAAK,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,GAAG,KAAK,EAAE,EAAE,UAAU,kDAerE"}
|
|
@@ -1,126 +1,124 @@
|
|
|
1
|
-
import { jsx as
|
|
2
|
-
import { useRef as
|
|
3
|
-
import { useModalFocus as
|
|
4
|
-
import { usePopoverTransition as
|
|
5
|
-
import
|
|
6
|
-
import
|
|
7
|
-
import
|
|
8
|
-
import { toKebab as
|
|
1
|
+
import { jsx as l, jsxs as q } from "react/jsx-runtime";
|
|
2
|
+
import { useRef as A, useId as D } from "react";
|
|
3
|
+
import { useModalFocus as G } from "../../hooks/use-modal-focus.js";
|
|
4
|
+
import { usePopoverTransition as J } from "../../hooks/use-popover-transition.js";
|
|
5
|
+
import Q from "../scrollbar/Scrollbar.js";
|
|
6
|
+
import U from "../modal-footer/ModalFooter.js";
|
|
7
|
+
import V from "../modal-header/ModalHeader.js";
|
|
8
|
+
import { toKebab as W } from "../../utils/string.js";
|
|
9
|
+
import { useOnExited as X } from "../../utils/useOnExited.js";
|
|
9
10
|
import o from "./Modal.module.scss.js";
|
|
10
|
-
function
|
|
11
|
-
className:
|
|
12
|
-
style:
|
|
13
|
-
size:
|
|
11
|
+
function Y({
|
|
12
|
+
className: e = "",
|
|
13
|
+
style: n,
|
|
14
|
+
size: i = "small",
|
|
14
15
|
title: r,
|
|
15
|
-
subtitle:
|
|
16
|
-
subtitlePlacement:
|
|
17
|
-
hideTitle:
|
|
18
|
-
showBackButton:
|
|
19
|
-
onBack:
|
|
16
|
+
subtitle: a,
|
|
17
|
+
subtitlePlacement: t = "below",
|
|
18
|
+
hideTitle: v = !1,
|
|
19
|
+
showBackButton: y = !1,
|
|
20
|
+
onBack: h,
|
|
20
21
|
backLabel: j,
|
|
21
|
-
backButtonProps:
|
|
22
|
-
onClose:
|
|
23
|
-
closeLabel:
|
|
24
|
-
closeButtonProps:
|
|
25
|
-
headerDivider:
|
|
26
|
-
headerAction:
|
|
27
|
-
headerSlot:
|
|
28
|
-
bodyPadding:
|
|
29
|
-
scrollable:
|
|
30
|
-
children:
|
|
31
|
-
footer:
|
|
32
|
-
footerLeading:
|
|
33
|
-
footerType:
|
|
34
|
-
footerDivider:
|
|
35
|
-
closeOnEscape:
|
|
36
|
-
manageFocus:
|
|
37
|
-
initialFocusRef:
|
|
38
|
-
active:
|
|
39
|
-
transition:
|
|
40
|
-
visible:
|
|
41
|
-
...
|
|
22
|
+
backButtonProps: x,
|
|
23
|
+
onClose: d,
|
|
24
|
+
closeLabel: I,
|
|
25
|
+
closeButtonProps: M,
|
|
26
|
+
headerDivider: N = !0,
|
|
27
|
+
headerAction: B,
|
|
28
|
+
headerSlot: F,
|
|
29
|
+
bodyPadding: m = "default",
|
|
30
|
+
scrollable: c = !0,
|
|
31
|
+
children: w,
|
|
32
|
+
footer: u,
|
|
33
|
+
footerLeading: f,
|
|
34
|
+
footerType: s = "2-actions",
|
|
35
|
+
footerDivider: z = !0,
|
|
36
|
+
closeOnEscape: C = !0,
|
|
37
|
+
manageFocus: R = !0,
|
|
38
|
+
initialFocusRef: g,
|
|
39
|
+
active: E,
|
|
40
|
+
transition: b,
|
|
41
|
+
visible: H,
|
|
42
|
+
...K
|
|
42
43
|
}) {
|
|
43
|
-
const
|
|
44
|
-
|
|
45
|
-
initialFocusRef:
|
|
46
|
-
closeOnEscape:
|
|
47
|
-
manageFocus:
|
|
48
|
-
onClose:
|
|
49
|
-
active:
|
|
44
|
+
const _ = A(null);
|
|
45
|
+
G(_, {
|
|
46
|
+
initialFocusRef: g,
|
|
47
|
+
closeOnEscape: C,
|
|
48
|
+
manageFocus: R,
|
|
49
|
+
onClose: d,
|
|
50
|
+
active: E
|
|
50
51
|
});
|
|
51
|
-
const
|
|
52
|
+
const p = D(), O = o[`modal--size-${W(i)}`], P = [
|
|
52
53
|
o["modal__body-inner"],
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
!
|
|
56
|
-
].filter(Boolean).join(" "),
|
|
54
|
+
m === "menu" && o["modal__body-inner--menu"],
|
|
55
|
+
m === "none" && o["modal__body-inner--none"],
|
|
56
|
+
!c && o["modal__body-inner--fill"]
|
|
57
|
+
].filter(Boolean).join(" "), S = /* @__PURE__ */ l("div", { className: P, children: w }), $ = [
|
|
57
58
|
o.modal__scroll,
|
|
58
|
-
!
|
|
59
|
-
].filter(Boolean).join(" "),
|
|
60
|
-
return /* @__PURE__ */
|
|
59
|
+
!c && o["modal__scroll--fill"]
|
|
60
|
+
].filter(Boolean).join(" "), k = u != null || f != null || s === "spacer-small" || s === "spacer-large";
|
|
61
|
+
return /* @__PURE__ */ q(
|
|
61
62
|
"div",
|
|
62
63
|
{
|
|
63
|
-
...
|
|
64
|
-
ref:
|
|
64
|
+
...K,
|
|
65
|
+
ref: _,
|
|
65
66
|
tabIndex: -1,
|
|
66
67
|
className: [
|
|
67
68
|
o.modal,
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
69
|
+
O,
|
|
70
|
+
b && o["modal--transition"],
|
|
71
|
+
b && H && o["modal--visible"],
|
|
72
|
+
e
|
|
72
73
|
].filter(Boolean).join(" "),
|
|
73
|
-
style:
|
|
74
|
+
style: n,
|
|
74
75
|
role: "dialog",
|
|
75
76
|
"aria-modal": "true",
|
|
76
|
-
"aria-labelledby":
|
|
77
|
+
"aria-labelledby": p,
|
|
77
78
|
children: [
|
|
78
|
-
/* @__PURE__ */
|
|
79
|
-
|
|
79
|
+
/* @__PURE__ */ l(
|
|
80
|
+
V,
|
|
80
81
|
{
|
|
81
82
|
title: r,
|
|
82
|
-
titleId:
|
|
83
|
-
subtitle:
|
|
84
|
-
subtitlePlacement:
|
|
85
|
-
hideTitle:
|
|
86
|
-
showBackButton:
|
|
87
|
-
onBack:
|
|
83
|
+
titleId: p,
|
|
84
|
+
subtitle: a,
|
|
85
|
+
subtitlePlacement: t,
|
|
86
|
+
hideTitle: v,
|
|
87
|
+
showBackButton: y,
|
|
88
|
+
onBack: h,
|
|
88
89
|
backLabel: j,
|
|
89
|
-
backButtonProps:
|
|
90
|
-
onClose:
|
|
91
|
-
closeLabel:
|
|
92
|
-
closeButtonProps:
|
|
93
|
-
divider:
|
|
94
|
-
headerAction:
|
|
95
|
-
headerSlot:
|
|
90
|
+
backButtonProps: x,
|
|
91
|
+
onClose: d,
|
|
92
|
+
closeLabel: I,
|
|
93
|
+
closeButtonProps: M,
|
|
94
|
+
divider: N,
|
|
95
|
+
headerAction: B,
|
|
96
|
+
headerSlot: F
|
|
96
97
|
}
|
|
97
98
|
),
|
|
98
|
-
/* @__PURE__ */
|
|
99
|
-
|
|
100
|
-
|
|
99
|
+
/* @__PURE__ */ l("div", { className: o.modal__body, children: /* @__PURE__ */ l(Q, { className: $, children: S }) }),
|
|
100
|
+
k && /* @__PURE__ */ l(
|
|
101
|
+
U,
|
|
101
102
|
{
|
|
102
|
-
type:
|
|
103
|
-
divider:
|
|
104
|
-
leading:
|
|
105
|
-
children:
|
|
103
|
+
type: s,
|
|
104
|
+
divider: z,
|
|
105
|
+
leading: f,
|
|
106
|
+
children: u
|
|
106
107
|
}
|
|
107
108
|
)
|
|
108
109
|
]
|
|
109
110
|
}
|
|
110
111
|
);
|
|
111
112
|
}
|
|
112
|
-
function to({ open:
|
|
113
|
-
const r =
|
|
114
|
-
return
|
|
115
|
-
|
|
116
|
-
s.current && !l && r && ((i = e.current) == null || i.call(e)), s.current = l;
|
|
117
|
-
}, [l, r]), r && !l ? null : /* @__PURE__ */ n(
|
|
118
|
-
Z,
|
|
113
|
+
function to({ open: e, onExited: n, ...i }) {
|
|
114
|
+
const r = e !== void 0, { mounted: a, visible: t } = J(e ?? !0);
|
|
115
|
+
return X(a, n, r), r && !a ? null : /* @__PURE__ */ l(
|
|
116
|
+
Y,
|
|
119
117
|
{
|
|
120
|
-
...
|
|
121
|
-
active:
|
|
118
|
+
...i,
|
|
119
|
+
active: e ?? !0,
|
|
122
120
|
transition: r,
|
|
123
|
-
visible:
|
|
121
|
+
visible: t
|
|
124
122
|
}
|
|
125
123
|
);
|
|
126
124
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Modal.js","sources":["../../../src/components/modal/Modal.tsx"],"sourcesContent":["import type {\n CSSProperties,\n HTMLAttributes,\n ReactNode,\n RefObject,\n} from 'react';\nimport { useEffect, useId, useRef } from 'react';\nimport { useModalFocus } from '@/hooks/use-modal-focus';\nimport { usePopoverTransition } from '@/hooks/use-popover-transition';\nimport Scrollbar from '@/components/scrollbar/Scrollbar';\nimport ModalFooter from '@/components/modal-footer/ModalFooter';\nimport type { ModalFooterType } from '@/components/modal-footer/ModalFooter';\nimport ModalHeader from '@/components/modal-header/ModalHeader';\nimport type { ModalSubtitlePlacement } from '@/components/modal-header/ModalHeader';\nimport type { BuiltInButtonProps } from '@/utils/props';\nimport { toKebab } from '@/utils/string';\nimport styles from './Modal.module.scss';\n\nexport type ModalSize = 'small' | 'medium' | 'large';\n\n/**\n * Body inset. `menu` is for MenuItem lists — 8px vertical / 16px horizontal so\n * row labels align with the 32px header/footer margins. `none` removes inset\n * for host-owned full-bleed layouts (e.g. settings sidebars).\n */\nexport type ModalBodyPadding = 'default' | 'menu' | 'none';\n\nexport type { ModalFooterType, ModalSubtitlePlacement };\n\nexport interface ModalProps extends Omit<\n HTMLAttributes<HTMLDivElement>,\n 'title' | 'children' | 'style'\n> {\n /** Extra class on the dialog root (size + chrome classes still apply). */\n className?: string;\n /** Inline style on the dialog root — prefer `className` for layout overrides. */\n style?: CSSProperties;\n /** Width variant. Figma: Size — Small 600px, Medium 704px, Large 832px. */\n size?: ModalSize;\n /** Modal heading text. Required for a11y; use `hideTitle` for close-only chrome. */\n title: ReactNode;\n /** Optional secondary line — placement via `subtitlePlacement`. */\n subtitle?: ReactNode;\n /**\n * Where the subtitle sits. Figma Modal Header: Below | Beside.\n * Default: `below`.\n */\n subtitlePlacement?: ModalSubtitlePlacement;\n /** Visually hide the title (close-only header); title remains for `aria-labelledby`. */\n hideTitle?: boolean;\n /** When true, shows a back-arrow button before the title. */\n showBackButton?: boolean;\n /** Called when the back button is clicked. */\n onBack?: () => void;\n /** Accessible name for the back button. Default: \"Go back\". */\n backLabel?: string;\n /** Extra attributes for the back button (e.g. `data-testid`). */\n backButtonProps?: BuiltInButtonProps;\n /** Called when the × close button is clicked. */\n onClose?: () => void;\n /** Accessible name for the close button. Default: \"Close\". */\n closeLabel?: string;\n /** Extra attributes for the close button (e.g. `data-testid`). */\n closeButtonProps?: BuiltInButtonProps;\n /** Show divider between header and body. Default: true. */\n headerDivider?: boolean;\n /** Optional control before close (Button, search field, …). */\n headerAction?: ReactNode;\n /**\n * Full-width slot rendered below the title row and above the header divider.\n * Accepts any ReactNode — SearchInput, Tabs, a custom toolbar, etc.\n */\n headerSlot?: ReactNode;\n /**\n * Body inset. Default is 32px horizontal / 28px vertical. Use `menu` for\n * MenuItem lists. Use `none` when the host owns padding.\n */\n bodyPadding?: ModalBodyPadding;\n /**\n * When false, the body fills the dialog height so host panes can manage\n * their own scroll regions (settings-style layouts). Default true sizes the\n * body to its content. Overflow always uses Scrollbar (default track width).\n */\n scrollable?: boolean;\n /** Body content. */\n children: ReactNode;\n /** Footer slot — typically Buttons. Wrapped in `ModalFooter`. */\n footer?: ReactNode;\n /** Left footer slot (pagination status, PaginationDots, …). */\n footerLeading?: ReactNode;\n /** Footer layout. Figma Modal Footer Type. Default: `2-actions`. */\n footerType?: ModalFooterType;\n /** Show divider between body and footer. Default: true. */\n footerDivider?: boolean;\n /**\n * Close on Escape via a document listener while mounted. Escapes already\n * handled by an inner widget (`defaultPrevented`) are ignored. Default: true.\n */\n closeOnEscape?: boolean;\n /**\n * Initial focus, Tab trap and focus restore. Set false when the host owns\n * focus (e.g. a floating-ui `FloatingFocusManager`); pair with\n * `closeOnEscape={false}` when the host also handles Escape. Initial focus\n * and restore use the value at mount; the Tab trap follows later updates.\n * Default: true.\n */\n manageFocus?: boolean;\n /**\n * Element to focus when the modal mounts (e.g. the first form field).\n * Default: the dialog root.\n */\n initialFocusRef?: RefObject<HTMLElement | null>;\n /**\n * Controlled visibility with the Compass enter/exit transition (fade + drop from the top + subtle scale 98%→100%,\n * reverse on close, `--duration-quick`); the modal unmounts after the exit,\n * and focus is restored then. Omit to keep the mount-controlled behavior\n * (always rendered, no transition). The host still owns the state.\n */\n open?: boolean;\n /** Called after the exit transition finishes and the modal unmounts (only when `open` is controlled). */\n onExited?: () => void;\n}\n\ninterface ModalPanelState {\n active: boolean;\n transition: boolean;\n visible: boolean;\n}\n\nfunction ModalPanel({\n className = '',\n style,\n size = 'small',\n title,\n subtitle,\n subtitlePlacement = 'below',\n hideTitle = false,\n showBackButton = false,\n onBack,\n backLabel,\n backButtonProps,\n onClose,\n closeLabel,\n closeButtonProps,\n headerDivider = true,\n headerAction,\n headerSlot,\n bodyPadding = 'default',\n scrollable = true,\n children,\n footer,\n footerLeading,\n footerType = '2-actions',\n footerDivider = true,\n closeOnEscape = true,\n manageFocus = true,\n initialFocusRef,\n active,\n transition,\n visible,\n ...rest\n}: ModalProps & ModalPanelState) {\n const rootRef = useRef<HTMLDivElement>(null);\n useModalFocus(rootRef, {\n initialFocusRef,\n closeOnEscape,\n manageFocus,\n onClose,\n active,\n });\n const titleId = useId();\n const sizeClass = styles[`modal--size-${toKebab(size)}`];\n const bodyInnerClass = [\n styles['modal__body-inner'],\n bodyPadding === 'menu' && styles['modal__body-inner--menu'],\n bodyPadding === 'none' && styles['modal__body-inner--none'],\n !scrollable && styles['modal__body-inner--fill'],\n ]\n .filter(Boolean)\n .join(' ');\n\n const bodyInner = <div className={bodyInnerClass}>{children}</div>;\n const scrollClass = [\n styles['modal__scroll'],\n !scrollable && styles['modal__scroll--fill'],\n ]\n .filter(Boolean)\n .join(' ');\n const showFooter =\n footer != null ||\n footerLeading != null ||\n footerType === 'spacer-small' ||\n footerType === 'spacer-large';\n\n return (\n <div\n {...rest}\n ref={rootRef}\n tabIndex={-1}\n className={[\n styles.modal,\n sizeClass,\n transition && styles['modal--transition'],\n transition && visible && styles['modal--visible'],\n className,\n ]\n .filter(Boolean)\n .join(' ')}\n style={style}\n role=\"dialog\"\n aria-modal=\"true\"\n aria-labelledby={titleId}\n >\n <ModalHeader\n title={title}\n titleId={titleId}\n subtitle={subtitle}\n subtitlePlacement={subtitlePlacement}\n hideTitle={hideTitle}\n showBackButton={showBackButton}\n onBack={onBack}\n backLabel={backLabel}\n backButtonProps={backButtonProps}\n onClose={onClose}\n closeLabel={closeLabel}\n closeButtonProps={closeButtonProps}\n divider={headerDivider}\n headerAction={headerAction}\n headerSlot={headerSlot}\n />\n\n <div className={styles['modal__body']}>\n <Scrollbar className={scrollClass}>{bodyInner}</Scrollbar>\n </div>\n\n {showFooter && (\n <ModalFooter\n type={footerType}\n divider={footerDivider}\n leading={footerLeading}\n >\n {footer}\n </ModalFooter>\n )}\n </div>\n );\n}\n\n/**\n * Modal dialog shell — composes ModalHeader, scrollable body, and ModalFooter.\n * Owns dialog-level focus and keyboard behavior: initial focus, Tab trap,\n * Escape, and focus restore on unmount. Host owns portal, overlay/backdrop,\n * scroll lock, stacking, and open state. Mount-controlled by default; pass\n * `open` for the enter/exit transition.\n */\nexport default function Modal({ open, onExited, ...props }: ModalProps) {\n const controlled = open !== undefined;\n const { mounted, visible } = usePopoverTransition(open ?? true);\n const wasMounted = useRef(mounted);\n const onExitedRef = useRef(onExited);\n onExitedRef.current = onExited;\n\n useEffect(() => {\n if (wasMounted.current && !mounted && controlled) {\n onExitedRef.current?.();\n }\n wasMounted.current = mounted;\n }, [mounted, controlled]);\n\n if (controlled && !mounted) return null;\n\n return (\n <ModalPanel\n {...props}\n active={open ?? true}\n transition={controlled}\n visible={visible}\n />\n );\n}\n"],"names":["ModalPanel","className","style","size","title","subtitle","subtitlePlacement","hideTitle","showBackButton","onBack","backLabel","backButtonProps","onClose","closeLabel","closeButtonProps","headerDivider","headerAction","headerSlot","bodyPadding","scrollable","children","footer","footerLeading","footerType","footerDivider","closeOnEscape","manageFocus","initialFocusRef","active","transition","visible","rest","rootRef","useRef","useModalFocus","titleId","useId","sizeClass","styles","toKebab","bodyInnerClass","bodyInner","jsx","scrollClass","showFooter","jsxs","ModalHeader","Scrollbar","ModalFooter","Modal","open","onExited","props","controlled","mounted","usePopoverTransition","wasMounted","onExitedRef","useEffect","_a"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"Modal.js","sources":["../../../src/components/modal/Modal.tsx"],"sourcesContent":["import type {\n CSSProperties,\n HTMLAttributes,\n ReactNode,\n RefObject,\n} from 'react';\nimport { useId, useRef } from 'react';\nimport { useModalFocus } from '@/hooks/use-modal-focus';\nimport { usePopoverTransition } from '@/hooks/use-popover-transition';\nimport Scrollbar from '@/components/scrollbar/Scrollbar';\nimport ModalFooter from '@/components/modal-footer/ModalFooter';\nimport type { ModalFooterType } from '@/components/modal-footer/ModalFooter';\nimport ModalHeader from '@/components/modal-header/ModalHeader';\nimport type { ModalSubtitlePlacement } from '@/components/modal-header/ModalHeader';\nimport type { BuiltInButtonProps } from '@/utils/props';\nimport { toKebab } from '@/utils/string';\nimport { useOnExited } from '@/utils/useOnExited';\nimport styles from './Modal.module.scss';\n\nexport type ModalSize = 'small' | 'medium' | 'large';\n\n/**\n * Body inset. `menu` is for MenuItem lists — 8px vertical / 16px horizontal so\n * row labels align with the 32px header/footer margins. `none` removes inset\n * for host-owned full-bleed layouts (e.g. settings sidebars).\n */\nexport type ModalBodyPadding = 'default' | 'menu' | 'none';\n\nexport type { ModalFooterType, ModalSubtitlePlacement };\n\nexport interface ModalProps extends Omit<\n HTMLAttributes<HTMLDivElement>,\n 'title' | 'children' | 'style'\n> {\n /** Extra class on the dialog root (size + chrome classes still apply). */\n className?: string;\n /** Inline style on the dialog root — prefer `className` for layout overrides. */\n style?: CSSProperties;\n /** Width variant. Figma: Size — Small 600px, Medium 704px, Large 832px. */\n size?: ModalSize;\n /** Modal heading text. Required for a11y; use `hideTitle` for close-only chrome. */\n title: ReactNode;\n /** Optional secondary line — placement via `subtitlePlacement`. */\n subtitle?: ReactNode;\n /**\n * Where the subtitle sits. Figma Modal Header: Below | Beside.\n * Default: `below`.\n */\n subtitlePlacement?: ModalSubtitlePlacement;\n /** Visually hide the title (close-only header); title remains for `aria-labelledby`. */\n hideTitle?: boolean;\n /** When true, shows a back-arrow button before the title. */\n showBackButton?: boolean;\n /** Called when the back button is clicked. */\n onBack?: () => void;\n /** Accessible name for the back button. Default: \"Go back\". */\n backLabel?: string;\n /** Extra attributes for the back button (e.g. `data-testid`). */\n backButtonProps?: BuiltInButtonProps;\n /** Called when the × close button is clicked. */\n onClose?: () => void;\n /** Accessible name for the close button. Default: \"Close\". */\n closeLabel?: string;\n /** Extra attributes for the close button (e.g. `data-testid`). */\n closeButtonProps?: BuiltInButtonProps;\n /** Show divider between header and body. Default: true. */\n headerDivider?: boolean;\n /** Optional control before close (Button, search field, …). */\n headerAction?: ReactNode;\n /**\n * Full-width slot rendered below the title row and above the header divider.\n * Accepts any ReactNode — SearchInput, Tabs, a custom toolbar, etc.\n */\n headerSlot?: ReactNode;\n /**\n * Body inset. Default is 32px horizontal / 28px vertical. Use `menu` for\n * MenuItem lists. Use `none` when the host owns padding.\n */\n bodyPadding?: ModalBodyPadding;\n /**\n * When false, the body fills the dialog height so host panes can manage\n * their own scroll regions (settings-style layouts). Default true sizes the\n * body to its content. Overflow always uses Scrollbar (default track width).\n */\n scrollable?: boolean;\n /** Body content. */\n children: ReactNode;\n /** Footer slot — typically Buttons. Wrapped in `ModalFooter`. */\n footer?: ReactNode;\n /** Left footer slot (pagination status, PaginationDots, …). */\n footerLeading?: ReactNode;\n /** Footer layout. Figma Modal Footer Type. Default: `2-actions`. */\n footerType?: ModalFooterType;\n /** Show divider between body and footer. Default: true. */\n footerDivider?: boolean;\n /**\n * Close on Escape via a document listener while mounted. Escapes already\n * handled by an inner widget (`defaultPrevented`) are ignored. Default: true.\n */\n closeOnEscape?: boolean;\n /**\n * Initial focus, Tab trap and focus restore. Set false when the host owns\n * focus (e.g. a floating-ui `FloatingFocusManager`); pair with\n * `closeOnEscape={false}` when the host also handles Escape. Initial focus\n * and restore use the value at mount; the Tab trap follows later updates.\n * Default: true.\n */\n manageFocus?: boolean;\n /**\n * Element to focus when the modal mounts (e.g. the first form field).\n * Default: the dialog root.\n */\n initialFocusRef?: RefObject<HTMLElement | null>;\n /**\n * Controlled visibility with the Compass enter/exit transition (fade + drop from the top + subtle scale 98%→100%,\n * reverse on close, `--duration-quick`); the modal unmounts after the exit,\n * and focus is restored then. Omit to keep the mount-controlled behavior\n * (always rendered, no transition). The host still owns the state.\n */\n open?: boolean;\n /** Called after the exit transition finishes and the modal unmounts (only when `open` is controlled). */\n onExited?: () => void;\n}\n\ninterface ModalPanelState {\n active: boolean;\n transition: boolean;\n visible: boolean;\n}\n\nfunction ModalPanel({\n className = '',\n style,\n size = 'small',\n title,\n subtitle,\n subtitlePlacement = 'below',\n hideTitle = false,\n showBackButton = false,\n onBack,\n backLabel,\n backButtonProps,\n onClose,\n closeLabel,\n closeButtonProps,\n headerDivider = true,\n headerAction,\n headerSlot,\n bodyPadding = 'default',\n scrollable = true,\n children,\n footer,\n footerLeading,\n footerType = '2-actions',\n footerDivider = true,\n closeOnEscape = true,\n manageFocus = true,\n initialFocusRef,\n active,\n transition,\n visible,\n ...rest\n}: ModalProps & ModalPanelState) {\n const rootRef = useRef<HTMLDivElement>(null);\n useModalFocus(rootRef, {\n initialFocusRef,\n closeOnEscape,\n manageFocus,\n onClose,\n active,\n });\n const titleId = useId();\n const sizeClass = styles[`modal--size-${toKebab(size)}`];\n const bodyInnerClass = [\n styles['modal__body-inner'],\n bodyPadding === 'menu' && styles['modal__body-inner--menu'],\n bodyPadding === 'none' && styles['modal__body-inner--none'],\n !scrollable && styles['modal__body-inner--fill'],\n ]\n .filter(Boolean)\n .join(' ');\n\n const bodyInner = <div className={bodyInnerClass}>{children}</div>;\n const scrollClass = [\n styles['modal__scroll'],\n !scrollable && styles['modal__scroll--fill'],\n ]\n .filter(Boolean)\n .join(' ');\n const showFooter =\n footer != null ||\n footerLeading != null ||\n footerType === 'spacer-small' ||\n footerType === 'spacer-large';\n\n return (\n <div\n {...rest}\n ref={rootRef}\n tabIndex={-1}\n className={[\n styles.modal,\n sizeClass,\n transition && styles['modal--transition'],\n transition && visible && styles['modal--visible'],\n className,\n ]\n .filter(Boolean)\n .join(' ')}\n style={style}\n role=\"dialog\"\n aria-modal=\"true\"\n aria-labelledby={titleId}\n >\n <ModalHeader\n title={title}\n titleId={titleId}\n subtitle={subtitle}\n subtitlePlacement={subtitlePlacement}\n hideTitle={hideTitle}\n showBackButton={showBackButton}\n onBack={onBack}\n backLabel={backLabel}\n backButtonProps={backButtonProps}\n onClose={onClose}\n closeLabel={closeLabel}\n closeButtonProps={closeButtonProps}\n divider={headerDivider}\n headerAction={headerAction}\n headerSlot={headerSlot}\n />\n\n <div className={styles['modal__body']}>\n <Scrollbar className={scrollClass}>{bodyInner}</Scrollbar>\n </div>\n\n {showFooter && (\n <ModalFooter\n type={footerType}\n divider={footerDivider}\n leading={footerLeading}\n >\n {footer}\n </ModalFooter>\n )}\n </div>\n );\n}\n\n/**\n * Modal dialog shell — composes ModalHeader, scrollable body, and ModalFooter.\n * Owns dialog-level focus and keyboard behavior: initial focus, Tab trap,\n * Escape, and focus restore on unmount. Host owns portal, overlay/backdrop,\n * scroll lock, stacking, and open state. Mount-controlled by default; pass\n * `open` for the enter/exit transition.\n */\nexport default function Modal({ open, onExited, ...props }: ModalProps) {\n const controlled = open !== undefined;\n const { mounted, visible } = usePopoverTransition(open ?? true);\n useOnExited(mounted, onExited, controlled);\n\n if (controlled && !mounted) return null;\n\n return (\n <ModalPanel\n {...props}\n active={open ?? true}\n transition={controlled}\n visible={visible}\n />\n );\n}\n"],"names":["ModalPanel","className","style","size","title","subtitle","subtitlePlacement","hideTitle","showBackButton","onBack","backLabel","backButtonProps","onClose","closeLabel","closeButtonProps","headerDivider","headerAction","headerSlot","bodyPadding","scrollable","children","footer","footerLeading","footerType","footerDivider","closeOnEscape","manageFocus","initialFocusRef","active","transition","visible","rest","rootRef","useRef","useModalFocus","titleId","useId","sizeClass","styles","toKebab","bodyInnerClass","bodyInner","jsx","scrollClass","showFooter","jsxs","ModalHeader","Scrollbar","ModalFooter","Modal","open","onExited","props","controlled","mounted","usePopoverTransition","useOnExited"],"mappings":"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"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
"use strict";Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}});const x=require("react/jsx-runtime"),l=require("react"),P=require("react-dom"),_=require("../../hooks/use-popover-transition.cjs"),b=require("../../utils/props.cjs"),h=require("../../utils/scrollLock.cjs"),j=require("../../utils/useOnExited.cjs"),f=require("./ModalOverlay.module.scss.cjs");function E({open:u,onClose:s,onExited:m,container:c,lockScroll:d=!0,zIndex:i,className:v,style:g,children:y,onMouseDown:a,onClick:n,...q}){const{mounted:r}=_.usePopoverTransition(u);j.useOnExited(r,m);const o=l.useRef(!1),t=c===void 0?typeof document>"u"?null:document.body:c;if(l.useEffect(()=>{if(!(!r||!t||!d))return h.lockBodyScroll(t.ownerDocument)},[r,t,d]),!r||!t)return null;const p=e=>{o.current=e.target!==e.currentTarget,a==null||a(e)},O=e=>{const T=o.current;o.current=!1,n==null||n(e),u&&!T&&e.target===e.currentTarget&&(s==null||s())};return P.createPortal(x.jsx("div",{...q,className:b.mergeClassNames(f.default["modal-overlay"],!u&&f.default["modal-overlay--exiting"],v),style:{...i!==void 0&&{zIndex:i},...g},onMouseDown:p,onClick:O,children:y}),t)}exports.default=E;
|
|
2
|
+
//# sourceMappingURL=ModalOverlay.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ModalOverlay.cjs","sources":["../../../src/components/modal/ModalOverlay.tsx"],"sourcesContent":["import {\n useEffect,\n useRef,\n type HTMLAttributes,\n type MouseEvent,\n type ReactNode,\n} from 'react';\nimport { createPortal } from 'react-dom';\nimport { usePopoverTransition } from '@/hooks/use-popover-transition';\nimport { mergeClassNames } from '@/utils/props';\nimport { lockBodyScroll } from '@/utils/scrollLock';\nimport { useOnExited } from '@/utils/useOnExited';\nimport styles from './ModalOverlay.module.scss';\n\nexport interface ModalOverlayProps extends HTMLAttributes<HTMLDivElement> {\n /** Whether the overlay is shown. Setting it to `false` plays the exit and then unmounts. */\n open: boolean;\n /** Called when the backdrop is pressed. Presses that start inside the dialog do not call it. */\n onClose?: () => void;\n /**\n * Called once the exit finishes and the overlay has unmounted. Not called if\n * the overlay itself is unmounted before the exit finishes, so do any\n * cleanup on unmount as well.\n */\n onExited?: () => void;\n /**\n * Portal node. Defaults to `document.body`; pass another element so a modal\n * inside another top layer can join it. `null` renders nothing until the node\n * exists (e.g. a ref held in state). Override `position` through `style`\n * when the container is a positioned stage. Scroll lock applies to that\n * node's document.\n */\n container?: HTMLElement | null;\n /**\n * Lock `document.body` scroll while the overlay is mounted. Turn off for an\n * overlay staged inside a frame (docs, Storybook) so it doesn't freeze the page.\n */\n lockScroll?: boolean;\n /**\n * Stacking order. Defaults to `--z-modal` (200); a host whose chrome sits\n * higher passes this or redefines `--z-modal`. Applied before `style`, so\n * `style.zIndex` wins.\n */\n zIndex?: number;\n /** Usually a `Modal`. */\n children?: ReactNode;\n}\n\n/**\n * Modal scrim: portals to `document.body` (or `container`), locks body scroll\n * while shown (opt out with `lockScroll={false}`), closes on backdrop press, and stays mounted for\n * `--duration-quick` after `open` becomes false so the fade-out can play.\n * Escape and focus stay on `Modal`. Hosts that already position their own\n * node can use `modalOverlayStyles` instead.\n */\nexport default function ModalOverlay({\n open,\n onClose,\n onExited,\n container,\n lockScroll = true,\n zIndex,\n className,\n style,\n children,\n onMouseDown,\n onClick,\n ...rest\n}: ModalOverlayProps) {\n const { mounted } = usePopoverTransition(open);\n useOnExited(mounted, onExited);\n const pressStartedInside = useRef(false);\n\n const target =\n container === undefined\n ? typeof document === 'undefined'\n ? null\n : document.body\n : container;\n\n useEffect(() => {\n if (!mounted || !target || !lockScroll) return undefined;\n return lockBodyScroll(target.ownerDocument);\n }, [mounted, target, lockScroll]);\n\n if (!mounted || !target) return null;\n\n const handleMouseDown = (e: MouseEvent<HTMLDivElement>) => {\n pressStartedInside.current = e.target !== e.currentTarget;\n onMouseDown?.(e);\n };\n\n const handleClick = (e: MouseEvent<HTMLDivElement>) => {\n const startedInside = pressStartedInside.current;\n pressStartedInside.current = false;\n onClick?.(e);\n if (open && !startedInside && e.target === e.currentTarget) onClose?.();\n };\n\n return createPortal(\n // eslint-disable-next-line jsx-a11y/click-events-have-key-events, jsx-a11y/no-static-element-interactions -- backdrop click is a pointer shortcut; keyboard users close with Escape (handled by Modal)\n <div\n {...rest}\n className={mergeClassNames(\n styles['modal-overlay'],\n !open && styles['modal-overlay--exiting'],\n className,\n )}\n style={{ ...(zIndex !== undefined && { zIndex }), ...style }}\n onMouseDown={handleMouseDown}\n onClick={handleClick}\n >\n {children}\n </div>,\n target,\n );\n}\n"],"names":["ModalOverlay","open","onClose","onExited","container","lockScroll","zIndex","className","style","children","onMouseDown","onClick","rest","mounted","usePopoverTransition","useOnExited","pressStartedInside","useRef","target","useEffect","lockBodyScroll","handleMouseDown","handleClick","startedInside","createPortal","jsx","mergeClassNames","styles"],"mappings":"6YAuDA,SAAwBA,EAAa,CACnC,KAAAC,EACA,QAAAC,EACA,SAAAC,EACA,UAAAC,EACA,WAAAC,EAAa,GACb,OAAAC,EACA,UAAAC,EACA,MAAAC,EACA,SAAAC,EACA,YAAAC,EACA,QAAAC,EACA,GAAGC,CACL,EAAsB,CACpB,KAAM,CAAE,QAAAC,CAAA,EAAYC,EAAAA,qBAAqBb,CAAI,EAC7Cc,EAAAA,YAAYF,EAASV,CAAQ,EAC7B,MAAMa,EAAqBC,EAAAA,OAAO,EAAK,EAEjCC,EACJd,IAAc,OACV,OAAO,SAAa,IAClB,KACA,SAAS,KACXA,EAON,GALAe,EAAAA,UAAU,IAAM,CACd,GAAI,GAACN,GAAW,CAACK,GAAU,CAACb,GAC5B,OAAOe,EAAAA,eAAeF,EAAO,aAAa,CAC5C,EAAG,CAACL,EAASK,EAAQb,CAAU,CAAC,EAE5B,CAACQ,GAAW,CAACK,EAAQ,OAAO,KAEhC,MAAMG,EAAmB,GAAkC,CACzDL,EAAmB,QAAU,EAAE,SAAW,EAAE,cAC5CN,GAAA,MAAAA,EAAc,EAChB,EAEMY,EAAe,GAAkC,CACrD,MAAMC,EAAgBP,EAAmB,QACzCA,EAAmB,QAAU,GAC7BL,GAAA,MAAAA,EAAU,GACNV,GAAQ,CAACsB,GAAiB,EAAE,SAAW,EAAE,gBAAerB,GAAA,MAAAA,IAC9D,EAEA,OAAOsB,EAAAA,aAELC,EAAAA,IAAC,MAAA,CACE,GAAGb,EACJ,UAAWc,EAAAA,gBACTC,EAAAA,QAAO,eAAe,EACtB,CAAC1B,GAAQ0B,EAAAA,QAAO,wBAAwB,EACxCpB,CAAA,EAEF,MAAO,CAAE,GAAID,IAAW,QAAa,CAAE,OAAAA,CAAA,EAAW,GAAGE,CAAA,EACrD,YAAaa,EACb,QAASC,EAER,SAAAb,CAAA,CAAA,EAEHS,CAAA,CAEJ"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
@keyframes _modal-overlay-fade-
|
|
1
|
+
@keyframes _modal-overlay-fade-in_9al07_1{0%{opacity:0}}._modal-overlay_9al07_7{position:fixed;top:0;right:0;bottom:0;left:0;display:flex;align-items:center;justify-content:center;padding:var(--spacing-xl) var(--spacing-xl) calc(var(--spacing-xl) + 2 * var(--spacing-xxl));z-index:var(--z-modal);background-color:var(--modal-overlay-bg);animation:_modal-overlay-fade-in_9al07_1 var(--duration-quick) var(--ease-entrance);transition:opacity var(--duration-quick) var(--ease-exit)}._modal-overlay--exiting_9al07_19{opacity:0;pointer-events:none}@media(prefers-reduced-motion:reduce){._modal-overlay_9al07_7{animation:none;transition:none}}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { HTMLAttributes, ReactNode } from 'react';
|
|
2
|
+
export interface ModalOverlayProps extends HTMLAttributes<HTMLDivElement> {
|
|
3
|
+
/** Whether the overlay is shown. Setting it to `false` plays the exit and then unmounts. */
|
|
4
|
+
open: boolean;
|
|
5
|
+
/** Called when the backdrop is pressed. Presses that start inside the dialog do not call it. */
|
|
6
|
+
onClose?: () => void;
|
|
7
|
+
/**
|
|
8
|
+
* Called once the exit finishes and the overlay has unmounted. Not called if
|
|
9
|
+
* the overlay itself is unmounted before the exit finishes, so do any
|
|
10
|
+
* cleanup on unmount as well.
|
|
11
|
+
*/
|
|
12
|
+
onExited?: () => void;
|
|
13
|
+
/**
|
|
14
|
+
* Portal node. Defaults to `document.body`; pass another element so a modal
|
|
15
|
+
* inside another top layer can join it. `null` renders nothing until the node
|
|
16
|
+
* exists (e.g. a ref held in state). Override `position` through `style`
|
|
17
|
+
* when the container is a positioned stage. Scroll lock applies to that
|
|
18
|
+
* node's document.
|
|
19
|
+
*/
|
|
20
|
+
container?: HTMLElement | null;
|
|
21
|
+
/**
|
|
22
|
+
* Lock `document.body` scroll while the overlay is mounted. Turn off for an
|
|
23
|
+
* overlay staged inside a frame (docs, Storybook) so it doesn't freeze the page.
|
|
24
|
+
*/
|
|
25
|
+
lockScroll?: boolean;
|
|
26
|
+
/**
|
|
27
|
+
* Stacking order. Defaults to `--z-modal` (200); a host whose chrome sits
|
|
28
|
+
* higher passes this or redefines `--z-modal`. Applied before `style`, so
|
|
29
|
+
* `style.zIndex` wins.
|
|
30
|
+
*/
|
|
31
|
+
zIndex?: number;
|
|
32
|
+
/** Usually a `Modal`. */
|
|
33
|
+
children?: ReactNode;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Modal scrim: portals to `document.body` (or `container`), locks body scroll
|
|
37
|
+
* while shown (opt out with `lockScroll={false}`), closes on backdrop press, and stays mounted for
|
|
38
|
+
* `--duration-quick` after `open` becomes false so the fade-out can play.
|
|
39
|
+
* Escape and focus stay on `Modal`. Hosts that already position their own
|
|
40
|
+
* node can use `modalOverlayStyles` instead.
|
|
41
|
+
*/
|
|
42
|
+
export default function ModalOverlay({ open, onClose, onExited, container, lockScroll, zIndex, className, style, children, onMouseDown, onClick, ...rest }: ModalOverlayProps): import('react').ReactPortal | null;
|
|
43
|
+
//# sourceMappingURL=ModalOverlay.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ModalOverlay.d.ts","sourceRoot":"","sources":["../../../src/components/modal/ModalOverlay.tsx"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,cAAc,EAEnB,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AAQf,MAAM,WAAW,iBAAkB,SAAQ,cAAc,CAAC,cAAc,CAAC;IACvE,4FAA4F;IAC5F,IAAI,EAAE,OAAO,CAAC;IACd,gGAAgG;IAChG,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAC;IACtB;;;;;;OAMG;IACH,SAAS,CAAC,EAAE,WAAW,GAAG,IAAI,CAAC;IAC/B;;;OAGG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB;;;;OAIG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,yBAAyB;IACzB,QAAQ,CAAC,EAAE,SAAS,CAAC;CACtB;AAED;;;;;;GAMG;AACH,MAAM,CAAC,OAAO,UAAU,YAAY,CAAC,EACnC,IAAI,EACJ,OAAO,EACP,QAAQ,EACR,SAAS,EACT,UAAiB,EACjB,MAAM,EACN,SAAS,EACT,KAAK,EACL,QAAQ,EACR,WAAW,EACX,OAAO,EACP,GAAG,IAAI,EACR,EAAE,iBAAiB,sCAgDnB"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { jsx as h } from "react/jsx-runtime";
|
|
2
|
+
import { useRef as E, useEffect as N } from "react";
|
|
3
|
+
import { createPortal as O } from "react-dom";
|
|
4
|
+
import { usePopoverTransition as P } from "../../hooks/use-popover-transition.js";
|
|
5
|
+
import { mergeClassNames as b } from "../../utils/props.js";
|
|
6
|
+
import { lockBodyScroll as j } from "../../utils/scrollLock.js";
|
|
7
|
+
import { useOnExited as B } from "../../utils/useOnExited.js";
|
|
8
|
+
import n from "./ModalOverlay.module.scss.js";
|
|
9
|
+
function J({
|
|
10
|
+
open: a,
|
|
11
|
+
onClose: m,
|
|
12
|
+
onExited: c,
|
|
13
|
+
container: i,
|
|
14
|
+
lockScroll: u = !0,
|
|
15
|
+
zIndex: f,
|
|
16
|
+
className: l,
|
|
17
|
+
style: p,
|
|
18
|
+
children: v,
|
|
19
|
+
onMouseDown: o,
|
|
20
|
+
onClick: s,
|
|
21
|
+
...y
|
|
22
|
+
}) {
|
|
23
|
+
const { mounted: e } = P(a);
|
|
24
|
+
B(e, c);
|
|
25
|
+
const d = E(!1), t = i === void 0 ? typeof document > "u" ? null : document.body : i;
|
|
26
|
+
if (N(() => {
|
|
27
|
+
if (!(!e || !t || !u))
|
|
28
|
+
return j(t.ownerDocument);
|
|
29
|
+
}, [e, t, u]), !e || !t) return null;
|
|
30
|
+
const g = (r) => {
|
|
31
|
+
d.current = r.target !== r.currentTarget, o == null || o(r);
|
|
32
|
+
}, x = (r) => {
|
|
33
|
+
const T = d.current;
|
|
34
|
+
d.current = !1, s == null || s(r), a && !T && r.target === r.currentTarget && (m == null || m());
|
|
35
|
+
};
|
|
36
|
+
return O(
|
|
37
|
+
// eslint-disable-next-line jsx-a11y/click-events-have-key-events, jsx-a11y/no-static-element-interactions -- backdrop click is a pointer shortcut; keyboard users close with Escape (handled by Modal)
|
|
38
|
+
/* @__PURE__ */ h(
|
|
39
|
+
"div",
|
|
40
|
+
{
|
|
41
|
+
...y,
|
|
42
|
+
className: b(
|
|
43
|
+
n["modal-overlay"],
|
|
44
|
+
!a && n["modal-overlay--exiting"],
|
|
45
|
+
l
|
|
46
|
+
),
|
|
47
|
+
style: { ...f !== void 0 && { zIndex: f }, ...p },
|
|
48
|
+
onMouseDown: g,
|
|
49
|
+
onClick: x,
|
|
50
|
+
children: v
|
|
51
|
+
}
|
|
52
|
+
),
|
|
53
|
+
t
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
export {
|
|
57
|
+
J as default
|
|
58
|
+
};
|
|
59
|
+
//# sourceMappingURL=ModalOverlay.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ModalOverlay.js","sources":["../../../src/components/modal/ModalOverlay.tsx"],"sourcesContent":["import {\n useEffect,\n useRef,\n type HTMLAttributes,\n type MouseEvent,\n type ReactNode,\n} from 'react';\nimport { createPortal } from 'react-dom';\nimport { usePopoverTransition } from '@/hooks/use-popover-transition';\nimport { mergeClassNames } from '@/utils/props';\nimport { lockBodyScroll } from '@/utils/scrollLock';\nimport { useOnExited } from '@/utils/useOnExited';\nimport styles from './ModalOverlay.module.scss';\n\nexport interface ModalOverlayProps extends HTMLAttributes<HTMLDivElement> {\n /** Whether the overlay is shown. Setting it to `false` plays the exit and then unmounts. */\n open: boolean;\n /** Called when the backdrop is pressed. Presses that start inside the dialog do not call it. */\n onClose?: () => void;\n /**\n * Called once the exit finishes and the overlay has unmounted. Not called if\n * the overlay itself is unmounted before the exit finishes, so do any\n * cleanup on unmount as well.\n */\n onExited?: () => void;\n /**\n * Portal node. Defaults to `document.body`; pass another element so a modal\n * inside another top layer can join it. `null` renders nothing until the node\n * exists (e.g. a ref held in state). Override `position` through `style`\n * when the container is a positioned stage. Scroll lock applies to that\n * node's document.\n */\n container?: HTMLElement | null;\n /**\n * Lock `document.body` scroll while the overlay is mounted. Turn off for an\n * overlay staged inside a frame (docs, Storybook) so it doesn't freeze the page.\n */\n lockScroll?: boolean;\n /**\n * Stacking order. Defaults to `--z-modal` (200); a host whose chrome sits\n * higher passes this or redefines `--z-modal`. Applied before `style`, so\n * `style.zIndex` wins.\n */\n zIndex?: number;\n /** Usually a `Modal`. */\n children?: ReactNode;\n}\n\n/**\n * Modal scrim: portals to `document.body` (or `container`), locks body scroll\n * while shown (opt out with `lockScroll={false}`), closes on backdrop press, and stays mounted for\n * `--duration-quick` after `open` becomes false so the fade-out can play.\n * Escape and focus stay on `Modal`. Hosts that already position their own\n * node can use `modalOverlayStyles` instead.\n */\nexport default function ModalOverlay({\n open,\n onClose,\n onExited,\n container,\n lockScroll = true,\n zIndex,\n className,\n style,\n children,\n onMouseDown,\n onClick,\n ...rest\n}: ModalOverlayProps) {\n const { mounted } = usePopoverTransition(open);\n useOnExited(mounted, onExited);\n const pressStartedInside = useRef(false);\n\n const target =\n container === undefined\n ? typeof document === 'undefined'\n ? null\n : document.body\n : container;\n\n useEffect(() => {\n if (!mounted || !target || !lockScroll) return undefined;\n return lockBodyScroll(target.ownerDocument);\n }, [mounted, target, lockScroll]);\n\n if (!mounted || !target) return null;\n\n const handleMouseDown = (e: MouseEvent<HTMLDivElement>) => {\n pressStartedInside.current = e.target !== e.currentTarget;\n onMouseDown?.(e);\n };\n\n const handleClick = (e: MouseEvent<HTMLDivElement>) => {\n const startedInside = pressStartedInside.current;\n pressStartedInside.current = false;\n onClick?.(e);\n if (open && !startedInside && e.target === e.currentTarget) onClose?.();\n };\n\n return createPortal(\n // eslint-disable-next-line jsx-a11y/click-events-have-key-events, jsx-a11y/no-static-element-interactions -- backdrop click is a pointer shortcut; keyboard users close with Escape (handled by Modal)\n <div\n {...rest}\n className={mergeClassNames(\n styles['modal-overlay'],\n !open && styles['modal-overlay--exiting'],\n className,\n )}\n style={{ ...(zIndex !== undefined && { zIndex }), ...style }}\n onMouseDown={handleMouseDown}\n onClick={handleClick}\n >\n {children}\n </div>,\n target,\n );\n}\n"],"names":["ModalOverlay","open","onClose","onExited","container","lockScroll","zIndex","className","style","children","onMouseDown","onClick","rest","mounted","usePopoverTransition","useOnExited","pressStartedInside","useRef","target","useEffect","lockBodyScroll","handleMouseDown","e","handleClick","startedInside","createPortal","jsx","mergeClassNames","styles"],"mappings":";;;;;;;;AAuDA,SAAwBA,EAAa;AAAA,EACnC,MAAAC;AAAA,EACA,SAAAC;AAAA,EACA,UAAAC;AAAA,EACA,WAAAC;AAAA,EACA,YAAAC,IAAa;AAAA,EACb,QAAAC;AAAA,EACA,WAAAC;AAAA,EACA,OAAAC;AAAA,EACA,UAAAC;AAAA,EACA,aAAAC;AAAA,EACA,SAAAC;AAAA,EACA,GAAGC;AACL,GAAsB;AACpB,QAAM,EAAE,SAAAC,EAAA,IAAYC,EAAqBb,CAAI;AAC7C,EAAAc,EAAYF,GAASV,CAAQ;AAC7B,QAAMa,IAAqBC,EAAO,EAAK,GAEjCC,IACJd,MAAc,SACV,OAAO,WAAa,MAClB,OACA,SAAS,OACXA;AAON,MALAe,EAAU,MAAM;AACd,QAAI,GAACN,KAAW,CAACK,KAAU,CAACb;AAC5B,aAAOe,EAAeF,EAAO,aAAa;AAAA,EAC5C,GAAG,CAACL,GAASK,GAAQb,CAAU,CAAC,GAE5B,CAACQ,KAAW,CAACK,EAAQ,QAAO;AAEhC,QAAMG,IAAkB,CAACC,MAAkC;AACzD,IAAAN,EAAmB,UAAUM,EAAE,WAAWA,EAAE,eAC5CZ,KAAA,QAAAA,EAAcY;AAAA,EAChB,GAEMC,IAAc,CAACD,MAAkC;AACrD,UAAME,IAAgBR,EAAmB;AACzC,IAAAA,EAAmB,UAAU,IAC7BL,KAAA,QAAAA,EAAUW,IACNrB,KAAQ,CAACuB,KAAiBF,EAAE,WAAWA,EAAE,kBAAepB,KAAA,QAAAA;AAAA,EAC9D;AAEA,SAAOuB;AAAA;AAAA,IAEL,gBAAAC;AAAA,MAAC;AAAA,MAAA;AAAA,QACE,GAAGd;AAAA,QACJ,WAAWe;AAAA,UACTC,EAAO,eAAe;AAAA,UACtB,CAAC3B,KAAQ2B,EAAO,wBAAwB;AAAA,UACxCrB;AAAA,QAAA;AAAA,QAEF,OAAO,EAAE,GAAID,MAAW,UAAa,EAAE,QAAAA,EAAA,GAAW,GAAGE,EAAA;AAAA,QACrD,aAAaa;AAAA,QACb,SAASE;AAAA,QAER,UAAAd;AAAA,MAAA;AAAA,IAAA;AAAA,IAEHS;AAAA,EAAA;AAEJ;"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}});require('./ModalOverlay.css');const e={"modal-overlay":"_modal-
|
|
1
|
+
"use strict";Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}});require('./ModalOverlay.css');const e={"modal-overlay":"_modal-overlay_9al07_7","modal-overlay-fade-in":"_modal-overlay-fade-in_9al07_1","modal-overlay--exiting":"_modal-overlay--exiting_9al07_19"};exports.default=e;
|
|
2
2
|
//# sourceMappingURL=ModalOverlay.module.scss.cjs.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import './ModalOverlay.css';const a = {
|
|
2
|
-
"modal-overlay": "_modal-
|
|
3
|
-
"modal-overlay-fade-in": "_modal-overlay-fade-
|
|
4
|
-
"modal-overlay--exiting": "_modal-overlay--
|
|
2
|
+
"modal-overlay": "_modal-overlay_9al07_7",
|
|
3
|
+
"modal-overlay-fade-in": "_modal-overlay-fade-in_9al07_1",
|
|
4
|
+
"modal-overlay--exiting": "_modal-overlay--exiting_9al07_19"
|
|
5
5
|
};
|
|
6
6
|
export {
|
|
7
7
|
a as default
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("./Modal.cjs"),l=require("./ModalOverlay.module.scss.cjs");exports.Modal=e.default;exports.
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("./Modal.cjs"),l=require("./ModalOverlay.cjs"),a=require("./ModalOverlay.module.scss.cjs");exports.Modal=e.default;exports.ModalOverlay=l.default;exports.modalOverlayStyles=a.default;
|
|
2
2
|
//# sourceMappingURL=index.cjs.map
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
export { default as Modal } from './Modal';
|
|
2
|
+
export { default as ModalOverlay } from './ModalOverlay';
|
|
2
3
|
export { default as modalOverlayStyles } from './ModalOverlay.module.scss';
|
|
4
|
+
export type { ModalOverlayProps } from './ModalOverlay';
|
|
3
5
|
export type { ModalBodyPadding, ModalFooterType, ModalProps, ModalSize, ModalSubtitlePlacement, } from './Modal';
|
|
4
6
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/modal/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,IAAI,KAAK,EAAE,MAAM,SAAS,CAAC;AAC3C,OAAO,EAAE,OAAO,IAAI,kBAAkB,EAAE,MAAM,4BAA4B,CAAC;AAC3E,YAAY,EACV,gBAAgB,EAChB,eAAe,EACf,UAAU,EACV,SAAS,EACT,sBAAsB,GACvB,MAAM,SAAS,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/modal/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,IAAI,KAAK,EAAE,MAAM,SAAS,CAAC;AAC3C,OAAO,EAAE,OAAO,IAAI,YAAY,EAAE,MAAM,gBAAgB,CAAC;AACzD,OAAO,EAAE,OAAO,IAAI,kBAAkB,EAAE,MAAM,4BAA4B,CAAC;AAC3E,YAAY,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AACxD,YAAY,EACV,gBAAgB,EAChB,eAAe,EACf,UAAU,EACV,SAAS,EACT,sBAAsB,GACvB,MAAM,SAAS,CAAC"}
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { default as l } from "./Modal.js";
|
|
2
|
-
import { default as r } from "./ModalOverlay.
|
|
2
|
+
import { default as r } from "./ModalOverlay.js";
|
|
3
|
+
import { default as d } from "./ModalOverlay.module.scss.js";
|
|
3
4
|
export {
|
|
4
5
|
l as Modal,
|
|
5
|
-
r as
|
|
6
|
+
r as ModalOverlay,
|
|
7
|
+
d as modalOverlayStyles
|
|
6
8
|
};
|
|
7
9
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
._password-
|
|
1
|
+
._password-input_xl3br_2{display:flex;flex-direction:column;gap:var(--spacing-xs);width:100%}._password-input__wrapper_xl3br_8{position:relative;display:flex;flex-direction:column;min-height:0;background-color:var(--center-channel-bg);border-radius:var(--radius-s);box-shadow:inset 0 0 0 1px rgba(var(--center-channel-color-rgb),.16);transition:box-shadow var(--duration-quick) var(--ease-transition)}._password-input_xl3br_2:has(._password-input__input_xl3br_18:not(:disabled)):hover:not(:focus-within) ._password-input__wrapper_xl3br_8{box-shadow:inset 0 0 0 1px rgba(var(--center-channel-color-rgb),.24)}._password-input_xl3br_2:has(._password-input__input_xl3br_18:focus-visible) ._password-input__wrapper_xl3br_8{box-shadow:inset 0 0 0 1px var(--focus-ring-color),0 0 0 1px var(--focus-ring-color)}._password-input_xl3br_2:has(._password-input__input_xl3br_18:disabled) ._password-input__wrapper_xl3br_8{background-color:var(--center-channel-bg);opacity:.56}._password-input__label_xl3br_28{position:absolute;left:var(--spacing-xs);top:50%;padding:0 var(--spacing-xxxs);transform:translateY(-50%);margin:0;font-family:Open Sans,sans-serif;font-size:var(--font-size-75);line-height:var(--line-height-25);font-weight:var(--font-weight-regular);color:rgba(var(--center-channel-color-rgb),.72);background-color:var(--center-channel-bg);pointer-events:none;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:calc(100% - 24px);transition:transform var(--duration-quick) var(--ease-transition),font-size var(--duration-quick) var(--ease-transition),color var(--duration-quick) var(--ease-transition),top var(--duration-quick) var(--ease-transition),left var(--duration-quick) var(--ease-transition)}._password-input--label-floated_xl3br_48 ._password-input__label_xl3br_28{top:0;transform:translateY(-50%);font-size:var(--font-size-25);line-height:14px;color:rgba(var(--center-channel-color-rgb),.72)}._password-input__inner_xl3br_55{display:flex;align-items:center;flex:1;min-width:0}._password-input__input_xl3br_18{flex:1 1 0;min-width:0;margin:0;padding:0;border:0;background:transparent;font-family:Open Sans,sans-serif;font-size:var(--font-size-75);line-height:var(--line-height-25);font-weight:var(--font-weight-regular);color:var(--center-channel-color)}._password-input__input_xl3br_18:focus{outline:none}._password-input__input_xl3br_18::placeholder{color:rgba(var(--center-channel-color-rgb),.64)}._password-input__input_xl3br_18:disabled{cursor:not-allowed}._password-input__trailing-icon_xl3br_83{display:inline-flex;align-items:center;justify-content:center;flex-shrink:0;margin-left:var(--spacing-xs);color:rgba(var(--center-channel-color-rgb),.64)}._password-input__reveal_xl3br_91{display:inline-flex;align-items:center;justify-content:center;margin:0;padding:0;border:0;border-radius:var(--radius-xs);background:transparent;cursor:pointer;color:inherit;outline:2px solid color-mix(in srgb,var(--focus-ring-color) 0%,transparent);outline-offset:0;transition:color var(--duration-quick) var(--ease-transition),outline-color var(--duration-quick) var(--ease-transition),outline-offset var(--duration-quick) var(--ease-transition)}._password-input__reveal_xl3br_91:hover:not(:disabled){color:var(--center-channel-color)}._password-input__reveal_xl3br_91:focus-visible{outline-color:var(--focus-ring-color);outline-offset:3px}._password-input__reveal_xl3br_91:disabled{cursor:not-allowed}._password-input--size-small_xl3br_116 ._password-input__wrapper_xl3br_8{min-height:32px}._password-input--size-small_xl3br_116 ._password-input__inner_xl3br_55{padding:var(--spacing-xs) var(--spacing-m)}._password-input--size-small_xl3br_116 ._password-input__input_xl3br_18{font-size:var(--font-size-75);line-height:var(--line-height-25)}._password-input--size-small_xl3br_116._password-input--label-floated_xl3br_48 ._password-input__label_xl3br_28{top:0;transform:translateY(-50%);font-size:var(--font-size-25);line-height:14px}._password-input--size-medium_xl3br_132 ._password-input__wrapper_xl3br_8{min-height:40px}._password-input--size-medium_xl3br_132 ._password-input__inner_xl3br_55{padding:var(--spacing-s) var(--spacing-l)}._password-input--size-medium_xl3br_132 ._password-input__input_xl3br_18{font-size:var(--font-size-100);line-height:var(--line-height-100)}._password-input--size-medium_xl3br_132 ._password-input__label_xl3br_28{font-size:var(--font-size-100);left:var(--spacing-m)}._password-input--size-medium_xl3br_132._password-input--label-floated_xl3br_48 ._password-input__label_xl3br_28{top:0;transform:translateY(-50%);font-size:var(--font-size-25);line-height:14px}._password-input--size-large_xl3br_152 ._password-input__wrapper_xl3br_8{min-height:48px}._password-input--size-large_xl3br_152 ._password-input__inner_xl3br_55{padding:var(--spacing-m) var(--spacing-l)}._password-input--size-large_xl3br_152 ._password-input__input_xl3br_18{font-size:var(--font-size-200);line-height:var(--line-height-200)}._password-input--size-large_xl3br_152 ._password-input__label_xl3br_28{font-size:var(--font-size-200);line-height:var(--line-height-200);left:var(--spacing-m)}._password-input--size-large_xl3br_152._password-input--label-floated_xl3br_48 ._password-input__label_xl3br_28{top:0;transform:translateY(-50%);font-size:var(--font-size-25);line-height:14px}._password-input_xl3br_2:has(._password-input__input_xl3br_18:focus-visible) ._password-input__label_xl3br_28{color:var(--button-bg)}._password-input_xl3br_2:not(._password-input--label-floated_xl3br_48) ._password-input__label_xl3br_28~* ._password-input__input_xl3br_18::placeholder{color:transparent}._password-input--invalid_xl3br_179 ._password-input__wrapper_xl3br_8{box-shadow:inset 0 0 0 1px var(--error-text, var(--color-danger))}._password-input--invalid_xl3br_179:has(._password-input__input_xl3br_18:focus-visible) ._password-input__wrapper_xl3br_8{box-shadow:inset 0 0 0 1px var(--error-text, var(--color-danger)),0 0 0 1px var(--error-text, var(--color-danger))}._password-input--invalid_xl3br_179 ._password-input__label_xl3br_28{color:var(--error-text, var(--color-danger))}._password-input--invalid_xl3br_179:has(._password-input__input_xl3br_18:focus-visible) ._password-input__label_xl3br_28{color:var(--error-text, var(--color-danger))}
|