Skip to content

feat(list): update to vue3 #2513

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Jul 15, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
97 changes: 47 additions & 50 deletions components/list/Item.jsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,11 @@
import PropTypes from '../_util/vue-types';
import classNames from 'classnames';
import {
getComponentFromProp,
isStringElement,
getListeners,
isEmptyElement,
} from '../_util/props-util';
import { getComponent, isStringElement, isEmptyElement, getSlot } from '../_util/props-util';
import { Col } from '../grid';
import { ConfigConsumerProps } from '../config-provider';
import { ListGridType } from './index';
import { cloneElement } from '../_util/vnode';
import { inject } from 'vue';

export const ListItemProps = {
prefixCls: PropTypes.string,
Expand All @@ -25,55 +21,56 @@ export const ListItemMetaProps = {
title: PropTypes.any,
};

export const Meta = {
functional: true,
name: 'AListItemMeta',
__ANT_LIST_ITEM_META: true,
inject: {
configProvider: { default: () => ConfigConsumerProps },
},
render(h, context) {
const { props, slots, listeners, injections } = context;
const slotsMap = slots();
const getPrefixCls = injections.configProvider.getPrefixCls;
const { prefixCls: customizePrefixCls } = props;
const prefixCls = getPrefixCls('list', customizePrefixCls);

const avatar = props.avatar || slotsMap.avatar;
const title = props.title || slotsMap.title;
const description = props.description || slotsMap.description;
const content = (
<div class={`${prefixCls}-item-meta-content`}>
{title && <h4 class={`${prefixCls}-item-meta-title`}>{title}</h4>}
{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>
);
},
export const Meta = (props, { slots, attrs }) => {
const configProvider = inject('configProvider', ConfigConsumerProps);
const { style, class: _cls } = attrs;
const getPrefixCls = configProvider.getPrefixCls;
const { prefixCls: customizePrefixCls } = props;
const prefixCls = getPrefixCls('list', customizePrefixCls);
const avatar = props.avatar || slots.avatar?.();
const title = props.title || slots.title?.();
const description = props.description || slots.description?.();
const content = (
<div class={`${prefixCls}-item-meta-content`}>
{title && <h4 class={`${prefixCls}-item-meta-title`}>{title}</h4>}
{description && <div class={`${prefixCls}-item-meta-description`}>{description}</div>}
</div>
);
return (
<div class={`${prefixCls}-item-meta`} style={style} class={_cls}>
{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) {
return grid[t] && Math.floor(24 / grid[t]);
}

export default {
name: 'AListItem',
inheritAttrs: false,
Meta,
props: ListItemProps,
inject: {
listContext: { default: () => ({}) },
configProvider: { default: () => ConfigConsumerProps },
setup() {
const listContext = inject('listContext', {});
const configProvider = inject('configProvider', ConfigConsumerProps);
return {
listContext,
configProvider,
};
},
methods: {
isItemContainsTextNodeAndNotSingular() {
const { $slots } = this;
const children = getSlot(this) || [];
let result;
const children = $slots.default || [];
children.forEach(element => {
if (isStringElement(element) && !isEmptyElement(element)) {
result = true;
Expand All @@ -83,7 +80,7 @@ export default {
},

isFlexMode() {
const extra = getComponentFromProp(this, 'extra');
const extra = getComponent(this, 'extra');
const { itemLayout } = this.listContext;
if (itemLayout === 'vertical') {
return !!extra;
Expand All @@ -93,12 +90,12 @@ export default {
},
render() {
const { grid, itemLayout } = this.listContext;
const { prefixCls: customizePrefixCls, $slots } = this;
const listeners = getListeners(this);
const { prefixCls: customizePrefixCls, $slots, $attrs } = this;
const { class: _className } = $attrs;
const getPrefixCls = this.configProvider.getPrefixCls;
const prefixCls = getPrefixCls('list', customizePrefixCls);
const extra = getComponentFromProp(this, 'extra');
const actions = getComponentFromProp(this, 'actions');
const extra = getComponent(this, 'extra');
const actions = getComponent(this, 'actions');

const actionsContent = actions && actions.length > 0 && (
<ul class={`${prefixCls}-item-action`} key="actions">
Expand All @@ -110,12 +107,12 @@ export default {
))}
</ul>
);

const children = $slots.default && $slots.default();
const Tag = grid ? 'div' : 'li';
const itemChildren = (
<Tag
{...{ on: listeners }}
class={classNames(`${prefixCls}-item`, {
{...$attrs}
class={classNames(`${prefixCls}-item `, _className, {
[`${prefixCls}-item-no-flex`]: !this.isFlexMode(),
})}
>
Expand All @@ -129,7 +126,7 @@ export default {
{extra}
</div>,
]
: [$slots.default, actionsContent, cloneElement(extra, { key: 'extra' })]}
: [children, actionsContent, cloneElement(extra, { key: 'extra' })]}
</Tag>
);

Expand Down
6 changes: 6 additions & 0 deletions components/list/__tests__/__snapshots__/empty.test.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@

exports[`List renders empty list 1`] = `
<div class="ant-list ant-list-split">
<!---->
<!---->
<div class="ant-spin-nested-loading">
<!---->
<div class="ant-spin-container">
<div class="ant-list-empty-text">
<div class="ant-empty ant-empty-normal">
Expand All @@ -16,9 +19,12 @@ exports[`List renders empty list 1`] = `
</g>
</svg></div>
<p class="ant-empty-description">No Data</p>
<!---->
</div>
</div>
</div>
</div>
<!---->
<!---->
</div>
`;
90 changes: 47 additions & 43 deletions components/list/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,9 @@ import Pagination, { PaginationConfig } from '../pagination';
import { Row } from '../grid';

import Item from './Item';
import {
initDefaultProps,
getComponentFromProp,
filterEmpty,
getListeners,
} from '../_util/props-util';
import { initDefaultProps, getComponent, getSlot } from '../_util/props-util';
import { cloneElement } from '../_util/vnode';
import Base from '../base';
import { provide, inject } from 'vue';

export { ListItemProps, ListItemMetaProps } from './Item';

Expand Down Expand Up @@ -56,6 +51,7 @@ export const ListProps = () => ({
});

const List = {
inheritAttrs: false,
Item,
name: 'AList',
props: initDefaultProps(ListProps(), {
Expand All @@ -65,14 +61,15 @@ const List = {
loading: false,
pagination: false,
}),
provide() {
created() {
provide('listContext', this);
},
setup() {
return {
listContext: this,
configProvider: inject('configProvider', ConfigConsumerProps),
};
},
inject: {
configProvider: { default: () => ConfigConsumerProps },
},

data() {
this.keys = [];
this.defaultPaginationProps = {
Expand Down Expand Up @@ -107,10 +104,13 @@ const List = {
}
};
},
renderItem2(item, index) {
const { $scopedSlots, rowKey } = this;
const renderItem = this.renderItem || $scopedSlots.renderItem;
if (!renderItem) return null;
innerRenderItem(item, index) {
const {
$slots: { renderItem },
rowKey,
} = this;
const renderer = this.renderItem || renderItem;
if (!renderer) return null;
let key;
if (typeof rowKey === 'function') {
key = rowKey(item);
Expand All @@ -126,21 +126,21 @@ const List = {

this.keys[index] = key;

return renderItem(item, index);
return renderer(item, index);
},

isSomethingAfterLastItem() {
const { pagination } = this;
const loadMore = getComponentFromProp(this, 'loadMore');
const footer = getComponentFromProp(this, 'footer');
const loadMore = getComponent(this, 'loadMore');
const footer = getComponent(this, 'footer');
return !!(loadMore || pagination || footer);
},

renderEmpty(prefixCls, renderEmpty) {
const { locale } = this;
return (
<div class={`${prefixCls}-empty-text`}>
{(locale && locale.emptyText) || renderEmpty(h, 'List')}
{(locale && locale.emptyText) || renderEmpty('List')}
</div>
);
},
Expand All @@ -157,17 +157,17 @@ const List = {
dataSource = [],
size,
loading,
$slots,
paginationCurrent,
paginationSize,
$attrs,
} = this;
const getPrefixCls = this.configProvider.getPrefixCls;
const prefixCls = getPrefixCls('list', customizePrefixCls);

const loadMore = getComponentFromProp(this, 'loadMore');
const footer = getComponentFromProp(this, 'footer');
const header = getComponentFromProp(this, 'header');
const children = filterEmpty($slots.default || []);
const { class: _cls, ...restAttrs } = $attrs;
const loadMore = getComponent(this, 'loadMore');
const footer = getComponent(this, 'footer');
const header = getComponent(this, 'header');
const children = getSlot(this);
let loadingProp = loading;
if (typeof loadingProp === 'boolean') {
loadingProp = {
Expand All @@ -189,22 +189,27 @@ const List = {
default:
break;
}
const classString = classNames(prefixCls, {
[`${prefixCls}-vertical`]: itemLayout === 'vertical',
[`${prefixCls}-${sizeCls}`]: sizeCls,
[`${prefixCls}-split`]: split,
[`${prefixCls}-bordered`]: bordered,
[`${prefixCls}-loading`]: isLoading,
[`${prefixCls}-grid`]: grid,
[`${prefixCls}-something-after-last-item`]: this.isSomethingAfterLastItem(),
});
const classString = classNames(
prefixCls,
{
[`${prefixCls}-vertical`]: itemLayout === 'vertical',
[`${prefixCls}-${sizeCls}`]: sizeCls,
[`${prefixCls}-split`]: split,
[`${prefixCls}-bordered`]: bordered,
[`${prefixCls}-loading`]: isLoading,
[`${prefixCls}-grid`]: grid,
[`${prefixCls}-something-after-last-item`]: this.isSomethingAfterLastItem(),
},
$attrs.class,
);
const paginationProps = {
...this.defaultPaginationProps,
total: dataSource.length,
current: paginationCurrent,
pageSize: paginationSize,
...(pagination || {}),
};
classString;
const largestPage = Math.ceil(paginationProps.total / paginationProps.pageSize);
if (paginationProps.current > largestPage) {
paginationProps.current = largestPage;
Expand Down Expand Up @@ -239,7 +244,7 @@ const List = {
let childrenContent;
childrenContent = isLoading && <div style={{ minHeight: 53 }} />;
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) =>
cloneElement(child, {
key: this.keys[index],
Expand All @@ -258,10 +263,10 @@ const List = {
const paginationPosition = paginationProps.position || 'bottom';

return (
<div class={classString} {...{ on: getListeners(this) }}>
<div class={classString} {...restAttrs}>
{(paginationPosition === 'top' || paginationPosition === 'both') && paginationContent}
{header && <div class={`${prefixCls}-header`}>{header}</div>}
<Spin {...{ props: loadingProp }}>
<Spin {...loadingProp}>
{childrenContent}
{children}
</Spin>
Expand All @@ -274,11 +279,10 @@ const List = {
};

/* istanbul ignore next */
List.install = function(Vue) {
Vue.use(Base);
Vue.component(List.name, List);
Vue.component(List.Item.name, List.Item);
Vue.component(List.Item.Meta.name, List.Item.Meta);
List.install = function(app) {
app.component(List.name, List);
app.component(List.Item.name, List.Item);
app.component(List.Item.Meta.name, List.Item.Meta);
};

export default List;