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 ?
{cover}
: null; const body = ( @@ -190,18 +183,14 @@ export default { {loading ? loadingBlock : children} ); - const actions = filterEmpty(this.$slots.actions); + const actions = filterEmpty(getComponent(this, 'action')); const actionDom = actions && actions.length ? ( ) : null; return ( -
+
{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 {
{tabBarExtraContent}
) : 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}
; }, };