@ricsam/react-spreadsheets 0.0.9 → 0.0.10

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
@@ -152,6 +152,29 @@ controlled `FormulaWorkbook` still needs to switch `activeSheet`, it returns
152
152
  `false` and queues the latest request; mounting that sheet applies the focus and
153
153
  reveal automatically.
154
154
 
155
+ If the same workbook can be mounted in more than one place, give each rendered
156
+ view a stable ID and target that ID when navigating. Selection caches are also
157
+ scoped to the view, so switching sheets or remounting one pane cannot restore a
158
+ selection from its duplicate:
159
+
160
+ ```tsx
161
+ <FormulaWorkbook
162
+ viewId="left-pane"
163
+ workbookName="Forecast"
164
+ engine={engine}
165
+ selectionManager={workbookSelection}
166
+ />
167
+
168
+ workbookSelection.focusRange(referenceAddress, {
169
+ align: "nearest",
170
+ viewId: "left-pane",
171
+ });
172
+ ```
173
+
174
+ Omitting `viewId` preserves the single-view API and targets the first matching
175
+ mounted workbook/sheet. Call `cancelPendingFocusRange()` if an app-level sheet
176
+ or pane switch is abandoned before the queued target mounts.
177
+
155
178
  For the range currently being inserted into a formula, use selection-manager's
156
179
  reference-picking mode instead. It preserves the primary/editing selection and
157
180
  the grid renders the picked range with an independent animated border:
package/dist/cjs/lib.cjs CHANGED
@@ -3312,9 +3312,24 @@ var import_formula_engine = require("@ricsam/formula-engine");
3312
3312
  class WorkbookSelectionManager {
3313
3313
  managers = new Map;
3314
3314
  lastFocusedManager = null;
3315
+ lastSelections = [];
3316
+ selectionsByView = new Map;
3315
3317
  listeners = new Set;
3316
3318
  cleanupFunctions = new Map;
3317
3319
  pendingRangeFocus = null;
3320
+ applyingRangeFocus = new Set;
3321
+ getViewKey(context) {
3322
+ return JSON.stringify([context.workbookName, context.sheetName, context.viewId ?? null]);
3323
+ }
3324
+ cloneSmArea(area) {
3325
+ return {
3326
+ start: { row: area.start.row, col: area.start.col },
3327
+ end: {
3328
+ row: area.end.row.type === "infinity" ? { type: "infinity" } : { type: "number", value: area.end.row.value },
3329
+ col: area.end.col.type === "infinity" ? { type: "infinity" } : { type: "number", value: area.end.col.value }
3330
+ }
3331
+ };
3332
+ }
3318
3333
  smAreaToSpreadsheetRange(area) {
3319
3334
  const rowEnd = area.end.row.type === "infinity" ? { type: "infinity", sign: "positive" } : { type: "number", value: area.end.row.value };
3320
3335
  const colEnd = area.end.col.type === "infinity" ? { type: "infinity", sign: "positive" } : { type: "number", value: area.end.col.value };
@@ -3345,43 +3360,69 @@ class WorkbookSelectionManager {
3345
3360
  range: this.smAreaToSpreadsheetRange(this.spreadsheetRangeToSmArea(address.range))
3346
3361
  };
3347
3362
  }
3348
- matchesContext(address, context) {
3349
- return address.workbookName === context.workbookName && address.sheetName === context.sheetName;
3363
+ areasToRangeAddresses(areas, context) {
3364
+ return areas.map((area) => ({
3365
+ workbookName: context.workbookName,
3366
+ sheetName: context.sheetName,
3367
+ range: this.smAreaToSpreadsheetRange(area)
3368
+ }));
3369
+ }
3370
+ rememberSelections(manager, context, areas = manager.getState().selections) {
3371
+ const cachedAreas = areas.map((area) => this.cloneSmArea(area));
3372
+ this.selectionsByView.set(this.getViewKey(context), cachedAreas);
3373
+ this.lastFocusedManager = { sm: manager, context };
3374
+ this.lastSelections = this.areasToRangeAddresses(cachedAreas, context);
3375
+ }
3376
+ matchesContext(address, context, viewId) {
3377
+ return address.workbookName === context.workbookName && address.sheetName === context.sheetName && (viewId === undefined || context.viewId === viewId);
3350
3378
  }
3351
3379
  applyRangeFocus(manager, context, request) {
3352
3380
  const selection = this.spreadsheetRangeToSmArea(request.address.range);
3353
- manager.setState({ selections: [selection] });
3354
- manager.focus();
3381
+ this.applyingRangeFocus.add(manager);
3382
+ try {
3383
+ manager.replaceSelections([selection]);
3384
+ manager.focus();
3385
+ } finally {
3386
+ this.applyingRangeFocus.delete(manager);
3387
+ }
3355
3388
  manager.revealRange(selection, request.options);
3356
- this.lastFocusedManager = { sm: manager, context };
3389
+ this.rememberSelections(manager, context, [selection]);
3357
3390
  this.emitChange();
3358
3391
  }
3359
3392
  applyPendingRangeFocusOnMount(manager, context) {
3360
3393
  const pending = this.pendingRangeFocus;
3361
- if (!pending || !this.matchesContext(pending.address, context))
3394
+ if (!pending || !this.matchesContext(pending.address, context, pending.options.viewId))
3362
3395
  return;
3363
3396
  queueMicrotask(() => {
3364
3397
  if (this.pendingRangeFocus !== pending)
3365
3398
  return;
3366
3399
  const mountedContext = this.managers.get(manager);
3367
- if (!mountedContext || !this.matchesContext(pending.address, mountedContext))
3400
+ if (!mountedContext || !this.matchesContext(pending.address, mountedContext, pending.options.viewId)) {
3368
3401
  return;
3402
+ }
3369
3403
  this.pendingRangeFocus = null;
3370
3404
  this.applyRangeFocus(manager, mountedContext, pending);
3371
3405
  });
3372
3406
  }
3373
3407
  add(manager, context) {
3374
- this.managers.set(manager, context);
3408
+ const registeredContext = { ...context };
3409
+ this.managers.set(manager, registeredContext);
3410
+ const cachedSelections = this.selectionsByView.get(this.getViewKey(registeredContext));
3411
+ if (cachedSelections && manager.getState().selections.length === 0) {
3412
+ manager.replaceSelections(cachedSelections);
3413
+ }
3375
3414
  const focusCleanup = manager.observeStateChange((state) => state.hasFocus, (hasFocus) => {
3376
- if (hasFocus) {
3377
- this.lastFocusedManager = { sm: manager, context };
3415
+ if (hasFocus && !this.applyingRangeFocus.has(manager)) {
3416
+ this.rememberSelections(manager, registeredContext);
3378
3417
  this.emitChange();
3379
3418
  }
3380
3419
  });
3381
3420
  const selectionCleanup = manager.observeStateChange((state) => JSON.stringify(state.selections), (selectionsJson) => {
3421
+ if (this.applyingRangeFocus.has(manager))
3422
+ return;
3382
3423
  const selections = JSON.parse(selectionsJson);
3383
3424
  if (selections && selections.length > 0) {
3384
- this.lastFocusedManager = { sm: manager, context };
3425
+ this.rememberSelections(manager, registeredContext, selections);
3385
3426
  this.emitChange();
3386
3427
  }
3387
3428
  });
@@ -3391,27 +3432,19 @@ class WorkbookSelectionManager {
3391
3432
  this.managers.delete(manager);
3392
3433
  this.cleanupFunctions.delete(manager);
3393
3434
  if (this.lastFocusedManager?.sm === manager) {
3435
+ this.rememberSelections(manager, registeredContext);
3394
3436
  this.lastFocusedManager = null;
3395
3437
  }
3396
3438
  };
3397
3439
  this.cleanupFunctions.set(manager, cleanup);
3398
- this.applyPendingRangeFocusOnMount(manager, context);
3440
+ this.applyPendingRangeFocusOnMount(manager, registeredContext);
3399
3441
  return cleanup;
3400
3442
  }
3401
3443
  getLastFocusedSelectionManager() {
3402
3444
  return this.lastFocusedManager;
3403
3445
  }
3404
3446
  getSelections() {
3405
- if (!this.lastFocusedManager) {
3406
- return [];
3407
- }
3408
- const { sm, context } = this.lastFocusedManager;
3409
- const state = sm.getState();
3410
- return state.selections.map((area) => ({
3411
- workbookName: context.workbookName,
3412
- sheetName: context.sheetName,
3413
- range: this.smAreaToSpreadsheetRange(area)
3414
- }));
3447
+ return this.lastSelections.map((selection) => this.cloneRangeAddress(selection));
3415
3448
  }
3416
3449
  getLastSelection() {
3417
3450
  const selections = this.getSelections();
@@ -3460,35 +3493,37 @@ class WorkbookSelectionManager {
3460
3493
  return `[${workbookName}]${sheetRef}!${rangeStr}`;
3461
3494
  }
3462
3495
  blur() {
3463
- this.managers.forEach((context, manager) => {
3496
+ this.managers.forEach((_context, manager) => {
3464
3497
  manager.clearSelections();
3465
3498
  manager.blur();
3466
3499
  });
3500
+ this.selectionsByView.clear();
3467
3501
  this.lastFocusedManager = null;
3502
+ this.lastSelections = [];
3468
3503
  this.emitChange();
3469
3504
  }
3470
- focusWorkbook(workbookName) {
3505
+ focusWorkbook(workbookName, viewId) {
3471
3506
  for (const [manager, context] of this.managers) {
3472
- if (context.workbookName === workbookName) {
3507
+ if (context.workbookName === workbookName && (viewId === undefined || context.viewId === viewId)) {
3473
3508
  manager.focus();
3474
- this.lastFocusedManager = { sm: manager, context };
3509
+ this.rememberSelections(manager, context);
3475
3510
  this.emitChange();
3476
3511
  return;
3477
3512
  }
3478
3513
  }
3479
3514
  }
3480
- focusSheet(workbookName, sheetName) {
3515
+ focusSheet(workbookName, sheetName, viewId) {
3481
3516
  for (const [manager, context] of this.managers) {
3482
- if (context.workbookName === workbookName && context.sheetName === sheetName) {
3517
+ if (context.workbookName === workbookName && context.sheetName === sheetName && (viewId === undefined || context.viewId === viewId)) {
3483
3518
  manager.focus();
3484
- this.lastFocusedManager = { sm: manager, context };
3519
+ this.rememberSelections(manager, context);
3485
3520
  this.emitChange();
3486
3521
  return true;
3487
3522
  }
3488
3523
  }
3489
3524
  return false;
3490
3525
  }
3491
- focusCell(workbookName, sheetName, row, col) {
3526
+ focusCell(workbookName, sheetName, row, col, options = {}) {
3492
3527
  return this.focusRange({
3493
3528
  workbookName,
3494
3529
  sheetName,
@@ -3499,7 +3534,7 @@ class WorkbookSelectionManager {
3499
3534
  col: { type: "number", value: col }
3500
3535
  }
3501
3536
  }
3502
- });
3537
+ }, options);
3503
3538
  }
3504
3539
  focusRange(address, options = {}) {
3505
3540
  const request = {
@@ -3507,7 +3542,7 @@ class WorkbookSelectionManager {
3507
3542
  options: { ...options }
3508
3543
  };
3509
3544
  for (const [manager, context] of this.managers) {
3510
- if (!this.matchesContext(request.address, context))
3545
+ if (!this.matchesContext(request.address, context, request.options.viewId))
3511
3546
  continue;
3512
3547
  this.pendingRangeFocus = null;
3513
3548
  this.applyRangeFocus(manager, context, request);
@@ -3516,6 +3551,9 @@ class WorkbookSelectionManager {
3516
3551
  this.pendingRangeFocus = request;
3517
3552
  return false;
3518
3553
  }
3554
+ cancelPendingFocusRange() {
3555
+ this.pendingRangeFocus = null;
3556
+ }
3519
3557
  emitChange() {
3520
3558
  const currentSelection = this.getLastSelection();
3521
3559
  const selections = currentSelection ? [currentSelection] : [];
@@ -3742,6 +3780,7 @@ function FormulaSheet({
3742
3780
  verboseErrors = false,
3743
3781
  selection,
3744
3782
  selectionManager,
3783
+ viewId,
3745
3784
  customCellStyle,
3746
3785
  customCellRenderer,
3747
3786
  onCellDataChangeError = queueCellEditError,
@@ -3913,7 +3952,8 @@ function FormulaSheet({
3913
3952
  if (selectionManager) {
3914
3953
  const workbookManagerCleanup = selectionManager.add(spreadsheetSelectionManager, {
3915
3954
  workbookName,
3916
- sheetName
3955
+ sheetName,
3956
+ ...viewId === undefined ? {} : { viewId }
3917
3957
  });
3918
3958
  cleanups.push(workbookManagerCleanup);
3919
3959
  }
@@ -3934,6 +3974,7 @@ function FormulaSheet({
3934
3974
  selectionManager,
3935
3975
  sheetName,
3936
3976
  showFormulas,
3977
+ viewId,
3937
3978
  workbookName
3938
3979
  ]);
3939
3980
  if (!sheet) {
@@ -4167,6 +4208,7 @@ function FormulaWorkbook({
4167
4208
  onActiveSheetChange,
4168
4209
  verboseErrors = false,
4169
4210
  selectionManager,
4211
+ viewId,
4170
4212
  components,
4171
4213
  sheetOverlayChildren,
4172
4214
  sheetSelectedOverlayIds,
@@ -4334,6 +4376,7 @@ function FormulaWorkbook({
4334
4376
  engine,
4335
4377
  verboseErrors,
4336
4378
  selectionManager,
4379
+ viewId,
4337
4380
  ...rest,
4338
4381
  components,
4339
4382
  overlayChildren: activeSheetOverlayChildren,
@@ -4510,4 +4553,4 @@ var useClickAway = (ref, callback) => {
4510
4553
  }, [ref, callback]);
4511
4554
  };
4512
4555
 
4513
- //# debugId=81701030D90BCDFB64756E2164756E21
4556
+ //# debugId=E584D6343578997564756E2164756E21