UNPKG

intro.js-react

Version:
292 lines (275 loc) 8.61 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.default = void 0; var _intro = _interopRequireDefault(require("intro.js")); var _propTypes = _interopRequireDefault(require("prop-types")); var _react = require("react"); var _server = require("react-dom/server"); var introJsPropTypes = _interopRequireWildcard(require("../../helpers/proptypes.cjs")); var introJsDefaultProps = _interopRequireWildcard(require("../../helpers/defaultProps.cjs")); var _server2 = require("../../helpers/server.cjs"); function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); } function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; } function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } /** * Intro.js Steps Component. */ class Steps extends _react.Component { /** * React Props * @type {Object} */ static propTypes = { enabled: _propTypes.default.bool, initialStep: _propTypes.default.number.isRequired, steps: _propTypes.default.arrayOf(_propTypes.default.shape({ element: _propTypes.default.oneOfType([_propTypes.default.string, /* istanbul ignore next */ typeof Element === 'undefined' ? _propTypes.default.any : _propTypes.default.instanceOf(Element)]), intro: _propTypes.default.node.isRequired, position: introJsPropTypes.tooltipPosition, tooltipClass: _propTypes.default.string, highlightClass: _propTypes.default.string })).isRequired, onStart: _propTypes.default.func, onExit: _propTypes.default.func.isRequired, onBeforeExit: _propTypes.default.func, onBeforeChange: _propTypes.default.func, onAfterChange: _propTypes.default.func, onChange: _propTypes.default.func, onPreventChange: _propTypes.default.func, onComplete: _propTypes.default.func, options: introJsPropTypes.options }; /** * React Default Props * @type {Object} */ static defaultProps = { enabled: false, onStart: null, onBeforeExit: null, onBeforeChange: null, onAfterChange: null, onChange: null, onPreventChange: null, onComplete: null, options: introJsDefaultProps.options }; /** * Creates a new instance of the component. * @class * @param {Object} props - The props of the component. */ constructor(props) { super(props); this.introJs = null; this.isConfigured = false; // We need to manually keep track of the visibility state to avoid a callback hell. this.isVisible = false; this.installIntroJs(); } /** * Lifecycle: componentDidMount. * We use this event to enable Intro.js steps at mount time if enabled right from the start. */ componentDidMount() { if (this.props.enabled) { this.configureIntroJs(); this.renderSteps(); } } /** * Lifecycle: componentDidUpdate. * @param {Object} prevProps - The previous props. */ componentDidUpdate(prevProps) { const { enabled, steps, options } = this.props; if (!this.isConfigured || prevProps.steps !== steps || prevProps.options !== options) { this.configureIntroJs(); this.renderSteps(); } if (prevProps.enabled !== enabled) { this.renderSteps(); } } /** * Lifecycle: componentWillUnmount. * We use this even to hide the steps when the component is unmounted. */ componentWillUnmount() { this.introJs.exit(); } /** * Triggered when Intro.js steps are exited. */ onExit = () => { const { onExit } = this.props; this.isVisible = false; onExit(this.introJs._currentStep); }; /** * Triggered before exiting the intro. * @return {Boolean} Returning `false` will prevent exiting the intro. */ onBeforeExit = () => { const { onBeforeExit } = this.props; if (onBeforeExit) { return onBeforeExit(this.introJs._currentStep); } return true; }; /** * Triggered before changing step. * @return {Boolean} Returning `false` will prevent the step transition. */ onBeforeChange = nextElement => { if (!this.isVisible) { return true; } const { onBeforeChange, onPreventChange } = this.props; if (onBeforeChange) { const continueStep = onBeforeChange(this.introJs._currentStep, nextElement); if (continueStep === false && onPreventChange) { setTimeout(() => { onPreventChange(this.introJs._currentStep); }, 0); } return continueStep; } return true; }; /** * Triggered after changing step. * @param {HTMLElement} element - The element associated to the new step. */ onAfterChange = element => { if (!this.isVisible) { return; } const { onAfterChange } = this.props; if (onAfterChange) { onAfterChange(this.introJs._currentStep, element); } }; /** * Triggered when changing step. * @param {HTMLElement} element - The element associated to the next step. */ onChange = element => { if (!this.isVisible) { return; } const { onChange } = this.props; if (onChange) { onChange(this.introJs._currentStep, element); } }; /** * Triggered when completing all the steps. */ onComplete = () => { const { onComplete } = this.props; if (onComplete) { onComplete(); } }; /** * Updates the element associated to a step based on its index. * This is useful when the associated element is not present in the DOM on page load. * @param {number} stepIndex - The index of the step to update. */ updateStepElement = stepIndex => { const element = document.querySelector(this.introJs._options.steps[stepIndex].element); if (element) { this.introJs._introItems[stepIndex].element = element; this.introJs._introItems[stepIndex].position = this.introJs._options.steps[stepIndex].position || 'auto'; } }; /** * Installs Intro.js. */ installIntroJs() { if ((0, _server2.isServer)()) { return; } this.introJs = (0, _intro.default)(); this.introJs.onexit(this.onExit); this.introJs.onbeforeexit(this.onBeforeExit); this.introJs.onbeforechange(this.onBeforeChange); this.introJs.onafterchange(this.onAfterChange); this.introJs.onchange(this.onChange); this.introJs.oncomplete(this.onComplete); } /** * Configures Intro.js if not already configured. */ configureIntroJs() { const { options, steps } = this.props; const sanitizedSteps = steps.map(step => { if ( /*#__PURE__*/(0, _react.isValidElement)(step.intro)) { return { ...step, intro: (0, _server.renderToStaticMarkup)(step.intro) }; } return step; }); this.introJs.setOptions({ ...options, steps: sanitizedSteps }); this.isConfigured = true; } /** * Renders the Intro.js steps. */ renderSteps() { const { enabled, initialStep, steps, onStart } = this.props; if (enabled && steps.length > 0 && !this.isVisible) { this.introJs.start(); this.isVisible = true; this.introJs.goToStepNumber(initialStep + 1); if (onStart) { onStart(this.introJs._currentStep); } } else if (!enabled && this.isVisible) { this.isVisible = false; this.introJs.exit(); } } /** * Renders the component. * @return {null} We do not want to render anything. */ render() { return null; } } exports.default = Steps;