@vgbire/react-keep-alive
Version:
React keepAlive
200 lines (199 loc) • 8.94 kB
JavaScript
import React, { useCallback, useEffect, useRef, useState } from 'react';
import { useNavigate } from 'react-router';
import { CloseCircleOutlined, LeftOutlined, RightOutlined, CloseOutlined } from '@ant-design/icons';
import { useSize } from 'ahooks';
import { Divider, Flex, Modal } from 'antd';
import '../index.scss';
import { useKeepAliveContext } from '.';
import i18n from '../i18n';
import { useRouterApi } from './hooks/use-router-api';
export const RouterTabs = () => {
const { tabs, active, theme, size } = useKeepAliveContext();
const [modal, contextHolder] = Modal.useModal();
// 主题style
const styles = {
itemBg: theme === 'dark' ? '#001628' : '#fafafa',
itemActiveBg: theme === 'dark' ? '#1677ff' : '#fff',
itemColor: theme === 'dark' ? '#ffffffa6' : '#999',
itemHoverColor: theme === 'dark' ? '#fff' : '#4096ff',
itemActiveColor: theme === 'dark' ? '#fff' : '#1677ff',
iconBg: theme === 'dark' ? '#042c4d' : '#fff',
iconColor: theme === 'dark' ? '#ffffffa6' : '#999',
hoverIconColor: theme === 'dark' ? '#fff' : '#000',
};
// 尺寸style
const sizeStyle = {
fontSize: size === 'large' ? '16px' : '14px',
padding: size === 'small' ? '6px 16px' : '8px 16px',
};
const { close, closeAll } = useRouterApi();
const navigate = useNavigate();
const [left, setLeft] = useState(0);
const tabsOuterRef = useRef();
const tabsInnerRef = useRef();
const [leftDisabled, setLeftDisabled] = useState(false);
const [rightDisabled, setRightDisabled] = useState(false);
const windowSize = useSize(document.querySelector('body'));
const [routerTabs, setRouterTabs] = useState([]);
useEffect(() => {
setRouterTabs(tabs.map((item) => (Object.assign(Object.assign({}, item), { iconColor: styles.iconColor, itemColor: item.key === active ? styles.itemActiveColor : styles.itemColor }))));
}, [tabs, theme, active]);
useEffect(() => {
const { offsetWidth, scrollWidth } = tabsOuterRef.current;
setRightDisabled(left <= offsetWidth - scrollWidth);
setLeftDisabled(left >= 0);
if (offsetWidth === scrollWidth) {
setLeft(0);
}
}, [tabs, left, windowSize]);
const prev = (offset) => {
if (leftDisabled)
return;
let cur = 0;
if (offset) {
cur = left + offset;
}
else {
const children = tabsInnerRef.current.children;
for (let i = 0; i < children.length; i++) {
const tab = children[i];
if (cur >= left + tabsOuterRef.current.offsetWidth) {
cur -= tab.offsetWidth;
}
else {
break;
}
}
cur = cur - 3;
}
if (cur > 0) {
cur = 0;
}
setLeft(cur);
};
const next = (offset) => {
if (rightDisabled)
return;
let cur = 0;
if (offset) {
setLeft(left - offset);
}
else {
const children = tabsInnerRef.current.children;
for (let i = 0; i < children.length; i++) {
const tab = children[i];
if (cur - tab.offsetWidth > left - tabsOuterRef.current.offsetWidth) {
cur -= tab.offsetWidth;
}
else {
break;
}
}
setLeft(cur - 3);
}
};
const stop = useCallback((e) => {
e.preventDefault();
}, []);
const preventHTMLScroll = (prevent) => {
const body = document.querySelector('body');
if (prevent) {
if (!leftDisabled || !rightDisabled) {
body.addEventListener('wheel', stop, { passive: false });
}
}
else {
body.removeEventListener('wheel', stop);
}
};
return (React.createElement(Flex, { className: "router-tab-box", style: { background: styles.itemBg } },
React.createElement(LeftOutlined, { className: "router-tab-icon", style: { cursor: leftDisabled ? 'not-allowed' : 'pointer', color: styles.iconColor, background: styles.iconBg }, onClick: () => {
prev();
} }),
React.createElement("div", { ref: tabsOuterRef, onWheel: (e) => {
const deltaY = e.deltaY;
const offset = Math.abs(deltaY);
deltaY > 0 ? next(offset) : prev(offset);
}, onMouseEnter: () => {
preventHTMLScroll(true);
}, onMouseLeave: () => {
preventHTMLScroll(false);
}, style: {
flex: 1,
overflow: 'hidden',
position: 'relative',
} },
React.createElement("div", { ref: tabsInnerRef, style: {
whiteSpace: 'nowrap',
position: 'relative',
display: 'inline-block',
transition: 'left .5s ease',
left,
} }, routerTabs.map((item, index) => {
return (
// <CSSTransition timeout={300} classNames="fade" key={item.key}>
React.createElement("div", { style: { display: 'inline-block' }, key: item.key },
index !== 0 && React.createElement(Divider, { type: "vertical", className: "tab-item-divider" }),
React.createElement("div", { className: "tab-item", onClick: () => {
navigate(item.key);
}, style: {
backgroundColor: item.key === active ? styles.itemActiveBg : styles.itemBg,
padding: sizeStyle.padding,
fontSize: sizeStyle.fontSize,
display: 'inline-block',
}, onMouseEnter: (e) => {
setRouterTabs(routerTabs.map((tab) => {
if (tab.key === active) {
return tab;
}
else {
return Object.assign(Object.assign({}, tab), { itemColor: tab.key === item.key ? styles.itemHoverColor : styles.itemColor });
}
}));
}, onMouseLeave: (e) => {
setRouterTabs(routerTabs.map((tab) => {
if (tab.key === active) {
return tab;
}
else {
return Object.assign(Object.assign({}, tab), { itemColor: styles.itemColor });
}
}));
} },
React.createElement("span", { className: "tab-item-label", style: { color: item.itemColor } }, item.label),
routerTabs.length > 1 && (React.createElement(CloseOutlined, { className: "tab-item-icon", style: { color: item.iconColor }, onClick: (e) => {
e.stopPropagation();
close(item.key);
}, onMouseEnter: (e) => {
setRouterTabs(routerTabs.map((tab) => (Object.assign(Object.assign({}, tab), { iconColor: tab.key === item.key ? styles.hoverIconColor : styles.iconColor }))));
}, onMouseLeave: (e) => {
setRouterTabs(routerTabs.map((tab) => (Object.assign(Object.assign({}, tab), { iconColor: styles.iconColor }))));
} })))));
}))),
React.createElement(RightOutlined, { className: "router-tab-icon", style: {
cursor: rightDisabled ? 'not-allowed' : 'pointer',
color: styles.iconColor,
background: styles.iconBg,
}, onClick: () => {
next();
} }),
routerTabs.length > 1 && (React.createElement(CloseCircleOutlined, { style: {
flexBasis: 20,
padding: '0 8px',
background: styles.iconBg,
color: styles.iconColor,
zIndex: 2,
}, onClick: () => {
modal.confirm({
title: i18n.t('closeAllTabTip'),
content: i18n.t('closeAllTabContent'),
cancelText: i18n.t('cancel'),
okText: i18n.t('confirm'),
onOk() {
closeAll();
},
});
} })),
contextHolder));
};
RouterTabs.displayName = 'RouterTabs';