Skip to content

Commit 62ed5f0

Browse files
committed
doc: update case8
1 parent 07d982b commit 62ed5f0

3 files changed

Lines changed: 142 additions & 299 deletions

File tree

packages/docs/pages/api-reference/props.mdx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ Cell data is designed to be JSON-serializable (see [Serialization Design](/devel
2323

2424
**Important**: This is the initial value given to the sheet. Changes made after initialization will not be reflected, so please be careful.
2525

26+
> **Performance guideline:** For smooth performance, we recommend keeping the total number of cells at around 2,000,000 or fewer (e.g. 200,000 rows × 10 columns). Beyond that threshold, initial data generation and memory consumption may start to degrade the user experience. See [Example 8 — Large Dataset](/examples/case8) for a working demo at this scale.
27+
2628
### Address Format
2729

2830
The format uses cell addresses as keys for cell configuration objects. For example, to set the text color of D3 to green, use `{D3: {style: {color: "#00FF00"}}}`.

packages/docs/pages/examples/case8.component.tsx

Lines changed: 101 additions & 203 deletions
Original file line numberDiff line numberDiff line change
@@ -1,208 +1,79 @@
11
'use client';
22

33
import * as React from 'react';
4-
import { GridSheet, buildInitialCellsFromOrigin, makeBorder } from '@gridsheet/react-core';
4+
import { GridSheet, buildInitialCells } from '@gridsheet/react-core';
55
import { useSpellbook } from '@gridsheet/react-core/spellbook';
66

7-
export default function LargeDatasetDemo() {
8-
const [isLoading, setIsLoading] = React.useState(true);
9-
const [progress, setProgress] = React.useState(0);
7+
const NUM_ROWS = 200000;
8+
const NUM_COLS = 10;
9+
10+
const generateHugeData = () => {
11+
const data: any[][] = [];
12+
for (let i = 1; i <= NUM_ROWS; i++) {
13+
data.push([
14+
i,
15+
`Row ${i}`,
16+
Math.floor(Math.random() * 10000),
17+
Math.floor(Math.random() * 10000),
18+
Math.floor(Math.random() * 10000),
19+
Math.floor(Math.random() * 10000),
20+
Math.floor(Math.random() * 10000),
21+
['Alpha', 'Beta', 'Gamma', 'Delta'][Math.floor(Math.random() * 4)],
22+
['Active', 'Inactive', 'Pending'][Math.floor(Math.random() * 3)],
23+
`Note ${i}`,
24+
]);
25+
}
26+
return data;
27+
};
1028

29+
export default function LargeDatasetDemo() {
1130
const book = useSpellbook();
31+
const containerRef = React.useRef<HTMLDivElement>(null);
32+
const [ready, setReady] = React.useState(false);
33+
34+
const sheetHeight = 400;
35+
const sheetWidth = typeof window !== 'undefined' ? Math.min(800, window.innerWidth - 60) : 800;
36+
37+
const initialCells = React.useMemo(
38+
() =>
39+
buildInitialCells({
40+
matrices: { A1: generateHugeData() },
41+
cells: {
42+
A0: { label: 'ID' },
43+
B0: { label: 'Name' },
44+
C0: { label: 'Value1' },
45+
D0: { label: 'Value2' },
46+
E0: { label: 'Value3' },
47+
F0: { label: 'Value4' },
48+
G0: { label: 'Value5' },
49+
H0: { label: 'Group' },
50+
I0: { label: 'Status' },
51+
J0: { label: 'Notes' },
52+
},
53+
ensured: { numRows: NUM_ROWS, numCols: NUM_COLS },
54+
}),
55+
[],
56+
);
1257

13-
// Generate large dataset efficiently
14-
const generateLargeDataset = React.useCallback(() => {
15-
const rows = 10000;
16-
const cols = 100;
17-
const cells: { [address: string]: any } = {};
18-
19-
// Helper function to convert column number to letter(s)
20-
const getColumnLetter = (colNum: number): string => {
21-
let result = '';
22-
while (colNum > 0) {
23-
colNum--;
24-
result = String.fromCharCode(65 + (colNum % 26)) + result;
25-
colNum = Math.floor(colNum / 26);
26-
}
27-
return result;
28-
};
29-
30-
// Generate column labels
31-
for (let col = 1; col <= cols; col++) {
32-
const colLetter = getColumnLetter(col);
33-
cells[colLetter] = {
34-
label: `Column ${col}`,
35-
};
58+
React.useEffect(() => {
59+
const el = containerRef.current;
60+
if (!el) {
61+
return;
3662
}
37-
38-
// Generate data rows efficiently
39-
const batchSize = 1000;
40-
const totalBatches = Math.ceil(rows / batchSize);
41-
42-
const generateBatch = (batchIndex: number) => {
43-
const startRow = batchIndex * batchSize + 1;
44-
const endRow = Math.min(startRow + batchSize - 1, rows);
45-
46-
for (let row = startRow; row <= endRow; row++) {
47-
for (let col = 1; col <= cols; col++) {
48-
const colLetter = getColumnLetter(col);
49-
const address = `${colLetter}${row}`;
50-
51-
// Generate different types of data based on column
52-
let value: string | number;
53-
let style: any = {
54-
fontSize: '11px',
55-
padding: '2px',
56-
...makeBorder({ all: '1px solid #e5e7eb' }),
57-
};
58-
59-
if (col === 1) {
60-
// ID column
61-
value = row;
62-
style = {
63-
...style,
64-
fontWeight: 'bold',
65-
textAlign: 'center',
66-
};
67-
} else if (col === 2) {
68-
// Name column
69-
const names = ['Alice', 'Bob', 'Charlie', 'Diana', 'Eve', 'Frank', 'Grace', 'Henry', 'Ivy', 'Jack'];
70-
value = `${names[(row - 1) % names.length]} ${Math.floor((row - 1) / names.length) + 1}`;
71-
style = {
72-
...style,
73-
fontWeight: '500',
74-
};
75-
} else if (col === 3) {
76-
// Department column
77-
const departments = [
78-
'Engineering',
79-
'Marketing',
80-
'Sales',
81-
'HR',
82-
'Finance',
83-
'Operations',
84-
'Product',
85-
'Support',
86-
];
87-
value = departments[(row - 1) % departments.length];
88-
style = {
89-
...style,
90-
textAlign: 'center',
91-
fontSize: '10px',
92-
};
93-
} else if (col === 4) {
94-
// Salary column
95-
value = Math.floor(Math.random() * 50000) + 30000;
96-
style = {
97-
...style,
98-
textAlign: 'right',
99-
fontWeight: '500',
100-
};
101-
} else if (col === 5) {
102-
// Status column
103-
const statuses = ['Active', 'Inactive', 'Pending', 'Terminated'];
104-
value = statuses[(row - 1) % statuses.length];
105-
style = {
106-
...style,
107-
textAlign: 'center',
108-
fontSize: '10px',
109-
};
110-
} else if (col <= 10) {
111-
// Additional data columns
112-
value = Math.floor(Math.random() * 1000);
113-
style = {
114-
...style,
115-
textAlign: 'right',
116-
};
117-
} else {
118-
// Generic data for remaining columns
119-
value = Math.floor(Math.random() * 100);
120-
style = {
121-
...style,
122-
textAlign: 'center',
123-
};
124-
}
125-
126-
cells[address] = {
127-
value,
128-
style,
129-
};
130-
}
131-
}
132-
133-
// Update progress
134-
const newProgress = Math.round(((batchIndex + 1) / totalBatches) * 100);
135-
setProgress(newProgress);
136-
137-
// Schedule next batch
138-
if (batchIndex + 1 < totalBatches) {
139-
setTimeout(() => generateBatch(batchIndex + 1), 0);
140-
} else {
141-
setIsLoading(false);
63+
const observer = new MutationObserver(() => {
64+
if (el.querySelector('.gs-initialized')) {
65+
setReady(true);
66+
observer.disconnect();
14267
}
143-
};
144-
145-
// Start batch generation
146-
generateBatch(0);
147-
return cells;
68+
});
69+
observer.observe(el, { attributes: true, subtree: true, attributeFilter: ['class'] });
70+
// Check immediately in case already initialized
71+
if (el.querySelector('.gs-initialized')) {
72+
setReady(true);
73+
}
74+
return () => observer.disconnect();
14875
}, []);
14976

150-
const [initialCells, setInitialCells] = React.useState<{ [address: string]: any }>({
151-
defaultCol: { width: 80 },
152-
defaultRow: { height: 20 },
153-
});
154-
155-
React.useEffect(() => {
156-
const cells = generateLargeDataset();
157-
setInitialCells(cells);
158-
}, [generateLargeDataset]);
159-
160-
if (isLoading) {
161-
return (
162-
<div
163-
style={{
164-
display: 'flex',
165-
flexDirection: 'column',
166-
alignItems: 'center',
167-
justifyContent: 'center',
168-
height: '400px',
169-
backgroundColor: '#f8fafc',
170-
borderRadius: '8px',
171-
border: '1px solid #e5e7eb',
172-
}}
173-
>
174-
<div style={{ fontSize: '18px', fontWeight: 'bold', marginBottom: '20px', color: '#374151' }}>
175-
Generating Large Dataset...
176-
</div>
177-
<div
178-
style={{
179-
width: '300px',
180-
height: '20px',
181-
backgroundColor: '#e5e7eb',
182-
borderRadius: '10px',
183-
overflow: 'hidden',
184-
}}
185-
>
186-
<div
187-
style={{
188-
width: `${progress}%`,
189-
height: '100%',
190-
backgroundColor: '#3b82f6',
191-
transition: 'width 0.3s ease',
192-
borderRadius: '10px',
193-
}}
194-
/>
195-
</div>
196-
<div style={{ marginTop: '10px', fontSize: '14px', color: '#6b7280' }}>{progress}% Complete</div>
197-
<div style={{ marginTop: '20px', fontSize: '12px', color: '#9ca3af', textAlign: 'center' }}>
198-
Creating 10,000 rows × 100 columns
199-
<br />
200-
Total: 1,000,000 cells
201-
</div>
202-
</div>
203-
);
204-
}
205-
20677
return (
20778
<div
20879
style={{
@@ -214,17 +85,44 @@ export default function LargeDatasetDemo() {
21485
padding: '20px',
21586
}}
21687
>
217-
<GridSheet
218-
book={book}
219-
sheetName="large-dataset"
220-
initialCells={initialCells}
221-
options={{
222-
sheetHeight: 400,
223-
sheetWidth: typeof window !== 'undefined' ? Math.min(800, window.innerWidth - 60) : 800,
224-
sheetResize: 'both',
225-
}}
226-
/>
88+
<div ref={containerRef} style={{ position: 'relative', width: sheetWidth, height: sheetHeight }}>
89+
<GridSheet
90+
book={book}
91+
sheetName="large-dataset"
92+
initialCells={initialCells}
93+
options={{
94+
sheetHeight,
95+
sheetWidth,
96+
sheetResize: 'both',
97+
}}
98+
/>
99+
{!ready && (
100+
<div
101+
style={{
102+
position: 'absolute',
103+
inset: 0,
104+
background: 'rgba(255, 255, 255, 0.8)',
105+
display: 'flex',
106+
alignItems: 'center',
107+
justifyContent: 'center',
108+
zIndex: 200,
109+
}}
110+
>
111+
<div
112+
style={{
113+
width: 32,
114+
height: 32,
115+
border: '3px solid #e0e0e0',
116+
borderTopColor: '#0077ff',
117+
borderRadius: '50%',
118+
animation: 'case8-spin 0.8s linear infinite',
119+
}}
120+
/>
121+
</div>
122+
)}
123+
</div>
227124
<style>{`
125+
@keyframes case8-spin { to { transform: rotate(360deg); } }
228126
.gs-row-odd .gs-cell { background-color: #ffffff; }
229127
.gs-row-even .gs-cell { background-color: #f0f4f8; }
230128
`}</style>

0 commit comments

Comments
 (0)