Skip to content

Commit 22141e7

Browse files
authored
refactor: list (#4242)
1 parent 31ed5c6 commit 22141e7

File tree

12 files changed

+589
-397
lines changed

12 files changed

+589
-397
lines changed

components/_util/hooks/useConfigInject.ts

+4-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { RequiredMark } from '../../form/Form';
2-
import { computed, ComputedRef, inject, UnwrapRef } from 'vue';
2+
import { computed, ComputedRef, inject, UnwrapRef, VNodeChild } from 'vue';
33
import {
44
ConfigProviderProps,
55
defaultConfigProvider,
@@ -21,13 +21,15 @@ export default (
2121
form?: ComputedRef<{
2222
requiredMark?: RequiredMark;
2323
}>;
24+
renderEmpty?: ComputedRef<(componentName?: string) => VNodeChild | JSX.Element>;
2425
} => {
2526
const configProvider = inject<UnwrapRef<ConfigProviderProps>>(
2627
'configProvider',
2728
defaultConfigProvider,
2829
);
2930
const prefixCls = computed(() => configProvider.getPrefixCls(name, props.prefixCls));
3031
const direction = computed(() => configProvider.direction);
32+
const renderEmpty = computed(() => configProvider.renderEmpty);
3133
const space = computed(() => configProvider.space);
3234
const pageHeader = computed(() => configProvider.pageHeader);
3335
const form = computed(() => configProvider.form);
@@ -42,5 +44,6 @@ export default (
4244
space,
4345
pageHeader,
4446
form,
47+
renderEmpty,
4548
};
4649
};

components/list/Item.tsx

+73-127
Original file line numberDiff line numberDiff line change
@@ -1,156 +1,102 @@
11
import PropTypes from '../_util/vue-types';
22
import classNames from '../_util/classNames';
3-
import { getComponent, isStringElement, isEmptyElement, getSlot } from '../_util/props-util';
3+
import { isStringElement, isEmptyElement, flattenChildren } from '../_util/props-util';
44
import { Col } from '../grid';
5-
import { defaultConfigProvider } from '../config-provider';
65
import { cloneElement } from '../_util/vnode';
7-
import { defineComponent, ExtractPropTypes, FunctionalComponent, inject } from 'vue';
6+
import { defineComponent, inject, ref } from 'vue';
7+
import ItemMeta from './ItemMeta';
8+
import useConfigInject from '../_util/hooks/useConfigInject';
9+
import { ListContextKey } from '.';
810

911
export const ListItemProps = {
1012
prefixCls: PropTypes.string,
1113
extra: PropTypes.any,
1214
actions: PropTypes.array,
1315
grid: PropTypes.any,
16+
colStyle: PropTypes.style,
1417
};
1518

16-
export const ListItemMetaProps = {
17-
avatar: PropTypes.any,
18-
description: PropTypes.any,
19-
prefixCls: PropTypes.string,
20-
title: PropTypes.any,
21-
};
22-
23-
export const ListItemMeta: FunctionalComponent<Partial<
24-
ExtractPropTypes<typeof ListItemMetaProps>
25-
>> = (props, { slots }) => {
26-
const configProvider = inject('configProvider', defaultConfigProvider);
27-
const { getPrefixCls } = configProvider;
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`}>
41-
{avatar && <div class={`${prefixCls}-item-meta-avatar`}>{avatar}</div>}
42-
{(title || description) && content}
43-
</div>
44-
);
45-
};
46-
47-
Object.assign(ListItemMeta, {
48-
props: ListItemMetaProps,
49-
__ANT_LIST_ITEM_META: true,
50-
displayName: 'AListItemMeta',
51-
});
52-
53-
function getGrid(grid, t) {
54-
return grid[t] && Math.floor(24 / grid[t]);
55-
}
56-
57-
export interface ListContext {
58-
grid?: any;
59-
itemLayout?: string;
60-
}
61-
6219
export default defineComponent({
6320
name: 'AListItem',
6421
inheritAttrs: false,
65-
Meta: ListItemMeta,
22+
Meta: ItemMeta,
6623
props: ListItemProps,
67-
setup() {
68-
const listContext = inject<ListContext>('listContext', {});
69-
const configProvider = inject('configProvider', defaultConfigProvider);
70-
return {
71-
listContext,
72-
configProvider,
73-
};
74-
},
75-
methods: {
76-
isItemContainsTextNodeAndNotSingular() {
77-
const children = getSlot(this) || [];
24+
slots: ['actions', 'extra'],
25+
setup(props, { slots, attrs }) {
26+
const { itemLayout, grid } = inject(ListContextKey, {
27+
grid: ref(),
28+
itemLayout: ref(),
29+
});
30+
const { prefixCls } = useConfigInject('list', props);
31+
32+
const isItemContainsTextNodeAndNotSingular = () => {
33+
const children = slots.default?.() || [];
7834
let result;
7935
children.forEach(element => {
8036
if (isStringElement(element) && !isEmptyElement(element)) {
8137
result = true;
8238
}
8339
});
8440
return result && children.length > 1;
85-
},
41+
};
8642

87-
isFlexMode() {
88-
const extra = getComponent(this, 'extra');
89-
const { itemLayout } = this.listContext;
90-
if (itemLayout === 'vertical') {
43+
const isFlexMode = () => {
44+
const extra = props.extra ?? slots.extra?.();
45+
if (itemLayout.value === 'vertical') {
9146
return !!extra;
9247
}
93-
return !this.isItemContainsTextNodeAndNotSingular();
94-
},
95-
},
96-
render() {
97-
const { grid, itemLayout } = this.listContext;
98-
const { prefixCls: customizePrefixCls, $attrs } = this;
99-
const { class: _className, ...restAttrs } = $attrs;
100-
const getPrefixCls = this.configProvider.getPrefixCls;
101-
const prefixCls = getPrefixCls('list', customizePrefixCls);
102-
const extra = getComponent(this, 'extra');
103-
let actions = getComponent(this, 'actions');
104-
actions = actions && !Array.isArray(actions) ? [actions] : actions;
105-
const actionsContent = actions && actions.length > 0 && (
106-
<ul class={`${prefixCls}-item-action`} key="actions">
107-
{actions.map((action, i) => (
108-
<li key={`${prefixCls}-item-action-${i}`}>
109-
{action}
110-
{i !== actions.length - 1 && <em class={`${prefixCls}-item-action-split`} />}
111-
</li>
112-
))}
113-
</ul>
114-
);
115-
const children = getSlot(this);
116-
const Tag = grid ? 'div' : 'li';
117-
const itemChildren = (
118-
<Tag
119-
{...restAttrs}
120-
class={classNames(`${prefixCls}-item`, _className, {
121-
[`${prefixCls}-item-no-flex`]: !this.isFlexMode(),
122-
})}
123-
>
124-
{itemLayout === 'vertical' && extra
125-
? [
126-
<div class={`${prefixCls}-item-main`} key="content">
127-
{children}
128-
{actionsContent}
129-
</div>,
130-
<div class={`${prefixCls}-item-extra`} key="extra">
131-
{extra}
132-
</div>,
133-
]
134-
: [children, actionsContent, cloneElement(extra, { key: 'extra' })]}
135-
</Tag>
136-
);
137-
138-
const mainContent = grid ? (
139-
<Col
140-
span={getGrid(grid, 'column')}
141-
xs={getGrid(grid, 'xs')}
142-
sm={getGrid(grid, 'sm')}
143-
md={getGrid(grid, 'md')}
144-
lg={getGrid(grid, 'lg')}
145-
xl={getGrid(grid, 'xl')}
146-
xxl={getGrid(grid, 'xxl')}
147-
>
148-
{itemChildren}
149-
</Col>
150-
) : (
151-
itemChildren
152-
);
48+
return !isItemContainsTextNodeAndNotSingular();
49+
};
15350

154-
return mainContent;
51+
return () => {
52+
const { class: className, ...restAttrs } = attrs;
53+
const pre = prefixCls.value;
54+
const extra = props.extra ?? slots.extra?.();
55+
const children = slots.default?.();
56+
let actions = props.actions ?? flattenChildren(slots.actions?.());
57+
actions = actions && !Array.isArray(actions) ? [actions] : actions;
58+
const actionsContent = actions && actions.length > 0 && (
59+
<ul class={`${pre}-item-action`} key="actions">
60+
{actions.map((action, i) => (
61+
<li key={`${pre}-item-action-${i}`}>
62+
{action}
63+
{i !== actions.length - 1 && <em class={`${pre}-item-action-split`} />}
64+
</li>
65+
))}
66+
</ul>
67+
);
68+
const Element = grid.value ? 'div' : 'li';
69+
const itemChildren = (
70+
<Element
71+
{...(restAttrs as any)} // `li` element `onCopy` prop args is not same as `div`
72+
class={classNames(
73+
`${pre}-item`,
74+
{
75+
[`${pre}-item-no-flex`]: !isFlexMode(),
76+
},
77+
className,
78+
)}
79+
>
80+
{itemLayout.value === 'vertical' && extra
81+
? [
82+
<div class={`${pre}-item-main`} key="content">
83+
{children}
84+
{actionsContent}
85+
</div>,
86+
<div class={`${pre}-item-extra`} key="extra">
87+
{extra}
88+
</div>,
89+
]
90+
: [children, actionsContent, cloneElement(extra, { key: 'extra' })]}
91+
</Element>
92+
);
93+
return grid.value ? (
94+
<Col flex={1} style={props.colStyle}>
95+
{itemChildren}
96+
</Col>
97+
) : (
98+
itemChildren
99+
);
100+
};
155101
},
156102
});

components/list/ItemMeta.tsx

+43
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import { defineComponent, ExtractPropTypes } from 'vue';
2+
import useConfigInject from '../_util/hooks/useConfigInject';
3+
import PropTypes from '../_util/vue-types';
4+
5+
export const listItemMetaProps = {
6+
avatar: PropTypes.any,
7+
description: PropTypes.any,
8+
prefixCls: PropTypes.string,
9+
title: PropTypes.any,
10+
};
11+
12+
export type ListItemMetaProps = Partial<ExtractPropTypes<typeof listItemMetaProps>>;
13+
14+
export default defineComponent({
15+
name: 'AListItemMeta',
16+
props: listItemMetaProps,
17+
displayName: 'AListItemMeta', // 兼容历史函数式组件
18+
__ANT_LIST_ITEM_META: true,
19+
slots: ['avatar', 'description', 'title'],
20+
setup(props, { slots }) {
21+
const { prefixCls } = useConfigInject('list', props);
22+
return () => {
23+
const classString = `${prefixCls.value}-item-meta`;
24+
const title = props.title ?? slots.title?.();
25+
const description = props.description ?? slots.description?.();
26+
const avatar = props.avatar ?? slots.avatar?.();
27+
const content = (
28+
<div class={`${prefixCls.value}-item-meta-content`}>
29+
{title && <h4 class={`${prefixCls.value}-item-meta-title`}>{title}</h4>}
30+
{description && (
31+
<div class={`${prefixCls.value}-item-meta-description`}>{description}</div>
32+
)}
33+
</div>
34+
);
35+
return (
36+
<div class={classString}>
37+
{avatar && <div class={`${prefixCls.value}-item-meta-avatar`}>{avatar}</div>}
38+
{(title || description) && content}
39+
</div>
40+
);
41+
};
42+
},
43+
});

0 commit comments

Comments
 (0)