import React from 'react' ; import { Tabs } from 'antd' ; import { TabsProps } from 'antd/es/tabs' ; import findIndex from 'lodash/findIndex' ; import { Dropdown , Menu } from 'antd/es' ; import { MenuProps } from 'antd/es/menu' ; import { FormattedMessage } from 'umi-plugin-react/locale' ; function addTab ( newTab , activedTabs ) { return [ ... activedTabs , newTab ] . filter ( item => item . path !== '/' ) . map ( ( item , index ) => ( { ... item , closable : ! ( activedTabs . length === 0 && index === 0 ) } ) ) ; } function switchAndUpdateTab ( activeIndex , tabName , extraTabProperties , children , activedTabs ) { const { path , content , refresh , ... rest } = activedTabs [ activeIndex ] ; activedTabs . splice ( activeIndex , 1 , { tab : tabName , content : refresh ? content : children , ... rest , ... extraTabProperties , } ) ; return activedTabs . map ( item => ( activedTabs . length === 1 ? { ... item , closable : false } : item ) ) ; } const closeCurrentTabMenuKey = 'closeCurrent' ; const closeOthersTabMenuKey = 'closeOthers' ; export interface TabView { /** tab's title */ tab : string ; key : string ; content : React . ReactChildren ; /** used to extends tab's properties */ [ k ] : any ; } export interface TabsViewProps { activeKey : string ; activeTitle : string ; handleTabChange : ( keyToSwitch : string , activedTabs : TabView [ ] ) => void ; extraTabProperties ? : { } ; tabsConfig ? : TabsProps ; afterRemoveTab ? : ( removeKey : string , nextTabKey : string , activedTabs : TabView [ ] ) => void ; /** children is used to create tab, switch and update tab */ children : React . ReactChildren ; isMobile ? : boolean ; collapsed ? : boolean ; fixedHeader ? : boolean ; layout ? : string ; siderWidth ? : number ; } interface TabsViewState { activedTabs : TabView [ ] ; activeKey : string ; nextTabKey : string ; } class TabsView extends React . Component < TabsViewProps , TabsViewState > { static getDerivedStateFromProps ( props , state ) { const { children , activeKey , activeTitle , extraTabProperties } = props ; const { activedTabs , nextTabKey } = state ; if ( nextTabKey ) { return { activedTabs , activeKey : nextTabKey , nextTabKey : null , } ; } const activedTabIndex = findIndex ( activedTabs , { key : activeKey } ) ; if ( activedTabIndex > - 1 ) { return { activedTabs : switchAndUpdateTab ( activedTabIndex , activeTitle , extraTabProperties , children , activedTabs ) , activeKey , } ; } const newTab = { tab : activeTitle , key : activeKey , content : children , ... extraTabProperties , } ; return { activedTabs : addTab ( newTab , activedTabs ) , activeKey , } ; } constructor ( props ) { super ( props ) ; this . state = { activedTabs : [ ] , activeKey : '' , nextTabKey : '' , } ; } handleTabsMenuClick = ( tabKey ) : MenuProps [ 'onClick' ] => event => { const { key } = event ; const { activedTabs } = this . state ; if ( key === closeCurrentTabMenuKey ) { this . remove ( tabKey ) ; } else if ( key === closeOthersTabMenuKey ) { const currentTab = activedTabs . filter ( item => item . key === tabKey ) ; this . setState ( { activedTabs : currentTab . map ( item => ( { ... item , closable : false } ) ) , } ) ; } } ; handleTabSwitch = ( keyToSwitch ) => { const { handleTabChange } = this . props ; const { activedTabs } = this . state ; if ( typeof handleTabChange === 'function' ) { handleTabChange ( keyToSwitch , activedTabs ) ; } } ; handleTabEdit = ( targetKey , action ) => { this [ action ] ( targetKey ) ; } ; remove = ( key ) => { const { afterRemoveTab } = this . props ; const { activedTabs , activeKey } = this . state ; if ( key !== activeKey ) { this . setState ( { activedTabs : activedTabs . filter ( item => item . key !== key ) , nextTabKey : activeKey , } , ( ) => { if ( typeof afterRemoveTab === 'function' ) { afterRemoveTab ( key , activeKey , activedTabs ) ; } } ) ; return ; } const targetIndex = findIndex ( activedTabs , { key } ) ; const nextIndex = targetIndex > 0 ? targetIndex - 1 : targetIndex + 1 ; const nextTabKey = activedTabs [ nextIndex ] . key ; this . setState ( { activedTabs : activedTabs . filter ( item => item . key !== key ) , nextTabKey , } , ( ) => { if ( typeof afterRemoveTab === 'function' ) { afterRemoveTab ( key , nextTabKey , activedTabs ) ; } } ) ; } ; getHeadWidth = ( ) => { const { isMobile , collapsed , fixedHeader , layout , siderWidth = 256 } = this . props ; if ( isMobile || ! fixedHeader || layout === 'topmenu' ) { return '100%' ; } return collapsed ? 'calc(100% - 80px)' : `calc(100% - ${ siderWidth }px)` ; } ; render ( ) { const { activedTabs , activeKey } = this . state ; const width = this . getHeadWidth ( ) ; const setMenu = ( key ) => ( < Menu onClick = { this . handleTabsMenuClick ( key ) } > < Menu . Item disabled = { activedTabs . length === 1 } key = { closeCurrentTabMenuKey } > < FormattedMessage id = "component.tabsView.context.close-current" /> < /Menu.Item> < Menu . Item disabled = { activedTabs . length === 1 } key = { closeOthersTabMenuKey } > < FormattedMessage id = "component.tabsView.context.close-others" / > < /Menu.Item> < /Menu>); const setTab = ( tab , key ) => ( < span onContextMenu = { event => event . preventDefault ( ) } > < Dropdown overlay = { setMenu ( key ) } trigger = { [ 'contextMenu' ] } > < span > < FormattedMessage id = { 'menu.' . concat ( tab ) } /> < /span> < /Dropdown> < /span>); const renderTabBar = ( props , DefaultTabBar ) => ( < div style = { { padding : '4px 20px 0 20px' , background : '#fff' , borderBottom : '1px solid #ccc' , zIndex : 1 , position : ( this . props . fixedHeader && 'fixed' ) || undefined , right : 0 , top : 64 , width , transition : 'width 0.2s' , } } > < DefaultTabBar { ... props } / > < /div>); return ( < Tabs className = "ant-pro-page-content-wrap-tabs" type = "editable-card" activeKey = { activeKey } tabBarStyle = { { borderBottom : 0 , margin : '0 0 10px 0' } } tabBarGutter = { 0 } hideAdd renderTabBar = { renderTabBar } onChange = { this . handleTabSwitch } onEdit = { this . handleTabEdit } > { activedTabs && activedTabs . length ? activedTabs . map ( ( item ) => ( < Tabs . TabPane tab = { setTab ( item . tab , item . key ) } key = { item . key } closable = { item . closable } > { item . content } < /Tabs.TabPane>)) : null } < /Tabs>); } } export default TabsView ;