UNPKG

@carbon/ibm-products

Version:

Carbon for IBM Products

303 lines (292 loc) 10.8 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('react'); var propsHelper = require('../../global/js/utils/props-helper.js'); var react = require('@carbon/react'); var index = require('../../_virtual/index.js'); var TearsheetShell = require('../Tearsheet/TearsheetShell.js'); var cx = require('classnames'); var devtools = require('../../global/js/utils/devtools.js'); var getNumberOfHiddenSteps = require('../../global/js/utils/getNumberOfHiddenSteps.js'); var lastIndexInArray = require('../../global/js/utils/lastIndexInArray.js'); var settings = require('../../settings.js'); var usePreviousValue = require('../../global/js/hooks/usePreviousValue.js'); var useCreateComponentFocus = require('../../global/js/hooks/useCreateComponentFocus.js'); var useValidCreateStepCount = require('../../global/js/hooks/useValidCreateStepCount.js'); var useResetCreateComponent = require('../../global/js/hooks/useResetCreateComponent.js'); var useCreateComponentStepChange = require('../../global/js/hooks/useCreateComponentStepChange.js'); var CreateInfluencer = require('../CreateInfluencer/CreateInfluencer.js'); const componentName = 'CreateTearsheet'; const blockClass = `${settings.pkg.prefix}--tearsheet-create`; // This is a general context for the steps container // containing information about the state of the container // and providing some callback methods for steps to use const StepsContext = /*#__PURE__*/React.createContext(null); // This is a context supplied separately to each step in the container // to let it know what number it is in the sequence of steps const StepNumberContext = /*#__PURE__*/React.createContext(-1); exports.CreateTearsheet = /*#__PURE__*/React.forwardRef((_ref, ref) => { let { // The component props, in alphabetical order (for consistency). backButtonText, cancelButtonText, children, className, experimentalSecondarySubmitText, firstFocusElement, description, hasError, influencerWidth = 'narrow', initialStep, label, nextButtonText, onClose, onRequestSubmit, open, selectorPrimaryFocus, slug: deprecated_slug, decorator, submitButtonText, title, verticalPosition = 'normal', // Collect any other property values passed in. ...rest } = _ref; const [createTearsheetActions, setCreateTearsheetActions] = React.useState([]); const [shouldViewAll, setShouldViewAll] = React.useState(false); const [currentStep, setCurrentStep] = React.useState(0); const [isSubmitting, setIsSubmitting] = React.useState(false); const [isDisabled, setIsDisabled] = React.useState(false); const [loadingPrevious, setLoadingPrevious] = React.useState(false); const [onPrevious, setOnPrevious] = React.useState(); const [onNext, setOnNext] = React.useState(); const [onMount, setOnMount] = React.useState(); const [stepData, setStepData] = React.useState([]); const [firstIncludedStep, setFirstIncludedStep] = React.useState(1); const [lastIncludedStep, setLastIncludedStep] = React.useState(); const [experimentalSecondarySubmit, setExperimentalSecondarySubmit] = React.useState(); const previousState = usePreviousValue.usePreviousValue({ currentStep, open }); const contentRef = React.useRef(null); React.useEffect(() => { const firstItem = stepData.findIndex(item => item.shouldIncludeStep === true) + 1; const lastItem = lastIndexInArray.lastIndexInArray(stepData, 'shouldIncludeStep', true); if (firstItem !== firstIncludedStep) { setCurrentStep(firstItem); setFirstIncludedStep(firstItem); } if (lastItem !== lastIncludedStep) { setLastIncludedStep(lastItem); } if (open && initialStep) { const numberOfHiddenSteps = getNumberOfHiddenSteps.getNumberOfHiddenSteps(stepData, initialStep); setCurrentStep(Number(initialStep + numberOfHiddenSteps)); } }, [firstIncludedStep, initialStep, lastIncludedStep, open, stepData]); useCreateComponentFocus.useCreateComponentFocus({ previousState, currentStep, blockClass, onMount, firstFocusElement }); useValidCreateStepCount.useValidCreateStepCount(stepData.length, componentName); useResetCreateComponent.useResetCreateComponent({ firstIncludedStep, previousState, /**@ts-ignore */ open, setCurrentStep, stepData, /**@ts-ignore */ initialStep, totalSteps: stepData?.length, componentName }); useCreateComponentStepChange.useCreateComponentStepChange({ firstIncludedStep, lastIncludedStep, stepData, onPrevious, onNext, isSubmitDisabled: isDisabled, setCurrentStep, setIsSubmitting, /**@ts-ignore */ setLoadingPrevious, loadingPrevious, onClose, onRequestSubmit, componentName, currentStep, backButtonText, cancelButtonText, submitButtonText, nextButtonText, isSubmitting, componentBlockClass: blockClass, experimentalSecondarySubmit, experimentalSecondarySubmitText: experimentalSecondarySubmit?.labelText ? experimentalSecondarySubmit.labelText : experimentalSecondarySubmitText, setCreateComponentActions: setCreateTearsheetActions }); return /*#__PURE__*/React.createElement(TearsheetShell.TearsheetShell, _rollupPluginBabelHelpers.extends({}, devtools.getDevtoolsProps(componentName), rest, { ref, actions: createTearsheetActions, className: cx(blockClass, className), description, influencer: /*#__PURE__*/React.createElement(CreateInfluencer.CreateInfluencer, { currentStep: currentStep, stepData: stepData }), influencerWidth, label, onClose, open, size: 'wide', decorator: decorator || deprecated_slug, title, verticalPosition, closeIconDescription: '', currentStep: currentStep, hasError: hasError, selectorPrimaryFocus: selectorPrimaryFocus }), /*#__PURE__*/React.createElement("div", { className: `${blockClass}__content`, ref: contentRef }, /*#__PURE__*/React.createElement(react.Form, { "aria-label": title }, /*#__PURE__*/React.createElement(StepsContext.Provider, { value: { currentStep, setExperimentalSecondarySubmit, setIsDisabled, setOnPrevious: fn => setOnPrevious(() => fn), setOnNext: fn => setOnNext(() => fn), setOnMount: fn => setOnMount(() => fn), setStepData, stepData } }, React.Children.toArray(children).filter(Boolean).map((child, index) => /*#__PURE__*/React.createElement(StepNumberContext.Provider, { value: index + 1, key: index }, child)))))); }); // Return a placeholder if not released and not enabled by feature flag exports.CreateTearsheet = settings.pkg.checkComponentEnabled(exports.CreateTearsheet, componentName); // The display name of the component, used by React. Note that displayName // is used in preference to relying on function.name. exports.CreateTearsheet.displayName = componentName; const deprecatedProps = { /** * @deprecated Property replaced by `decorator` */ slug: propsHelper.deprecateProp(index.default.node, 'Property replaced by `decorator`') }; // Note that the descriptions here should be kept in sync with those for the // corresponding props for TearsheetNarrow and TearsheetShell components. exports.CreateTearsheet.propTypes = { /** * The back button text */ backButtonText: index.default.string.isRequired, /** * The cancel button text */ cancelButtonText: index.default.string.isRequired, /** * The main content of the tearsheet */ children: index.default.node, /** * An optional class or classes to be added to the outermost element. */ className: index.default.string, /** * Optional prop that allows you to pass any component. */ decorator: index.default.node, /** * A description of the flow, displayed in the header area of the tearsheet. */ description: index.default.node, /** * The experimentalSecondary submit button text */ experimentalSecondarySubmitText: index.default.string, /** * Specifies elements to focus on first on render. */ firstFocusElement: index.default.string, /** * To indicate an error occurred in the Tearsheet step * Used to pass this value to TearsheetShell */ hasError: index.default.bool, /** * Used to set the size of the influencer */ influencerWidth: index.default.oneOf(['narrow', 'wide']), /** * This can be used to open the component to a step other than the first step. * For example, a create flow was previously in progress, data was saved, and * is now being completed. */ initialStep: index.default.number, /** * A label for the tearsheet, displayed in the header area of the tearsheet * to maintain context for the tearsheet (e.g. as the title changes from page * to page of a multi-page task). */ label: index.default.node, /** * The next button text */ nextButtonText: index.default.string.isRequired, /** * An optional handler that is called when the user closes the tearsheet (by * clicking the close button, if enabled, or clicking outside, if enabled). * Returning `false` here prevents the modal from closing. */ onClose: index.default.func, /** * Specify a handler for submitting the multi step tearsheet (final step). * This function can _optionally_ return a promise that is either resolved or rejected and the CreateTearsheet will handle the submitting state of the create button. */ onRequestSubmit: index.default.func.isRequired, /** * Specifies whether the tearsheet is currently open. */ open: index.default.bool, /** * Specify a CSS selector that matches the DOM element that should be * focused when the Modal opens. */ selectorPrimaryFocus: index.default.string, /** * The submit button text */ submitButtonText: index.default.string.isRequired, /** * The main title of the tearsheet, displayed in the header area. */ title: index.default.node, /** * The position of the top of tearsheet in the viewport. The 'normal' * position (the default) is a short distance down from the top of the * viewport, leaving room at the top for a global header bar to show through * from below. The 'lower' position provides a little extra room at the top * to allow an action bar navigation or breadcrumbs to also show through. */ verticalPosition: index.default.oneOf(['normal', 'lower']), ...deprecatedProps }; exports.StepNumberContext = StepNumberContext; exports.StepsContext = StepsContext;