@ricsam/react-spreadsheets 0.0.14 → 0.0.15

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/cjs/lib.cjs CHANGED
@@ -1893,6 +1893,7 @@ var Spreadsheet = import_react4.forwardRef(({
1893
1893
  "aria-describedby": ariaDescribedBy,
1894
1894
  cellData: controlledCellData,
1895
1895
  onCellDataChange,
1896
+ onCellDataPatch,
1896
1897
  onCellDataChangeError,
1897
1898
  rowCount,
1898
1899
  columnCount,
@@ -2097,6 +2098,7 @@ var Spreadsheet = import_react4.forwardRef(({
2097
2098
  }, [overlayPlaceholder, containerDivRef, viewport, gridDims, placeholderPos]);
2098
2099
  const effectsRefOb = {
2099
2100
  onCellDataChange,
2101
+ onCellDataPatch,
2100
2102
  onCellDataChangeError,
2101
2103
  controlledCellData,
2102
2104
  parseValue,
@@ -2343,13 +2345,13 @@ var Spreadsheet = import_react4.forwardRef(({
2343
2345
  return selectionManager.listenToUpdateData((updates) => {
2344
2346
  const editingState = selectionManager.isEditing;
2345
2347
  try {
2346
- const { cellData, onCellDataChange } = effectsRef.current;
2347
- const newData = new Map(cellData);
2348
+ const { cellData, onCellDataChange, onCellDataPatch, controlledCellData } = effectsRef.current;
2349
+ const patch = new Map;
2348
2350
  updates.forEach(({ value, rowIndex, colIndex }) => {
2349
2351
  const key = `${indexToColumn(colIndex)}${rowIndex + 1}`;
2350
2352
  const parseValue = effectsRef.current.parseValue;
2351
2353
  if (value === "") {
2352
- newData.delete(key);
2354
+ patch.set(key, undefined);
2353
2355
  } else {
2354
2356
  let parsedValue = value;
2355
2357
  if (parseValue && typeof value === "string") {
@@ -2369,10 +2371,23 @@ var Spreadsheet = import_react4.forwardRef(({
2369
2371
  });
2370
2372
  }
2371
2373
  }
2372
- newData.set(key, parsedValue);
2374
+ patch.set(key, parsedValue);
2373
2375
  }
2374
2376
  });
2375
- onCellDataChange?.(newData);
2377
+ if (onCellDataPatch) {
2378
+ onCellDataPatch(patch);
2379
+ if (controlledCellData !== undefined)
2380
+ return;
2381
+ }
2382
+ const newData = new Map(cellData);
2383
+ patch.forEach((value, key) => {
2384
+ if (value === undefined)
2385
+ newData.delete(key);
2386
+ else
2387
+ newData.set(key, value);
2388
+ });
2389
+ if (!onCellDataPatch)
2390
+ onCellDataChange?.(newData);
2376
2391
  setInternalCellData(newData);
2377
2392
  } catch (error) {
2378
2393
  console.error("Failed to update spreadsheet cell data:", error);
@@ -3248,12 +3263,370 @@ var Spreadsheet = import_react4.forwardRef(({
3248
3263
  Spreadsheet.displayName = "Spreadsheet";
3249
3264
  // src/workbook/workbook.tsx
3250
3265
  var import_formula_engine2 = require("@ricsam/formula-engine");
3251
- var import_react6 = __toESM(require("react"), 1);
3266
+ var import_react7 = __toESM(require("react"), 1);
3252
3267
 
3253
- // src/utils/use-scheme-root.ts
3268
+ // src/workbook/sheet-navigation.tsx
3254
3269
  var import_react5 = require("react");
3270
+ var jsx_runtime4 = require("react/jsx-runtime");
3271
+ function SheetNavigation({
3272
+ sheets,
3273
+ activeSheet,
3274
+ workbookName,
3275
+ panelId,
3276
+ onSelect,
3277
+ onAdd,
3278
+ onRename,
3279
+ onDelete,
3280
+ children
3281
+ }) {
3282
+ const id = import_react5.useId();
3283
+ const root = import_react5.useRef(null);
3284
+ const menuButton = import_react5.useRef(null);
3285
+ const menu = import_react5.useRef(null);
3286
+ const input = import_react5.useRef(null);
3287
+ const [menuOpen, setMenuOpen] = import_react5.useState(false);
3288
+ const [renaming, setRenaming] = import_react5.useState(null);
3289
+ const [name, setName] = import_react5.useState("");
3290
+ const [error, setError] = import_react5.useState("");
3291
+ const [notice, setNotice] = import_react5.useState("");
3292
+ const selected = sheets.includes(activeSheet) ? activeSheet : sheets[0];
3293
+ const focusTab = (sheet = selected) => {
3294
+ const tabs = root.current?.querySelectorAll('[role="tab"]');
3295
+ tabs?.[sheets.indexOf(sheet ?? "")]?.focus();
3296
+ };
3297
+ import_react5.useEffect(() => {
3298
+ const tab = root.current?.querySelector('[aria-selected="true"]');
3299
+ const strip = tab?.parentElement;
3300
+ if (!tab || !strip)
3301
+ return;
3302
+ const bounds = strip.getBoundingClientRect();
3303
+ const target = tab.getBoundingClientRect();
3304
+ if (target.left < bounds.left)
3305
+ strip.scrollLeft -= bounds.left - target.left;
3306
+ else if (target.right > bounds.right)
3307
+ strip.scrollLeft += target.right - bounds.right;
3308
+ }, [activeSheet, sheets.join("\x00")]);
3309
+ import_react5.useEffect(() => {
3310
+ if (renaming) {
3311
+ input.current?.focus();
3312
+ input.current?.select();
3313
+ }
3314
+ }, [renaming]);
3315
+ import_react5.useEffect(() => {
3316
+ if (!menuOpen)
3317
+ return;
3318
+ menu.current?.querySelector("button")?.focus();
3319
+ const outside = (event) => {
3320
+ if (!root.current?.contains(event.target))
3321
+ setMenuOpen(false);
3322
+ };
3323
+ document.addEventListener("pointerdown", outside);
3324
+ return () => document.removeEventListener("pointerdown", outside);
3325
+ }, [menuOpen]);
3326
+ import_react5.useEffect(() => {
3327
+ setMenuOpen(false);
3328
+ setRenaming(null);
3329
+ setError("");
3330
+ }, [workbookName]);
3331
+ import_react5.useEffect(() => {
3332
+ if (renaming && !sheets.includes(renaming)) {
3333
+ setRenaming(null);
3334
+ setError("");
3335
+ }
3336
+ }, [renaming, sheets.join("\x00")]);
3337
+ const run = (action) => {
3338
+ try {
3339
+ action();
3340
+ setError("");
3341
+ return true;
3342
+ } catch (error) {
3343
+ setError(error instanceof Error ? error.message : "Unable to update this sheet. Please try again.");
3344
+ return false;
3345
+ }
3346
+ };
3347
+ const startRename = (sheet) => {
3348
+ setMenuOpen(false);
3349
+ setError("");
3350
+ setRenaming(sheet);
3351
+ setName(sheet);
3352
+ };
3353
+ const cancelRename = () => {
3354
+ setRenaming(null);
3355
+ setError("");
3356
+ focusTab();
3357
+ };
3358
+ const saveRename = (event) => {
3359
+ event.preventDefault();
3360
+ if (!renaming)
3361
+ return;
3362
+ const next = name.trim();
3363
+ if (!next) {
3364
+ setError("Enter a sheet name.");
3365
+ input.current?.focus();
3366
+ return;
3367
+ }
3368
+ if (sheets.some((sheet) => sheet !== renaming && sheet.toLocaleLowerCase() === next.toLocaleLowerCase())) {
3369
+ setError("A sheet with this name already exists. Choose a different name.");
3370
+ input.current?.focus();
3371
+ return;
3372
+ }
3373
+ if (run(() => {
3374
+ if (next !== renaming)
3375
+ onRename(renaming, next);
3376
+ })) {
3377
+ setRenaming(null);
3378
+ setNotice(`Sheet renamed to ${next}.`);
3379
+ menuButton.current?.focus();
3380
+ }
3381
+ };
3382
+ return /* @__PURE__ */ jsx_runtime4.jsxs("div", {
3383
+ ref: root,
3384
+ className: "rsp-sheet-navigation",
3385
+ onKeyDown: (event) => event.stopPropagation(),
3386
+ children: [
3387
+ /* @__PURE__ */ jsx_runtime4.jsx("span", {
3388
+ className: "rsp-sr-only",
3389
+ role: "status",
3390
+ children: notice
3391
+ }),
3392
+ renaming && /* @__PURE__ */ jsx_runtime4.jsxs("form", {
3393
+ className: "rsp-sheet-rename",
3394
+ onSubmit: saveRename,
3395
+ onKeyDown: (event) => {
3396
+ if (event.key === "Escape") {
3397
+ event.preventDefault();
3398
+ cancelRename();
3399
+ }
3400
+ },
3401
+ children: [
3402
+ /* @__PURE__ */ jsx_runtime4.jsx("label", {
3403
+ htmlFor: `${id}-name`,
3404
+ children: "Sheet name"
3405
+ }),
3406
+ /* @__PURE__ */ jsx_runtime4.jsx("input", {
3407
+ ref: input,
3408
+ id: `${id}-name`,
3409
+ className: "rsp-sheet-tab-input",
3410
+ value: name,
3411
+ onChange: (event) => {
3412
+ setName(event.target.value);
3413
+ setError("");
3414
+ },
3415
+ "aria-invalid": !!error,
3416
+ "aria-describedby": error ? `${id}-error` : undefined,
3417
+ "data-testid": `rename-sheet-input-${renaming}`
3418
+ }),
3419
+ /* @__PURE__ */ jsx_runtime4.jsx("button", {
3420
+ type: "submit",
3421
+ className: "rsp-sheet-action",
3422
+ children: "Save"
3423
+ }),
3424
+ /* @__PURE__ */ jsx_runtime4.jsx("button", {
3425
+ type: "button",
3426
+ className: "rsp-sheet-action",
3427
+ onClick: cancelRename,
3428
+ children: "Cancel"
3429
+ })
3430
+ ]
3431
+ }),
3432
+ error && /* @__PURE__ */ jsx_runtime4.jsx("div", {
3433
+ id: `${id}-error`,
3434
+ className: "rsp-sheet-error",
3435
+ role: "alert",
3436
+ children: error
3437
+ }),
3438
+ /* @__PURE__ */ jsx_runtime4.jsxs("div", {
3439
+ className: "rsp-sheet-tabs",
3440
+ children: [
3441
+ /* @__PURE__ */ jsx_runtime4.jsx("div", {
3442
+ className: "rsp-sheet-tabs-scroll",
3443
+ role: "tablist",
3444
+ "aria-label": `${workbookName} sheets`,
3445
+ children: sheets.map((sheet, index) => /* @__PURE__ */ jsx_runtime4.jsx("button", {
3446
+ type: "button",
3447
+ role: "tab",
3448
+ className: `rsp-sheet-tab${activeSheet === sheet ? " rsp-sheet-tab-active" : ""}`,
3449
+ "aria-controls": panelId,
3450
+ "aria-selected": activeSheet === sheet,
3451
+ tabIndex: sheet === selected ? 0 : -1,
3452
+ title: sheet,
3453
+ "data-testid": `sheet-tab-${sheet}`,
3454
+ onClick: () => {
3455
+ setMenuOpen(false);
3456
+ onSelect(sheet);
3457
+ },
3458
+ onDoubleClick: () => startRename(sheet),
3459
+ onKeyDown: (event) => {
3460
+ const rtl = getComputedStyle(event.currentTarget).direction === "rtl";
3461
+ let next;
3462
+ if (event.key === "ArrowRight")
3463
+ next = (index + (rtl ? -1 : 1) + sheets.length) % sheets.length;
3464
+ if (event.key === "ArrowLeft")
3465
+ next = (index + (rtl ? 1 : -1) + sheets.length) % sheets.length;
3466
+ if (event.key === "Home")
3467
+ next = 0;
3468
+ if (event.key === "End")
3469
+ next = sheets.length - 1;
3470
+ if (next !== undefined) {
3471
+ event.preventDefault();
3472
+ onSelect(sheets[next]);
3473
+ focusTab(sheets[next]);
3474
+ }
3475
+ if (event.key === "F2") {
3476
+ event.preventDefault();
3477
+ startRename(sheet);
3478
+ }
3479
+ },
3480
+ children: /* @__PURE__ */ jsx_runtime4.jsx("span", {
3481
+ children: sheet
3482
+ })
3483
+ }, sheet))
3484
+ }),
3485
+ /* @__PURE__ */ jsx_runtime4.jsx("button", {
3486
+ type: "button",
3487
+ className: "rsp-add-sheet-button",
3488
+ title: "Add Sheet",
3489
+ "aria-label": "Add Sheet",
3490
+ "data-testid": `add-sheet-${workbookName}`,
3491
+ onClick: () => {
3492
+ setMenuOpen(false);
3493
+ run(onAdd);
3494
+ },
3495
+ children: /* @__PURE__ */ jsx_runtime4.jsx("svg", {
3496
+ "aria-hidden": "true",
3497
+ width: "16",
3498
+ height: "16",
3499
+ viewBox: "0 0 24 24",
3500
+ fill: "none",
3501
+ stroke: "currentColor",
3502
+ strokeWidth: "2",
3503
+ strokeLinecap: "round",
3504
+ children: /* @__PURE__ */ jsx_runtime4.jsx("path", {
3505
+ d: "M12 5v14M5 12h14"
3506
+ })
3507
+ })
3508
+ }),
3509
+ /* @__PURE__ */ jsx_runtime4.jsx("button", {
3510
+ ref: menuButton,
3511
+ type: "button",
3512
+ className: "rsp-icon-button rsp-sheet-menu-trigger",
3513
+ title: "Sheet actions",
3514
+ "aria-label": selected ? `Sheet actions for ${selected}` : "Sheet actions",
3515
+ disabled: !selected,
3516
+ "aria-haspopup": "menu",
3517
+ "aria-expanded": menuOpen,
3518
+ "aria-controls": menuOpen ? `${id}-menu` : undefined,
3519
+ onClick: () => setMenuOpen((open) => !open),
3520
+ onKeyDown: (event) => {
3521
+ if (event.key === "ArrowDown" || event.key === "ArrowUp") {
3522
+ event.preventDefault();
3523
+ setMenuOpen(true);
3524
+ }
3525
+ },
3526
+ children: /* @__PURE__ */ jsx_runtime4.jsxs("svg", {
3527
+ "aria-hidden": "true",
3528
+ width: "16",
3529
+ height: "16",
3530
+ viewBox: "0 0 24 24",
3531
+ fill: "currentColor",
3532
+ children: [
3533
+ /* @__PURE__ */ jsx_runtime4.jsx("circle", {
3534
+ cx: "5",
3535
+ cy: "12",
3536
+ r: "2"
3537
+ }),
3538
+ /* @__PURE__ */ jsx_runtime4.jsx("circle", {
3539
+ cx: "12",
3540
+ cy: "12",
3541
+ r: "2"
3542
+ }),
3543
+ /* @__PURE__ */ jsx_runtime4.jsx("circle", {
3544
+ cx: "19",
3545
+ cy: "12",
3546
+ r: "2"
3547
+ })
3548
+ ]
3549
+ })
3550
+ }),
3551
+ children
3552
+ ]
3553
+ }),
3554
+ menuOpen && selected && /* @__PURE__ */ jsx_runtime4.jsxs("div", {
3555
+ ref: menu,
3556
+ id: `${id}-menu`,
3557
+ role: "menu",
3558
+ "aria-label": `Actions for ${selected}`,
3559
+ className: "rsp-sheet-menu",
3560
+ onBlur: (event) => {
3561
+ if (!event.currentTarget.contains(event.relatedTarget) && event.relatedTarget !== menuButton.current)
3562
+ setMenuOpen(false);
3563
+ },
3564
+ onKeyDown: (event) => {
3565
+ const items = Array.from(menu.current?.querySelectorAll("button") ?? []);
3566
+ const index = items.indexOf(document.activeElement);
3567
+ if (["ArrowDown", "ArrowUp", "Home", "End"].includes(event.key)) {
3568
+ event.preventDefault();
3569
+ const next = event.key === "Home" ? 0 : event.key === "End" ? items.length - 1 : (index + (event.key === "ArrowDown" ? 1 : -1) + items.length) % items.length;
3570
+ items[next]?.focus();
3571
+ }
3572
+ if (event.key === "Escape") {
3573
+ event.preventDefault();
3574
+ setMenuOpen(false);
3575
+ menuButton.current?.focus();
3576
+ }
3577
+ },
3578
+ children: [
3579
+ /* @__PURE__ */ jsx_runtime4.jsx("div", {
3580
+ className: "rsp-sheet-menu-name",
3581
+ title: selected,
3582
+ children: selected
3583
+ }),
3584
+ /* @__PURE__ */ jsx_runtime4.jsxs("button", {
3585
+ type: "button",
3586
+ role: "menuitem",
3587
+ onClick: () => startRename(selected),
3588
+ "data-testid": `rename-sheet-${selected}`,
3589
+ children: [
3590
+ "Rename sheet ",
3591
+ /* @__PURE__ */ jsx_runtime4.jsx("span", {
3592
+ "aria-hidden": "true",
3593
+ children: "F2"
3594
+ })
3595
+ ]
3596
+ }),
3597
+ /* @__PURE__ */ jsx_runtime4.jsx("button", {
3598
+ type: "button",
3599
+ role: "menuitem",
3600
+ "aria-disabled": sheets.length <= 1,
3601
+ className: "rsp-sheet-delete",
3602
+ "data-testid": `delete-sheet-${selected}`,
3603
+ onClick: () => {
3604
+ if (sheets.length <= 1)
3605
+ return;
3606
+ if (window.confirm(`Delete sheet "${selected}" and its contents?`)) {
3607
+ if (run(() => onDelete(selected))) {
3608
+ setMenuOpen(false);
3609
+ setNotice(`Sheet ${selected} deleted.`);
3610
+ menuButton.current?.focus();
3611
+ }
3612
+ }
3613
+ },
3614
+ children: "Delete sheet"
3615
+ }),
3616
+ sheets.length <= 1 && /* @__PURE__ */ jsx_runtime4.jsx("div", {
3617
+ className: "rsp-sheet-menu-hint",
3618
+ children: "Keep at least one sheet."
3619
+ })
3620
+ ]
3621
+ })
3622
+ ]
3623
+ });
3624
+ }
3625
+
3626
+ // src/utils/use-scheme-root.ts
3627
+ var import_react6 = require("react");
3255
3628
  function useSchemeRoot() {
3256
- const [element, setElement] = import_react5.useState(null);
3629
+ const [element, setElement] = import_react6.useState(null);
3257
3630
  const scheme = useColorScheme(element);
3258
3631
  return { ref: setElement, "data-rsp-scheme": scheme };
3259
3632
  }
@@ -3634,7 +4007,7 @@ class WorkbookSelectionManager {
3634
4007
  }
3635
4008
 
3636
4009
  // src/workbook/workbook.tsx
3637
- var jsx_runtime4 = require("react/jsx-runtime");
4010
+ var jsx_runtime5 = require("react/jsx-runtime");
3638
4011
  var getWrapCellStyle = (style) => style?.wrapText ? {
3639
4012
  whiteSpace: "pre-wrap",
3640
4013
  overflowWrap: "anywhere",
@@ -3665,7 +4038,7 @@ var getBorderCellStyle = (style) => (() => {
3665
4038
  ...borderSides.left && { borderLeftColor: style.borderColor }
3666
4039
  };
3667
4040
  })();
3668
- var Edit2Icon = ({ className = "", ...props }) => /* @__PURE__ */ jsx_runtime4.jsxs("svg", {
4041
+ var PlusIcon = ({ className = "", ...props }) => /* @__PURE__ */ jsx_runtime5.jsxs("svg", {
3669
4042
  xmlns: "http://www.w3.org/2000/svg",
3670
4043
  width: "16",
3671
4044
  height: "16",
@@ -3678,72 +4051,15 @@ var Edit2Icon = ({ className = "", ...props }) => /* @__PURE__ */ jsx_runtime4.j
3678
4051
  className,
3679
4052
  ...props,
3680
4053
  children: [
3681
- /* @__PURE__ */ jsx_runtime4.jsx("path", {
3682
- d: "M17 3a2.85 2.83 0 1 1 4 4L7.5 20.5 2 22l1.5-5.5Z"
3683
- }),
3684
- /* @__PURE__ */ jsx_runtime4.jsx("path", {
3685
- d: "m15 5 4 4"
3686
- })
3687
- ]
3688
- });
3689
- var Trash2Icon = ({ className = "", ...props }) => /* @__PURE__ */ jsx_runtime4.jsxs("svg", {
3690
- xmlns: "http://www.w3.org/2000/svg",
3691
- width: "16",
3692
- height: "16",
3693
- viewBox: "0 0 24 24",
3694
- fill: "none",
3695
- stroke: "currentColor",
3696
- strokeWidth: "2",
3697
- strokeLinecap: "round",
3698
- strokeLinejoin: "round",
3699
- className,
3700
- ...props,
3701
- children: [
3702
- /* @__PURE__ */ jsx_runtime4.jsx("path", {
3703
- d: "M3 6h18"
3704
- }),
3705
- /* @__PURE__ */ jsx_runtime4.jsx("path", {
3706
- d: "M19 6v14c0 1-1 2-2 2H7c-1 0-2-1-2-2V6"
3707
- }),
3708
- /* @__PURE__ */ jsx_runtime4.jsx("path", {
3709
- d: "M8 6V4c0-1 1-2 2-2h4c1 0 2 1 2 2v2"
3710
- }),
3711
- /* @__PURE__ */ jsx_runtime4.jsx("line", {
3712
- x1: "10",
3713
- x2: "10",
3714
- y1: "11",
3715
- y2: "17"
3716
- }),
3717
- /* @__PURE__ */ jsx_runtime4.jsx("line", {
3718
- x1: "14",
3719
- x2: "14",
3720
- y1: "11",
3721
- y2: "17"
3722
- })
3723
- ]
3724
- });
3725
- var PlusIcon = ({ className = "", ...props }) => /* @__PURE__ */ jsx_runtime4.jsxs("svg", {
3726
- xmlns: "http://www.w3.org/2000/svg",
3727
- width: "16",
3728
- height: "16",
3729
- viewBox: "0 0 24 24",
3730
- fill: "none",
3731
- stroke: "currentColor",
3732
- strokeWidth: "2",
3733
- strokeLinecap: "round",
3734
- strokeLinejoin: "round",
3735
- className,
3736
- ...props,
3737
- children: [
3738
- /* @__PURE__ */ jsx_runtime4.jsx("path", {
4054
+ /* @__PURE__ */ jsx_runtime5.jsx("path", {
3739
4055
  d: "M5 12h14"
3740
4056
  }),
3741
- /* @__PURE__ */ jsx_runtime4.jsx("path", {
4057
+ /* @__PURE__ */ jsx_runtime5.jsx("path", {
3742
4058
  d: "M12 5v14"
3743
4059
  })
3744
4060
  ]
3745
4061
  });
3746
- var MinusIcon = ({ className = "", ...props }) => /* @__PURE__ */ jsx_runtime4.jsx("svg", {
4062
+ var MinusIcon = ({ className = "", ...props }) => /* @__PURE__ */ jsx_runtime5.jsx("svg", {
3747
4063
  xmlns: "http://www.w3.org/2000/svg",
3748
4064
  width: "16",
3749
4065
  height: "16",
@@ -3755,13 +4071,13 @@ var MinusIcon = ({ className = "", ...props }) => /* @__PURE__ */ jsx_runtime4.j
3755
4071
  strokeLinejoin: "round",
3756
4072
  className,
3757
4073
  ...props,
3758
- children: /* @__PURE__ */ jsx_runtime4.jsx("path", {
4074
+ children: /* @__PURE__ */ jsx_runtime5.jsx("path", {
3759
4075
  d: "M5 12h14"
3760
4076
  })
3761
4077
  });
3762
4078
  function useEngine(engine) {
3763
- const [state, setState] = import_react6.useState(() => engine.getState());
3764
- import_react6.default.useEffect(() => {
4079
+ const [state, setState] = import_react7.useState(() => engine.getState());
4080
+ import_react7.default.useEffect(() => {
3765
4081
  return engine.onUpdate(() => {
3766
4082
  setState(engine.getState());
3767
4083
  });
@@ -3867,11 +4183,11 @@ function FormulaSheet({
3867
4183
  ...rest
3868
4184
  }) {
3869
4185
  const schemeRoot = useSchemeRoot();
3870
- const [selectedArea, setSelectedArea] = import_react6.useState(null);
3871
- const clipboardManager = import_react6.default.useMemo(() => controlledClipboardManager ?? new WorkbookClipboardManager(engine), [controlledClipboardManager, engine]);
4186
+ const [selectedArea, setSelectedArea] = import_react7.useState(null);
4187
+ const clipboardManager = import_react7.default.useMemo(() => controlledClipboardManager ?? new WorkbookClipboardManager(engine), [controlledClipboardManager, engine]);
3872
4188
  const state = useEngine(engine);
3873
4189
  const sheet = state.workbooks.get(workbookName)?.sheets.get(sheetName);
3874
- const getEngineTableAt = import_react6.useCallback((cell) => {
4190
+ const getEngineTableAt = import_react7.useCallback((cell) => {
3875
4191
  const table = engine.isCellInTable({
3876
4192
  workbookName,
3877
4193
  sheetName,
@@ -3903,27 +4219,21 @@ function FormulaSheet({
3903
4219
  } : {}
3904
4220
  };
3905
4221
  }, [engine, sheetName, workbookName]);
3906
- const formulaNavigation = import_react6.useMemo(() => ({
4222
+ const formulaNavigation = import_react7.useMemo(() => ({
3907
4223
  ...selection?.navigation,
3908
4224
  getTableAt: selection?.navigation?.getTableAt ?? getEngineTableAt
3909
4225
  }), [getEngineTableAt, selection?.navigation]);
3910
- const onCellDataChange = import_react6.useCallback((updatedSpreadsheet) => {
3911
- const data = new Map(updatedSpreadsheet);
3912
- data.forEach((value, key) => {
3913
- if (typeof value === "string") {
4226
+ const onCellDataPatch = import_react7.useCallback((patch) => {
4227
+ engine.transact(() => {
4228
+ patch.forEach((value, key) => {
3914
4229
  const { colIndex, rowIndex } = import_formula_engine2.parseCellReference(key);
3915
- const coercedValue = coerceCellInput(value, engine.getCellDataType({ workbookName, sheetName, colIndex, rowIndex }));
3916
- if (coercedValue === undefined) {
3917
- data.delete(key);
3918
- } else {
3919
- data.set(key, coercedValue);
3920
- }
3921
- }
4230
+ const address = { workbookName, sheetName, colIndex, rowIndex };
4231
+ engine.setCellContent(address, typeof value === "string" ? coerceCellInput(value, engine.getCellDataType(address)) : value);
4232
+ });
3922
4233
  });
3923
- engine.setSheetContent({ sheetName, workbookName }, data);
3924
4234
  }, [sheetName, engine, workbookName]);
3925
4235
  const passedSelectionEffects = selection?.effects;
3926
- const selectionManagerEffects = import_react6.useCallback((spreadsheetSelectionManager) => {
4236
+ const selectionManagerEffects = import_react7.useCallback((spreadsheetSelectionManager) => {
3927
4237
  const convertSmAreaToSpreadsheetRange = (area) => {
3928
4238
  const rowEnd = area.end.row.type === "infinity" ? {
3929
4239
  type: "infinity",
@@ -4047,7 +4357,7 @@ function FormulaSheet({
4047
4357
  viewId,
4048
4358
  workbookName
4049
4359
  ]);
4050
- const renderCallbacks = import_react6.useMemo(() => {
4360
+ const renderCallbacks = import_react7.useMemo(() => {
4051
4361
  const metadata = engine.getWorkbookMetadata(workbookName);
4052
4362
  const defaults = metadata?.defaultCellStyle;
4053
4363
  const fontStyle = (style) => ({
@@ -4172,14 +4482,14 @@ function FormulaSheet({
4172
4482
  Object.assign(conditionalStyleProps, fontStyle(conditionalStyle), getWrapContentStyle(conditionalStyle));
4173
4483
  let defaultElement;
4174
4484
  if (showFormulas) {
4175
- defaultElement = /* @__PURE__ */ jsx_runtime4.jsx("div", {
4485
+ defaultElement = /* @__PURE__ */ jsx_runtime5.jsx("div", {
4176
4486
  "data-conditional-background": conditionalStyle?.backgroundColor,
4177
4487
  "data-conditional-text-color": conditionalStyle?.color,
4178
4488
  style: conditionalStyleProps,
4179
4489
  children: getCellDisplayText(value)
4180
4490
  });
4181
4491
  } else if (typeof value === "boolean") {
4182
- defaultElement = /* @__PURE__ */ jsx_runtime4.jsx("div", {
4492
+ defaultElement = /* @__PURE__ */ jsx_runtime5.jsx("div", {
4183
4493
  "data-conditional-background": conditionalStyle?.backgroundColor,
4184
4494
  "data-conditional-text-color": conditionalStyle?.color,
4185
4495
  "data-type": "boolean",
@@ -4192,7 +4502,7 @@ function FormulaSheet({
4192
4502
  children: value ? "TRUE" : "FALSE"
4193
4503
  });
4194
4504
  } else if (typeof value === "number") {
4195
- defaultElement = /* @__PURE__ */ jsx_runtime4.jsx("div", {
4505
+ defaultElement = /* @__PURE__ */ jsx_runtime5.jsx("div", {
4196
4506
  "data-conditional-background": conditionalStyle?.backgroundColor,
4197
4507
  "data-conditional-text-color": conditionalStyle?.color,
4198
4508
  "data-type": "number",
@@ -4203,7 +4513,7 @@ function FormulaSheet({
4203
4513
  })
4204
4514
  });
4205
4515
  } else {
4206
- defaultElement = /* @__PURE__ */ jsx_runtime4.jsx("div", {
4516
+ defaultElement = /* @__PURE__ */ jsx_runtime5.jsx("div", {
4207
4517
  "data-conditional-background": conditionalStyle?.backgroundColor,
4208
4518
  "data-conditional-text-color": conditionalStyle?.color,
4209
4519
  style: { ...conditionalStyleProps, width: "100%", textAlign: "left" },
@@ -4228,29 +4538,29 @@ function FormulaSheet({
4228
4538
  customCellRenderer
4229
4539
  ]);
4230
4540
  if (!sheet) {
4231
- return /* @__PURE__ */ jsx_runtime4.jsxs("div", {
4541
+ return /* @__PURE__ */ jsx_runtime5.jsxs("div", {
4232
4542
  className: "rsp-root rsp-workbook",
4233
4543
  ...schemeRoot,
4234
4544
  children: [
4235
4545
  toolbar,
4236
- /* @__PURE__ */ jsx_runtime4.jsx("div", {
4546
+ /* @__PURE__ */ jsx_runtime5.jsx("div", {
4237
4547
  style: { flex: 1 }
4238
4548
  })
4239
4549
  ]
4240
4550
  });
4241
4551
  }
4242
- return /* @__PURE__ */ jsx_runtime4.jsxs("div", {
4552
+ return /* @__PURE__ */ jsx_runtime5.jsxs("div", {
4243
4553
  className: "rsp-root rsp-workbook",
4244
4554
  ...schemeRoot,
4245
4555
  children: [
4246
4556
  toolbar,
4247
- /* @__PURE__ */ jsx_runtime4.jsx("div", {
4557
+ /* @__PURE__ */ jsx_runtime5.jsx("div", {
4248
4558
  className: "rsp-workbook-body",
4249
- children: /* @__PURE__ */ jsx_runtime4.jsx(Spreadsheet, {
4559
+ children: /* @__PURE__ */ jsx_runtime5.jsx(Spreadsheet, {
4250
4560
  disableClipboard: true,
4251
4561
  style: { height: "100%", width: "100%" },
4252
4562
  cellData: sheet?.content,
4253
- onCellDataChange,
4563
+ onCellDataPatch,
4254
4564
  onCellDataChangeError,
4255
4565
  ...rest,
4256
4566
  components,
@@ -4298,18 +4608,16 @@ function FormulaWorkbook({
4298
4608
  ...rest
4299
4609
  }) {
4300
4610
  const schemeRoot = useSchemeRoot();
4301
- const state = useEngine(engine);
4302
- const [renamingSheet, setRenamingSheet] = import_react6.useState(null);
4303
- const [newSheetName, setNewSheetName] = import_react6.useState("");
4304
- const [spreadsheetZoom, setSpreadsheetZoom] = import_react6.useState(1);
4305
- const renameCommittedRef = import_react6.useRef(false);
4306
- const clipboardManager = import_react6.default.useMemo(() => controlledClipboardManager ?? new WorkbookClipboardManager(engine), [controlledClipboardManager, engine]);
4611
+ useEngine(engine);
4612
+ const panelId = import_react7.default.useId();
4613
+ const [spreadsheetZoom, setSpreadsheetZoom] = import_react7.useState(1);
4614
+ const clipboardManager = import_react7.default.useMemo(() => controlledClipboardManager ?? new WorkbookClipboardManager(engine), [controlledClipboardManager, engine]);
4307
4615
  const sheetNames = engine.getOrderedSheetNames(workbookName);
4308
- const [internalActiveSheet, setInternalActiveSheet] = import_react6.useState(() => {
4616
+ const [internalActiveSheet, setInternalActiveSheet] = import_react7.useState(() => {
4309
4617
  return sheetNames[0] || "";
4310
4618
  });
4311
- const isControlledRef = import_react6.useRef(controlledActiveSheet !== undefined);
4312
- import_react6.default.useEffect(() => {
4619
+ const isControlledRef = import_react7.useRef(controlledActiveSheet !== undefined);
4620
+ import_react7.default.useEffect(() => {
4313
4621
  const isCurrentlyControlled = controlledActiveSheet !== undefined;
4314
4622
  if (isControlledRef.current !== isCurrentlyControlled) {
4315
4623
  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.`);
@@ -4317,100 +4625,61 @@ function FormulaWorkbook({
4317
4625
  isControlledRef.current = isCurrentlyControlled;
4318
4626
  }, [controlledActiveSheet]);
4319
4627
  const activeSheet = controlledActiveSheet ?? internalActiveSheet;
4320
- const handleActiveSheetChange = import_react6.useCallback((sheetName) => {
4628
+ const handleActiveSheetChange = import_react7.useCallback((sheetName) => {
4321
4629
  if (onActiveSheetChange) {
4322
4630
  onActiveSheetChange(sheetName);
4323
- } else {
4324
- setInternalActiveSheet(sheetName);
4325
4631
  }
4326
- }, [onActiveSheetChange]);
4632
+ if (controlledActiveSheet === undefined)
4633
+ setInternalActiveSheet(sheetName);
4634
+ }, [onActiveSheetChange, controlledActiveSheet]);
4327
4635
  const activeSheetOverlayChildren = sheetOverlayChildren?.[activeSheet];
4328
4636
  const activeSheetSelectedOverlayId = sheetSelectedOverlayIds?.[activeSheet];
4329
4637
  const activeSheetColumnWidths = sheetLayouts?.[activeSheet]?.columnWidths;
4330
4638
  const activeSheetRowHeights = sheetLayouts?.[activeSheet]?.rowHeights;
4331
- const handleOverlaySelect = import_react6.useCallback((overlayId) => {
4639
+ const handleOverlaySelect = import_react7.useCallback((overlayId) => {
4332
4640
  onOverlaySelect?.(activeSheet, overlayId);
4333
4641
  }, [onOverlaySelect, activeSheet]);
4334
- const handleOverlayChildrenChange = import_react6.useCallback((children) => {
4642
+ const handleOverlayChildrenChange = import_react7.useCallback((children) => {
4335
4643
  onOverlayChildrenChange?.(activeSheet, children);
4336
4644
  }, [onOverlayChildrenChange, activeSheet]);
4337
- const handleColumnWidthsChange = import_react6.useCallback((columnWidths) => {
4645
+ const handleColumnWidthsChange = import_react7.useCallback((columnWidths) => {
4338
4646
  onSheetColumnWidthsChange?.(activeSheet, columnWidths);
4339
4647
  }, [activeSheet, onSheetColumnWidthsChange]);
4340
- const handleRowHeightsChange = import_react6.useCallback((rowHeights) => {
4648
+ const handleRowHeightsChange = import_react7.useCallback((rowHeights) => {
4341
4649
  onSheetRowHeightsChange?.(activeSheet, rowHeights);
4342
4650
  }, [activeSheet, onSheetRowHeightsChange]);
4343
4651
  const addSheet = () => {
4344
- try {
4345
- const newSheet = engine.createSheet({
4346
- workbookName
4347
- });
4348
- handleActiveSheetChange(newSheet.name);
4349
- } catch (error) {
4350
- console.error("Failed to add sheet:", error);
4351
- }
4352
- };
4353
- const startRenaming = (sheetName) => {
4354
- renameCommittedRef.current = false;
4355
- setRenamingSheet(sheetName);
4356
- setNewSheetName(sheetName);
4357
- };
4358
- const cancelRenaming = () => {
4359
- setRenamingSheet(null);
4360
- setNewSheetName("");
4652
+ const newSheet = engine.createSheet({ workbookName });
4653
+ handleActiveSheetChange(newSheet.name);
4361
4654
  };
4362
4655
  const renameSheet = (oldName, newName) => {
4363
- if (renameCommittedRef.current) {
4364
- cancelRenaming();
4365
- return;
4366
- }
4367
- if (!newName.trim() || newName.trim() === oldName) {
4368
- cancelRenaming();
4369
- return;
4370
- }
4371
- try {
4372
- renameCommittedRef.current = true;
4373
- engine.renameSheet({
4374
- workbookName,
4375
- sheetName: oldName,
4376
- newSheetName: newName.trim()
4377
- });
4378
- if (activeSheet === oldName) {
4379
- handleActiveSheetChange(newName.trim());
4380
- }
4381
- cancelRenaming();
4382
- } catch (error) {
4383
- console.error("Failed to rename sheet:", error);
4384
- cancelRenaming();
4385
- }
4656
+ engine.renameSheet({ workbookName, sheetName: oldName, newSheetName: newName });
4657
+ if (activeSheet === oldName)
4658
+ handleActiveSheetChange(newName);
4386
4659
  };
4387
4660
  const deleteSheet = (sheetName) => {
4388
4661
  if (sheetNames.length <= 1) {
4389
4662
  console.error("Cannot delete the last sheet");
4390
4663
  return;
4391
4664
  }
4392
- try {
4393
- engine.removeSheet({ workbookName, sheetName });
4394
- if (activeSheet === sheetName) {
4395
- const remainingSheets = sheetNames.filter((name) => name !== sheetName);
4396
- const nextSheet = remainingSheets[0];
4397
- if (nextSheet) {
4398
- handleActiveSheetChange(nextSheet);
4399
- }
4665
+ engine.removeSheet({ workbookName, sheetName });
4666
+ if (activeSheet === sheetName) {
4667
+ const remainingSheets = sheetNames.filter((name) => name !== sheetName);
4668
+ const nextSheet = remainingSheets[Math.min(sheetNames.indexOf(sheetName), remainingSheets.length - 1)];
4669
+ if (nextSheet) {
4670
+ handleActiveSheetChange(nextSheet);
4400
4671
  }
4401
- } catch (error) {
4402
- console.error("Failed to delete sheet:", error);
4403
4672
  }
4404
4673
  };
4405
- return /* @__PURE__ */ jsx_runtime4.jsxs("div", {
4674
+ return /* @__PURE__ */ jsx_runtime5.jsxs("div", {
4406
4675
  className: "rsp-root rsp-workbook",
4407
4676
  ...schemeRoot,
4408
4677
  children: [
4409
4678
  toolbar,
4410
- /* @__PURE__ */ jsx_runtime4.jsxs("div", {
4679
+ /* @__PURE__ */ jsx_runtime5.jsxs("div", {
4411
4680
  className: "rsp-workbook-header",
4412
4681
  children: [
4413
- /* @__PURE__ */ jsx_runtime4.jsx("div", {
4682
+ /* @__PURE__ */ jsx_runtime5.jsx("div", {
4414
4683
  style: {
4415
4684
  display: "flex",
4416
4685
  alignItems: "center",
@@ -4419,13 +4688,13 @@ function FormulaWorkbook({
4419
4688
  overflow: "hidden",
4420
4689
  textOverflow: "ellipsis"
4421
4690
  },
4422
- children: /* @__PURE__ */ jsx_runtime4.jsx("span", {
4691
+ children: /* @__PURE__ */ jsx_runtime5.jsx("span", {
4423
4692
  className: "rsp-workbook-name",
4424
4693
  "data-testid": `workbook-name-${workbookName}`,
4425
4694
  children: workbookName
4426
4695
  })
4427
4696
  }),
4428
- /* @__PURE__ */ jsx_runtime4.jsxs("span", {
4697
+ /* @__PURE__ */ jsx_runtime5.jsxs("span", {
4429
4698
  className: "rsp-zoom-value",
4430
4699
  "data-testid": `sheet-count-${workbookName}`,
4431
4700
  children: [
@@ -4435,16 +4704,19 @@ function FormulaWorkbook({
4435
4704
  })
4436
4705
  ]
4437
4706
  }),
4438
- /* @__PURE__ */ jsx_runtime4.jsx("div", {
4439
- style: { flex: 1, overflow: "hidden" },
4440
- children: /* @__PURE__ */ jsx_runtime4.jsx("div", {
4707
+ /* @__PURE__ */ jsx_runtime5.jsx("div", {
4708
+ id: panelId,
4709
+ role: "tabpanel",
4710
+ "aria-label": activeSheet || "Sheet",
4711
+ style: { flex: 1, minHeight: 0, overflow: "hidden" },
4712
+ children: /* @__PURE__ */ jsx_runtime5.jsx("div", {
4441
4713
  style: {
4442
4714
  transform: spreadsheetZoom !== 1 ? `scale(${spreadsheetZoom})` : undefined,
4443
4715
  transformOrigin: "0 0",
4444
4716
  width: spreadsheetZoom !== 1 ? `${100 / spreadsheetZoom}%` : "100%",
4445
4717
  height: spreadsheetZoom !== 1 ? `${100 / spreadsheetZoom}%` : "100%"
4446
4718
  },
4447
- children: /* @__PURE__ */ jsx_runtime4.jsx(FormulaSheet, {
4719
+ children: /* @__PURE__ */ jsx_runtime5.jsx(FormulaSheet, {
4448
4720
  sheetName: activeSheet,
4449
4721
  workbookName,
4450
4722
  engine,
@@ -4468,138 +4740,53 @@ function FormulaWorkbook({
4468
4740
  }, `${workbookName}-${activeSheet}`)
4469
4741
  })
4470
4742
  }),
4471
- /* @__PURE__ */ jsx_runtime4.jsxs("div", {
4472
- className: "rsp-sheet-tabs",
4473
- children: [
4474
- /* @__PURE__ */ jsx_runtime4.jsxs("div", {
4475
- className: "rsp-sheet-tabs-scroll",
4476
- children: [
4477
- /* @__PURE__ */ jsx_runtime4.jsx("div", {
4478
- style: { display: "flex", alignItems: "center", gap: 4 },
4479
- children: sheetNames.map((sheetName) => {
4480
- const isRenaming = renamingSheet === sheetName;
4481
- const isActive = activeSheet === sheetName;
4482
- return /* @__PURE__ */ jsx_runtime4.jsx("div", {
4483
- children: isRenaming ? /* @__PURE__ */ jsx_runtime4.jsx("div", {
4484
- style: { display: "flex", alignItems: "center", gap: 4 },
4485
- children: /* @__PURE__ */ jsx_runtime4.jsx("input", {
4486
- className: "rsp-sheet-tab-input",
4487
- type: "text",
4488
- value: newSheetName,
4489
- onChange: (e) => setNewSheetName(e.target.value),
4490
- onKeyDown: (e) => {
4491
- if (e.key === "Enter") {
4492
- renameSheet(sheetName, newSheetName);
4493
- } else if (e.key === "Escape") {
4494
- cancelRenaming();
4495
- }
4496
- },
4497
- onBlur: () => {
4498
- if (newSheetName.trim() && newSheetName.trim() !== sheetName) {
4499
- renameSheet(sheetName, newSheetName);
4500
- } else {
4501
- cancelRenaming();
4502
- }
4503
- },
4504
- autoFocus: true,
4505
- "data-testid": `rename-sheet-input-${sheetName}`
4506
- })
4507
- }) : /* @__PURE__ */ jsx_runtime4.jsxs("div", {
4508
- className: cn("rsp-sheet-tab", {
4509
- "rsp-sheet-tab-active": isActive
4510
- }),
4511
- onClick: () => handleActiveSheetChange(sheetName),
4512
- onDoubleClick: () => startRenaming(sheetName),
4513
- "data-testid": `sheet-tab-${sheetName}`,
4514
- children: [
4515
- /* @__PURE__ */ jsx_runtime4.jsx("span", {
4516
- children: sheetName
4517
- }),
4518
- /* @__PURE__ */ jsx_runtime4.jsxs("div", {
4519
- className: "sheet-tab-actions",
4520
- children: [
4521
- /* @__PURE__ */ jsx_runtime4.jsx("button", {
4522
- type: "button",
4523
- className: "rsp-icon-button",
4524
- onClick: (e) => {
4525
- e.stopPropagation();
4526
- startRenaming(sheetName);
4527
- },
4528
- title: "Rename Sheet",
4529
- "data-testid": `rename-sheet-${sheetName}`,
4530
- children: /* @__PURE__ */ jsx_runtime4.jsx(Edit2Icon, {
4531
- style: { width: 12, height: 12 }
4532
- })
4533
- }),
4534
- sheetNames.length > 1 && /* @__PURE__ */ jsx_runtime4.jsx("button", {
4535
- type: "button",
4536
- className: "rsp-icon-button rsp-icon-button-delete",
4537
- onClick: (e) => {
4538
- e.stopPropagation();
4539
- if (confirm(`Are you sure you want to delete sheet "${sheetName}"?`)) {
4540
- deleteSheet(sheetName);
4541
- }
4542
- },
4543
- title: "Delete Sheet",
4544
- "data-testid": `delete-sheet-${sheetName}`,
4545
- children: /* @__PURE__ */ jsx_runtime4.jsx(Trash2Icon, {
4546
- style: { width: 12, height: 12 }
4547
- })
4548
- })
4549
- ]
4550
- })
4551
- ]
4552
- })
4553
- }, sheetName);
4554
- })
4555
- }),
4556
- /* @__PURE__ */ jsx_runtime4.jsx("button", {
4557
- type: "button",
4558
- className: "rsp-add-sheet-button",
4559
- onClick: addSheet,
4560
- title: "Add Sheet",
4561
- "data-testid": `add-sheet-${workbookName}`,
4562
- children: /* @__PURE__ */ jsx_runtime4.jsx(PlusIcon, {
4563
- style: { width: 12, height: 12 }
4564
- })
4743
+ /* @__PURE__ */ jsx_runtime5.jsx(SheetNavigation, {
4744
+ sheets: sheetNames,
4745
+ activeSheet,
4746
+ workbookName,
4747
+ panelId,
4748
+ onSelect: handleActiveSheetChange,
4749
+ onAdd: addSheet,
4750
+ onRename: renameSheet,
4751
+ onDelete: deleteSheet,
4752
+ children: /* @__PURE__ */ jsx_runtime5.jsxs("div", {
4753
+ className: "rsp-zoom-controls",
4754
+ children: [
4755
+ /* @__PURE__ */ jsx_runtime5.jsx("button", {
4756
+ type: "button",
4757
+ className: "rsp-icon-button",
4758
+ onClick: () => setSpreadsheetZoom(Math.max(0.5, Math.round((spreadsheetZoom - 0.1) * 10) / 10)),
4759
+ disabled: spreadsheetZoom <= 0.5,
4760
+ title: "Zoom Out",
4761
+ style: { opacity: spreadsheetZoom <= 0.5 ? 0.5 : 1 },
4762
+ children: /* @__PURE__ */ jsx_runtime5.jsx(MinusIcon, {
4763
+ style: { width: 12, height: 12 }
4565
4764
  })
4566
- ]
4567
- }),
4568
- /* @__PURE__ */ jsx_runtime4.jsxs("div", {
4569
- className: "rsp-zoom-controls",
4570
- children: [
4571
- /* @__PURE__ */ jsx_runtime4.jsx("button", {
4572
- type: "button",
4573
- className: "rsp-icon-button",
4574
- onClick: () => setSpreadsheetZoom(Math.max(0.5, spreadsheetZoom - 0.1)),
4575
- disabled: spreadsheetZoom <= 0.5,
4576
- title: "Zoom Out",
4577
- style: { opacity: spreadsheetZoom <= 0.5 ? 0.5 : 1 },
4578
- children: /* @__PURE__ */ jsx_runtime4.jsx(MinusIcon, {
4579
- style: { width: 12, height: 12 }
4580
- })
4581
- }),
4582
- /* @__PURE__ */ jsx_runtime4.jsxs("span", {
4583
- className: "rsp-zoom-value",
4584
- children: [
4585
- Math.round(spreadsheetZoom * 100),
4586
- "%"
4587
- ]
4588
- }),
4589
- /* @__PURE__ */ jsx_runtime4.jsx("button", {
4590
- type: "button",
4591
- className: "rsp-icon-button",
4592
- onClick: () => setSpreadsheetZoom(Math.min(2, spreadsheetZoom + 0.1)),
4593
- disabled: spreadsheetZoom >= 2,
4594
- title: "Zoom In",
4595
- style: { opacity: spreadsheetZoom >= 2 ? 0.5 : 1 },
4596
- children: /* @__PURE__ */ jsx_runtime4.jsx(PlusIcon, {
4597
- style: { width: 12, height: 12 }
4598
- })
4765
+ }),
4766
+ /* @__PURE__ */ jsx_runtime5.jsxs("button", {
4767
+ type: "button",
4768
+ className: "rsp-zoom-value rsp-zoom-reset",
4769
+ "aria-label": `Zoom ${Math.round(spreadsheetZoom * 100)}%. Reset to 100%`,
4770
+ title: "Reset zoom to 100%",
4771
+ onClick: () => setSpreadsheetZoom(1),
4772
+ children: [
4773
+ Math.round(spreadsheetZoom * 100),
4774
+ "%"
4775
+ ]
4776
+ }),
4777
+ /* @__PURE__ */ jsx_runtime5.jsx("button", {
4778
+ type: "button",
4779
+ className: "rsp-icon-button",
4780
+ onClick: () => setSpreadsheetZoom(Math.min(2, Math.round((spreadsheetZoom + 0.1) * 10) / 10)),
4781
+ disabled: spreadsheetZoom >= 2,
4782
+ title: "Zoom In",
4783
+ style: { opacity: spreadsheetZoom >= 2 ? 0.5 : 1 },
4784
+ children: /* @__PURE__ */ jsx_runtime5.jsx(PlusIcon, {
4785
+ style: { width: 12, height: 12 }
4599
4786
  })
4600
- ]
4601
- })
4602
- ]
4787
+ })
4788
+ ]
4789
+ })
4603
4790
  })
4604
4791
  ]
4605
4792
  });
@@ -4611,9 +4798,9 @@ var DEFAULT_ZOOM = 1;
4611
4798
  var MIN_ZOOM = 0.5;
4612
4799
  var MAX_ZOOM = 3;
4613
4800
  // src/utils/use-click-away.ts
4614
- var import_react7 = require("react");
4801
+ var import_react8 = require("react");
4615
4802
  var useClickAway = (ref, callback) => {
4616
- import_react7.useEffect(() => {
4803
+ import_react8.useEffect(() => {
4617
4804
  const handleClickAway = (event) => {
4618
4805
  if (ref) {
4619
4806
  const away = !ref.contains(event.target);
@@ -4627,4 +4814,4 @@ var useClickAway = (ref, callback) => {
4627
4814
  }, [ref, callback]);
4628
4815
  };
4629
4816
 
4630
- //# debugId=07CD4F6A4866444B64756E2164756E21
4817
+ //# debugId=0C8DF59ED93BC04F64756E2164756E21