Skip to content

Commit b28b181

Browse files
authored
chore: Add renderRawItem support (#10)
* feat: Add raw render * fix: no memo * feat: Support renderRawItem * test: Add test case * test: Not crash * test: more test
1 parent ec21fb5 commit b28b181

File tree

14 files changed

+301
-59
lines changed

14 files changed

+301
-59
lines changed

.prettierrc

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,6 @@
33
"semi": true,
44
"singleQuote": true,
55
"tabWidth": 2,
6-
"trailingComma": "all"
6+
"trailingComma": "all",
7+
"arrowParens": "avoid"
78
}

docs/demo/raw-render.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
## Raw Render
2+
3+
<code src="../../examples/raw-render.tsx">

docs/index.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,4 @@
22
title: rc-overflow
33
---
44

5-
<embed src="../README.md"></embed>
5+
<embed src="../README.md" />

examples/basic.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import React from 'react';
2-
import Overflow from 'rc-overflow';
2+
import Overflow from '../src';
33
import '../assets/index.less';
44
import './common.less';
55

examples/blink.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import React from 'react';
2-
import Overflow from 'rc-overflow';
2+
import Overflow from '../src';
33
import '../assets/index.less';
44
import './common.less';
55

examples/fill-width.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import React from 'react';
2-
import Overflow from 'rc-overflow';
2+
import Overflow from '../src';
33
import '../assets/index.less';
44
import './common.less';
55

examples/raw-render.tsx

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
import React from 'react';
2+
import Overflow from '../src';
3+
import '../assets/index.less';
4+
import './common.less';
5+
6+
interface ItemType {
7+
value: string | number;
8+
label: string;
9+
}
10+
11+
function createData(count: number): ItemType[] {
12+
const data: ItemType[] = new Array(count).fill(undefined).map((_, index) => ({
13+
value: index,
14+
label: `Label ${index}`,
15+
}));
16+
17+
return data;
18+
}
19+
20+
function renderRawItem(item: ItemType) {
21+
return (
22+
<Overflow.Item component="span">
23+
<div
24+
style={{
25+
margin: '0 16px 0 8px',
26+
padding: '4px 8px',
27+
background: 'rgba(255, 0, 0, 0.2)',
28+
}}
29+
>
30+
{item.label}
31+
</div>
32+
</Overflow.Item>
33+
);
34+
}
35+
36+
function renderRest(items: ItemType[]) {
37+
return (
38+
<div
39+
style={{
40+
margin: '0 16px 0 8px',
41+
padding: '4px 8px',
42+
background: 'rgba(255, 0, 0, 0.2)',
43+
}}
44+
>
45+
+{items.length}...
46+
</div>
47+
);
48+
}
49+
50+
const Demo = () => {
51+
const [responsive, setResponsive] = React.useState(true);
52+
const [data, setData] = React.useState(createData(1));
53+
54+
return (
55+
<div style={{ padding: 32 }}>
56+
<button
57+
type="button"
58+
onClick={() => {
59+
setResponsive(!responsive);
60+
}}
61+
>
62+
{responsive ? 'Responsive' : 'MaxCount: 6'}
63+
</button>
64+
<select
65+
style={{ width: 200, height: 32 }}
66+
value={data.length}
67+
onChange={({ target: { value } }) => {
68+
setData(createData(Number(value)));
69+
}}
70+
>
71+
<option value={0}>0</option>
72+
<option value={1}>1</option>
73+
<option value={2}>2</option>
74+
<option value={3}>3</option>
75+
<option value={5}>5</option>
76+
<option value={10}>10</option>
77+
<option value={20}>20</option>
78+
<option value={200}>200</option>
79+
</select>
80+
81+
<div
82+
style={{
83+
border: '5px solid green',
84+
padding: 8,
85+
maxWidth: 300,
86+
marginTop: 32,
87+
}}
88+
>
89+
<Overflow<ItemType>
90+
data={data}
91+
renderRawItem={renderRawItem}
92+
renderRest={renderRest}
93+
maxCount={responsive ? 'responsive' : 6}
94+
/>
95+
</div>
96+
</div>
97+
);
98+
};
99+
100+
export default Demo;

src/Item.tsx

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import classNames from 'classnames';
33
import ResizeObserver from 'rc-resize-observer';
44
import { ComponentType } from './Overflow';
55

6-
export interface ItemProps<ItemType> {
6+
export interface ItemProps<ItemType> extends React.HTMLAttributes<any> {
77
prefixCls: string;
88
item?: ItemType;
99
className?: string;
@@ -31,7 +31,7 @@ export default function Item<ItemType>(props: ItemProps<ItemType>) {
3131
children,
3232
display,
3333
order,
34-
component: Component,
34+
component: Component = 'div',
3535
...restProps
3636
} = props;
3737

@@ -50,7 +50,8 @@ export default function Item<ItemType>(props: ItemProps<ItemType>) {
5050
);
5151

5252
// ================================ Render ================================
53-
const childNode = item !== undefined ? renderItem!(item) : children;
53+
const childNode =
54+
renderItem && item !== undefined ? renderItem(item) : children;
5455

5556
let itemNode = (
5657
<Component

src/Overflow.tsx

Lines changed: 60 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,19 @@ import classNames from 'classnames';
44
import ResizeObserver from 'rc-resize-observer';
55
import Item from './Item';
66
import { useBatchFrameState } from './hooks/useBatchFrameState';
7+
import RawItem from './RawItem';
8+
9+
export const OverflowContext = React.createContext<{
10+
prefixCls: string;
11+
responsive: boolean;
12+
order: number;
13+
item: any;
14+
itemKey: React.Key;
15+
registerSize: (key: React.Key, width: number | null) => void;
16+
display: boolean;
17+
18+
// renderItem={mergedRenderItem}
19+
}>(null);
720

821
const RESPONSIVE = 'responsive' as const;
922

@@ -22,7 +35,8 @@ export interface OverflowProps<ItemType> extends React.HTMLAttributes<any> {
2235
/** Used for `responsive`. It will limit render node to avoid perf issue */
2336
itemWidth?: number;
2437
renderItem?: (item: ItemType) => React.ReactNode;
25-
renderItemProps?: (item: ItemType) => React.HTMLAttributes<any>;
38+
/** @private Do not use in your production. Render raw node that need wrap Item by developer self */
39+
renderRawItem?: (item: ItemType, index: number) => React.ReactElement;
2640
maxCount?: number | typeof RESPONSIVE;
2741
renderRest?:
2842
| React.ReactNode
@@ -44,16 +58,16 @@ function Overflow<ItemType = any>(
4458
prefixCls = 'rc-overflow',
4559
data = [],
4660
renderItem,
61+
renderRawItem,
4762
itemKey,
4863
itemWidth = 10,
4964
style,
5065
className,
5166
maxCount,
5267
renderRest = defaultRenderRest,
53-
renderItemProps,
5468
suffix,
5569
component: Component = 'div',
56-
itemComponent = 'div',
70+
itemComponent,
5771
...restProps
5872
} = props;
5973

@@ -234,14 +248,28 @@ function Overflow<ItemType = any>(
234248
component: itemComponent,
235249
};
236250

237-
let overflowNode = (
238-
<Component
239-
className={classNames(prefixCls, className)}
240-
style={style}
241-
ref={ref}
242-
{...restProps}
243-
>
244-
{mergedData.map((item, index) => {
251+
// Choice render fun by `renderRawItem`
252+
const internalRenderItemNode = renderRawItem
253+
? (item: ItemType, index: number) => {
254+
const key = getKey(item, index);
255+
256+
return (
257+
<OverflowContext.Provider
258+
key={key}
259+
value={{
260+
...itemSharedProps,
261+
order: index,
262+
item,
263+
itemKey: key,
264+
registerSize,
265+
display: index <= displayCount,
266+
}}
267+
>
268+
{renderRawItem(item, index)}
269+
</OverflowContext.Provider>
270+
);
271+
}
272+
: (item: ItemType, index: number) => {
245273
const key = getKey(item, index);
246274

247275
return (
@@ -254,10 +282,18 @@ function Overflow<ItemType = any>(
254282
itemKey={key}
255283
registerSize={registerSize}
256284
display={index <= displayCount}
257-
{...renderItemProps?.(item)}
258285
/>
259286
);
260-
})}
287+
};
288+
289+
let overflowNode = (
290+
<Component
291+
className={classNames(prefixCls, className)}
292+
style={style}
293+
ref={ref}
294+
{...restProps}
295+
>
296+
{mergedData.map(internalRenderItemNode)}
261297

262298
{/* Rest Count Item */}
263299
{showRest ? (
@@ -304,11 +340,18 @@ function Overflow<ItemType = any>(
304340

305341
const ForwardOverflow = React.forwardRef(Overflow);
306342

307-
ForwardOverflow.displayName = 'Overflow';
308-
309-
// Convert to generic type
310-
export default ForwardOverflow as <ItemType = any>(
343+
type ForwardOverflowType = <ItemType = any>(
311344
props: React.PropsWithChildren<OverflowProps<ItemType>> & {
312345
ref?: React.Ref<HTMLDivElement>;
313346
},
314347
) => React.ReactElement;
348+
349+
type FilledOverflowType = ForwardOverflowType & {
350+
Item: typeof RawItem;
351+
};
352+
353+
ForwardOverflow.displayName = 'Overflow';
354+
((ForwardOverflow as unknown) as FilledOverflowType).Item = RawItem;
355+
356+
// Convert to generic type
357+
export default (ForwardOverflow as unknown) as FilledOverflowType;

src/RawItem.tsx

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import * as React from 'react';
2+
import Item from './Item';
3+
import { ComponentType, OverflowContext } from './Overflow';
4+
5+
export interface RawItemProps extends React.HTMLAttributes<any> {
6+
component?: ComponentType;
7+
children?: React.ReactNode;
8+
}
9+
10+
export default function RawItem(props: RawItemProps) {
11+
const context = React.useContext(OverflowContext);
12+
13+
// Render directly when context not provided
14+
if (!context) {
15+
const { component: Component = 'div', ...restProps } = props;
16+
return <Component {...restProps} />;
17+
}
18+
19+
return <Item {...context} {...props} />;
20+
}

0 commit comments

Comments
 (0)