feat: update tabs

pull/2494/head
tangjinzhou 2020-06-23 22:34:08 +08:00
parent 132a1e58bf
commit 8ce8e9a821
9 changed files with 133 additions and 186 deletions

View File

@ -1,12 +1,12 @@
import { inject } from 'vue'; import { inject } from 'vue';
import omit from 'omit.js';
import Tabs from '../tabs'; import Tabs from '../tabs';
import Row from '../row'; import Row from '../row';
import Col from '../col'; import Col from '../col';
import PropTypes from '../_util/vue-types'; 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 BaseMixin from '../_util/BaseMixin';
import { ConfigConsumerProps } from '../config-provider'; import { ConfigConsumerProps } from '../config-provider';
import isPlainObject from 'lodash/isPlainObject';
const { TabPane } = Tabs; const { TabPane } = Tabs;
export default { export default {
@ -54,7 +54,7 @@ export default {
isContainGrid(obj = []) { isContainGrid(obj = []) {
let containGrid; let containGrid;
obj.forEach(element => { obj.forEach(element => {
if (element && getSlotOptions(element).__ANT_CARD_GRID) { if (element && isPlainObject(element.type) && element.type.__ANT_CARD_GRID) {
containGrid = true; containGrid = true;
} }
}); });
@ -75,18 +75,17 @@ export default {
activeTabKey, activeTabKey,
defaultActiveTabKey, defaultActiveTabKey,
} = this.$props; } = this.$props;
const children = getSlot(this);
const getPrefixCls = this.configProvider.getPrefixCls; const getPrefixCls = this.configProvider.getPrefixCls;
const prefixCls = getPrefixCls('card', customizePrefixCls); const prefixCls = getPrefixCls('card', customizePrefixCls);
const { $slots, $scopedSlots } = this;
const tabBarExtraContent = getComponent(this, 'tabBarExtraContent'); const tabBarExtraContent = getComponent(this, 'tabBarExtraContent');
const classString = { const classString = {
[`${prefixCls}`]: true, [`${prefixCls}`]: true,
[`${prefixCls}-loading`]: loading, [`${prefixCls}-loading`]: loading,
[`${prefixCls}-bordered`]: bordered, [`${prefixCls}-bordered`]: bordered,
[`${prefixCls}-hoverable`]: !!hoverable, [`${prefixCls}-hoverable`]: !!hoverable,
[`${prefixCls}-contain-grid`]: this.isContainGrid($slots.default), [`${prefixCls}-contain-grid`]: this.isContainGrid(children),
[`${prefixCls}-contain-tabs`]: tabList && tabList.length, [`${prefixCls}-contain-tabs`]: tabList && tabList.length,
[`${prefixCls}-${size}`]: size !== 'default', [`${prefixCls}-${size}`]: size !== 'default',
[`${prefixCls}-type-${type}`]: !!type, [`${prefixCls}-type-${type}`]: !!type,
@ -142,16 +141,12 @@ export default {
const hasActiveTabKey = activeTabKey !== undefined; const hasActiveTabKey = activeTabKey !== undefined;
const tabsProps = { const tabsProps = {
props: { size: 'large',
size: 'large', [hasActiveTabKey ? 'activeKey' : 'defaultActiveKey']: hasActiveTabKey
[hasActiveTabKey ? 'activeKey' : 'defaultActiveKey']: hasActiveTabKey ? activeTabKey
? activeTabKey : defaultActiveTabKey,
: defaultActiveTabKey, tabBarExtraContent,
tabBarExtraContent, onChange: this.onTabChange,
},
on: {
change: this.onTabChange,
},
class: `${prefixCls}-head-tabs`, class: `${prefixCls}-head-tabs`,
}; };
@ -160,10 +155,9 @@ export default {
tabList && tabList.length ? ( tabList && tabList.length ? (
<Tabs {...tabsProps}> <Tabs {...tabsProps}>
{tabList.map(item => { {tabList.map(item => {
const { tab: temp, scopedSlots = {} } = item; const { tab: temp, children = {} } = item;
const name = scopedSlots.tab; const name = children.tab;
const tab = const tab = temp !== undefined ? temp : children[name] ? children[name](item) : null;
temp !== undefined ? temp : $scopedSlots[name] ? $scopedSlots[name](item) : null;
return <TabPane tab={tab} key={item.key} disabled={item.disabled} />; return <TabPane tab={tab} key={item.key} disabled={item.disabled} />;
})} })}
</Tabs> </Tabs>
@ -182,7 +176,6 @@ export default {
); );
} }
const children = $slots.default;
const cover = getComponent(this, 'cover'); const cover = getComponent(this, 'cover');
const coverDom = cover ? <div class={`${prefixCls}-cover`}>{cover}</div> : null; const coverDom = cover ? <div class={`${prefixCls}-cover`}>{cover}</div> : null;
const body = ( const body = (
@ -190,18 +183,14 @@ export default {
{loading ? loadingBlock : children} {loading ? loadingBlock : children}
</div> </div>
); );
const actions = filterEmpty(this.$slots.actions); const actions = filterEmpty(getComponent(this, 'action'));
const actionDom = const actionDom =
actions && actions.length ? ( actions && actions.length ? (
<ul class={`${prefixCls}-actions`}>{this.getAction(actions)}</ul> <ul class={`${prefixCls}-actions`}>{this.getAction(actions)}</ul>
) : null; ) : null;
return ( return (
<div <div class={classString} ref="cardContainerRef">
class={classString}
ref="cardContainerRef"
{...{ on: omit(getListeners(this), ['tabChange', 'tab-change']) }}
>
{head} {head}
{coverDom} {coverDom}
{children ? body : null} {children ? body : null}

View File

@ -5,7 +5,7 @@ import RightOutlined from '@ant-design/icons-vue/RightOutlined';
import ScrollableInkTabBar from '../vc-tabs/src/ScrollableInkTabBar'; import ScrollableInkTabBar from '../vc-tabs/src/ScrollableInkTabBar';
import { cloneElement } from '../_util/vnode'; import { cloneElement } from '../_util/vnode';
import PropTypes from '../_util/vue-types'; import PropTypes from '../_util/vue-types';
import { getListeners } from '../_util/props-util';
const TabBar = { const TabBar = {
name: 'TabBar', name: 'TabBar',
inheritAttrs: false, inheritAttrs: false,
@ -55,25 +55,22 @@ const TabBar = {
)} )}
</span> </span>
); );
// Additional className for style usage // Additional className for style usage
const cls = { const cls = {
[this.$attrs.class]: this.$attrs.class,
[`${prefixCls}-${tabPosition}-bar`]: true, [`${prefixCls}-${tabPosition}-bar`]: true,
[`${prefixCls}-${size}-bar`]: !!size, [`${prefixCls}-${size}-bar`]: !!size,
[`${prefixCls}-card-bar`]: type && type.indexOf('card') >= 0, [`${prefixCls}-card-bar`]: type && type.indexOf('card') >= 0,
}; };
const renderProps = { const renderProps = {
props: { ...this.$props,
...this.$props, ...this.$attrs,
...this.$attrs, inkBarAnimated,
inkBarAnimated, extraContent: tabBarExtraContent,
extraContent: tabBarExtraContent, prevIcon,
prevIcon, nextIcon,
nextIcon,
},
style: tabBarStyle, style: tabBarStyle,
on: getListeners(this),
class: cls, class: cls,
}; };

View File

@ -1,20 +1,15 @@
import ref from 'vue-ref';
import Vue from 'vue';
import Tabs from './tabs'; import Tabs from './tabs';
import TabPane from '../vc-tabs/src/TabPane'; import TabPane from '../vc-tabs/src/TabPane';
import TabContent from '../vc-tabs/src/TabContent'; import TabContent from '../vc-tabs/src/TabContent';
import Base from '../base';
Tabs.TabPane = { ...TabPane, name: 'ATabPane', __ANT_TAB_PANE: true }; Tabs.TabPane = { ...TabPane, name: 'ATabPane', __ANT_TAB_PANE: true };
Tabs.TabContent = { ...TabContent, name: 'ATabContent' }; Tabs.TabContent = { ...TabContent, name: 'ATabContent' };
Vue.use(ref, { name: 'ant-ref' });
/* istanbul ignore next */ /* istanbul ignore next */
Tabs.install = function(Vue) { Tabs.install = function(app) {
Vue.use(Base); app.component(Tabs.name, Tabs);
Vue.component(Tabs.name, Tabs); app.component(Tabs.TabPane.name, Tabs.TabPane);
Vue.component(Tabs.TabPane.name, Tabs.TabPane); app.component(Tabs.TabContent.name, Tabs.TabContent);
Vue.component(Tabs.TabContent.name, Tabs.TabContent);
}; };
export default Tabs; export default Tabs;

View File

@ -1,3 +1,4 @@
import { inject } from 'vue';
import CloseOutlined from '@ant-design/icons-vue/CloseOutlined'; import CloseOutlined from '@ant-design/icons-vue/CloseOutlined';
import PlusOutlined from '@ant-design/icons-vue/PlusOutlined'; import PlusOutlined from '@ant-design/icons-vue/PlusOutlined';
import VcTabs, { TabPane } from '../vc-tabs/src'; import VcTabs, { TabPane } from '../vc-tabs/src';
@ -5,10 +6,12 @@ import TabContent from '../vc-tabs/src/TabContent';
import { isFlexSupported } from '../_util/styleChecker'; import { isFlexSupported } from '../_util/styleChecker';
import PropTypes from '../_util/vue-types'; import PropTypes from '../_util/vue-types';
import { import {
getComponentFromProp, getComponent,
getOptionProps, getOptionProps,
filterEmpty, filterEmpty,
getListeners, findDOMNode,
getPropsData,
getSlot,
} from '../_util/props-util'; } from '../_util/props-util';
import { cloneElement } from '../_util/vnode'; import { cloneElement } from '../_util/vnode';
import isValid from '../_util/isValid'; import isValid from '../_util/isValid';
@ -37,12 +40,14 @@ export default {
tabBarGutter: PropTypes.number, tabBarGutter: PropTypes.number,
renderTabBar: PropTypes.func, renderTabBar: PropTypes.func,
}, },
inject: { setup() {
configProvider: { default: () => ConfigConsumerProps }, return {
configProvider: inject('configProvider', ConfigConsumerProps),
};
}, },
mounted() { mounted() {
const NO_FLEX = ' no-flex'; const NO_FLEX = ' no-flex';
const tabNode = this.$el; const tabNode = findDOMNode(this);
if (tabNode && !isFlexSupported && tabNode.className.indexOf(NO_FLEX) === -1) { if (tabNode && !isFlexSupported && tabNode.className.indexOf(NO_FLEX) === -1) {
tabNode.className += NO_FLEX; tabNode.className += NO_FLEX;
} }
@ -55,6 +60,7 @@ export default {
} }
}, },
handleChange(activeKey) { handleChange(activeKey) {
this.$emit('update:activeKey', activeKey);
this.$emit('change', activeKey); this.$emit('change', activeKey);
}, },
createNewTab(targetKey) { createNewTab(targetKey) {
@ -82,11 +88,12 @@ export default {
hideAdd, hideAdd,
renderTabBar, renderTabBar,
} = props; } = props;
const { class: className, style, ...restProps } = this.$attrs;
const getPrefixCls = this.configProvider().getPrefixCls; const getPrefixCls = this.configProvider().getPrefixCls;
const prefixCls = getPrefixCls('tabs', customizePrefixCls); 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; let tabPaneAnimated = typeof animated === 'object' ? animated.tabPane : animated;
// card tabs should not have animation // card tabs should not have animation
@ -94,6 +101,7 @@ export default {
tabPaneAnimated = 'animated' in props ? tabPaneAnimated : false; tabPaneAnimated = 'animated' in props ? tabPaneAnimated : false;
} }
const cls = { const cls = {
[className]: className,
[`${prefixCls}-vertical`]: tabPosition === 'left' || tabPosition === 'right', [`${prefixCls}-vertical`]: tabPosition === 'left' || tabPosition === 'right',
[`${prefixCls}-${size}`]: !!size, [`${prefixCls}-${size}`]: !!size,
[`${prefixCls}-card`]: type.indexOf('card') >= 0, [`${prefixCls}-card`]: type.indexOf('card') >= 0,
@ -105,7 +113,7 @@ export default {
if (type === 'editable-card') { if (type === 'editable-card') {
childrenWithClose = []; childrenWithClose = [];
children.forEach((child, index) => { children.forEach((child, index) => {
const props = getOptionProps(child); const props = getPropsData(child);
let closable = props.closable; let closable = props.closable;
closable = typeof closable === 'undefined' ? true : closable; closable = typeof closable === 'undefined' ? true : closable;
const closeIcon = closable ? ( const closeIcon = closable ? (
@ -116,14 +124,12 @@ export default {
) : null; ) : null;
childrenWithClose.push( childrenWithClose.push(
cloneElement(child, { cloneElement(child, {
props: { tab: (
tab: ( <div class={closable ? undefined : `${prefixCls}-tab-unclosable`}>
<div class={closable ? undefined : `${prefixCls}-tab-unclosable`}> {getComponent(child, 'tab')}
{getComponentFromProp(child, 'tab')} {closeIcon}
{closeIcon} </div>
</div> ),
),
},
key: child.key || index, key: child.key || index,
}), }),
); );
@ -143,40 +149,33 @@ export default {
<div class={`${prefixCls}-extra-content`}>{tabBarExtraContent}</div> <div class={`${prefixCls}-extra-content`}>{tabBarExtraContent}</div>
) : null; ) : null;
const renderTabBarSlot = renderTabBar || this.$scopedSlots.renderTabBar; const renderTabBarSlot = renderTabBar || this.$slots.renderTabBar;
const listeners = getListeners(this);
const tabBarProps = { const tabBarProps = {
props: { ...this.$props,
...this.$props, prefixCls,
prefixCls, tabBarExtraContent,
tabBarExtraContent, renderTabBar: renderTabBarSlot,
renderTabBar: renderTabBarSlot, ...restProps,
},
on: listeners,
}; };
const contentCls = { const contentCls = {
[`${prefixCls}-${tabPosition}-content`]: true, [`${prefixCls}-${tabPosition}-content`]: true,
[`${prefixCls}-card-content`]: type.indexOf('card') >= 0, [`${prefixCls}-card-content`]: type.indexOf('card') >= 0,
}; };
const tabsProps = { const tabsProps = {
props: { ...getOptionProps(this),
...getOptionProps(this), prefixCls,
prefixCls, tabBarPosition: tabPosition,
tabBarPosition: tabPosition, // https://github.com/vueComponent/ant-design-vue/issues/2030
// https://github.com/vueComponent/ant-design-vue/issues/2030 // tabBarProps renderTabBar on
// tabBarProps renderTabBar on // keybabel jsx mergeTabBar tabBarProps
// keybabel jsx mergeTabBar tabBarProps renderTabBar: () => <TabBar key="tabBar" {...tabBarProps} />,
renderTabBar: () => <TabBar key="tabBar" {...tabBarProps} />, renderTabContent: () => (
renderTabContent: () => ( <TabContent class={contentCls} animated={tabPaneAnimated} animatedWithMargin />
<TabContent class={contentCls} animated={tabPaneAnimated} animatedWithMargin /> ),
), children: childrenWithClose.length > 0 ? childrenWithClose : children,
children: childrenWithClose.length > 0 ? childrenWithClose : children, __propsSymbol__: Symbol(),
__propsSymbol__: Symbol(), ...restProps,
}, onChange: this.handleChange,
on: {
...listeners,
change: this.handleChange,
},
class: cls, class: cls,
}; };
return <VcTabs {...tabsProps} />; return <VcTabs {...tabsProps} />;

View File

@ -2,6 +2,7 @@ import { cloneElement } from '../../_util/vnode';
import PropTypes from '../../_util/vue-types'; import PropTypes from '../../_util/vue-types';
import BaseMixin from '../../_util/BaseMixin'; import BaseMixin from '../../_util/BaseMixin';
import createRefHooks from '../../_util/createRefHooks'; import createRefHooks from '../../_util/createRefHooks';
import { getSlot } from '../../_util/props-util';
function noop() {} function noop() {}
export default { export default {
name: 'TabBarRootNode', name: 'TabBarRootNode',
@ -26,7 +27,7 @@ export default {
}; };
const topOrBottom = tabBarPosition === 'top' || tabBarPosition === 'bottom'; const topOrBottom = tabBarPosition === 'top' || tabBarPosition === 'bottom';
const tabBarExtraContentStyle = topOrBottom ? { float: 'right' } : {}; const tabBarExtraContentStyle = topOrBottom ? { float: 'right' } : {};
const children = this.$slots.default; const children = getSlot(this);
let newChildren = children; let newChildren = children;
if (extraContent) { if (extraContent) {
newChildren = [ newChildren = [

View File

@ -1,8 +1,9 @@
import warning from 'warning'; import warning from 'warning';
import PropTypes from '../../_util/vue-types'; import PropTypes from '../../_util/vue-types';
import BaseMixin from '../../_util/BaseMixin'; import BaseMixin from '../../_util/BaseMixin';
import { getOptionProps, getComponentFromProp } from '../../_util/props-util'; import { getComponent, getPropsData } from '../../_util/props-util';
import { isVertical } from './utils'; import { isVertical } from './utils';
import createRefHooks from '../../_util/createRefHooks';
function noop() {} function noop() {}
export default { export default {
name: 'TabBarTabsNode', name: 'TabBarTabsNode',
@ -30,32 +31,25 @@ export default {
direction, direction,
} = this.$props; } = this.$props;
const rst = []; const rst = [];
const renderTabBarNode = this.renderTabBarNode || this.$scopedSlots.renderTabBarNode; const renderTabBarNode = this.renderTabBarNode || this.$slots.renderTabBarNode;
children.forEach((child, index) => { children.forEach((child, index) => {
if (!child) { if (!child) {
return; return;
} }
const props = getOptionProps(child); const props = getPropsData(child);
const key = child.key; const key = child.key;
let cls = activeKey === key ? `${prefixCls}-tab-active` : ''; let cls = activeKey === key ? `${prefixCls}-tab-active` : '';
cls += ` ${prefixCls}-tab`; cls += ` ${prefixCls}-tab`;
const events = { on: {} }; const events = {};
const disabled = props.disabled || props.disabled === ''; const disabled = props.disabled;
if (disabled) { if (disabled) {
cls += ` ${prefixCls}-tab-disabled`; cls += ` ${prefixCls}-tab-disabled`;
} else { } else {
events.on.click = () => { events.onClick = () => {
this.__emit('tabClick', key); this.__emit('tabClick', key);
}; };
} }
const directives = []; const tab = getComponent(child, 'tab');
if (activeKey === key) {
directives.push({
name: 'ant-ref',
value: saveRef('activeTab'),
});
}
const tab = getComponentFromProp(child, 'tab');
let gutter = tabBarGutter && index === children.length - 1 ? 0 : tabBarGutter; let gutter = tabBarGutter && index === children.length - 1 ? 0 : tabBarGutter;
gutter = typeof gutter === 'number' ? `${gutter}px` : gutter; gutter = typeof gutter === 'number' ? `${gutter}px` : gutter;
const marginProperty = direction === 'rtl' ? 'marginLeft' : 'marginRight'; const marginProperty = direction === 'rtl' ? 'marginLeft' : 'marginRight';
@ -72,7 +66,7 @@ export default {
class={cls} class={cls}
key={key} key={key}
style={style} style={style}
{...{ directives }} {...createRefHooks(activeKey === key ? saveRef('activeTab') : noop)}
> >
{tab} {tab}
</div> </div>
@ -84,19 +78,6 @@ export default {
rst.push(node); rst.push(node);
}); });
return ( return <div {...createRefHooks(this.saveRef('navTabsContainer'))}>{rst}</div>;
<div
{...{
directives: [
{
name: 'ant-ref',
value: this.saveRef('navTabsContainer'),
},
],
}}
>
{rst}
</div>
);
}, },
}; };

View File

@ -6,17 +6,15 @@ import {
getTransformPropValue, getTransformPropValue,
getMarginStyle, getMarginStyle,
} from './utils'; } from './utils';
import { getSlot } from '../../_util/props-util';
export default { export default {
name: 'TabContent', name: 'TabContent',
props: { props: {
animated: { type: Boolean, default: true }, animated: PropTypes.bool.def(true),
animatedWithMargin: { type: Boolean, default: true }, animatedWithMargin: PropTypes.bool.def(true),
prefixCls: { prefixCls: PropTypes.string.def('ant-tabs'),
default: 'ant-tabs',
type: String,
},
activeKey: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), activeKey: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
tabBarPosition: String, tabBarPosition: PropTypes.string,
direction: PropTypes.string, direction: PropTypes.string,
destroyInactiveTabPane: PropTypes.bool, destroyInactiveTabPane: PropTypes.bool,
}, },
@ -30,10 +28,9 @@ export default {
}, },
}, },
methods: { methods: {
getTabPanes() { getTabPanes(children) {
const props = this.$props; const props = this.$props;
const activeKey = props.activeKey; const activeKey = props.activeKey;
const children = this.$slots.default || [];
const newChildren = []; const newChildren = [];
children.forEach(child => { children.forEach(child => {
@ -44,11 +41,9 @@ export default {
const active = activeKey === key; const active = activeKey === key;
newChildren.push( newChildren.push(
cloneElement(child, { cloneElement(child, {
props: { active,
active, destroyInactiveTabPane: props.destroyInactiveTabPane,
destroyInactiveTabPane: props.destroyInactiveTabPane, rootPrefixCls: props.prefixCls,
rootPrefixCls: props.prefixCls,
},
}), }),
); );
}); });
@ -59,8 +54,9 @@ export default {
render() { render() {
const { activeKey, tabBarPosition, animated, animatedWithMargin, direction, classes } = this; const { activeKey, tabBarPosition, animated, animatedWithMargin, direction, classes } = this;
let style = {}; let style = {};
if (animated && this.$slots.default) { const children = getSlot(this);
const activeIndex = getActiveIndex(this.$slots.default, activeKey); if (animated && children) {
const activeIndex = getActiveIndex(children, activeKey);
if (activeIndex !== -1) { if (activeIndex !== -1) {
const animatedStyle = animatedWithMargin const animatedStyle = animatedWithMargin
? getMarginStyle(activeIndex, tabBarPosition) ? getMarginStyle(activeIndex, tabBarPosition)
@ -74,7 +70,7 @@ export default {
} }
return ( return (
<div class={classes} style={style}> <div class={classes} style={style}>
{this.getTabPanes()} {this.getTabPanes(children || [])}
</div> </div>
); );
}, },

View File

@ -1,5 +1,6 @@
import { inject } from 'vue';
import PropTypes from '../../_util/vue-types'; import PropTypes from '../../_util/vue-types';
import { getComponentFromProp } from '../../_util/props-util'; import { getComponent, getSlot } from '../../_util/props-util';
import Sentinel from './Sentinel'; import Sentinel from './Sentinel';
export default { export default {
@ -14,13 +15,16 @@ export default {
closable: PropTypes.bool, closable: PropTypes.bool,
disabled: PropTypes.bool, disabled: PropTypes.bool,
}, },
inject: { setup() {
sentinelContext: { default: () => ({}) }, return {
_isActived: undefined,
sentinelContext: inject('sentinelContext', {}),
};
}, },
render() { render() {
const { destroyInactiveTabPane, active, forceRender, rootPrefixCls } = this.$props; const { destroyInactiveTabPane, active, forceRender, rootPrefixCls } = this.$props;
const children = this.$slots.default; const children = getSlot(this);
const placeholder = getComponentFromProp(this, 'placeholder'); const placeholder = getComponent(this, 'placeholder');
this._isActived = this._isActived || active; this._isActived = this._isActived || active;
const prefixCls = `${rootPrefixCls}-tabpane`; const prefixCls = `${rootPrefixCls}-tabpane`;
const cls = { const cls = {

View File

@ -1,9 +1,9 @@
import omit from 'omit.js'; import { provide } from 'vue';
import BaseMixin from '../../_util/BaseMixin'; import BaseMixin from '../../_util/BaseMixin';
import PropTypes from '../../_util/vue-types'; import PropTypes from '../../_util/vue-types';
import raf from 'raf'; import raf from 'raf';
import KeyCode from './KeyCode'; import KeyCode from './KeyCode';
import { getOptionProps, getListeners } from '../../_util/props-util'; import { getOptionProps } from '../../_util/props-util';
import { cloneElement } from '../../_util/vnode'; import { cloneElement } from '../../_util/vnode';
import Sentinel from './Sentinel'; import Sentinel from './Sentinel';
import isValid from '../../_util/isValid'; import isValid from '../../_util/isValid';
@ -28,10 +28,7 @@ function activeKeyIsValid(props, key) {
export default { export default {
name: 'Tabs', name: 'Tabs',
mixins: [BaseMixin], mixins: [BaseMixin],
model: { inheritAttrs: false,
prop: 'activeKey',
event: 'change',
},
props: { props: {
destroyInactiveTabPane: PropTypes.bool, destroyInactiveTabPane: PropTypes.bool,
renderTabBar: PropTypes.func.isRequired, renderTabBar: PropTypes.func.isRequired,
@ -47,6 +44,7 @@ export default {
tabBarGutter: PropTypes.number, tabBarGutter: PropTypes.number,
}, },
data() { data() {
provide('sentinelContext', this);
const props = getOptionProps(this); const props = getOptionProps(this);
let activeKey; let activeKey;
if ('activeKey' in props) { if ('activeKey' in props) {
@ -60,11 +58,6 @@ export default {
_activeKey: activeKey, _activeKey: activeKey,
}; };
}, },
provide() {
return {
sentinelContext: this,
};
},
watch: { watch: {
__propsSymbol__() { __propsSymbol__() {
const nextProps = getOptionProps(this); const nextProps = getOptionProps(this);
@ -86,12 +79,8 @@ export default {
}, },
methods: { methods: {
onTabClick(activeKey, e) { onTabClick(activeKey, e) {
if ( if (this.tabBar.props && this.tabBar.props.onTabClick) {
this.tabBar.componentOptions && this.tabBar.props.onTabClick(activeKey, e);
this.tabBar.componentOptions.listeners &&
this.tabBar.componentOptions.listeners.tabClick
) {
this.tabBar.componentOptions.listeners.tabClick(activeKey, e);
} }
this.setActiveKey(activeKey); this.setActiveKey(activeKey);
}, },
@ -146,6 +135,7 @@ export default {
_activeKey: activeKey, _activeKey: activeKey,
}); });
} }
this.$emit('update:activeKey', activeKey);
this.__emit('change', activeKey); this.__emit('change', activeKey);
} }
}, },
@ -197,7 +187,9 @@ export default {
direction, direction,
tabBarGutter, tabBarGutter,
} = props; } = props;
const { class: className, onChange, ...restProps } = this.$attrs;
const cls = { const cls = {
[className]: className,
[prefixCls]: 1, [prefixCls]: 1,
[`${prefixCls}-${tabBarPosition}`]: 1, [`${prefixCls}-${tabBarPosition}`]: 1,
[`${prefixCls}-rtl`]: direction === 'rtl', [`${prefixCls}-rtl`]: direction === 'rtl',
@ -205,32 +197,24 @@ export default {
this.tabBar = renderTabBar(); this.tabBar = renderTabBar();
const tabBar = cloneElement(this.tabBar, { const tabBar = cloneElement(this.tabBar, {
props: { prefixCls,
prefixCls, navWrapper,
navWrapper, tabBarPosition,
tabBarPosition, panels: props.children,
panels: props.children, activeKey: this.$data._activeKey,
activeKey: this.$data._activeKey, direction,
direction, tabBarGutter,
tabBarGutter, onKeydown: this.onNavKeyDown,
}, onTabClick: this.onTabClick,
on: {
keydown: this.onNavKeyDown,
tabClick: this.onTabClick,
},
key: 'tabBar', key: 'tabBar',
}); });
const tabContent = cloneElement(renderTabContent(), { const tabContent = cloneElement(renderTabContent(), {
props: { prefixCls,
prefixCls, tabBarPosition,
tabBarPosition, activeKey: this.$data._activeKey,
activeKey: this.$data._activeKey, destroyInactiveTabPane,
destroyInactiveTabPane, direction,
direction, onChange: this.setActiveKey,
},
on: {
change: this.setActiveKey,
},
children: props.children, children: props.children,
key: 'tabContent', key: 'tabContent',
}); });
@ -257,10 +241,11 @@ export default {
} else { } else {
contents.push(tabBar, sentinelStart, tabContent, sentinelEnd); contents.push(tabBar, sentinelStart, tabContent, sentinelEnd);
} }
const listeners = { const p = {
...omit(getListeners(this), ['change']), ...restProps,
scroll: this.onScroll, onScroll: this.onScroll,
class: cls,
}; };
return <div {...{ on: listeners, class: cls }}>{contents}</div>; return <div {...p}>{contents}</div>;
}, },
}; };