feat: update tabs

pull/2494/head
tangjinzhou 4 years ago
parent 132a1e58bf
commit 8ce8e9a821

@ -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 ? (
<Tabs {...tabsProps}>
{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 <TabPane tab={tab} key={item.key} disabled={item.disabled} />;
})}
</Tabs>
@ -182,7 +176,6 @@ export default {
);
}
const children = $slots.default;
const cover = getComponent(this, 'cover');
const coverDom = cover ? <div class={`${prefixCls}-cover`}>{cover}</div> : null;
const body = (
@ -190,18 +183,14 @@ export default {
{loading ? loadingBlock : children}
</div>
);
const actions = filterEmpty(this.$slots.actions);
const actions = filterEmpty(getComponent(this, 'action'));
const actionDom =
actions && actions.length ? (
<ul class={`${prefixCls}-actions`}>{this.getAction(actions)}</ul>
) : null;
return (
<div
class={classString}
ref="cardContainerRef"
{...{ on: omit(getListeners(this), ['tabChange', 'tab-change']) }}
>
<div class={classString} ref="cardContainerRef">
{head}
{coverDom}
{children ? body : null}

@ -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 = {
)}
</span>
);
// 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,
};

@ -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;

@ -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: (
<div class={closable ? undefined : `${prefixCls}-tab-unclosable`}>
{getComponentFromProp(child, 'tab')}
{closeIcon}
</div>
),
},
tab: (
<div class={closable ? undefined : `${prefixCls}-tab-unclosable`}>
{getComponent(child, 'tab')}
{closeIcon}
</div>
),
key: child.key || index,
}),
);
@ -143,40 +149,33 @@ export default {
<div class={`${prefixCls}-extra-content`}>{tabBarExtraContent}</div>
) : 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
// keybabel jsx mergeTabBar tabBarProps
renderTabBar: () => <TabBar key="tabBar" {...tabBarProps} />,
renderTabContent: () => (
<TabContent class={contentCls} animated={tabPaneAnimated} animatedWithMargin />
),
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
// keybabel jsx mergeTabBar tabBarProps
renderTabBar: () => <TabBar key="tabBar" {...tabBarProps} />,
renderTabContent: () => (
<TabContent class={contentCls} animated={tabPaneAnimated} animatedWithMargin />
),
children: childrenWithClose.length > 0 ? childrenWithClose : children,
__propsSymbol__: Symbol(),
...restProps,
onChange: this.handleChange,
class: cls,
};
return <VcTabs {...tabsProps} />;

@ -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 = [

@ -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}
</div>
@ -84,19 +78,6 @@ export default {
rst.push(node);
});
return (
<div
{...{
directives: [
{
name: 'ant-ref',
value: this.saveRef('navTabsContainer'),
},
],
}}
>
{rst}
</div>
);
return <div {...createRefHooks(this.saveRef('navTabsContainer'))}>{rst}</div>;
},
};

@ -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 (
<div class={classes} style={style}>
{this.getTabPanes()}
{this.getTabPanes(children || [])}
</div>
);
},

@ -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 = {

@ -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 <div {...{ on: listeners, class: cls }}>{contents}</div>;
return <div {...p}>{contents}</div>;
},
};

Loading…
Cancel
Save