Skip to content

Commit c75d246

Browse files
committed
simplify size cleanup
1 parent f99ff51 commit c75d246

3 files changed

Lines changed: 137 additions & 81 deletions

File tree

src/NotificationList/index.tsx

Lines changed: 126 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { CSSMotionList } from '@rc-component/motion';
22
import type { CSSMotionProps } from '@rc-component/motion';
3-
import { composeRef } from '@rc-component/util/lib/ref';
3+
import { useComposeRef } from '@rc-component/util/lib/ref';
44
import { clsx } from 'clsx';
55
import * as React from 'react';
66
import useListPosition from '../hooks/useListPosition';
@@ -40,6 +40,116 @@ export interface NotificationListProps {
4040
onAllRemoved?: (placement: Placement) => void;
4141
}
4242

43+
interface NotificationListItemProps {
44+
config: NotificationListConfig;
45+
components?: ComponentsType;
46+
contextClassNames?: NotificationClassNames;
47+
classNames?: NotificationClassNames;
48+
styles?: NotificationStyles;
49+
motionClassName?: string;
50+
motionStyle?: React.CSSProperties;
51+
nodeRef: React.Ref<HTMLDivElement>;
52+
prefixCls: string;
53+
offset?: number;
54+
notificationIndex: number;
55+
stackInThreshold: boolean;
56+
listHovering: boolean;
57+
stackEnabled: boolean;
58+
pauseOnHover?: boolean;
59+
setNodeSize: (key: string, node: HTMLDivElement | null) => void;
60+
onNoticeClose?: (key: React.Key) => void;
61+
}
62+
63+
const NotificationListItem: React.FC<NotificationListItemProps> = (props) => {
64+
const {
65+
config,
66+
components,
67+
contextClassNames,
68+
classNames,
69+
styles,
70+
motionClassName,
71+
motionStyle,
72+
nodeRef,
73+
prefixCls,
74+
offset,
75+
notificationIndex,
76+
stackInThreshold,
77+
listHovering,
78+
stackEnabled,
79+
pauseOnHover,
80+
setNodeSize,
81+
onNoticeClose,
82+
} = props;
83+
const { key, placement: itemPlacement, ...notificationConfig } = config;
84+
const strKey = String(key);
85+
86+
const setItemRef = React.useCallback(
87+
(node: HTMLDivElement | null) => {
88+
setNodeSize(strKey, node);
89+
},
90+
[setNodeSize, strKey],
91+
);
92+
const ref = useComposeRef(nodeRef, setItemRef);
93+
94+
return (
95+
<Notification
96+
{...notificationConfig}
97+
ref={ref}
98+
prefixCls={prefixCls}
99+
offset={offset}
100+
notificationIndex={notificationIndex}
101+
stackInThreshold={stackInThreshold}
102+
className={clsx(contextClassNames?.notice, config.className)}
103+
style={config.style}
104+
classNames={{
105+
wrapper: clsx(classNames?.wrapper, config.classNames?.wrapper),
106+
root: clsx(classNames?.root, config.classNames?.root, motionClassName),
107+
icon: clsx(classNames?.icon, config.classNames?.icon),
108+
section: clsx(classNames?.section, config.classNames?.section),
109+
close: clsx(classNames?.close, config.classNames?.close),
110+
progress: clsx(classNames?.progress, config.classNames?.progress),
111+
}}
112+
styles={{
113+
wrapper: {
114+
...styles?.wrapper,
115+
...config.styles?.wrapper,
116+
},
117+
root: {
118+
...styles?.root,
119+
...config.styles?.root,
120+
...motionStyle,
121+
},
122+
icon: {
123+
...styles?.icon,
124+
...config.styles?.icon,
125+
},
126+
section: {
127+
...styles?.section,
128+
...config.styles?.section,
129+
},
130+
close: {
131+
...styles?.close,
132+
...config.styles?.close,
133+
},
134+
progress: {
135+
...styles?.progress,
136+
...config.styles?.progress,
137+
},
138+
}}
139+
components={{
140+
...components,
141+
...config.components,
142+
}}
143+
hovering={stackEnabled && listHovering}
144+
pauseOnHover={config.pauseOnHover ?? pauseOnHover}
145+
onClose={() => {
146+
config.onClose?.();
147+
onNoticeClose?.(key);
148+
}}
149+
/>
150+
);
151+
};
152+
43153
const NotificationList: React.FC<NotificationListProps> = (props) => {
44154
const {
45155
configList = [],
@@ -148,71 +258,31 @@ const NotificationList: React.FC<NotificationListProps> = (props) => {
148258
}}
149259
>
150260
{({ config, className: motionClassName, style: motionStyle, index = 0 }, nodeRef) => {
151-
const { key, placement: itemPlacement, ...notificationConfig } = config;
261+
const { key } = config;
152262
const strKey = String(key);
153263
const notificationIndex = keyList.length - index - 1;
154264
const stackInThreshold = stackEnabled && notificationIndex < threshold;
155265

156-
const setItemRef = (node: HTMLDivElement | null) => {
157-
setNodeSize(strKey, node);
158-
};
159-
160266
return (
161-
<Notification
267+
<NotificationListItem
162268
key={key}
163-
{...notificationConfig}
164-
ref={composeRef(nodeRef, setItemRef)}
269+
config={config}
270+
components={components}
271+
contextClassNames={contextClassNames}
272+
classNames={classNames}
273+
styles={styles}
274+
motionClassName={motionClassName}
275+
motionStyle={motionStyle}
276+
nodeRef={nodeRef}
165277
prefixCls={prefixCls}
166278
offset={notificationPosition.get(strKey)}
167279
notificationIndex={notificationIndex}
168280
stackInThreshold={stackInThreshold}
169-
className={clsx(contextClassNames?.notice, config.className)}
170-
style={config.style}
171-
classNames={{
172-
wrapper: clsx(classNames?.wrapper, config.classNames?.wrapper),
173-
root: clsx(classNames?.root, config.classNames?.root, motionClassName),
174-
icon: clsx(classNames?.icon, config.classNames?.icon),
175-
section: clsx(classNames?.section, config.classNames?.section),
176-
close: clsx(classNames?.close, config.classNames?.close),
177-
progress: clsx(classNames?.progress, config.classNames?.progress),
178-
}}
179-
styles={{
180-
wrapper: {
181-
...styles?.wrapper,
182-
...config.styles?.wrapper,
183-
},
184-
root: {
185-
...styles?.root,
186-
...config.styles?.root,
187-
...motionStyle,
188-
},
189-
icon: {
190-
...styles?.icon,
191-
...config.styles?.icon,
192-
},
193-
section: {
194-
...styles?.section,
195-
...config.styles?.section,
196-
},
197-
close: {
198-
...styles?.close,
199-
...config.styles?.close,
200-
},
201-
progress: {
202-
...styles?.progress,
203-
...config.styles?.progress,
204-
},
205-
}}
206-
components={{
207-
...components,
208-
...config.components,
209-
}}
210-
hovering={stackEnabled && listHovering}
211-
pauseOnHover={config.pauseOnHover ?? pauseOnHover}
212-
onClose={() => {
213-
config.onClose?.();
214-
onNoticeClose?.(key);
215-
}}
281+
listHovering={listHovering}
282+
stackEnabled={stackEnabled}
283+
pauseOnHover={pauseOnHover}
284+
setNodeSize={setNodeSize}
285+
onNoticeClose={onNoticeClose}
216286
/>
217287
);
218288
}}

src/hooks/useClosable.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ export type ParsedClosableConfig = ClosableConfig &
1818
export default function useClosable(
1919
closable?: ClosableType,
2020
): [boolean, ParsedClosableConfig, ReturnType<typeof pickAttrs>] {
21+
// Convert boolean shorthand into the object shape used by render logic.
2122
const closableObj = React.useMemo(() => {
2223
if (closable === false) {
2324
return {
@@ -33,6 +34,7 @@ export default function useClosable(
3334
return {};
3435
}, [closable]);
3536

37+
// Fill defaults so callers can read closeIcon and disabled without extra guards.
3638
const closableConfig = React.useMemo<ParsedClosableConfig>(
3739
() => ({
3840
...closableObj,
@@ -42,6 +44,7 @@ export default function useClosable(
4244
[closableObj],
4345
);
4446

47+
// Forward aria-* props from the closable config to the close button.
4548
const closableAriaProps = React.useMemo(() => pickAttrs(closableConfig, true), [closableConfig]);
4649

4750
return [!!closable, closableConfig, closableAriaProps];

src/hooks/useListPosition/useSizes.ts

Lines changed: 8 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -12,29 +12,21 @@ export type NodeSizeMap = Record<string, NodeSize>;
1212
*/
1313
export default function useSizes() {
1414
const [sizeMap, setSizeMap] = React.useState<NodeSizeMap>({});
15-
const cleanUpMapRef = React.useRef<Record<string, ReturnType<typeof setTimeout>>>({});
1615

1716
const setNodeSize = React.useCallback((key: string, node: HTMLDivElement | null) => {
1817
if (!node) {
19-
cleanUpMapRef.current[key] = setTimeout(() => {
20-
delete cleanUpMapRef.current[key];
21-
22-
setSizeMap((prevSizeMap) => {
23-
if (!(key in prevSizeMap)) {
24-
return prevSizeMap;
25-
}
26-
27-
const nextSizeMap = { ...prevSizeMap };
28-
delete nextSizeMap[key];
29-
return nextSizeMap;
30-
});
18+
setSizeMap((prevSizeMap) => {
19+
if (!(key in prevSizeMap)) {
20+
return prevSizeMap;
21+
}
22+
23+
const nextSizeMap = { ...prevSizeMap };
24+
delete nextSizeMap[key];
25+
return nextSizeMap;
3126
});
3227
return;
3328
}
3429

35-
clearTimeout(cleanUpMapRef.current[key]);
36-
delete cleanUpMapRef.current[key];
37-
3830
const nextSize = {
3931
width: node.offsetWidth,
4032
height: node.offsetHeight,
@@ -53,14 +45,5 @@ export default function useSizes() {
5345
});
5446
}, []);
5547

56-
React.useEffect(
57-
() => () => {
58-
Object.values(cleanUpMapRef.current).forEach((timer) => {
59-
clearTimeout(timer);
60-
});
61-
},
62-
[],
63-
);
64-
6548
return [sizeMap, setNodeSize] as const;
6649
}

0 commit comments

Comments
 (0)