Skip to content

Commit 9d5343a

Browse files
zkwolftangjinzhou
andauthored
refactor: descriptions (#6213)
* refactor: descriptions * fix: ts error Co-authored-by: tangjinzhou <[email protected]>
1 parent 2d54e21 commit 9d5343a

File tree

14 files changed

+325
-302
lines changed

14 files changed

+325
-302
lines changed

components/_util/hooks/useBreakpoint.ts

+5-3
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,21 @@
11
import type { Ref } from 'vue';
22
import { onMounted, onUnmounted, ref } from 'vue';
33
import type { ScreenMap } from '../../_util/responsiveObserve';
4-
import ResponsiveObserve from '../../_util/responsiveObserve';
4+
import useResponsiveObserve from '../../_util/responsiveObserve';
55

66
function useBreakpoint(): Ref<ScreenMap> {
77
const screens = ref<ScreenMap>({});
88
let token = null;
9+
const responsiveObserve = useResponsiveObserve();
10+
911
onMounted(() => {
10-
token = ResponsiveObserve.subscribe(supportScreens => {
12+
token = responsiveObserve.value.subscribe(supportScreens => {
1113
screens.value = supportScreens;
1214
});
1315
});
1416

1517
onUnmounted(() => {
16-
ResponsiveObserve.unsubscribe(token);
18+
responsiveObserve.value.unsubscribe(token);
1719
});
1820

1921
return screens;

components/_util/responsiveObserve.ts

+75-64
Original file line numberDiff line numberDiff line change
@@ -1,75 +1,86 @@
1+
import type { GlobalToken } from '../theme/interface';
2+
import { useToken } from '../theme/internal';
3+
import useMemo from './hooks/useMemo';
4+
15
export type Breakpoint = 'xxxl' | 'xxl' | 'xl' | 'lg' | 'md' | 'sm' | 'xs';
26
export type BreakpointMap = Record<Breakpoint, string>;
37
export type ScreenMap = Partial<Record<Breakpoint, boolean>>;
48
export type ScreenSizeMap = Partial<Record<Breakpoint, number>>;
59

610
export const responsiveArray: Breakpoint[] = ['xxxl', 'xxl', 'xl', 'lg', 'md', 'sm', 'xs'];
11+
type SubscribeFunc = (screens: ScreenMap) => void;
712

8-
export const responsiveMap: BreakpointMap = {
9-
xs: '(max-width: 575px)',
10-
sm: '(min-width: 576px)',
11-
md: '(min-width: 768px)',
12-
lg: '(min-width: 992px)',
13-
xl: '(min-width: 1200px)',
14-
xxl: '(min-width: 1600px)',
15-
xxxl: '(min-width: 2000px)',
16-
};
13+
const getResponsiveMap = (token: GlobalToken): BreakpointMap => ({
14+
xs: `(max-width: ${token.screenXSMax}px)`,
15+
sm: `(min-width: ${token.screenSM}px)`,
16+
md: `(min-width: ${token.screenMD}px)`,
17+
lg: `(min-width: ${token.screenLG}px)`,
18+
xl: `(min-width: ${token.screenXL}px)`,
19+
xxl: `(min-width: ${token.screenXXL}px)`,
20+
xxxl: `{min-width: ${token.screenXXXL}px}`,
21+
});
1722

18-
type SubscribeFunc = (screens: ScreenMap) => void;
19-
const subscribers = new Map<Number, SubscribeFunc>();
20-
let subUid = -1;
21-
let screens = {};
23+
export default function useResponsiveObserver() {
24+
const [, token] = useToken();
25+
const responsiveMap: BreakpointMap = getResponsiveMap(token.value);
2226

23-
const responsiveObserve = {
24-
matchHandlers: {} as {
25-
[prop: string]: {
26-
mql: MediaQueryList;
27-
listener: ((this: MediaQueryList, ev: MediaQueryListEvent) => any) | null;
28-
};
29-
},
30-
dispatch(pointMap: ScreenMap) {
31-
screens = pointMap;
32-
subscribers.forEach(func => func(screens));
33-
return subscribers.size >= 1;
34-
},
35-
subscribe(func: SubscribeFunc): number {
36-
if (!subscribers.size) this.register();
37-
subUid += 1;
38-
subscribers.set(subUid, func);
39-
func(screens);
40-
return subUid;
41-
},
42-
unsubscribe(token: number) {
43-
subscribers.delete(token);
44-
if (!subscribers.size) this.unregister();
45-
},
46-
unregister() {
47-
Object.keys(responsiveMap).forEach((screen: string) => {
48-
const matchMediaQuery = responsiveMap[screen];
49-
const handler = this.matchHandlers[matchMediaQuery];
50-
handler?.mql.removeListener(handler?.listener);
51-
});
52-
subscribers.clear();
53-
},
54-
register() {
55-
Object.keys(responsiveMap).forEach((screen: string) => {
56-
const matchMediaQuery = responsiveMap[screen];
57-
const listener = ({ matches }: { matches: boolean }) => {
58-
this.dispatch({
59-
...screens,
60-
[screen]: matches,
61-
});
62-
};
63-
const mql = window.matchMedia(matchMediaQuery);
64-
mql.addListener(listener);
65-
this.matchHandlers[matchMediaQuery] = {
66-
mql,
67-
listener,
68-
};
27+
// To avoid repeat create instance, we add `useMemo` here.
28+
return useMemo(() => {
29+
const subscribers = new Map<Number, SubscribeFunc>();
30+
let subUid = -1;
31+
let screens = {};
6932

70-
listener(mql);
71-
});
72-
},
73-
};
33+
return {
34+
matchHandlers: {} as {
35+
[prop: string]: {
36+
mql: MediaQueryList;
37+
listener: ((this: MediaQueryList, ev: MediaQueryListEvent) => any) | null;
38+
};
39+
},
40+
dispatch(pointMap: ScreenMap) {
41+
screens = pointMap;
42+
subscribers.forEach(func => func(screens));
43+
return subscribers.size >= 1;
44+
},
45+
subscribe(func: SubscribeFunc): number {
46+
if (!subscribers.size) this.register();
47+
subUid += 1;
48+
subscribers.set(subUid, func);
49+
func(screens);
50+
return subUid;
51+
},
52+
unsubscribe(paramToken: number) {
53+
subscribers.delete(paramToken);
54+
if (!subscribers.size) this.unregister();
55+
},
56+
unregister() {
57+
Object.keys(responsiveMap).forEach((screen: string) => {
58+
const matchMediaQuery = responsiveMap[screen];
59+
const handler = this.matchHandlers[matchMediaQuery];
60+
handler?.mql.removeListener(handler?.listener);
61+
});
62+
subscribers.clear();
63+
},
64+
register() {
65+
Object.keys(responsiveMap).forEach((screen: string) => {
66+
const matchMediaQuery = responsiveMap[screen];
67+
const listener = ({ matches }: { matches: boolean }) => {
68+
this.dispatch({
69+
...screens,
70+
[screen]: matches,
71+
});
72+
};
73+
const mql = window.matchMedia(matchMediaQuery);
74+
mql.addListener(listener);
75+
this.matchHandlers[matchMediaQuery] = {
76+
mql,
77+
listener,
78+
};
7479

75-
export default responsiveObserve;
80+
listener(mql);
81+
});
82+
},
83+
responsiveMap,
84+
};
85+
}, [token]);
86+
}

components/descriptions/Cell.tsx

+4-4
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@ interface CellProps extends HTMLAttributes {
1111
labelStyle?: CSSProperties;
1212
contentStyle?: CSSProperties;
1313
bordered?: boolean;
14-
label?: VNodeTypes;
15-
content?: VNodeTypes;
14+
label?: number | VNodeTypes;
15+
content?: number | VNodeTypes;
1616
colon?: boolean;
1717
}
1818

@@ -49,7 +49,7 @@ const Cell: FunctionalComponent<CellProps> = props => {
4949
return (
5050
<Component class={[`${itemPrefixCls}-item`]} colSpan={span}>
5151
<div class={`${itemPrefixCls}-item-container`}>
52-
{label && (
52+
{(label || label === 0) && (
5353
<span
5454
class={[
5555
`${itemPrefixCls}-item-label`,
@@ -62,7 +62,7 @@ const Cell: FunctionalComponent<CellProps> = props => {
6262
{label}
6363
</span>
6464
)}
65-
{content && (
65+
{(content || content === 0) && (
6666
<span class={`${itemPrefixCls}-item-content`} style={contentStyle}>
6767
{content}
6868
</span>

components/descriptions/index.en-US.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
category: Components
33
type: Data Display
44
title: Descriptions
5-
cover: https://gw.alipayobjects.com/zos/alicdn/MjtG9_FOI/Descriptions.svg
5+
cover: https://mdn.alipayobjects.com/huamei_7uahnr/afts/img/A*fHdlTpif6XQAAAAAAAAAAAAADrJ8AQ/original
66
---
77

88
Display multiple read-only fields in groups.

components/descriptions/index.tsx

+16-9
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,15 @@ import {
2020
} from 'vue';
2121
import warning from '../_util/warning';
2222
import type { Breakpoint, ScreenMap } from '../_util/responsiveObserve';
23-
import ResponsiveObserve, { responsiveArray } from '../_util/responsiveObserve';
23+
import useResponsiveObserve, { responsiveArray } from '../_util/responsiveObserve';
2424
import Row from './Row';
2525
import PropTypes from '../_util/vue-types';
2626
import { cloneElement } from '../_util/vnode';
2727
import { flattenChildren } from '../_util/props-util';
2828
import useConfigInject from '../config-provider/hooks/useConfigInject';
2929

30+
import useStyle from './style';
31+
3032
export const DescriptionsItemProps = {
3133
prefixCls: String,
3234
label: PropTypes.any,
@@ -82,7 +84,7 @@ function getColumn(column: DescriptionsProps['column'], screens: ScreenMap): num
8284
return 3;
8385
}
8486

85-
function getFilledItem(node: VNode, span: number | undefined, rowRestCol: number): VNode {
87+
function getFilledItem(node: VNode, rowRestCol: number, span?: number): VNode {
8688
let clone = node;
8789

8890
if (span === undefined || span > rowRestCol) {
@@ -106,12 +108,12 @@ function getRows(children: VNode[], column: number) {
106108
let tmpRow: VNode[] = [];
107109
let rowRestCol = column;
108110
childNodes.forEach((node, index) => {
109-
const span: number | undefined = node.props?.span;
111+
const span: number = node.props?.span;
110112
const mergedSpan = span || 1;
111113

112114
// Additional handle last one
113115
if (index === childNodes.length - 1) {
114-
tmpRow.push(getFilledItem(node, span, rowRestCol));
116+
tmpRow.push(getFilledItem(node, rowRestCol, span));
115117
rows.push(tmpRow);
116118
return;
117119
}
@@ -120,7 +122,7 @@ function getRows(children: VNode[], column: number) {
120122
rowRestCol -= mergedSpan;
121123
tmpRow.push(node);
122124
} else {
123-
tmpRow.push(getFilledItem(node, mergedSpan, rowRestCol));
125+
tmpRow.push(getFilledItem(node, rowRestCol, mergedSpan));
124126
rows.push(tmpRow);
125127
rowRestCol = column;
126128
tmpRow = [];
@@ -167,8 +169,12 @@ const Descriptions = defineComponent({
167169
const { prefixCls, direction } = useConfigInject('descriptions', props);
168170
let token: number;
169171
const screens = ref<ScreenMap>({});
172+
173+
const [wrapSSR, hashId] = useStyle(prefixCls);
174+
const responsiveObserve = useResponsiveObserve();
175+
170176
onBeforeMount(() => {
171-
token = ResponsiveObserve.subscribe(screen => {
177+
token = responsiveObserve.value.subscribe(screen => {
172178
if (typeof props.column !== 'object') {
173179
return;
174180
}
@@ -177,7 +183,7 @@ const Descriptions = defineComponent({
177183
});
178184

179185
onBeforeUnmount(() => {
180-
ResponsiveObserve.unsubscribe(token);
186+
responsiveObserve.value.unsubscribe(token);
181187
});
182188

183189
provide(descriptionsContext, {
@@ -200,7 +206,7 @@ const Descriptions = defineComponent({
200206
const children = slots.default?.();
201207
const rows = getRows(children, mergeColumn.value);
202208

203-
return (
209+
return wrapSSR(
204210
<div
205211
class={[
206212
prefixCls.value,
@@ -209,6 +215,7 @@ const Descriptions = defineComponent({
209215
[`${prefixCls.value}-bordered`]: !!bordered,
210216
[`${prefixCls.value}-rtl`]: direction.value === 'rtl',
211217
},
218+
hashId.value,
212219
]}
213220
>
214221
{(title || extra) && (
@@ -234,7 +241,7 @@ const Descriptions = defineComponent({
234241
</tbody>
235242
</table>
236243
</div>
237-
</div>
244+
</div>,
238245
);
239246
};
240247
},

components/descriptions/index.zh-CN.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ category: Components
33
type: 数据展示
44
title: Descriptions
55
subtitle: 描述列表
6-
cover: https://gw.alipayobjects.com/zos/alicdn/MjtG9_FOI/Descriptions.svg
6+
cover: https://mdn.alipayobjects.com/huamei_7uahnr/afts/img/A*fHdlTpif6XQAAAAAAAAAAAAADrJ8AQ/original
77
---
88

99
成组展示多个只读字段。

0 commit comments

Comments
 (0)