mirror of
https://github.com/ant-design/ant-design.git
synced 2024-11-27 20:49:53 +08:00
feat: Tabs support items (#36889)
* refactor: Using items * docs: replace with items * docs: deprecated demo * test: Update snapshot * docs: simple basic demo * test: coverage
This commit is contained in:
parent
37cc36ac7e
commit
628968f460
12
components/tabs/TabPane.tsx
Normal file
12
components/tabs/TabPane.tsx
Normal file
@ -0,0 +1,12 @@
|
||||
import type * as React from 'react';
|
||||
import type { TabPaneProps } from 'rc-tabs/lib/TabPanelList/TabPane';
|
||||
|
||||
const TabPane: React.FC<TabPaneProps> = () => null;
|
||||
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
TabPane.displayName = 'DeprecatedTabPane';
|
||||
}
|
||||
|
||||
export { TabPaneProps };
|
||||
|
||||
export default TabPane;
|
@ -1088,6 +1088,150 @@ exports[`renders ./components/tabs/demo/custom-tab-bar-node.md extend context co
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`renders ./components/tabs/demo/deprecated.md extend context correctly 1`] = `
|
||||
<div
|
||||
class="ant-tabs ant-tabs-top"
|
||||
>
|
||||
<div
|
||||
class="ant-tabs-nav"
|
||||
role="tablist"
|
||||
>
|
||||
<div
|
||||
class="ant-tabs-nav-wrap"
|
||||
>
|
||||
<div
|
||||
class="ant-tabs-nav-list"
|
||||
style="transform:translate(0px, 0px)"
|
||||
>
|
||||
<div
|
||||
class="ant-tabs-tab ant-tabs-tab-active"
|
||||
>
|
||||
<div
|
||||
aria-selected="true"
|
||||
class="ant-tabs-tab-btn"
|
||||
role="tab"
|
||||
tabindex="0"
|
||||
>
|
||||
Tab 1
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="ant-tabs-tab"
|
||||
>
|
||||
<div
|
||||
aria-selected="false"
|
||||
class="ant-tabs-tab-btn"
|
||||
role="tab"
|
||||
tabindex="0"
|
||||
>
|
||||
Tab 2
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="ant-tabs-tab"
|
||||
>
|
||||
<div
|
||||
aria-selected="false"
|
||||
class="ant-tabs-tab-btn"
|
||||
role="tab"
|
||||
tabindex="0"
|
||||
>
|
||||
Tab 3
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="ant-tabs-ink-bar ant-tabs-ink-bar-animated"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="ant-tabs-nav-operations ant-tabs-nav-operations-hidden"
|
||||
>
|
||||
<button
|
||||
aria-controls="null-more-popup"
|
||||
aria-expanded="false"
|
||||
aria-haspopup="listbox"
|
||||
aria-hidden="true"
|
||||
class="ant-tabs-nav-more"
|
||||
id="null-more"
|
||||
style="visibility:hidden;order:1"
|
||||
tabindex="-1"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
aria-label="ellipsis"
|
||||
class="anticon anticon-ellipsis"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="ellipsis"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M176 511a56 56 0 10112 0 56 56 0 10-112 0zm280 0a56 56 0 10112 0 56 56 0 10-112 0zm280 0a56 56 0 10112 0 56 56 0 10-112 0z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</button>
|
||||
<div>
|
||||
<div
|
||||
class="ant-tabs-dropdown"
|
||||
style="opacity:0"
|
||||
>
|
||||
<ul
|
||||
aria-label="expanded dropdown"
|
||||
class="ant-tabs-dropdown-menu ant-tabs-dropdown-menu-root ant-tabs-dropdown-menu-vertical"
|
||||
data-menu-list="true"
|
||||
id="null-more-popup"
|
||||
role="listbox"
|
||||
tabindex="-1"
|
||||
/>
|
||||
<div
|
||||
aria-hidden="true"
|
||||
style="display:none"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="ant-tabs-content-holder"
|
||||
>
|
||||
<div
|
||||
class="ant-tabs-content ant-tabs-content-top"
|
||||
>
|
||||
<div
|
||||
aria-hidden="false"
|
||||
class="ant-tabs-tabpane ant-tabs-tabpane-active"
|
||||
role="tabpanel"
|
||||
tabindex="0"
|
||||
>
|
||||
Content of Tab Pane 1
|
||||
</div>
|
||||
<div
|
||||
aria-hidden="true"
|
||||
class="ant-tabs-tabpane"
|
||||
role="tabpanel"
|
||||
style="display:none"
|
||||
tabindex="-1"
|
||||
/>
|
||||
<div
|
||||
aria-hidden="true"
|
||||
class="ant-tabs-tabpane"
|
||||
role="tabpanel"
|
||||
style="display:none"
|
||||
tabindex="-1"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`renders ./components/tabs/demo/disabled.md extend context correctly 1`] = `
|
||||
<div
|
||||
class="ant-tabs ant-tabs-top"
|
||||
|
@ -955,6 +955,131 @@ exports[`renders ./components/tabs/demo/custom-tab-bar-node.md correctly 1`] = `
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`renders ./components/tabs/demo/deprecated.md correctly 1`] = `
|
||||
<div
|
||||
class="ant-tabs ant-tabs-top"
|
||||
>
|
||||
<div
|
||||
class="ant-tabs-nav"
|
||||
role="tablist"
|
||||
>
|
||||
<div
|
||||
class="ant-tabs-nav-wrap"
|
||||
>
|
||||
<div
|
||||
class="ant-tabs-nav-list"
|
||||
style="transform:translate(0px, 0px)"
|
||||
>
|
||||
<div
|
||||
class="ant-tabs-tab ant-tabs-tab-active"
|
||||
>
|
||||
<div
|
||||
aria-selected="true"
|
||||
class="ant-tabs-tab-btn"
|
||||
role="tab"
|
||||
tabindex="0"
|
||||
>
|
||||
Tab 1
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="ant-tabs-tab"
|
||||
>
|
||||
<div
|
||||
aria-selected="false"
|
||||
class="ant-tabs-tab-btn"
|
||||
role="tab"
|
||||
tabindex="0"
|
||||
>
|
||||
Tab 2
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="ant-tabs-tab"
|
||||
>
|
||||
<div
|
||||
aria-selected="false"
|
||||
class="ant-tabs-tab-btn"
|
||||
role="tab"
|
||||
tabindex="0"
|
||||
>
|
||||
Tab 3
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="ant-tabs-ink-bar ant-tabs-ink-bar-animated"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="ant-tabs-nav-operations ant-tabs-nav-operations-hidden"
|
||||
>
|
||||
<button
|
||||
aria-controls="null-more-popup"
|
||||
aria-expanded="false"
|
||||
aria-haspopup="listbox"
|
||||
aria-hidden="true"
|
||||
class="ant-tabs-nav-more"
|
||||
id="null-more"
|
||||
style="visibility:hidden;order:1"
|
||||
tabindex="-1"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
aria-label="ellipsis"
|
||||
class="anticon anticon-ellipsis"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="ellipsis"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M176 511a56 56 0 10112 0 56 56 0 10-112 0zm280 0a56 56 0 10112 0 56 56 0 10-112 0zm280 0a56 56 0 10112 0 56 56 0 10-112 0z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="ant-tabs-content-holder"
|
||||
>
|
||||
<div
|
||||
class="ant-tabs-content ant-tabs-content-top"
|
||||
>
|
||||
<div
|
||||
aria-hidden="false"
|
||||
class="ant-tabs-tabpane ant-tabs-tabpane-active"
|
||||
role="tabpanel"
|
||||
tabindex="0"
|
||||
>
|
||||
Content of Tab Pane 1
|
||||
</div>
|
||||
<div
|
||||
aria-hidden="true"
|
||||
class="ant-tabs-tabpane"
|
||||
role="tabpanel"
|
||||
style="display:none"
|
||||
tabindex="-1"
|
||||
/>
|
||||
<div
|
||||
aria-hidden="true"
|
||||
class="ant-tabs-tabpane"
|
||||
role="tabpanel"
|
||||
style="display:none"
|
||||
tabindex="-1"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`renders ./components/tabs/demo/disabled.md correctly 1`] = `
|
||||
<div
|
||||
class="ant-tabs ant-tabs-top"
|
||||
|
@ -3,6 +3,7 @@ import Tabs from '..';
|
||||
import mountTest from '../../../tests/shared/mountTest';
|
||||
import rtlTest from '../../../tests/shared/rtlTest';
|
||||
import { fireEvent, render } from '../../../tests/utils';
|
||||
import { resetWarned } from '../../_util/warning';
|
||||
|
||||
const { TabPane } = Tabs;
|
||||
|
||||
@ -105,4 +106,23 @@ describe('Tabs', () => {
|
||||
);
|
||||
expect(wrapper2.firstChild).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('deprecated warning', () => {
|
||||
resetWarned();
|
||||
const errorSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
|
||||
|
||||
const { container } = render(
|
||||
<Tabs>
|
||||
<TabPane />
|
||||
invalidate
|
||||
</Tabs>,
|
||||
);
|
||||
|
||||
expect(container.querySelectorAll('.ant-tabs-tab')).toHaveLength(1);
|
||||
|
||||
expect(errorSpy).toHaveBeenCalledWith(
|
||||
'Warning: [antd: Tabs] Tabs.TabPane is deprecated. Please use `items` directly.',
|
||||
);
|
||||
errorSpy.mockRestore();
|
||||
});
|
||||
});
|
||||
|
@ -17,24 +17,32 @@ Default activate first tab.
|
||||
import { Tabs } from 'antd';
|
||||
import React from 'react';
|
||||
|
||||
const { TabPane } = Tabs;
|
||||
|
||||
const onChange = (key: string) => {
|
||||
console.log(key);
|
||||
};
|
||||
|
||||
const App: React.FC = () => (
|
||||
<Tabs defaultActiveKey="1" onChange={onChange}>
|
||||
<TabPane tab="Tab 1" key="1">
|
||||
Content of Tab Pane 1
|
||||
</TabPane>
|
||||
<TabPane tab="Tab 2" key="2">
|
||||
Content of Tab Pane 2
|
||||
</TabPane>
|
||||
<TabPane tab="Tab 3" key="3">
|
||||
Content of Tab Pane 3
|
||||
</TabPane>
|
||||
</Tabs>
|
||||
<Tabs
|
||||
defaultActiveKey="1"
|
||||
onChange={onChange}
|
||||
items={[
|
||||
{
|
||||
label: `Tab 1`,
|
||||
key: '1',
|
||||
children: `Content of Tab Pane 1`,
|
||||
},
|
||||
{
|
||||
label: `Tab 2`,
|
||||
key: '2',
|
||||
children: `Content of Tab Pane 2`,
|
||||
},
|
||||
{
|
||||
label: `Tab 3`,
|
||||
key: '3',
|
||||
children: `Content of Tab Pane 3`,
|
||||
},
|
||||
]}
|
||||
/>
|
||||
);
|
||||
|
||||
export default App;
|
||||
|
@ -17,27 +17,24 @@ Should be used at the top of container, needs to override styles.
|
||||
import { Tabs } from 'antd';
|
||||
import React from 'react';
|
||||
|
||||
const { TabPane } = Tabs;
|
||||
const items = new Array(3).fill(null).map((_, i) => {
|
||||
const id = String(i + 1);
|
||||
return {
|
||||
label: `Tab Title ${id}`,
|
||||
key: id,
|
||||
children: (
|
||||
<>
|
||||
<p>Content of Tab Pane {id}</p>
|
||||
<p>Content of Tab Pane {id}</p>
|
||||
<p>Content of Tab Pane {id}</p>
|
||||
</>
|
||||
),
|
||||
};
|
||||
});
|
||||
|
||||
const App: React.FC = () => (
|
||||
<div className="card-container">
|
||||
<Tabs type="card">
|
||||
<TabPane tab="Tab Title 1" key="1">
|
||||
<p>Content of Tab Pane 1</p>
|
||||
<p>Content of Tab Pane 1</p>
|
||||
<p>Content of Tab Pane 1</p>
|
||||
</TabPane>
|
||||
<TabPane tab="Tab Title 2" key="2">
|
||||
<p>Content of Tab Pane 2</p>
|
||||
<p>Content of Tab Pane 2</p>
|
||||
<p>Content of Tab Pane 2</p>
|
||||
</TabPane>
|
||||
<TabPane tab="Tab Title 3" key="3">
|
||||
<p>Content of Tab Pane 3</p>
|
||||
<p>Content of Tab Pane 3</p>
|
||||
<p>Content of Tab Pane 3</p>
|
||||
</TabPane>
|
||||
</Tabs>
|
||||
<Tabs type="card" items={items} />
|
||||
</div>
|
||||
);
|
||||
|
||||
|
@ -17,24 +17,23 @@ Another type of Tabs, which doesn't support vertical mode.
|
||||
import { Tabs } from 'antd';
|
||||
import React from 'react';
|
||||
|
||||
const { TabPane } = Tabs;
|
||||
|
||||
const onChange = (key: string) => {
|
||||
console.log(key);
|
||||
};
|
||||
|
||||
const App: React.FC = () => (
|
||||
<Tabs onChange={onChange} type="card">
|
||||
<TabPane tab="Tab 1" key="1">
|
||||
Content of Tab Pane 1
|
||||
</TabPane>
|
||||
<TabPane tab="Tab 2" key="2">
|
||||
Content of Tab Pane 2
|
||||
</TabPane>
|
||||
<TabPane tab="Tab 3" key="3">
|
||||
Content of Tab Pane 3
|
||||
</TabPane>
|
||||
</Tabs>
|
||||
<Tabs
|
||||
onChange={onChange}
|
||||
type="card"
|
||||
items={new Array(3).fill(null).map((_, i) => {
|
||||
const id = String(i + 1);
|
||||
return {
|
||||
label: `Tab ${id}`,
|
||||
key: id,
|
||||
children: `Content of Tab Pane ${id}`,
|
||||
};
|
||||
})}
|
||||
/>
|
||||
);
|
||||
|
||||
export default App;
|
||||
|
@ -17,20 +17,19 @@ Centered tabs.
|
||||
import { Tabs } from 'antd';
|
||||
import React from 'react';
|
||||
|
||||
const { TabPane } = Tabs;
|
||||
|
||||
const App: React.FC = () => (
|
||||
<Tabs defaultActiveKey="1" centered>
|
||||
<TabPane tab="Tab 1" key="1">
|
||||
Content of Tab Pane 1
|
||||
</TabPane>
|
||||
<TabPane tab="Tab 2" key="2">
|
||||
Content of Tab Pane 2
|
||||
</TabPane>
|
||||
<TabPane tab="Tab 3" key="3">
|
||||
Content of Tab Pane 3
|
||||
</TabPane>
|
||||
</Tabs>
|
||||
<Tabs
|
||||
defaultActiveKey="1"
|
||||
centered
|
||||
items={new Array(3).fill(null).map((_, i) => {
|
||||
const id = String(i + 1);
|
||||
return {
|
||||
label: `Tab ${id}`,
|
||||
key: id,
|
||||
children: `Content of Tab Pane ${id}`,
|
||||
};
|
||||
})}
|
||||
/>
|
||||
);
|
||||
|
||||
export default App;
|
||||
|
@ -17,16 +17,14 @@ Hide default plus icon, and bind event for customized trigger.
|
||||
import { Button, Tabs } from 'antd';
|
||||
import React, { useRef, useState } from 'react';
|
||||
|
||||
const { TabPane } = Tabs;
|
||||
|
||||
const defaultPanes = Array.from({ length: 2 }).map((_, index) => {
|
||||
const defaultPanes = new Array(2).fill(null).map((_, index) => {
|
||||
const id = String(index + 1);
|
||||
return { title: `Tab ${id}`, content: `Content of Tab Pane ${index + 1}`, key: id };
|
||||
return { label: `Tab ${id}`, children: `Content of Tab Pane ${index + 1}`, key: id };
|
||||
});
|
||||
|
||||
const App: React.FC = () => {
|
||||
const [activeKey, setActiveKey] = useState(defaultPanes[0].key);
|
||||
const [panes, setPanes] = useState(defaultPanes);
|
||||
const [items, setItems] = useState(defaultPanes);
|
||||
const newTabIndex = useRef(0);
|
||||
|
||||
const onChange = (key: string) => {
|
||||
@ -35,18 +33,18 @@ const App: React.FC = () => {
|
||||
|
||||
const add = () => {
|
||||
const newActiveKey = `newTab${newTabIndex.current++}`;
|
||||
setPanes([...panes, { title: 'New Tab', content: 'New Tab Pane', key: newActiveKey }]);
|
||||
setItems([...items, { label: 'New Tab', children: 'New Tab Pane', key: newActiveKey }]);
|
||||
setActiveKey(newActiveKey);
|
||||
};
|
||||
|
||||
const remove = (targetKey: string) => {
|
||||
const targetIndex = panes.findIndex(pane => pane.key === targetKey);
|
||||
const newPanes = panes.filter(pane => pane.key !== targetKey);
|
||||
const targetIndex = items.findIndex(pane => pane.key === targetKey);
|
||||
const newPanes = items.filter(pane => pane.key !== targetKey);
|
||||
if (newPanes.length && targetKey === activeKey) {
|
||||
const { key } = newPanes[targetIndex === newPanes.length ? targetIndex - 1 : targetIndex];
|
||||
setActiveKey(key);
|
||||
}
|
||||
setPanes(newPanes);
|
||||
setItems(newPanes);
|
||||
};
|
||||
|
||||
const onEdit = (targetKey: string, action: 'add' | 'remove') => {
|
||||
@ -62,13 +60,14 @@ const App: React.FC = () => {
|
||||
<div style={{ marginBottom: 16 }}>
|
||||
<Button onClick={add}>ADD</Button>
|
||||
</div>
|
||||
<Tabs hideAdd onChange={onChange} activeKey={activeKey} type="editable-card" onEdit={onEdit}>
|
||||
{panes.map(pane => (
|
||||
<TabPane tab={pane.title} key={pane.key}>
|
||||
{pane.content}
|
||||
</TabPane>
|
||||
))}
|
||||
</Tabs>
|
||||
<Tabs
|
||||
hideAdd
|
||||
onChange={onChange}
|
||||
activeKey={activeKey}
|
||||
type="editable-card"
|
||||
onEdit={onEdit}
|
||||
items={items}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
@ -20,8 +20,6 @@ import React, { useRef, useState } from 'react';
|
||||
import { DndProvider, useDrag, useDrop } from 'react-dnd';
|
||||
import { HTML5Backend } from 'react-dnd-html5-backend';
|
||||
|
||||
const { TabPane } = Tabs;
|
||||
|
||||
const type = 'DraggableTabNode';
|
||||
interface DraggableTabPaneProps extends React.HTMLAttributes<HTMLDivElement> {
|
||||
index: React.Key;
|
||||
@ -62,16 +60,16 @@ const DraggableTabNode = ({ index, children, moveNode }: DraggableTabPaneProps)
|
||||
);
|
||||
};
|
||||
|
||||
const DraggableTabs: React.FC<{ children: React.ReactNode }> = props => {
|
||||
const { children } = props;
|
||||
const DraggableTabs: React.FC<TabsProps> = props => {
|
||||
const { items = [] } = props;
|
||||
const [order, setOrder] = useState<React.Key[]>([]);
|
||||
|
||||
const moveTabNode = (dragKey: React.Key, hoverKey: React.Key) => {
|
||||
const newOrder = order.slice();
|
||||
|
||||
React.Children.forEach(children, (c: React.ReactElement) => {
|
||||
if (c.key && newOrder.indexOf(c.key) === -1) {
|
||||
newOrder.push(c.key);
|
||||
items.forEach(item => {
|
||||
if (item.key && newOrder.indexOf(item.key) === -1) {
|
||||
newOrder.push(item.key);
|
||||
}
|
||||
});
|
||||
|
||||
@ -94,12 +92,7 @@ const DraggableTabs: React.FC<{ children: React.ReactNode }> = props => {
|
||||
</DefaultTabBar>
|
||||
);
|
||||
|
||||
const tabs: React.ReactElement[] = [];
|
||||
React.Children.forEach(children, (c: React.ReactElement) => {
|
||||
tabs.push(c);
|
||||
});
|
||||
|
||||
const orderTabs = tabs.slice().sort((a, b) => {
|
||||
const orderItems = [...items].sort((a, b) => {
|
||||
const orderA = order.indexOf(a.key!);
|
||||
const orderB = order.indexOf(b.key!);
|
||||
|
||||
@ -113,33 +106,30 @@ const DraggableTabs: React.FC<{ children: React.ReactNode }> = props => {
|
||||
return 1;
|
||||
}
|
||||
|
||||
const ia = tabs.indexOf(a);
|
||||
const ib = tabs.indexOf(b);
|
||||
const ia = items.indexOf(a);
|
||||
const ib = items.indexOf(b);
|
||||
|
||||
return ia - ib;
|
||||
});
|
||||
|
||||
return (
|
||||
<DndProvider backend={HTML5Backend}>
|
||||
<Tabs renderTabBar={renderTabBar} {...props}>
|
||||
{orderTabs}
|
||||
</Tabs>
|
||||
<Tabs renderTabBar={renderTabBar} {...props} items={orderItems} />
|
||||
</DndProvider>
|
||||
);
|
||||
};
|
||||
|
||||
const App: React.FC = () => (
|
||||
<DraggableTabs>
|
||||
<TabPane tab="tab 1" key="1">
|
||||
Content of Tab Pane 1
|
||||
</TabPane>
|
||||
<TabPane tab="tab 2" key="2">
|
||||
Content of Tab Pane 2
|
||||
</TabPane>
|
||||
<TabPane tab="tab 3" key="3">
|
||||
Content of Tab Pane 3
|
||||
</TabPane>
|
||||
</DraggableTabs>
|
||||
<DraggableTabs
|
||||
items={new Array(3).fill(null).map((_, i) => {
|
||||
const id = String(i + 1);
|
||||
return {
|
||||
label: `tab ${id}`,
|
||||
key: id,
|
||||
children: `Content of Tab Pane ${id}`,
|
||||
};
|
||||
})}
|
||||
/>
|
||||
);
|
||||
|
||||
export default App;
|
||||
|
@ -19,8 +19,6 @@ import { Tabs } from 'antd';
|
||||
import React from 'react';
|
||||
import { Sticky, StickyContainer } from 'react-sticky';
|
||||
|
||||
const { TabPane } = Tabs;
|
||||
|
||||
const renderTabBar: TabsProps['renderTabBar'] = (props, DefaultTabBar) => (
|
||||
<Sticky bottomOffset={80}>
|
||||
{({ style }) => (
|
||||
@ -29,19 +27,19 @@ const renderTabBar: TabsProps['renderTabBar'] = (props, DefaultTabBar) => (
|
||||
</Sticky>
|
||||
);
|
||||
|
||||
const items = new Array(3).fill(null).map((_, i) => {
|
||||
const id = String(i + 1);
|
||||
return {
|
||||
label: `Tab ${id}`,
|
||||
key: id,
|
||||
children: `Content of Tab Pane ${id}`,
|
||||
style: i === 0 ? { height: 200 } : undefined,
|
||||
};
|
||||
});
|
||||
|
||||
const App: React.FC = () => (
|
||||
<StickyContainer>
|
||||
<Tabs defaultActiveKey="1" renderTabBar={renderTabBar}>
|
||||
<TabPane tab="Tab 1" key="1" style={{ height: 200 }}>
|
||||
Content of Tab Pane 1
|
||||
</TabPane>
|
||||
<TabPane tab="Tab 2" key="2">
|
||||
Content of Tab Pane 2
|
||||
</TabPane>
|
||||
<TabPane tab="Tab 3" key="3">
|
||||
Content of Tab Pane 3
|
||||
</TabPane>
|
||||
</Tabs>
|
||||
<Tabs defaultActiveKey="1" renderTabBar={renderTabBar} items={items} />
|
||||
</StickyContainer>
|
||||
);
|
||||
|
||||
|
36
components/tabs/demo/deprecated.md
Normal file
36
components/tabs/demo/deprecated.md
Normal file
@ -0,0 +1,36 @@
|
||||
---
|
||||
order: -1
|
||||
title:
|
||||
zh-CN: 基础用法(废弃的语法糖)
|
||||
en-US: Basic usage (deprecated syntactic sugar)
|
||||
version: < 4.23.0
|
||||
---
|
||||
|
||||
## zh-CN
|
||||
|
||||
默认选中第一项。
|
||||
|
||||
## en-US
|
||||
|
||||
Default activate first tab.
|
||||
|
||||
```tsx
|
||||
import { Tabs } from 'antd';
|
||||
import React from 'react';
|
||||
|
||||
const App: React.FC = () => (
|
||||
<Tabs defaultActiveKey="1">
|
||||
<Tabs.TabPane tab="Tab 1" key="1">
|
||||
Content of Tab Pane 1
|
||||
</Tabs.TabPane>
|
||||
<Tabs.TabPane tab="Tab 2" key="2">
|
||||
Content of Tab Pane 2
|
||||
</Tabs.TabPane>
|
||||
<Tabs.TabPane tab="Tab 3" key="3">
|
||||
Content of Tab Pane 3
|
||||
</Tabs.TabPane>
|
||||
</Tabs>
|
||||
);
|
||||
|
||||
export default App;
|
||||
```
|
@ -17,20 +17,28 @@ Disabled a tab.
|
||||
import { Tabs } from 'antd';
|
||||
import React from 'react';
|
||||
|
||||
const { TabPane } = Tabs;
|
||||
|
||||
const App: React.FC = () => (
|
||||
<Tabs defaultActiveKey="1">
|
||||
<TabPane tab="Tab 1" key="1">
|
||||
Tab 1
|
||||
</TabPane>
|
||||
<TabPane tab="Tab 2" disabled key="2">
|
||||
Tab 2
|
||||
</TabPane>
|
||||
<TabPane tab="Tab 3" key="3">
|
||||
Tab 3
|
||||
</TabPane>
|
||||
</Tabs>
|
||||
<Tabs
|
||||
defaultActiveKey="1"
|
||||
items={[
|
||||
{
|
||||
label: 'Tab 1',
|
||||
key: '1',
|
||||
children: 'Tab 1',
|
||||
},
|
||||
{
|
||||
label: 'Tab 2',
|
||||
key: '2',
|
||||
children: 'Tab 2',
|
||||
disabled: true,
|
||||
},
|
||||
{
|
||||
label: 'Tab 3',
|
||||
key: '3',
|
||||
children: 'Tab 3',
|
||||
},
|
||||
]}
|
||||
/>
|
||||
);
|
||||
|
||||
export default App;
|
||||
|
@ -17,22 +17,20 @@ Only card type Tabs support adding & closable. +Use `closable={false}` to disabl
|
||||
import { Tabs } from 'antd';
|
||||
import React, { useRef, useState } from 'react';
|
||||
|
||||
const { TabPane } = Tabs;
|
||||
|
||||
const initialPanes = [
|
||||
{ title: 'Tab 1', content: 'Content of Tab 1', key: '1' },
|
||||
{ title: 'Tab 2', content: 'Content of Tab 2', key: '2' },
|
||||
const initialItems = [
|
||||
{ label: 'Tab 1', children: 'Content of Tab 1', key: '1' },
|
||||
{ label: 'Tab 2', children: 'Content of Tab 2', key: '2' },
|
||||
{
|
||||
title: 'Tab 3',
|
||||
content: 'Content of Tab 3',
|
||||
label: 'Tab 3',
|
||||
children: 'Content of Tab 3',
|
||||
key: '3',
|
||||
closable: false,
|
||||
},
|
||||
];
|
||||
|
||||
const App: React.FC = () => {
|
||||
const [activeKey, setActiveKey] = useState(initialPanes[0].key);
|
||||
const [panes, setPanes] = useState(initialPanes);
|
||||
const [activeKey, setActiveKey] = useState(initialItems[0].key);
|
||||
const [items, setItems] = useState(initialItems);
|
||||
const newTabIndex = useRef(0);
|
||||
|
||||
const onChange = (newActiveKey: string) => {
|
||||
@ -41,21 +39,21 @@ const App: React.FC = () => {
|
||||
|
||||
const add = () => {
|
||||
const newActiveKey = `newTab${newTabIndex.current++}`;
|
||||
const newPanes = [...panes];
|
||||
newPanes.push({ title: 'New Tab', content: 'Content of new Tab', key: newActiveKey });
|
||||
setPanes(newPanes);
|
||||
const newPanes = [...items];
|
||||
newPanes.push({ label: 'New Tab', children: 'Content of new Tab', key: newActiveKey });
|
||||
setItems(newPanes);
|
||||
setActiveKey(newActiveKey);
|
||||
};
|
||||
|
||||
const remove = (targetKey: string) => {
|
||||
let newActiveKey = activeKey;
|
||||
let lastIndex = -1;
|
||||
panes.forEach((pane, i) => {
|
||||
if (pane.key === targetKey) {
|
||||
items.forEach((item, i) => {
|
||||
if (item.key === targetKey) {
|
||||
lastIndex = i - 1;
|
||||
}
|
||||
});
|
||||
const newPanes = panes.filter(pane => pane.key !== targetKey);
|
||||
const newPanes = items.filter(item => item.key !== targetKey);
|
||||
if (newPanes.length && newActiveKey === targetKey) {
|
||||
if (lastIndex >= 0) {
|
||||
newActiveKey = newPanes[lastIndex].key;
|
||||
@ -63,7 +61,7 @@ const App: React.FC = () => {
|
||||
newActiveKey = newPanes[0].key;
|
||||
}
|
||||
}
|
||||
setPanes(newPanes);
|
||||
setItems(newPanes);
|
||||
setActiveKey(newActiveKey);
|
||||
};
|
||||
|
||||
@ -76,13 +74,13 @@ const App: React.FC = () => {
|
||||
};
|
||||
|
||||
return (
|
||||
<Tabs type="editable-card" onChange={onChange} activeKey={activeKey} onEdit={onEdit}>
|
||||
{panes.map(pane => (
|
||||
<TabPane tab={pane.title} key={pane.key} closable={pane.closable}>
|
||||
{pane.content}
|
||||
</TabPane>
|
||||
))}
|
||||
</Tabs>
|
||||
<Tabs
|
||||
type="editable-card"
|
||||
onChange={onChange}
|
||||
activeKey={activeKey}
|
||||
onEdit={onEdit}
|
||||
items={items}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -17,8 +17,6 @@ You can add extra actions to the right or left or even both side of Tabs.
|
||||
import { Button, Checkbox, Divider, Tabs } from 'antd';
|
||||
import React, { useMemo, useState } from 'react';
|
||||
|
||||
const { TabPane } = Tabs;
|
||||
|
||||
const CheckboxGroup = Checkbox.Group;
|
||||
|
||||
const operations = <Button>Extra Action</Button>;
|
||||
@ -32,6 +30,15 @@ const options = ['left', 'right'];
|
||||
|
||||
type PositionType = 'left' | 'right';
|
||||
|
||||
const items = new Array(3).fill(null).map((_, i) => {
|
||||
const id = String(i + 1);
|
||||
return {
|
||||
label: `Tab ${id}`,
|
||||
key: id,
|
||||
children: `Content of tab ${id}`,
|
||||
};
|
||||
});
|
||||
|
||||
const App: React.FC = () => {
|
||||
const [position, setPosition] = useState<PositionType[]>(['left', 'right']);
|
||||
|
||||
@ -46,17 +53,7 @@ const App: React.FC = () => {
|
||||
|
||||
return (
|
||||
<>
|
||||
<Tabs tabBarExtraContent={operations}>
|
||||
<TabPane tab="Tab 1" key="1">
|
||||
Content of tab 1
|
||||
</TabPane>
|
||||
<TabPane tab="Tab 2" key="2">
|
||||
Content of tab 2
|
||||
</TabPane>
|
||||
<TabPane tab="Tab 3" key="3">
|
||||
Content of tab 3
|
||||
</TabPane>
|
||||
</Tabs>
|
||||
<Tabs tabBarExtraContent={operations} items={items} />
|
||||
<br />
|
||||
<br />
|
||||
<br />
|
||||
@ -71,17 +68,7 @@ const App: React.FC = () => {
|
||||
/>
|
||||
<br />
|
||||
<br />
|
||||
<Tabs tabBarExtraContent={slot}>
|
||||
<TabPane tab="Tab 1" key="1">
|
||||
Content of tab 1
|
||||
</TabPane>
|
||||
<TabPane tab="Tab 2" key="2">
|
||||
Content of tab 2
|
||||
</TabPane>
|
||||
<TabPane tab="Tab 3" key="3">
|
||||
Content of tab 3
|
||||
</TabPane>
|
||||
</Tabs>
|
||||
<Tabs tabBarExtraContent={slot} items={items} />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
@ -18,33 +18,24 @@ import { AndroidOutlined, AppleOutlined } from '@ant-design/icons';
|
||||
import { Tabs } from 'antd';
|
||||
import React from 'react';
|
||||
|
||||
const { TabPane } = Tabs;
|
||||
|
||||
const App: React.FC = () => (
|
||||
<Tabs defaultActiveKey="2">
|
||||
<TabPane
|
||||
tab={
|
||||
<Tabs
|
||||
defaultActiveKey="2"
|
||||
items={[AppleOutlined, AndroidOutlined].map((Icon, i) => {
|
||||
const id = String(i + 1);
|
||||
|
||||
return {
|
||||
label: (
|
||||
<span>
|
||||
<AppleOutlined />
|
||||
Tab 1
|
||||
<Icon />
|
||||
Tab {id}
|
||||
</span>
|
||||
}
|
||||
key="1"
|
||||
>
|
||||
Tab 1
|
||||
</TabPane>
|
||||
<TabPane
|
||||
tab={
|
||||
<span>
|
||||
<AndroidOutlined />
|
||||
Tab 2
|
||||
</span>
|
||||
}
|
||||
key="2"
|
||||
>
|
||||
Tab 2
|
||||
</TabPane>
|
||||
</Tabs>
|
||||
),
|
||||
key: id,
|
||||
children: `Tab ${id}`,
|
||||
};
|
||||
})}
|
||||
/>
|
||||
);
|
||||
|
||||
export default App;
|
||||
|
@ -18,13 +18,10 @@ Default activate first tab.
|
||||
import { Select, Tabs } from 'antd';
|
||||
import React, { useState } from 'react';
|
||||
|
||||
const { TabPane } = Tabs;
|
||||
const { Option } = Select;
|
||||
|
||||
const positionList = ['left', 'right', 'top', 'bottom'];
|
||||
|
||||
const list = Array.from({ length: 20 }).map((_, index) => index);
|
||||
|
||||
const App: React.FC = () => {
|
||||
const [parentPos, setParentPos] = useState(undefined);
|
||||
const [childPos, setChildPos] = useState(undefined);
|
||||
@ -81,25 +78,39 @@ const App: React.FC = () => {
|
||||
<Option value="editable-card">Parent - card edit</Option>
|
||||
</Select>
|
||||
|
||||
<Tabs defaultActiveKey="1" tabPosition={parentPos} type={parentType}>
|
||||
<TabPane tab="Tab 1" key="1">
|
||||
<Tabs
|
||||
defaultActiveKey="1"
|
||||
tabPosition={parentPos}
|
||||
type={parentType}
|
||||
items={[
|
||||
{
|
||||
label: 'Tab 1',
|
||||
key: '1',
|
||||
children: (
|
||||
<Tabs
|
||||
defaultActiveKey="1"
|
||||
tabPosition={childPos}
|
||||
type={childType}
|
||||
style={{ height: 300 }}
|
||||
>
|
||||
{list.map(key => (
|
||||
<TabPane tab={`Tab ${key}`} key={key}>
|
||||
TTTT {key}
|
||||
</TabPane>
|
||||
))}
|
||||
</Tabs>
|
||||
</TabPane>
|
||||
<TabPane tab="Tab 2" key="2">
|
||||
Content of Tab Pane 2
|
||||
</TabPane>
|
||||
</Tabs>
|
||||
items={new Array(20).fill(null).map((_, index) => {
|
||||
const key = String(index);
|
||||
|
||||
return {
|
||||
label: `Tab ${key}`,
|
||||
key,
|
||||
children: `TTTT ${key}`,
|
||||
};
|
||||
})}
|
||||
/>
|
||||
),
|
||||
},
|
||||
{
|
||||
label: 'Tab 2',
|
||||
key: '2',
|
||||
children: 'Content of Tab Pane 2',
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
@ -18,8 +18,6 @@ import type { RadioChangeEvent } from 'antd';
|
||||
import { Radio, Space, Tabs } from 'antd';
|
||||
import React, { useState } from 'react';
|
||||
|
||||
const { TabPane } = Tabs;
|
||||
|
||||
type TabPosition = 'left' | 'right' | 'top' | 'bottom';
|
||||
|
||||
const App: React.FC = () => {
|
||||
@ -40,17 +38,17 @@ const App: React.FC = () => {
|
||||
<Radio.Button value="right">right</Radio.Button>
|
||||
</Radio.Group>
|
||||
</Space>
|
||||
<Tabs tabPosition={tabPosition}>
|
||||
<TabPane tab="Tab 1" key="1">
|
||||
Content of Tab 1
|
||||
</TabPane>
|
||||
<TabPane tab="Tab 2" key="2">
|
||||
Content of Tab 2
|
||||
</TabPane>
|
||||
<TabPane tab="Tab 3" key="3">
|
||||
Content of Tab 3
|
||||
</TabPane>
|
||||
</Tabs>
|
||||
<Tabs
|
||||
tabPosition={tabPosition}
|
||||
items={new Array(3).fill(null).map((_, i) => {
|
||||
const id = String(i + 1);
|
||||
return {
|
||||
label: `Tab ${id}`,
|
||||
key: id,
|
||||
children: `Content of Tab ${id}`,
|
||||
};
|
||||
})}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
@ -19,8 +19,6 @@ import { Radio, Tabs } from 'antd';
|
||||
import type { SizeType } from 'antd/es/config-provider/SizeContext';
|
||||
import React, { useState } from 'react';
|
||||
|
||||
const { TabPane } = Tabs;
|
||||
|
||||
const App: React.FC = () => {
|
||||
const [size, setSize] = useState<SizeType>('small');
|
||||
|
||||
@ -35,28 +33,32 @@ const App: React.FC = () => {
|
||||
<Radio.Button value="middle">Middle</Radio.Button>
|
||||
<Radio.Button value="large">Large</Radio.Button>
|
||||
</Radio.Group>
|
||||
<Tabs defaultActiveKey="1" size={size} style={{ marginBottom: 32 }}>
|
||||
<TabPane tab="Tab 1" key="1">
|
||||
Content of tab 1
|
||||
</TabPane>
|
||||
<TabPane tab="Tab 2" key="2">
|
||||
Content of tab 2
|
||||
</TabPane>
|
||||
<TabPane tab="Tab 3" key="3">
|
||||
Content of tab 3
|
||||
</TabPane>
|
||||
</Tabs>
|
||||
<Tabs defaultActiveKey="1" type="card" size={size}>
|
||||
<TabPane tab="Card Tab 1" key="1">
|
||||
Content of card tab 1
|
||||
</TabPane>
|
||||
<TabPane tab="Card Tab 2" key="2">
|
||||
Content of card tab 2
|
||||
</TabPane>
|
||||
<TabPane tab="Card Tab 3" key="3">
|
||||
Content of card tab 3
|
||||
</TabPane>
|
||||
</Tabs>
|
||||
<Tabs
|
||||
defaultActiveKey="1"
|
||||
size={size}
|
||||
style={{ marginBottom: 32 }}
|
||||
items={new Array(3).fill(null).map((_, i) => {
|
||||
const id = String(i + 1);
|
||||
return {
|
||||
label: `Tab ${id}`,
|
||||
key: id,
|
||||
children: `Content of tab ${id}`,
|
||||
};
|
||||
})}
|
||||
/>
|
||||
<Tabs
|
||||
defaultActiveKey="1"
|
||||
type="card"
|
||||
size={size}
|
||||
items={new Array(3).fill(null).map((_, i) => {
|
||||
const id = String(i + 1);
|
||||
return {
|
||||
label: `Card Tab ${id}`,
|
||||
key: id,
|
||||
children: `Content of card tab ${id}`,
|
||||
};
|
||||
})}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
@ -18,8 +18,6 @@ import type { RadioChangeEvent } from 'antd';
|
||||
import { Radio, Tabs } from 'antd';
|
||||
import React, { useState } from 'react';
|
||||
|
||||
const { TabPane } = Tabs;
|
||||
|
||||
type TabPosition = 'left' | 'right' | 'top' | 'bottom';
|
||||
|
||||
const App: React.FC = () => {
|
||||
@ -35,13 +33,20 @@ const App: React.FC = () => {
|
||||
<Radio.Button value="top">Horizontal</Radio.Button>
|
||||
<Radio.Button value="left">Vertical</Radio.Button>
|
||||
</Radio.Group>
|
||||
<Tabs defaultActiveKey="1" tabPosition={mode} style={{ height: 220 }}>
|
||||
{[...Array.from({ length: 30 }, (_, i) => i)].map(i => (
|
||||
<TabPane tab={`Tab-${i}`} key={i} disabled={i === 28}>
|
||||
Content of tab {i}
|
||||
</TabPane>
|
||||
))}
|
||||
</Tabs>
|
||||
<Tabs
|
||||
defaultActiveKey="1"
|
||||
tabPosition={mode}
|
||||
style={{ height: 220 }}
|
||||
items={new Array(30).fill(null).map((_, i) => {
|
||||
const id = String(i);
|
||||
return {
|
||||
label: `Tab-${id}`,
|
||||
key: id,
|
||||
disabled: i === 28,
|
||||
children: `Content of tab ${id}`,
|
||||
};
|
||||
})}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
35
components/tabs/hooks/useLegacyItems.ts
Normal file
35
components/tabs/hooks/useLegacyItems.ts
Normal file
@ -0,0 +1,35 @@
|
||||
import * as React from 'react';
|
||||
import toArray from 'rc-util/lib/Children/toArray';
|
||||
import type { Tab } from 'rc-tabs/lib/interface';
|
||||
import type { TabsProps, TabPaneProps } from '..';
|
||||
import warning from '../../_util/warning';
|
||||
|
||||
function filter<T>(items: (T | null)[]): T[] {
|
||||
return items.filter(item => item) as T[];
|
||||
}
|
||||
|
||||
export default function useLegacyItems(items?: TabsProps['items'], children?: React.ReactNode) {
|
||||
if (items) {
|
||||
return items;
|
||||
}
|
||||
|
||||
warning(!children, 'Tabs', 'Tabs.TabPane is deprecated. Please use `items` directly.');
|
||||
|
||||
const childrenItems = toArray(children).map((node: React.ReactElement<TabPaneProps>) => {
|
||||
if (React.isValidElement(node)) {
|
||||
const { key, props } = node;
|
||||
const { tab, ...restProps } = props || {};
|
||||
|
||||
const item: Tab = {
|
||||
key: String(key),
|
||||
...restProps,
|
||||
label: tab,
|
||||
};
|
||||
return item;
|
||||
}
|
||||
|
||||
return null;
|
||||
});
|
||||
|
||||
return filter(childrenItems);
|
||||
}
|
@ -16,6 +16,32 @@ Ant Design has 3 types of Tabs for different situations.
|
||||
- Normal Tabs: for functional aspects of a page.
|
||||
- [Radio.Button](/components/radio/#components-radio-demo-radiobutton): for secondary tabs.
|
||||
|
||||
### Usage upgrade after 4.23.0
|
||||
|
||||
```__react
|
||||
import Alert from '../alert';
|
||||
ReactDOM.render(<Alert message="After version 4.23.0, we provide a simpler usage <Tabs items={[...]} /> with better performance and potential of writing simpler code style in your applications. Meanwhile, we deprecated the old usage in browser console, we will remove it in antd 5.0." />, mountNode);
|
||||
```
|
||||
|
||||
```jsx
|
||||
// works when >=4.23.0, recommended ✅
|
||||
const items = [
|
||||
{ label: 'Tab 1', key: 'item-1', children: 'Content 1' }, // remember to pass the key prop
|
||||
{ label: 'Tab 2', key: 'item-2', children: 'Content 2' },
|
||||
];
|
||||
return <Tabs items={items} />;
|
||||
|
||||
// works when <4.23.0, deprecated when >=4.23.0 🙅🏻♀️
|
||||
<Tabs>
|
||||
<Tabs.TabPane tab="Tab 1" key="item-1">
|
||||
Content 1
|
||||
</Tabs.TabPane>
|
||||
<Tabs.TabPane tab="Tab 2" key="item-2">
|
||||
Content 2
|
||||
</Tabs.TabPane>
|
||||
</Tabs>;
|
||||
```
|
||||
|
||||
## API
|
||||
|
||||
### Tabs
|
||||
|
@ -3,7 +3,7 @@ import EllipsisOutlined from '@ant-design/icons/EllipsisOutlined';
|
||||
import PlusOutlined from '@ant-design/icons/PlusOutlined';
|
||||
import classNames from 'classnames';
|
||||
import type { TabsProps as RcTabsProps } from 'rc-tabs';
|
||||
import RcTabs, { TabPane, TabPaneProps } from 'rc-tabs';
|
||||
import RcTabs from 'rc-tabs';
|
||||
import type { EditableConfig } from 'rc-tabs/lib/interface';
|
||||
import * as React from 'react';
|
||||
|
||||
@ -11,6 +11,8 @@ import { ConfigContext } from '../config-provider';
|
||||
import type { SizeType } from '../config-provider/SizeContext';
|
||||
import SizeContext from '../config-provider/SizeContext';
|
||||
import warning from '../_util/warning';
|
||||
import useLegacyItems from './hooks/useLegacyItems';
|
||||
import TabPane, { TabPaneProps } from './TabPane';
|
||||
|
||||
export type TabsType = 'line' | 'card' | 'editable-card';
|
||||
export type TabsPosition = 'top' | 'right' | 'bottom' | 'left';
|
||||
@ -24,6 +26,7 @@ export interface TabsProps extends Omit<RcTabsProps, 'editable'> {
|
||||
centered?: boolean;
|
||||
addIcon?: React.ReactNode;
|
||||
onEdit?: (e: React.MouseEvent | React.KeyboardEvent | string, action: 'add' | 'remove') => void;
|
||||
children?: React.ReactNode;
|
||||
}
|
||||
|
||||
function Tabs({
|
||||
@ -34,6 +37,8 @@ function Tabs({
|
||||
hideAdd,
|
||||
centered,
|
||||
addIcon,
|
||||
children,
|
||||
items,
|
||||
...props
|
||||
}: TabsProps) {
|
||||
const { prefixCls: customizePrefixCls, moreIcon = <EllipsisOutlined /> } = props;
|
||||
@ -59,6 +64,8 @@ function Tabs({
|
||||
'`onPrevClick` and `onNextClick` has been removed. Please use `onTabScroll` instead.',
|
||||
);
|
||||
|
||||
const mergedItems = useLegacyItems(items, children);
|
||||
|
||||
return (
|
||||
<SizeContext.Consumer>
|
||||
{contextSize => {
|
||||
@ -68,6 +75,7 @@ function Tabs({
|
||||
direction={direction}
|
||||
moreTransitionName={`${rootPrefixCls}-slide-up`}
|
||||
{...props}
|
||||
items={mergedItems}
|
||||
className={classNames(
|
||||
{
|
||||
[`${prefixCls}-${size}`]: size,
|
||||
|
@ -19,6 +19,32 @@ Ant Design 依次提供了三级选项卡,分别用于不同的场景。
|
||||
- 既可用于容器顶部,也可用于容器内部,是最通用的 Tabs。
|
||||
- [Radio.Button](/components/radio/#components-radio-demo-radiobutton) 可作为更次级的页签来使用。
|
||||
|
||||
### 4.23.0 用法升级
|
||||
|
||||
```__react
|
||||
import Alert from '../alert';
|
||||
ReactDOM.render(<Alert message="在 4.23.0 版本后,我们提供了 <Tabs items={[...]} /> 的简写方式,有更好的性能和更方便的数据组织方式,开发者不再需要自行拼接 JSX。同时我们废弃了原先的写法,你还是可以在 4.x 继续使用,但会在控制台看到警告,并会在 5.0 后移除。" />, mountNode);
|
||||
```
|
||||
|
||||
```jsx
|
||||
// >=4.23.0 可用,推荐的写法 ✅
|
||||
const items = [
|
||||
{ label: '项目 1', key: 'item-1', children: '内容 1' }, // 务必填写 key
|
||||
{ label: '项目 2', key: 'item-2', children: '内容 2' },
|
||||
];
|
||||
return <Tabs items={items} />;
|
||||
|
||||
// <4.23.0 可用,>=4.23.0 时不推荐 🙅🏻♀️
|
||||
<Tabs>
|
||||
<Tabs.TabPane tab="项目 1" key="item-1">
|
||||
内容 1
|
||||
</Tabs.TabPane>
|
||||
<Tabs.TabPane tab="项目 2" key="item-2">
|
||||
内容 2
|
||||
</Tabs.TabPane>
|
||||
</Tabs>;
|
||||
```
|
||||
|
||||
## API
|
||||
|
||||
### Tabs
|
||||
@ -31,6 +57,7 @@ Ant Design 依次提供了三级选项卡,分别用于不同的场景。
|
||||
| centered | 标签居中展示 | boolean | false | 4.4.0 |
|
||||
| defaultActiveKey | 初始化选中面板的 key,如果没有设置 activeKey | string | `第一个面板` | |
|
||||
| hideAdd | 是否隐藏加号图标,在 `type="editable-card"` 时有效 | boolean | false | |
|
||||
| items | 配置选项卡内容 | [TabItem](#TabItem) | [] | 4.23.0 |
|
||||
| moreIcon | 自定义折叠 icon | ReactNode | <EllipsisOutlined /> | 4.14.0 |
|
||||
| popupClassName | 更多菜单的 `className` | string | - | 4.21.0 |
|
||||
| renderTabBar | 替换 TabBar,用于二次封装标签头 | (props: DefaultTabBarProps, DefaultTabBar: React.ComponentClass) => React.ReactElement | - | |
|
||||
@ -48,7 +75,7 @@ Ant Design 依次提供了三级选项卡,分别用于不同的场景。
|
||||
|
||||
> 更多属性查看 [rc-tabs tabs](https://github.com/react-component/tabs#tabs)
|
||||
|
||||
### Tabs.TabPane
|
||||
### TabItem
|
||||
|
||||
| 参数 | 说明 | 类型 | 默认值 |
|
||||
| ----------- | ----------------------------------------------- | --------- | ------ |
|
||||
@ -56,6 +83,5 @@ Ant Design 依次提供了三级选项卡,分别用于不同的场景。
|
||||
| disabled | 禁用某一项 | boolean | false |
|
||||
| forceRender | 被隐藏时是否渲染 DOM 结构 | boolean | false |
|
||||
| key | 对应 activeKey | string | - |
|
||||
| tab | 选项卡头显示文字 | ReactNode | - |
|
||||
|
||||
> 更多属性查看 [rc-tabs tabpane](https://github.com/react-component/tabs#tabpane)
|
||||
| label | 选项卡头显示文字 | ReactNode | - |
|
||||
| children | 选项卡头显示内容 | ReactNode | - |
|
||||
|
@ -149,7 +149,7 @@
|
||||
"rc-steps": "~4.1.0",
|
||||
"rc-switch": "~3.2.0",
|
||||
"rc-table": "~7.26.0",
|
||||
"rc-tabs": "~11.16.0",
|
||||
"rc-tabs": "~12.0.0-alpha.1",
|
||||
"rc-textarea": "~0.3.0",
|
||||
"rc-tooltip": "~5.2.0",
|
||||
"rc-tree": "~5.6.5",
|
||||
|
@ -499,7 +499,11 @@ ReactDOM.render(<Demo />, document.getElementById('container'));
|
||||
);
|
||||
|
||||
if (meta.version) {
|
||||
codeBox = <Badge.Ribbon text={meta.version}>{codeBox}</Badge.Ribbon>;
|
||||
codeBox = (
|
||||
<Badge.Ribbon text={meta.version} color={meta.version.includes('<') ? 'red' : null}>
|
||||
{codeBox}
|
||||
</Badge.Ribbon>
|
||||
);
|
||||
}
|
||||
|
||||
return codeBox;
|
||||
|
Loading…
Reference in New Issue
Block a user