@extend-ai/react-xlsx 0.8.3 → 0.8.6

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/README.md ADDED
@@ -0,0 +1,392 @@
1
+ # @extend-ai/react-xlsx
2
+
3
+ React components and hooks for rendering `.xlsx` workbooks in the browser.
4
+
5
+ `@extend-ai/react-xlsx` gives you:
6
+
7
+ - A drop-in `XlsxViewer` for workbook previews
8
+ - A provider/controller API for custom spreadsheet experiences
9
+ - Worksheet rendering with frozen panes, tables, merged cells, conditional formatting, sparklines, selection, resizing, copy/paste, and zoom
10
+ - Embedded worksheet images, shapes, form controls, charts, and chartsheet tabs
11
+ - Worker-backed parsing and large-file guardrails
12
+ - Thumbnail helpers for building sheet strips, previews, and navigation UIs
13
+ - TypeScript types for viewer state, workbook metadata, charts, images, tables, and render hooks
14
+
15
+ ## Install
16
+
17
+ ```bash
18
+ npm install @extend-ai/react-xlsx react react-dom
19
+ ```
20
+
21
+ ```bash
22
+ pnpm add @extend-ai/react-xlsx react react-dom
23
+ ```
24
+
25
+ `react` and `react-dom` are peer dependencies.
26
+
27
+ ## Main Entry Points
28
+
29
+ The package exports three useful levels of API:
30
+
31
+ 1. `XlsxViewer`
32
+ A ready-to-render workbook viewer with built-in toolbar, sheet tabs, grid rendering, charts, images, and selection state.
33
+
34
+ 2. `XlsxViewerProvider` + viewer hooks
35
+ Shared controller context for custom toolbars, side panels, thumbnail strips, or other UI around the workbook.
36
+
37
+ 3. `useXlsxViewerController`
38
+ A lower-level controller hook for fully controlled integrations.
39
+
40
+ ## Quick Start
41
+
42
+ ### Basic viewer
43
+
44
+ Use `XlsxViewer` when you want the smallest integration surface.
45
+
46
+ ```tsx
47
+ import * as React from "react";
48
+ import { XlsxViewer } from "@extend-ai/react-xlsx";
49
+
50
+ export function WorkbookPreview() {
51
+ const [file, setFile] = React.useState<ArrayBuffer | undefined>();
52
+ const [fileName, setFileName] = React.useState<string | undefined>();
53
+
54
+ return (
55
+ <div style={{ display: "grid", gap: 12 }}>
56
+ <input
57
+ type="file"
58
+ accept=".xlsx,.xlsm,.xls"
59
+ onChange={async (event) => {
60
+ const nextFile = event.target.files?.[0];
61
+ setFile(nextFile ? await nextFile.arrayBuffer() : undefined);
62
+ setFileName(nextFile?.name);
63
+ }}
64
+ />
65
+
66
+ <XlsxViewer
67
+ file={file}
68
+ fileName={fileName}
69
+ height={600}
70
+ emptyState="Choose a workbook to preview."
71
+ />
72
+ </div>
73
+ );
74
+ }
75
+ ```
76
+
77
+ You can also load a remote workbook with `src`:
78
+
79
+ ```tsx
80
+ import { XlsxViewer } from "@extend-ai/react-xlsx";
81
+
82
+ export function RemoteWorkbookPreview() {
83
+ return <XlsxViewer src="/reports/quarterly-model.xlsx" height="70vh" />;
84
+ }
85
+ ```
86
+
87
+ ### Provider and hooks
88
+
89
+ Use `XlsxViewerProvider` when custom UI needs access to the active workbook, selection, zoom, charts, images, tables, or editing commands.
90
+
91
+ ```tsx
92
+ import {
93
+ DefaultXlsxToolbar,
94
+ XlsxViewer,
95
+ XlsxViewerProvider,
96
+ useXlsxViewerSelection,
97
+ useXlsxViewerZoom,
98
+ } from "@extend-ai/react-xlsx";
99
+
100
+ function WorkbookStatus() {
101
+ const { activeCellAddress, selectedRangeAddress } = useXlsxViewerSelection();
102
+ const { zoomScale, zoomIn, zoomOut, canZoomIn, canZoomOut } = useXlsxViewerZoom();
103
+
104
+ return (
105
+ <div style={{ display: "flex", gap: 8, alignItems: "center" }}>
106
+ <span>{selectedRangeAddress ?? activeCellAddress ?? "No selection"}</span>
107
+ <button type="button" onClick={zoomOut} disabled={!canZoomOut}>
108
+ -
109
+ </button>
110
+ <span>{zoomScale}%</span>
111
+ <button type="button" onClick={zoomIn} disabled={!canZoomIn}>
112
+ +
113
+ </button>
114
+ </div>
115
+ );
116
+ }
117
+
118
+ export function WorkbookWorkspace({ file }: { file: ArrayBuffer }) {
119
+ return (
120
+ <XlsxViewerProvider file={file} fileName="model.xlsx">
121
+ <DefaultXlsxToolbar />
122
+ <WorkbookStatus />
123
+ <XlsxViewer height="70vh" showDefaultToolbar={false} />
124
+ </XlsxViewerProvider>
125
+ );
126
+ }
127
+ ```
128
+
129
+ ### Controlled controller
130
+
131
+ Use `useXlsxViewerController` when you want to own the controller instance and pass it into several components.
132
+
133
+ ```tsx
134
+ import {
135
+ XlsxViewer,
136
+ useXlsxViewerController,
137
+ } from "@extend-ai/react-xlsx";
138
+
139
+ export function ControlledWorkbook({ file }: { file: ArrayBuffer }) {
140
+ const controller = useXlsxViewerController({
141
+ file,
142
+ fileName: "forecast.xlsx",
143
+ readOnlyAboveBytes: 10 * 1024 * 1024,
144
+ });
145
+
146
+ return (
147
+ <div style={{ display: "grid", gap: 12 }}>
148
+ <button type="button" onClick={controller.exportXlsx} disabled={!controller.canExport}>
149
+ Export XLSX
150
+ </button>
151
+
152
+ <XlsxViewer controller={controller} height={640} />
153
+ </div>
154
+ );
155
+ }
156
+ ```
157
+
158
+ ## Useful Hooks
159
+
160
+ These hooks work inside `XlsxViewer` or `XlsxViewerProvider` context.
161
+
162
+ - `useXlsxViewer()` for full controller access
163
+ - `useXlsxViewerSelection()` for active cell and range state
164
+ - `useXlsxViewerZoom()` for zoom controls and limits
165
+ - `useXlsxViewerEditing()` for editing, undo/redo, fill, merge, clipboard, and export actions
166
+ - `useXlsxViewerTables()` for table metadata and table sorting
167
+ - `useXlsxViewerImages()` for embedded image and chart selection, movement, and resizing
168
+ - `useXlsxViewerCharts()` for chart and chartsheet state
169
+ - `useXlsxViewerThumbnails(options)` for painting worksheet thumbnails into your own canvases
170
+
171
+ ## Thumbnail Hook
172
+
173
+ `useXlsxViewerThumbnails` returns paint functions for each worksheet so you can build your own sheet strip or navigation UI.
174
+
175
+ ```tsx
176
+ import * as React from "react";
177
+ import {
178
+ XlsxViewerProvider,
179
+ useXlsxViewerThumbnails,
180
+ type XlsxSheetThumbnail,
181
+ } from "@extend-ai/react-xlsx";
182
+
183
+ function SheetThumbnail({ thumbnail }: { thumbnail: XlsxSheetThumbnail }) {
184
+ const canvasRef = React.useRef<HTMLCanvasElement | null>(null);
185
+
186
+ React.useEffect(() => {
187
+ thumbnail.paint(canvasRef.current);
188
+ }, [thumbnail]);
189
+
190
+ return (
191
+ <canvas
192
+ ref={canvasRef}
193
+ width={thumbnail.width}
194
+ height={thumbnail.height}
195
+ style={{ width: thumbnail.width, height: thumbnail.height }}
196
+ />
197
+ );
198
+ }
199
+
200
+ function SheetThumbnailStrip() {
201
+ const { thumbnails } = useXlsxViewerThumbnails({
202
+ includeHeaders: true,
203
+ resolution: { maxWidth: 180, maxHeight: 120 },
204
+ });
205
+
206
+ return (
207
+ <div style={{ display: "flex", gap: 12, overflowX: "auto" }}>
208
+ {thumbnails.map((thumbnail) => (
209
+ <SheetThumbnail
210
+ key={thumbnail.workbookSheetIndex}
211
+ thumbnail={thumbnail}
212
+ />
213
+ ))}
214
+ </div>
215
+ );
216
+ }
217
+
218
+ export function ThumbnailExample({ file }: { file: ArrayBuffer }) {
219
+ return (
220
+ <XlsxViewerProvider file={file}>
221
+ <SheetThumbnailStrip />
222
+ </XlsxViewerProvider>
223
+ );
224
+ }
225
+ ```
226
+
227
+ Notes:
228
+
229
+ - `resolution` accepts either a single max dimension or `{ maxWidth, maxHeight }`.
230
+ - Thumbnails preserve worksheet aspect ratio and paint into your supplied `<canvas>`.
231
+ - The current implementation renders a bounded top-left worksheet preview, including loaded embedded worksheet images, shapes, and form controls, but does not include charts.
232
+
233
+ ## Custom Rendering
234
+
235
+ The viewer exposes render props for common UI integration points.
236
+
237
+ ```tsx
238
+ import { XlsxViewer } from "@extend-ai/react-xlsx";
239
+
240
+ export function CustomWorkbook({ file }: { file: ArrayBuffer }) {
241
+ return (
242
+ <XlsxViewer
243
+ file={file}
244
+ height={600}
245
+ selectionColor="#2563eb"
246
+ renderImage={({ image, style }) => (
247
+ <img
248
+ src={image.src}
249
+ alt={image.description ?? image.name ?? ""}
250
+ style={{ ...style, objectFit: "contain" }}
251
+ />
252
+ )}
253
+ renderTableHeaderMenu={({ column, direction, sortAscending, sortDescending, triggerIcon, triggerProps }) => (
254
+ <span>
255
+ <button type="button" {...triggerProps}>
256
+ {triggerIcon}
257
+ </button>
258
+ <button type="button" onClick={sortAscending}>
259
+ Sort A to Z{direction === "ascending" ? " selected" : ""}
260
+ </button>
261
+ <button type="button" onClick={sortDescending}>
262
+ Sort Z to A{direction === "descending" ? " selected" : ""}
263
+ </button>
264
+ <span>{column.name}</span>
265
+ </span>
266
+ )}
267
+ />
268
+ );
269
+ }
270
+ ```
271
+
272
+ Apply `triggerProps` to the table-header trigger button so clicks do not leak into grid selection.
273
+
274
+ ## Large Files
275
+
276
+ `XlsxViewer` includes guardrails for large workbooks.
277
+
278
+ ```tsx
279
+ import { XlsxViewer } from "@extend-ai/react-xlsx";
280
+
281
+ export function LargeWorkbookPreview({ file }: { file: ArrayBuffer }) {
282
+ return (
283
+ <XlsxViewer
284
+ file={file}
285
+ maxFileSizeBytes={50 * 1024 * 1024}
286
+ readOnlyAboveBytes={10 * 1024 * 1024}
287
+ deferLoadingAboveBytes={20 * 1024 * 1024}
288
+ fileTooLargeState={({ displayFileName, fileSizeBytes, maxFileSizeBytes }) => (
289
+ <div>
290
+ <strong>{displayFileName}</strong> is too large to open here.
291
+ <div>
292
+ {Math.round(fileSizeBytes / (1024 * 1024))} MB of{" "}
293
+ {Math.round(maxFileSizeBytes / (1024 * 1024))} MB allowed
294
+ </div>
295
+ </div>
296
+ )}
297
+ />
298
+ );
299
+ }
300
+ ```
301
+
302
+ Notes:
303
+
304
+ - `maxFileSizeBytes` defaults to `25 MB`.
305
+ - `readOnlyAboveBytes` can disable mutation actions for larger files.
306
+ - `deferLoadingAboveBytes` waits for `controller.continueDeferredLoad()` before parsing files above the threshold.
307
+ - `useWorker` defaults to `true` when browser workers are available.
308
+
309
+ ## Viewer Props
310
+
311
+ `XlsxViewerProps` includes all controller options plus rendering options.
312
+
313
+ Common source and loading props:
314
+
315
+ - `file?: ArrayBuffer`
316
+ - `src?: string`
317
+ - `fileName?: string`
318
+ - `controller?: XlsxViewerController`
319
+ - `useWorker?: boolean`
320
+ - `maxFileSizeBytes?: number`
321
+ - `readOnly?: boolean`
322
+ - `readOnlyAboveBytes?: number`
323
+ - `deferLoadingAboveBytes?: number`
324
+ - `showHiddenSheets?: boolean`
325
+ - `skipXmlParsing?: boolean`
326
+
327
+ Common rendering props:
328
+
329
+ - `height?: React.CSSProperties["height"]`
330
+ - `className?: string`
331
+ - `isDark?: boolean`
332
+ - `rounded?: boolean`
333
+ - `showDefaultToolbar?: boolean`
334
+ - `toolbar?: React.ReactNode | ((controller: XlsxViewerController) => React.ReactNode)`
335
+ - `experimentalCanvas?: boolean`
336
+ - `enableGestureZoom?: boolean`
337
+ - `enableCanvasSelectionAnimation?: boolean`
338
+ - `allowResizeInReadOnly?: boolean`
339
+ - `selectionColor?: string`
340
+ - `selectionFillColor?: string`
341
+ - `selectionHeaderColor?: string`
342
+ - `showImages?: boolean`
343
+ - `emptyState?: React.ReactNode`
344
+ - `loadingState?: React.ReactNode`
345
+ - `errorState?: React.ReactNode | ((error: Error) => React.ReactNode)`
346
+ - `fileTooLargeState?: React.ReactNode | ((props: XlsxFileTooLargeRenderProps) => React.ReactNode)`
347
+ - `renderImage?: (props: XlsxImageRenderProps) => React.ReactNode`
348
+ - `renderImageSelection?: (props: XlsxImageSelectionRenderProps) => React.ReactNode`
349
+ - `renderChartLoading?: (props: XlsxChartLoadingRenderProps) => React.ReactNode`
350
+ - `renderTableHeaderMenu?: (props: XlsxTableHeaderMenuRenderProps) => React.ReactNode`
351
+
352
+ ## Workbook Support
353
+
354
+ Primary support is for OOXML `.xlsx` workbooks.
355
+
356
+ Supported worksheet features include:
357
+
358
+ - Frozen panes, merged cells, row and column sizing, hidden rows and columns, and gridline settings
359
+ - Cell styles, fills, borders, alignment, number formats, formulas, cached formula values, and cell controls
360
+ - Tables, table sorting, conditional formatting, data validation metadata, and sparklines
361
+ - Embedded images, shapes, form controls, worksheet charts, and chartsheet tabs
362
+ - Copy, paste, undo, redo, merge, unmerge, fill, CSV export, and XLSX export
363
+
364
+ Chart rendering supports common Excel chart families including column, bar, line, area, scatter, pie, doughnut, radar, bubble, stock, surface, waterfall, funnel, box-and-whisker, sunburst, treemap, region map, combo charts, and chartsheets.
365
+
366
+ Legacy `.xls` and macro-enabled `.xlsm` files have limited support. The viewer only displays workbook data that `@dukelib/sheets-wasm` can parse, and format-specific XML features may be missing or skipped.
367
+
368
+ ## Exported Types
369
+
370
+ The package exports the main types you are likely to use for custom integrations:
371
+
372
+ - `UseXlsxViewerControllerOptions`
373
+ - `XlsxViewerProps`
374
+ - `XlsxViewerProviderProps`
375
+ - `XlsxViewerController`
376
+ - `XlsxViewerSelection`
377
+ - `XlsxViewerZoom`
378
+ - `XlsxViewerEditing`
379
+ - `XlsxViewerTables`
380
+ - `XlsxViewerImages`
381
+ - `XlsxViewerCharts`
382
+ - `XlsxViewerThumbnails`
383
+ - `XlsxSheetThumbnail`
384
+ - `UseXlsxViewerThumbnailsOptions`
385
+ - `XlsxChart`, `XlsxChartSeries`, `XlsxChartAxis`, `XlsxChartsheet`
386
+ - `XlsxImage`, `XlsxImageRect`, `XlsxImageRenderProps`, `XlsxImageSelectionRenderProps`
387
+ - `XlsxTable`, `XlsxTableColumn`, `XlsxTableHeaderMenuRenderProps`
388
+ - `XlsxWorkbookTab`, `XlsxCellAddress`, `XlsxCellRange`
389
+
390
+ ## License
391
+
392
+ See the repository license for usage terms.