UNPKG

@kiwicom/orbit-components

Version:

Orbit-components is a React component library which provides developers with the easiest possible way of building Kiwi.com's products.

638 lines (632 loc) 33.3 kB
"use strict"; "use client"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault").default; var _interopRequireWildcard = require("@babel/runtime/helpers/interopRequireWildcard").default; exports.__esModule = true; exports.default = exports.ModalSection = exports.ModalHeader = exports.ModalFooter = void 0; var React = _interopRequireWildcard(require("react")); var _clsx = _interopRequireDefault(require("clsx")); var _ModalContext = require("./ModalContext"); var _ModalCloseButton = _interopRequireDefault(require("./ModalCloseButton")); var _consts = require("./consts"); var _useRandomId = _interopRequireDefault(require("../hooks/useRandomId")); var _useMediaQuery = _interopRequireDefault(require("../hooks/useMediaQuery")); var _consts2 = _interopRequireDefault(require("../hooks/useFocusTrap/consts")); var _usePrevious = _interopRequireDefault(require("../hooks/usePrevious")); var _useLockScrolling = _interopRequireDefault(require("../hooks/useLockScrolling")); var _useTheme = _interopRequireDefault(require("../hooks/useTheme")); var _ModalHeader = _interopRequireDefault(require("./ModalHeader")); exports.ModalHeader = _ModalHeader.default; var _ModalSection = _interopRequireDefault(require("./ModalSection")); exports.ModalSection = _ModalSection.default; var _ModalFooter = _interopRequireDefault(require("./ModalFooter")); exports.ModalFooter = _ModalFooter.default; const maxWidthClasses = { [_consts.SIZES.EXTRASMALL]: "max-w-modal-extra-small", [_consts.SIZES.SMALL]: "max-w-modal-small", [_consts.SIZES.NORMAL]: "max-w-modal-normal", [_consts.SIZES.LARGE]: "max-w-modal-large", [_consts.SIZES.EXTRALARGE]: "max-w-modal-extra-large", largeMobile: { [_consts.SIZES.EXTRASMALL]: "lm:max-w-modal-extra-small", [_consts.SIZES.SMALL]: "lm:max-w-modal-small", [_consts.SIZES.NORMAL]: "lm:max-w-modal-normal", [_consts.SIZES.LARGE]: "lm:max-w-modal-large", [_consts.SIZES.EXTRALARGE]: "lm:max-w-modal-extra-large" }, footer: { [_consts.SIZES.EXTRASMALL]: "lm:[&_.orbit-modal-footer]:max-w-modal-extra-small", [_consts.SIZES.SMALL]: "lm:[&_.orbit-modal-footer]:max-w-modal-small", [_consts.SIZES.NORMAL]: "lm:[&_.orbit-modal-footer]:max-w-modal-normal", [_consts.SIZES.LARGE]: "lm:[&_.orbit-modal-footer]:max-w-modal-large", [_consts.SIZES.EXTRALARGE]: "lm:[&_.orbit-modal-footer]:max-w-modal-extra-large" } }; const OFFSET = 40; /** * @orbit-doc-start * README * ---------- * # Modal * * To implement Modal component into your project you'll need to the import at least the Modal and the [ModalSection](#modalsection): * * ```jsx * import Modal, { ModalSection } from "@kiwicom/orbit-components/lib/Modal"; * ``` * * > You might need the Portal also. See it's [docs](https://orbit.kiwi/utilities/portal/). * * After adding import into your project you can use it simply like: * * ```jsx * <Modal> * <ModalSection>Hello World!</ModalSection> * </Modal> * ``` * * The Modal component has big variety of usage, please check examples for usage [below](#use-cases). * * ## Props * * Table below contains all types of the props available in the Modal component. * * | Name | Type | Default | Description | * | :------------------ | :------------------------- | :--------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | * | children | `React.Node` | | The content of the Modal. [See Subcomponents](#subcomponents) | * | triggerRef | `React.RefObject` | | The ref to the element which triggers the Modal. | * | lockScrolling | `boolean` | `true` | Whether to prevent scrolling of the rest of the page while Modal is open. This is on by default to provide a better user experience. | * | scrollingElementRef | ref (object or function) | | The scrolling element, which depends on the viewport. | * | dataTest | `string` | | Optional prop for testing purposes. | * | id | `string` | | Set `id` for `Modal`. | * | fixedFooter | `boolean` | `false` | If `true` the ModalFooter will be fixed to the bottom of window. | * | isMobileFullPage | `boolean` | `false` | If `true` the Modal will look like a page on mobile devices. | * | size | [`enum`](#modal-enum) | `"normal"` | The maximum width of the Modal on desktop viewport. | * | onClose | `event => void \| Promise` | | Function for handling onClose event. If you don't pass any function the Close button will not be displayed and it will not be possible to close the Modal. [See Functional specs](#functional-specs). | * | preventOverlayClose | `boolean` | | Property for preventing closing of modal when there is a action on overlay. BEWARE: This should be used only in very specials edge-cases! It breaks user experience. | * | hasCloseButton | `boolean` | `true` | Defines whether the Modal displays a close button. If you disable this, we recommend adding some kind of an alternative. | * | disableAnimation | `boolean` | `false` | Defines whether the Modal performs the slide in animation on mobile. If you want to improve your [CLS](https://web.dev/cls/) score, you might want to set this to `true`. | * | mobileHeader | `boolean` | `true` | If `false` the ModalHeader will not have MobileHeader and CloseContainer. | * | labelClose | `string` | | The label for the close button. It is required all the time, unless `hasCloseButton` is explicitly set to `false`. | * | onScroll | `event => void \| Promise` | | Function for handling `onScroll` event. [See Functional specs](#functional-specs). | * | ariaLabelledby | `string` | | The `aria-labelledby` attribute of the Modal. It should be used if `title` is not defined on the ModalHeader. | * | ariaDescribedby | `string` | | The `aria-describedby` attribute of the Modal. It should be used if `description` is not defined on the ModalHeader. | * | ariaLabel | `string` | | The `aria-label` attribute of the Modal. It should be used if `title` is not defined on the ModalHeader and `ariaLabelledby` is undefined. | * * ### Modal enum * * | size | * | :------------- | * | `"extraSmall"` | * | `"small"` | * | `"normal"` | * | `"large` | * | `"extraLarge"` | * * ### Functional specs * * - To select the Close Button element for testing purposes, use [data-test="ModalCloseButton"] selector. * * - To type a reference you're passing to a modal, use the following example: * * ```jsx * const modalRef = React.useRef<React.ElementRef<typeof Modal> | null>(null) * ``` * * - You might want to get the current scroll position of a Modal component, which might change based on media queries. Reading it constantly would degrade performance. Instead, get it on demand by using the `getScrollPosition` method in a Modal instance like this: * * ```jsx * class Component extends React.Component { * const modalRef = React.useRef<React.ElementRef<typeof Modal> | null>(null) * * const getScroll = () => { * if (modalRef.current) { * setLocalScrollPosition(modalRef.current.getScrollPosition()); * } * }; * * render() { * return ( * <Modal ref={modalRef}> * Some content. * </Modal> * ); * } * } * ``` * * - To set the scroll position of a Modal component, use the `setScrollPosition` method in a Modal instance like this: * * ```jsx * class Component extends React.Component { * const modalRef = React.useRef<React.ElementRef<typeof Modal> | null>(null) * * setScroll = () => { * if (modalRef.current) { * modalRef.current.setScrollPosition(100); * } * }; * render() { * return ( * <Modal ref={modalRef}> * <ModalSection>Example usage of setting up the scrollTop position</ModalSection> * <ModalFooter> * <Button onClick={this.setScroll}>Change scrollTop</Button> * </ModalFooter> * </Modal> * ); * } * } * ``` * * --- * * ## Subcomponents * * Modal component offers a good flexibility and many variations in its usage. There are three subcomponents which you might use. * * ### ModalSection * * ```jsx * import Modal, { ModalSection } from "@kiwicom/orbit-components/lib/Modal"; * ``` * * #### Usage * * ```jsx * <Modal> * <ModalSection suppressed>Hello World!</ModalSection> * </Modal> * ``` * * #### Props * * Table below contains all types of the props in the ModalSection component. * * | Name | Type | Default | Description | * | :----------- | :----------- | :------ | :------------------------------------------------------ | * | **children** | `React.Node` | | Content of the ModalSection component. | * | dataTest | `string` | | Optional prop for testing purposes. | * | suppressed | `boolean` | `false` | If `true` the ModalSection will have cloudy background. | * * ### ModalHeader * * ```jsx * import Modal, { ModalHeader } from "@kiwicom/orbit-components/lib/Modal"; * ``` * * #### Usage * * ```jsx * <Modal> * <ModalHeader title="Orbit design system">Hello World!</ModalHeader> * </Modal> * ``` * * #### Props * * Table below contains all types of the props in the ModalHeader component. * * | Name | Type | Default | Description | * | :----------- | :----------------------------------- | :------ | :----------------------------------------------------- | * | children | `React.Node` | | The content of the ModalHeader. | * | dataTest | `string` | | Optional prop for testing purposes. | * | description | `React.Node` | | The displayed description of the ModalHeader. | * | illustration | `React.Element<typeof Illustration>` | | The displayed Illustration of the ModalHeader. | * | suppressed | `boolean` | `false` | If `true` the ModalHeader will have cloudy background. | * | title | `React.Node` | | The displayed title of the ModalHeader. | * * ### ModalFooter * * ```jsx * import Modal, { ModalFooter } from "@kiwicom/orbit-components/lib/Modal"; * * // and probably Button * import Button from "@kiwicom/orbit-components/lib/Button"; * ``` * * #### Usage: * * ```jsx * <Modal fixedFooter> * <ModalFooter flex={["0 0 auto", "1 1 100%"]}> * <Button type="secondary" iconLeft={<ChevronBackward />}> * Back * </Button> * <Button block>Continue to Payment</Button> * </ModalFooter> * </Modal> * ``` * * #### Props * * Table below contains all types of the props in the ModalFooter component. * * | Name | Type | Default | Description | * | :----------- | :-------------------------- | :---------- | :----------------------------------------------------------------------------------------------- | * | **children** | `React.Node` | | The content of the ModalFooter. | * | dataTest | `string` | | Optional prop for testing purposes. | * | flex | `string` or `Array<string>` | `"0 1 auto` | The flex attribute(s) for children of the ModalFooter. [See Functional specs](#functional-specs) | * * #### ModalFooter Functional specs * * - You can set up different `flex` attribute for every children, or use one for all. See [flex property docs](https://www.w3schools.com/cssref/css3_pr_flex.asp) for more information. * * ## Use cases * * Although this component offers good flexibility of usage, there are tiny limitations for usage. * * ### Wrapper ModalSections * * If you need to wrap the children into custom component, wrap all of the children into **one wrapper**, e.g.: * * ```jsx * // good * <Modal fixedFooter> * <CustomWrapper> * <ModalHeader /> * <ModalSection> * My content * </ModalSection> * <ModalSection> * My content * </ModalSection> * <ModalFooter /> * </CustomWrapper> * </Modal> * * // bad, the CSS styles will be broken * <Modal fixedFooter> * <ModalHeader /> * <CustomWrapper> * <ModalSection> * My content * </ModalSection> * <ModalSection> * My content * </ModalSection> * </CustomWrapper> * <ModalFooter /> * </Modal> * ``` * * * Accessibility * ------------- * # Accessibility * * ## Modal * * The Modal component has been designed with accessibility in mind. * * To ease the keyboard navigation, when opening a modal, the focus is moved to the first focusable element inside the modal. It is also impossible to focus anything outside of the modal while it is open. * When closing the modal, the focus can be moved back to the element that triggered the modal automatically, if the prop `triggerRef` is defined with a ref to that element. * * Besides that, assistive ARIA attributes are applied automatically to the modal and its children, to ensure that screen readers can announce the modal and its content correctly. * * If you prefer, you can also provide those attributes manually, as described in the table below: * * | Name | Type | Description | * | :-------------- | :------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | * | ariaLabelledby | `string` | The `id` of an element that serves as a label (title) for the modal. | * | ariaDescribedby | `string` | The `id` of an element that serves as a description for the modal. | * | ariaLabel | `string` | Text that labels the modal content. Think of it as the title of the modal. This should be used if `title` is not passed to `ModalHeader` and `ariaLabelledby` is undefined. | * * All the props above are optional, but recommended to use to ensure the best experience for all users. * * If you use a ModalHeader with `title` and `description` props defined, they are automatically assigned as `aria-labelledby` and `aria-describedby`, respectively. * However, if needed, you can overwrite the values by passing the corresponding props. * * The `ariaLabelledby` and `ariaDescribedby` props can reference multiple ids, separated by a space. * The elements with those ids can be hidden, so that their text is only announced by screen readers. * * Be mindful that all descriptive texts, even if invisible on the screen, should be translated and provide context for users of assistive technologies. * * * @orbit-doc-end */ const Modal = ({ size = _consts.SIZES.NORMAL, scrollingElementRef, children, onClose, triggerRef, fixedFooter = false, isMobileFullPage = false, preventOverlayClose = false, onScroll, hasCloseButton = true, mobileHeader = true, disableAnimation = false, dataTest, id, labelClose = "Close", lockScrolling = true, ariaLabel, ariaLabelledby, ariaDescribedby, ref }) => { const [loaded, setLoaded] = React.useState(false); const [scrolled, setScrolled] = React.useState(false); const [fullyScrolled, setFullyScrolled] = React.useState(false); const [hasModalTitle, setHasModalTitle] = React.useState(false); const [hasModalDescription, setHasModalDescription] = React.useState(false); const [hasModalSection, setHasModalSection] = React.useState(false); const [clickedModalBody, setClickedModalBody] = React.useState(false); const [fixedClose, setFixedClose] = React.useState(false); const [modalWidth, setModalWidth] = React.useState(0); const [footerHeight, setFooterHeight] = React.useState(0); const focusableElements = React.useRef([]); const modalContent = React.useRef(null); const modalBody = React.useRef(null); const modalTitleID = (0, _useRandomId.default)(); const modalDescriptionID = (0, _useRandomId.default)(); const theme = (0, _useTheme.default)(); const { isLargeMobile } = (0, _useMediaQuery.default)(); const scrollingElement = React.useRef(null); const setScrollingElementRefs = React.useCallback(node => { scrollingElement.current = node; if (scrollingElementRef) { if (typeof scrollingElementRef === "function") { scrollingElementRef(node); } else { // eslint-disable-next-line no-param-reassign scrollingElementRef.current = node; } } }, [scrollingElementRef]); (0, _useLockScrolling.default)(scrollingElement, lockScrolling, [isLargeMobile]); const modalContentRef = React.useCallback(node => { modalContent.current = node; if (!isLargeMobile) setScrollingElementRefs(node); }, [isLargeMobile, setScrollingElementRefs]); const modalBodyRef = React.useCallback(node => { modalBody.current = node; if (isLargeMobile) setScrollingElementRefs(node); }, [isLargeMobile, setScrollingElementRefs]); const prevChildren = (0, _usePrevious.default)(children); const setDimensions = () => { const content = modalContent.current; if (!content) return; const footerEl = content.querySelector(".orbit-modal-footer"); const contentDimensions = content.getBoundingClientRect(); setModalWidth(contentDimensions.width); if (footerEl?.clientHeight) { setFooterHeight(footerEl.clientHeight); } }; const decideFixedFooter = () => { if (!modalContent.current || !modalBody.current) return; // if the content height is smaller than window height, we need to explicitly set fullyScrolled to true const content = modalContent.current; const body = modalBody.current; const contentHeight = content.scrollHeight > content.offsetHeight + OFFSET ? content.offsetHeight : content.scrollHeight; // when scrollHeight + topPadding - scrollingElementHeight is smaller than or equal to window height setFullyScrolled(contentHeight + OFFSET - body.scrollTop <= window.innerHeight); }; React.useEffect(() => { const findFocusableElements = () => { return Array.from(modalContent.current?.querySelectorAll(_consts2.default) || []); }; // Find all focusable elements within the modal focusableElements.current = findFocusableElements(); if (focusableElements.current.length) { focusableElements.current[0].focus(); } if (!modalContent.current) return undefined; const observer = new MutationObserver(() => { focusableElements.current = findFocusableElements(); }); // Start observing the modal content for DOM changes observer.observe(modalContent.current, { childList: true, // Watch for added/removed nodes subtree: true // Watch all descendants, not just direct children }); return () => { observer.disconnect(); }; }, []); const handleKeyDown = event => { if (onClose && event.key === "Escape") { event.stopPropagation(); onClose(event); } const firstElement = focusableElements.current[0]; const lastElement = focusableElements.current[focusableElements.current.length - 1]; if (event.key === "Tab") { if (event.shiftKey) { if (document.activeElement === firstElement) { event.preventDefault(); lastElement.focus(); } } else if (document.activeElement === lastElement) { event.preventDefault(); firstElement.focus(); } } }; const handleClickOutside = event => { const clickedOutside = onClose && preventOverlayClose === false && !clickedModalBody && modalContent.current && event.target instanceof Element && !modalContent.current.contains(event.target) && event.target.className && /orbit-modal-wrapper|orbit-modal-body/.test(event.target.className); if (clickedOutside && onClose) onClose(event); setClickedModalBody(false); }; const setScrollStates = (target, fullScrollOffset, fixCloseOffset, scrollBegin, mobile) => { const content = modalContent.current; if (!content) return; const { height: contentHeight } = content.getBoundingClientRect(); /* Only for desktop, we need to check if the scrollHeight of content is bigger than actual height if so, we need to you use the contentHeight + padding as bottom scroll point, otherwise actual scrollHeight of the target is enough. */ const scrollHeight = !mobile && target.scrollHeight > contentHeight + 80 ? contentHeight + 80 : target.scrollHeight; setScrolled(target.scrollTop >= Number(scrollBegin) + (!mobile ? target.scrollTop : 0)); setFixedClose(target.scrollTop >= fixCloseOffset); // set fullyScrolled state sooner than the exact end of the scroll (with fullScrollOffset value) setFullyScrolled(fixedFooter && target.scrollTop >= scrollHeight - target.clientHeight - fullScrollOffset); }; const getScrollTopPoint = mobile => { const content = modalContent.current; if (!content) return null; const headingEl = content.querySelector(".orbit-modal-heading"); if (headingEl) { const { top } = headingEl.getBoundingClientRect(); return top; } if (mobile) return OFFSET; const { top } = content.getBoundingClientRect(); return top; }; const handleScroll = event => { if (event.target instanceof HTMLDivElement && event.target === modalBody.current) { if (onScroll) onScroll(event); setScrollStates(event.target, OFFSET, OFFSET, getScrollTopPoint()); } }; const handleMobileScroll = event => { if (onScroll) onScroll(event); if (event.target instanceof HTMLDivElement && event.target === modalContent.current) { setScrollStates(event.target, 10, 1, getScrollTopPoint(true), true); } }; const handleMouseDown = () => { /* This is due to issue where it was possible to close Modal, even though click started (onMouseDown) in ModalWrapper. */ setClickedModalBody(true); }; const callContextFunctions = React.useCallback(() => { setDimensions(); decideFixedFooter(); }, []); const getScrollPosition = () => { if (scrollingElement.current) { return scrollingElement.current.scrollTop; } return null; }; const setScrollPosition = value => { if (scrollingElement.current) { scrollingElement.current.scrollTop = value; } }; React.useImperativeHandle(ref, () => ({ getScrollPosition, setScrollPosition, modalBody, modalContent })); // eslint-disable-next-line consistent-return React.useEffect(() => { if (disableAnimation) { decideFixedFooter(); setDimensions(); } else { const timer = setTimeout(() => { setLoaded(true); decideFixedFooter(); setDimensions(); }, 15); return () => { clearTimeout(timer); }; } // the Modal can only transition in on mount // eslint-disable-next-line react-hooks/exhaustive-deps }, []); React.useEffect(() => { const handleResize = () => { setDimensions(); decideFixedFooter(); }; window.addEventListener("resize", handleResize); return () => { window.removeEventListener("resize", handleResize); }; }, []); React.useEffect(() => { if (children !== prevChildren) { decideFixedFooter(); setDimensions(); } }, [children, prevChildren]); React.useEffect(() => { return () => { // eslint-disable-next-line react-hooks/exhaustive-deps triggerRef?.current?.focus(); }; }, [triggerRef]); const hasCloseContainer = mobileHeader && (hasModalTitle || onClose && hasCloseButton); const value = React.useMemo(() => ({ setHasModalTitle, setHasModalDescription, setHasModalSection: () => setHasModalSection(true), removeHasModalSection: () => setHasModalSection(false), callContextFunctions, setFooterHeight, hasModalSection, hasMobileHeader: mobileHeader, isMobileFullPage, closable: Boolean(onClose), isInsideModal: true, titleID: modalTitleID, descriptionID: modalDescriptionID }), [callContextFunctions, hasModalSection, isMobileFullPage, mobileHeader, onClose, modalTitleID, modalDescriptionID]); const cssVars = { ...(!isLargeMobile ? { maxHeight: isMobileFullPage ? "100%" : `calc(100% - ${theme.orbit.space800} - ${fixedFooter && Boolean(footerHeight) ? footerHeight : 0}px)`, bottom: `${(!isMobileFullPage ? parseInt(theme.orbit.space800, 10) : 0) + (fixedFooter && Boolean(footerHeight) ? footerHeight : 0)}px` } : { "--orbit-modal-footer-height": fixedFooter ? `${footerHeight}px` : "0" }), "--orbit-modal-width": `${modalWidth}px` }; return ( /*#__PURE__*/ // eslint-disable-next-line jsx-a11y/no-noninteractive-element-interactions React.createElement("div", { className: (0, _clsx.default)("orbit-modal-body", "z-overlay font-base fixed inset-0 box-border size-full overflow-x-hidden outline-none", !isMobileFullPage && "bg-[black]/50", "lm:overflow-y-auto lm:p-1000 lm:bg-[black]/50"), tabIndex: -1, onKeyDown: handleKeyDown, onScroll: handleScroll, onClick: handleClickOutside, "data-test": dataTest, id: id, ref: modalBodyRef, role: "dialog", "aria-modal": "true", "aria-label": ariaLabel, "aria-labelledby": ariaLabelledby || (hasModalTitle ? modalTitleID : undefined), "aria-describedby": ariaDescribedby || (hasModalDescription ? modalDescriptionID : undefined) }, /*#__PURE__*/React.createElement("div", { className: (0, _clsx.default)("orbit-modal-wrapper", "fixed mx-auto my-0 box-border flex min-h-full w-full items-start", disableAnimation ? !isMobileFullPage && "top-800" : ["duration-normal transition-[top] ease-in-out", loaded ? !isMobileFullPage && "top-800" : "top-full"], "lm:relative lm:top-0 lm:items-center", maxWidthClasses.largeMobile[size]) }, /*#__PURE__*/React.createElement("div", { className: (0, _clsx.default)("orbit-modal-wrapper-content", "lm:rounded-modal lm:overflow-visible overflow-y-auto overflow-x-hidden", "font-base bg-elevation-flat shadow-level4 absolute box-border w-full", "lm:relative lm:bottom-auto lm:pb-0", "lm:[&_.orbit-modal-section:last-of-type]:pb-1000 lm:[&_.orbit-modal-section:last-of-type:after]:content-none lm:[&_.orbit-modal-section:last-of-type]:mb-[var(--orbit-modal-footer-height,0px)]", "lm:[&_.orbit-modal-mobile-header]:w-[calc(var(--orbit-modal-width)-48px-theme(spacing.1000))]", "lm:[&:has(.orbit-modal-header-container:last-child)]:pb-1000", footerHeight && "lm:[&_.orbit-modal-section]:rounded-b-none", !hasModalSection && "[&_.orbit-modal-header-container]:mb-800 lm:[&_.orbit-modal-header-container]:mb-[var(--orbit-modal-footer-height,0px)]", isMobileFullPage ? "top-0 max-h-full" : ["rounded-t-modal", scrolled && "[&_.orbit-modal-mobile-header]:rounded-tl-modal", scrolled && "[&_.orbit-modal-mobile-header]:top-800"], fixedFooter && footerHeight && ["[&_.orbit-modal-footer]:p-400 [&_.orbit-modal-footer]:fixed [&_.orbit-modal-footer]:bottom-0", "[&_.orbit-modal-footer]:duration-fast [&_.orbit-modal-footer]:transition-shadow [&_.orbit-modal-footer]:ease-in-out", fullyScrolled ? "[&_.orbit-modal-footer]:shadow-modal-scrolled" : "[&_.orbit-modal-footer]:shadow-modal lm:[&_.orbit-modal-footer]:rounded-b-none", "[&_.orbit-modal-section:last-of-type]:pb-600 [&_.orbit-modal-section:last-of-type]:mb-0"], fixedFooter ? ["lm:[&_.orbit-modal-footer]:!p-800", fullyScrolled && "lm:[&_.orbit-modal-footer]:absolute"] : "lm:[&_.orbit-modal-footer]:p-800", fullyScrolled && "lm:[&_.orbit-modal-footer]:shadow-none", scrolled ? ["[&_.orbit-modal-mobile-header]:visible [&_.orbit-modal-mobile-header]:opacity-100", "lm:[&_.orbit-modal-mobile-header]:top-0"] : "lm:[&_.orbit-modal-mobile-header]:-top-1000", modalWidth ? "lm:[&_.orbit-modal-footer]:max-w-[var(--orbit-modal-width)]" : maxWidthClasses.footer[size]), style: cssVars, onScroll: handleMobileScroll, ref: modalContentRef, onMouseDown: handleMouseDown }, hasCloseContainer && /*#__PURE__*/React.createElement("div", { className: (0, _clsx.default)("z-overlay h-form-box-large pointer-events-none right-0 box-border flex w-full items-center justify-end", "duration-fast transition-[shadow,_background-color] ease-in-out", "lm:rounded-none", fixedClose || scrolled ? "lm:top-0 lm:right-auto fixed" : "absolute", !isMobileFullPage && (fixedClose || scrolled) ? "top-800" : "top-0", !isMobileFullPage && "rounded-t-modal", modalWidth ? "max-w-[var(--orbit-modal-width)]" : maxWidthClasses[size], scrolled && "shadow-fixed bg-white-normal", "[&_+_.orbit-modal-section:first-of-type]:pt-1300 [&_+_.orbit-modal-section:first-of-type]:m-0 [&_+_.orbit-modal-section:first-of-type]:border-t-0", "[&_.orbit-button-primitive]:me-100 [&_.orbit-button-primitive]:pointer-events-auto", "[&_.orbit-button-primitive_svg]:duration-fast [&_.orbit-button-primitive_svg]:text-ink-normal [&_.orbit-button-primitive_svg]:transition-[color] [&_.orbit-button-primitive_svg]:ease-in-out", "[&_.orbit-button-primitive:hover_svg]:text-ink-light-hover [&_.orbit-button-primitive:active_svg]:text-ink-light-active"), "data-test": "CloseContainer" }, onClose && hasCloseButton && /*#__PURE__*/React.createElement(_ModalCloseButton.default, { onClick: onClose, dataTest: _consts.CLOSE_BUTTON_DATA_TEST, title: labelClose })), /*#__PURE__*/React.createElement(_ModalContext.ModalContext.Provider, { value: value }, children)))) ); }; var _default = exports.default = Modal;