UNPKG

@carbon/ibm-products

Version:

Carbon for IBM Products

201 lines (192 loc) 6.65 kB
/** * Copyright IBM Corp. 2020, 2025 * * This source code is licensed under the Apache-2.0 license found in the * LICENSE file in the root directory of this source tree. */ import { extends as _extends } from '../../_virtual/_rollupPluginBabelHelpers.js'; import { unstable_FeatureFlags, ComposedModal } from '@carbon/react'; import React__default, { useRef, useState, useCallback, useEffect } from 'react'; import PropTypes from '../../_virtual/index.js'; import cx from 'classnames'; import { getDevtoolsProps } from '../../global/js/utils/devtools.js'; import { pkg } from '../../settings.js'; import InterstitialScreenHeader from './InterstitialScreenHeader.js'; import InterstitialScreenBody from './InterstitialScreenBody.js'; import InterstitialScreenFooter from './InterstitialScreenFooter.js'; import { InterstitialScreenContext, blockClass } from './context.js'; const componentName = 'InterstitialScreen'; // Define the type for InterstitialScreen, extending it to include Header /** * InterstitialScreen can be a full page or an overlay, and are * shown on the first time a user accesses a new experience * (e.g. upon first login or first time opening a page where a * newly purchased capability is presented). */ let InterstitialScreen = /*#__PURE__*/React__default.forwardRef((props, ref) => { const { children, className, ariaLabel = 'Interstitial screen', isFullScreen = false, open = false, launcherButtonRef, onClose, ...rest } = props; const backupRef = useRef(null); const _forwardedRef = ref || backupRef; const scrollRef = useRef(undefined); const startButtonRef = useRef(undefined); const nextButtonRef = useRef(undefined); const [isVisibleClass, setIsVisibleClass] = useState(null); const [progStep, setProgStep] = useState(0); const bodyScrollRef = useRef(null); const [stepCount, setStepCount] = useState(0); const [disableButtonConfig, setDisableButtonConfig] = useState({ skip: false, back: false, next: false, start: false }); const variantClass = isFullScreen ? `${blockClass}--full-screen` : `${blockClass}--modal`; const [bodyChildrenData, setBodyChildrenData] = useState(null); const handleClose = useCallback(actionName => { setProgStep(0); onClose?.(actionName ?? 'close'); }, [onClose]); useEffect(() => { if (!open) { setProgStep(0); } startButtonRef.current?.focus(); }, [open, progStep, onClose]); useEffect(() => { // for modal only, "is-visible" triggers animation setIsVisibleClass(!isFullScreen && open ? 'is-visible' : null); nextButtonRef?.current?.focus(); if (!open && launcherButtonRef) { setTimeout(() => { launcherButtonRef.current.focus(); }, 0); } }, [launcherButtonRef, isFullScreen, open]); // hitting escape key also closes this component useEffect(() => { const close = e => { const { key } = e; if (key === 'Escape') { handleClose('close'); } }; window.addEventListener('keydown', close); return () => window.removeEventListener('keydown', close); }, [handleClose]); if (!open) { return null; } const renderModal = () => { return /*#__PURE__*/React__default.createElement(unstable_FeatureFlags, { enableExperimentalFocusWrapWithoutSentinels: true }, /*#__PURE__*/React__default.createElement(ComposedModal, _extends({}, rest, { preventCloseOnClickOutside: true, className: cx(blockClass, // Apply the block class to the main HTML element className // Apply any supplied class names to the main HTML element. ), size: "lg", onClose: handleClose, open: open, ref: _forwardedRef, "aria-label": ariaLabel }, getDevtoolsProps(componentName)), children)); }; const renderFullScreen = () => { return /*#__PURE__*/React__default.createElement("div", _extends({}, rest, { className: cx(blockClass, // Apply the block class to the main HTML element className, // Apply any supplied class names to the main HTML element. variantClass, isVisibleClass), role: "main", "aria-label": ariaLabel, ref: ref }, getDevtoolsProps(componentName)), /*#__PURE__*/React__default.createElement("div", { className: `${blockClass}--container` }, children)); }; const handleGotoStep = targetStep => { setProgStep(targetStep); scrollRef.current.scrollToView(targetStep); scrollBodyToTop(); }; const scrollBodyToTop = () => { bodyScrollRef.current?.scroll?.({ top: 0, behavior: 'smooth' }); }; return /*#__PURE__*/React__default.createElement(InterstitialScreenContext.Provider, { value: { bodyChildrenData, setBodyChildrenData, isFullScreen, handleClose, progStep, setProgStep, bodyScrollRef, scrollRef, handleGotoStep, stepCount, setStepCount, disableButtonConfig, setDisableButtonConfig } }, isFullScreen ? renderFullScreen() : renderModal()); }); InterstitialScreen.Header = InterstitialScreenHeader; InterstitialScreen.Body = InterstitialScreenBody; InterstitialScreen.Footer = InterstitialScreenFooter; // Return a placeholder if not released and not enabled by feature flag InterstitialScreen = pkg.checkComponentEnabled(InterstitialScreen, componentName); // The display name of the component, used by React. Note that displayName // is used in preference to relying on function.name. InterstitialScreen.displayName = componentName; // The types and DocGen commentary for the component props, // in alphabetical order (for consistency). // See https://www.npmjs.com/package/prop-types#usage. InterstitialScreen.propTypes = { /** * The aria label applied to the Interstitial Screen component */ ariaLabel: PropTypes.string, children: PropTypes.node, /** * Provide an optional class to be applied to the containing node. */ className: PropTypes.string, /** * Tooltip text and aria label for the Close button icon. */ closeIconDescription: PropTypes.string, /** * Specifies whether the component is shown as a full-screen * experience, else it is shown as a modal by default. */ isFullScreen: PropTypes.bool, /** * Provide a ref to return focus to once the interstitial is closed. */ launcherButtonRef: PropTypes.any, /** * Function to call when the close button is clicked. */ onClose: PropTypes.func, /** * Specifies whether the component is currently open. */ open: PropTypes.bool }; export { InterstitialScreen };