monarc
Version:
MONARC's Obviously Not A Redux Clone
592 lines (471 loc) • 15.4 kB
JavaScript
;
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