UNPKG

@carbon/ibm-products

Version:

Carbon for IBM Products

219 lines (207 loc) 7.71 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. */ 'use strict'; var _rollupPluginBabelHelpers = require('../../_virtual/_rollupPluginBabelHelpers.js'); var react = require('@carbon/react'); var React = require('react'); var index = require('../../_virtual/index.js'); var cx = require('classnames'); var devtools = require('../../global/js/utils/devtools.js'); var settings = require('../../settings.js'); var InterstitialScreenHeader = require('./InterstitialScreenHeader.js'); var InterstitialScreenBody = require('./InterstitialScreenBody.js'); var InterstitialScreenFooter = require('./InterstitialScreenFooter.js'); // The block part of our conventional BEM class names (blockClass__E--M). const blockClass = `${settings.pkg.prefix}--interstitial-screen`; const componentName = 'InterstitialScreen'; // NOTE: the component SCSS is not imported here: it is rolled up separately. // Default values can be included here and then assigned to the prop params, // e.g. prop = defaults.prop, // This gathers default values together neatly and ensures non-primitive // values are initialized early to avoid react making unnecessary re-renders. // Note that default values are not required for props that are 'required', // nor for props where the component can apply undefined values reasonably. // Default values should be provided when the component needs to make a choice // or assumption when a prop is not supplied. // Define the type for InterstitialScreen, extending it to include Header const InterstitialScreenContext = /*#__PURE__*/React.createContext({ progStep: 0, stepCount: 0 }); /** * 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). */ exports.InterstitialScreen = /*#__PURE__*/React.forwardRef((props, ref) => { const { children, className, interstitialAriaLabel = 'Interstitial screen', isFullScreen = false, isOpen = false, launcherButtonRef, onClose, ...rest } = props; const backupRef = React.useRef(null); const _forwardedRef = ref || backupRef; const scrollRef = React.useRef(undefined); const startButtonRef = React.useRef(undefined); const nextButtonRef = React.useRef(undefined); const [isVisibleClass, setIsVisibleClass] = React.useState(null); const [progStep, setProgStep] = React.useState(0); const bodyScrollRef = React.useRef(null); const [stepCount, setStepCount] = React.useState(0); const [disableButtonConfig, setDisableButtonConfig] = React.useState({ skip: false, back: false, next: false, start: false }); const variantClass = isFullScreen ? `${blockClass}--full-screen` : `${blockClass}--modal`; const [bodyChildrenData, setBodyChildrenData] = React.useState(null); const handleClose = React.useCallback(actionName => { setProgStep(0); onClose?.(actionName ?? 'close'); }, [onClose]); React.useEffect(() => { if (!isOpen) { setProgStep(0); } startButtonRef.current?.focus(); }, [isOpen, progStep, onClose]); React.useEffect(() => { // for modal only, "is-visible" triggers animation setIsVisibleClass(!isFullScreen && isOpen ? 'is-visible' : null); nextButtonRef?.current?.focus(); if (!isOpen && launcherButtonRef) { setTimeout(() => { launcherButtonRef.current.focus(); }, 0); } }, [launcherButtonRef, isFullScreen, isOpen]); // hitting escape key also closes this component React.useEffect(() => { const close = e => { const { key } = e; if (key === 'Escape') { handleClose('close'); } }; window.addEventListener('keydown', close); return () => window.removeEventListener('keydown', close); }, [handleClose]); if (!isOpen) { return null; } const renderModal = () => { return /*#__PURE__*/React.createElement(react.unstable_FeatureFlags, { enableExperimentalFocusWrapWithoutSentinels: true }, /*#__PURE__*/React.createElement(react.ComposedModal, _rollupPluginBabelHelpers.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: isOpen, ref: _forwardedRef, "aria-label": interstitialAriaLabel }, devtools.getDevtoolsProps(componentName)), children)); }; const renderFullScreen = () => { return /*#__PURE__*/React.createElement("div", _rollupPluginBabelHelpers.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": interstitialAriaLabel, ref: ref }, devtools.getDevtoolsProps(componentName)), /*#__PURE__*/React.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.createElement(InterstitialScreenContext.Provider, { value: { bodyChildrenData, setBodyChildrenData, isFullScreen, handleClose, progStep, setProgStep, bodyScrollRef, scrollRef, handleGotoStep, stepCount, setStepCount, disableButtonConfig, setDisableButtonConfig } }, isFullScreen ? renderFullScreen() : renderModal()); }); exports.InterstitialScreen.Header = InterstitialScreenHeader.default; exports.InterstitialScreen.Body = InterstitialScreenBody.default; exports.InterstitialScreen.Footer = InterstitialScreenFooter.default; // Return a placeholder if not released and not enabled by feature flag exports.InterstitialScreen = settings.pkg.checkComponentEnabled(exports.InterstitialScreen, componentName); // The display name of the component, used by React. Note that displayName // is used in preference to relying on function.name. exports.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. exports.InterstitialScreen.propTypes = { children: index.default.node, /** * Provide an optional class to be applied to the containing node. */ className: index.default.string, /** * Tooltip text and aria label for the Close button icon. */ closeIconDescription: index.default.string, /** * The aria label applied to the Interstitial Screen component */ interstitialAriaLabel: index.default.string, /** * Specifies whether the component is shown as a full-screen * experience, else it is shown as a modal by default. */ isFullScreen: index.default.bool, /** * Specifies whether the component is currently open. */ isOpen: index.default.bool, /** * Provide a ref to return focus to once the interstitial is closed. */ launcherButtonRef: index.default.any, /** * Function to call when the close button is clicked. */ onClose: index.default.func }; exports.InterstitialScreenContext = InterstitialScreenContext;