Skip to content

Commit dedb5ad

Browse files
committed
chore: Record items width
1 parent ea88dce commit dedb5ad

File tree

7 files changed

+162
-9
lines changed

7 files changed

+162
-9
lines changed

assets/index.less

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,10 @@
55
flex-wrap: nowrap;
66

77
&-item {
8-
margin-right: 4px;
8+
margin-left: 0.5em;
9+
margin-right: 1em;
910
background: rgba(0, 255, 0, 0.2);
1011
padding: 4px 8px;
12+
border: 1px solid red;
1113
}
1214
}

examples/basic.tsx

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import React from 'react';
22
import Overflow from '../src';
33
import '../assets/index.less';
4+
import './common.less';
45

56
interface ItemType {
67
value: string | number;
@@ -17,23 +18,32 @@ function createData(count: number): ItemType[] {
1718
}
1819

1920
const Demo = () => {
20-
const [data, setData] = React.useState(createData(10));
21+
const [data, setData] = React.useState(createData(5));
2122

2223
return (
23-
<div>
24+
<div style={{ padding: 32 }}>
2425
<select
26+
style={{ width: 200, height: 32 }}
2527
value={data.length}
2628
onChange={({ target: { value } }) => {
2729
setData(createData(Number(value)));
2830
}}
2931
>
3032
<option value={0}>0</option>
33+
<option value={1}>1</option>
3134
<option value={5}>5</option>
3235
<option value={10}>10</option>
3336
<option value={20}>20</option>
3437
</select>
3538

36-
<div style={{ border: '5px solid green', padding: 8, maxWidth: 300 }}>
39+
<div
40+
style={{
41+
border: '5px solid green',
42+
padding: 8,
43+
maxWidth: 300,
44+
marginTop: 32,
45+
}}
46+
>
3747
<Overflow<ItemType> data={data} renderItem={(item) => item.label} />
3848
</div>
3949
</div>

examples/common.less

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
* {
2+
box-sizing: border-box;
3+
}

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,8 @@
4242
},
4343
"dependencies": {
4444
"@babel/runtime": "^7.11.1",
45-
"classnames": "^2.2.1"
45+
"classnames": "^2.2.1",
46+
"rc-resize-observer": "^0.3.0"
4647
},
4748
"devDependencies": {
4849
"@types/classnames": "^2.2.9",

src/Item.tsx

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
import * as React from 'react';
2+
import classNames from 'classnames';
3+
import ResizeObserver from 'rc-resize-observer';
4+
import { getFullWidth } from './util';
5+
6+
export interface ItemProps<ItemType> {
7+
prefixCls: string;
8+
item?: ItemType;
9+
className?: string;
10+
renderItem?: (item: ItemType) => React.ReactNode;
11+
disabled?: boolean;
12+
itemKey?: React.Key;
13+
registerSize: (key: React.Key, width: number) => void;
14+
children?: React.ReactNode;
15+
}
16+
17+
export default function Item<ItemType>(props: ItemProps<ItemType>) {
18+
const {
19+
prefixCls,
20+
item,
21+
renderItem,
22+
disabled,
23+
registerSize,
24+
itemKey,
25+
className,
26+
children,
27+
} = props;
28+
29+
// ================================ Effect ================================
30+
function internalRegisterSize(width: number) {
31+
registerSize(itemKey!, width);
32+
}
33+
34+
React.useEffect(
35+
() => () => {
36+
internalRegisterSize(0);
37+
},
38+
[],
39+
);
40+
41+
// ================================ Render ================================
42+
const childNode = item !== undefined ? renderItem!(item) : children;
43+
44+
let itemNode = (
45+
<div className={classNames(prefixCls, className)}>{childNode}</div>
46+
);
47+
48+
if (!disabled) {
49+
itemNode = (
50+
<ResizeObserver
51+
onResize={(_, element) => {
52+
const width = getFullWidth(element);
53+
internalRegisterSize(width);
54+
}}
55+
>
56+
{itemNode}
57+
</ResizeObserver>
58+
);
59+
}
60+
61+
return itemNode;
62+
}

src/Overflow.tsx

Lines changed: 67 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
import * as React from 'react';
22
import classNames from 'classnames';
3+
import ResizeObserver from 'rc-resize-observer';
4+
import { getFullWidth } from './util';
5+
import Item from './Item';
36

47
export interface OverflowProps<ItemType> {
58
prefixCls?: string;
@@ -8,6 +11,8 @@ export interface OverflowProps<ItemType> {
811
data?: ItemType[];
912
itemKey?: React.Key | ((item: ItemType) => React.Key);
1013
renderItem?: (item: ItemType) => React.ReactNode;
14+
disabled?: boolean;
15+
maxCount?: number | 'responsive';
1116
}
1217

1318
function Overflow<ItemType = any>(
@@ -21,8 +26,18 @@ function Overflow<ItemType = any>(
2126
itemKey,
2227
style,
2328
className,
29+
disabled,
30+
maxCount = 'responsive',
2431
} = props;
2532

33+
const [containerWidth, setContainerWidth] = React.useState(0);
34+
const [itemWidths, setItemWidths] = React.useState(
35+
new Map<React.Key, number>(),
36+
);
37+
const [overflowWidth, setOverflowWidth] = React.useState(0);
38+
39+
const itemPrefixCls = `${prefixCls}-item`;
40+
2641
// ================================= Item =================================
2742
const getKey = React.useCallback(
2843
(item: ItemType, index: number) => {
@@ -39,20 +54,68 @@ function Overflow<ItemType = any>(
3954
[renderItem],
4055
);
4156

57+
// ================================= Size =================================
58+
function onOverflowResize(_: object, element: HTMLElement) {
59+
setContainerWidth(element.clientWidth);
60+
}
61+
62+
function registerSize(key: React.Key, width: number) {
63+
const clone = new Map(itemWidths);
64+
console.log('==>>>', key, width);
65+
66+
if (!width) {
67+
clone.delete(key);
68+
} else {
69+
clone.set(key, width);
70+
}
71+
72+
setItemWidths(clone);
73+
}
74+
75+
function registerOverflowSize(_: React.Key, width: number) {
76+
console.log('Overflow >>>', width);
77+
setOverflowWidth(width);
78+
}
79+
4280
// ================================ Render ================================
43-
return (
81+
let overflowNode = (
4482
<div className={classNames(prefixCls, className)} style={style} ref={ref}>
4583
{data.map((item, index) => {
4684
const key = getKey(item, index);
4785

4886
return (
49-
<div className={`${prefixCls}-item`} key={key}>
50-
{mergedRenderItem(item)}
51-
</div>
87+
<Item<ItemType>
88+
key={key}
89+
item={item}
90+
prefixCls={itemPrefixCls}
91+
renderItem={mergedRenderItem}
92+
itemKey={key}
93+
registerSize={registerSize}
94+
disabled={disabled}
95+
/>
5296
);
5397
})}
98+
99+
<Item
100+
prefixCls={itemPrefixCls}
101+
className={`${itemPrefixCls}-overflow`}
102+
disabled={disabled}
103+
registerSize={registerOverflowSize}
104+
>
105+
Overflow
106+
</Item>
54107
</div>
55108
);
109+
110+
if (!disabled) {
111+
overflowNode = (
112+
<ResizeObserver onResize={onOverflowResize}>
113+
{overflowNode}
114+
</ResizeObserver>
115+
);
116+
}
117+
118+
return overflowNode;
56119
}
57120

58121
const ForwardOverflow = React.forwardRef(Overflow);

src/util.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
function px2num(value: string = '') {
2+
return Number(`0${value}`.replace('px', ''));
3+
}
4+
5+
export function getFullWidth(element: HTMLElement) {
6+
const { offsetWidth } = element;
7+
const { marginLeft, marginRight } = getComputedStyle(element);
8+
const left = px2num(marginLeft);
9+
const right = px2num(marginRight);
10+
11+
return left + offsetWidth + right;
12+
}

0 commit comments

Comments
 (0)