Skip to content

Commit 8059966

Browse files
author
逆寒
authored
feat(list): update to vue3 (#2513)
* feat(list): update to vue3 * feat(list): refactor meta to functional * feat(list): code review
1 parent 6c77afe commit 8059966

File tree

3 files changed

+100
-93
lines changed

3 files changed

+100
-93
lines changed

components/list/Item.jsx

+47-50
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,11 @@
11
import PropTypes from '../_util/vue-types';
22
import classNames from 'classnames';
3-
import {
4-
getComponentFromProp,
5-
isStringElement,
6-
getListeners,
7-
isEmptyElement,
8-
} from '../_util/props-util';
3+
import { getComponent, isStringElement, isEmptyElement, getSlot } from '../_util/props-util';
94
import { Col } from '../grid';
105
import { ConfigConsumerProps } from '../config-provider';
116
import { ListGridType } from './index';
127
import { cloneElement } from '../_util/vnode';
8+
import { inject } from 'vue';
139

1410
export const ListItemProps = {
1511
prefixCls: PropTypes.string,
@@ -25,55 +21,56 @@ export const ListItemMetaProps = {
2521
title: PropTypes.any,
2622
};
2723

28-
export const Meta = {
29-
functional: true,
30-
name: 'AListItemMeta',
31-
__ANT_LIST_ITEM_META: true,
32-
inject: {
33-
configProvider: { default: () => ConfigConsumerProps },
34-
},
35-
render(h, context) {
36-
const { props, slots, listeners, injections } = context;
37-
const slotsMap = slots();
38-
const getPrefixCls = injections.configProvider.getPrefixCls;
39-
const { prefixCls: customizePrefixCls } = props;
40-
const prefixCls = getPrefixCls('list', customizePrefixCls);
41-
42-
const avatar = props.avatar || slotsMap.avatar;
43-
const title = props.title || slotsMap.title;
44-
const description = props.description || slotsMap.description;
45-
const content = (
46-
<div class={`${prefixCls}-item-meta-content`}>
47-
{title && <h4 class={`${prefixCls}-item-meta-title`}>{title}</h4>}
48-
{description && <div class={`${prefixCls}-item-meta-description`}>{description}</div>}
49-
</div>
50-
);
51-
return (
52-
<div {...{ on: listeners }} class={`${prefixCls}-item-meta`}>
53-
{avatar && <div class={`${prefixCls}-item-meta-avatar`}>{avatar}</div>}
54-
{(title || description) && content}
55-
</div>
56-
);
57-
},
24+
export const Meta = (props, { slots, attrs }) => {
25+
const configProvider = inject('configProvider', ConfigConsumerProps);
26+
const { style, class: _cls } = attrs;
27+
const getPrefixCls = configProvider.getPrefixCls;
28+
const { prefixCls: customizePrefixCls } = props;
29+
const prefixCls = getPrefixCls('list', customizePrefixCls);
30+
const avatar = props.avatar || slots.avatar?.();
31+
const title = props.title || slots.title?.();
32+
const description = props.description || slots.description?.();
33+
const content = (
34+
<div class={`${prefixCls}-item-meta-content`}>
35+
{title && <h4 class={`${prefixCls}-item-meta-title`}>{title}</h4>}
36+
{description && <div class={`${prefixCls}-item-meta-description`}>{description}</div>}
37+
</div>
38+
);
39+
return (
40+
<div class={`${prefixCls}-item-meta`} style={style} class={_cls}>
41+
{avatar && <div class={`${prefixCls}-item-meta-avatar`}>{avatar}</div>}
42+
{(title || description) && content}
43+
</div>
44+
);
5845
};
5946

47+
Object.assign(Meta, {
48+
props: ListItemMetaProps,
49+
inheritAttrs: false,
50+
__ANT_LIST_ITEM_META: true,
51+
});
52+
6053
function getGrid(grid, t) {
6154
return grid[t] && Math.floor(24 / grid[t]);
6255
}
6356

6457
export default {
6558
name: 'AListItem',
59+
inheritAttrs: false,
6660
Meta,
6761
props: ListItemProps,
68-
inject: {
69-
listContext: { default: () => ({}) },
70-
configProvider: { default: () => ConfigConsumerProps },
62+
setup() {
63+
const listContext = inject('listContext', {});
64+
const configProvider = inject('configProvider', ConfigConsumerProps);
65+
return {
66+
listContext,
67+
configProvider,
68+
};
7169
},
7270
methods: {
7371
isItemContainsTextNodeAndNotSingular() {
74-
const { $slots } = this;
72+
const children = getSlot(this) || [];
7573
let result;
76-
const children = $slots.default || [];
7774
children.forEach(element => {
7875
if (isStringElement(element) && !isEmptyElement(element)) {
7976
result = true;
@@ -83,7 +80,7 @@ export default {
8380
},
8481

8582
isFlexMode() {
86-
const extra = getComponentFromProp(this, 'extra');
83+
const extra = getComponent(this, 'extra');
8784
const { itemLayout } = this.listContext;
8885
if (itemLayout === 'vertical') {
8986
return !!extra;
@@ -93,12 +90,12 @@ export default {
9390
},
9491
render() {
9592
const { grid, itemLayout } = this.listContext;
96-
const { prefixCls: customizePrefixCls, $slots } = this;
97-
const listeners = getListeners(this);
93+
const { prefixCls: customizePrefixCls, $slots, $attrs } = this;
94+
const { class: _className } = $attrs;
9895
const getPrefixCls = this.configProvider.getPrefixCls;
9996
const prefixCls = getPrefixCls('list', customizePrefixCls);
100-
const extra = getComponentFromProp(this, 'extra');
101-
const actions = getComponentFromProp(this, 'actions');
97+
const extra = getComponent(this, 'extra');
98+
const actions = getComponent(this, 'actions');
10299

103100
const actionsContent = actions && actions.length > 0 && (
104101
<ul class={`${prefixCls}-item-action`} key="actions">
@@ -110,12 +107,12 @@ export default {
110107
))}
111108
</ul>
112109
);
113-
110+
const children = $slots.default && $slots.default();
114111
const Tag = grid ? 'div' : 'li';
115112
const itemChildren = (
116113
<Tag
117-
{...{ on: listeners }}
118-
class={classNames(`${prefixCls}-item`, {
114+
{...$attrs}
115+
class={classNames(`${prefixCls}-item `, _className, {
119116
[`${prefixCls}-item-no-flex`]: !this.isFlexMode(),
120117
})}
121118
>
@@ -129,7 +126,7 @@ export default {
129126
{extra}
130127
</div>,
131128
]
132-
: [$slots.default, actionsContent, cloneElement(extra, { key: 'extra' })]}
129+
: [children, actionsContent, cloneElement(extra, { key: 'extra' })]}
133130
</Tag>
134131
);
135132

components/list/__tests__/__snapshots__/empty.test.js.snap

+6
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,10 @@
22

33
exports[`List renders empty list 1`] = `
44
<div class="ant-list ant-list-split">
5+
<!---->
6+
<!---->
57
<div class="ant-spin-nested-loading">
8+
<!---->
69
<div class="ant-spin-container">
710
<div class="ant-list-empty-text">
811
<div class="ant-empty ant-empty-normal">
@@ -16,9 +19,12 @@ exports[`List renders empty list 1`] = `
1619
</g>
1720
</svg></div>
1821
<p class="ant-empty-description">No Data</p>
22+
<!---->
1923
</div>
2024
</div>
2125
</div>
2226
</div>
27+
<!---->
28+
<!---->
2329
</div>
2430
`;

components/list/index.jsx

+47-43
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,9 @@ import Pagination, { PaginationConfig } from '../pagination';
88
import { Row } from '../grid';
99

1010
import Item from './Item';
11-
import {
12-
initDefaultProps,
13-
getComponentFromProp,
14-
filterEmpty,
15-
getListeners,
16-
} from '../_util/props-util';
11+
import { initDefaultProps, getComponent, getSlot } from '../_util/props-util';
1712
import { cloneElement } from '../_util/vnode';
18-
import Base from '../base';
13+
import { provide, inject } from 'vue';
1914

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

@@ -56,6 +51,7 @@ export const ListProps = () => ({
5651
});
5752

5853
const List = {
54+
inheritAttrs: false,
5955
Item,
6056
name: 'AList',
6157
props: initDefaultProps(ListProps(), {
@@ -65,14 +61,15 @@ const List = {
6561
loading: false,
6662
pagination: false,
6763
}),
68-
provide() {
64+
created() {
65+
provide('listContext', this);
66+
},
67+
setup() {
6968
return {
70-
listContext: this,
69+
configProvider: inject('configProvider', ConfigConsumerProps),
7170
};
7271
},
73-
inject: {
74-
configProvider: { default: () => ConfigConsumerProps },
75-
},
72+
7673
data() {
7774
this.keys = [];
7875
this.defaultPaginationProps = {
@@ -107,10 +104,13 @@ const List = {
107104
}
108105
};
109106
},
110-
renderItem2(item, index) {
111-
const { $scopedSlots, rowKey } = this;
112-
const renderItem = this.renderItem || $scopedSlots.renderItem;
113-
if (!renderItem) return null;
107+
innerRenderItem(item, index) {
108+
const {
109+
$slots: { renderItem },
110+
rowKey,
111+
} = this;
112+
const renderer = this.renderItem || renderItem;
113+
if (!renderer) return null;
114114
let key;
115115
if (typeof rowKey === 'function') {
116116
key = rowKey(item);
@@ -126,21 +126,21 @@ const List = {
126126

127127
this.keys[index] = key;
128128

129-
return renderItem(item, index);
129+
return renderer(item, index);
130130
},
131131

132132
isSomethingAfterLastItem() {
133133
const { pagination } = this;
134-
const loadMore = getComponentFromProp(this, 'loadMore');
135-
const footer = getComponentFromProp(this, 'footer');
134+
const loadMore = getComponent(this, 'loadMore');
135+
const footer = getComponent(this, 'footer');
136136
return !!(loadMore || pagination || footer);
137137
},
138138

139139
renderEmpty(prefixCls, renderEmpty) {
140140
const { locale } = this;
141141
return (
142142
<div class={`${prefixCls}-empty-text`}>
143-
{(locale && locale.emptyText) || renderEmpty(h, 'List')}
143+
{(locale && locale.emptyText) || renderEmpty('List')}
144144
</div>
145145
);
146146
},
@@ -157,17 +157,17 @@ const List = {
157157
dataSource = [],
158158
size,
159159
loading,
160-
$slots,
161160
paginationCurrent,
162161
paginationSize,
162+
$attrs,
163163
} = this;
164164
const getPrefixCls = this.configProvider.getPrefixCls;
165165
const prefixCls = getPrefixCls('list', customizePrefixCls);
166-
167-
const loadMore = getComponentFromProp(this, 'loadMore');
168-
const footer = getComponentFromProp(this, 'footer');
169-
const header = getComponentFromProp(this, 'header');
170-
const children = filterEmpty($slots.default || []);
166+
const { class: _cls, ...restAttrs } = $attrs;
167+
const loadMore = getComponent(this, 'loadMore');
168+
const footer = getComponent(this, 'footer');
169+
const header = getComponent(this, 'header');
170+
const children = getSlot(this);
171171
let loadingProp = loading;
172172
if (typeof loadingProp === 'boolean') {
173173
loadingProp = {
@@ -189,22 +189,27 @@ const List = {
189189
default:
190190
break;
191191
}
192-
const classString = classNames(prefixCls, {
193-
[`${prefixCls}-vertical`]: itemLayout === 'vertical',
194-
[`${prefixCls}-${sizeCls}`]: sizeCls,
195-
[`${prefixCls}-split`]: split,
196-
[`${prefixCls}-bordered`]: bordered,
197-
[`${prefixCls}-loading`]: isLoading,
198-
[`${prefixCls}-grid`]: grid,
199-
[`${prefixCls}-something-after-last-item`]: this.isSomethingAfterLastItem(),
200-
});
192+
const classString = classNames(
193+
prefixCls,
194+
{
195+
[`${prefixCls}-vertical`]: itemLayout === 'vertical',
196+
[`${prefixCls}-${sizeCls}`]: sizeCls,
197+
[`${prefixCls}-split`]: split,
198+
[`${prefixCls}-bordered`]: bordered,
199+
[`${prefixCls}-loading`]: isLoading,
200+
[`${prefixCls}-grid`]: grid,
201+
[`${prefixCls}-something-after-last-item`]: this.isSomethingAfterLastItem(),
202+
},
203+
$attrs.class,
204+
);
201205
const paginationProps = {
202206
...this.defaultPaginationProps,
203207
total: dataSource.length,
204208
current: paginationCurrent,
205209
pageSize: paginationSize,
206210
...(pagination || {}),
207211
};
212+
classString;
208213
const largestPage = Math.ceil(paginationProps.total / paginationProps.pageSize);
209214
if (paginationProps.current > largestPage) {
210215
paginationProps.current = largestPage;
@@ -239,7 +244,7 @@ const List = {
239244
let childrenContent;
240245
childrenContent = isLoading && <div style={{ minHeight: 53 }} />;
241246
if (splitDataSource.length > 0) {
242-
const items = splitDataSource.map((item, index) => this.renderItem2(item, index));
247+
const items = splitDataSource.map((item, index) => this.innerRenderItem(item, index));
243248
const childrenList = items.map((child, index) =>
244249
cloneElement(child, {
245250
key: this.keys[index],
@@ -258,10 +263,10 @@ const List = {
258263
const paginationPosition = paginationProps.position || 'bottom';
259264

260265
return (
261-
<div class={classString} {...{ on: getListeners(this) }}>
266+
<div class={classString} {...restAttrs}>
262267
{(paginationPosition === 'top' || paginationPosition === 'both') && paginationContent}
263268
{header && <div class={`${prefixCls}-header`}>{header}</div>}
264-
<Spin {...{ props: loadingProp }}>
269+
<Spin {...loadingProp}>
265270
{childrenContent}
266271
{children}
267272
</Spin>
@@ -274,11 +279,10 @@ const List = {
274279
};
275280

276281
/* istanbul ignore next */
277-
List.install = function(Vue) {
278-
Vue.use(Base);
279-
Vue.component(List.name, List);
280-
Vue.component(List.Item.name, List.Item);
281-
Vue.component(List.Item.Meta.name, List.Item.Meta);
282+
List.install = function(app) {
283+
app.component(List.name, List);
284+
app.component(List.Item.name, List.Item);
285+
app.component(List.Item.Meta.name, List.Item.Meta);
282286
};
283287

284288
export default List;

0 commit comments

Comments
 (0)