refactor: card
parent
fd27c65507
commit
28212b821e
|
@ -1,46 +1,47 @@
|
||||||
import type { VNodeTypes, PropType, VNode, ExtractPropTypes } from 'vue';
|
import type { VNodeTypes, PropType, VNode, ExtractPropTypes } from 'vue';
|
||||||
import { inject, isVNode, defineComponent } from 'vue';
|
import { isVNode, defineComponent, renderSlot } from 'vue';
|
||||||
import { tuple } from '../_util/type';
|
|
||||||
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, getSlot, isEmptyElement } from '../_util/props-util';
|
import { flattenChildren, isEmptyElement } from '../_util/props-util';
|
||||||
import BaseMixin from '../_util/BaseMixin';
|
import BaseMixin from '../_util/BaseMixin';
|
||||||
import type { SizeType } from '../config-provider';
|
import type { SizeType } from '../config-provider';
|
||||||
import { defaultConfigProvider } from '../config-provider';
|
|
||||||
import isPlainObject from 'lodash-es/isPlainObject';
|
import isPlainObject from 'lodash-es/isPlainObject';
|
||||||
|
import useConfigInject from '../_util/hooks/useConfigInject';
|
||||||
|
import devWarning from '../vc-util/devWarning';
|
||||||
export interface CardTabListType {
|
export interface CardTabListType {
|
||||||
key: string;
|
key: string;
|
||||||
tab: VNodeTypes;
|
tab: any;
|
||||||
|
/** @deprecated Please use `customTab` instead. */
|
||||||
slots?: { tab: string };
|
slots?: { tab: string };
|
||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type CardType = 'inner';
|
export type CardType = 'inner';
|
||||||
|
export type CardSize = 'default' | 'small';
|
||||||
|
|
||||||
const { TabPane } = Tabs;
|
const { TabPane } = Tabs;
|
||||||
|
|
||||||
const cardProps = () => ({
|
const cardProps = () => ({
|
||||||
prefixCls: PropTypes.string,
|
prefixCls: PropTypes.string,
|
||||||
title: PropTypes.VNodeChild,
|
title: PropTypes.any,
|
||||||
extra: PropTypes.VNodeChild,
|
extra: PropTypes.any,
|
||||||
bordered: PropTypes.looseBool.def(true),
|
bordered: PropTypes.looseBool.def(true),
|
||||||
bodyStyle: PropTypes.style,
|
bodyStyle: PropTypes.style,
|
||||||
headStyle: PropTypes.style,
|
headStyle: PropTypes.style,
|
||||||
loading: PropTypes.looseBool.def(false),
|
loading: PropTypes.looseBool.def(false),
|
||||||
hoverable: PropTypes.looseBool.def(false),
|
hoverable: PropTypes.looseBool.def(false),
|
||||||
type: PropTypes.string,
|
type: { type: String as PropType<CardType> },
|
||||||
size: PropTypes.oneOf(tuple('default', 'small')),
|
size: { type: String as PropType<CardSize> },
|
||||||
actions: PropTypes.VNodeChild,
|
actions: PropTypes.any,
|
||||||
tabList: {
|
tabList: {
|
||||||
type: Array as PropType<CardTabListType[]>,
|
type: Array as PropType<CardTabListType[]>,
|
||||||
},
|
},
|
||||||
tabBarExtraContent: PropTypes.VNodeChild,
|
tabBarExtraContent: PropTypes.any,
|
||||||
activeTabKey: PropTypes.string,
|
activeTabKey: PropTypes.string,
|
||||||
defaultActiveTabKey: PropTypes.string,
|
defaultActiveTabKey: PropTypes.string,
|
||||||
cover: PropTypes.VNodeChild,
|
cover: PropTypes.any,
|
||||||
onTabChange: {
|
onTabChange: {
|
||||||
type: Function as PropType<(key: string) => void>,
|
type: Function as PropType<(key: string) => void>,
|
||||||
},
|
},
|
||||||
|
@ -52,18 +53,10 @@ const Card = defineComponent({
|
||||||
name: 'ACard',
|
name: 'ACard',
|
||||||
mixins: [BaseMixin],
|
mixins: [BaseMixin],
|
||||||
props: cardProps(),
|
props: cardProps(),
|
||||||
setup() {
|
slots: ['title', 'extra', 'tabBarExtraContent', 'actions', 'cover', 'customTab'],
|
||||||
return {
|
setup(props, { slots }) {
|
||||||
configProvider: inject('configProvider', defaultConfigProvider),
|
const { prefixCls, direction, size } = useConfigInject('card', props);
|
||||||
};
|
const getAction = (actions: VNodeTypes[]) => {
|
||||||
},
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
widerPadding: false,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
getAction(actions: VNodeTypes[]) {
|
|
||||||
const actionList = actions.map((action, index) =>
|
const actionList = actions.map((action, index) =>
|
||||||
(isVNode(action) && !isEmptyElement(action)) || !isVNode(action) ? (
|
(isVNode(action) && !isEmptyElement(action)) || !isVNode(action) ? (
|
||||||
<li style={{ width: `${100 / actions.length}%` }} key={`action-${index}`}>
|
<li style={{ width: `${100 / actions.length}%` }} key={`action-${index}`}>
|
||||||
|
@ -72,11 +65,11 @@ const Card = defineComponent({
|
||||||
) : null,
|
) : null,
|
||||||
);
|
);
|
||||||
return actionList;
|
return actionList;
|
||||||
},
|
};
|
||||||
triggerTabChange(key: string) {
|
const triggerTabChange = (key: string) => {
|
||||||
this.$emit('tabChange', key);
|
props.onTabChange?.(key);
|
||||||
},
|
};
|
||||||
isContainGrid(obj: VNode[] = []) {
|
const isContainGrid = (obj: VNode[] = []) => {
|
||||||
let containGrid: boolean;
|
let containGrid: boolean;
|
||||||
obj.forEach(element => {
|
obj.forEach(element => {
|
||||||
if (element && isPlainObject(element.type) && (element.type as any).__ANT_CARD_GRID) {
|
if (element && isPlainObject(element.type) && (element.type as any).__ANT_CARD_GRID) {
|
||||||
|
@ -84,145 +77,129 @@ const Card = defineComponent({
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return containGrid;
|
return containGrid;
|
||||||
},
|
|
||||||
},
|
|
||||||
render() {
|
|
||||||
const {
|
|
||||||
prefixCls: customizePrefixCls,
|
|
||||||
headStyle = {},
|
|
||||||
bodyStyle = {},
|
|
||||||
loading,
|
|
||||||
bordered = true,
|
|
||||||
size = 'default',
|
|
||||||
type,
|
|
||||||
tabList,
|
|
||||||
hoverable,
|
|
||||||
activeTabKey,
|
|
||||||
defaultActiveTabKey,
|
|
||||||
} = this.$props;
|
|
||||||
const { $slots } = this;
|
|
||||||
const children = getSlot(this);
|
|
||||||
const { getPrefixCls } = this.configProvider;
|
|
||||||
const prefixCls = getPrefixCls('card', customizePrefixCls);
|
|
||||||
|
|
||||||
const tabBarExtraContent = getComponent(this, 'tabBarExtraContent');
|
|
||||||
const classString = {
|
|
||||||
[`${prefixCls}`]: true,
|
|
||||||
[`${prefixCls}-loading`]: loading,
|
|
||||||
[`${prefixCls}-bordered`]: bordered,
|
|
||||||
[`${prefixCls}-hoverable`]: !!hoverable,
|
|
||||||
[`${prefixCls}-contain-grid`]: this.isContainGrid(children),
|
|
||||||
[`${prefixCls}-contain-tabs`]: tabList && tabList.length,
|
|
||||||
[`${prefixCls}-${size}`]: size !== 'default',
|
|
||||||
[`${prefixCls}-type-${type}`]: !!type,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const loadingBlockStyle =
|
return () => {
|
||||||
bodyStyle.padding === 0 || bodyStyle.padding === '0px' ? { padding: 24 } : undefined;
|
const {
|
||||||
|
headStyle = {},
|
||||||
|
bodyStyle = {},
|
||||||
|
loading,
|
||||||
|
bordered = true,
|
||||||
|
type,
|
||||||
|
tabList,
|
||||||
|
hoverable,
|
||||||
|
activeTabKey,
|
||||||
|
defaultActiveTabKey,
|
||||||
|
tabBarExtraContent = slots.tabBarExtraContent?.(),
|
||||||
|
title = slots.title?.(),
|
||||||
|
extra = slots.extra?.(),
|
||||||
|
actions = slots.actions?.(),
|
||||||
|
cover = slots.cover?.(),
|
||||||
|
} = props;
|
||||||
|
const children = flattenChildren(slots.default?.());
|
||||||
|
const pre = prefixCls.value;
|
||||||
|
const classString = {
|
||||||
|
[`${pre}`]: true,
|
||||||
|
[`${pre}-loading`]: loading,
|
||||||
|
[`${pre}-bordered`]: bordered,
|
||||||
|
[`${pre}-hoverable`]: !!hoverable,
|
||||||
|
[`${pre}-contain-grid`]: isContainGrid(children),
|
||||||
|
[`${pre}-contain-tabs`]: tabList && tabList.length,
|
||||||
|
[`${pre}-${size.value}`]: size.value,
|
||||||
|
[`${pre}-type-${type}`]: !!type,
|
||||||
|
[`${pre}-rtl`]: direction.value === 'rtl',
|
||||||
|
};
|
||||||
|
|
||||||
const loadingBlock = (
|
const loadingBlockStyle =
|
||||||
<div class={`${prefixCls}-loading-content`} style={loadingBlockStyle}>
|
bodyStyle.padding === 0 || bodyStyle.padding === '0px' ? { padding: '24px' } : undefined;
|
||||||
<Row gutter={8}>
|
|
||||||
<Col span={22}>
|
|
||||||
<div class={`${prefixCls}-loading-block`} />
|
|
||||||
</Col>
|
|
||||||
</Row>
|
|
||||||
<Row gutter={8}>
|
|
||||||
<Col span={8}>
|
|
||||||
<div class={`${prefixCls}-loading-block`} />
|
|
||||||
</Col>
|
|
||||||
<Col span={15}>
|
|
||||||
<div class={`${prefixCls}-loading-block`} />
|
|
||||||
</Col>
|
|
||||||
</Row>
|
|
||||||
<Row gutter={8}>
|
|
||||||
<Col span={6}>
|
|
||||||
<div class={`${prefixCls}-loading-block`} />
|
|
||||||
</Col>
|
|
||||||
<Col span={18}>
|
|
||||||
<div class={`${prefixCls}-loading-block`} />
|
|
||||||
</Col>
|
|
||||||
</Row>
|
|
||||||
<Row gutter={8}>
|
|
||||||
<Col span={13}>
|
|
||||||
<div class={`${prefixCls}-loading-block`} />
|
|
||||||
</Col>
|
|
||||||
<Col span={9}>
|
|
||||||
<div class={`${prefixCls}-loading-block`} />
|
|
||||||
</Col>
|
|
||||||
</Row>
|
|
||||||
<Row gutter={8}>
|
|
||||||
<Col span={4}>
|
|
||||||
<div class={`${prefixCls}-loading-block`} />
|
|
||||||
</Col>
|
|
||||||
<Col span={3}>
|
|
||||||
<div class={`${prefixCls}-loading-block`} />
|
|
||||||
</Col>
|
|
||||||
<Col span={16}>
|
|
||||||
<div class={`${prefixCls}-loading-block`} />
|
|
||||||
</Col>
|
|
||||||
</Row>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
|
|
||||||
const hasActiveTabKey = activeTabKey !== undefined;
|
const block = <div class={`${pre}-loading-block`} />;
|
||||||
const tabsProps = {
|
const loadingBlock = (
|
||||||
size: 'large' as SizeType,
|
<div class={`${pre}-loading-content`} style={loadingBlockStyle}>
|
||||||
[hasActiveTabKey ? 'activeKey' : 'defaultActiveKey']: hasActiveTabKey
|
<Row gutter={8}>
|
||||||
? activeTabKey
|
<Col span={22}>{block}</Col>
|
||||||
: defaultActiveTabKey,
|
</Row>
|
||||||
tabBarExtraContent,
|
<Row gutter={8}>
|
||||||
onChange: this.triggerTabChange,
|
<Col span={8}>{block}</Col>
|
||||||
class: `${prefixCls}-head-tabs`,
|
<Col span={15}>{block}</Col>
|
||||||
};
|
</Row>
|
||||||
|
<Row gutter={8}>
|
||||||
let head;
|
<Col span={6}>{block}</Col>
|
||||||
const tabs =
|
<Col span={18}>{block}</Col>
|
||||||
tabList && tabList.length ? (
|
</Row>
|
||||||
<Tabs {...tabsProps}>
|
<Row gutter={8}>
|
||||||
{tabList.map(item => {
|
<Col span={13}>{block}</Col>
|
||||||
const { tab: temp, slots } = item as CardTabListType;
|
<Col span={9}>{block}</Col>
|
||||||
const name = slots?.tab;
|
</Row>
|
||||||
const tab = temp !== undefined ? temp : $slots[name] ? $slots[name](item) : null;
|
<Row gutter={8}>
|
||||||
return <TabPane tab={tab} key={item.key} disabled={item.disabled} />;
|
<Col span={4}>{block}</Col>
|
||||||
})}
|
<Col span={3}>{block}</Col>
|
||||||
</Tabs>
|
<Col span={16}>{block}</Col>
|
||||||
) : null;
|
</Row>
|
||||||
const titleDom = getComponent(this, 'title');
|
|
||||||
const extraDom = getComponent(this, 'extra');
|
|
||||||
if (titleDom || extraDom || tabs) {
|
|
||||||
head = (
|
|
||||||
<div class={`${prefixCls}-head`} style={headStyle}>
|
|
||||||
<div class={`${prefixCls}-head-wrapper`}>
|
|
||||||
{titleDom && <div class={`${prefixCls}-head-title`}>{titleDom}</div>}
|
|
||||||
{extraDom && <div class={`${prefixCls}-extra`}>{extraDom}</div>}
|
|
||||||
</div>
|
|
||||||
{tabs}
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
|
||||||
|
|
||||||
const cover = getComponent(this, 'cover');
|
const hasActiveTabKey = activeTabKey !== undefined;
|
||||||
const coverDom = cover ? <div class={`${prefixCls}-cover`}>{cover}</div> : null;
|
const tabsProps = {
|
||||||
const body = (
|
size: 'large' as SizeType,
|
||||||
<div class={`${prefixCls}-body`} style={bodyStyle}>
|
[hasActiveTabKey ? 'activeKey' : 'defaultActiveKey']: hasActiveTabKey
|
||||||
{loading ? loadingBlock : children}
|
? activeTabKey
|
||||||
</div>
|
: defaultActiveTabKey,
|
||||||
);
|
onChange: triggerTabChange,
|
||||||
const actions = getComponent(this, 'actions');
|
class: `${pre}-head-tabs`,
|
||||||
const actionDom =
|
};
|
||||||
actions && actions.length ? (
|
|
||||||
<ul class={`${prefixCls}-actions`}>{this.getAction(actions)}</ul>
|
|
||||||
) : null;
|
|
||||||
|
|
||||||
return (
|
let head;
|
||||||
<div class={classString} ref="cardContainerRef">
|
const tabs =
|
||||||
{head}
|
tabList && tabList.length ? (
|
||||||
{coverDom}
|
<Tabs
|
||||||
{children ? body : null}
|
{...tabsProps}
|
||||||
{actionDom}
|
v-slots={{ rightExtra: tabBarExtraContent ? () => tabBarExtraContent : null }}
|
||||||
</div>
|
>
|
||||||
);
|
{tabList.map(item => {
|
||||||
|
const { tab: temp, slots: itemSlots } = item as CardTabListType;
|
||||||
|
const name = itemSlots?.tab;
|
||||||
|
devWarning(
|
||||||
|
!itemSlots,
|
||||||
|
'Card',
|
||||||
|
`tabList slots is deprecated, Please use \`customTab\` instead.`,
|
||||||
|
);
|
||||||
|
let tab = temp !== undefined ? temp : slots[name] ? slots[name](item) : null;
|
||||||
|
tab = renderSlot(slots, 'customTab', item as any, () => [tab]);
|
||||||
|
return <TabPane tab={tab} key={item.key} disabled={item.disabled} />;
|
||||||
|
})}
|
||||||
|
</Tabs>
|
||||||
|
) : null;
|
||||||
|
if (title || extra || tabs) {
|
||||||
|
head = (
|
||||||
|
<div class={`${pre}-head`} style={headStyle}>
|
||||||
|
<div class={`${pre}-head-wrapper`}>
|
||||||
|
{title && <div class={`${pre}-head-title`}>{title}</div>}
|
||||||
|
{extra && <div class={`${pre}-extra`}>{extra}</div>}
|
||||||
|
</div>
|
||||||
|
{tabs}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const coverDom = cover ? <div class={`${pre}-cover`}>{cover}</div> : null;
|
||||||
|
const body = (
|
||||||
|
<div class={`${pre}-body`} style={bodyStyle}>
|
||||||
|
{loading ? loadingBlock : children}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
const actionDom =
|
||||||
|
actions && actions.length ? <ul class={`${pre}-actions`}>{getAction(actions)}</ul> : null;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div class={classString} ref="cardContainerRef">
|
||||||
|
{head}
|
||||||
|
{coverDom}
|
||||||
|
{children && children.length ? body : null}
|
||||||
|
{actionDom}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -1,30 +1,23 @@
|
||||||
import { defineComponent, inject } from 'vue';
|
import { defineComponent, computed } from 'vue';
|
||||||
import PropTypes from '../_util/vue-types';
|
import useConfigInject from '../_util/hooks/useConfigInject';
|
||||||
import { defaultConfigProvider } from '../config-provider';
|
|
||||||
import { getSlot } from '../_util/props-util';
|
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: 'ACardGrid',
|
name: 'ACardGrid',
|
||||||
__ANT_CARD_GRID: true,
|
__ANT_CARD_GRID: true,
|
||||||
props: {
|
props: {
|
||||||
prefixCls: PropTypes.string,
|
prefixCls: String,
|
||||||
hoverable: PropTypes.looseBool,
|
hoverable: { type: Boolean, default: true },
|
||||||
},
|
},
|
||||||
setup() {
|
setup(props, { slots }) {
|
||||||
return {
|
const { prefixCls } = useConfigInject('card', props);
|
||||||
configProvider: inject('configProvider', defaultConfigProvider),
|
const classNames = computed(() => {
|
||||||
|
return {
|
||||||
|
[`${prefixCls.value}-grid`]: true,
|
||||||
|
[`${prefixCls.value}-grid-hoverable`]: props.hoverable,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
return () => {
|
||||||
|
return <div class={classNames.value}>{slots.default?.()}</div>;
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
render() {
|
|
||||||
const { prefixCls: customizePrefixCls, hoverable = true } = this.$props;
|
|
||||||
|
|
||||||
const { getPrefixCls } = this.configProvider;
|
|
||||||
const prefixCls = getPrefixCls('card', customizePrefixCls);
|
|
||||||
|
|
||||||
const classString = {
|
|
||||||
[`${prefixCls}-grid`]: true,
|
|
||||||
[`${prefixCls}-grid-hoverable`]: hoverable,
|
|
||||||
};
|
|
||||||
return <div class={classString}>{getSlot(this)}</div>;
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,52 +1,47 @@
|
||||||
import { defineComponent, inject } from 'vue';
|
import { defineComponent } from 'vue';
|
||||||
import PropTypes from '../_util/vue-types';
|
import PropTypes from '../_util/vue-types';
|
||||||
import { getComponent } from '../_util/props-util';
|
import { getPropsSlot } from '../_util/props-util';
|
||||||
import { defaultConfigProvider } from '../config-provider';
|
import useConfigInject from '../_util/hooks/useConfigInject';
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: 'ACardMeta',
|
name: 'ACardMeta',
|
||||||
props: {
|
props: {
|
||||||
prefixCls: PropTypes.string,
|
prefixCls: PropTypes.string,
|
||||||
title: PropTypes.VNodeChild,
|
title: PropTypes.any,
|
||||||
description: PropTypes.VNodeChild,
|
description: PropTypes.any,
|
||||||
avatar: PropTypes.VNodeChild,
|
avatar: PropTypes.any,
|
||||||
},
|
},
|
||||||
setup() {
|
slots: ['title', 'description', 'avatar'],
|
||||||
return {
|
setup(props, { slots }) {
|
||||||
configProvider: inject('configProvider', defaultConfigProvider),
|
const { prefixCls } = useConfigInject('card', props);
|
||||||
};
|
return () => {
|
||||||
},
|
const classString = {
|
||||||
render() {
|
[`${prefixCls.value}-meta`]: true,
|
||||||
const { prefixCls: customizePrefixCls } = this.$props;
|
};
|
||||||
|
const avatar = getPropsSlot(slots, props, 'avatar');
|
||||||
|
const title = getPropsSlot(slots, props, 'title');
|
||||||
|
const description = getPropsSlot(slots, props, 'description');
|
||||||
|
|
||||||
const { getPrefixCls } = this.configProvider;
|
const avatarDom = avatar ? (
|
||||||
const prefixCls = getPrefixCls('card', customizePrefixCls);
|
<div class={`${prefixCls.value}-meta-avatar`}>{avatar}</div>
|
||||||
|
|
||||||
const classString = {
|
|
||||||
[`${prefixCls}-meta`]: true,
|
|
||||||
};
|
|
||||||
|
|
||||||
const avatar = getComponent(this, 'avatar');
|
|
||||||
const title = getComponent(this, 'title');
|
|
||||||
const description = getComponent(this, 'description');
|
|
||||||
|
|
||||||
const avatarDom = avatar ? <div class={`${prefixCls}-meta-avatar`}>{avatar}</div> : null;
|
|
||||||
const titleDom = title ? <div class={`${prefixCls}-meta-title`}>{title}</div> : null;
|
|
||||||
const descriptionDom = description ? (
|
|
||||||
<div class={`${prefixCls}-meta-description`}>{description}</div>
|
|
||||||
) : null;
|
|
||||||
const MetaDetail =
|
|
||||||
titleDom || descriptionDom ? (
|
|
||||||
<div class={`${prefixCls}-meta-detail`}>
|
|
||||||
{titleDom}
|
|
||||||
{descriptionDom}
|
|
||||||
</div>
|
|
||||||
) : null;
|
) : null;
|
||||||
return (
|
const titleDom = title ? <div class={`${prefixCls.value}-meta-title`}>{title}</div> : null;
|
||||||
<div class={classString}>
|
const descriptionDom = description ? (
|
||||||
{avatarDom}
|
<div class={`${prefixCls.value}-meta-description`}>{description}</div>
|
||||||
{MetaDetail}
|
) : null;
|
||||||
</div>
|
const MetaDetail =
|
||||||
);
|
titleDom || descriptionDom ? (
|
||||||
|
<div class={`${prefixCls.value}-meta-detail`}>
|
||||||
|
{titleDom}
|
||||||
|
{descriptionDom}
|
||||||
|
</div>
|
||||||
|
) : null;
|
||||||
|
return (
|
||||||
|
<div class={classString}>
|
||||||
|
{avatarDom}
|
||||||
|
{MetaDetail}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
|
@ -365,7 +365,7 @@ exports[`renders ./components/card/demo/tabs.vue correctly 1`] = `
|
||||||
<!----><button type="button" class="ant-tabs-nav-more" style="visibility: hidden; order: 1;" tabindex="-1" aria-hidden="true" aria-haspopup="listbox" aria-controls="rc-tabs-test-more-popup" id="rc-tabs-test-more" aria-expanded="false"><span role="img" aria-label="ellipsis" class="anticon anticon-ellipsis"><svg focusable="false" class="" data-icon="ellipsis" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M176 511a56 56 0 10112 0 56 56 0 10-112 0zm280 0a56 56 0 10112 0 56 56 0 10-112 0zm280 0a56 56 0 10112 0 56 56 0 10-112 0z"></path></svg></span></button>
|
<!----><button type="button" class="ant-tabs-nav-more" style="visibility: hidden; order: 1;" tabindex="-1" aria-hidden="true" aria-haspopup="listbox" aria-controls="rc-tabs-test-more-popup" id="rc-tabs-test-more" aria-expanded="false"><span role="img" aria-label="ellipsis" class="anticon anticon-ellipsis"><svg focusable="false" class="" data-icon="ellipsis" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M176 511a56 56 0 10112 0 56 56 0 10-112 0zm280 0a56 56 0 10112 0 56 56 0 10-112 0zm280 0a56 56 0 10112 0 56 56 0 10-112 0z"></path></svg></span></button>
|
||||||
<!---->
|
<!---->
|
||||||
</div>
|
</div>
|
||||||
<!---->
|
<div class="ant-tabs-extra-content"><a href="#">More</a></div>
|
||||||
<!---->
|
<!---->
|
||||||
</div>
|
</div>
|
||||||
<div class="ant-tabs-content-holder">
|
<div class="ant-tabs-content-holder">
|
||||||
|
|
|
@ -5,7 +5,7 @@ exports[`Card should still have padding when card which set padding to 0 is load
|
||||||
<!---->
|
<!---->
|
||||||
<!---->
|
<!---->
|
||||||
<div class="ant-card-body" style="padding: 0px;">
|
<div class="ant-card-body" style="padding: 0px;">
|
||||||
<div class="ant-card-loading-content">
|
<div class="ant-card-loading-content" style="padding: 24px;">
|
||||||
<div class="ant-row" style="margin-left: -4px; margin-right: -4px;">
|
<div class="ant-row" style="margin-left: -4px; margin-right: -4px;">
|
||||||
<div class="ant-col ant-col-22" style="padding-left: 4px; padding-right: 4px;">
|
<div class="ant-col ant-col-22" style="padding-left: 4px; padding-right: 4px;">
|
||||||
<div class="ant-card-loading-block"></div>
|
<div class="ant-card-loading-block"></div>
|
||||||
|
|
|
@ -24,8 +24,8 @@ More content can be hosted
|
||||||
:active-tab-key="key"
|
:active-tab-key="key"
|
||||||
@tabChange="key => onTabChange(key, 'key')"
|
@tabChange="key => onTabChange(key, 'key')"
|
||||||
>
|
>
|
||||||
<template #customRender="item">
|
<template #customTab="item">
|
||||||
<span>
|
<span v-if="item.key === 'tab1'">
|
||||||
<home-outlined />
|
<home-outlined />
|
||||||
{{ item.key }}
|
{{ item.key }}
|
||||||
</span>
|
</span>
|
||||||
|
@ -63,8 +63,7 @@ export default defineComponent({
|
||||||
const tabList = [
|
const tabList = [
|
||||||
{
|
{
|
||||||
key: 'tab1',
|
key: 'tab1',
|
||||||
// tab: 'tab1',
|
tab: 'tab1',
|
||||||
slots: { tab: 'customRender' },
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'tab2',
|
key: 'tab2',
|
||||||
|
|
|
@ -17,22 +17,30 @@ A card can be used to display content related to a single subject. The content c
|
||||||
|
|
||||||
| Property | Description | Type | Default | Version |
|
| Property | Description | Type | Default | Version |
|
||||||
| --- | --- | --- | --- | --- |
|
| --- | --- | --- | --- | --- |
|
||||||
| actions | The action list, shows at the bottom of the Card. | slots | - | |
|
|
||||||
| activeTabKey | Current TabPane's key | string | - | |
|
| activeTabKey | Current TabPane's key | string | - | |
|
||||||
| headStyle | Inline style to apply to the card head | object | - | |
|
| headStyle | Inline style to apply to the card head | object | - | |
|
||||||
| bodyStyle | Inline style to apply to the card content | object | - | |
|
| bodyStyle | Inline style to apply to the card content | object | - | |
|
||||||
| bordered | Toggles rendering of the border around the card | boolean | `true` | |
|
| bordered | Toggles rendering of the border around the card | boolean | `true` | |
|
||||||
| cover | Card cover | slot | - | |
|
|
||||||
| defaultActiveTabKey | Initial active TabPane's key, if `activeTabKey` is not set. | string | - | |
|
| defaultActiveTabKey | Initial active TabPane's key, if `activeTabKey` is not set. | string | - | |
|
||||||
| extra | Content to render in the top-right corner of the card | string\|slot | - | |
|
| extra | Content to render in the top-right corner of the card | string\|slot | - | |
|
||||||
| hoverable | Lift up when hovering card | boolean | false | |
|
| hoverable | Lift up when hovering card | boolean | false | |
|
||||||
| loading | Shows a loading indicator while the contents of the card are being fetched | boolean | false | |
|
| loading | Shows a loading indicator while the contents of the card are being fetched | boolean | false | |
|
||||||
| tabList | List of TabPane's head, Custom tabs can be created with the slots property | Array<{key: string, tab: any, slots: {tab: 'XXX'}}> | - | |
|
| tabList | List of TabPane's head, Custom tabs with the customTab(v3.0) slot | Array<{key: string, tab: any}> | - | |
|
||||||
| tabBarExtraContent | Extra content in tab bar | slot | - | 1.5.0 |
|
|
||||||
| size | Size of card | `default` \| `small` | `default` | |
|
| size | Size of card | `default` \| `small` | `default` | |
|
||||||
| title | Card title | string\|slot | - | |
|
| title | Card title | string\|slot | - | |
|
||||||
| type | Card style type, can be set to `inner` or not set | string | - | |
|
| type | Card style type, can be set to `inner` or not set | string | - | |
|
||||||
|
|
||||||
|
### Card Slots
|
||||||
|
|
||||||
|
| Slot Name | Description | Type |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| customTab | custom tabList tab | { item: tabList[number] } | |
|
||||||
|
| title | Card title | - | |
|
||||||
|
| extra | Content to render in the top-right corner of the card | - | |
|
||||||
|
| tabBarExtraContent | Extra content in tab bar | - | |
|
||||||
|
| actions | The action list, shows at the bottom of the Card. | - | |
|
||||||
|
| cover | Card cover | - | |
|
||||||
|
|
||||||
### events
|
### events
|
||||||
|
|
||||||
| Events Name | Description | Arguments | Version |
|
| Events Name | Description | Arguments | Version |
|
||||||
|
|
|
@ -18,22 +18,30 @@ cover: https://gw.alipayobjects.com/zos/antfincdn/NqXt8DJhky/Card.svg
|
||||||
|
|
||||||
| 参数 | 说明 | 类型 | 默认值 | 版本 |
|
| 参数 | 说明 | 类型 | 默认值 | 版本 |
|
||||||
| --- | --- | --- | --- | --- |
|
| --- | --- | --- | --- | --- |
|
||||||
| actions | 卡片操作组,位置在卡片底部 | slots | - | |
|
|
||||||
| activeTabKey | 当前激活页签的 key | string | - | |
|
| activeTabKey | 当前激活页签的 key | string | - | |
|
||||||
| headStyle | 自定义标题区域样式 | object | - | |
|
| headStyle | 自定义标题区域样式 | object | - | |
|
||||||
| bodyStyle | 内容区域自定义样式 | object | - | |
|
| bodyStyle | 内容区域自定义样式 | object | - | |
|
||||||
| bordered | 是否有边框 | boolean | true | |
|
| bordered | 是否有边框 | boolean | true | |
|
||||||
| cover | 卡片封面 | slot | - | |
|
|
||||||
| defaultActiveTabKey | 初始化选中页签的 key,如果没有设置 activeTabKey | string | 第一个页签 | |
|
| defaultActiveTabKey | 初始化选中页签的 key,如果没有设置 activeTabKey | string | 第一个页签 | |
|
||||||
| extra | 卡片右上角的操作区域 | string\|slot | - | |
|
| extra | 卡片右上角的操作区域 | string\|slot | - | |
|
||||||
| hoverable | 鼠标移过时可浮起 | boolean | false | |
|
| hoverable | 鼠标移过时可浮起 | boolean | false | |
|
||||||
| loading | 当卡片内容还在加载中时,可以用 loading 展示一个占位 | boolean | false | |
|
| loading | 当卡片内容还在加载中时,可以用 loading 展示一个占位 | boolean | false | |
|
||||||
| tabList | 页签标题列表, 可以通过 slots 属性自定义 tab | Array<{key: string, tab: any, slots: {tab: 'XXX'}}> | - | |
|
| tabList | 页签标题列表, 可以通过 customTab(v3.0) 插槽自定义 tab | Array<{key: string, tab: any}> | - | |
|
||||||
| tabBarExtraContent | tab bar 上额外的元素 | slot | 无 | 1.5.0 |
|
|
||||||
| size | card 的尺寸 | `default` \| `small` | `default` | |
|
| size | card 的尺寸 | `default` \| `small` | `default` | |
|
||||||
| title | 卡片标题 | string\|slot | - | |
|
| title | 卡片标题 | string\|slot | - | |
|
||||||
| type | 卡片类型,可设置为 `inner` 或 不设置 | string | - | |
|
| type | 卡片类型,可设置为 `inner` 或 不设置 | string | - | |
|
||||||
|
|
||||||
|
### Card 插槽
|
||||||
|
|
||||||
|
| 插槽名称 | 说明 | 参数 |
|
||||||
|
| ------------------ | -------------------------- | ------------------------- | --- |
|
||||||
|
| customTab | 自定义 tabList tab 标签 | { item: tabList[number] } | |
|
||||||
|
| title | 卡片标题 | - | |
|
||||||
|
| extra | 卡片右上角的操作区域 | - | |
|
||||||
|
| tabBarExtraContent | tab bar 上额外的元素 | - | |
|
||||||
|
| actions | 卡片操作组,位置在卡片底部 | - | |
|
||||||
|
| cover | 卡片封面 | - | |
|
||||||
|
|
||||||
### 事件
|
### 事件
|
||||||
|
|
||||||
| 事件名称 | 说明 | 回调参数 | 版本 |
|
| 事件名称 | 说明 | 回调参数 | 版本 |
|
||||||
|
|
|
@ -2,8 +2,7 @@
|
||||||
@import '../../style/mixins/index';
|
@import '../../style/mixins/index';
|
||||||
|
|
||||||
@card-prefix-cls: ~'@{ant-prefix}-card';
|
@card-prefix-cls: ~'@{ant-prefix}-card';
|
||||||
@card-head-height: 48px;
|
@card-hoverable-hover-border: transparent;
|
||||||
@card-hover-border: fade(@black, 9%);
|
|
||||||
@card-action-icon-size: 16px;
|
@card-action-icon-size: 16px;
|
||||||
|
|
||||||
@gradient-min: fade(@card-skeleton-bg, 20%);
|
@gradient-min: fade(@card-skeleton-bg, 20%);
|
||||||
|
@ -15,14 +14,17 @@
|
||||||
position: relative;
|
position: relative;
|
||||||
background: @card-background;
|
background: @card-background;
|
||||||
border-radius: @card-radius;
|
border-radius: @card-radius;
|
||||||
transition: all 0.3s;
|
|
||||||
|
&-rtl {
|
||||||
|
direction: rtl;
|
||||||
|
}
|
||||||
|
|
||||||
&-hoverable {
|
&-hoverable {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
transition: box-shadow 0.3s border-color 0.3s;
|
transition: box-shadow 0.3s, border-color 0.3s;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
border-color: @card-hover-border;
|
border-color: @card-hoverable-hover-border;
|
||||||
box-shadow: @card-shadow;
|
box-shadow: @card-shadow;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -37,7 +39,7 @@
|
||||||
padding: 0 @card-padding-base;
|
padding: 0 @card-padding-base;
|
||||||
color: @card-head-color;
|
color: @card-head-color;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
font-size: @font-size-lg;
|
font-size: @card-head-font-size;
|
||||||
background: @card-head-background;
|
background: @card-head-background;
|
||||||
border-bottom: @border-width-base @border-style-base @border-color-split;
|
border-bottom: @border-width-base @border-style-base @border-color-split;
|
||||||
border-radius: @card-radius @card-radius 0 0;
|
border-radius: @card-radius @card-radius 0 0;
|
||||||
|
@ -55,11 +57,18 @@
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
|
|
||||||
|
> .@{ant-prefix}-typography,
|
||||||
|
> .@{ant-prefix}-typography-edit-content {
|
||||||
|
left: 0;
|
||||||
|
margin-top: 0;
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.@{ant-prefix}-tabs {
|
.@{ant-prefix}-tabs {
|
||||||
clear: both;
|
clear: both;
|
||||||
margin-bottom: -17px;
|
margin-bottom: @card-head-tabs-margin-bottom;
|
||||||
color: @text-color;
|
color: @text-color;
|
||||||
font-weight: normal;
|
font-weight: normal;
|
||||||
font-size: @font-size-base;
|
font-size: @font-size-base;
|
||||||
|
@ -75,9 +84,14 @@
|
||||||
// https://stackoverflow.com/a/22429853/3040605
|
// https://stackoverflow.com/a/22429853/3040605
|
||||||
margin-left: auto;
|
margin-left: auto;
|
||||||
padding: @card-head-padding 0;
|
padding: @card-head-padding 0;
|
||||||
color: @text-color;
|
color: @card-head-extra-color;
|
||||||
font-weight: normal;
|
font-weight: normal;
|
||||||
font-size: @font-size-base;
|
font-size: @font-size-base;
|
||||||
|
|
||||||
|
.@{card-prefix-cls}-rtl & {
|
||||||
|
margin-right: auto;
|
||||||
|
margin-left: 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&-body {
|
&-body {
|
||||||
|
@ -100,11 +114,16 @@
|
||||||
1px 1px 0 0 @border-color-split, 1px 0 0 0 @border-color-split inset,
|
1px 1px 0 0 @border-color-split, 1px 0 0 0 @border-color-split inset,
|
||||||
0 1px 0 0 @border-color-split inset;
|
0 1px 0 0 @border-color-split inset;
|
||||||
transition: all 0.3s;
|
transition: all 0.3s;
|
||||||
|
|
||||||
|
.@{card-prefix-cls}-rtl & {
|
||||||
|
float: right;
|
||||||
|
}
|
||||||
|
|
||||||
&-hoverable {
|
&-hoverable {
|
||||||
&:hover {
|
&:hover {
|
||||||
position: relative;
|
position: relative;
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
box-shadow: @box-shadow-base;
|
box-shadow: @card-shadow;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -118,11 +137,18 @@
|
||||||
padding-bottom: 0;
|
padding-bottom: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&-bordered &-cover {
|
||||||
|
margin-top: -1px;
|
||||||
|
margin-right: -1px;
|
||||||
|
margin-left: -1px;
|
||||||
|
}
|
||||||
|
|
||||||
&-cover {
|
&-cover {
|
||||||
> * {
|
> * {
|
||||||
display: block;
|
display: block;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
img {
|
img {
|
||||||
border-radius: @card-radius @card-radius 0 0;
|
border-radius: @card-radius @card-radius 0 0;
|
||||||
}
|
}
|
||||||
|
@ -138,16 +164,20 @@
|
||||||
|
|
||||||
& > li {
|
& > li {
|
||||||
float: left;
|
float: left;
|
||||||
margin: 12px 0;
|
margin: @card-actions-li-margin;
|
||||||
color: @text-color-secondary;
|
color: @text-color-secondary;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
|
||||||
|
.@{card-prefix-cls}-rtl & {
|
||||||
|
float: right;
|
||||||
|
}
|
||||||
|
|
||||||
> span {
|
> span {
|
||||||
position: relative;
|
position: relative;
|
||||||
display: block;
|
display: block;
|
||||||
min-width: 32px;
|
min-width: 32px;
|
||||||
font-size: @font-size-base;
|
font-size: @font-size-base;
|
||||||
line-height: 22px;
|
line-height: @line-height-base;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
|
@ -156,7 +186,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
a:not(.@{ant-prefix}-btn),
|
a:not(.@{ant-prefix}-btn),
|
||||||
> .anticon {
|
> .@{iconfont-css-prefix} {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
color: @text-color-secondary;
|
color: @text-color-secondary;
|
||||||
|
@ -168,7 +198,7 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
> .anticon {
|
> .@{iconfont-css-prefix} {
|
||||||
font-size: @card-action-icon-size;
|
font-size: @card-action-icon-size;
|
||||||
line-height: 22px;
|
line-height: 22px;
|
||||||
}
|
}
|
||||||
|
@ -176,6 +206,11 @@
|
||||||
|
|
||||||
&:not(:last-child) {
|
&:not(:last-child) {
|
||||||
border-right: @border-width-base @border-style-base @border-color-split;
|
border-right: @border-width-base @border-style-base @border-color-split;
|
||||||
|
|
||||||
|
.@{card-prefix-cls}-rtl & {
|
||||||
|
border-right: none;
|
||||||
|
border-left: @border-width-base @border-style-base @border-color-split;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -205,12 +240,18 @@
|
||||||
&-avatar {
|
&-avatar {
|
||||||
float: left;
|
float: left;
|
||||||
padding-right: 16px;
|
padding-right: 16px;
|
||||||
|
|
||||||
|
.@{card-prefix-cls}-rtl & {
|
||||||
|
float: right;
|
||||||
|
padding-right: 0;
|
||||||
|
padding-left: 16px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&-detail {
|
&-detail {
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
> div:not(:last-child) {
|
> div:not(:last-child) {
|
||||||
margin-bottom: 8px;
|
margin-bottom: @margin-xs;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +0,0 @@
|
||||||
import '../../style/index.less';
|
|
||||||
import './index.less';
|
|
||||||
|
|
||||||
// style dependencies
|
|
||||||
import '../../tabs/style';
|
|
||||||
import '../../row/style';
|
|
||||||
import '../../col/style';
|
|
|
@ -1,8 +1,3 @@
|
||||||
@card-head-height-sm: 36px;
|
|
||||||
@card-padding-base-sm: (@card-padding-base / 2);
|
|
||||||
@card-head-padding-sm: (@card-head-padding / 2);
|
|
||||||
@card-head-font-size-sm: @font-size-base;
|
|
||||||
|
|
||||||
.@{card-prefix-cls}-small {
|
.@{card-prefix-cls}-small {
|
||||||
> .@{card-prefix-cls}-head {
|
> .@{card-prefix-cls}-head {
|
||||||
min-height: @card-head-height-sm;
|
min-height: @card-head-height-sm;
|
||||||
|
|
|
@ -657,14 +657,24 @@
|
||||||
// ---
|
// ---
|
||||||
@card-head-color: @heading-color;
|
@card-head-color: @heading-color;
|
||||||
@card-head-background: transparent;
|
@card-head-background: transparent;
|
||||||
|
@card-head-font-size: @font-size-lg;
|
||||||
|
@card-head-font-size-sm: @font-size-base;
|
||||||
@card-head-padding: 16px;
|
@card-head-padding: 16px;
|
||||||
|
@card-head-padding-sm: (@card-head-padding / 2);
|
||||||
|
@card-head-height: 48px;
|
||||||
|
@card-head-height-sm: 36px;
|
||||||
@card-inner-head-padding: 12px;
|
@card-inner-head-padding: 12px;
|
||||||
@card-padding-base: 24px;
|
@card-padding-base: 24px;
|
||||||
@card-actions-background: @background-color-light;
|
@card-padding-base-sm: (@card-padding-base / 2);
|
||||||
|
@card-actions-background: @component-background;
|
||||||
|
@card-actions-li-margin: 12px 0;
|
||||||
@card-skeleton-bg: #cfd8dc;
|
@card-skeleton-bg: #cfd8dc;
|
||||||
@card-background: @component-background;
|
@card-background: @component-background;
|
||||||
@card-shadow: 0 2px 8px rgba(0, 0, 0, 0.09);
|
@card-shadow: 0 1px 2px -2px rgba(0, 0, 0, 0.16), 0 3px 6px 0 rgba(0, 0, 0, 0.12),
|
||||||
@card-radius: @border-radius-sm;
|
0 5px 12px 4px rgba(0, 0, 0, 0.09);
|
||||||
|
@card-radius: @border-radius-base;
|
||||||
|
@card-head-tabs-margin-bottom: -17px;
|
||||||
|
@card-head-extra-color: @text-color;
|
||||||
|
|
||||||
// Comment
|
// Comment
|
||||||
// ---
|
// ---
|
||||||
|
|
|
@ -32,7 +32,7 @@ Ant Design has 3 types of Tabs for different situations.
|
||||||
|
|
||||||
### Tabs Slots
|
### Tabs Slots
|
||||||
|
|
||||||
| 插槽名称 | 说明 | 参数 |
|
| Slot Name | Description | Type |
|
||||||
| ------------ | ------------------------------ | ----------------- | --- |
|
| ------------ | ------------------------------ | ----------------- | --- |
|
||||||
| renderTabBar | Replace the TabBar | { DefaultTabBar } | |
|
| renderTabBar | Replace the TabBar | { DefaultTabBar } | |
|
||||||
| leftExtra | Extra content in tab bar left | - | - |
|
| leftExtra | Extra content in tab bar left | - | - |
|
||||||
|
|
Loading…
Reference in New Issue