UNPKG

react-view-router

Version:
306 lines 11.3 kB
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