@particle-academy/fancy-sheets 0.6.1 → 0.6.3

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/dist/index.d.cts CHANGED
@@ -1,5 +1,5 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
- import { ReactNode } from 'react';
2
+ import React$1, { ReactNode } from 'react';
3
3
 
4
4
  /** Column-letter + row-number string, e.g. "A1", "BC42" */
5
5
  type CellAddress = string;
@@ -105,12 +105,14 @@ interface SelectionState {
105
105
  interface SpreadsheetContextMenuItem {
106
106
  /** Display label */
107
107
  label: string;
108
- /** Called with the active cell address when the item is clicked */
109
- onClick: (address: string) => void;
108
+ /** Called with the active cell address when the item is clicked (omit for submenu parents) */
109
+ onClick?: (address: string) => void;
110
110
  /** Whether the item is disabled — static or per-cell function */
111
111
  disabled?: boolean | ((address: string) => boolean);
112
112
  /** Render with danger styling */
113
113
  danger?: boolean;
114
+ /** Nested sub-items — renders as a submenu when present */
115
+ items?: SpreadsheetContextMenuItem[];
114
116
  }
115
117
  interface SpreadsheetProps {
116
118
  children: ReactNode;
@@ -184,13 +186,17 @@ declare namespace SpreadsheetGrid {
184
186
  var displayName: string;
185
187
  }
186
188
 
189
+ /** Toolbar button groups that can be toggled on/off */
190
+ type ToolbarButton = "undo" | "bold" | "align" | "freeze" | "format" | "decimals" | "formulaBar";
187
191
  interface SpreadsheetToolbarProps {
188
- children?: React.ReactNode;
192
+ children?: React$1.ReactNode;
189
193
  className?: string;
190
194
  /** Additional toolbar content appended after default buttons */
191
- extra?: React.ReactNode;
195
+ extra?: React$1.ReactNode;
196
+ /** Which built-in button groups to show (default: all). Omit to show everything. */
197
+ buttons?: ToolbarButton[];
192
198
  }
193
- declare function SpreadsheetToolbar({ children, className, extra }: SpreadsheetToolbarProps): react_jsx_runtime.JSX.Element;
199
+ declare function SpreadsheetToolbar({ children, className, extra, buttons: buttonsProp }: SpreadsheetToolbarProps): react_jsx_runtime.JSX.Element;
194
200
  declare namespace SpreadsheetToolbar {
195
201
  var displayName: string;
196
202
  }
@@ -263,10 +269,12 @@ interface SheetWorkbookProps {
263
269
  hideTabs?: boolean;
264
270
  /** Extra content appended to the default toolbar */
265
271
  toolbarExtra?: ReactNode;
272
+ /** Which built-in toolbar buttons to show (default: all). Pass [] for only custom extra. */
273
+ toolbarButtons?: ToolbarButton[];
266
274
  /** Custom context menu items */
267
275
  contextMenuItems?: SpreadsheetContextMenuItem[] | ((address: string) => SpreadsheetContextMenuItem[]);
268
276
  }
269
- declare function SheetWorkbook({ hideToolbar, hideTabs, toolbarExtra, contextMenuItems, ...props }: SheetWorkbookProps): react_jsx_runtime.JSX.Element;
277
+ declare function SheetWorkbook({ hideToolbar, hideTabs, toolbarExtra, toolbarButtons, contextMenuItems, ...props }: SheetWorkbookProps): react_jsx_runtime.JSX.Element;
270
278
  declare namespace SheetWorkbook {
271
279
  var displayName: string;
272
280
  }
@@ -295,4 +303,4 @@ declare function workbookToCSV(workbook: WorkbookData, sheetId?: string): string
295
303
  type FormulaRangeFunction = (args: CellValue[][]) => CellValue;
296
304
  declare function registerFunction(name: string, fn: FormulaRangeFunction): void;
297
305
 
298
- export { type CellAddress, type CellComment, type CellData, type CellFormat, type CellMap, type CellRange, type CellValue, type ColumnWidths, type FormulaRangeFunction, type MergedRegion, type SelectionState, Sheet, type SheetData, type SheetProps, SheetWorkbook, type SheetWorkbookProps, Spreadsheet, type SpreadsheetContextMenuItem, type SpreadsheetContextValue, type SpreadsheetProps, type TextAlign, type WorkbookData, columnToLetter, createEmptySheet, createEmptyWorkbook, csvToWorkbook, letterToColumn, parseAddress, parseCSV, registerFunction, stringifyCSV, toAddress, useSpreadsheet, workbookToCSV };
306
+ export { type CellAddress, type CellComment, type CellData, type CellFormat, type CellMap, type CellRange, type CellValue, type ColumnWidths, type FormulaRangeFunction, type MergedRegion, type SelectionState, Sheet, type SheetData, type SheetProps, SheetWorkbook, type SheetWorkbookProps, Spreadsheet, type SpreadsheetContextMenuItem, type SpreadsheetContextValue, type SpreadsheetProps, type TextAlign, type ToolbarButton, type WorkbookData, columnToLetter, createEmptySheet, createEmptyWorkbook, csvToWorkbook, letterToColumn, parseAddress, parseCSV, registerFunction, stringifyCSV, toAddress, useSpreadsheet, workbookToCSV };
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
- import { ReactNode } from 'react';
2
+ import React$1, { ReactNode } from 'react';
3
3
 
4
4
  /** Column-letter + row-number string, e.g. "A1", "BC42" */
5
5
  type CellAddress = string;
@@ -105,12 +105,14 @@ interface SelectionState {
105
105
  interface SpreadsheetContextMenuItem {
106
106
  /** Display label */
107
107
  label: string;
108
- /** Called with the active cell address when the item is clicked */
109
- onClick: (address: string) => void;
108
+ /** Called with the active cell address when the item is clicked (omit for submenu parents) */
109
+ onClick?: (address: string) => void;
110
110
  /** Whether the item is disabled — static or per-cell function */
111
111
  disabled?: boolean | ((address: string) => boolean);
112
112
  /** Render with danger styling */
113
113
  danger?: boolean;
114
+ /** Nested sub-items — renders as a submenu when present */
115
+ items?: SpreadsheetContextMenuItem[];
114
116
  }
115
117
  interface SpreadsheetProps {
116
118
  children: ReactNode;
@@ -184,13 +186,17 @@ declare namespace SpreadsheetGrid {
184
186
  var displayName: string;
185
187
  }
186
188
 
189
+ /** Toolbar button groups that can be toggled on/off */
190
+ type ToolbarButton = "undo" | "bold" | "align" | "freeze" | "format" | "decimals" | "formulaBar";
187
191
  interface SpreadsheetToolbarProps {
188
- children?: React.ReactNode;
192
+ children?: React$1.ReactNode;
189
193
  className?: string;
190
194
  /** Additional toolbar content appended after default buttons */
191
- extra?: React.ReactNode;
195
+ extra?: React$1.ReactNode;
196
+ /** Which built-in button groups to show (default: all). Omit to show everything. */
197
+ buttons?: ToolbarButton[];
192
198
  }
193
- declare function SpreadsheetToolbar({ children, className, extra }: SpreadsheetToolbarProps): react_jsx_runtime.JSX.Element;
199
+ declare function SpreadsheetToolbar({ children, className, extra, buttons: buttonsProp }: SpreadsheetToolbarProps): react_jsx_runtime.JSX.Element;
194
200
  declare namespace SpreadsheetToolbar {
195
201
  var displayName: string;
196
202
  }
@@ -263,10 +269,12 @@ interface SheetWorkbookProps {
263
269
  hideTabs?: boolean;
264
270
  /** Extra content appended to the default toolbar */
265
271
  toolbarExtra?: ReactNode;
272
+ /** Which built-in toolbar buttons to show (default: all). Pass [] for only custom extra. */
273
+ toolbarButtons?: ToolbarButton[];
266
274
  /** Custom context menu items */
267
275
  contextMenuItems?: SpreadsheetContextMenuItem[] | ((address: string) => SpreadsheetContextMenuItem[]);
268
276
  }
269
- declare function SheetWorkbook({ hideToolbar, hideTabs, toolbarExtra, contextMenuItems, ...props }: SheetWorkbookProps): react_jsx_runtime.JSX.Element;
277
+ declare function SheetWorkbook({ hideToolbar, hideTabs, toolbarExtra, toolbarButtons, contextMenuItems, ...props }: SheetWorkbookProps): react_jsx_runtime.JSX.Element;
270
278
  declare namespace SheetWorkbook {
271
279
  var displayName: string;
272
280
  }
@@ -295,4 +303,4 @@ declare function workbookToCSV(workbook: WorkbookData, sheetId?: string): string
295
303
  type FormulaRangeFunction = (args: CellValue[][]) => CellValue;
296
304
  declare function registerFunction(name: string, fn: FormulaRangeFunction): void;
297
305
 
298
- export { type CellAddress, type CellComment, type CellData, type CellFormat, type CellMap, type CellRange, type CellValue, type ColumnWidths, type FormulaRangeFunction, type MergedRegion, type SelectionState, Sheet, type SheetData, type SheetProps, SheetWorkbook, type SheetWorkbookProps, Spreadsheet, type SpreadsheetContextMenuItem, type SpreadsheetContextValue, type SpreadsheetProps, type TextAlign, type WorkbookData, columnToLetter, createEmptySheet, createEmptyWorkbook, csvToWorkbook, letterToColumn, parseAddress, parseCSV, registerFunction, stringifyCSV, toAddress, useSpreadsheet, workbookToCSV };
306
+ export { type CellAddress, type CellComment, type CellData, type CellFormat, type CellMap, type CellRange, type CellValue, type ColumnWidths, type FormulaRangeFunction, type MergedRegion, type SelectionState, Sheet, type SheetData, type SheetProps, SheetWorkbook, type SheetWorkbookProps, Spreadsheet, type SpreadsheetContextMenuItem, type SpreadsheetContextValue, type SpreadsheetProps, type TextAlign, type ToolbarButton, type WorkbookData, columnToLetter, createEmptySheet, createEmptyWorkbook, csvToWorkbook, letterToColumn, parseAddress, parseCSV, registerFunction, stringifyCSV, toAddress, useSpreadsheet, workbookToCSV };
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { createContext, memo, useCallback, useState, useContext, useRef, useEffect, useMemo, useReducer } from 'react';
1
+ import React, { createContext, memo, useCallback, useState, useContext, useRef, useEffect, useMemo, useReducer } from 'react';
2
2
  import { cn, ContextMenu } from '@particle-academy/react-fancy';
3
3
  import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
4
4
 
@@ -2033,6 +2033,26 @@ function tsvToCells(tsv) {
2033
2033
  const cols = Math.max(...values.map((v) => v.length));
2034
2034
  return { values, rows, cols };
2035
2035
  }
2036
+ function renderMenuItems(items, activeCell) {
2037
+ return items.map((item, i) => {
2038
+ if (item.items && item.items.length > 0) {
2039
+ return /* @__PURE__ */ jsxs(ContextMenu.Sub, { children: [
2040
+ /* @__PURE__ */ jsx(ContextMenu.SubTrigger, { children: item.label }),
2041
+ /* @__PURE__ */ jsx(ContextMenu.SubContent, { children: renderMenuItems(item.items, activeCell) })
2042
+ ] }, i);
2043
+ }
2044
+ return /* @__PURE__ */ jsx(
2045
+ ContextMenu.Item,
2046
+ {
2047
+ onClick: () => item.onClick?.(activeCell),
2048
+ disabled: typeof item.disabled === "function" ? item.disabled(activeCell) : item.disabled,
2049
+ danger: item.danger,
2050
+ children: item.label
2051
+ },
2052
+ i
2053
+ );
2054
+ });
2055
+ }
2036
2056
  function SpreadsheetGrid({ className }) {
2037
2057
  const {
2038
2058
  columnCount,
@@ -2252,25 +2272,17 @@ function SpreadsheetGrid({ className }) {
2252
2272
  if (!items || items.length === 0) return null;
2253
2273
  return /* @__PURE__ */ jsxs(Fragment, { children: [
2254
2274
  /* @__PURE__ */ jsx(ContextMenu.Separator, {}),
2255
- items.map((item, i) => /* @__PURE__ */ jsx(
2256
- ContextMenu.Item,
2257
- {
2258
- onClick: () => item.onClick(selection.activeCell),
2259
- disabled: typeof item.disabled === "function" ? item.disabled(selection.activeCell) : item.disabled,
2260
- danger: item.danger,
2261
- children: item.label
2262
- },
2263
- i
2264
- ))
2275
+ renderMenuItems(items, selection.activeCell)
2265
2276
  ] });
2266
2277
  })()
2267
2278
  ] })
2268
2279
  ] });
2269
2280
  }
2270
2281
  SpreadsheetGrid.displayName = "SpreadsheetGrid";
2282
+ var ALL_BUTTONS = ["undo", "bold", "align", "freeze", "format", "decimals", "formulaBar"];
2271
2283
  var btnClass = "inline-flex items-center justify-center rounded px-2 py-1 text-[12px] font-medium text-zinc-600 transition-colors hover:bg-zinc-100 disabled:opacity-40 dark:text-zinc-300 dark:hover:bg-zinc-800";
2272
2284
  var activeBtnClass = "bg-zinc-200 dark:bg-zinc-700";
2273
- function DefaultToolbar({ extra }) {
2285
+ function DefaultToolbar({ extra, buttons }) {
2274
2286
  const {
2275
2287
  selection,
2276
2288
  activeSheet,
@@ -2320,105 +2332,97 @@ function DefaultToolbar({ extra }) {
2320
2332
  }
2321
2333
  };
2322
2334
  const formulaBarValue = editingCell ? editValue : cell?.formula ? "=" + cell.formula : cell?.value != null ? String(cell.value) : "";
2323
- return /* @__PURE__ */ jsxs(Fragment, { children: [
2324
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-0.5 border-b border-zinc-200 px-1.5 py-1 dark:border-zinc-700", children: [
2325
- /* @__PURE__ */ jsx("button", { className: btnClass, onClick: undo, disabled: !canUndo || readOnly, title: "Undo (Ctrl+Z)", children: /* @__PURE__ */ jsxs("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
2326
- /* @__PURE__ */ jsx("polyline", { points: "1 4 1 10 7 10" }),
2327
- /* @__PURE__ */ jsx("path", { d: "M3.51 15a9 9 0 1 0 2.13-9.36L1 10" })
2328
- ] }) }),
2329
- /* @__PURE__ */ jsx("button", { className: btnClass, onClick: redo, disabled: !canRedo || readOnly, title: "Redo (Ctrl+Y)", children: /* @__PURE__ */ jsxs("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
2330
- /* @__PURE__ */ jsx("polyline", { points: "23 4 23 10 17 10" }),
2331
- /* @__PURE__ */ jsx("path", { d: "M20.49 15a9 9 0 1 1-2.12-9.36L23 10" })
2332
- ] }) }),
2333
- /* @__PURE__ */ jsx("div", { className: "mx-1 h-4 w-px bg-zinc-200 dark:bg-zinc-700" }),
2334
- /* @__PURE__ */ jsx(
2335
- "button",
2336
- {
2337
- className: cn(btnClass, isBold && activeBtnClass),
2338
- onClick: () => setCellFormat(selectedAddresses, { bold: !isBold }),
2339
- disabled: readOnly,
2340
- title: "Bold",
2341
- children: /* @__PURE__ */ jsx("span", { className: "font-bold", children: "B" })
2342
- }
2343
- ),
2344
- /* @__PURE__ */ jsx(
2345
- "button",
2346
- {
2347
- className: cn(btnClass, isItalic && activeBtnClass),
2348
- onClick: () => setCellFormat(selectedAddresses, { italic: !isItalic }),
2349
- disabled: readOnly,
2350
- title: "Italic",
2351
- children: /* @__PURE__ */ jsx("span", { className: "italic", children: "I" })
2352
- }
2353
- ),
2354
- /* @__PURE__ */ jsx("div", { className: "mx-1 h-4 w-px bg-zinc-200 dark:bg-zinc-700" }),
2355
- ["left", "center", "right"].map((align) => /* @__PURE__ */ jsx(
2356
- "button",
2357
- {
2358
- className: cn(btnClass, textAlign === align && activeBtnClass),
2359
- onClick: () => setCellFormat(selectedAddresses, { textAlign: align }),
2360
- disabled: readOnly,
2361
- title: `Align ${align}`,
2362
- children: /* @__PURE__ */ jsxs("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", children: [
2363
- /* @__PURE__ */ jsx("line", { x1: "3", y1: "6", x2: "21", y2: "6" }),
2364
- /* @__PURE__ */ jsx("line", { x1: align === "left" ? "3" : align === "center" ? "6" : "9", y1: "12", x2: align === "left" ? "15" : align === "center" ? "18" : "21", y2: "12" }),
2365
- /* @__PURE__ */ jsx("line", { x1: "3", y1: "18", x2: "21", y2: "18" })
2366
- ] })
2367
- },
2368
- align
2369
- )),
2370
- /* @__PURE__ */ jsx("div", { className: "mx-1 h-4 w-px bg-zinc-200 dark:bg-zinc-700" }),
2371
- /* @__PURE__ */ jsx(
2372
- "button",
2373
- {
2374
- className: cn(btnClass, activeSheet.frozenRows > 0 && activeBtnClass),
2375
- onClick: () => {
2376
- if (activeSheet.frozenRows > 0) {
2377
- setFrozenRows(0);
2378
- } else {
2379
- const row = selection.activeCell.match(/\d+/);
2380
- setFrozenRows(row ? parseInt(row[0], 10) - 1 || 1 : 1);
2381
- }
2382
- },
2383
- disabled: readOnly,
2384
- title: activeSheet.frozenRows > 0 ? `Unfreeze rows (${activeSheet.frozenRows} frozen)` : "Freeze rows above current cell",
2385
- children: /* @__PURE__ */ jsxs("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", children: [
2386
- /* @__PURE__ */ jsx("line", { x1: "3", y1: "9", x2: "21", y2: "9" }),
2387
- /* @__PURE__ */ jsx("line", { x1: "3", y1: "4", x2: "21", y2: "4" }),
2388
- /* @__PURE__ */ jsx("line", { x1: "3", y1: "14", x2: "21", y2: "14", strokeDasharray: "3 3" }),
2389
- /* @__PURE__ */ jsx("line", { x1: "3", y1: "19", x2: "21", y2: "19", strokeDasharray: "3 3" })
2390
- ] })
2391
- }
2392
- ),
2393
- /* @__PURE__ */ jsx(
2394
- "button",
2395
- {
2396
- className: cn(btnClass, activeSheet.frozenCols > 0 && activeBtnClass),
2397
- onClick: () => {
2398
- if (activeSheet.frozenCols > 0) {
2399
- setFrozenCols(0);
2400
- } else {
2401
- const colMatch = selection.activeCell.match(/^([A-Z]+)/);
2402
- if (colMatch) {
2403
- const col = colMatch[1].split("").reduce((acc, ch) => acc * 26 + ch.charCodeAt(0) - 64, 0) - 1;
2404
- setFrozenCols(col || 1);
2335
+ const divider = /* @__PURE__ */ jsx("div", { className: "mx-1 h-4 w-px bg-zinc-200 dark:bg-zinc-700" });
2336
+ const groups = [];
2337
+ if (buttons.has("undo")) {
2338
+ groups.push(
2339
+ /* @__PURE__ */ jsxs(React.Fragment, { children: [
2340
+ /* @__PURE__ */ jsx("button", { className: btnClass, onClick: undo, disabled: !canUndo || readOnly, title: "Undo (Ctrl+Z)", children: /* @__PURE__ */ jsxs("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
2341
+ /* @__PURE__ */ jsx("polyline", { points: "1 4 1 10 7 10" }),
2342
+ /* @__PURE__ */ jsx("path", { d: "M3.51 15a9 9 0 1 0 2.13-9.36L1 10" })
2343
+ ] }) }),
2344
+ /* @__PURE__ */ jsx("button", { className: btnClass, onClick: redo, disabled: !canRedo || readOnly, title: "Redo (Ctrl+Y)", children: /* @__PURE__ */ jsxs("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
2345
+ /* @__PURE__ */ jsx("polyline", { points: "23 4 23 10 17 10" }),
2346
+ /* @__PURE__ */ jsx("path", { d: "M20.49 15a9 9 0 1 1-2.12-9.36L23 10" })
2347
+ ] }) })
2348
+ ] }, "undo")
2349
+ );
2350
+ }
2351
+ if (buttons.has("bold")) {
2352
+ groups.push(
2353
+ /* @__PURE__ */ jsxs(React.Fragment, { children: [
2354
+ /* @__PURE__ */ jsx("button", { className: cn(btnClass, isBold && activeBtnClass), onClick: () => setCellFormat(selectedAddresses, { bold: !isBold }), disabled: readOnly, title: "Bold", children: /* @__PURE__ */ jsx("span", { className: "font-bold", children: "B" }) }),
2355
+ /* @__PURE__ */ jsx("button", { className: cn(btnClass, isItalic && activeBtnClass), onClick: () => setCellFormat(selectedAddresses, { italic: !isItalic }), disabled: readOnly, title: "Italic", children: /* @__PURE__ */ jsx("span", { className: "italic", children: "I" }) })
2356
+ ] }, "bold")
2357
+ );
2358
+ }
2359
+ if (buttons.has("align")) {
2360
+ groups.push(
2361
+ /* @__PURE__ */ jsx(React.Fragment, { children: ["left", "center", "right"].map((align) => /* @__PURE__ */ jsx("button", { className: cn(btnClass, textAlign === align && activeBtnClass), onClick: () => setCellFormat(selectedAddresses, { textAlign: align }), disabled: readOnly, title: `Align ${align}`, children: /* @__PURE__ */ jsxs("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", children: [
2362
+ /* @__PURE__ */ jsx("line", { x1: "3", y1: "6", x2: "21", y2: "6" }),
2363
+ /* @__PURE__ */ jsx("line", { x1: align === "left" ? "3" : align === "center" ? "6" : "9", y1: "12", x2: align === "left" ? "15" : align === "center" ? "18" : "21", y2: "12" }),
2364
+ /* @__PURE__ */ jsx("line", { x1: "3", y1: "18", x2: "21", y2: "18" })
2365
+ ] }) }, align)) }, "align")
2366
+ );
2367
+ }
2368
+ if (buttons.has("freeze")) {
2369
+ groups.push(
2370
+ /* @__PURE__ */ jsxs(React.Fragment, { children: [
2371
+ /* @__PURE__ */ jsx(
2372
+ "button",
2373
+ {
2374
+ className: cn(btnClass, activeSheet.frozenRows > 0 && activeBtnClass),
2375
+ onClick: () => {
2376
+ if (activeSheet.frozenRows > 0) {
2377
+ setFrozenRows(0);
2405
2378
  } else {
2406
- setFrozenCols(1);
2379
+ const row = selection.activeCell.match(/\d+/);
2380
+ setFrozenRows(row ? parseInt(row[0], 10) - 1 || 1 : 1);
2407
2381
  }
2408
- }
2409
- },
2410
- disabled: readOnly,
2411
- title: activeSheet.frozenCols > 0 ? `Unfreeze columns (${activeSheet.frozenCols} frozen)` : "Freeze columns left of current cell",
2412
- children: /* @__PURE__ */ jsxs("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", children: [
2413
- /* @__PURE__ */ jsx("line", { x1: "9", y1: "3", x2: "9", y2: "21" }),
2414
- /* @__PURE__ */ jsx("line", { x1: "4", y1: "3", x2: "4", y2: "21" }),
2415
- /* @__PURE__ */ jsx("line", { x1: "14", y1: "3", x2: "14", y2: "21", strokeDasharray: "3 3" }),
2416
- /* @__PURE__ */ jsx("line", { x1: "19", y1: "3", x2: "19", y2: "21", strokeDasharray: "3 3" })
2417
- ] })
2418
- }
2419
- ),
2420
- /* @__PURE__ */ jsx("div", { className: "mx-1 h-4 w-px bg-zinc-200 dark:bg-zinc-700" }),
2421
- /* @__PURE__ */ jsxs(
2382
+ },
2383
+ disabled: readOnly,
2384
+ title: activeSheet.frozenRows > 0 ? `Unfreeze rows (${activeSheet.frozenRows} frozen)` : "Freeze rows above current cell",
2385
+ children: /* @__PURE__ */ jsxs("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", children: [
2386
+ /* @__PURE__ */ jsx("line", { x1: "3", y1: "9", x2: "21", y2: "9" }),
2387
+ /* @__PURE__ */ jsx("line", { x1: "3", y1: "4", x2: "21", y2: "4" }),
2388
+ /* @__PURE__ */ jsx("line", { x1: "3", y1: "14", x2: "21", y2: "14", strokeDasharray: "3 3" }),
2389
+ /* @__PURE__ */ jsx("line", { x1: "3", y1: "19", x2: "21", y2: "19", strokeDasharray: "3 3" })
2390
+ ] })
2391
+ }
2392
+ ),
2393
+ /* @__PURE__ */ jsx(
2394
+ "button",
2395
+ {
2396
+ className: cn(btnClass, activeSheet.frozenCols > 0 && activeBtnClass),
2397
+ onClick: () => {
2398
+ if (activeSheet.frozenCols > 0) {
2399
+ setFrozenCols(0);
2400
+ } else {
2401
+ const colMatch = selection.activeCell.match(/^([A-Z]+)/);
2402
+ if (colMatch) {
2403
+ const col = colMatch[1].split("").reduce((acc, ch) => acc * 26 + ch.charCodeAt(0) - 64, 0) - 1;
2404
+ setFrozenCols(col || 1);
2405
+ } else {
2406
+ setFrozenCols(1);
2407
+ }
2408
+ }
2409
+ },
2410
+ disabled: readOnly,
2411
+ title: activeSheet.frozenCols > 0 ? `Unfreeze columns (${activeSheet.frozenCols} frozen)` : "Freeze columns left of current cell",
2412
+ children: /* @__PURE__ */ jsxs("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", children: [
2413
+ /* @__PURE__ */ jsx("line", { x1: "9", y1: "3", x2: "9", y2: "21" }),
2414
+ /* @__PURE__ */ jsx("line", { x1: "4", y1: "3", x2: "4", y2: "21" }),
2415
+ /* @__PURE__ */ jsx("line", { x1: "14", y1: "3", x2: "14", y2: "21", strokeDasharray: "3 3" }),
2416
+ /* @__PURE__ */ jsx("line", { x1: "19", y1: "3", x2: "19", y2: "21", strokeDasharray: "3 3" })
2417
+ ] })
2418
+ }
2419
+ )
2420
+ ] }, "freeze")
2421
+ );
2422
+ }
2423
+ if (buttons.has("format")) {
2424
+ groups.push(
2425
+ /* @__PURE__ */ jsx(React.Fragment, { children: /* @__PURE__ */ jsxs(
2422
2426
  "select",
2423
2427
  {
2424
2428
  className: "h-6 rounded border border-zinc-200 bg-transparent px-1 text-[11px] text-zinc-600 outline-none hover:border-zinc-300 dark:border-zinc-700 dark:text-zinc-400 dark:hover:border-zinc-600",
@@ -2436,39 +2440,32 @@ function DefaultToolbar({ extra }) {
2436
2440
  /* @__PURE__ */ jsx("option", { value: "datetime", children: "Date & Time" })
2437
2441
  ]
2438
2442
  }
2439
- ),
2440
- /* @__PURE__ */ jsxs(
2441
- "button",
2442
- {
2443
- className: btnClass,
2444
- onClick: () => setCellFormat(selectedAddresses, { decimals: Math.max(0, currentDecimals - 1) }),
2445
- disabled: readOnly || currentDecimals <= 0,
2446
- title: `Decrease decimal places (currently ${currentDecimals})`,
2447
- children: [
2448
- /* @__PURE__ */ jsx("span", { className: "text-[10px]", children: ".0" }),
2449
- /* @__PURE__ */ jsx("span", { className: "text-[8px]", children: "\u2190" })
2450
- ]
2451
- }
2452
- ),
2453
- /* @__PURE__ */ jsxs(
2454
- "button",
2455
- {
2456
- className: btnClass,
2457
- onClick: () => setCellFormat(selectedAddresses, { decimals: currentDecimals + 1 }),
2458
- disabled: readOnly,
2459
- title: `Increase decimal places (currently ${currentDecimals})`,
2460
- children: [
2461
- /* @__PURE__ */ jsx("span", { className: "text-[10px]", children: ".00" }),
2462
- /* @__PURE__ */ jsx("span", { className: "text-[8px]", children: "\u2192" })
2463
- ]
2464
- }
2465
- ),
2466
- extra && /* @__PURE__ */ jsxs(Fragment, { children: [
2467
- /* @__PURE__ */ jsx("div", { className: "mx-1 h-4 w-px bg-zinc-200 dark:bg-zinc-700" }),
2468
- extra
2469
- ] })
2470
- ] }),
2471
- /* @__PURE__ */ jsxs("div", { "data-fancy-sheets-formula-bar": "", className: "flex items-center gap-2 border-b border-zinc-200 px-2 py-1 dark:border-zinc-700", children: [
2443
+ ) }, "format")
2444
+ );
2445
+ }
2446
+ if (buttons.has("decimals")) {
2447
+ groups.push(
2448
+ /* @__PURE__ */ jsxs(React.Fragment, { children: [
2449
+ /* @__PURE__ */ jsxs("button", { className: btnClass, onClick: () => setCellFormat(selectedAddresses, { decimals: Math.max(0, currentDecimals - 1) }), disabled: readOnly || currentDecimals <= 0, title: `Decrease decimal places (currently ${currentDecimals})`, children: [
2450
+ /* @__PURE__ */ jsx("span", { className: "text-[10px]", children: ".0" }),
2451
+ /* @__PURE__ */ jsx("span", { className: "text-[8px]", children: "\u2190" })
2452
+ ] }),
2453
+ /* @__PURE__ */ jsxs("button", { className: btnClass, onClick: () => setCellFormat(selectedAddresses, { decimals: currentDecimals + 1 }), disabled: readOnly, title: `Increase decimal places (currently ${currentDecimals})`, children: [
2454
+ /* @__PURE__ */ jsx("span", { className: "text-[10px]", children: ".00" }),
2455
+ /* @__PURE__ */ jsx("span", { className: "text-[8px]", children: "\u2192" })
2456
+ ] })
2457
+ ] }, "decimals")
2458
+ );
2459
+ }
2460
+ if (extra) {
2461
+ groups.push(/* @__PURE__ */ jsx(React.Fragment, { children: extra }, "extra"));
2462
+ }
2463
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
2464
+ groups.length > 0 && /* @__PURE__ */ jsx("div", { className: "flex items-center gap-0.5 border-b border-zinc-200 px-1.5 py-1 dark:border-zinc-700", children: groups.map((group, i) => /* @__PURE__ */ jsxs(React.Fragment, { children: [
2465
+ i > 0 && divider,
2466
+ group
2467
+ ] }, i)) }),
2468
+ buttons.has("formulaBar") && /* @__PURE__ */ jsxs("div", { "data-fancy-sheets-formula-bar": "", className: "flex items-center gap-2 border-b border-zinc-200 px-2 py-1 dark:border-zinc-700", children: [
2472
2469
  /* @__PURE__ */ jsx("span", { className: "w-12 shrink-0 text-center text-[11px] font-medium text-zinc-500 dark:text-zinc-400", children: selection.activeCell }),
2473
2470
  /* @__PURE__ */ jsx("div", { className: "h-4 w-px bg-zinc-200 dark:bg-zinc-700" }),
2474
2471
  /* @__PURE__ */ jsx(
@@ -2485,8 +2482,9 @@ function DefaultToolbar({ extra }) {
2485
2482
  ] })
2486
2483
  ] });
2487
2484
  }
2488
- function SpreadsheetToolbar({ children, className, extra }) {
2489
- return /* @__PURE__ */ jsx("div", { "data-fancy-sheets-toolbar": "", className: cn("", className), children: children ?? /* @__PURE__ */ jsx(DefaultToolbar, { extra }) });
2485
+ function SpreadsheetToolbar({ children, className, extra, buttons: buttonsProp }) {
2486
+ const buttonsSet = new Set(buttonsProp ?? ALL_BUTTONS);
2487
+ return /* @__PURE__ */ jsx("div", { "data-fancy-sheets-toolbar": "", className: cn("", className), children: children ?? /* @__PURE__ */ jsx(DefaultToolbar, { extra, buttons: buttonsSet }) });
2490
2488
  }
2491
2489
  SpreadsheetToolbar.displayName = "SpreadsheetToolbar";
2492
2490
  function SpreadsheetSheetTabs({ className }) {
@@ -2685,11 +2683,12 @@ function SheetWorkbook({
2685
2683
  hideToolbar = false,
2686
2684
  hideTabs = false,
2687
2685
  toolbarExtra,
2686
+ toolbarButtons,
2688
2687
  contextMenuItems,
2689
2688
  ...props
2690
2689
  }) {
2691
2690
  return /* @__PURE__ */ jsxs(Spreadsheet, { ...props, contextMenuItems, children: [
2692
- !hideToolbar && /* @__PURE__ */ jsx(Spreadsheet.Toolbar, { extra: toolbarExtra }),
2691
+ !hideToolbar && /* @__PURE__ */ jsx(Spreadsheet.Toolbar, { extra: toolbarExtra, buttons: toolbarButtons }),
2693
2692
  /* @__PURE__ */ jsx(Spreadsheet.Grid, {}),
2694
2693
  !hideTabs && /* @__PURE__ */ jsx(Spreadsheet.SheetTabs, {})
2695
2694
  ] });