xstate-helpers
Version:
A collection of helpers for XState (with React).
164 lines (142 loc) • 4.58 kB
JavaScript
import React from 'react';
import ReactDOM from 'react-dom';
const LOCAL_STORAGE_KEY_IS_ENABLED = 'xstate-helpers::isEnabled';
const LOCAL_STORAGE_KEY_OVERRIDE_OPTIONS = 'xstate-helpers::overrideOptions'; // declare global {
const XStateInspectLoader = ({
children,
initialIsEnabled = false,
options,
wrapperElement,
forceEnabled,
styles,
stylesIframe
}) => {
const [loading, setLoading] = React.useState(true);
const [overrideOptions, _setOverrideOptions] = React.useState(() => getItem(LOCAL_STORAGE_KEY_OVERRIDE_OPTIONS, {}));
const setOverrideOptions = value => {
_setOverrideOptions(value);
setItem(LOCAL_STORAGE_KEY_OVERRIDE_OPTIONS, value);
};
const [isEnabled, _setIsEnabled] = React.useState(() => forceEnabled != null ? forceEnabled : getItem(LOCAL_STORAGE_KEY_IS_ENABLED, initialIsEnabled));
const setIsEnabled = value => {
_setIsEnabled(value);
setItem(LOCAL_STORAGE_KEY_IS_ENABLED, value);
};
React.useEffect(() => {
if (forceEnabled !== undefined) {
setIsEnabled(forceEnabled);
}
}, [forceEnabled]);
const optionsRef = React.useRef(options);
React.useEffect(() => {
// expose an interface for setting open/closed directly on console
const XStateInspector = {
enable: () => {
setIsEnabled(true);
},
disable: () => {
setIsEnabled(false);
},
overrideOptions: (overrideOptions = {}) => {
setOverrideOptions(overrideOptions);
return { ...optionsRef.current,
...overrideOptions
};
}
};
window.XStateInspector = XStateInspector;
}, []);
const finalOptions = { ...options,
...overrideOptions
};
React.useEffect(() => {
let active = true; // keep track if this effect was cleaned up
import('@xstate/inspect').then(({
inspect
}) => {
if (!active || !isEnabled) {
return;
}
if (wrapperElement) {
if (typeof wrapperElement === 'string') {
// eslint-disable-next-line
wrapperElement = document.querySelector(wrapperElement);
if (!wrapperElement) {
console.error('Cannot initialize XStateInspectorLoader, invalid wrapperElement selector given');
return;
}
}
} else {
// no wrapper given, we make our own
wrapperElement = document.createElement('div');
document.body.insertBefore(wrapperElement, document.body.firstChild);
}
ReactDOM.render( /*#__PURE__*/React.createElement(XStateInspector, {
styles,
stylesIframe,
inspect,
options: finalOptions
}, null), wrapperElement);
setLoading(false);
});
return () => {
active = false;
if (wrapperElement && typeof wrapperElement !== 'string') {
ReactDOM.unmountComponentAtNode(wrapperElement);
}
};
}, [isEnabled, JSON.stringify(finalOptions)]);
return isEnabled && loading ? null : /*#__PURE__*/React.createElement(React.Fragment, null, children);
};
const defaultStyles = {
height: '600px',
overflow: 'hidden',
overflowY: 'scroll'
};
const defaultStylesIframe = {
width: '100%',
height: '5000px'
};
const XStateInspector = ({
inspect,
options,
styles = defaultStyles,
stylesIframe = defaultStylesIframe
}) => {
const ref = /*#__PURE__*/React.createRef();
React.useLayoutEffect(() => {
const inspector = inspect({
iframe: options !== null && options !== void 0 && options.iframe ? options.iframe : () => ref.current,
...options
});
return () => inspector === null || inspector === void 0 ? void 0 : inspector.disconnect(); // eslint-disable-next-line react-hooks/exhaustive-deps
}, [JSON.stringify(options)]);
if ((options === null || options === void 0 ? void 0 : options.iframe) === false) {
return null;
}
return /*#__PURE__*/React.createElement('div', {
style: styles
}, /*#__PURE__*/React.createElement('iframe', {
ref,
title: 'xstate-inspector',
style: stylesIframe,
'data-xstate': true
}, null));
};
function getItem(key, defaultValue) {
let initialValue = undefined;
try {
initialValue = JSON.parse(localStorage.getItem(key));
} catch {}
if (typeof initialValue === 'undefined' || initialValue === null) {
return defaultValue;
} else {
return initialValue;
}
}
function setItem(key, value) {
try {
localStorage.setItem(key, JSON.stringify(value));
} catch {}
}
export { LOCAL_STORAGE_KEY_IS_ENABLED, LOCAL_STORAGE_KEY_OVERRIDE_OPTIONS, XStateInspectLoader };