@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.
Files changed (2) hide show
  1. package/docs/Spreadsheet.md +140 -77
  2. package/package.json +1 -1
@@ -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
- ## Basic Usage
12
+ ## Components
13
+
14
+ ### Compound API (full control)
13
15
 
14
16
  ```tsx
15
- <div style={{ height: 600 }}>
16
- <Spreadsheet>
17
- <Spreadsheet.Toolbar />
18
- <Spreadsheet.Grid />
19
- <Spreadsheet.SheetTabs />
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
- The component fills its container wrap it in a sized element (height is required).
24
+ All sub-components are optionalomit `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, formatting, and structural changes |
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 defaults |
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
- ### Spreadsheet.Grid
52
-
53
- The main editable cell grid. Handles navigation, selection, editing, keyboard shortcuts, and clipboard.
74
+ ### SheetWorkbook
54
75
 
55
- | Prop | Type | Description |
56
- |------|------|-------------|
57
- | className | `string` | Additional CSS classes |
76
+ All `Spreadsheet` props plus:
58
77
 
59
- ### Spreadsheet.SheetTabs
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
- Bottom tab bar for multi-sheet workbooks. Supports add/rename/delete/switch.
84
+ ### Sheet
62
85
 
63
- | Prop | Type | Description |
64
- |------|------|-------------|
65
- | className | `string` | Additional CSS classes |
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>; // sparse — only non-empty cells
85
- columnWidths: Record<number, number>; // sparse overrides
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; // raw entered value
103
- formula?: string; // original formula string (e.g. "=SUM(A1:A10)")
104
- computedValue?: CellValue; // evaluated result
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" | "number" | "percent";
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 newSheet = createEmptySheet("sheet-2", "Summary");
220
+ const sheet = createEmptySheet("sheet-2", "Summary");
128
221
  ```
129
222
 
130
223
  ## useSpreadsheet Hook
131
224
 
132
- Access full workbook state and actions from any child of `<Spreadsheet>`.
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
- See `SpreadsheetContextValue` in source for the full shape — key properties:
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 to a list of cells |
156
- | `navigate(dir, extend?)` | Move active cell (arrow-key equivalent) |
157
- | `startEdit(val?) / confirmEdit() / cancelEdit()` | Edit lifecycle |
158
- | `addSheet() / renameSheet() / deleteSheet() / setActiveSheet()` | Sheet management |
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() / redo()` | History navigation (50-step stack) |
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 / X / V | Copy / Cut / Paste |
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 how to register custom ones
278
+ - [Formulas](./formulas.md) — all 80+ built-in functions and custom formula registration
216
279
  - [CSV Import/Export](./csv.md) — `csvToWorkbook`, `workbookToCSV`, `parseCSV`, `stringifyCSV`
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@particle-academy/fancy-sheets",
3
- "version": "0.6.0",
3
+ "version": "0.6.1",
4
4
  "description": "Spreadsheet editor with formula engine, multi-sheet tabs, and full cell editing",
5
5
  "repository": {
6
6
  "type": "git",