react-joyride
Version:
Create guided tours for your apps
1,822 lines (1,546 loc) • 85.6 kB
JavaScript
import React, { isValidElement } from 'react';
import 'prop-types';
import treeChanges from 'tree-changes';
import is from 'is-lite';
import ReactDOM, { createPortal } from 'react-dom';
import ExecutionEnvironment from 'exenv';
import scroll from 'scroll';
import scrollParent from 'scrollparent';
import { isValidElementType, Element, ForwardRef, typeOf } from 'react-is';
import deepmerge from 'deepmerge';
import Floater from 'react-floater';
function ownKeys(object, enumerableOnly) {
var keys = Object.keys(object);
if (Object.getOwnPropertySymbols) {
var symbols = Object.getOwnPropertySymbols(object);
if (enumerableOnly) {
symbols = symbols.filter(function (sym) {
return Object.getOwnPropertyDescriptor(object, sym).enumerable;
});
}
keys.push.apply(keys, symbols);
}
return keys;
}
function _objectSpread2(target) {
for (var i = 1; i < arguments.length; i++) {
var source = arguments[i] != null ? arguments[i] : {};
if (i % 2) {
ownKeys(Object(source), true).forEach(function (key) {
_defineProperty(target, key, source[key]);
});
} else if (Object.getOwnPropertyDescriptors) {
Object.defineProperties(target, Object.getOwnPropertyDescriptors(source));
} else {
ownKeys(Object(source)).forEach(function (key) {
Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key));
});
}
}
return target;
}
function _classCallCheck(instance, Constructor) {
if (!(instance instanceof Constructor)) {
throw new TypeError("Cannot call a class as a function");
}
}
function _defineProperties(target, props) {
for (var i = 0; i < props.length; i++) {
var descriptor = props[i];
descriptor.enumerable = descriptor.enumerable || false;
descriptor.configurable = true;
if ("value" in descriptor) descriptor.writable = true;
Object.defineProperty(target, descriptor.key, descriptor);
}
}
function _createClass(Constructor, protoProps, staticProps) {
if (protoProps) _defineProperties(Constructor.prototype, protoProps);
if (staticProps) _defineProperties(Constructor, staticProps);
return Constructor;
}
function _defineProperty(obj, key, value) {
if (key in obj) {
Object.defineProperty(obj, key, {
value: value,
enumerable: true,
configurable: true,
writable: true
});
} else {
obj[key] = value;
}
return obj;
}
function _extends() {
_extends = Object.assign || function (target) {
for (var i = 1; i < arguments.length; i++) {
var source = arguments[i];
for (var key in source) {
if (Object.prototype.hasOwnProperty.call(source, key)) {
target[key] = source[key];
}
}
}
return target;
};
return _extends.apply(this, arguments);
}
function _inherits(subClass, superClass) {
if (typeof superClass !== "function" && superClass !== null) {
throw new TypeError("Super expression must either be null or a function");
}
subClass.prototype = Object.create(superClass && superClass.prototype, {
constructor: {
value: subClass,
writable: true,
configurable: true
}
});
if (superClass) _setPrototypeOf(subClass, superClass);
}
function _getPrototypeOf(o) {
_getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) {
return o.__proto__ || Object.getPrototypeOf(o);
};
return _getPrototypeOf(o);
}
function _setPrototypeOf(o, p) {
_setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) {
o.__proto__ = p;
return o;
};
return _setPrototypeOf(o, p);
}
function _isNativeReflectConstruct() {
if (typeof Reflect === "undefined" || !Reflect.construct) return false;
if (Reflect.construct.sham) return false;
if (typeof Proxy === "function") return true;
try {
Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {}));
return true;
} catch (e) {
return false;
}
}
function _objectWithoutPropertiesLoose(source, excluded) {
if (source == null) return {};
var target = {};
var sourceKeys = Object.keys(source);
var key, i;
for (i = 0; i < sourceKeys.length; i++) {
key = sourceKeys[i];
if (excluded.indexOf(key) >= 0) continue;
target[key] = source[key];
}
return target;
}
function _objectWithoutProperties(source, excluded) {
if (source == null) return {};
var target = _objectWithoutPropertiesLoose(source, excluded);
var key, i;
if (Object.getOwnPropertySymbols) {
var sourceSymbolKeys = Object.getOwnPropertySymbols(source);
for (i = 0; i < sourceSymbolKeys.length; i++) {
key = sourceSymbolKeys[i];
if (excluded.indexOf(key) >= 0) continue;
if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue;
target[key] = source[key];
}
}
return target;
}
function _assertThisInitialized(self) {
if (self === void 0) {
throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
}
return self;
}
function _possibleConstructorReturn(self, call) {
if (call && (typeof call === "object" || typeof call === "function")) {
return call;
} else if (call !== void 0) {
throw new TypeError("Derived constructors may only return object or undefined");
}
return _assertThisInitialized(self);
}
function _createSuper(Derived) {
var hasNativeReflectConstruct = _isNativeReflectConstruct();
return function _createSuperInternal() {
var Super = _getPrototypeOf(Derived),
result;
if (hasNativeReflectConstruct) {
var NewTarget = _getPrototypeOf(this).constructor;
result = Reflect.construct(Super, arguments, NewTarget);
} else {
result = Super.apply(this, arguments);
}
return _possibleConstructorReturn(this, result);
};
}
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 STATUS = {
IDLE: 'idle',
READY: 'ready',
WAITING: 'waiting',
RUNNING: 'running',
PAUSED: 'paused',
SKIPPED: 'skipped',
FINISHED: 'finished',
ERROR: 'error'
};
var canUseDOM = ExecutionEnvironment.canUseDOM;
var isReact16 = createPortal !== undefined;
/**
* Get the current browser
*
* @param {string} userAgent
*
* @returns {String}
*/
function getBrowser() {
var userAgent = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : navigator.userAgent;
var browser = userAgent;
if (typeof window === 'undefined') {
browser = 'node';
} else if (document.documentMode) {
browser = 'ie';
} else if (/Edge/.test(userAgent)) {
browser = 'edge';
} // Opera 8.0+
else if (Boolean(window.opera) || userAgent.indexOf(' OPR/') >= 0) {
browser = 'opera';
} // Firefox 1.0+
else if (typeof window.InstallTrigger !== 'undefined') {
browser = 'firefox';
} // Chrome 1+
else if (window.chrome) {
browser = 'chrome';
} // Safari (and Chrome iOS, Firefox iOS)
else if (/(Version\/([0-9._]+).*Safari|CriOS|FxiOS| Mobile\/)/.test(userAgent)) {
browser = 'safari';
}
return browser;
}
/**
* Get the toString Object type
* @param {*} value
* @returns {string}
*/
function getObjectType(value) {
return Object.prototype.toString.call(value).slice(8, -1).toLowerCase();
}
/**
* Get text from React components
*
* @param {*} root
*
* @returns {string}
*/
function getText(root) {
var content = [];
var recurse = function recurse(child) {
/* istanbul ignore else */
if (typeof child === 'string' || typeof child === 'number') {
content.push(child);
} else if (Array.isArray(child)) {
child.forEach(function (c) {
return recurse(c);
});
} else if (child && child.props) {
var children = child.props.children;
if (Array.isArray(children)) {
children.forEach(function (c) {
return recurse(c);
});
} else {
recurse(children);
}
}
};
recurse(root);
return content.join(' ').trim();
}
function hasOwnProperty(value, key) {
return Object.prototype.hasOwnProperty.call(value, key);
}
function hasValidKeys(value, keys) {
if (!is.plainObject(value) || !is.array(keys)) {
return false;
}
return Object.keys(value).every(function (d) {
return keys.indexOf(d) !== -1;
});
}
/**
* Convert hex to RGB
*
* @param {string} hex
* @returns {Array}
*/
function hexToRGB(hex) {
var shorthandRegex = /^#?([a-f\d])([a-f\d])([a-f\d])$/i;
var properHex = hex.replace(shorthandRegex, function (m, r, g, b) {
return r + r + g + g + b + b;
});
var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(properHex);
return result ? [parseInt(result[1], 16), parseInt(result[2], 16), parseInt(result[3], 16)] : [];
}
/**
* Decide if the step shouldn't skip the beacon
* @param {Object} step
*
* @returns {boolean}
*/
function hideBeacon(step) {
return step.disableBeacon || step.placement === 'center';
}
/**
* Compare if two variables are equal
*
* @param {*} left
* @param {*} right
*
* @returns {boolean}
*/
function isEqual(left, right) {
var type;
var hasReactElement = /*#__PURE__*/isValidElement(left) || /*#__PURE__*/isValidElement(right);
var hasUndefined = is.undefined(left) || is.undefined(right);
if (getObjectType(left) !== getObjectType(right) || hasReactElement || hasUndefined) {
return false;
}
if (is.domElement(left)) {
return left.isSameNode(right);
}
if (is.number(left)) {
return left === right;
}
if (is["function"](left)) {
return left.toString() === right.toString();
}
for (var key in left) {
/* istanbul ignore else */
if (hasOwnProperty(left, key)) {
if (typeof left[key] === 'undefined' || typeof right[key] === 'undefined') {
return false;
}
type = getObjectType(left[key]);
if (['object', 'array'].indexOf(type) !== -1 && isEqual(left[key], right[key])) {
continue;
}
if (type === 'function' && isEqual(left[key], right[key])) {
continue;
}
if (left[key] !== right[key]) {
return false;
}
}
}
for (var p in right) {
/* istanbul ignore else */
if (hasOwnProperty(right, p)) {
if (typeof left[p] === 'undefined') {
return false;
}
}
}
return true;
}
/**
* Detect legacy browsers
*
* @returns {boolean}
*/
function isLegacy() {
return !(['chrome', 'safari', 'firefox', 'opera'].indexOf(getBrowser()) !== -1);
}
/**
* Log method calls if debug is enabled
*
* @private
* @param {Object} arg
* @param {string} arg.title - The title the logger was called from
* @param {Object|Array} [arg.data] - The data to be logged
* @param {boolean} [arg.warn] - If true, the message will be a warning
* @param {boolean} [arg.debug] - Nothing will be logged unless debug is true
*/
function log(_ref) {
var title = _ref.title,
data = _ref.data,
_ref$warn = _ref.warn,
warn = _ref$warn === void 0 ? false : _ref$warn,
_ref$debug = _ref.debug,
debug = _ref$debug === void 0 ? false : _ref$debug;
/* eslint-disable no-console */
var logFn = warn ? console.warn || console.error : console.log;
if (debug) {
if (title && data) {
console.groupCollapsed("%creact-joyride: ".concat(title), 'color: #ff0044; font-weight: bold; font-size: 12px;');
if (Array.isArray(data)) {
data.forEach(function (d) {
if (is.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');
}
}
/* eslint-enable */
}
var defaultState = {
action: '',
controlled: false,
index: 0,
lifecycle: LIFECYCLE.INIT,
size: 0,
status: STATUS.IDLE
};
var validKeys = ['action', 'index', 'lifecycle', 'status'];
function createStore(props) {
var store = new Map();
var data = new Map();
var Store = /*#__PURE__*/function () {
function Store() {
var _this = this;
var _ref = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {},
_ref$continuous = _ref.continuous,
continuous = _ref$continuous === void 0 ? false : _ref$continuous,
stepIndex = _ref.stepIndex,
_ref$steps = _ref.steps,
_steps = _ref$steps === void 0 ? [] : _ref$steps;
_classCallCheck(this, Store);
_defineProperty(this, "listener", void 0);
_defineProperty(this, "setSteps", function (steps) {
var _this$getState = _this.getState(),
size = _this$getState.size,
status = _this$getState.status;
var state = {
size: steps.length,
status: status
};
data.set('steps', steps);
if (status === STATUS.WAITING && !size && steps.length) {
state.status = STATUS.RUNNING;
}
_this.setState(state);
});
_defineProperty(this, "addListener", function (listener) {
_this.listener = listener;
});
_defineProperty(this, "update", function (state) {
if (!hasValidKeys(state, validKeys)) {
throw new Error("State is not valid. Valid keys: ".concat(validKeys.join(', ')));
}
_this.setState(_objectSpread2({}, _this.getNextState(_objectSpread2(_objectSpread2(_objectSpread2({}, _this.getState()), state), {}, {
action: state.action || ACTIONS.UPDATE
}), true)));
});
_defineProperty(this, "start", function (nextIndex) {
var _this$getState2 = _this.getState(),
index = _this$getState2.index,
size = _this$getState2.size;
_this.setState(_objectSpread2(_objectSpread2({}, _this.getNextState({
action: ACTIONS.START,
index: is.number(nextIndex) ? nextIndex : index
}, true)), {}, {
status: size ? STATUS.RUNNING : STATUS.WAITING
}));
});
_defineProperty(this, "stop", function () {
var advance = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
var _this$getState3 = _this.getState(),
index = _this$getState3.index,
status = _this$getState3.status;
if ([STATUS.FINISHED, STATUS.SKIPPED].indexOf(status) !== -1) return;
_this.setState(_objectSpread2(_objectSpread2({}, _this.getNextState({
action: ACTIONS.STOP,
index: index + (advance ? 1 : 0)
})), {}, {
status: STATUS.PAUSED
}));
});
_defineProperty(this, "close", function () {
var _this$getState4 = _this.getState(),
index = _this$getState4.index,
status = _this$getState4.status;
if (status !== STATUS.RUNNING) return;
_this.setState(_objectSpread2({}, _this.getNextState({
action: ACTIONS.CLOSE,
index: index + 1
})));
});
_defineProperty(this, "go", function (nextIndex) {
var _this$getState5 = _this.getState(),
controlled = _this$getState5.controlled,
status = _this$getState5.status;
if (controlled || status !== STATUS.RUNNING) return;
var step = _this.getSteps()[nextIndex];
_this.setState(_objectSpread2(_objectSpread2({}, _this.getNextState({
action: ACTIONS.GO,
index: nextIndex
})), {}, {
status: step ? status : STATUS.FINISHED
}));
});
_defineProperty(this, "info", function () {
return _this.getState();
});
_defineProperty(this, "next", function () {
var _this$getState6 = _this.getState(),
index = _this$getState6.index,
status = _this$getState6.status;
if (status !== STATUS.RUNNING) return;
_this.setState(_this.getNextState({
action: ACTIONS.NEXT,
index: index + 1
}));
});
_defineProperty(this, "open", function () {
var _this$getState7 = _this.getState(),
status = _this$getState7.status;
if (status !== STATUS.RUNNING) return;
_this.setState(_objectSpread2({}, _this.getNextState({
action: ACTIONS.UPDATE,
lifecycle: LIFECYCLE.TOOLTIP
})));
});
_defineProperty(this, "prev", function () {
var _this$getState8 = _this.getState(),
index = _this$getState8.index,
status = _this$getState8.status;
if (status !== STATUS.RUNNING) return;
_this.setState(_objectSpread2({}, _this.getNextState({
action: ACTIONS.PREV,
index: index - 1
})));
});
_defineProperty(this, "reset", function () {
var restart = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
var _this$getState9 = _this.getState(),
controlled = _this$getState9.controlled;
if (controlled) return;
_this.setState(_objectSpread2(_objectSpread2({}, _this.getNextState({
action: ACTIONS.RESET,
index: 0
})), {}, {
status: restart ? STATUS.RUNNING : STATUS.READY
}));
});
_defineProperty(this, "skip", function () {
var _this$getState10 = _this.getState(),
status = _this$getState10.status;
if (status !== STATUS.RUNNING) return;
_this.setState({
action: ACTIONS.SKIP,
lifecycle: LIFECYCLE.INIT,
status: STATUS.SKIPPED
});
});
this.setState({
action: ACTIONS.INIT,
controlled: is.number(stepIndex),
continuous: continuous,
index: is.number(stepIndex) ? stepIndex : 0,
lifecycle: LIFECYCLE.INIT,
status: _steps.length ? STATUS.READY : STATUS.IDLE
}, true);
this.setSteps(_steps);
}
_createClass(Store, [{
key: "setState",
value: function setState(nextState) {
var initial = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
var state = this.getState();
var _state$nextState = _objectSpread2(_objectSpread2({}, state), nextState),
action = _state$nextState.action,
index = _state$nextState.index,
lifecycle = _state$nextState.lifecycle,
size = _state$nextState.size,
status = _state$nextState.status;
store.set('action', action);
store.set('index', index);
store.set('lifecycle', lifecycle);
store.set('size', size);
store.set('status', status);
if (initial) {
store.set('controlled', nextState.controlled);
store.set('continuous', nextState.continuous);
}
/* istanbul ignore else */
if (this.listener && this.hasUpdatedState(state)) {
// console.log('▶ ▶ ▶ NEW STATE', this.getState());
this.listener(this.getState());
}
}
}, {
key: "getState",
value: function getState() {
if (!store.size) {
return _objectSpread2({}, defaultState);
}
return {
action: store.get('action') || '',
controlled: store.get('controlled') || false,
index: parseInt(store.get('index'), 10),
lifecycle: store.get('lifecycle') || '',
size: store.get('size') || 0,
status: store.get('status') || ''
};
}
}, {
key: "getNextState",
value: function getNextState(state) {
var force = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
var _this$getState11 = this.getState(),
action = _this$getState11.action,
controlled = _this$getState11.controlled,
index = _this$getState11.index,
size = _this$getState11.size,
status = _this$getState11.status;
var newIndex = is.number(state.index) ? state.index : index;
var nextIndex = controlled && !force ? index : Math.min(Math.max(newIndex, 0), size);
return {
action: state.action || action,
controlled: controlled,
index: nextIndex,
lifecycle: state.lifecycle || LIFECYCLE.INIT,
size: state.size || size,
status: nextIndex === size ? STATUS.FINISHED : state.status || status
};
}
}, {
key: "hasUpdatedState",
value: function hasUpdatedState(oldState) {
var before = JSON.stringify(oldState);
var after = JSON.stringify(this.getState());
return before !== after;
}
}, {
key: "getSteps",
value: function getSteps() {
var steps = data.get('steps');
return Array.isArray(steps) ? steps : [];
}
}, {
key: "getHelpers",
value: function 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
};
}
}]);
return Store;
}();
return new Store(props);
}
function scrollDoc() {
return document.scrollingElement || document.createElement('body');
}
/**
* Find the bounding client rect
*
* @private
* @param {HTMLElement} element - The target element
* @returns {Object}
*/
function getClientRect(element) {
if (!element) {
return {};
}
return element.getBoundingClientRect();
}
/**
* Helper function to get the browser-normalized "document height"
* @returns {Number}
*/
function getDocumentHeight() {
var _document = document,
body = _document.body,
html = _document.documentElement;
if (!body || !html) {
return 0;
}
return Math.max(body.scrollHeight, body.offsetHeight, html.clientHeight, html.scrollHeight, html.offsetHeight);
}
/**
* Find and return the target DOM element based on a step's 'target'.
*
* @private
* @param {string|HTMLElement} element
*
* @returns {HTMLElement|null}
*/
function getElement(element) {
/* istanbul ignore else */
if (typeof element === 'string') {
return document.querySelector(element);
}
return element;
}
/**
* Get computed style property
*
* @param {HTMLElement} el
*
* @returns {Object}
*/
function getStyleComputedProperty(el) {
if (!el || el.nodeType !== 1) {
return {};
}
return getComputedStyle(el);
}
/**
* Get scroll parent with fix
*
* @param {HTMLElement} element
* @param {boolean} skipFix
* @param {boolean} [forListener]
*
* @returns {*}
*/
function getScrollParent(element, skipFix, forListener) {
var parent = scrollParent(element);
if (parent.isSameNode(scrollDoc())) {
if (forListener) {
return document;
}
return scrollDoc();
}
var hasScrolling = parent.scrollHeight > parent.offsetHeight;
/* istanbul ignore else */
if (!hasScrolling && !skipFix) {
parent.style.overflow = 'initial';
return scrollDoc();
}
return parent;
}
/**
* Check if the element has custom scroll parent
*
* @param {HTMLElement} element
* @param {boolean} skipFix
*
* @returns {boolean}
*/
function hasCustomScrollParent(element, skipFix) {
if (!element) return false;
var parent = getScrollParent(element, skipFix);
return !parent.isSameNode(scrollDoc());
}
/**
* Check if the element has custom offset parent
*
* @param {HTMLElement} element
*
* @returns {boolean}
*/
function hasCustomOffsetParent(element) {
return element.offsetParent !== document.body;
}
/**
* Check if an element has fixed/sticky position
* @param {HTMLElement|Node} el
* @param {string} [type]
*
* @returns {boolean}
*/
function hasPosition(el) {
var type = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'fixed';
if (!el || !(el instanceof HTMLElement)) {
return false;
}
var nodeName = el.nodeName;
if (nodeName === 'BODY' || nodeName === 'HTML') {
return false;
}
if (getStyleComputedProperty(el).position === type) {
return true;
}
return hasPosition(el.parentNode, type);
}
/**
* Check if the element is visible
*
* @param {HTMLElement} element
*
* @returns {boolean}
*/
function isElementVisible(element) {
if (!element) return false;
var parentElement = element;
while (parentElement) {
if (parentElement === document.body) break;
/* istanbul ignore else */
if (parentElement instanceof HTMLElement) {
var _getComputedStyle = getComputedStyle(parentElement),
display = _getComputedStyle.display,
visibility = _getComputedStyle.visibility;
if (display === 'none' || visibility === 'hidden') {
return false;
}
}
parentElement = parentElement.parentNode;
}
return true;
}
/**
* Find and return the target DOM element based on a step's 'target'.
*
* @private
* @param {string|HTMLElement} element
* @param {number} offset
* @param {boolean} skipFix
*
* @returns {HTMLElement|undefined}
*/
function getElementPosition(element, offset, skipFix) {
var elementRect = getClientRect(element);
var parent = getScrollParent(element, skipFix);
var hasScrollParent = hasCustomScrollParent(element, skipFix);
var parentTop = 0;
/* istanbul ignore else */
if (parent instanceof HTMLElement) {
parentTop = parent.scrollTop;
}
var top = elementRect.top + (!hasScrollParent && !hasPosition(element) ? parentTop : 0);
return Math.floor(top - offset);
}
/**
* Get the offsetTop of each element up to the body
*
* @param {HTMLElement} element
*
* @returns {number}
*/
function getTopOffset(element) {
if (element instanceof HTMLElement) {
if (element.offsetParent instanceof HTMLElement) {
return getTopOffset(element.offsetParent) + element.offsetTop;
}
return element.offsetTop;
}
return 0;
}
/**
* Get the scrollTop position
*
* @param {HTMLElement} element
* @param {number} offset
* @param {boolean} skipFix
*
* @returns {number}
*/
function getScrollTo(element, offset, skipFix) {
if (!element) {
return 0;
}
var parent = scrollParent(element);
var top = getTopOffset(element);
if (hasCustomScrollParent(element, skipFix) && !hasCustomOffsetParent(element)) {
top -= getTopOffset(parent);
}
return Math.floor(top - offset);
}
/**
* Scroll to position
* @param {number} value
* @param {HTMLElement} element
* @param {number} scrollDuration
* @returns {Promise<*>}
*/
function scrollTo(value) {
var element = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : scrollDoc();
var scrollDuration = arguments.length > 2 ? arguments[2] : undefined;
return new Promise(function (resolve, reject) {
var scrollTop = element.scrollTop;
var limit = value > scrollTop ? value - scrollTop : scrollTop - value;
scroll.top(element, value, {
duration: limit < 100 ? 50 : scrollDuration
}, function (error) {
if (error && error.message !== 'Element already at target scroll position') {
return reject(error);
}
return resolve();
});
});
}
function createChainableTypeChecker(validate) {
function checkType(isRequired, props, propName, componentName, location, propFullName) {
var componentNameSafe = componentName || '<<anonymous>>';
var propFullNameSafe = propFullName || propName;
/* istanbul ignore else */
if (props[propName] == null) {
if (isRequired) {
return new Error("Required ".concat(location, " `").concat(propFullNameSafe, "` was not specified in `").concat(componentNameSafe, "`."));
}
return null;
}
for (var _len = arguments.length, args = new Array(_len > 6 ? _len - 6 : 0), _key = 6; _key < _len; _key++) {
args[_key - 6] = arguments[_key];
}
return validate.apply(void 0, [props, propName, componentNameSafe, location, propFullNameSafe].concat(args));
}
var chainedCheckType = checkType.bind(null, false);
chainedCheckType.isRequired = checkType.bind(null, true);
return chainedCheckType;
}
createChainableTypeChecker(function (props, propName, componentName, location, propFullName) {
var propValue = props[propName];
var Component = propValue;
if (! /*#__PURE__*/React.isValidElement(propValue) && isValidElementType(propValue)) {
var ownProps = {
ref: function ref() {},
step: {}
};
Component = /*#__PURE__*/React.createElement(Component, ownProps);
}
if (is.string(propValue) || is.number(propValue) || !isValidElementType(propValue) || !([Element, ForwardRef].indexOf(typeOf(Component)) !== -1)) {
return new Error("Invalid ".concat(location, " `").concat(propFullName, "` supplied to `").concat(componentName, "`. Expected a React class or forwardRef."));
}
return undefined;
});
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',
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() {
var stepStyles = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
var options = deepmerge(defaultOptions, stepStyles.options || {});
var width = 290;
if (window.innerWidth > 480) {
width = 380;
}
if (options.width) {
if (window.innerWidth < options.width) {
width = window.innerWidth - 30;
} else {
width = options.width; //eslint-disable-line prefer-destructuring
}
}
var overlay = {
bottom: 0,
left: 0,
overflow: 'hidden',
position: 'absolute',
right: 0,
top: 0,
zIndex: options.zIndex
};
var defaultStyles = {
beacon: _objectSpread2(_objectSpread2({}, buttonBase), {}, {
display: '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(".concat(hexToRGB(options.primaryColor).join(','), ", 0.2)"),
border: "2px solid ".concat(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: 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: _objectSpread2(_objectSpread2({}, buttonBase), {}, {
backgroundColor: options.primaryColor,
borderRadius: 4,
color: '#fff'
}),
buttonBack: _objectSpread2(_objectSpread2({}, buttonBase), {}, {
color: options.primaryColor,
marginLeft: 'auto',
marginRight: 5
}),
buttonClose: _objectSpread2(_objectSpread2({}, buttonBase), {}, {
color: options.textColor,
height: 14,
padding: 15,
position: 'absolute',
right: 0,
top: 0,
width: 14
}),
buttonSkip: _objectSpread2(_objectSpread2({}, buttonBase), {}, {
color: options.textColor,
fontSize: 14
}),
overlay: _objectSpread2(_objectSpread2({}, overlay), {}, {
backgroundColor: options.overlayColor,
mixBlendMode: 'hard-light'
}),
overlayLegacy: _objectSpread2({}, overlay),
overlayLegacyCenter: _objectSpread2(_objectSpread2({}, overlay), {}, {
backgroundColor: options.overlayColor
}),
spotlight: _objectSpread2(_objectSpread2({}, spotlight), {}, {
backgroundColor: 'gray'
}),
spotlightLegacy: _objectSpread2(_objectSpread2({}, spotlight), {}, {
boxShadow: "0 0 0 9999px ".concat(options.overlayColor, ", ").concat(options.spotlightShadow)
}),
floaterStyles: {
arrow: {
color: options.arrowColor
},
options: {
zIndex: options.zIndex
}
},
options: options
};
return deepmerge(defaultStyles, stepStyles);
}
var DEFAULTS = {
floaterProps: {
options: {
preventOverflow: {
boundariesElement: 'scrollParent'
}
},
wrapperOptions: {
offset: -18,
position: true
}
},
locale: {
back: 'Back',
close: 'Close',
last: 'Last',
next: 'Next',
open: 'Open the dialog',
skip: 'Skip'
},
step: {
event: 'click',
placement: 'bottom',
offset: 10
}
};
function getTourProps(props) {
var sharedTourProps = ['beaconComponent', 'disableCloseOnEsc', 'disableOverlay', 'disableOverlayClose', 'disableScrolling', 'disableScrollParentFix', 'floaterProps', 'hideBackButton', 'locale', 'showProgress', 'showSkipButton', 'spotlightClicks', 'spotlightPadding', 'styles', 'tooltipComponent'];
return Object.keys(props).filter(function (d) {
return sharedTourProps.indexOf(d) !== -1;
}).reduce(function (acc, i) {
acc[i] = props[i]; //eslint-disable-line react/destructuring-assignment
return acc;
}, {});
}
function getMergedStep(step, props) {
if (!step) return null;
var mergedStep = deepmerge.all([getTourProps(props), DEFAULTS.step, step], {
isMergeableObject: is.plainObject
});
var mergedStyles = getStyles(deepmerge(props.styles || {}, step.styles || {}));
var scrollParent = hasCustomScrollParent(getElement(step.target), mergedStep.disableScrollParentFix);
var floaterProps = deepmerge.all([props.floaterProps || {}, DEFAULTS.floaterProps, mergedStep.floaterProps || {}]); // Set react-floater props
floaterProps.offset = mergedStep.offset;
floaterProps.styles = deepmerge(floaterProps.styles || {}, mergedStyles.floaterStyles || {});
delete mergedStyles.floaterStyles;
floaterProps.offset += props.spotlightPadding || step.spotlightPadding || 0;
if (step.placementBeacon) {
floaterProps.wrapperOptions.placement = step.placementBeacon;
}
if (scrollParent) {
floaterProps.options.preventOverflow.boundariesElement = 'window';
}
return _objectSpread2(_objectSpread2({}, mergedStep), {}, {
locale: deepmerge.all([DEFAULTS.locale, props.locale || {}, mergedStep.locale || {}]),
floaterProps: floaterProps,
styles: mergedStyles
});
}
/**
* Validate if a step is valid
*
* @param {Object} step - A step object
* @param {boolean} debug
*
* @returns {boolean} - True if the step is valid, false otherwise
*/
function validateStep(step) {
var debug = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
if (!is.plainObject(step)) {
log({
title: 'validateStep',
data: 'step must be an object',
warn: true,
debug: debug
});
return false;
}
if (!step.target) {
log({
title: 'validateStep',
data: 'target is missing from the step',
warn: true,
debug: debug
});
return false;
}
return true;
}
/**
* Validate if steps is valid
*
* @param {Array} steps - A steps array
* @param {boolean} debug
*
* @returns {boolean} - True if the steps are valid, false otherwise
*/
function validateSteps(steps) {
var debug = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
if (!is.array(steps)) {
log({
title: 'validateSteps',
data: 'steps must be an array',
warn: true,
debug: debug
});
return false;
}
return steps.every(function (d) {
return validateStep(d, debug);
});
}
var Scope = function Scope(_element) {
var _this = this;
var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
_classCallCheck(this, Scope);
_defineProperty(this, "element", void 0);
_defineProperty(this, "options", void 0);
_defineProperty(this, "canBeTabbed", function (element) {
var tabIndex = element.tabIndex;
if (tabIndex === null || tabIndex < 0) tabIndex = undefined;
var isTabIndexNaN = isNaN(tabIndex);
return !isTabIndexNaN && _this.canHaveFocus(element);
});
_defineProperty(this, "canHaveFocus", function (element) {
var validTabNodes = /input|select|textarea|button|object/;
var nodeName = element.nodeName.toLowerCase();
var res = validTabNodes.test(nodeName) && !element.getAttribute('disabled') || nodeName === 'a' && !!element.getAttribute('href');
return res && _this.isVisible(element);
});
_defineProperty(this, "findValidTabElements", function () {
return [].slice.call(_this.element.querySelectorAll('*'), 0).filter(_this.canBeTabbed);
});
_defineProperty(this, "handleKeyDown", function (e) {
var _this$options$keyCode = _this.options.keyCode,
keyCode = _this$options$keyCode === void 0 ? 9 : _this$options$keyCode;
/* istanbul ignore else */
if (e.keyCode === keyCode) {
_this.interceptTab(e);
}
});
_defineProperty(this, "interceptTab", function (event) {
event.preventDefault();
var elements = _this.findValidTabElements();
var shiftKey = event.shiftKey;
if (!elements.length) {
return;
}
var x = elements.indexOf(document.activeElement);
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();
});
_defineProperty(this, "isHidden", function (element) {
var noSize = element.offsetWidth <= 0 && element.offsetHeight <= 0;
var style = window.getComputedStyle(element);
if (noSize && !element.innerHTML) return true;
return noSize && style.getPropertyValue('overflow') !== 'visible' || style.getPropertyValue('display') === 'none';
});
_defineProperty(this, "isVisible", function (element) {
var parentElement = element;
while (parentElement) {
/* istanbul ignore else */
if (parentElement instanceof HTMLElement) {
if (parentElement === document.body) break;
/* istanbul ignore else */
if (_this.isHidden(parentElement)) return false;
parentElement = parentElement.parentNode;
}
}
return true;
});
_defineProperty(this, "removeScope", function () {
window.removeEventListener('keydown', _this.handleKeyDown);
});
_defineProperty(this, "checkFocus", function (target) {
if (document.activeElement !== target) {
target.focus();
window.requestAnimationFrame(function () {
return _this.checkFocus(target);
});
}
});
_defineProperty(this, "setFocus", function () {
var selector = _this.options.selector;
if (!selector) return;
var target = _this.element.querySelector(selector);
/* istanbul ignore else */
if (target) {
window.requestAnimationFrame(function () {
return _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();
};
var JoyrideBeacon = /*#__PURE__*/function (_React$Component) {
_inherits(JoyrideBeacon, _React$Component);
var _super = _createSuper(JoyrideBeacon);
function JoyrideBeacon(props) {
var _this;
_classCallCheck(this, JoyrideBeacon);
_this = _super.call(this, props);
_defineProperty(_assertThisInitialized(_this), "setBeaconRef", function (c) {
_this.beacon = c;
});
if (!props.beaconComponent) {
var head = document.head || document.getElementsByTagName('head')[0];
var style = document.createElement('style');
var css = "\n @keyframes joyride-beacon-inner {\n 20% {\n opacity: 0.9;\n }\n \n 90% {\n opacity: 0.7;\n }\n }\n \n @keyframes joyride-beacon-outer {\n 0% {\n transform: scale(1);\n }\n \n 45% {\n opacity: 0.7;\n transform: scale(0.75);\n }\n \n 100% {\n opacity: 0.9;\n transform: scale(1);\n }\n }\n ";
style.type = 'text/css';
style.id = 'joyride-beacon-animation';
if (props.nonce !== undefined) {
style.setAttribute('nonce', props.nonce);
}
style.appendChild(document.createTextNode(css));
head.appendChild(style);
}
return _this;
}
_createClass(JoyrideBeacon, [{
key: "componentDidMount",
value: function componentDidMount() {
var _this2 = this;
var shouldFocus = this.props.shouldFocus;
setTimeout(function () {
if (is.domElement(_this2.beacon) && shouldFocus) {
_this2.beacon.focus();
}
}, 0);
}
}, {
key: "componentWillUnmount",
value: function componentWillUnmount() {
var style = document.getElementById('joyride-beacon-animation');
if (style) {
style.parentNode.removeChild(style);
}
}
}, {
key: "render",
value: function render() {
var _this$props = this.props,
beaconComponent = _this$props.beaconComponent,
locale = _this$props.locale,
onClickOrHover = _this$props.onClickOrHover,
styles = _this$props.styles;
var props = {
'aria-label': locale.open,
onClick: onClickOrHover,
onMouseEnter: onClickOrHover,
ref: this.setBeaconRef,
title: locale.open
};
var component;
if (beaconComponent) {
var BeaconComponent = beaconComponent;
component = /*#__PURE__*/React.createElement(BeaconComponent, props);
} else {
component = /*#__PURE__*/React.createElement("button", _extends({
key: "JoyrideBeacon",
className: "react-joyride__beacon",
style: styles.beacon,
type: "button"
}, props), /*#__PURE__*/React.createElement("span", {
style: styles.beaconInner
}), /*#__PURE__*/React.createElement("span", {
style: styles.beaconOuter
}));
}
return component;
}
}]);
return JoyrideBeacon;
}(React.Component);
var JoyrideSpotlight = function JoyrideSpotlight(_ref) {
var styles = _ref.styles;
return /*#__PURE__*/React.createElement("div", {
key: "JoyrideSpotlight",
className: "react-joyride__spotlight",
style: styles
});
};
var _excluded$2 = ["mixBlendMode", "zIndex"];
var JoyrideOverlay = /*#__PURE__*/function (_React$Component) {
_inherits(JoyrideOverlay, _React$Component);
var _super = _createSuper(JoyrideOverlay);
function JoyrideOverlay() {
var _this;
_classCallCheck(this, JoyrideOverlay);
for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
args[_key] = arguments[_key];
}
_this = _super.call.apply(_super, [this].concat(args));
_defineProperty(_assertThisInitialized(_this), "_isMounted", false);
_defineProperty(_assertThisInitialized(_this), "state", {
mouseOverSpotlight: false,
isScrolling: false,
showSpotlight: true
});
_defineProperty(_assertThisInitialized(_this), "handleMouseMove", function (e) {
var mouseOverSpotlight = _this.state.mouseOverSpotlight;
var _this$spotlightStyles = _this.spotlightStyles,
height = _this$spotlightStyles.height,
left = _this$spotlightStyles.left,
position = _this$spotlightStyles.position,
top = _this$spotlightStyles.top,
width = _this$spotlightStyles.width;
var offsetY = position === 'fixed' ? e.clientY : e.pageY;
var offsetX = position === 'fixed' ? e.clientX : e.pageX;
var inSpotlightHeight = offsetY >= top && offsetY <= top + height;
var inSpotlightWidth = offsetX >= left && offsetX <= left + width;
var inSpotlight = inSpotlightWidth && inSpotlightHeight;
if (inSpotlight !== mouseOverSpotlight) {
_this.updateState({
mouseOverSpotlight: inSpotlight
});
}
});
_defineProperty(_assertThisInitialized(_this), "handleScroll", function () {
var target = _this.props.target;
var element = getElement(target);
if (_this.scrollParent !== document) {
var isScrolling = _this.state.isScrolling;
if (!isScrolling) {
_this.updateState({
isScrolling: true,
showSpotlight: false
});
}
clearTimeout(_this.scrollTimeout);
_this.scrollTimeout = setTimeout(function () {
_this.updateState({
isScrolling: false,
showSpotlight: true
});
}, 50);
} else if (hasPosition(element, 'sticky')) {
_this.updateState({});
}
});
_defineProperty(_assertThisInitialized(_this), "handleResize", function () {
clearTimeout(_this.resizeTimeout);
_this.resizeTimeout = setTimeout(function () {
if (!_this._isMounted) {
return;
}
_this.forceUpdate();
}, 100);
});
return _this;
}
_createClass(JoyrideOverlay, [{
key: "componentDidMount",
value: function componentDidMount() {
var _this$props = this.props;
_this$props.debug;
_this$props.disableScrolling;
var disableScrollParentFix = _this$props.disableScrollParentFix,
target = _this$props.target;
var element = getElement(target);
this.scrollParent = getScrollParent(element, disableScrollParentFix, true);
this._isMounted = true;
window.addEventListener('resize', this.handleResize);
}
}, {
key: "componentDidUpdate",
value: function componentDidUpdate(prevProps) {
var _this2 = this;
var _this$props2 = this.props,
lifecycle = _this$props2.lifecycle,
spotlightClicks = _this$props2.spotlightClicks;
var _treeChanges = treeChanges(prevProps, this.props),
changed = _treeChanges.changed;
/* istanbul ignore else */
if (changed('lifecycle', LIFECYCLE.TOOLTIP)) {
this.scrollParent.addEventListener('scroll', this.handleScroll, {
passive: true
});
setTimeout(function () {
var isScrolling = _this2.state.isScrolling;
if (!isScrolling) {
_this2.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);
}
}
}
}, {
key: "componentWillUnmount",
value: function componentWillUnmount() {
this._isMounted = false;
window.removeEventListener('mousemove', this.handleMouseMove);
window.removeEventListener('resize', this.handleResize);
clearTimeout(this.resizeTimeout);
clearTimeout(this.scrollTimeout);
this.scrollParent.removeEventListener('scroll', this.handleScroll);
}
}, {
key: "spotlightStyles",
get: function get() {
var showSpotlight = this.state.showSpotlight;
var _this$props3 = this.props,
disableScrollParentFix = _this$props3.disableScrollParentFix,
spotlightClicks = _this$props3.spotlightClicks,
spotlightPadding = _this$props3.spotlightPadding,
styles = _this$props3.styles,
target = _this$props3.target;
var element = getElement(target);
var elementRect = getClientRect(element);
var isFixedTarget = hasPosition(element);
var top = getElementPosition(element, spotlightPadding, disableScrollParentFix);
return _objectSpread2(_objectSpread2({}, isLegacy() ? styles.spotlightLegacy : styles.spotlight), {}, {
height: Math.round(elementRect.height + spotlightPadding * 2),
left: Math.round(elementRect.left - spotlightPadding),
opacity: showSpotlight ? 1 : 0,
pointerEvents: spotl