Skip to content

Commit 1bc6e9a

Browse files
committed
feat: Init support suffix
1 parent 1698ce8 commit 1bc6e9a

File tree

4 files changed

+184
-7
lines changed

4 files changed

+184
-7
lines changed

assets/index.less

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,5 +9,6 @@
99
background: rgba(0, 255, 0, 0.2);
1010
box-shadow: 0 0 1px black;
1111
flex: none;
12+
max-width: 100%;
1213
}
1314
}

examples/fill-width.tsx

Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
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 renderItem(item: ItemType) {
21+
return (
22+
<div
23+
style={{
24+
margin: '0 16px 0 8px',
25+
padding: '4px 8px',
26+
background: 'rgba(255, 0, 0, 0.2)',
27+
}}
28+
>
29+
{item.label}
30+
</div>
31+
);
32+
}
33+
34+
function renderRest(items: ItemType[]) {
35+
return (
36+
<div
37+
style={{
38+
margin: '0 16px 0 8px',
39+
padding: '4px 8px',
40+
background: 'rgba(255, 0, 0, 0.2)',
41+
}}
42+
>
43+
+{items.length}...
44+
</div>
45+
);
46+
}
47+
48+
const inputStyle: React.CSSProperties = {
49+
border: 'none',
50+
fontSize: 12,
51+
margin: 0,
52+
outline: 'none',
53+
lineHeight: '20px',
54+
fontFamily: '-apple-system',
55+
padding: '0 4px',
56+
};
57+
58+
const Demo = () => {
59+
const [responsive, setResponsive] = React.useState(true);
60+
const [inputValue, setInputValue] = React.useState('');
61+
const [inputWidth, setInputWidth] = React.useState(0);
62+
const [data, setData] = React.useState(createData(1));
63+
const measureRef = React.useRef<HTMLDivElement>();
64+
65+
React.useLayoutEffect(() => {
66+
setInputWidth(measureRef.current.offsetWidth);
67+
}, [inputValue]);
68+
69+
return (
70+
<div style={{ padding: 32 }}>
71+
<button
72+
type="button"
73+
onClick={() => {
74+
setResponsive(!responsive);
75+
}}
76+
>
77+
{responsive ? 'Responsive' : 'MaxCount: 6'}
78+
</button>
79+
<select
80+
style={{ width: 200, height: 32 }}
81+
value={data.length}
82+
onChange={({ target: { value } }) => {
83+
setData(createData(Number(value)));
84+
}}
85+
>
86+
<option value={0}>0</option>
87+
<option value={1}>1</option>
88+
<option value={2}>2</option>
89+
<option value={3}>3</option>
90+
<option value={5}>5</option>
91+
<option value={10}>10</option>
92+
<option value={20}>20</option>
93+
<option value={200}>200</option>
94+
</select>
95+
96+
<div
97+
style={{
98+
border: '5px solid green',
99+
padding: 8,
100+
maxWidth: 300,
101+
marginTop: 32,
102+
}}
103+
>
104+
<Overflow<ItemType>
105+
data={data}
106+
renderItem={renderItem}
107+
renderRest={renderRest}
108+
maxCount={responsive ? 'responsive' : 6}
109+
suffix={
110+
<div style={{ position: 'relative', maxWidth: '100%' }}>
111+
<input
112+
style={{
113+
...inputStyle,
114+
background: 'rgba(0, 0, 0, 0.1)',
115+
width: inputWidth,
116+
minWidth: 10,
117+
maxWidth: '100%',
118+
}}
119+
value={inputValue}
120+
onChange={(e) => {
121+
setInputValue(e.target.value);
122+
}}
123+
/>
124+
<div
125+
style={{
126+
...inputStyle,
127+
pointerEvents: 'none',
128+
position: 'absolute',
129+
left: 0,
130+
top: `200%`,
131+
}}
132+
ref={measureRef}
133+
>
134+
{inputValue}
135+
</div>
136+
</div>
137+
}
138+
/>
139+
</div>
140+
</div>
141+
);
142+
};
143+
144+
export default Demo;

src/Item.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ export default function Item<ItemType>(props: ItemProps<ItemType>) {
5252
style={{
5353
opacity: mergedHidden ? 0.2 : 1,
5454
height: mergedHidden ? 0 : undefined,
55-
overflowY: responsive ? 'hidden' : undefined,
55+
overflowY: mergedHidden ? 'hidden' : undefined,
5656
order: responsive ? order : undefined,
5757
pointerEvents: mergedHidden ? 'none' : undefined,
5858
}}

src/Overflow.tsx

Lines changed: 38 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ export interface OverflowProps<ItemType> {
2020
renderRest?:
2121
| React.ReactNode
2222
| ((omittedItems: ItemType[]) => React.ReactNode);
23+
suffix?: React.ReactNode;
2324
}
2425

2526
function defaultRenderRest<ItemType>(omittedItems: ItemType[]) {
@@ -40,6 +41,7 @@ function Overflow<ItemType = any>(
4041
className,
4142
maxCount,
4243
renderRest = defaultRenderRest,
44+
suffix,
4345
} = props;
4446

4547
const createUseState = useBatchFrameState();
@@ -51,14 +53,17 @@ function Overflow<ItemType = any>(
5153

5254
const [prevRestWidth, setPrevRestWidth] = createUseState(0);
5355
const [restWidth, setRestWidth] = createUseState(0);
54-
// Always use the max width to avoid blink
55-
const mergedRestWidth = Math.max(prevRestWidth, restWidth);
56+
57+
const [suffixWidth, setSuffixWidth] = createUseState(0);
5658

5759
const [displayCount, setDisplayCount] = useState(0);
5860
const [restReady, setRestReady] = useState(false);
5961

6062
const itemPrefixCls = `${prefixCls}-item`;
6163

64+
// Always use the max width to avoid blink
65+
const mergedRestWidth = Math.max(prevRestWidth, restWidth);
66+
6267
// ================================= Data =================================
6368
const isResponsive = maxCount === RESPONSIVE;
6469

@@ -132,14 +137,18 @@ function Overflow<ItemType = any>(
132137
setPrevRestWidth(restWidth);
133138
}
134139

140+
function registerSuffixSize(_: React.Key, width: number | null) {
141+
setSuffixWidth(width!);
142+
}
143+
135144
// ================================ Effect ================================
136145
function getItemWidth(index: number) {
137146
return itemWidths.get(getKey(mergedData[index], index));
138147
}
139148

140149
React.useLayoutEffect(() => {
141150
if (containerWidth && mergedRestWidth && mergedData) {
142-
let totalWidth = 0;
151+
let totalWidth = suffixWidth;
143152

144153
const len = mergedData.length;
145154
const lastIndex = len - 1;
@@ -174,9 +183,18 @@ function Overflow<ItemType = any>(
174183
}
175184
}
176185
}
177-
}, [containerWidth, itemWidths, mergedRestWidth, getKey, mergedData]);
186+
}, [
187+
containerWidth,
188+
itemWidths,
189+
mergedRestWidth,
190+
suffixWidth,
191+
getKey,
192+
mergedData,
193+
]);
178194

179195
// ================================ Render ================================
196+
const displayRest = restReady && !!omittedItems.length;
197+
180198
let overflowNode = (
181199
<div className={classNames(prefixCls, className)} style={style} ref={ref}>
182200
{mergedData.map((item, index) => {
@@ -200,18 +218,32 @@ function Overflow<ItemType = any>(
200218
{/* Rest Count Item */}
201219
{showRest ? (
202220
<Item
203-
order={displayCount}
221+
order={displayRest ? displayCount : mergedData.length}
204222
prefixCls={itemPrefixCls}
205223
className={`${itemPrefixCls}-rest`}
206224
responsive={isResponsive}
207225
registerSize={registerOverflowSize}
208-
display={restReady && !!omittedItems.length}
226+
display={displayRest}
209227
>
210228
{typeof renderRest === 'function'
211229
? renderRest(omittedItems)
212230
: renderRest}
213231
</Item>
214232
) : null}
233+
234+
{/* Suffix Node */}
235+
{suffix && (
236+
<Item
237+
order={displayCount}
238+
prefixCls={prefixCls}
239+
className={`${itemPrefixCls}-suffix`}
240+
responsive={isResponsive}
241+
registerSize={registerSuffixSize}
242+
display
243+
>
244+
{suffix}
245+
</Item>
246+
)}
215247
</div>
216248
);
217249

0 commit comments

Comments
 (0)