react-view-router
Version:
react-view-router
306 lines • 11.3 kB
JavaScript
import _slicedToArray from "@babel/runtime/helpers/slicedToArray";
import "core-js/modules/es6.array.map.js";
import React, { Fragment, useRef, useCallback, useLayoutEffect, useImperativeHandle, useState, useMemo } from 'react';
import { innumerable } from './util';
var KEEP_ALIVE_ANCHOR = 'keep-alive-anchor';
var KEEP_ALIVE_REPLACOR = 'keep-alive-replacor';
var KEEP_ALIVE_POSITION = 'keep-alive-position';
var KEEP_ALIVE_KEEP_COPIES = 'keep-alive-keep-copies';
function Component(props) {
var utils = props.utils,
active = props.active,
children = props.children,
name = props.name,
_props$anchor = props.anchor,
anchor = _props$anchor === void 0 ? null : _props$anchor,
inner = props.inner;
var appendChild = utils.appendChild,
insertBefore = utils.insertBefore;
var _useState = useState(() => {
var holder = utils.createDocumentFragment();
return {
name,
inner,
holder,
active,
anchor,
anchorRoot: null,
mountRoot: null,
childNodes: [],
unmounting: false,
insertBefore,
appendChild,
position: null
};
}),
_useState2 = _slicedToArray(_useState, 1),
$refs = _useState2[0];
$refs.anchor = anchor;
$refs.anchorRoot = useMemo(() => {
var anchor = $refs.anchor;
if (!anchor) return null;
return inner ? anchor : anchor.parentNode || null;
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [$refs.anchor, inner]);
$refs.active = $refs.active || active;
$refs.insertBefore = insertBefore;
$refs.appendChild = appendChild;
useLayoutEffect(() => {
var unhooks = [];
var hook = (el, methodName, cb, replacor) => {
var old = el[methodName];
if (!old || old._hooked) {
console.error(`[keep-alive]warning: hook method "${methodName}" ${old ? 'already hooked' : 'not exist'}!`);
return;
}
var newMethod = function newMethod() {
var mountRoot = $refs.mountRoot,
active = $refs.active,
unmounting = $refs.unmounting;
if (!mountRoot || !active || unmounting) return old.apply(this, arguments);
// @ts-ignore
// eslint-disable-next-line prefer-spread
cb && cb.apply(this, arguments);
if (replacor) return replacor(old, ...arguments);
// eslint-disable-next-line prefer-spread
return mountRoot[methodName].apply(mountRoot, arguments);
};
newMethod._hooked = true;
el[methodName] = newMethod;
unhooks.push(() => el[methodName] = old);
};
hook($refs.holder, 'appendChild', node => {
$refs.childNodes.push(node);
}, $refs.inner ? undefined : (fn, node) => $refs.anchorRoot.insertBefore(node, $refs.anchor));
hook($refs.holder, 'removeChild', node => {
var idx = $refs.childNodes.findIndex(v => v === node);
if (~idx) $refs.childNodes.splice(idx, 1);
});
hook($refs.holder, 'insertBefore', (newNode, node) => {
var idx = $refs.childNodes.findIndex(v => v === node);
if (~idx) $refs.childNodes.splice(idx, 0, newNode);
});
hook($refs.holder, 'replaceChild', (newChild, oldChild) => {
var idx = $refs.childNodes.findIndex(v => v === oldChild);
if (~idx) $refs.childNodes.splice(idx, 1, newChild);
});
['hasChildNodes', 'contains', 'getRootNode'].forEach(name => hook($refs.holder, name));
return () => unhooks.forEach(cb => cb());
}, [$refs, $refs.holder]);
var mountView = useCallback((mountRoot, anchor) => {
if (!anchor || !mountRoot) return;
var holder = $refs.holder,
appendChild = $refs.appendChild,
insertBefore = $refs.insertBefore,
inner = $refs.inner;
if (anchor.mountName && anchor.mountName !== $refs.name) {
anchor.unmountView();
}
$refs.childNodes = [...holder.childNodes];
$refs.childNodes.forEach(child => {
if (inner) appendChild(mountRoot, child);else insertBefore(mountRoot, child, anchor);
// if (child && child[KEEP_ALIVE_REPLACOR] && child.mountName) {
// let replacor = (child as any)[KEEP_ALIVE_REPLACOR];
// let item = replacor[child.mountName];
// item && item.mountView(mountRoot, child);
// }
});
anchor.mountName = $refs.name;
$refs.mountRoot = mountRoot;
var position = $refs.position;
if (position && mountRoot.scrollTo) mountRoot.scrollTo(position.x, position.y);
}, [$refs]);
var unmountView = useCallback(() => {
var childNodes = $refs.childNodes,
active = $refs.active;
if (!active || !childNodes.length) return;
$refs.unmounting = true;
try {
var _appendChild = $refs.appendChild,
_insertBefore = $refs.insertBefore,
holder = $refs.holder,
mountRoot = $refs.mountRoot,
_anchor = $refs.anchor,
_inner = $refs.inner;
var position = {
x: mountRoot.scrollLeft,
y: mountRoot.scrollTop
};
var isValidChild = _anchor && mountRoot.contains(_anchor);
var nodes = childNodes.splice(0, childNodes.length);
nodes.forEach(child => {
var _mountRoot$dataset;
if (child && child[KEEP_ALIVE_REPLACOR]) child.unmountView();else {
var p = {
x: child.scrollLeft,
y: child.scrollTop
};
innumerable(child, KEEP_ALIVE_POSITION, p.x || p.y ? p : null);
}
_appendChild(holder, child);
if ((_mountRoot$dataset = mountRoot.dataset) !== null && _mountRoot$dataset !== void 0 && _mountRoot$dataset.keepAliveKeepCopies) {
var cloneNode = child.cloneNode();
if (_inner || !isValidChild) _appendChild(mountRoot, cloneNode);else _insertBefore(mountRoot, cloneNode, _anchor);
}
});
$refs.mountRoot = null;
$refs.position = position.x || position.y ? position : null;
if (_anchor.mountName === $refs.name) _anchor.mountName = '';
} finally {
$refs.unmounting = false;
}
}, [$refs]);
useMemo(() => {
if (!anchor) return;
if (!anchor.unmountView) {
anchor.unmountView = function () {
if (!this.mountName || !this[KEEP_ALIVE_REPLACOR]) return;
var replacor = this[KEEP_ALIVE_REPLACOR];
var item = replacor && replacor[this.mountName];
item && item.unmountView();
};
}
var replacor = anchor[KEEP_ALIVE_REPLACOR];
if (!replacor) {
replacor = {};
innumerable(anchor, KEEP_ALIVE_REPLACOR, replacor);
}
var item = replacor[$refs.name] = {};
item.$refs = $refs;
item.unmountView = unmountView;
item.mountView = mountView;
}, [$refs, anchor, mountView, unmountView]);
useLayoutEffect(() => {
if (!$refs.active) return;
var anchor = $refs.anchor,
anchorRoot = $refs.anchorRoot,
mountRoot = $refs.mountRoot;
if (mountRoot && !anchorRoot) unmountView();
if (!anchorRoot) return;
if (active) {
if (anchorRoot !== mountRoot || anchor.mountName !== $refs.name) mountView(anchorRoot, anchor);
} else unmountView();
}, [active, $refs, mountView, unmountView]);
useLayoutEffect(() => () => {
var active = $refs.active,
mountRoot = $refs.mountRoot;
if (active && mountRoot) unmountView();
}, [$refs, unmountView]);
return $refs.active ? utils.createPortal(children, $refs.holder, name) : null;
}
var KeepAliveAnchor = /*#__PURE__*/React.forwardRef((props, ref) => {
var utils = props.utils,
_props$children = props.children,
children = _props$children === void 0 ? '' : _props$children;
var anchorRef = useRef(null);
useImperativeHandle(ref, () => anchorRef.current, [anchorRef]);
useLayoutEffect(() => {
var _style;
var current = anchorRef.current;
if (!current || current[KEEP_ALIVE_ANCHOR]) return;
(_style = current.style) === null || _style === void 0 || _style.setProperty('display', 'none', 'important');
innumerable(current, KEEP_ALIVE_ANCHOR, true);
}, [anchorRef, utils]);
useLayoutEffect(() => {
var current = anchorRef.current;
if (!current) return;
if (current.textContent != children) current.textContent = children;
}, [anchorRef, children]);
return /*#__PURE__*/React.createElement('i', {
key: KEEP_ALIVE_ANCHOR,
style: {
display: 'none'
},
ref: anchorRef
});
});
function createAnchor(utils, ref, text = '') {
return /*#__PURE__*/React.createElement(KeepAliveAnchor, {
ref,
utils
}, text);
}
function createAnchorText(anchorName) {
return anchorName ? `${KEEP_ALIVE_ANCHOR} ${anchorName}` : KEEP_ALIVE_ANCHOR;
}
var KeepAlive = /*#__PURE__*/React.forwardRef((props, ref) => {
var activeName = props.activeName,
_props$anchorName = props.anchorName,
anchorName = _props$anchorName === void 0 ? '' : _props$anchorName,
anchor = props.anchor,
anchorRef = props.anchorRef,
utils = props.utils,
children = props.children,
_props$extra = props.extra,
extra = _props$extra === void 0 ? {} : _props$extra;
var _useState3 = useState(0),
_useState4 = _slicedToArray(_useState3, 2),
ready = _useState4[0],
setReady = _useState4[1];
var _useState5 = useState([]),
_useState6 = _slicedToArray(_useState5, 2),
nodes = _useState6[0],
setNodes = _useState6[1];
var _useState7 = useState(() => Object.assign(anchorRef || {
current: null
}, {
activeName: ''
})),
_useState8 = _slicedToArray(_useState7, 1),
$refs = _useState8[0];
$refs.ready = ready;
$refs.nodes = nodes;
$refs.extra = extra;
$refs.remove = useCallback((name, triggerRender = true) => {
var idx = nodes.findIndex(res => res.name === name);
if (~idx) {
nodes.splice(idx, 1);
triggerRender && setNodes([...nodes]);
}
return idx;
}, [nodes]);
$refs.find = useCallback(name => nodes.find(res => res.name === name), [nodes]);
useImperativeHandle(ref, () => $refs);
useLayoutEffect(() => {
var current = $refs.current;
anchorRef && ($refs.current = anchorRef.current);
setReady(ready => {
if (!$refs.current) return 0;
return $refs.current === current ? ready || 1 : ready + 1;
});
}, [$refs, anchorRef]);
useLayoutEffect(() => {
if (!activeName) {
$refs.activeName = '';
return;
}
var idx = nodes.findIndex(res => res.name === activeName);
if (~idx) {
if (children == null) nodes.splice(idx, 1);else nodes[idx].node = children;
} else nodes.push(Object.assign({
name: activeName,
node: children
}, $refs.extra));
$refs.activeName = activeName;
}, [$refs, nodes, children, activeName]);
useLayoutEffect(() => {
if (!activeName) {
$refs.activeNode = undefined;
return;
}
$refs.activeNode = nodes.find(v => v.name === activeName);
}, [$refs, activeName, nodes]);
return /*#__PURE__*/React.createElement(Fragment, {}, anchor || createAnchor(utils, $refs, createAnchorText(anchorName)), Boolean(ready) && nodes.map(({
name,
node
}) => /*#__PURE__*/React.createElement(Component, {
active: name === activeName,
anchor: $refs.current,
name,
key: name,
utils
}, node)));
});
export { createAnchor, createAnchorText, KEEP_ALIVE_ANCHOR, KEEP_ALIVE_REPLACOR, KEEP_ALIVE_KEEP_COPIES };
export default KeepAlive;
//# sourceMappingURL=keep-alive.js.map