@particle-academy/fancy-sheets 0.6.0 → 0.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/docs/Spreadsheet.md +140 -77
- package/package.json +1 -1
package/docs/Spreadsheet.md
CHANGED
|
@@ -1,27 +1,50 @@
|
|
|
1
1
|
# Spreadsheet
|
|
2
2
|
|
|
3
|
-
A full-featured spreadsheet component with formulas, formatting, selection, multi-sheet workbooks, clipboard, CSV import/export, and undo/redo. Compound API.
|
|
3
|
+
A full-featured spreadsheet component with formulas, formatting, selection, multi-sheet workbooks, clipboard, CSV import/export, comments, and undo/redo. Compound API.
|
|
4
4
|
|
|
5
5
|
## Import
|
|
6
6
|
|
|
7
7
|
```tsx
|
|
8
|
-
import { Spreadsheet, useSpreadsheet } from "@particle-academy/fancy-sheets";
|
|
8
|
+
import { Spreadsheet, Sheet, SheetWorkbook, useSpreadsheet } from "@particle-academy/fancy-sheets";
|
|
9
9
|
import "@particle-academy/fancy-sheets/styles.css";
|
|
10
10
|
```
|
|
11
11
|
|
|
12
|
-
##
|
|
12
|
+
## Components
|
|
13
|
+
|
|
14
|
+
### Compound API (full control)
|
|
13
15
|
|
|
14
16
|
```tsx
|
|
15
|
-
<
|
|
16
|
-
<Spreadsheet
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
</Spreadsheet>
|
|
21
|
-
</div>
|
|
17
|
+
<Spreadsheet data={workbook} onChange={setWorkbook}>
|
|
18
|
+
<Spreadsheet.Toolbar />
|
|
19
|
+
<Spreadsheet.Grid />
|
|
20
|
+
<Spreadsheet.SheetTabs />
|
|
21
|
+
</Spreadsheet>
|
|
22
22
|
```
|
|
23
23
|
|
|
24
|
-
|
|
24
|
+
All sub-components are optional — omit `Toolbar` or `SheetTabs` to hide them.
|
|
25
|
+
|
|
26
|
+
### SheetWorkbook (batteries-included)
|
|
27
|
+
|
|
28
|
+
Convenience wrapper with props to toggle chrome:
|
|
29
|
+
|
|
30
|
+
```tsx
|
|
31
|
+
<SheetWorkbook
|
|
32
|
+
data={workbook}
|
|
33
|
+
onChange={setWorkbook}
|
|
34
|
+
hideToolbar={false}
|
|
35
|
+
hideTabs={false}
|
|
36
|
+
toolbarExtra={<button>Custom</button>}
|
|
37
|
+
contextMenuItems={[{ label: "Highlight", onClick: (addr) => ... }]}
|
|
38
|
+
/>
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
### Sheet (lean single-sheet)
|
|
42
|
+
|
|
43
|
+
Takes `SheetData` directly — no workbook wrapper, no tabs, no toolbar:
|
|
44
|
+
|
|
45
|
+
```tsx
|
|
46
|
+
<Sheet data={sheetData} onChange={setSheetData} columnCount={6} rowCount={10} />
|
|
47
|
+
```
|
|
25
48
|
|
|
26
49
|
## Props
|
|
27
50
|
|
|
@@ -36,33 +59,36 @@ The component fills its container — wrap it in a sized element (height is requ
|
|
|
36
59
|
| rowCount | `number` | `100` | Number of rows |
|
|
37
60
|
| defaultColumnWidth | `number` | `100` | Default column width in px |
|
|
38
61
|
| rowHeight | `number` | `28` | Row height in px |
|
|
39
|
-
| readOnly | `boolean` | `false` | Disable editing
|
|
62
|
+
| readOnly | `boolean` | `false` | Disable editing |
|
|
63
|
+
| contextMenuItems | `ContextMenuItem[] \| (addr) => ContextMenuItem[]` | - | Custom right-click items |
|
|
40
64
|
| className | `string` | - | Additional CSS classes |
|
|
41
65
|
|
|
42
66
|
### Spreadsheet.Toolbar
|
|
43
67
|
|
|
44
|
-
Default toolbar with format buttons (bold, italic, align), undo/redo, and freeze controls. Pass `children` to replace entirely.
|
|
45
|
-
|
|
46
68
|
| Prop | Type | Description |
|
|
47
69
|
|------|------|-------------|
|
|
48
|
-
| children | `ReactNode` | Replace
|
|
70
|
+
| children | `ReactNode` | Replace default toolbar entirely |
|
|
71
|
+
| extra | `ReactNode` | Append content after default buttons (before formula bar) |
|
|
49
72
|
| className | `string` | Additional CSS classes |
|
|
50
73
|
|
|
51
|
-
###
|
|
52
|
-
|
|
53
|
-
The main editable cell grid. Handles navigation, selection, editing, keyboard shortcuts, and clipboard.
|
|
74
|
+
### SheetWorkbook
|
|
54
75
|
|
|
55
|
-
|
|
56
|
-
|------|------|-------------|
|
|
57
|
-
| className | `string` | Additional CSS classes |
|
|
76
|
+
All `Spreadsheet` props plus:
|
|
58
77
|
|
|
59
|
-
|
|
78
|
+
| Prop | Type | Default | Description |
|
|
79
|
+
|------|------|---------|-------------|
|
|
80
|
+
| hideToolbar | `boolean` | `false` | Hide the toolbar |
|
|
81
|
+
| hideTabs | `boolean` | `false` | Hide the sheet tabs |
|
|
82
|
+
| toolbarExtra | `ReactNode` | - | Extra toolbar content |
|
|
60
83
|
|
|
61
|
-
|
|
84
|
+
### Sheet
|
|
62
85
|
|
|
63
|
-
| Prop | Type | Description |
|
|
64
|
-
|
|
65
|
-
|
|
|
86
|
+
| Prop | Type | Default | Description |
|
|
87
|
+
|------|------|---------|-------------|
|
|
88
|
+
| data | `SheetData` | - | Single sheet data (controlled) |
|
|
89
|
+
| onChange | `(data: SheetData) => void` | - | Called on data change |
|
|
90
|
+
| contextMenuItems | see Spreadsheet | - | Custom right-click items |
|
|
91
|
+
| _(plus columnCount, rowCount, etc.)_ | | | |
|
|
66
92
|
|
|
67
93
|
## Data Model
|
|
68
94
|
|
|
@@ -81,8 +107,8 @@ interface WorkbookData {
|
|
|
81
107
|
interface SheetData {
|
|
82
108
|
id: string;
|
|
83
109
|
name: string;
|
|
84
|
-
cells: Record<CellAddress, CellData>;
|
|
85
|
-
columnWidths: Record<number, number>;
|
|
110
|
+
cells: Record<CellAddress, CellData>;
|
|
111
|
+
columnWidths: Record<number, number>;
|
|
86
112
|
mergedRegions: MergedRegion[];
|
|
87
113
|
columnFilters: Record<number, string>;
|
|
88
114
|
sortColumn?: number;
|
|
@@ -95,14 +121,12 @@ interface SheetData {
|
|
|
95
121
|
### CellData
|
|
96
122
|
|
|
97
123
|
```ts
|
|
98
|
-
type CellAddress = string; // "A1", "AA99", ...
|
|
99
|
-
type CellValue = string | number | boolean | null;
|
|
100
|
-
|
|
101
124
|
interface CellData {
|
|
102
|
-
value: CellValue;
|
|
103
|
-
formula?: string;
|
|
104
|
-
computedValue?: CellValue;
|
|
125
|
+
value: CellValue;
|
|
126
|
+
formula?: string;
|
|
127
|
+
computedValue?: CellValue;
|
|
105
128
|
format?: CellFormat;
|
|
129
|
+
comment?: CellComment;
|
|
106
130
|
}
|
|
107
131
|
```
|
|
108
132
|
|
|
@@ -113,38 +137,101 @@ interface CellFormat {
|
|
|
113
137
|
bold?: boolean;
|
|
114
138
|
italic?: boolean;
|
|
115
139
|
textAlign?: "left" | "center" | "right";
|
|
116
|
-
displayFormat?: "auto" | "date" | "datetime" | "
|
|
140
|
+
displayFormat?: "auto" | "text" | "number" | "date" | "datetime" | "percentage" | "currency";
|
|
117
141
|
decimals?: number;
|
|
142
|
+
backgroundColor?: string; // CSS color
|
|
143
|
+
color?: string; // font color
|
|
144
|
+
fontSize?: number; // px
|
|
145
|
+
borderTop?: string; // CSS color (renders 1px solid)
|
|
146
|
+
borderRight?: string;
|
|
147
|
+
borderBottom?: string;
|
|
148
|
+
borderLeft?: string;
|
|
149
|
+
}
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
When `backgroundColor` is set without `color`, text auto-defaults to dark gray (`#1f2937`) so content stays readable in both light and dark modes.
|
|
153
|
+
|
|
154
|
+
### CellComment
|
|
155
|
+
|
|
156
|
+
```ts
|
|
157
|
+
interface CellComment {
|
|
158
|
+
text: string;
|
|
159
|
+
author?: string;
|
|
160
|
+
color?: string; // default: "#f59e0b" (amber)
|
|
118
161
|
}
|
|
119
162
|
```
|
|
120
163
|
|
|
164
|
+
Comments render as:
|
|
165
|
+
- A small colored triangle in the cell's top-right corner
|
|
166
|
+
- A 1px border around the cell in the comment color
|
|
167
|
+
- A hover tooltip showing author + text
|
|
168
|
+
|
|
169
|
+
### Context Menu Items
|
|
170
|
+
|
|
171
|
+
```ts
|
|
172
|
+
interface SpreadsheetContextMenuItem {
|
|
173
|
+
label: string;
|
|
174
|
+
onClick: (address: string) => void;
|
|
175
|
+
disabled?: boolean | ((address: string) => boolean);
|
|
176
|
+
danger?: boolean;
|
|
177
|
+
}
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
Pass as array (static) or callback (dynamic per cell):
|
|
181
|
+
|
|
182
|
+
```tsx
|
|
183
|
+
// Static
|
|
184
|
+
<Spreadsheet contextMenuItems={[
|
|
185
|
+
{ label: "Highlight", onClick: (addr) => highlight(addr) },
|
|
186
|
+
]}>
|
|
187
|
+
|
|
188
|
+
// Dynamic (context-aware)
|
|
189
|
+
<Spreadsheet contextMenuItems={(addr) => {
|
|
190
|
+
const cell = sheet.cells[addr];
|
|
191
|
+
return cell?.comment
|
|
192
|
+
? [{ label: "Edit Comment", onClick: ... }, { label: "Delete Comment", danger: true, onClick: ... }]
|
|
193
|
+
: [{ label: "Add Comment", onClick: ... }];
|
|
194
|
+
}}>
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
Items appear after a separator below the built-in items (Copy, Paste, Clear, Freeze).
|
|
198
|
+
|
|
199
|
+
## Custom Formulas
|
|
200
|
+
|
|
201
|
+
```tsx
|
|
202
|
+
import { registerFunction } from "@particle-academy/fancy-sheets";
|
|
203
|
+
import type { FormulaRangeFunction } from "@particle-academy/fancy-sheets";
|
|
204
|
+
|
|
205
|
+
const myFormula: FormulaRangeFunction = (args) => {
|
|
206
|
+
const value = Number(args[0]?.[0] ?? 0);
|
|
207
|
+
return value > 100 ? "High" : "Low";
|
|
208
|
+
};
|
|
209
|
+
|
|
210
|
+
registerFunction("PRIORITY", myFormula);
|
|
211
|
+
// Usage in cells: =PRIORITY(A1)
|
|
212
|
+
```
|
|
213
|
+
|
|
121
214
|
## Helpers
|
|
122
215
|
|
|
123
216
|
```ts
|
|
124
217
|
import { createEmptyWorkbook, createEmptySheet } from "@particle-academy/fancy-sheets";
|
|
125
218
|
|
|
126
219
|
const workbook = createEmptyWorkbook();
|
|
127
|
-
const
|
|
220
|
+
const sheet = createEmptySheet("sheet-2", "Summary");
|
|
128
221
|
```
|
|
129
222
|
|
|
130
223
|
## useSpreadsheet Hook
|
|
131
224
|
|
|
132
|
-
Access
|
|
225
|
+
Access workbook state and actions from any child of `<Spreadsheet>`:
|
|
133
226
|
|
|
134
227
|
```tsx
|
|
135
|
-
import { useSpreadsheet } from "@particle-academy/fancy-sheets";
|
|
136
|
-
|
|
137
228
|
function ExportButton() {
|
|
138
229
|
const { workbook } = useSpreadsheet();
|
|
139
|
-
return (
|
|
140
|
-
<button onClick={() => download(workbookToCSV(workbook))}>
|
|
141
|
-
Export CSV
|
|
142
|
-
</button>
|
|
143
|
-
);
|
|
230
|
+
return <button onClick={() => download(workbookToCSV(workbook))}>Export</button>;
|
|
144
231
|
}
|
|
145
232
|
```
|
|
146
233
|
|
|
147
|
-
|
|
234
|
+
Key properties:
|
|
148
235
|
|
|
149
236
|
| Property | Description |
|
|
150
237
|
|----------|-------------|
|
|
@@ -152,33 +239,12 @@ See `SpreadsheetContextValue` in source for the full shape — key properties:
|
|
|
152
239
|
| `activeSheet` | Currently displayed SheetData |
|
|
153
240
|
| `selection` | `{ activeCell, ranges }` |
|
|
154
241
|
| `setCellValue(addr, val)` | Update a single cell |
|
|
155
|
-
| `setCellFormat(addrs[], fmt)` | Apply formatting
|
|
156
|
-
| `navigate(dir, extend?)` | Move active cell
|
|
157
|
-
| `startEdit
|
|
158
|
-
| `addSheet
|
|
242
|
+
| `setCellFormat(addrs[], fmt)` | Apply formatting |
|
|
243
|
+
| `navigate(dir, extend?)` | Move active cell |
|
|
244
|
+
| `startEdit / confirmEdit / cancelEdit` | Edit lifecycle |
|
|
245
|
+
| `addSheet / renameSheet / deleteSheet / setActiveSheet` | Sheet management |
|
|
159
246
|
| `setFrozenRows(n) / setFrozenCols(n)` | Freeze controls |
|
|
160
|
-
| `undo
|
|
161
|
-
| `canUndo / canRedo` | Booleans |
|
|
162
|
-
|
|
163
|
-
## Controlled Usage
|
|
164
|
-
|
|
165
|
-
```tsx
|
|
166
|
-
const [data, setData] = useState<WorkbookData>(createEmptyWorkbook());
|
|
167
|
-
|
|
168
|
-
<Spreadsheet data={data} onChange={setData}>
|
|
169
|
-
<Spreadsheet.Toolbar />
|
|
170
|
-
<Spreadsheet.Grid />
|
|
171
|
-
<Spreadsheet.SheetTabs />
|
|
172
|
-
</Spreadsheet>
|
|
173
|
-
```
|
|
174
|
-
|
|
175
|
-
## Read-Only Display
|
|
176
|
-
|
|
177
|
-
```tsx
|
|
178
|
-
<Spreadsheet defaultData={reportData} readOnly>
|
|
179
|
-
<Spreadsheet.Grid />
|
|
180
|
-
</Spreadsheet>
|
|
181
|
-
```
|
|
247
|
+
| `undo / redo / canUndo / canRedo` | History (50-step) |
|
|
182
248
|
|
|
183
249
|
## Keyboard Shortcuts
|
|
184
250
|
|
|
@@ -186,13 +252,13 @@ const [data, setData] = useState<WorkbookData>(createEmptyWorkbook());
|
|
|
186
252
|
|------|--------|
|
|
187
253
|
| Arrow keys | Move active cell |
|
|
188
254
|
| Shift + arrows | Extend selection |
|
|
189
|
-
| Ctrl/Cmd + arrows | Jump to edge |
|
|
190
255
|
| Enter / F2 | Start editing |
|
|
191
256
|
| Esc | Cancel edit |
|
|
192
257
|
| Tab / Shift+Tab | Move right/left |
|
|
193
|
-
| Ctrl/Cmd + C /
|
|
258
|
+
| Ctrl/Cmd + C / V | Copy / Paste |
|
|
194
259
|
| Ctrl/Cmd + Z / Y | Undo / Redo |
|
|
195
260
|
| Ctrl/Cmd + B / I | Bold / Italic |
|
|
261
|
+
| Delete / Backspace | Clear cell |
|
|
196
262
|
|
|
197
263
|
## Data Attributes
|
|
198
264
|
|
|
@@ -202,15 +268,12 @@ const [data, setData] = useState<WorkbookData>(createEmptyWorkbook());
|
|
|
202
268
|
| `data-fancy-sheets-toolbar` | Toolbar |
|
|
203
269
|
| `data-fancy-sheets-formula-bar` | Formula bar |
|
|
204
270
|
| `data-fancy-sheets-grid` | Grid container |
|
|
205
|
-
| `data-fancy-sheets-column-headers` | Column header row |
|
|
206
|
-
| `data-fancy-sheets-row-header` | Row header cell |
|
|
207
271
|
| `data-fancy-sheets-cell` | Individual cell |
|
|
208
272
|
| `data-fancy-sheets-cell-editor` | Active edit input |
|
|
209
273
|
| `data-fancy-sheets-selection` | Selection overlay |
|
|
210
|
-
| `data-fancy-sheets-resize-handle` | Column resize handle |
|
|
211
274
|
| `data-fancy-sheets-tabs` | Sheet tabs |
|
|
212
275
|
|
|
213
276
|
## See Also
|
|
214
277
|
|
|
215
|
-
- [Formulas](./formulas.md) — all 80+ built-in functions and
|
|
278
|
+
- [Formulas](./formulas.md) — all 80+ built-in functions and custom formula registration
|
|
216
279
|
- [CSV Import/Export](./csv.md) — `csvToWorkbook`, `workbookToCSV`, `parseCSV`, `stringifyCSV`
|