Skip to content

Commit c25ae82

Browse files
committed
fix
1 parent 54e2815 commit c25ae82

File tree

7 files changed

+631
-119
lines changed

7 files changed

+631
-119
lines changed

devtools-www/pages/full-demo.page.tsx

Lines changed: 365 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,365 @@
1+
import {
2+
InfiniteTable,
3+
DataSource,
4+
GroupRowsState,
5+
InfiniteTablePropColumnTypes,
6+
DataSourcePropRowSelection_MultiRow,
7+
InfiniteTableColumn,
8+
InfiniteTableColumnRenderValueParam,
9+
DataSourcePropAggregationReducers,
10+
DataSourceGroupBy,
11+
components,
12+
DataSourcePropFilterValue,
13+
} from '@infinite-table/infinite-react';
14+
import * as React from 'react';
15+
import { useState } from 'react';
16+
17+
const { CheckBox } = components;
18+
19+
type Developer = {
20+
id: number;
21+
firstName: string;
22+
lastName: string;
23+
country: string;
24+
city: string;
25+
currency: string;
26+
preferredLanguage: string;
27+
stack: string;
28+
canDesign: 'yes' | 'no';
29+
hobby: string;
30+
salary: number;
31+
age: number;
32+
};
33+
34+
const avgReducer = {
35+
initialValue: 0,
36+
reducer: (acc: number, sum: number) => acc + sum,
37+
done: (value: number, arr: any[]) =>
38+
arr.length ? Math.floor(value / arr.length) : 0,
39+
};
40+
const aggregationReducers: DataSourcePropAggregationReducers<Developer> = {
41+
salary: {
42+
field: 'salary',
43+
44+
...avgReducer,
45+
},
46+
age: {
47+
field: 'age',
48+
...avgReducer,
49+
},
50+
currency: {
51+
field: 'currency',
52+
initialValue: new Set<string>(),
53+
reducer: (acc: Set<string>, value: string) => {
54+
acc.add(value);
55+
return acc;
56+
},
57+
done: (value: Set<string>) => {
58+
return value.size > 1 ? 'Mixed' : value.values().next().value;
59+
},
60+
},
61+
canDesign: {
62+
field: 'canDesign',
63+
64+
initialValue: false,
65+
reducer: (acc: boolean | null, value: 'yes' | 'no') => {
66+
if (acc === null) {
67+
return acc;
68+
}
69+
if (acc === false && value === 'yes') {
70+
return null;
71+
}
72+
if (acc === true && value === 'no') {
73+
return null;
74+
}
75+
return acc;
76+
},
77+
},
78+
};
79+
80+
const flags = {
81+
'United States': '🇺🇸',
82+
Canada: '🇨🇦',
83+
France: '🇫🇷',
84+
Germany: '🇩🇪',
85+
'United Kingdom': '🇬🇧',
86+
'South Africa': '🇿🇦',
87+
'New Zealand': '🇳🇿',
88+
Sweden: '🇸🇪',
89+
China: '🇨🇳',
90+
Brazil: '🇧🇷',
91+
Turkey: '🇹🇷',
92+
Italy: '🇮🇹',
93+
India: '🇮🇳',
94+
Indonesia: '🇮🇩',
95+
Japan: '🇯🇵',
96+
Argentina: '🇦🇷',
97+
'Saudi Arabia': '🇸🇦',
98+
Mexico: '🇲🇽',
99+
'United Arab Emirates': '🇦🇪',
100+
};
101+
function getColumns(): Record<string, InfiniteTableColumn<Developer>> {
102+
return {
103+
age: {
104+
field: 'age',
105+
header: 'Age',
106+
type: 'number',
107+
defaultWidth: 100,
108+
renderValue: ({ value }) => value,
109+
},
110+
salary: {
111+
header: 'Compensation',
112+
field: 'salary',
113+
type: 'number',
114+
defaultWidth: 210,
115+
},
116+
currency: { field: 'currency', header: 'Currency', defaultWidth: 100 },
117+
preferredLanguage: {
118+
field: 'preferredLanguage',
119+
header: 'Programming Language',
120+
},
121+
122+
canDesign: {
123+
defaultWidth: 135,
124+
field: 'canDesign',
125+
header: 'Design Skills',
126+
renderMenuIcon: false,
127+
renderValue: ({ value }) => {
128+
return (
129+
<div style={{ display: 'flex', alignItems: 'center' }}>
130+
<CheckBox
131+
defaultChecked={value === null ? null : value === 'yes'}
132+
domProps={{
133+
style: {
134+
marginRight: 10,
135+
},
136+
}}
137+
/>
138+
{value === null ? 'Some' : value === 'yes' ? 'Yes' : 'No'}
139+
</div>
140+
);
141+
},
142+
},
143+
country: {
144+
field: 'country',
145+
header: 'Country',
146+
renderValue: ({ value }) => {
147+
return (
148+
<span>
149+
{(flags as any)[value] || null} {value}
150+
</span>
151+
);
152+
},
153+
},
154+
firstName: { field: 'firstName', header: 'First Name' },
155+
stack: { field: 'stack', header: 'Stack' },
156+
157+
city: {
158+
field: 'city',
159+
header: 'City',
160+
renderHeader: ({ column }) => `${column.computedVisibleIndex} City`,
161+
},
162+
};
163+
}
164+
165+
// 250+100+210+100+150+135+150+150+150+150
166+
// → 123.456,789
167+
const groupColumn: InfiniteTableColumn<Developer> = {
168+
header: 'Grouping',
169+
field: 'firstName',
170+
defaultWidth: 250,
171+
renderSelectionCheckBox: true,
172+
defaultFilterable: false,
173+
174+
// in this function we have access to collapsed info
175+
// and grouping info about the current row - see rowInfo.groupBy
176+
renderValue: ({
177+
value,
178+
rowInfo,
179+
}: InfiniteTableColumnRenderValueParam<Developer>) => {
180+
if (!rowInfo.isGroupRow) {
181+
return `${rowInfo.indexInAll} ${rowInfo.id} ${value}`;
182+
}
183+
const groupBy = rowInfo.groupBy || [];
184+
const collapsed = rowInfo.collapsed;
185+
const currentGroupBy = groupBy[groupBy.length - 1];
186+
187+
if (currentGroupBy?.field === 'age') {
188+
return `🥳 ${value}${collapsed ? ' 🤷‍♂️' : ''}`;
189+
}
190+
191+
return `${rowInfo.indexInAll} ${value}`;
192+
},
193+
};
194+
195+
const defaultGroupRowsState = new GroupRowsState({
196+
//make all groups collapsed by default
197+
collapsedRows: true,
198+
expandedRows: [
199+
['United States'],
200+
['United States', 'backend'],
201+
['France'],
202+
['Turkey'],
203+
],
204+
});
205+
206+
const columnTypes: InfiniteTablePropColumnTypes<Developer> = {
207+
number: {
208+
align: 'end',
209+
style: () => {
210+
return {};
211+
},
212+
renderValue: ({ value, data, rowInfo }) => {
213+
return new Intl.NumberFormat('en-US', {
214+
style: 'currency',
215+
currency:
216+
rowInfo.isGroupRow && rowInfo.data?.currency === 'Mixed'
217+
? 'USD'
218+
: data?.currency || 'USD',
219+
}).format(value);
220+
},
221+
},
222+
};
223+
224+
const defaultRowSelection: DataSourcePropRowSelection_MultiRow = {
225+
selectedRows: [['United States'], ['India'], ['France'], ['Turkey']],
226+
deselectedRows: [['United States', 'frontend']],
227+
defaultSelection: false,
228+
};
229+
230+
const defaultFilterValue: DataSourcePropFilterValue<Developer> = [
231+
{
232+
field: 'age',
233+
filter: {
234+
operator: 'gt',
235+
type: 'number',
236+
value: null,
237+
},
238+
},
239+
];
240+
241+
const domProps = {
242+
style: {
243+
// minHeight: '50vh',
244+
width: '90vw',
245+
height: '90vh',
246+
margin: 5,
247+
},
248+
};
249+
250+
export default function App() {
251+
const [{ min, max }, setMinMax] = useState({ min: 0, max: 0 });
252+
const columns = React.useMemo(() => {
253+
const cols = getColumns();
254+
255+
if (cols.salary) {
256+
cols.salary.render = ({ renderBag, value }) => {
257+
const increase: number = Math.abs(max - min);
258+
const percentage = ((value - min) / increase) * 100;
259+
const alpha = Number((percentage / 100).toPrecision(2)) + 0.2;
260+
261+
const backgroundColor = `rgba(255, 0, 0, ${alpha})`;
262+
return (
263+
<div
264+
style={{
265+
position: 'absolute',
266+
display: 'flex',
267+
alignItems: 'center',
268+
justifyContent: 'flex-end',
269+
top: 0,
270+
bottom: 0,
271+
left: 0,
272+
right: 0,
273+
backgroundColor,
274+
}}
275+
>
276+
{renderBag.all}
277+
</div>
278+
);
279+
};
280+
}
281+
282+
return cols;
283+
}, [min, max]);
284+
285+
const groupBy: DataSourceGroupBy<Developer>[] = React.useMemo(
286+
() => [
287+
{
288+
field: 'country',
289+
},
290+
{ field: 'stack' },
291+
],
292+
[],
293+
);
294+
295+
const [columnVisibility, setColumnVisibility] = useState<
296+
Record<string, false>
297+
>({});
298+
return (
299+
<>
300+
<button
301+
onClick={() =>
302+
setColumnVisibility({
303+
age: false,
304+
salary: false,
305+
currency: false,
306+
preferredLanguage: false,
307+
308+
canDesign: false,
309+
country: false,
310+
firstName: false,
311+
stack: false,
312+
313+
city: false,
314+
'group-by': false,
315+
})
316+
}
317+
>
318+
hide all
319+
</button>
320+
<DataSource<Developer>
321+
data={dataSource}
322+
primaryKey="id"
323+
defaultFilterValue={defaultFilterValue}
324+
filterMode="local"
325+
useGroupKeysForMultiRowSelection
326+
defaultRowSelection={defaultRowSelection}
327+
defaultSortInfo={{
328+
dir: -1,
329+
field: 'country',
330+
}}
331+
onDataArrayChange={(data) => {
332+
const min = Math.min(...data.map((data) => data.salary ?? 0));
333+
const max = Math.max(...data.map((data) => data.salary ?? 0));
334+
335+
setMinMax({ min, max });
336+
}}
337+
defaultGroupRowsState={defaultGroupRowsState}
338+
aggregationReducers={aggregationReducers}
339+
groupBy={groupBy}
340+
>
341+
<InfiniteTable<Developer>
342+
debugId="full-demo"
343+
groupRenderStrategy="single-column"
344+
defaultColumnPinning={{
345+
'group-by': true,
346+
}}
347+
columnVisibility={columnVisibility}
348+
domProps={domProps}
349+
defaultActiveRowIndex={0}
350+
groupColumn={groupColumn}
351+
licenseKey={process.env.NEXT_PUBLIC_INFINITE_LICENSE_KEY}
352+
columns={columns}
353+
columnTypes={columnTypes}
354+
columnDefaultWidth={150}
355+
/>
356+
</DataSource>
357+
</>
358+
);
359+
}
360+
361+
const dataSource = () => {
362+
return fetch(process.env.NEXT_PUBLIC_BASE_URL + '/developers100')
363+
.then((r) => r.json())
364+
.then((data: Developer[]) => data);
365+
};

0 commit comments

Comments
 (0)