UNPKG

flytask-joyride

Version:
1,623 lines (1,595 loc) 66.8 kB
"use strict"; var __create = Object.create; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __getProtoOf = Object.getPrototypeOf; var __hasOwnProp = Object.prototype.hasOwnProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( // If the importer is in node compatibility mode or this is not an ESM // file that has been converted to a CommonJS file using a Babel- // compatible transform (i.e. "__esModule" has not been set), then set // "default" to the CommonJS "module.exports" for node compatibility. isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod )); var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); // src/index.tsx var index_exports = {}; __export(index_exports, { ACTIONS: () => ACTIONS, EVENTS: () => EVENTS, LIFECYCLE: () => LIFECYCLE, ORIGIN: () => ORIGIN, STATUS: () => STATUS, default: () => components_default }); module.exports = __toCommonJS(index_exports); // src/components/index.tsx var React9 = __toESM(require("react")); var import_deep_equal = __toESM(require("@gilbarbara/deep-equal")); var import_is_lite6 = __toESM(require("is-lite")); var import_tree_changes3 = __toESM(require("tree-changes")); // src/modules/dom.ts var import_scroll = __toESM(require("scroll")); var import_scrollparent = __toESM(require("scrollparent")); function canUseDOM() { var _a; return !!(typeof window !== "undefined" && ((_a = window.document) == null ? void 0 : _a.createElement)); } function getClientRect(element) { if (!element) { return null; } return element.getBoundingClientRect(); } function getDocumentHeight(median = false) { const { body, documentElement } = document; if (!body || !documentElement) { return 0; } if (median) { const heights = [ body.scrollHeight, body.offsetHeight, documentElement.clientHeight, documentElement.scrollHeight, documentElement.offsetHeight ].sort((a, b) => a - b); const middle = Math.floor(heights.length / 2); if (heights.length % 2 === 0) { return (heights[middle - 1] + heights[middle]) / 2; } return heights[middle]; } return Math.max( body.scrollHeight, body.offsetHeight, documentElement.clientHeight, documentElement.scrollHeight, documentElement.offsetHeight ); } function getElement(element) { if (typeof element === "string") { try { return document.querySelector(element); } catch (error) { if (process.env.NODE_ENV !== "production") { console.error(error); } return null; } } return element; } function getStyleComputedProperty(el) { if (!el || el.nodeType !== 1) { return null; } return getComputedStyle(el); } function getScrollParent(element, skipFix, forListener) { if (!element) { return scrollDocument(); } const parent = (0, import_scrollparent.default)(element); if (parent) { if (parent.isSameNode(scrollDocument())) { if (forListener) { return document; } return scrollDocument(); } const hasScrolling = parent.scrollHeight > parent.offsetHeight; if (!hasScrolling && !skipFix) { parent.style.overflow = "initial"; return scrollDocument(); } } return parent; } function hasCustomScrollParent(element, skipFix) { if (!element) { return false; } const parent = getScrollParent(element, skipFix); return parent ? !parent.isSameNode(scrollDocument()) : false; } function hasCustomOffsetParent(element) { return element.offsetParent !== document.body; } function hasPosition(el, type = "fixed") { if (!el || !(el instanceof HTMLElement)) { return false; } const { nodeName } = el; const styles = getStyleComputedProperty(el); if (nodeName === "BODY" || nodeName === "HTML") { return false; } if (styles && styles.position === type) { return true; } if (!el.parentNode) { return false; } return hasPosition(el.parentNode, type); } function isElementVisible(element) { var _a; if (!element) { return false; } let parentElement = element; while (parentElement) { if (parentElement === document.body) { break; } if (parentElement instanceof HTMLElement) { const { display, visibility } = getComputedStyle(parentElement); if (display === "none" || visibility === "hidden") { return false; } } parentElement = (_a = parentElement.parentElement) != null ? _a : null; } return true; } function getElementPosition(element, offset, skipFix) { var _a; const elementRect = getClientRect(element); const parent = getScrollParent(element, skipFix); const hasScrollParent = hasCustomScrollParent(element, skipFix); let parentTop = 0; let top = (_a = elementRect == null ? void 0 : elementRect.top) != null ? _a : 0; if (parent instanceof HTMLElement) { parentTop = parent.scrollTop; if (!hasScrollParent && !hasPosition(element)) { top += parentTop; } if (!parent.isSameNode(scrollDocument())) { top += scrollDocument().scrollTop; } } return Math.floor(top - offset); } function getScrollTo(element, offset, skipFix) { var _a; if (!element) { return 0; } const { offsetTop = 0, scrollTop = 0 } = (_a = (0, import_scrollparent.default)(element)) != null ? _a : {}; let top = element.getBoundingClientRect().top + scrollTop; if (!!offsetTop && (hasCustomScrollParent(element, skipFix) || hasCustomOffsetParent(element))) { top -= offsetTop; } const output = Math.floor(top - offset); return output < 0 ? 0 : output; } function scrollDocument() { var _a; return (_a = document.scrollingElement) != null ? _a : document.documentElement; } function scrollTo(value, options) { const { duration, element } = options; return new Promise((resolve, reject) => { const { scrollTop } = element; const limit = value > scrollTop ? value - scrollTop : scrollTop - value; import_scroll.default.top(element, value, { duration: limit < 100 ? 50 : duration }, (error) => { if (error && error.message !== "Element already at target scroll position") { return reject(error); } return resolve(); }); }); } // src/modules/helpers.ts var import_react = require("react"); var import_react_dom = require("react-dom"); var import_is_lite = __toESM(require("is-lite")); // src/literals/index.ts var ACTIONS = { INIT: "init", START: "start", STOP: "stop", RESET: "reset", PREV: "prev", NEXT: "next", GO: "go", CLOSE: "close", SKIP: "skip", UPDATE: "update" }; var EVENTS = { TOUR_START: "tour:start", STEP_BEFORE: "step:before", BEACON: "beacon", TOOLTIP: "tooltip", STEP_AFTER: "step:after", TOUR_END: "tour:end", TOUR_STATUS: "tour:status", TARGET_NOT_FOUND: "error:target_not_found", ERROR: "error" }; var LIFECYCLE = { INIT: "init", READY: "ready", BEACON: "beacon", TOOLTIP: "tooltip", COMPLETE: "complete", ERROR: "error" }; var ORIGIN = { BUTTON_CLOSE: "button_close", BUTTON_PRIMARY: "button_primary", KEYBOARD: "keyboard", OVERLAY: "overlay" }; var STATUS = { IDLE: "idle", READY: "ready", WAITING: "waiting", RUNNING: "running", PAUSED: "paused", SKIPPED: "skipped", FINISHED: "finished", ERROR: "error" }; // src/modules/helpers.ts var isReact16 = import_react_dom.createPortal !== void 0; function getBrowser(userAgent = navigator.userAgent) { let browser = userAgent; if (typeof window === "undefined") { browser = "node"; } else if (document.documentMode) { browser = "ie"; } else if (/Edge/.test(userAgent)) { browser = "edge"; } else if (Boolean(window.opera) || userAgent.includes(" OPR/")) { browser = "opera"; } else if (typeof window.InstallTrigger !== "undefined") { browser = "firefox"; } else if (window.chrome) { browser = "chrome"; } else if (/(Version\/([\d._]+).*Safari|CriOS|FxiOS| Mobile\/)/.test(userAgent)) { browser = "safari"; } return browser; } function getText(root) { const content = []; const recurse = (child) => { if (typeof child === "string" || typeof child === "number") { content.push(child); } else if (Array.isArray(child)) { child.forEach((c) => recurse(c)); } else if ((0, import_react.isValidElement)(child)) { const { children } = child.props; if (Array.isArray(children)) { children.forEach((c) => recurse(c)); } else { recurse(children); } } }; recurse(root); return content.join(" ").trim(); } function hasValidKeys(object, keys) { if (!import_is_lite.default.plainObject(object) || !import_is_lite.default.array(keys)) { return false; } return Object.keys(object).every((d) => keys.includes(d)); } function hexToRGB(hex) { const shorthandRegex = /^#?([\da-f])([\da-f])([\da-f])$/i; const properHex = hex.replace(shorthandRegex, (_m, r, g, b) => r + r + g + g + b + b); const result = /^#?([\da-f]{2})([\da-f]{2})([\da-f]{2})$/i.exec(properHex); return result ? [parseInt(result[1], 16), parseInt(result[2], 16), parseInt(result[3], 16)] : []; } function hideBeacon(step) { return step.disableBeacon || step.placement === "center"; } function isLegacy() { return !["chrome", "safari", "firefox", "opera"].includes(getBrowser()); } function log({ data, debug = false, title, warn = false }) { const logFn = warn ? console.warn || console.error : console.log; if (debug) { if (title && data) { console.groupCollapsed( `%creact-joyride: ${title}`, "color: #ff0044; font-weight: bold; font-size: 12px;" ); if (Array.isArray(data)) { data.forEach((d) => { if (import_is_lite.default.plainObject(d) && d.key) { logFn.apply(console, [d.key, d.value]); } else { logFn.apply(console, [d]); } }); } else { logFn.apply(console, [data]); } console.groupEnd(); } else { console.error("Missing title or data props"); } } } function objectKeys(input) { return Object.keys(input); } function omit(input, ...filter) { if (!import_is_lite.default.plainObject(input)) { throw new TypeError("Expected an object"); } const output = {}; for (const key in input) { if ({}.hasOwnProperty.call(input, key)) { if (!filter.includes(key)) { output[key] = input[key]; } } } return output; } function pick(input, ...filter) { if (!import_is_lite.default.plainObject(input)) { throw new TypeError("Expected an object"); } if (!filter.length) { return input; } const output = {}; for (const key in input) { if ({}.hasOwnProperty.call(input, key)) { if (filter.includes(key)) { output[key] = input[key]; } } } return output; } function shouldScroll(options) { const { isFirstStep, lifecycle, previousLifecycle, scrollToFirstStep, step, target } = options; return !step.disableScrolling && (!isFirstStep || scrollToFirstStep || lifecycle === LIFECYCLE.TOOLTIP) && step.placement !== "center" && (!step.isFixed || !hasPosition(target)) && // fixed steps don't need to scroll previousLifecycle !== lifecycle && [LIFECYCLE.BEACON, LIFECYCLE.TOOLTIP].includes(lifecycle); } // src/modules/step.ts var import_deepmerge2 = __toESM(require("deepmerge")); var import_is_lite2 = __toESM(require("is-lite")); // src/defaults.ts var import_lodash = require("lodash"); var defaultFloaterProps = { options: { preventOverflow: { boundariesElement: "scrollParent" } }, wrapperOptions: { offset: -18, position: true } }; var defaultLocale = { back: "Back", close: "Close", last: "Last", next: "Next", nextLabelWithProgress: "Next (Step {step} of {steps})", open: "Open the dialog", skip: "Skip" }; var defaultStep = { event: "click", placement: "bottom", offset: 10, disableBeacon: false, disableCloseOnEsc: false, disableOverlay: false, disableOverlayClose: false, disableScrollParentFix: false, disableScrolling: false, hideBackButton: false, hideCloseButton: false, hideFooter: false, isFixed: false, locale: defaultLocale, showProgress: false, showSkipButton: false, spotlightClicks: false, spotlightPadding: 10 }; var defaultProps = { continuous: false, debug: false, disableCloseOnEsc: false, disableOverlay: false, disableOverlayClose: false, disableScrolling: false, disableScrollParentFix: false, getHelpers: (0, import_lodash.noop)(), hideBackButton: false, run: true, scrollOffset: 20, scrollDuration: 300, scrollToFirstStep: false, showSkipButton: false, showProgress: false, spotlightClicks: false, spotlightPadding: 10, steps: [] }; // src/styles.ts var import_deepmerge = __toESM(require("deepmerge")); var defaultOptions = { arrowColor: "#fff", backgroundColor: "#fff", beaconSize: 36, overlayColor: "rgba(0, 0, 0, 0.5)", primaryColor: "#f04", spotlightShadow: "0 0 15px rgba(0, 0, 0, 0.5)", textColor: "#333", width: 380, zIndex: 100 }; var buttonBase = { backgroundColor: "transparent", border: 0, borderRadius: 0, color: "#555", cursor: "pointer", fontSize: 16, lineHeight: 1, padding: 8, WebkitAppearance: "none" }; var spotlight = { borderRadius: 4, position: "absolute" }; function getStyles(props, step) { var _a, _b, _c, _d, _e; const { floaterProps, styles } = props; const mergedFloaterProps = (0, import_deepmerge.default)((_a = step.floaterProps) != null ? _a : {}, floaterProps != null ? floaterProps : {}); const mergedStyles = (0, import_deepmerge.default)(styles != null ? styles : {}, (_b = step.styles) != null ? _b : {}); const options = (0, import_deepmerge.default)(defaultOptions, mergedStyles.options || {}); const hideBeacon2 = step.placement === "center" || step.disableBeacon; let { width } = options; if (window.innerWidth > 480) { width = 380; } if ("width" in options) { width = typeof options.width === "number" && window.innerWidth < options.width ? window.innerWidth - 30 : options.width; } const overlay = { bottom: 0, left: 0, overflow: "hidden", position: "absolute", right: 0, top: 0, zIndex: options.zIndex }; const defaultStyles = { beacon: { ...buttonBase, display: hideBeacon2 ? "none" : "inline-block", height: options.beaconSize, position: "relative", width: options.beaconSize, zIndex: options.zIndex }, beaconInner: { animation: "joyride-beacon-inner 1.2s infinite ease-in-out", backgroundColor: options.primaryColor, borderRadius: "50%", display: "block", height: "50%", left: "50%", opacity: 0.7, position: "absolute", top: "50%", transform: "translate(-50%, -50%)", width: "50%" }, beaconOuter: { animation: "joyride-beacon-outer 1.2s infinite ease-in-out", backgroundColor: `rgba(${hexToRGB(options.primaryColor).join(",")}, 0.2)`, border: `2px solid ${options.primaryColor}`, borderRadius: "50%", boxSizing: "border-box", display: "block", height: "100%", left: 0, opacity: 0.9, position: "absolute", top: 0, transformOrigin: "center", width: "100%" }, tooltip: { backgroundColor: options.backgroundColor, borderRadius: 5, boxSizing: "border-box", color: options.textColor, fontSize: 16, maxWidth: "100%", padding: 15, position: "relative", width }, tooltipContainer: { lineHeight: 1.4, textAlign: "center" }, tooltipTitle: { fontSize: 18, margin: 0 }, tooltipContent: { padding: "20px 10px" }, tooltipFooter: { alignItems: "center", display: "flex", justifyContent: "flex-end", marginTop: 15 }, tooltipFooterSpacer: { flex: 1 }, buttonNext: { ...buttonBase, backgroundColor: options.primaryColor, borderRadius: 4, color: "#fff" }, buttonBack: { ...buttonBase, color: options.primaryColor, marginLeft: "auto", marginRight: 5 }, buttonClose: { ...buttonBase, color: options.textColor, height: 14, padding: 15, position: "absolute", right: 0, top: 0, width: 14 }, buttonSkip: { ...buttonBase, color: options.textColor, fontSize: 14 }, overlay: { ...overlay, backgroundColor: options.overlayColor, mixBlendMode: "hard-light" }, overlayLegacy: { ...overlay }, overlayLegacyCenter: { ...overlay, backgroundColor: options.overlayColor }, spotlight: { ...spotlight, backgroundColor: "gray" }, spotlightLegacy: { ...spotlight, boxShadow: `0 0 0 9999px ${options.overlayColor}, ${options.spotlightShadow}` }, floaterStyles: { arrow: { color: (_e = (_d = (_c = mergedFloaterProps == null ? void 0 : mergedFloaterProps.styles) == null ? void 0 : _c.arrow) == null ? void 0 : _d.color) != null ? _e : options.arrowColor }, options: { zIndex: options.zIndex + 100 } }, options }; return (0, import_deepmerge.default)(defaultStyles, mergedStyles); } // src/modules/step.ts function getTourProps(props) { return pick( props, "beaconComponent", "disableCloseOnEsc", "disableOverlay", "disableOverlayClose", "disableScrolling", "disableScrollParentFix", "floaterProps", "hideBackButton", "hideCloseButton", "locale", "showProgress", "showSkipButton", "spotlightClicks", "spotlightPadding", "styles", "tooltipComponent" ); } function getMergedStep(props, currentStep) { var _a, _b, _c, _d, _e, _f; const step = currentStep != null ? currentStep : {}; const mergedStep = import_deepmerge2.default.all([defaultStep, getTourProps(props), step], { isMergeableObject: import_is_lite2.default.plainObject }); const mergedStyles = getStyles(props, mergedStep); const scrollParent2 = hasCustomScrollParent( getElement(mergedStep.target), mergedStep.disableScrollParentFix ); const floaterProps = import_deepmerge2.default.all([ defaultFloaterProps, (_a = props.floaterProps) != null ? _a : {}, (_b = mergedStep.floaterProps) != null ? _b : {} ]); floaterProps.offset = mergedStep.offset; floaterProps.styles = (0, import_deepmerge2.default)((_c = floaterProps.styles) != null ? _c : {}, mergedStyles.floaterStyles); floaterProps.offset += (_e = (_d = props.spotlightPadding) != null ? _d : mergedStep.spotlightPadding) != null ? _e : 0; if (mergedStep.placementBeacon && floaterProps.wrapperOptions) { floaterProps.wrapperOptions.placement = mergedStep.placementBeacon; } if (scrollParent2 && floaterProps.options.preventOverflow) { floaterProps.options.preventOverflow.boundariesElement = "window"; } return { ...mergedStep, locale: import_deepmerge2.default.all([defaultLocale, (_f = props.locale) != null ? _f : {}, mergedStep.locale || {}]), floaterProps, styles: omit(mergedStyles, "floaterStyles") }; } function validateStep(step, debug = false) { if (!import_is_lite2.default.plainObject(step)) { log({ title: "validateStep", data: "step must be an object", warn: true, debug }); return false; } if (!step.target) { log({ title: "validateStep", data: "target is missing from the step", warn: true, debug }); return false; } return true; } function validateSteps(steps, debug = false) { if (!import_is_lite2.default.array(steps)) { log({ title: "validateSteps", data: "steps must be an array", warn: true, debug }); return false; } return steps.every((d) => validateStep(d, debug)); } // src/modules/store.ts var import_is_lite3 = __toESM(require("is-lite")); var defaultState = { action: "init", controlled: false, index: 0, lifecycle: LIFECYCLE.INIT, origin: null, size: 0, status: STATUS.IDLE }; var validKeys = objectKeys(omit(defaultState, "controlled", "size")); var Store = class { constructor(options) { this.data = /* @__PURE__ */ new Map(); this.store = /* @__PURE__ */ new Map(); this.addListener = (listener) => { this.listener = listener; }; this.setSteps = (steps) => { const { size, status } = this.getState(); const state = { size: steps.length, status }; this.data.set("steps", steps); if (status === STATUS.WAITING && !size && steps.length) { state.status = STATUS.RUNNING; } this.setState(state); }; this.getPopper = (name) => { if (name === "beacon") { return this.beaconPopper; } return this.tooltipPopper; }; this.setPopper = (name, popper) => { if (name === "beacon") { this.beaconPopper = popper; } else { this.tooltipPopper = popper; } }; this.cleanupPoppers = () => { this.beaconPopper = null; this.tooltipPopper = null; }; this.close = (origin = null) => { const { index, status } = this.getState(); if (status !== STATUS.RUNNING) { return; } this.setState({ ...this.getNextState({ action: ACTIONS.CLOSE, index: index + 1, origin }) }); }; this.go = (nextIndex) => { const { controlled, status } = this.getState(); if (controlled || status !== STATUS.RUNNING) { return; } const step = this.getSteps()[nextIndex]; this.setState({ ...this.getNextState({ action: ACTIONS.GO, index: nextIndex }), status: step ? status : STATUS.FINISHED }); }; this.info = () => this.getState(); this.next = () => { const { index, status } = this.getState(); if (status !== STATUS.RUNNING) { return; } this.setState(this.getNextState({ action: ACTIONS.NEXT, index: index + 1 })); }; this.open = () => { const { status } = this.getState(); if (status !== STATUS.RUNNING) { return; } this.setState({ ...this.getNextState({ action: ACTIONS.UPDATE, lifecycle: LIFECYCLE.TOOLTIP }) }); }; this.prev = () => { const { index, status } = this.getState(); if (status !== STATUS.RUNNING) { return; } this.setState({ ...this.getNextState({ action: ACTIONS.PREV, index: index - 1 }) }); }; this.reset = (restart = false) => { const { controlled } = this.getState(); if (controlled) { return; } this.setState({ ...this.getNextState({ action: ACTIONS.RESET, index: 0 }), status: restart ? STATUS.RUNNING : STATUS.READY }); }; this.skip = () => { const { status } = this.getState(); if (status !== STATUS.RUNNING) { return; } this.setState({ action: ACTIONS.SKIP, lifecycle: LIFECYCLE.INIT, status: STATUS.SKIPPED }); }; this.start = (nextIndex) => { const { index, size } = this.getState(); this.setState({ ...this.getNextState( { action: ACTIONS.START, index: import_is_lite3.default.number(nextIndex) ? nextIndex : index }, true ), status: size ? STATUS.RUNNING : STATUS.WAITING }); }; this.stop = (advance = false) => { const { index, status } = this.getState(); if ([STATUS.FINISHED, STATUS.SKIPPED].includes(status)) { return; } this.setState({ ...this.getNextState({ action: ACTIONS.STOP, index: index + (advance ? 1 : 0) }), status: STATUS.PAUSED }); }; this.update = (state) => { var _a, _b; if (!hasValidKeys(state, validKeys)) { throw new Error(`State is not valid. Valid keys: ${validKeys.join(", ")}`); } this.setState({ ...this.getNextState( { ...this.getState(), ...state, action: (_a = state.action) != null ? _a : ACTIONS.UPDATE, origin: (_b = state.origin) != null ? _b : null }, true ) }); }; const { continuous = false, stepIndex, steps = [] } = options != null ? options : {}; this.setState( { action: ACTIONS.INIT, controlled: import_is_lite3.default.number(stepIndex), continuous, index: import_is_lite3.default.number(stepIndex) ? stepIndex : 0, lifecycle: LIFECYCLE.INIT, origin: null, status: steps.length ? STATUS.READY : STATUS.IDLE }, true ); this.beaconPopper = null; this.tooltipPopper = null; this.listener = null; this.setSteps(steps); } getState() { if (!this.store.size) { return { ...defaultState }; } return { action: this.store.get("action") || "", controlled: this.store.get("controlled") || false, index: parseInt(this.store.get("index"), 10), lifecycle: this.store.get("lifecycle") || "", origin: this.store.get("origin") || null, size: this.store.get("size") || 0, status: this.store.get("status") || "" }; } getNextState(state, force = false) { var _a, _b, _c, _d, _e; const { action, controlled, index, size, status } = this.getState(); const newIndex = import_is_lite3.default.number(state.index) ? state.index : index; const nextIndex = controlled && !force ? index : Math.min(Math.max(newIndex, 0), size); return { action: (_a = state.action) != null ? _a : action, controlled, index: nextIndex, lifecycle: (_b = state.lifecycle) != null ? _b : LIFECYCLE.INIT, origin: (_c = state.origin) != null ? _c : null, size: (_d = state.size) != null ? _d : size, status: nextIndex === size ? STATUS.FINISHED : (_e = state.status) != null ? _e : status }; } getSteps() { const steps = this.data.get("steps"); return Array.isArray(steps) ? steps : []; } hasUpdatedState(oldState) { const before = JSON.stringify(oldState); const after = JSON.stringify(this.getState()); return before !== after; } setState(nextState, initial = false) { const state = this.getState(); const { action, index, lifecycle, origin = null, size, status } = { ...state, ...nextState }; this.store.set("action", action); this.store.set("index", index); this.store.set("lifecycle", lifecycle); this.store.set("origin", origin); this.store.set("size", size); this.store.set("status", status); if (initial) { this.store.set("controlled", nextState.controlled); this.store.set("continuous", nextState.continuous); } if (this.listener && this.hasUpdatedState(state)) { this.listener(this.getState()); } } getHelpers() { return { close: this.close, go: this.go, info: this.info, next: this.next, open: this.open, prev: this.prev, reset: this.reset, skip: this.skip }; } }; function createStore(options) { return new Store(options); } // src/components/Overlay.tsx var React2 = __toESM(require("react")); var import_tree_changes = __toESM(require("tree-changes")); // src/components/Spotlight.tsx var React = __toESM(require("react")); function JoyrideSpotlight({ styles }) { return /* @__PURE__ */ React.createElement( "div", { key: "JoyrideSpotlight", className: "react-joyride__spotlight", "data-test-id": "spotlight", style: styles } ); } var Spotlight_default = JoyrideSpotlight; // src/components/Overlay.tsx var JoyrideOverlay = class extends React2.Component { constructor() { super(...arguments); this.isActive = false; this.state = { isScrolling: false, mouseOverSpotlight: false, showSpotlight: true }; this.hideSpotlight = () => { const { continuous, disableOverlay, lifecycle } = this.props; const hiddenLifecycles = [ LIFECYCLE.INIT, LIFECYCLE.BEACON, LIFECYCLE.COMPLETE, LIFECYCLE.ERROR ]; return disableOverlay || (continuous ? hiddenLifecycles.includes(lifecycle) : lifecycle !== LIFECYCLE.TOOLTIP); }; this.handleMouseMove = (event) => { const { mouseOverSpotlight } = this.state; const { height, left, position, top, width } = this.spotlightStyles; const offsetY = position === "fixed" ? event.clientY : event.pageY; const offsetX = position === "fixed" ? event.clientX : event.pageX; const inSpotlightHeight = offsetY >= top && offsetY <= top + height; const inSpotlightWidth = offsetX >= left && offsetX <= left + width; const inSpotlight = inSpotlightWidth && inSpotlightHeight; if (inSpotlight !== mouseOverSpotlight) { this.updateState({ mouseOverSpotlight: inSpotlight }); } }; this.handleScroll = () => { const { target } = this.props; const element = getElement(target); if (this.scrollParent !== document) { const { isScrolling } = this.state; if (!isScrolling) { this.updateState({ isScrolling: true, showSpotlight: false }); } clearTimeout(this.scrollTimeout); this.scrollTimeout = window.setTimeout(() => { this.updateState({ isScrolling: false, showSpotlight: true }); }, 50); } else if (hasPosition(element, "sticky")) { this.updateState({}); } }; this.handleResize = () => { clearTimeout(this.resizeTimeout); this.resizeTimeout = window.setTimeout(() => { if (!this.isActive) { return; } this.forceUpdate(); }, 100); }; } componentDidMount() { const { debug, disableScrolling, disableScrollParentFix = false, target } = this.props; const element = getElement(target); this.scrollParent = getScrollParent(element != null ? element : document.body, disableScrollParentFix, true); this.isActive = true; if (process.env.NODE_ENV !== "production") { if (!disableScrolling && hasCustomScrollParent(element, true)) { log({ title: "step has a custom scroll parent and can cause trouble with scrolling", data: [{ key: "parent", value: this.scrollParent }], debug }); } } window.addEventListener("resize", this.handleResize); } componentDidUpdate(previousProps) { var _a; const { lifecycle, spotlightClicks } = this.props; const { changed } = (0, import_tree_changes.default)(previousProps, this.props); if (changed("lifecycle", LIFECYCLE.TOOLTIP)) { (_a = this.scrollParent) == null ? void 0 : _a.addEventListener("scroll", this.handleScroll, { passive: true }); setTimeout(() => { const { isScrolling } = this.state; if (!isScrolling) { this.updateState({ showSpotlight: true }); } }, 100); } if (changed("spotlightClicks") || changed("disableOverlay") || changed("lifecycle")) { if (spotlightClicks && lifecycle === LIFECYCLE.TOOLTIP) { window.addEventListener("mousemove", this.handleMouseMove, false); } else if (lifecycle !== LIFECYCLE.TOOLTIP) { window.removeEventListener("mousemove", this.handleMouseMove); } } } componentWillUnmount() { var _a; this.isActive = false; window.removeEventListener("mousemove", this.handleMouseMove); window.removeEventListener("resize", this.handleResize); clearTimeout(this.resizeTimeout); clearTimeout(this.scrollTimeout); (_a = this.scrollParent) == null ? void 0 : _a.removeEventListener("scroll", this.handleScroll); } get overlayStyles() { const { mouseOverSpotlight } = this.state; const { disableOverlayClose, placement, styles } = this.props; let baseStyles = styles.overlay; if (isLegacy()) { baseStyles = placement === "center" ? styles.overlayLegacyCenter : styles.overlayLegacy; } return { cursor: disableOverlayClose ? "default" : "pointer", height: getDocumentHeight(), pointerEvents: mouseOverSpotlight ? "none" : "auto", ...baseStyles }; } get spotlightStyles() { var _a, _b, _c; const { showSpotlight } = this.state; const { disableScrollParentFix = false, spotlightClicks, spotlightPadding = 0, styles, target } = this.props; const element = getElement(target); const elementRect = getClientRect(element); const isFixedTarget = hasPosition(element); const top = getElementPosition(element, spotlightPadding, disableScrollParentFix); return { ...isLegacy() ? styles.spotlightLegacy : styles.spotlight, height: Math.round(((_a = elementRect == null ? void 0 : elementRect.height) != null ? _a : 0) + spotlightPadding * 2), left: Math.round(((_b = elementRect == null ? void 0 : elementRect.left) != null ? _b : 0) - spotlightPadding), opacity: showSpotlight ? 1 : 0, pointerEvents: spotlightClicks ? "none" : "auto", position: isFixedTarget ? "fixed" : "absolute", top, transition: "opacity 0.2s", width: Math.round(((_c = elementRect == null ? void 0 : elementRect.width) != null ? _c : 0) + spotlightPadding * 2) }; } updateState(state) { if (!this.isActive) { return; } this.setState((previousState) => ({ ...previousState, ...state })); } render() { const { showSpotlight } = this.state; const { onClickOverlay, placement } = this.props; const { hideSpotlight, overlayStyles, spotlightStyles } = this; if (hideSpotlight()) { return null; } let spotlight2 = placement !== "center" && showSpotlight && /* @__PURE__ */ React2.createElement(Spotlight_default, { styles: spotlightStyles }); if (getBrowser() === "safari") { const { mixBlendMode, zIndex, ...safariOverlay } = overlayStyles; spotlight2 = /* @__PURE__ */ React2.createElement("div", { style: { ...safariOverlay } }, spotlight2); delete overlayStyles.backgroundColor; } return /* @__PURE__ */ React2.createElement( "div", { className: "react-joyride__overlay", "data-test-id": "overlay", onClick: onClickOverlay, role: "presentation", style: overlayStyles }, spotlight2 ); } }; // src/components/Portal.tsx var React3 = __toESM(require("react")); var ReactDOM = __toESM(require("react-dom")); var import_client = require("react-dom/client"); var JoyridePortal = class extends React3.Component { constructor() { super(...arguments); this.node = null; this.rootNode = null; } componentDidMount() { const { id } = this.props; if (!canUseDOM()) { return; } this.node = document.createElement("div"); this.node.id = id; document.body.appendChild(this.node); if (!isReact16) { throw new Error("react-joyride requires React 16.3 or later."); } this.rootNode = (0, import_client.createRoot)(this.node); this.rootNode.render(this.props.children); } componentDidUpdate() { if (!canUseDOM()) { return; } if (!isReact16) { throw new Error("react-joyride requires React 16.3 or later."); } } componentWillUnmount() { var _a; if (!canUseDOM() || !this.node) { return; } if (!isReact16) { (_a = this.rootNode) == null ? void 0 : _a.unmount(); } if (this.node.parentNode === document.body) { document.body.removeChild(this.node); this.node = null; } } renderReact16() { if (!canUseDOM() || !isReact16 || !this.node) { return null; } const { children } = this.props; return ReactDOM.createPortal(children, this.node); } render() { if (!isReact16) { return null; } return this.renderReact16(); } }; // src/components/Step.tsx var React8 = __toESM(require("react")); var import_react_floater = __toESM(require("react-floater")); var import_is_lite5 = __toESM(require("is-lite")); var import_tree_changes2 = __toESM(require("tree-changes")); // src/modules/scope.ts var Scope = class { constructor(element, options) { this.canBeTabbed = (element) => { const { tabIndex } = element; if (tabIndex === null || tabIndex < 0) { return false; } return this.canHaveFocus(element); }; this.canHaveFocus = (element) => { const validTabNodes = /input|select|textarea|button|object/; const nodeName = element.nodeName.toLowerCase(); const isValid = validTabNodes.test(nodeName) && !element.getAttribute("disabled") || nodeName === "a" && !!element.getAttribute("href"); return isValid && this.isVisible(element); }; this.findValidTabElements = () => [].slice.call(this.element.querySelectorAll("*"), 0).filter(this.canBeTabbed); this.handleKeyDown = (event) => { const { code = "Tab" } = this.options; if (event.code === code) { this.interceptTab(event); } }; this.interceptTab = (event) => { event.preventDefault(); const elements = this.findValidTabElements(); const { shiftKey } = event; if (!elements.length) { return; } let x = document.activeElement ? elements.indexOf(document.activeElement) : 0; if (x === -1 || !shiftKey && x + 1 === elements.length) { x = 0; } else if (shiftKey && x === 0) { x = elements.length - 1; } else { x += shiftKey ? -1 : 1; } elements[x].focus(); }; // eslint-disable-next-line class-methods-use-this this.isHidden = (element) => { const noSize = element.offsetWidth <= 0 && element.offsetHeight <= 0; const style = window.getComputedStyle(element); if (noSize && !element.innerHTML) { return true; } return noSize && style.getPropertyValue("overflow") !== "visible" || style.getPropertyValue("display") === "none"; }; this.isVisible = (element) => { let parentElement = element; while (parentElement) { if (parentElement instanceof HTMLElement) { if (parentElement === document.body) { break; } if (this.isHidden(parentElement)) { return false; } parentElement = parentElement.parentNode; } } return true; }; this.removeScope = () => { window.removeEventListener("keydown", this.handleKeyDown); }; this.checkFocus = (target) => { if (document.activeElement !== target) { target.focus(); window.requestAnimationFrame(() => this.checkFocus(target)); } }; this.setFocus = () => { const { selector } = this.options; if (!selector) { return; } const target = this.element.querySelector(selector); if (target) { window.requestAnimationFrame(() => this.checkFocus(target)); } }; if (!(element instanceof HTMLElement)) { throw new TypeError("Invalid parameter: element must be an HTMLElement"); } this.element = element; this.options = options; window.addEventListener("keydown", this.handleKeyDown, false); this.setFocus(); } }; // src/components/Beacon.tsx var React4 = __toESM(require("react")); var import_react_innertext = __toESM(require("react-innertext")); var import_is_lite4 = __toESM(require("is-lite")); var JoyrideBeacon = class extends React4.Component { constructor(props) { super(props); this.beacon = null; this.setBeaconRef = (c) => { this.beacon = c; }; if (props.beaconComponent) { return; } const head = document.head || document.getElementsByTagName("head")[0]; const style = document.createElement("style"); style.id = "joyride-beacon-animation"; if (props.nonce) { style.setAttribute("nonce", props.nonce); } const css = ` @keyframes joyride-beacon-inner { 20% { opacity: 0.9; } 90% { opacity: 0.7; } } @keyframes joyride-beacon-outer { 0% { transform: scale(1); } 45% { opacity: 0.7; transform: scale(0.75); } 100% { opacity: 0.9; transform: scale(1); } } `; style.appendChild(document.createTextNode(css)); head.appendChild(style); } componentDidMount() { const { shouldFocus } = this.props; if (process.env.NODE_ENV !== "production") { if (!import_is_lite4.default.domElement(this.beacon)) { console.warn("beacon is not a valid DOM element"); } } setTimeout(() => { if (import_is_lite4.default.domElement(this.beacon) && shouldFocus) { this.beacon.focus(); } }, 0); } componentWillUnmount() { const style = document.getElementById("joyride-beacon-animation"); if (style == null ? void 0 : style.parentNode) { style.parentNode.removeChild(style); } } render() { const { beaconComponent, continuous, index, isLastStep, locale, onClickOrHover, size, step, styles } = this.props; const title = import_is_lite4.default.string(locale.open) ? locale.open : (0, import_react_innertext.default)(locale.open); const sharedProps = { "aria-label": title, onClick: onClickOrHover, onMouseEnter: onClickOrHover, ref: this.setBeaconRef, title }; let component; if (beaconComponent) { const BeaconComponent = beaconComponent; component = /* @__PURE__ */ React4.createElement( BeaconComponent, { continuous, index, isLastStep, size, step, ...sharedProps } ); } else { component = /* @__PURE__ */ React4.createElement( "button", { key: "JoyrideBeacon", className: "react-joyride__beacon", "data-test-id": "button-beacon", style: styles.beacon, type: "button", ...sharedProps }, /* @__PURE__ */ React4.createElement("span", { style: styles.beaconInner }), /* @__PURE__ */ React4.createElement("span", { style: styles.beaconOuter }) ); } return component; } }; // src/components/Tooltip/index.tsx var React7 = __toESM(require("react")); // src/components/Tooltip/Container.tsx var React6 = __toESM(require("react")); // src/components/Tooltip/CloseButton.tsx var import_react2 = __toESM(require("react")); function JoyrideTooltipCloseButton({ styles, ...props }) { const { color, height, width, ...style } = styles; return /* @__PURE__ */ import_react2.default.createElement("button", { style, type: "button", ...props }, /* @__PURE__ */ import_react2.default.createElement( "svg", { height: typeof height === "number" ? `${height}px` : height, preserveAspectRatio: "xMidYMid", version: "1.1", viewBox: "0 0 18 18", width: typeof width === "number" ? `${width}px` : width, xmlns: "http://www.w3.org/2000/svg" }, /* @__PURE__ */ import_react2.default.createElement("g", null, /* @__PURE__ */ import_react2.default.createElement( "path", { d: "M8.13911129,9.00268191 L0.171521827,17.0258467 C-0.0498027049,17.248715 -0.0498027049,17.6098394 0.171521827,17.8327545 C0.28204354,17.9443526 0.427188206,17.9998706 0.572051765,17.9998706 C0.71714958,17.9998706 0.862013139,17.9443526 0.972581703,17.8327545 L9.0000937,9.74924618 L17.0276057,17.8327545 C17.1384085,17.9443526 17.2832721,17.9998706 17.4281356,17.9998706 C17.5729992,17.9998706 17.718097,17.9443526 17.8286656,17.8327545 C18.0499901,17.6098862 18.0499901,17.2487618 17.8286656,17.0258467 L9.86135722,9.00268191 L17.8340066,0.973848225 C18.0553311,0.750979934 18.0553311,0.389855532 17.8340066,0.16694039 C17.6126821,-0.0556467968 17.254037,-0.0556467968 17.0329467,0.16694039 L9.00042166,8.25611765 L0.967006424,0.167268345 C0.745681892,-0.0553188426 0.387317931,-0.0553188426 0.165993399,0.167268345 C-0.0553311331,0.390136635 -0.0553311331,0.751261038 0.165993399,0.974176179 L8.13920499,9.00268191 L8.13911129,9.00268191 Z", fill: color } )) )); } var CloseButton_default = JoyrideTooltipCloseButton; // src/components/Tooltip/Container.tsx function JoyrideTooltipContainer(props) { const { backProps, closeProps, index, isLastStep, primaryProps, skipProps, step, tooltipProps } = props; const { content, hideBackButton, hideCloseButton, hideFooter, showSkipButton, styles, title } = step; const output = { primary: primaryProps.title }; if (output.primary) { output.primary = /* @__PURE__ */ React6.createElement( "button", { "data-test-id": "button-primary", style: styles.buttonNext, type: "button", ...primaryProps }, output.primary ); } if (showSkipButton && !isLastStep) { output.skip = /* @__PURE__ */ React6.createElement( "button", { "aria-live": "off", "data-test-id": "button-skip", style: styles.buttonSkip, type: "button", ...skipProps }, skipProps.title ); } if (!hideBackButton && index > 0) { output.back = /* @__PURE__ */ React6.createElement("button", { "data-test-id": "button-back", style: styles.buttonBack, type: "button", ...backProps }, backProps.title); } output.close = !hideCloseButton && /* @__PURE__ */ React6.createElement(CloseButton_default, { "data-test-id": "button-close", styles: styles.buttonClose, ...closeProps }); return /* @__PURE__ */ React6.createElement( "div", { key: "JoyrideTooltip", "aria-label": getText(title) || getText(content), className: "react-joyride__tooltip", style: styles.tooltip, ...tooltipProps }, /* @__PURE__ */ React6.createElement("div", { style: styles.tooltipContainer }, title && /* @__PURE__ */ React6.createElement("h1", { "aria-label": getText(title), style: styles.tooltipTitle }, title), /* @__PURE__ */ React6.createElement("div", { style: styles.tooltipContent }, content)), !hideFooter && /* @__PURE__ */ React6.createElement("div", { style: styles.tooltipFooter }, /* @__PURE__ */ React6.createElement("div", { style: styles.tooltipFooterSpacer }, output.skip), output.back, output.primary), output.close ); } var Container_default = JoyrideTooltipContainer; // src/components/Tooltip/index.tsx var JoyrideTooltip = class extends React7.Component { constructor() { super(...arguments); this.handleClickBack = (event) => { event.preventDefault(); const { helpers } = this.props; helpers.prev(); }; this.handleClickClose = (event) => { event.preventDefault(); const { helpers } = this.props; helpers.close("button_close"); }; this.handleClickPrimary = (event) => { event.preventDefault(); const { continuous, helpers } = this.props; if (!continuous) { helpers.close("button_primary"); return; } helpers.next(); }; this.handleClickSkip = (event) => { event.preventDefault(); const { helpers } = this.props; helpers.skip(); }; this.getElementsProps = () => { const { continuous, index, isLastStep, setTooltipRef, size, step } = this.props; const back = getText(step.locale.back); const close = getText(step.locale.close); const last = getText(step.locale.last); const next = getText(step.locale.next); const skip = getText(step.locale.skip); let primaryLabel = close; let primaryText = close; if (continuous) { primaryLabel = next; primaryText = next; if (step.showProgress && !isLastStep) { primaryLabel = getText(step.locale.nextLabelWithProgress).replace("{step}", String(index + 1)).replace("{steps}", String(size)); primaryText = `${next} (${index + 1}/${size})`; } if (isLastStep) { primaryLabel = last; primaryText = last; } } return { backProps: { "aria-label": back, "data-action": "back", onClick: this.handleClickBack, role: "button", title: back }, closeProps: { "aria-label": close, "data-action": "close", onClick: this.handleClickClose, role: "button", title: close }, primaryProps: { "aria-label": primaryLabel, "data-action": "primary", onClick: this.handleClickPrimary, role: "button", title: primaryText }, skipProps: { "aria-label": skip, "data-action": "skip",