import React, { useState, useRef } from "react"; import classNames from "classnames"; import { isChildOfType } from "../_util/is-child-of-type"; import { TabBar } from "./TabBar"; import { TabPanel } from "./TabPanel"; import { TabsProps, Tab } from "./TabProps"; import { useConfig } from "../_util/config-context"; const getHiddenPanelStyle: ( vertical: boolean ) => React.CSSProperties = vertical => ({ height: 0, width: 0, overflow: "hidden", opacity: 0, padding: 0, margin: 0, pointerEvents: "none", display: "block", transform: vertical ? "translate3d(10px, 0, 0)" : "translate3d(0, 10px, 0)", }); const animatedPanelStyle: React.CSSProperties = { transition: "opacity .45s ease, transform .45s ease", transformOrigin: "center", }; const defaultTabBarRender = c => {c}; export const Tabs = React.forwardRef(function Tabs( { tabs = [], destroyInactiveTabPanel = true, activeId, onActive, defaultActiveId, children, animated = true, placement, ceiling, addon, maxHeight, className, tabBarRender = defaultTabBarRender, tabBarStyle, activeTabAutoScrollIntoView = true, disableTabScrolling = false, ...props }: TabsProps, ref: React.Ref ) { const { classPrefix } = useConfig(); // 如果都没有定义,获取第一个没被禁用的选项卡 id if ( typeof activeId === "undefined" && typeof defaultActiveId === "undefined" ) { const firstAvailable = tabs.find(x => !x.disabled); // eslint-disable-next-line no-param-reassign defaultActiveId = firstAvailable ? firstAvailable.id : null; } // 注意 useState 是个 Hooks,不能写在 if 里面 const [internalActiveId, setInternalActiveId] = useState(defaultActiveId); // 非受控模式下,使用内部状态 if (typeof activeId === "undefined") { // eslint-disable-next-line no-param-reassign activeId = internalActiveId; // eslint-disable-next-line no-param-reassign onActive = (onActive => (tab: Tab, evt: React.SyntheticEvent) => { setInternalActiveId(tab.id); if (typeof onActive === "function") { onActive(tab, evt); } })(onActive); } const panelRenderedSetRef = useRef>(new Set()); function getTabPanels() { const panelRenderedSet = panelRenderedSetRef.current; const panelChildren = []; React.Children.forEach(children, (child, index) => { if (isChildOfType(child, TabPanel)) { const { props, key } = child; const { style = {}, id } = props; const active = activeId === props.id; if (active && !panelRenderedSet.has(activeId)) { panelRenderedSet.add(activeId); } if (!active) { Object.assign(style, getHiddenPanelStyle(placement === "left")); } if (animated) { Object.assign(style, animatedPanelStyle); } if ( child.props.forceRender || (destroyInactiveTabPanel && active) || (!destroyInactiveTabPanel && panelRenderedSet.has(props.id)) ) { panelChildren.push( React.cloneElement(child, { key: key || id, style }) ); } else { panelChildren.push( ); } } else if (React.isValidElement(child)) { panelChildren.push(React.cloneElement(child, { key: index })); } else { panelChildren.push(
{child}
); } }); return panelChildren; } const containerClassName = classNames(`${classPrefix}-tabs`, { // 垂直布局 [`${classPrefix}-tabs--vertical`]: placement === "left", [`${classPrefix}-tabs--ceiling`]: ceiling, [className]: className, }); return (
{getTabPanels()}
); }); Tabs.displayName = "Tabs";