From 8ce8e9a8218656a778af7f3bf185ea24bb4c313a Mon Sep 17 00:00:00 2001
From: tangjinzhou <415800467@qq.com>
Date: Tue, 23 Jun 2020 22:34:08 +0800
Subject: [PATCH] feat: update tabs
---
components/card/Card.jsx | 43 +++++-------
components/tabs/TabBar.jsx | 19 +++--
components/tabs/index.js | 13 ++--
components/tabs/tabs.jsx | 85 +++++++++++------------
components/vc-tabs/src/TabBarRootNode.jsx | 3 +-
components/vc-tabs/src/TabBarTabsNode.jsx | 39 +++--------
components/vc-tabs/src/TabContent.jsx | 30 ++++----
components/vc-tabs/src/TabPane.jsx | 14 ++--
components/vc-tabs/src/Tabs.jsx | 73 ++++++++-----------
9 files changed, 133 insertions(+), 186 deletions(-)
diff --git a/components/card/Card.jsx b/components/card/Card.jsx
index a431743f3..c00c98be3 100644
--- a/components/card/Card.jsx
+++ b/components/card/Card.jsx
@@ -1,12 +1,12 @@
import { inject } from 'vue';
-import omit from 'omit.js';
import Tabs from '../tabs';
import Row from '../row';
import Col from '../col';
import PropTypes from '../_util/vue-types';
-import { getComponent, getSlotOptions, filterEmpty, getListeners } from '../_util/props-util';
+import { getComponent, filterEmpty, getSlot } from '../_util/props-util';
import BaseMixin from '../_util/BaseMixin';
import { ConfigConsumerProps } from '../config-provider';
+import isPlainObject from 'lodash/isPlainObject';
const { TabPane } = Tabs;
export default {
@@ -54,7 +54,7 @@ export default {
isContainGrid(obj = []) {
let containGrid;
obj.forEach(element => {
- if (element && getSlotOptions(element).__ANT_CARD_GRID) {
+ if (element && isPlainObject(element.type) && element.type.__ANT_CARD_GRID) {
containGrid = true;
}
});
@@ -75,18 +75,17 @@ export default {
activeTabKey,
defaultActiveTabKey,
} = this.$props;
-
+ const children = getSlot(this);
const getPrefixCls = this.configProvider.getPrefixCls;
const prefixCls = getPrefixCls('card', customizePrefixCls);
- const { $slots, $scopedSlots } = this;
const tabBarExtraContent = getComponent(this, 'tabBarExtraContent');
const classString = {
[`${prefixCls}`]: true,
[`${prefixCls}-loading`]: loading,
[`${prefixCls}-bordered`]: bordered,
[`${prefixCls}-hoverable`]: !!hoverable,
- [`${prefixCls}-contain-grid`]: this.isContainGrid($slots.default),
+ [`${prefixCls}-contain-grid`]: this.isContainGrid(children),
[`${prefixCls}-contain-tabs`]: tabList && tabList.length,
[`${prefixCls}-${size}`]: size !== 'default',
[`${prefixCls}-type-${type}`]: !!type,
@@ -142,16 +141,12 @@ export default {
const hasActiveTabKey = activeTabKey !== undefined;
const tabsProps = {
- props: {
- size: 'large',
- [hasActiveTabKey ? 'activeKey' : 'defaultActiveKey']: hasActiveTabKey
- ? activeTabKey
- : defaultActiveTabKey,
- tabBarExtraContent,
- },
- on: {
- change: this.onTabChange,
- },
+ size: 'large',
+ [hasActiveTabKey ? 'activeKey' : 'defaultActiveKey']: hasActiveTabKey
+ ? activeTabKey
+ : defaultActiveTabKey,
+ tabBarExtraContent,
+ onChange: this.onTabChange,
class: `${prefixCls}-head-tabs`,
};
@@ -160,10 +155,9 @@ export default {
tabList && tabList.length ? (
{tabList.map(item => {
- const { tab: temp, scopedSlots = {} } = item;
- const name = scopedSlots.tab;
- const tab =
- temp !== undefined ? temp : $scopedSlots[name] ? $scopedSlots[name](item) : null;
+ const { tab: temp, children = {} } = item;
+ const name = children.tab;
+ const tab = temp !== undefined ? temp : children[name] ? children[name](item) : null;
return ;
})}
@@ -182,7 +176,6 @@ export default {
);
}
- const children = $slots.default;
const cover = getComponent(this, 'cover');
const coverDom = cover ?
+
{head}
{coverDom}
{children ? body : null}
diff --git a/components/tabs/TabBar.jsx b/components/tabs/TabBar.jsx
index f03a2cc7a..a4575ddad 100644
--- a/components/tabs/TabBar.jsx
+++ b/components/tabs/TabBar.jsx
@@ -5,7 +5,7 @@ import RightOutlined from '@ant-design/icons-vue/RightOutlined';
import ScrollableInkTabBar from '../vc-tabs/src/ScrollableInkTabBar';
import { cloneElement } from '../_util/vnode';
import PropTypes from '../_util/vue-types';
-import { getListeners } from '../_util/props-util';
+
const TabBar = {
name: 'TabBar',
inheritAttrs: false,
@@ -55,25 +55,22 @@ const TabBar = {
)}
);
-
// Additional className for style usage
const cls = {
+ [this.$attrs.class]: this.$attrs.class,
[`${prefixCls}-${tabPosition}-bar`]: true,
[`${prefixCls}-${size}-bar`]: !!size,
[`${prefixCls}-card-bar`]: type && type.indexOf('card') >= 0,
};
const renderProps = {
- props: {
- ...this.$props,
- ...this.$attrs,
- inkBarAnimated,
- extraContent: tabBarExtraContent,
- prevIcon,
- nextIcon,
- },
+ ...this.$props,
+ ...this.$attrs,
+ inkBarAnimated,
+ extraContent: tabBarExtraContent,
+ prevIcon,
+ nextIcon,
style: tabBarStyle,
- on: getListeners(this),
class: cls,
};
diff --git a/components/tabs/index.js b/components/tabs/index.js
index 5b30c430e..1cc9df23f 100644
--- a/components/tabs/index.js
+++ b/components/tabs/index.js
@@ -1,20 +1,15 @@
-import ref from 'vue-ref';
-import Vue from 'vue';
import Tabs from './tabs';
import TabPane from '../vc-tabs/src/TabPane';
import TabContent from '../vc-tabs/src/TabContent';
-import Base from '../base';
Tabs.TabPane = { ...TabPane, name: 'ATabPane', __ANT_TAB_PANE: true };
Tabs.TabContent = { ...TabContent, name: 'ATabContent' };
-Vue.use(ref, { name: 'ant-ref' });
/* istanbul ignore next */
-Tabs.install = function(Vue) {
- Vue.use(Base);
- Vue.component(Tabs.name, Tabs);
- Vue.component(Tabs.TabPane.name, Tabs.TabPane);
- Vue.component(Tabs.TabContent.name, Tabs.TabContent);
+Tabs.install = function(app) {
+ app.component(Tabs.name, Tabs);
+ app.component(Tabs.TabPane.name, Tabs.TabPane);
+ app.component(Tabs.TabContent.name, Tabs.TabContent);
};
export default Tabs;
diff --git a/components/tabs/tabs.jsx b/components/tabs/tabs.jsx
index ab94db596..b16a3f6f6 100644
--- a/components/tabs/tabs.jsx
+++ b/components/tabs/tabs.jsx
@@ -1,3 +1,4 @@
+import { inject } from 'vue';
import CloseOutlined from '@ant-design/icons-vue/CloseOutlined';
import PlusOutlined from '@ant-design/icons-vue/PlusOutlined';
import VcTabs, { TabPane } from '../vc-tabs/src';
@@ -5,10 +6,12 @@ import TabContent from '../vc-tabs/src/TabContent';
import { isFlexSupported } from '../_util/styleChecker';
import PropTypes from '../_util/vue-types';
import {
- getComponentFromProp,
+ getComponent,
getOptionProps,
filterEmpty,
- getListeners,
+ findDOMNode,
+ getPropsData,
+ getSlot,
} from '../_util/props-util';
import { cloneElement } from '../_util/vnode';
import isValid from '../_util/isValid';
@@ -37,12 +40,14 @@ export default {
tabBarGutter: PropTypes.number,
renderTabBar: PropTypes.func,
},
- inject: {
- configProvider: { default: () => ConfigConsumerProps },
+ setup() {
+ return {
+ configProvider: inject('configProvider', ConfigConsumerProps),
+ };
},
mounted() {
const NO_FLEX = ' no-flex';
- const tabNode = this.$el;
+ const tabNode = findDOMNode(this);
if (tabNode && !isFlexSupported && tabNode.className.indexOf(NO_FLEX) === -1) {
tabNode.className += NO_FLEX;
}
@@ -55,6 +60,7 @@ export default {
}
},
handleChange(activeKey) {
+ this.$emit('update:activeKey', activeKey);
this.$emit('change', activeKey);
},
createNewTab(targetKey) {
@@ -82,11 +88,12 @@ export default {
hideAdd,
renderTabBar,
} = props;
+ const { class: className, style, ...restProps } = this.$attrs;
const getPrefixCls = this.configProvider().getPrefixCls;
const prefixCls = getPrefixCls('tabs', customizePrefixCls);
- const children = filterEmpty(this.$slots.default);
+ const children = filterEmpty(getSlot(this));
- let tabBarExtraContent = getComponentFromProp(this, 'tabBarExtraContent');
+ let tabBarExtraContent = getComponent(this, 'tabBarExtraContent');
let tabPaneAnimated = typeof animated === 'object' ? animated.tabPane : animated;
// card tabs should not have animation
@@ -94,6 +101,7 @@ export default {
tabPaneAnimated = 'animated' in props ? tabPaneAnimated : false;
}
const cls = {
+ [className]: className,
[`${prefixCls}-vertical`]: tabPosition === 'left' || tabPosition === 'right',
[`${prefixCls}-${size}`]: !!size,
[`${prefixCls}-card`]: type.indexOf('card') >= 0,
@@ -105,7 +113,7 @@ export default {
if (type === 'editable-card') {
childrenWithClose = [];
children.forEach((child, index) => {
- const props = getOptionProps(child);
+ const props = getPropsData(child);
let closable = props.closable;
closable = typeof closable === 'undefined' ? true : closable;
const closeIcon = closable ? (
@@ -116,14 +124,12 @@ export default {
) : null;
childrenWithClose.push(
cloneElement(child, {
- props: {
- tab: (
-
- {getComponentFromProp(child, 'tab')}
- {closeIcon}
-
- ),
- },
+ tab: (
+
+ {getComponent(child, 'tab')}
+ {closeIcon}
+
+ ),
key: child.key || index,
}),
);
@@ -143,40 +149,33 @@ export default {
) : null;
- const renderTabBarSlot = renderTabBar || this.$scopedSlots.renderTabBar;
- const listeners = getListeners(this);
+ const renderTabBarSlot = renderTabBar || this.$slots.renderTabBar;
const tabBarProps = {
- props: {
- ...this.$props,
- prefixCls,
- tabBarExtraContent,
- renderTabBar: renderTabBarSlot,
- },
- on: listeners,
+ ...this.$props,
+ prefixCls,
+ tabBarExtraContent,
+ renderTabBar: renderTabBarSlot,
+ ...restProps,
};
const contentCls = {
[`${prefixCls}-${tabPosition}-content`]: true,
[`${prefixCls}-card-content`]: type.indexOf('card') >= 0,
};
const tabsProps = {
- props: {
- ...getOptionProps(this),
- prefixCls,
- tabBarPosition: tabPosition,
- // https://github.com/vueComponent/ant-design-vue/issues/2030
- // 如仅传递 tabBarProps 会导致,第二次执行 renderTabBar 时,丢失 on 属性,
- // 添加key之后,会在babel jsx 插件中做一次merge,最终TabBar接收的是一个新的对象,而不是 tabBarProps
- renderTabBar: () =>
,
- renderTabContent: () => (
-
- ),
- children: childrenWithClose.length > 0 ? childrenWithClose : children,
- __propsSymbol__: Symbol(),
- },
- on: {
- ...listeners,
- change: this.handleChange,
- },
+ ...getOptionProps(this),
+ prefixCls,
+ tabBarPosition: tabPosition,
+ // https://github.com/vueComponent/ant-design-vue/issues/2030
+ // 如仅传递 tabBarProps 会导致,第二次执行 renderTabBar 时,丢失 on 属性,
+ // 添加key之后,会在babel jsx 插件中做一次merge,最终TabBar接收的是一个新的对象,而不是 tabBarProps
+ renderTabBar: () =>
,
+ renderTabContent: () => (
+
+ ),
+ children: childrenWithClose.length > 0 ? childrenWithClose : children,
+ __propsSymbol__: Symbol(),
+ ...restProps,
+ onChange: this.handleChange,
class: cls,
};
return
;
diff --git a/components/vc-tabs/src/TabBarRootNode.jsx b/components/vc-tabs/src/TabBarRootNode.jsx
index 3a6735314..f4dae37e4 100644
--- a/components/vc-tabs/src/TabBarRootNode.jsx
+++ b/components/vc-tabs/src/TabBarRootNode.jsx
@@ -2,6 +2,7 @@ import { cloneElement } from '../../_util/vnode';
import PropTypes from '../../_util/vue-types';
import BaseMixin from '../../_util/BaseMixin';
import createRefHooks from '../../_util/createRefHooks';
+import { getSlot } from '../../_util/props-util';
function noop() {}
export default {
name: 'TabBarRootNode',
@@ -26,7 +27,7 @@ export default {
};
const topOrBottom = tabBarPosition === 'top' || tabBarPosition === 'bottom';
const tabBarExtraContentStyle = topOrBottom ? { float: 'right' } : {};
- const children = this.$slots.default;
+ const children = getSlot(this);
let newChildren = children;
if (extraContent) {
newChildren = [
diff --git a/components/vc-tabs/src/TabBarTabsNode.jsx b/components/vc-tabs/src/TabBarTabsNode.jsx
index 8bab1b939..7abaa0814 100644
--- a/components/vc-tabs/src/TabBarTabsNode.jsx
+++ b/components/vc-tabs/src/TabBarTabsNode.jsx
@@ -1,8 +1,9 @@
import warning from 'warning';
import PropTypes from '../../_util/vue-types';
import BaseMixin from '../../_util/BaseMixin';
-import { getOptionProps, getComponentFromProp } from '../../_util/props-util';
+import { getComponent, getPropsData } from '../../_util/props-util';
import { isVertical } from './utils';
+import createRefHooks from '../../_util/createRefHooks';
function noop() {}
export default {
name: 'TabBarTabsNode',
@@ -30,32 +31,25 @@ export default {
direction,
} = this.$props;
const rst = [];
- const renderTabBarNode = this.renderTabBarNode || this.$scopedSlots.renderTabBarNode;
+ const renderTabBarNode = this.renderTabBarNode || this.$slots.renderTabBarNode;
children.forEach((child, index) => {
if (!child) {
return;
}
- const props = getOptionProps(child);
+ const props = getPropsData(child);
const key = child.key;
let cls = activeKey === key ? `${prefixCls}-tab-active` : '';
cls += ` ${prefixCls}-tab`;
- const events = { on: {} };
- const disabled = props.disabled || props.disabled === '';
+ const events = {};
+ const disabled = props.disabled;
if (disabled) {
cls += ` ${prefixCls}-tab-disabled`;
} else {
- events.on.click = () => {
+ events.onClick = () => {
this.__emit('tabClick', key);
};
}
- const directives = [];
- if (activeKey === key) {
- directives.push({
- name: 'ant-ref',
- value: saveRef('activeTab'),
- });
- }
- const tab = getComponentFromProp(child, 'tab');
+ const tab = getComponent(child, 'tab');
let gutter = tabBarGutter && index === children.length - 1 ? 0 : tabBarGutter;
gutter = typeof gutter === 'number' ? `${gutter}px` : gutter;
const marginProperty = direction === 'rtl' ? 'marginLeft' : 'marginRight';
@@ -72,7 +66,7 @@ export default {
class={cls}
key={key}
style={style}
- {...{ directives }}
+ {...createRefHooks(activeKey === key ? saveRef('activeTab') : noop)}
>
{tab}
@@ -84,19 +78,6 @@ export default {
rst.push(node);
});
- return (
-
- {rst}
-
- );
+ return
{rst}
;
},
};
diff --git a/components/vc-tabs/src/TabContent.jsx b/components/vc-tabs/src/TabContent.jsx
index 207a1e22d..942216731 100644
--- a/components/vc-tabs/src/TabContent.jsx
+++ b/components/vc-tabs/src/TabContent.jsx
@@ -6,17 +6,15 @@ import {
getTransformPropValue,
getMarginStyle,
} from './utils';
+import { getSlot } from '../../_util/props-util';
export default {
name: 'TabContent',
props: {
- animated: { type: Boolean, default: true },
- animatedWithMargin: { type: Boolean, default: true },
- prefixCls: {
- default: 'ant-tabs',
- type: String,
- },
+ animated: PropTypes.bool.def(true),
+ animatedWithMargin: PropTypes.bool.def(true),
+ prefixCls: PropTypes.string.def('ant-tabs'),
activeKey: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
- tabBarPosition: String,
+ tabBarPosition: PropTypes.string,
direction: PropTypes.string,
destroyInactiveTabPane: PropTypes.bool,
},
@@ -30,10 +28,9 @@ export default {
},
},
methods: {
- getTabPanes() {
+ getTabPanes(children) {
const props = this.$props;
const activeKey = props.activeKey;
- const children = this.$slots.default || [];
const newChildren = [];
children.forEach(child => {
@@ -44,11 +41,9 @@ export default {
const active = activeKey === key;
newChildren.push(
cloneElement(child, {
- props: {
- active,
- destroyInactiveTabPane: props.destroyInactiveTabPane,
- rootPrefixCls: props.prefixCls,
- },
+ active,
+ destroyInactiveTabPane: props.destroyInactiveTabPane,
+ rootPrefixCls: props.prefixCls,
}),
);
});
@@ -59,8 +54,9 @@ export default {
render() {
const { activeKey, tabBarPosition, animated, animatedWithMargin, direction, classes } = this;
let style = {};
- if (animated && this.$slots.default) {
- const activeIndex = getActiveIndex(this.$slots.default, activeKey);
+ const children = getSlot(this);
+ if (animated && children) {
+ const activeIndex = getActiveIndex(children, activeKey);
if (activeIndex !== -1) {
const animatedStyle = animatedWithMargin
? getMarginStyle(activeIndex, tabBarPosition)
@@ -74,7 +70,7 @@ export default {
}
return (
- {this.getTabPanes()}
+ {this.getTabPanes(children || [])}
);
},
diff --git a/components/vc-tabs/src/TabPane.jsx b/components/vc-tabs/src/TabPane.jsx
index 3a3c665f3..e1742a3b4 100644
--- a/components/vc-tabs/src/TabPane.jsx
+++ b/components/vc-tabs/src/TabPane.jsx
@@ -1,5 +1,6 @@
+import { inject } from 'vue';
import PropTypes from '../../_util/vue-types';
-import { getComponentFromProp } from '../../_util/props-util';
+import { getComponent, getSlot } from '../../_util/props-util';
import Sentinel from './Sentinel';
export default {
@@ -14,13 +15,16 @@ export default {
closable: PropTypes.bool,
disabled: PropTypes.bool,
},
- inject: {
- sentinelContext: { default: () => ({}) },
+ setup() {
+ return {
+ _isActived: undefined,
+ sentinelContext: inject('sentinelContext', {}),
+ };
},
render() {
const { destroyInactiveTabPane, active, forceRender, rootPrefixCls } = this.$props;
- const children = this.$slots.default;
- const placeholder = getComponentFromProp(this, 'placeholder');
+ const children = getSlot(this);
+ const placeholder = getComponent(this, 'placeholder');
this._isActived = this._isActived || active;
const prefixCls = `${rootPrefixCls}-tabpane`;
const cls = {
diff --git a/components/vc-tabs/src/Tabs.jsx b/components/vc-tabs/src/Tabs.jsx
index 1364f21f4..455654e79 100644
--- a/components/vc-tabs/src/Tabs.jsx
+++ b/components/vc-tabs/src/Tabs.jsx
@@ -1,9 +1,9 @@
-import omit from 'omit.js';
+import { provide } from 'vue';
import BaseMixin from '../../_util/BaseMixin';
import PropTypes from '../../_util/vue-types';
import raf from 'raf';
import KeyCode from './KeyCode';
-import { getOptionProps, getListeners } from '../../_util/props-util';
+import { getOptionProps } from '../../_util/props-util';
import { cloneElement } from '../../_util/vnode';
import Sentinel from './Sentinel';
import isValid from '../../_util/isValid';
@@ -28,10 +28,7 @@ function activeKeyIsValid(props, key) {
export default {
name: 'Tabs',
mixins: [BaseMixin],
- model: {
- prop: 'activeKey',
- event: 'change',
- },
+ inheritAttrs: false,
props: {
destroyInactiveTabPane: PropTypes.bool,
renderTabBar: PropTypes.func.isRequired,
@@ -47,6 +44,7 @@ export default {
tabBarGutter: PropTypes.number,
},
data() {
+ provide('sentinelContext', this);
const props = getOptionProps(this);
let activeKey;
if ('activeKey' in props) {
@@ -60,11 +58,6 @@ export default {
_activeKey: activeKey,
};
},
- provide() {
- return {
- sentinelContext: this,
- };
- },
watch: {
__propsSymbol__() {
const nextProps = getOptionProps(this);
@@ -86,12 +79,8 @@ export default {
},
methods: {
onTabClick(activeKey, e) {
- if (
- this.tabBar.componentOptions &&
- this.tabBar.componentOptions.listeners &&
- this.tabBar.componentOptions.listeners.tabClick
- ) {
- this.tabBar.componentOptions.listeners.tabClick(activeKey, e);
+ if (this.tabBar.props && this.tabBar.props.onTabClick) {
+ this.tabBar.props.onTabClick(activeKey, e);
}
this.setActiveKey(activeKey);
},
@@ -146,6 +135,7 @@ export default {
_activeKey: activeKey,
});
}
+ this.$emit('update:activeKey', activeKey);
this.__emit('change', activeKey);
}
},
@@ -197,7 +187,9 @@ export default {
direction,
tabBarGutter,
} = props;
+ const { class: className, onChange, ...restProps } = this.$attrs;
const cls = {
+ [className]: className,
[prefixCls]: 1,
[`${prefixCls}-${tabBarPosition}`]: 1,
[`${prefixCls}-rtl`]: direction === 'rtl',
@@ -205,32 +197,24 @@ export default {
this.tabBar = renderTabBar();
const tabBar = cloneElement(this.tabBar, {
- props: {
- prefixCls,
- navWrapper,
- tabBarPosition,
- panels: props.children,
- activeKey: this.$data._activeKey,
- direction,
- tabBarGutter,
- },
- on: {
- keydown: this.onNavKeyDown,
- tabClick: this.onTabClick,
- },
+ prefixCls,
+ navWrapper,
+ tabBarPosition,
+ panels: props.children,
+ activeKey: this.$data._activeKey,
+ direction,
+ tabBarGutter,
+ onKeydown: this.onNavKeyDown,
+ onTabClick: this.onTabClick,
key: 'tabBar',
});
const tabContent = cloneElement(renderTabContent(), {
- props: {
- prefixCls,
- tabBarPosition,
- activeKey: this.$data._activeKey,
- destroyInactiveTabPane,
- direction,
- },
- on: {
- change: this.setActiveKey,
- },
+ prefixCls,
+ tabBarPosition,
+ activeKey: this.$data._activeKey,
+ destroyInactiveTabPane,
+ direction,
+ onChange: this.setActiveKey,
children: props.children,
key: 'tabContent',
});
@@ -257,10 +241,11 @@ export default {
} else {
contents.push(tabBar, sentinelStart, tabContent, sentinelEnd);
}
- const listeners = {
- ...omit(getListeners(this), ['change']),
- scroll: this.onScroll,
+ const p = {
+ ...restProps,
+ onScroll: this.onScroll,
+ class: cls,
};
- return
{contents}
;
+ return
{contents}
;
},
};