UNPKG

xstate-helpers

Version:

A collection of helpers for XState (with React).

164 lines (142 loc) • 4.58 kB
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 };