UNPKG

monarc

Version:

MONARC's Obviously Not A Redux Clone

592 lines (471 loc) 15.4 kB
'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); var invariant = require('tiny-invariant'); var React = require('react'); var serialize = require('@redux-devtools/serialize'); var jsan = require('jsan'); function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; } var invariant__default = /*#__PURE__*/_interopDefaultLegacy(invariant); var React__default = /*#__PURE__*/_interopDefaultLegacy(React); var jsan__default = /*#__PURE__*/_interopDefaultLegacy(jsan); 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 splitReducer(anyReducer) { var reducerProvider = anyReducer; var reducers = anyReducer; if (typeof reducerProvider.reducer !== 'undefined') { return [reducerProvider.reducer, reducerProvider.Provider]; } if (typeof anyReducer === 'function') { return [anyReducer, React.Fragment]; } !Array.isArray(reducers) ? invariant__default["default"](false, 'invalid reducer(s) supplied') : void 0; !(reducers.length !== 0) ? invariant__default["default"](false, 'no reducers supplied') : void 0; if (reducers.length === 1) { return [reducers[0], React.Fragment]; } var reducer = function reducer(state, action) { var update = function update(updated, reduce) { return reduce(updated, action); }; return reducers.reduce(update, state); }; return [reducer, React.Fragment]; } // --------------------------------------------------------------------- function full(wrapReducer, useValue, defaults) { var initContext = React.createContext(null); var useInit = function useInit() { return React.useContext(initContext); }; var withPlugin = function withPlugin(anyReducer, options) { var _splitReducer = splitReducer(anyReducer), reducer = _splitReducer[0], Provider = _splitReducer[1]; var ps = {}; var opts = _extends({}, defaults, options); var wrapped = wrapReducer(reducer, ps, opts); var PluginProvider = function PluginProvider(_ref) { var children = _ref.children; var value = useValue(ps, opts); return React__default["default"].createElement(initContext.Provider, { value: value }, React__default["default"].createElement(Provider, null, children)); }; return { reducer: wrapped, Provider: PluginProvider }; }; var context = initContext; var usePlugin = useInit; return [withPlugin, usePlugin, context]; } function simple(wrapReducer, defaults) { var withPlugin = function withPlugin(anyReducer, options) { var _splitReducer2 = splitReducer(anyReducer), reducer = _splitReducer2[0], Provider = _splitReducer2[1]; var ps = {}; var opts = _extends({}, defaults, options); var wrapped = wrapReducer(reducer, ps, opts); return { reducer: wrapped, Provider: Provider }; }; return [withPlugin]; } function createPlugin(wrapReducer, useValue, defaults) { if (typeof useValue === 'function') { return full(wrapReducer, useValue, defaults); } return simple(wrapReducer, useValue); } var storeContext = /*#__PURE__*/React.createContext({ dispatch: function dispatch() { return undefined; }, state: {} }); function useDispatch() { var _useContext = React.useContext(storeContext), dispatch = _useContext.dispatch; return React.useCallback(function (action) { if (typeof action.then === 'function') { action.then(dispatch); } else { dispatch(action); } }, [dispatch]); } function useStore() { var _useContext2 = React.useContext(storeContext), state = _useContext2.state; return state; } var useIsomorphicLayoutEffect = typeof window !== 'undefined' ? React.useLayoutEffect : React.useEffect; function isPromise(value) { return typeof value.then === 'function'; } // --------------------------------------------------------------------- function createContainer(Component, anyReducer, dispatcher) { var _splitReducer = splitReducer(anyReducer), reducer = _splitReducer[0], Provider = _splitReducer[1]; var StoreContainer = function StoreContainer(_ref) { var initialState = _ref.initialState, children = _ref.children; var _useReducer = React.useReducer(reducer, initialState), state = _useReducer[0], dispatch = _useReducer[1]; var value = React.useMemo(function () { return { state: state, dispatch: dispatch }; }, [state]); useIsomorphicLayoutEffect(function () { if (typeof dispatcher === 'undefined') { return; } dispatcher.dispatch = function (action) { if (isPromise(action)) { action.then(dispatch); } else { dispatch(action); } }; }, []); return React__default["default"].createElement(storeContext.Provider, { value: value }, React__default["default"].createElement(Provider, null, React__default["default"].createElement(storeContext.Consumer, null, function (current) { return React__default["default"].createElement(Component, { store: current.state }, children); }))); }; return StoreContainer; } function swap(current, from, to) { var popped = from.pop(); if (typeof popped !== 'undefined') { to.push(current); } return popped; } function defaultGetSet(state) { return state; } function wrapReducer$2(reduce, ps, options) { var get = options.getState === defaultGetSet; var set = options.setState === defaultGetSet; var both = get === false && set === false; var none = get === true && set === true; // the user can't supply only one function for getState / setState, // that's probably an error !(reduce.name !== 'autoSave') ? invariant__default["default"](false, 'cannot call withAutoSave before withUndoRedo') : void 0; !(typeof options.getState === 'function') ? invariant__default["default"](false, 'missing getState function') : void 0; !(typeof options.setState === 'function') ? invariant__default["default"](false, 'missing setState function') : void 0; !(none || both) ? invariant__default["default"](false, 'you must supply both getState and setState') : void 0; !options.undoAction ? invariant__default["default"](false, 'invalid undoAction value') : void 0; !options.redoAction ? invariant__default["default"](false, 'invalid redoAction value') : void 0; !(options.maxUndo >= 0) ? invariant__default["default"](false, 'invalid maxUndo value') : void 0; // initialize our state ps.prev = null; ps.undo = []; ps.redo = []; var PS = ps; var MAX_UNDO = options.maxUndo; var SET = options.setState; var GET = options.getState; var UNDO = options.undoAction; var REDO = options.redoAction; return function undoRedo(state, action) { var stream = action.undoStream === true && action.type === PS.prev; var reset = action.undoReset === true; var skip = action.undoSkip === true; var next; var updated; var current; switch (action.type) { case UNDO: if (PS.undo.length === 0) { return state; } PS.prev = null; current = GET(state); next = swap(current, PS.undo, PS.redo); if (typeof next !== 'undefined') { return SET(next, state); } return state; case REDO: if (PS.redo.length === 0) { return state; } PS.prev = null; current = GET(state); next = swap(current, PS.redo, PS.undo); if (typeof next !== 'undefined') { return SET(next, state); } return state; default: updated = reduce(state, action); if (skip === false && reset === false && stream === false) { next = GET(updated); current = GET(state); if (current !== next) { if (MAX_UNDO && MAX_UNDO === PS.undo.length) { PS.undo.shift(); } PS.undo.push(current); PS.redo = []; } } if (reset === true) { PS.undo = []; PS.redo = []; } PS.prev = action.type; break; } return updated; }; } function useValue$2(ps) { var PS = ps; var canUndo = PS.undo.length !== 0; var canRedo = PS.redo.length !== 0; return React.useMemo(function () { return { canUndo: canUndo, canRedo: canRedo }; }, [canUndo, canRedo]); } var defaults$2 = { setState: defaultGetSet, getState: defaultGetSet, undoAction: 'UNDO', redoAction: 'REDO', maxUndo: 50 }; // --------------------------------------------------------------------- var _createPlugin$2 = /*#__PURE__*/createPlugin(wrapReducer$2, useValue$2, defaults$2), withUndoRedo = _createPlugin$2[0], useUndoRedo = _createPlugin$2[1], undoContext = _createPlugin$2[2]; function save(ps, onSave, onBeforeUpdate) { if (ps.timer !== null) { clearTimeout(ps.timer); ps.timer = null; } if (ps.state === null) { return; } if (onSave.length === 2) { onSave(ps.state, function () { ps.render(); }); return; } onSave(ps.state); if (onBeforeUpdate !== true) { ps.render(); } } function wrapReducer$1(reduce, ps, options) { !(typeof options.onSave === 'function') ? invariant__default["default"](false, 'missing onSave function') : void 0; !(options.delay >= 0) ? invariant__default["default"](false, 'invalid delay value') : void 0; // initialize our state ps.render = function () { return undefined; }; ps.state = null; ps.timer = null; var PS = ps; var DELAY = options.delay; var LATER = options.onUpdate; var NOW = options.onBeforeUpdate; var SAVE = save.bind(null, PS, options.onSave); if (typeof window !== 'undefined') { window.addEventListener('beforeunload', function () { if (PS.timer !== null) { SAVE(true); } }); } return function autoSave(state, action) { var updated = reduce(state, action); var timer = PS.timer !== null; var saveLater = false; var saveNow = false; if (typeof NOW === 'function') { saveNow = NOW(state, updated, action, timer); } if (PS.timer === null && saveNow === false) { saveLater = LATER(state, updated, action); } if (saveLater === true) { PS.timer = setTimeout(SAVE, DELAY); } if (saveNow === true) { PS.state = state; SAVE(true); } PS.state = updated; return updated; }; } function useValue$1(ps, options) { var _useState = React.useState(0), counter = _useState[0], setCounter = _useState[1]; var PS = ps; var isSaved = PS.timer === null; // the little function below here is just a dirty trick to make this // component render when our timer expires and we have saved our data. // beware that we cannot call it inside the reducer, otherwise there // would be two components trying to render at the same time (this // component and the store container) and react doesn't like it... React.useEffect(function () { PS.render = function () { return setCounter(counter + 1); }; }, [counter]); // eslint-disable-line // save our state on unmount if there's a timer active React.useEffect(function () { var onSave = options.onSave; return function () { PS.render = function () { return undefined; }; if (PS.timer) { save(PS, onSave, true); } }; }, []); // eslint-disable-line return React.useMemo(function () { return { isSaved: isSaved }; }, [isSaved]); } var defaults$1 = { onUpdate: function onUpdate() { return true; }, delay: 5 * 1000 }; // --------------------------------------------------------------------- var _createPlugin$1 = /*#__PURE__*/createPlugin(wrapReducer$1, useValue$1, defaults$1), withAutoSave = _createPlugin$1[0], useAutoSave = _createPlugin$1[1], saveContext = _createPlugin$1[2]; var TIME_TRAVEL = '__REDUX_DEVTOOLS_DISPATCH__'; var defaults = { serialize: true }; // --------------------------------------------------------------------- function wrapReducer(reduce, ps) { ps.send = function () { return undefined; }; ps.init = function () { return undefined; }; ps.initial = null; return function devTools(state, action) { var _action = action; var _ps = ps; var updated; if (_action.type !== TIME_TRAVEL) { updated = reduce(state, action); _ps.send(action, updated); return updated; } switch (_action.payload.type) { case 'COMMIT': updated = reduce(state, action); _ps.init(updated); break; case 'ROLLBACK': updated = _action.state; _ps.init(updated); break; case 'RESET': updated = ps.initial; _ps.init(updated); break; case 'JUMP_TO_ACTION': case 'JUMP_TO_STATE': updated = _action.state; break; default: updated = state; break; } return updated; }; } function useValue(ps, options) { var dispatch = useDispatch(); var store = useStore(); React.useEffect(function () { var installed = typeof window !== 'undefined' && Boolean(window.__REDUX_DEVTOOLS_EXTENSION__); if (installed === false) { return function () { return null; }; } var serialize$1 = options.serialize; var extension = window.__REDUX_DEVTOOLS_EXTENSION__; var devtools = extension.connect(_extends({}, defaults, options)); var parse; if (serialize$1.immutable) { var serializer = serialize.immutable(serialize$1.immutable, serialize$1.refs); parse = serializer.parse; } else { parse = jsan__default["default"].parse; } var unsubscribe = devtools.subscribe(function (message) { if (message.type === 'DISPATCH') { var state = parse(message.state); var action = { type: TIME_TRAVEL, payload: message.payload, state: state }; dispatch(action); } }); ps.send = devtools.send; ps.init = devtools.init; ps.initial = store; devtools.init(store); return function () { unsubscribe(); extension.disconnect(); }; }, []); // eslint-disable-line } // --------------------------------------------------------------------- var _createPlugin = /*#__PURE__*/createPlugin(wrapReducer, useValue), withDevTools = _createPlugin[0]; exports.createContainer = createContainer; exports.createPlugin = createPlugin; exports.saveContext = saveContext; exports.storeContext = storeContext; exports.undoContext = undoContext; exports.useAutoSave = useAutoSave; exports.useDispatch = useDispatch; exports.useStore = useStore; exports.useUndoRedo = useUndoRedo; exports.withAutoSave = withAutoSave; exports.withDevTools = withDevTools; exports.withUndoRedo = withUndoRedo; //# sourceMappingURL=monarc.cjs.development.js.map