@ricsam/react-spreadsheets 0.0.2 → 0.0.4

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 CHANGED
@@ -88,11 +88,11 @@ top of `FormulaSheet`.
88
88
 
89
89
  ## Theming
90
90
 
91
- All colors, fonts and sizes are CSS custom properties declared on `.rsp-root`.
92
- Override them anywhere in your cascade:
91
+ All colors, fonts and sizes are CSS custom properties declared on `:root`, so
92
+ you can override them from **any** ancestor of the grid:
93
93
 
94
94
  ```css
95
- .rsp-root {
95
+ .my-panel {
96
96
  --rsp-accent: #7c3aed;
97
97
  --rsp-bg: #ffffff;
98
98
  --rsp-header-bg: #f4f4f5;
@@ -100,9 +100,42 @@ Override them anywhere in your cascade:
100
100
  }
101
101
  ```
102
102
 
103
- Light and dark are resolved automatically with `light-dark()`. To pin a subtree
104
- to one mode, set `color-scheme: light` (or `dark`) on `.rsp-root`; canvas
105
- gridlines follow the same setting.
103
+ ### Light and dark
104
+
105
+ By default the grid follows the user's OS preference.
106
+
107
+ The grid deliberately does **not** declare `color-scheme` on `.rsp-root`, so a
108
+ pin you set on an ancestor is respected. Pin a subtree with plain CSS:
109
+
110
+ ```css
111
+ .workbook-stage { color-scheme: light; } /* always light */
112
+ .workbook-stage { color-scheme: dark; } /* always dark */
113
+ ```
114
+
115
+ or with the bundled helper classes:
116
+
117
+ ```tsx
118
+ <FormulaWorkbook className="rsp-theme-dark" ... />
119
+ ```
120
+
121
+ Canvas gridlines and every DOM surface follow the same resolved scheme. The
122
+ component resolves the inherited `color-scheme` and reflects it on the grid root
123
+ as `data-rsp-scheme`, which drives the `--rsp-light` / `--rsp-dark` token
124
+ switches.
125
+
126
+ > **Note** — the stylesheet intentionally avoids `light-dark()`. Bundlers that
127
+ > target browsers without native support (Vite's Lightning CSS, for example)
128
+ > lower it to switch variables that only exist next to a `color-scheme`
129
+ > declaration, which makes shared-root tokens compute to an invalid value — in
130
+ > production builds only. An equivalent space-toggle is used instead.
131
+
132
+ ### Cell fills stay readable
133
+
134
+ A `backgroundColor` coming from your document model is usually a single literal
135
+ color with no dark-mode variant. To stop light fills from pairing with the dark
136
+ theme's near-white text, the grid derives a readable ink for any cell that sets
137
+ a background but no explicit `color`. Set `color` yourself to opt out, and use
138
+ `getContrastingTextColor` if you want the same behaviour elsewhere.
106
139
 
107
140
  Per-cell styling is done in JS:
108
141
 
package/dist/cjs/lib.cjs CHANGED
@@ -92,6 +92,7 @@ __export(exports_lib, {
92
92
  getCellReference: () => getCellReference,
93
93
  getCellSnapAnchorFromRect: () => getCellSnapAnchorFromRect,
94
94
  getColumnEdgePosition: () => getColumnEdgePosition,
95
+ getContrastingTextColor: () => getContrastingTextColor,
95
96
  getNearestColumnEdgeIndex: () => getNearestColumnEdgeIndex,
96
97
  getNearestRowEdgeIndex: () => getNearestRowEdgeIndex,
97
98
  getRectFromCellSnapAnchor: () => getRectFromCellSnapAnchor,
@@ -105,7 +106,9 @@ __export(exports_lib, {
105
106
  normalizeCellSnapAnchor: () => normalizeCellSnapAnchor,
106
107
  onCellEditError: () => onCellEditError,
107
108
  parseCellReference: () => parseCellReference,
109
+ parseCssColor: () => parseCssColor,
108
110
  queueCellEditError: () => queueCellEditError,
111
+ relativeLuminance: () => relativeLuminance,
109
112
  resizeCellSnapAnchorToNearestEdges: () => resizeCellSnapAnchorToNearestEdges,
110
113
  rowToLetter: () => rowToLetter,
111
114
  useClickAway: () => useClickAway,
@@ -174,6 +177,85 @@ function useColorScheme(element) {
174
177
  return scheme;
175
178
  }
176
179
 
180
+ // src/utils/contrast-text.ts
181
+ var DARK_INK = "#101828";
182
+ var LIGHT_INK = "#ffffff";
183
+ var NAMED_COLORS = {
184
+ white: [255, 255, 255],
185
+ black: [0, 0, 0],
186
+ red: [255, 0, 0],
187
+ green: [0, 128, 0],
188
+ blue: [0, 0, 255],
189
+ yellow: [255, 255, 0],
190
+ orange: [255, 165, 0],
191
+ purple: [128, 0, 128],
192
+ gray: [128, 128, 128],
193
+ grey: [128, 128, 128],
194
+ silver: [192, 192, 192]
195
+ };
196
+ var clamp255 = (value) => Math.min(255, Math.max(0, value));
197
+ function parseCssColor(input) {
198
+ const value = input.trim().toLowerCase();
199
+ if (!value || value === "transparent" || value === "currentcolor" || value === "inherit") {
200
+ return;
201
+ }
202
+ if (value.includes("var(") || value.includes("color-mix(") || value.includes("gradient")) {
203
+ return;
204
+ }
205
+ const named = NAMED_COLORS[value];
206
+ if (named) {
207
+ return { r: named[0], g: named[1], b: named[2], a: 1 };
208
+ }
209
+ if (value.startsWith("#")) {
210
+ const hex = value.slice(1);
211
+ const expand = (part) => parseInt(part + part, 16);
212
+ if (hex.length === 3 || hex.length === 4) {
213
+ const r = expand(hex[0]);
214
+ const g = expand(hex[1]);
215
+ const b = expand(hex[2]);
216
+ const a = hex.length === 4 ? expand(hex[3]) / 255 : 1;
217
+ return Number.isNaN(r + g + b) ? undefined : { r, g, b, a };
218
+ }
219
+ if (hex.length === 6 || hex.length === 8) {
220
+ const r = parseInt(hex.slice(0, 2), 16);
221
+ const g = parseInt(hex.slice(2, 4), 16);
222
+ const b = parseInt(hex.slice(4, 6), 16);
223
+ const a = hex.length === 8 ? parseInt(hex.slice(6, 8), 16) / 255 : 1;
224
+ return Number.isNaN(r + g + b) ? undefined : { r, g, b, a };
225
+ }
226
+ return;
227
+ }
228
+ const rgbMatch = value.match(/^rgba?\(([^)]+)\)$/);
229
+ if (rgbMatch) {
230
+ const parts = rgbMatch[1].split(/[\s,/]+/).filter(Boolean);
231
+ if (parts.length < 3)
232
+ return;
233
+ const toChannel = (part) => part.endsWith("%") ? parseFloat(part) / 100 * 255 : parseFloat(part);
234
+ const r = toChannel(parts[0]);
235
+ const g = toChannel(parts[1]);
236
+ const b = toChannel(parts[2]);
237
+ const alphaPart = parts[3];
238
+ const a = alphaPart === undefined ? 1 : alphaPart.endsWith("%") ? parseFloat(alphaPart) / 100 : parseFloat(alphaPart);
239
+ if ([r, g, b, a].some((channel) => Number.isNaN(channel)))
240
+ return;
241
+ return { r: clamp255(r), g: clamp255(g), b: clamp255(b), a };
242
+ }
243
+ return;
244
+ }
245
+ function relativeLuminance({ r, g, b }) {
246
+ const channel = (value) => {
247
+ const srgb = value / 255;
248
+ return srgb <= 0.03928 ? srgb / 12.92 : Math.pow((srgb + 0.055) / 1.055, 2.4);
249
+ };
250
+ return 0.2126 * channel(r) + 0.7152 * channel(g) + 0.0722 * channel(b);
251
+ }
252
+ function getContrastingTextColor(background) {
253
+ const color = parseCssColor(background);
254
+ if (!color || color.a < 0.5)
255
+ return;
256
+ return relativeLuminance(color) > 0.45 ? DARK_INK : LIGHT_INK;
257
+ }
258
+
177
259
  // src/utils/cn.ts
178
260
  function cn(...values) {
179
261
  const out = [];
@@ -1352,16 +1434,27 @@ var resizeCellSnapAnchorToNearestEdges = (anchor, rect, handle, columnWidths, ro
1352
1434
 
1353
1435
  // src/spreadsheet/spreadsheet.tsx
1354
1436
  var jsx_runtime3 = require("react/jsx-runtime");
1355
- var resolveGridLineColor = (element, colorScheme) => {
1356
- const fallback = colorScheme === "dark" ? "#4b5563" : "#e1e5e9";
1357
- if (!element || typeof getComputedStyle !== "function") {
1437
+ var resolveGridLineColor = (probe, colorScheme) => {
1438
+ const fallback = colorScheme === "dark" ? "#232c3d" : "#e8ebf1";
1439
+ if (!probe || typeof getComputedStyle !== "function") {
1358
1440
  return fallback;
1359
1441
  }
1360
- const declared = getComputedStyle(element).getPropertyValue("--rsp-gridline").trim();
1361
- if (!declared || declared.includes("light-dark(")) {
1442
+ const resolved = getComputedStyle(probe).color?.trim();
1443
+ if (!resolved || resolved.includes("light-dark(") || resolved === "rgba(0, 0, 0, 0)") {
1362
1444
  return fallback;
1363
1445
  }
1364
- return declared;
1446
+ return resolved;
1447
+ };
1448
+ var withReadableTextColor = (style) => {
1449
+ if (!style?.backgroundColor || style.color)
1450
+ return style;
1451
+ const background = style.backgroundColor;
1452
+ if (typeof background !== "string")
1453
+ return style;
1454
+ const color = getContrastingTextColor(background);
1455
+ if (!color)
1456
+ return style;
1457
+ return { ...style, color };
1365
1458
  };
1366
1459
  var hasBorderStyle = (style) => Boolean(style?.borderColor || style?.borderTopColor || style?.borderRightColor || style?.borderBottomColor || style?.borderLeftColor);
1367
1460
  var CellComponent = import_react4.default.memo(({
@@ -1404,7 +1497,7 @@ var CellComponent = import_react4.default.memo(({
1404
1497
  isSelected,
1405
1498
  isBeingEdited: isBeingEdited || false
1406
1499
  };
1407
- const customStyle = customCellStyle?.(cellStyleCallbackData);
1500
+ const customStyle = withReadableTextColor(customCellStyle?.(cellStyleCallbackData));
1408
1501
  const canHaveFillHandle = import_selection_manager.useSelectionManager(selectionManager, () => {
1409
1502
  return selectionManager.canCellHaveFillHandle({
1410
1503
  row: rowIndex,
@@ -1436,7 +1529,8 @@ var CellComponent = import_react4.default.memo(({
1436
1529
  top: y,
1437
1530
  width: getColumnWidth(col) - 1,
1438
1531
  height: getRowHeight(row) - 1,
1439
- ...customStyle
1532
+ ...customStyle,
1533
+ ...isBeingEdited ? { backgroundColor: undefined, color: undefined } : null
1440
1534
  },
1441
1535
  children: [
1442
1536
  isBeingEdited ? /* @__PURE__ */ jsx_runtime3.jsx("textarea", {
@@ -1686,6 +1780,7 @@ var Spreadsheet = import_react4.forwardRef(({
1686
1780
  const [{ width, height }, containerRef] = useResizeObserver();
1687
1781
  const [containerDivRef, setContainerDivRef] = import_react4.useState(null);
1688
1782
  const colorScheme = useColorScheme(containerDivRef);
1783
+ const gridLineProbeRef = import_react4.useRef(null);
1689
1784
  const selectionManager = import_selection_manager.useInitializeSelectionManager({
1690
1785
  getNumCols: () => ({ type: "infinity" }),
1691
1786
  getNumRows: () => ({ type: "infinity" }),
@@ -1879,7 +1974,7 @@ var Spreadsheet = import_react4.forwardRef(({
1879
1974
  if (!ctx)
1880
1975
  return;
1881
1976
  ctx.clearRect(0, 0, width, height);
1882
- ctx.strokeStyle = resolveGridLineColor(canvasRef.current, colorScheme);
1977
+ ctx.strokeStyle = resolveGridLineColor(gridLineProbeRef.current, colorScheme);
1883
1978
  ctx.lineWidth = 1;
1884
1979
  let x = gridDims.headerWidth;
1885
1980
  let colIndex = 0;
@@ -2627,6 +2722,7 @@ var Spreadsheet = import_react4.forwardRef(({
2627
2722
  }), [selectionManager]);
2628
2723
  return /* @__PURE__ */ jsx_runtime3.jsx("div", {
2629
2724
  className: cn("rsp-root", className),
2725
+ "data-rsp-scheme": colorScheme,
2630
2726
  style: { position: "relative", ...style },
2631
2727
  ...containerProps,
2632
2728
  children: /* @__PURE__ */ jsx_runtime3.jsx("div", {
@@ -2642,6 +2738,18 @@ var Spreadsheet = import_react4.forwardRef(({
2642
2738
  },
2643
2739
  tabIndex: 0,
2644
2740
  children: [
2741
+ /* @__PURE__ */ jsx_runtime3.jsx("span", {
2742
+ ref: gridLineProbeRef,
2743
+ "aria-hidden": "true",
2744
+ style: {
2745
+ position: "absolute",
2746
+ width: 0,
2747
+ height: 0,
2748
+ visibility: "hidden",
2749
+ pointerEvents: "none",
2750
+ color: "var(--rsp-gridline)"
2751
+ }
2752
+ }),
2645
2753
  /* @__PURE__ */ jsx_runtime3.jsx("canvas", {
2646
2754
  ref: canvasRef,
2647
2755
  className: "rsp-canvas",
@@ -2818,7 +2926,15 @@ var Spreadsheet = import_react4.forwardRef(({
2818
2926
  Spreadsheet.displayName = "Spreadsheet";
2819
2927
  // src/workbook/workbook.tsx
2820
2928
  var import_formula_engine2 = require("@ricsam/formula-engine");
2821
- var import_react5 = __toESM(require("react"));
2929
+ var import_react6 = __toESM(require("react"));
2930
+
2931
+ // src/utils/use-scheme-root.ts
2932
+ var import_react5 = require("react");
2933
+ function useSchemeRoot() {
2934
+ const [element, setElement] = import_react5.useState(null);
2935
+ const scheme = useColorScheme(element);
2936
+ return { ref: setElement, "data-rsp-scheme": scheme };
2937
+ }
2822
2938
 
2823
2939
  // src/workbook/border-sides.ts
2824
2940
  function normalizeBorderSides(sides, defaultAllWhenUnset = false) {
@@ -3224,8 +3340,8 @@ var MinusIcon = ({ className = "", ...props }) => /* @__PURE__ */ jsx_runtime4.j
3224
3340
  })
3225
3341
  });
3226
3342
  function useEngine(engine) {
3227
- const [state, setState] = import_react5.useState(() => engine.getState());
3228
- import_react5.default.useEffect(() => {
3343
+ const [state, setState] = import_react6.useState(() => engine.getState());
3344
+ import_react6.default.useEffect(() => {
3229
3345
  return engine.onUpdate(() => {
3230
3346
  setState(engine.getState());
3231
3347
  });
@@ -3329,11 +3445,12 @@ function FormulaSheet({
3329
3445
  showFormulas = false,
3330
3446
  ...rest
3331
3447
  }) {
3332
- const [selectedArea, setSelectedArea] = import_react5.useState(null);
3333
- const clipboardManager = import_react5.default.useMemo(() => controlledClipboardManager ?? new WorkbookClipboardManager(engine), [controlledClipboardManager, engine]);
3448
+ const schemeRoot = useSchemeRoot();
3449
+ const [selectedArea, setSelectedArea] = import_react6.useState(null);
3450
+ const clipboardManager = import_react6.default.useMemo(() => controlledClipboardManager ?? new WorkbookClipboardManager(engine), [controlledClipboardManager, engine]);
3334
3451
  const state = useEngine(engine);
3335
3452
  const sheet = state.workbooks.get(workbookName)?.sheets.get(sheetName);
3336
- const onCellDataChange = import_react5.useCallback((updatedSpreadsheet) => {
3453
+ const onCellDataChange = import_react6.useCallback((updatedSpreadsheet) => {
3337
3454
  const data = new Map(updatedSpreadsheet);
3338
3455
  data.forEach((value, key) => {
3339
3456
  if (typeof value === "string") {
@@ -3349,7 +3466,7 @@ function FormulaSheet({
3349
3466
  engine.setSheetContent({ sheetName, workbookName }, data);
3350
3467
  }, [sheetName, engine, workbookName]);
3351
3468
  const passedSelectionEffects = selection?.effects;
3352
- const selectionManagerEffects = import_react5.useCallback((spreadsheetSelectionManager) => {
3469
+ const selectionManagerEffects = import_react6.useCallback((spreadsheetSelectionManager) => {
3353
3470
  const convertSmAreaToSpreadsheetRange = (area) => {
3354
3471
  const rowEnd = area.end.row.type === "infinity" ? {
3355
3472
  type: "infinity",
@@ -3474,6 +3591,7 @@ function FormulaSheet({
3474
3591
  if (!sheet) {
3475
3592
  return /* @__PURE__ */ jsx_runtime4.jsxs("div", {
3476
3593
  className: "rsp-root rsp-workbook",
3594
+ ...schemeRoot,
3477
3595
  children: [
3478
3596
  toolbar,
3479
3597
  /* @__PURE__ */ jsx_runtime4.jsx("div", {
@@ -3484,6 +3602,7 @@ function FormulaSheet({
3484
3602
  }
3485
3603
  return /* @__PURE__ */ jsx_runtime4.jsxs("div", {
3486
3604
  className: "rsp-root rsp-workbook",
3605
+ ...schemeRoot,
3487
3606
  children: [
3488
3607
  toolbar,
3489
3608
  /* @__PURE__ */ jsx_runtime4.jsx("div", {
@@ -3554,32 +3673,32 @@ function FormulaSheet({
3554
3673
  const isLastColumn = cell.colIndex === tableInfo.start.colIndex + tableInfo.headers.size - 1;
3555
3674
  const isLastRow = tableInfo.endRow.type === "number" ? cell.rowIndex === tableInfo.endRow.value : false;
3556
3675
  const style = {
3557
- border: "1px solid var(--table-border-light)"
3676
+ border: "1px solid var(--rsp-table-border-light)"
3558
3677
  };
3559
3678
  if (isHeaderRow) {
3560
- style.backgroundColor = "var(--table-header-bg)";
3561
- style.color = "var(--table-header-color)";
3679
+ style.backgroundColor = "var(--rsp-table-header-bg)";
3680
+ style.color = "var(--rsp-table-header-color)";
3562
3681
  style.fontWeight = "bold";
3563
- style.borderBottom = "2px solid var(--table-header-border)";
3682
+ style.borderBottom = "2px solid var(--rsp-table-header-border)";
3564
3683
  } else {
3565
3684
  const dataRowIndex = cell.rowIndex - tableInfo.start.rowIndex - 1;
3566
3685
  if (dataRowIndex % 2 === 0) {
3567
- style.backgroundColor = "var(--table-even-row-bg)";
3686
+ style.backgroundColor = "var(--rsp-table-even-row-bg)";
3568
3687
  } else {
3569
- style.backgroundColor = "var(--table-odd-row-bg)";
3688
+ style.backgroundColor = "var(--rsp-table-odd-row-bg)";
3570
3689
  }
3571
3690
  }
3572
3691
  if (isFirstColumn) {
3573
- style.borderLeft = "2px solid var(--table-border-color)";
3692
+ style.borderLeft = "2px solid var(--rsp-table-border-color)";
3574
3693
  }
3575
3694
  if (isLastColumn) {
3576
- style.borderRight = "2px solid var(--table-border-color)";
3695
+ style.borderRight = "2px solid var(--rsp-table-border-color)";
3577
3696
  }
3578
3697
  if (isHeaderRow) {
3579
- style.borderTop = "2px solid var(--table-border-color)";
3698
+ style.borderTop = "2px solid var(--rsp-table-border-color)";
3580
3699
  }
3581
3700
  if (isLastRow) {
3582
- style.borderBottom = "2px solid var(--table-border-color)";
3701
+ style.borderBottom = "2px solid var(--rsp-table-border-color)";
3583
3702
  }
3584
3703
  if (conditionalStyle?.backgroundColor) {
3585
3704
  style.backgroundColor = conditionalStyle.backgroundColor;
@@ -3715,18 +3834,19 @@ function FormulaWorkbook({
3715
3834
  showFormulas = false,
3716
3835
  ...rest
3717
3836
  }) {
3837
+ const schemeRoot = useSchemeRoot();
3718
3838
  const state = useEngine(engine);
3719
- const [renamingSheet, setRenamingSheet] = import_react5.useState(null);
3720
- const [newSheetName, setNewSheetName] = import_react5.useState("");
3721
- const [spreadsheetZoom, setSpreadsheetZoom] = import_react5.useState(1);
3722
- const renameCommittedRef = import_react5.useRef(false);
3723
- const clipboardManager = import_react5.default.useMemo(() => controlledClipboardManager ?? new WorkbookClipboardManager(engine), [controlledClipboardManager, engine]);
3839
+ const [renamingSheet, setRenamingSheet] = import_react6.useState(null);
3840
+ const [newSheetName, setNewSheetName] = import_react6.useState("");
3841
+ const [spreadsheetZoom, setSpreadsheetZoom] = import_react6.useState(1);
3842
+ const renameCommittedRef = import_react6.useRef(false);
3843
+ const clipboardManager = import_react6.default.useMemo(() => controlledClipboardManager ?? new WorkbookClipboardManager(engine), [controlledClipboardManager, engine]);
3724
3844
  const sheetNames = engine.getOrderedSheetNames(workbookName);
3725
- const [internalActiveSheet, setInternalActiveSheet] = import_react5.useState(() => {
3845
+ const [internalActiveSheet, setInternalActiveSheet] = import_react6.useState(() => {
3726
3846
  return sheetNames[0] || "";
3727
3847
  });
3728
- const isControlledRef = import_react5.useRef(controlledActiveSheet !== undefined);
3729
- import_react5.default.useEffect(() => {
3848
+ const isControlledRef = import_react6.useRef(controlledActiveSheet !== undefined);
3849
+ import_react6.default.useEffect(() => {
3730
3850
  const isCurrentlyControlled = controlledActiveSheet !== undefined;
3731
3851
  if (isControlledRef.current !== isCurrentlyControlled) {
3732
3852
  console.error(`FormulaWorkbook: Component changed from ${isControlledRef.current ? "controlled" : "uncontrolled"} to ${isCurrentlyControlled ? "controlled" : "uncontrolled"} mode. ` + `A component should not switch between controlled and uncontrolled modes. ` + `Decide between using activeSheet and onActiveSheetChange props (controlled) or neither (uncontrolled) for the lifetime of the component.`);
@@ -3734,7 +3854,7 @@ function FormulaWorkbook({
3734
3854
  isControlledRef.current = isCurrentlyControlled;
3735
3855
  }, [controlledActiveSheet]);
3736
3856
  const activeSheet = controlledActiveSheet ?? internalActiveSheet;
3737
- const handleActiveSheetChange = import_react5.useCallback((sheetName) => {
3857
+ const handleActiveSheetChange = import_react6.useCallback((sheetName) => {
3738
3858
  if (onActiveSheetChange) {
3739
3859
  onActiveSheetChange(sheetName);
3740
3860
  } else {
@@ -3745,16 +3865,16 @@ function FormulaWorkbook({
3745
3865
  const activeSheetSelectedOverlayId = sheetSelectedOverlayIds?.[activeSheet];
3746
3866
  const activeSheetColumnWidths = sheetLayouts?.[activeSheet]?.columnWidths;
3747
3867
  const activeSheetRowHeights = sheetLayouts?.[activeSheet]?.rowHeights;
3748
- const handleOverlaySelect = import_react5.useCallback((overlayId) => {
3868
+ const handleOverlaySelect = import_react6.useCallback((overlayId) => {
3749
3869
  onOverlaySelect?.(activeSheet, overlayId);
3750
3870
  }, [onOverlaySelect, activeSheet]);
3751
- const handleOverlayChildrenChange = import_react5.useCallback((children) => {
3871
+ const handleOverlayChildrenChange = import_react6.useCallback((children) => {
3752
3872
  onOverlayChildrenChange?.(activeSheet, children);
3753
3873
  }, [onOverlayChildrenChange, activeSheet]);
3754
- const handleColumnWidthsChange = import_react5.useCallback((columnWidths) => {
3874
+ const handleColumnWidthsChange = import_react6.useCallback((columnWidths) => {
3755
3875
  onSheetColumnWidthsChange?.(activeSheet, columnWidths);
3756
3876
  }, [activeSheet, onSheetColumnWidthsChange]);
3757
- const handleRowHeightsChange = import_react5.useCallback((rowHeights) => {
3877
+ const handleRowHeightsChange = import_react6.useCallback((rowHeights) => {
3758
3878
  onSheetRowHeightsChange?.(activeSheet, rowHeights);
3759
3879
  }, [activeSheet, onSheetRowHeightsChange]);
3760
3880
  const addSheet = () => {
@@ -3821,6 +3941,7 @@ function FormulaWorkbook({
3821
3941
  };
3822
3942
  return /* @__PURE__ */ jsx_runtime4.jsxs("div", {
3823
3943
  className: "rsp-root rsp-workbook",
3944
+ ...schemeRoot,
3824
3945
  children: [
3825
3946
  toolbar,
3826
3947
  /* @__PURE__ */ jsx_runtime4.jsxs("div", {
@@ -4026,9 +4147,9 @@ var DEFAULT_ZOOM = 1;
4026
4147
  var MIN_ZOOM = 0.5;
4027
4148
  var MAX_ZOOM = 3;
4028
4149
  // src/utils/use-click-away.ts
4029
- var import_react6 = require("react");
4150
+ var import_react7 = require("react");
4030
4151
  var useClickAway = (ref, callback) => {
4031
- import_react6.useEffect(() => {
4152
+ import_react7.useEffect(() => {
4032
4153
  const handleClickAway = (event) => {
4033
4154
  if (ref) {
4034
4155
  const away = !ref.contains(event.target);
@@ -4042,4 +4163,4 @@ var useClickAway = (ref, callback) => {
4042
4163
  }, [ref, callback]);
4043
4164
  };
4044
4165
 
4045
- //# debugId=2D3DCCBBF4E8DCA364756E2164756E21
4166
+ //# debugId=A383A8C557A8A55164756E2164756E21