feat(list): update to vue3 (#2513)
* feat(list): update to vue3 * feat(list): refactor meta to functional * feat(list): code reviewpull/2682/head
parent
6c77afec12
commit
80599668df
|
@ -1,15 +1,11 @@
|
||||||
import PropTypes from '../_util/vue-types';
|
import PropTypes from '../_util/vue-types';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import {
|
import { getComponent, isStringElement, isEmptyElement, getSlot } from '../_util/props-util';
|
||||||
getComponentFromProp,
|
|
||||||
isStringElement,
|
|
||||||
getListeners,
|
|
||||||
isEmptyElement,
|
|
||||||
} from '../_util/props-util';
|
|
||||||
import { Col } from '../grid';
|
import { Col } from '../grid';
|
||||||
import { ConfigConsumerProps } from '../config-provider';
|
import { ConfigConsumerProps } from '../config-provider';
|
||||||
import { ListGridType } from './index';
|
import { ListGridType } from './index';
|
||||||
import { cloneElement } from '../_util/vnode';
|
import { cloneElement } from '../_util/vnode';
|
||||||
|
import { inject } from 'vue';
|
||||||
|
|
||||||
export const ListItemProps = {
|
export const ListItemProps = {
|
||||||
prefixCls: PropTypes.string,
|
prefixCls: PropTypes.string,
|
||||||
|
@ -25,55 +21,56 @@ export const ListItemMetaProps = {
|
||||||
title: PropTypes.any,
|
title: PropTypes.any,
|
||||||
};
|
};
|
||||||
|
|
||||||
export const Meta = {
|
export const Meta = (props, { slots, attrs }) => {
|
||||||
functional: true,
|
const configProvider = inject('configProvider', ConfigConsumerProps);
|
||||||
name: 'AListItemMeta',
|
const { style, class: _cls } = attrs;
|
||||||
__ANT_LIST_ITEM_META: true,
|
const getPrefixCls = configProvider.getPrefixCls;
|
||||||
inject: {
|
const { prefixCls: customizePrefixCls } = props;
|
||||||
configProvider: { default: () => ConfigConsumerProps },
|
const prefixCls = getPrefixCls('list', customizePrefixCls);
|
||||||
},
|
const avatar = props.avatar || slots.avatar?.();
|
||||||
render(h, context) {
|
const title = props.title || slots.title?.();
|
||||||
const { props, slots, listeners, injections } = context;
|
const description = props.description || slots.description?.();
|
||||||
const slotsMap = slots();
|
const content = (
|
||||||
const getPrefixCls = injections.configProvider.getPrefixCls;
|
<div class={`${prefixCls}-item-meta-content`}>
|
||||||
const { prefixCls: customizePrefixCls } = props;
|
{title && <h4 class={`${prefixCls}-item-meta-title`}>{title}</h4>}
|
||||||
const prefixCls = getPrefixCls('list', customizePrefixCls);
|
{description && <div class={`${prefixCls}-item-meta-description`}>{description}</div>}
|
||||||
|
</div>
|
||||||
const avatar = props.avatar || slotsMap.avatar;
|
);
|
||||||
const title = props.title || slotsMap.title;
|
return (
|
||||||
const description = props.description || slotsMap.description;
|
<div class={`${prefixCls}-item-meta`} style={style} class={_cls}>
|
||||||
const content = (
|
{avatar && <div class={`${prefixCls}-item-meta-avatar`}>{avatar}</div>}
|
||||||
<div class={`${prefixCls}-item-meta-content`}>
|
{(title || description) && content}
|
||||||
{title && <h4 class={`${prefixCls}-item-meta-title`}>{title}</h4>}
|
</div>
|
||||||
{description && <div class={`${prefixCls}-item-meta-description`}>{description}</div>}
|
);
|
||||||
</div>
|
|
||||||
);
|
|
||||||
return (
|
|
||||||
<div {...{ on: listeners }} class={`${prefixCls}-item-meta`}>
|
|
||||||
{avatar && <div class={`${prefixCls}-item-meta-avatar`}>{avatar}</div>}
|
|
||||||
{(title || description) && content}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Object.assign(Meta, {
|
||||||
|
props: ListItemMetaProps,
|
||||||
|
inheritAttrs: false,
|
||||||
|
__ANT_LIST_ITEM_META: true,
|
||||||
|
});
|
||||||
|
|
||||||
function getGrid(grid, t) {
|
function getGrid(grid, t) {
|
||||||
return grid[t] && Math.floor(24 / grid[t]);
|
return grid[t] && Math.floor(24 / grid[t]);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'AListItem',
|
name: 'AListItem',
|
||||||
|
inheritAttrs: false,
|
||||||
Meta,
|
Meta,
|
||||||
props: ListItemProps,
|
props: ListItemProps,
|
||||||
inject: {
|
setup() {
|
||||||
listContext: { default: () => ({}) },
|
const listContext = inject('listContext', {});
|
||||||
configProvider: { default: () => ConfigConsumerProps },
|
const configProvider = inject('configProvider', ConfigConsumerProps);
|
||||||
|
return {
|
||||||
|
listContext,
|
||||||
|
configProvider,
|
||||||
|
};
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
isItemContainsTextNodeAndNotSingular() {
|
isItemContainsTextNodeAndNotSingular() {
|
||||||
const { $slots } = this;
|
const children = getSlot(this) || [];
|
||||||
let result;
|
let result;
|
||||||
const children = $slots.default || [];
|
|
||||||
children.forEach(element => {
|
children.forEach(element => {
|
||||||
if (isStringElement(element) && !isEmptyElement(element)) {
|
if (isStringElement(element) && !isEmptyElement(element)) {
|
||||||
result = true;
|
result = true;
|
||||||
|
@ -83,7 +80,7 @@ export default {
|
||||||
},
|
},
|
||||||
|
|
||||||
isFlexMode() {
|
isFlexMode() {
|
||||||
const extra = getComponentFromProp(this, 'extra');
|
const extra = getComponent(this, 'extra');
|
||||||
const { itemLayout } = this.listContext;
|
const { itemLayout } = this.listContext;
|
||||||
if (itemLayout === 'vertical') {
|
if (itemLayout === 'vertical') {
|
||||||
return !!extra;
|
return !!extra;
|
||||||
|
@ -93,12 +90,12 @@ export default {
|
||||||
},
|
},
|
||||||
render() {
|
render() {
|
||||||
const { grid, itemLayout } = this.listContext;
|
const { grid, itemLayout } = this.listContext;
|
||||||
const { prefixCls: customizePrefixCls, $slots } = this;
|
const { prefixCls: customizePrefixCls, $slots, $attrs } = this;
|
||||||
const listeners = getListeners(this);
|
const { class: _className } = $attrs;
|
||||||
const getPrefixCls = this.configProvider.getPrefixCls;
|
const getPrefixCls = this.configProvider.getPrefixCls;
|
||||||
const prefixCls = getPrefixCls('list', customizePrefixCls);
|
const prefixCls = getPrefixCls('list', customizePrefixCls);
|
||||||
const extra = getComponentFromProp(this, 'extra');
|
const extra = getComponent(this, 'extra');
|
||||||
const actions = getComponentFromProp(this, 'actions');
|
const actions = getComponent(this, 'actions');
|
||||||
|
|
||||||
const actionsContent = actions && actions.length > 0 && (
|
const actionsContent = actions && actions.length > 0 && (
|
||||||
<ul class={`${prefixCls}-item-action`} key="actions">
|
<ul class={`${prefixCls}-item-action`} key="actions">
|
||||||
|
@ -110,12 +107,12 @@ export default {
|
||||||
))}
|
))}
|
||||||
</ul>
|
</ul>
|
||||||
);
|
);
|
||||||
|
const children = $slots.default && $slots.default();
|
||||||
const Tag = grid ? 'div' : 'li';
|
const Tag = grid ? 'div' : 'li';
|
||||||
const itemChildren = (
|
const itemChildren = (
|
||||||
<Tag
|
<Tag
|
||||||
{...{ on: listeners }}
|
{...$attrs}
|
||||||
class={classNames(`${prefixCls}-item`, {
|
class={classNames(`${prefixCls}-item `, _className, {
|
||||||
[`${prefixCls}-item-no-flex`]: !this.isFlexMode(),
|
[`${prefixCls}-item-no-flex`]: !this.isFlexMode(),
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
|
@ -129,7 +126,7 @@ export default {
|
||||||
{extra}
|
{extra}
|
||||||
</div>,
|
</div>,
|
||||||
]
|
]
|
||||||
: [$slots.default, actionsContent, cloneElement(extra, { key: 'extra' })]}
|
: [children, actionsContent, cloneElement(extra, { key: 'extra' })]}
|
||||||
</Tag>
|
</Tag>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,10 @@
|
||||||
|
|
||||||
exports[`List renders empty list 1`] = `
|
exports[`List renders empty list 1`] = `
|
||||||
<div class="ant-list ant-list-split">
|
<div class="ant-list ant-list-split">
|
||||||
|
<!---->
|
||||||
|
<!---->
|
||||||
<div class="ant-spin-nested-loading">
|
<div class="ant-spin-nested-loading">
|
||||||
|
<!---->
|
||||||
<div class="ant-spin-container">
|
<div class="ant-spin-container">
|
||||||
<div class="ant-list-empty-text">
|
<div class="ant-list-empty-text">
|
||||||
<div class="ant-empty ant-empty-normal">
|
<div class="ant-empty ant-empty-normal">
|
||||||
|
@ -16,9 +19,12 @@ exports[`List renders empty list 1`] = `
|
||||||
</g>
|
</g>
|
||||||
</svg></div>
|
</svg></div>
|
||||||
<p class="ant-empty-description">No Data</p>
|
<p class="ant-empty-description">No Data</p>
|
||||||
|
<!---->
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<!---->
|
||||||
|
<!---->
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|
|
@ -8,14 +8,9 @@ import Pagination, { PaginationConfig } from '../pagination';
|
||||||
import { Row } from '../grid';
|
import { Row } from '../grid';
|
||||||
|
|
||||||
import Item from './Item';
|
import Item from './Item';
|
||||||
import {
|
import { initDefaultProps, getComponent, getSlot } from '../_util/props-util';
|
||||||
initDefaultProps,
|
|
||||||
getComponentFromProp,
|
|
||||||
filterEmpty,
|
|
||||||
getListeners,
|
|
||||||
} from '../_util/props-util';
|
|
||||||
import { cloneElement } from '../_util/vnode';
|
import { cloneElement } from '../_util/vnode';
|
||||||
import Base from '../base';
|
import { provide, inject } from 'vue';
|
||||||
|
|
||||||
export { ListItemProps, ListItemMetaProps } from './Item';
|
export { ListItemProps, ListItemMetaProps } from './Item';
|
||||||
|
|
||||||
|
@ -56,6 +51,7 @@ export const ListProps = () => ({
|
||||||
});
|
});
|
||||||
|
|
||||||
const List = {
|
const List = {
|
||||||
|
inheritAttrs: false,
|
||||||
Item,
|
Item,
|
||||||
name: 'AList',
|
name: 'AList',
|
||||||
props: initDefaultProps(ListProps(), {
|
props: initDefaultProps(ListProps(), {
|
||||||
|
@ -65,14 +61,15 @@ const List = {
|
||||||
loading: false,
|
loading: false,
|
||||||
pagination: false,
|
pagination: false,
|
||||||
}),
|
}),
|
||||||
provide() {
|
created() {
|
||||||
|
provide('listContext', this);
|
||||||
|
},
|
||||||
|
setup() {
|
||||||
return {
|
return {
|
||||||
listContext: this,
|
configProvider: inject('configProvider', ConfigConsumerProps),
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
inject: {
|
|
||||||
configProvider: { default: () => ConfigConsumerProps },
|
|
||||||
},
|
|
||||||
data() {
|
data() {
|
||||||
this.keys = [];
|
this.keys = [];
|
||||||
this.defaultPaginationProps = {
|
this.defaultPaginationProps = {
|
||||||
|
@ -107,10 +104,13 @@ const List = {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
renderItem2(item, index) {
|
innerRenderItem(item, index) {
|
||||||
const { $scopedSlots, rowKey } = this;
|
const {
|
||||||
const renderItem = this.renderItem || $scopedSlots.renderItem;
|
$slots: { renderItem },
|
||||||
if (!renderItem) return null;
|
rowKey,
|
||||||
|
} = this;
|
||||||
|
const renderer = this.renderItem || renderItem;
|
||||||
|
if (!renderer) return null;
|
||||||
let key;
|
let key;
|
||||||
if (typeof rowKey === 'function') {
|
if (typeof rowKey === 'function') {
|
||||||
key = rowKey(item);
|
key = rowKey(item);
|
||||||
|
@ -126,13 +126,13 @@ const List = {
|
||||||
|
|
||||||
this.keys[index] = key;
|
this.keys[index] = key;
|
||||||
|
|
||||||
return renderItem(item, index);
|
return renderer(item, index);
|
||||||
},
|
},
|
||||||
|
|
||||||
isSomethingAfterLastItem() {
|
isSomethingAfterLastItem() {
|
||||||
const { pagination } = this;
|
const { pagination } = this;
|
||||||
const loadMore = getComponentFromProp(this, 'loadMore');
|
const loadMore = getComponent(this, 'loadMore');
|
||||||
const footer = getComponentFromProp(this, 'footer');
|
const footer = getComponent(this, 'footer');
|
||||||
return !!(loadMore || pagination || footer);
|
return !!(loadMore || pagination || footer);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -140,7 +140,7 @@ const List = {
|
||||||
const { locale } = this;
|
const { locale } = this;
|
||||||
return (
|
return (
|
||||||
<div class={`${prefixCls}-empty-text`}>
|
<div class={`${prefixCls}-empty-text`}>
|
||||||
{(locale && locale.emptyText) || renderEmpty(h, 'List')}
|
{(locale && locale.emptyText) || renderEmpty('List')}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
@ -157,17 +157,17 @@ const List = {
|
||||||
dataSource = [],
|
dataSource = [],
|
||||||
size,
|
size,
|
||||||
loading,
|
loading,
|
||||||
$slots,
|
|
||||||
paginationCurrent,
|
paginationCurrent,
|
||||||
paginationSize,
|
paginationSize,
|
||||||
|
$attrs,
|
||||||
} = this;
|
} = this;
|
||||||
const getPrefixCls = this.configProvider.getPrefixCls;
|
const getPrefixCls = this.configProvider.getPrefixCls;
|
||||||
const prefixCls = getPrefixCls('list', customizePrefixCls);
|
const prefixCls = getPrefixCls('list', customizePrefixCls);
|
||||||
|
const { class: _cls, ...restAttrs } = $attrs;
|
||||||
const loadMore = getComponentFromProp(this, 'loadMore');
|
const loadMore = getComponent(this, 'loadMore');
|
||||||
const footer = getComponentFromProp(this, 'footer');
|
const footer = getComponent(this, 'footer');
|
||||||
const header = getComponentFromProp(this, 'header');
|
const header = getComponent(this, 'header');
|
||||||
const children = filterEmpty($slots.default || []);
|
const children = getSlot(this);
|
||||||
let loadingProp = loading;
|
let loadingProp = loading;
|
||||||
if (typeof loadingProp === 'boolean') {
|
if (typeof loadingProp === 'boolean') {
|
||||||
loadingProp = {
|
loadingProp = {
|
||||||
|
@ -189,15 +189,19 @@ const List = {
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
const classString = classNames(prefixCls, {
|
const classString = classNames(
|
||||||
[`${prefixCls}-vertical`]: itemLayout === 'vertical',
|
prefixCls,
|
||||||
[`${prefixCls}-${sizeCls}`]: sizeCls,
|
{
|
||||||
[`${prefixCls}-split`]: split,
|
[`${prefixCls}-vertical`]: itemLayout === 'vertical',
|
||||||
[`${prefixCls}-bordered`]: bordered,
|
[`${prefixCls}-${sizeCls}`]: sizeCls,
|
||||||
[`${prefixCls}-loading`]: isLoading,
|
[`${prefixCls}-split`]: split,
|
||||||
[`${prefixCls}-grid`]: grid,
|
[`${prefixCls}-bordered`]: bordered,
|
||||||
[`${prefixCls}-something-after-last-item`]: this.isSomethingAfterLastItem(),
|
[`${prefixCls}-loading`]: isLoading,
|
||||||
});
|
[`${prefixCls}-grid`]: grid,
|
||||||
|
[`${prefixCls}-something-after-last-item`]: this.isSomethingAfterLastItem(),
|
||||||
|
},
|
||||||
|
$attrs.class,
|
||||||
|
);
|
||||||
const paginationProps = {
|
const paginationProps = {
|
||||||
...this.defaultPaginationProps,
|
...this.defaultPaginationProps,
|
||||||
total: dataSource.length,
|
total: dataSource.length,
|
||||||
|
@ -205,6 +209,7 @@ const List = {
|
||||||
pageSize: paginationSize,
|
pageSize: paginationSize,
|
||||||
...(pagination || {}),
|
...(pagination || {}),
|
||||||
};
|
};
|
||||||
|
classString;
|
||||||
const largestPage = Math.ceil(paginationProps.total / paginationProps.pageSize);
|
const largestPage = Math.ceil(paginationProps.total / paginationProps.pageSize);
|
||||||
if (paginationProps.current > largestPage) {
|
if (paginationProps.current > largestPage) {
|
||||||
paginationProps.current = largestPage;
|
paginationProps.current = largestPage;
|
||||||
|
@ -239,7 +244,7 @@ const List = {
|
||||||
let childrenContent;
|
let childrenContent;
|
||||||
childrenContent = isLoading && <div style={{ minHeight: 53 }} />;
|
childrenContent = isLoading && <div style={{ minHeight: 53 }} />;
|
||||||
if (splitDataSource.length > 0) {
|
if (splitDataSource.length > 0) {
|
||||||
const items = splitDataSource.map((item, index) => this.renderItem2(item, index));
|
const items = splitDataSource.map((item, index) => this.innerRenderItem(item, index));
|
||||||
const childrenList = items.map((child, index) =>
|
const childrenList = items.map((child, index) =>
|
||||||
cloneElement(child, {
|
cloneElement(child, {
|
||||||
key: this.keys[index],
|
key: this.keys[index],
|
||||||
|
@ -258,10 +263,10 @@ const List = {
|
||||||
const paginationPosition = paginationProps.position || 'bottom';
|
const paginationPosition = paginationProps.position || 'bottom';
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div class={classString} {...{ on: getListeners(this) }}>
|
<div class={classString} {...restAttrs}>
|
||||||
{(paginationPosition === 'top' || paginationPosition === 'both') && paginationContent}
|
{(paginationPosition === 'top' || paginationPosition === 'both') && paginationContent}
|
||||||
{header && <div class={`${prefixCls}-header`}>{header}</div>}
|
{header && <div class={`${prefixCls}-header`}>{header}</div>}
|
||||||
<Spin {...{ props: loadingProp }}>
|
<Spin {...loadingProp}>
|
||||||
{childrenContent}
|
{childrenContent}
|
||||||
{children}
|
{children}
|
||||||
</Spin>
|
</Spin>
|
||||||
|
@ -274,11 +279,10 @@ const List = {
|
||||||
};
|
};
|
||||||
|
|
||||||
/* istanbul ignore next */
|
/* istanbul ignore next */
|
||||||
List.install = function(Vue) {
|
List.install = function(app) {
|
||||||
Vue.use(Base);
|
app.component(List.name, List);
|
||||||
Vue.component(List.name, List);
|
app.component(List.Item.name, List.Item);
|
||||||
Vue.component(List.Item.name, List.Item);
|
app.component(List.Item.Meta.name, List.Item.Meta);
|
||||||
Vue.component(List.Item.Meta.name, List.Item.Meta);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default List;
|
export default List;
|
||||||
|
|
Loading…
Reference in New Issue