diff --git a/components/card/__tests__/__snapshots__/demo.test.js.snap b/components/card/__tests__/__snapshots__/demo.test.js.snap
index 62e9666410..304f9d17b7 100644
--- a/components/card/__tests__/__snapshots__/demo.test.js.snap
+++ b/components/card/__tests__/__snapshots__/demo.test.js.snap
@@ -674,7 +674,7 @@ exports[`renders ./components/card/demo/tabs.md correctly 1`] = `
class="ant-tabs ant-tabs-top ant-card-head-tabs ant-tabs-large ant-tabs-line"
>
@@ -809,7 +809,7 @@ exports[`renders ./components/card/demo/tabs.md correctly 1`] = `
class="ant-tabs ant-tabs-top ant-card-head-tabs ant-tabs-large ant-tabs-line"
>
diff --git a/components/tabs/TabBar.tsx b/components/tabs/TabBar.tsx
new file mode 100644
index 0000000000..76d22a9da5
--- /dev/null
+++ b/components/tabs/TabBar.tsx
@@ -0,0 +1,51 @@
+import * as React from 'react';
+import Icon from '../icon';
+import ScrollableInkTabBar from 'rc-tabs/lib/ScrollableInkTabBar';
+import { TabsProps } from './index';
+
+export default class TabBar extends React.Component
{
+ render() {
+ const {
+ tabBarStyle,
+ animated = true,
+ renderTabBar,
+ tabBarExtraContent,
+ tabPosition,
+ prefixCls,
+ } = this.props;
+ const inkBarAnimated = typeof animated === 'object' ? animated.inkBar : animated;
+
+ const isVertical = tabPosition === 'left' || tabPosition === 'right';
+ const prevIconType = isVertical ? 'up' : 'left';
+ const nextIconType = isVertical ? 'down' : 'right';
+ const prevIcon = (
+
+
+
+ );
+ const nextIcon = (
+
+
+
+ );
+
+ const renderProps = {
+ ...this.props,
+ inkBarAnimated,
+ extraContent: tabBarExtraContent,
+ style: tabBarStyle,
+ prevIcon,
+ nextIcon,
+ };
+
+ let RenderTabBar: React.ReactElement;
+
+ if (renderTabBar) {
+ RenderTabBar = renderTabBar(renderProps, ScrollableInkTabBar);
+ } else {
+ RenderTabBar = ;
+ }
+
+ return React.cloneElement(RenderTabBar);
+ }
+}
diff --git a/components/tabs/__tests__/__snapshots__/demo.test.js.snap b/components/tabs/__tests__/__snapshots__/demo.test.js.snap
index d3fa6d0f20..4c6d7b7488 100644
--- a/components/tabs/__tests__/__snapshots__/demo.test.js.snap
+++ b/components/tabs/__tests__/__snapshots__/demo.test.js.snap
@@ -576,6 +576,145 @@ exports[`renders ./components/tabs/demo/custom-add-trigger.md correctly 1`] = `
`;
+exports[`renders ./components/tabs/demo/custom-tab-bar.md correctly 1`] = `
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Content of Tab Pane 1
+
+
+
+
+
+
+`;
+
exports[`renders ./components/tabs/demo/disabled.md correctly 1`] = `
+`;
+
exports[`Tabs tabPosition remove card 1`] = `
{
expect(wrapper).toMatchSnapshot();
});
});
+
+ describe('renderTabBar', () => {
+ it('custom-tab-bar', () => {
+ const wrapper = render(
+
custom-tab-bar
}>
+ foo
+
+ );
+ expect(wrapper).toMatchSnapshot();
+ });
+ });
});
diff --git a/components/tabs/demo/custom-tab-bar.md b/components/tabs/demo/custom-tab-bar.md
new file mode 100644
index 0000000000..ebeb286a43
--- /dev/null
+++ b/components/tabs/demo/custom-tab-bar.md
@@ -0,0 +1,39 @@
+---
+order: 12
+title:
+ zh-CN: 自定义页签头
+ en-US: Customized bar of tab
+---
+
+## zh-CN
+
+使用 react-sticky 组件实现吸顶效果。
+
+## en-US
+
+use react-sticky.
+
+````jsx
+import { Tabs } from 'antd';
+import { StickyContainer, Sticky } from 'react-sticky';
+
+const TabPane = Tabs.TabPane;
+
+const renderTabBar = (props, DefaultTabBar) => (
+
+ {({ style }) => (
+
+ )}
+
+);
+
+ReactDOM.render(
+
+
+ Content of Tab Pane 1
+ Content of Tab Pane 2
+ Content of Tab Pane 3
+
+ ,
+ mountNode);
+````
diff --git a/components/tabs/index.en-US.md b/components/tabs/index.en-US.md
index b5a5993ef2..e0dfcd57e6 100644
--- a/components/tabs/index.en-US.md
+++ b/components/tabs/index.en-US.md
@@ -23,6 +23,7 @@ Ant Design has 3 types of Tabs for different situations.
| -------- | ----------- | ---- | ------- |
| activeKey | Current TabPane's key | string | - |
| animated | Whether to change tabs with animation. Only works while `tabPosition="top"\|"bottom"` | boolean \| {inkBar:boolean, tabPane:boolean} | `true`, `false` when `type="card"` |
+| renderTabBar | replace the TabBar | (props: DefaultTabBarProps, DefaultTabBar: React.ReactNode) => React.ReactNode | - |
| defaultActiveKey | Initial active TabPane's key, if `activeKey` is not set. | string | - |
| hideAdd | Hide plus icon or not. Only works while `type="editable-card"` | boolean | `false` |
| size | preset tab bar size | `large` \| `default` \| `small` | `default` |
diff --git a/components/tabs/index.tsx b/components/tabs/index.tsx
index db56320778..5fc911febc 100755
--- a/components/tabs/index.tsx
+++ b/components/tabs/index.tsx
@@ -1,8 +1,8 @@
import * as React from 'react';
import * as ReactDOM from 'react-dom';
import RcTabs, { TabPane } from 'rc-tabs';
-import ScrollableInkTabBar from 'rc-tabs/lib/ScrollableInkTabBar';
import TabContent from 'rc-tabs/lib/TabContent';
+import TabBar from './TabBar';
import classNames from 'classnames';
import Icon from '../icon';
import warning from '../_util/warning';
@@ -30,6 +30,7 @@ export interface TabsProps {
className?: string;
animated?: boolean | { inkBar: boolean; tabPane: boolean; };
tabBarGutter?: number;
+ renderTabBar?: (props: TabsProps, DefaultTabBar: React.ReactNode) => React.ReactElement
;
}
// Tabs
@@ -51,13 +52,6 @@ export default class Tabs extends React.Component {
hideAdd: false,
};
- createNewTab = (targetKey: React.MouseEvent) => {
- const onEdit = this.props.onEdit;
- if (onEdit) {
- onEdit(targetKey, 'add');
- }
- }
-
removeTab = (targetKey: string, e: React.MouseEvent) => {
e.stopPropagation();
if (!targetKey) {
@@ -77,6 +71,13 @@ export default class Tabs extends React.Component {
}
}
+ createNewTab = (targetKey: React.MouseEvent) => {
+ const { onEdit } = this.props;
+ if (onEdit) {
+ onEdit(targetKey, 'add');
+ }
+ }
+
componentDidMount() {
const NO_FLEX = ' no-flex';
const tabNode = ReactDOM.findDOMNode(this) as Element;
@@ -93,21 +94,12 @@ export default class Tabs extends React.Component {
type = 'line',
tabPosition,
children,
+ animated = true,
tabBarExtraContent,
- tabBarStyle,
hideAdd,
- onTabClick,
- onPrevClick,
- onNextClick,
- animated = true,
- tabBarGutter,
} = this.props;
- let { inkBarAnimated, tabPaneAnimated } = typeof animated === 'object' ? {
- inkBarAnimated: animated.inkBar, tabPaneAnimated: animated.tabPane,
- } : {
- inkBarAnimated: animated, tabPaneAnimated: animated,
- };
+ let tabPaneAnimated = typeof animated === 'object' ? animated.tabPane : animated;
// card tabs should not have animation
if (type !== 'line') {
@@ -166,41 +158,12 @@ export default class Tabs extends React.Component {
) : null;
- const renderTabBar = () => {
- const isVertical = tabPosition === 'left' || tabPosition === 'right';
- const prevIconType = isVertical ? 'up' : 'left';
- const nextIconType = isVertical ? 'down' : 'right';
- const prevIcon = (
-
-
-
- );
- const nextIcon = (
-
-
-
- );
- return (
-
- );
- };
-
return (
}
renderTabContent={() => }
onChange={this.handleChange}
>
diff --git a/components/tabs/index.zh-CN.md b/components/tabs/index.zh-CN.md
index c4eff58f21..10522b44d4 100644
--- a/components/tabs/index.zh-CN.md
+++ b/components/tabs/index.zh-CN.md
@@ -26,6 +26,7 @@ Ant Design 依次提供了三级选项卡,分别用于不同的场景。
| --- | --- | --- | --- |
| activeKey | 当前激活 tab 面板的 key | string | 无 |
| animated | 是否使用动画切换 Tabs,在 `tabPosition=top|bottom` 时有效 | boolean \| {inkBar:boolean, tabPane:boolean} | true, 当 type="card" 时为 false |
+| renderTabBar | 替换TabBar,用于二次封装标签头 | (props: DefaultTabBarProps, DefaultTabBar: React.ReactNode) => React.ReactNode | 无 |
| defaultActiveKey | 初始化选中面板的 key,如果没有设置 activeKey | string | 第一个面板 |
| hideAdd | 是否隐藏加号图标,在 `type="editable-card"` 时有效 | boolean | false |
| size | 大小,提供 `large` `default` 和 `small` 三种大小 | string | 'default' |
diff --git a/package.json b/package.json
index c7e273f3aa..e4ef0c5fa7 100644
--- a/package.json
+++ b/package.json
@@ -164,6 +164,7 @@
"react-intl": "^2.0.1",
"react-resizable": "^1.7.5",
"react-router-dom": "^4.2.2",
+ "react-sticky": "^6.0.3",
"react-sublime-video": "^0.2.0",
"react-virtualized": "~9.20.0",
"remark-frontmatter": "^1.1.0",