@ricsam/react-spreadsheets 0.0.9 → 0.0.11
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 +28 -0
- package/dist/cjs/lib.cjs +85 -34
- package/dist/cjs/lib.cjs.map +5 -5
- package/dist/cjs/package.json +1 -1
- package/dist/mjs/lib.mjs +88 -37
- package/dist/mjs/lib.mjs.map +5 -5
- package/dist/mjs/package.json +1 -1
- package/dist/types/lib.d.ts +2 -2
- package/dist/types/workbook/workbook-selection-manager.d.ts +22 -7
- package/dist/types/workbook/workbook.d.ts +8 -4
- package/package.json +3 -3
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:
|
|
@@ -316,6 +339,11 @@ Per-cell styling is done in JS:
|
|
|
316
339
|
| `selection` | Selection state, navigation model, and `effects(selectionManager)` for copy/paste/fill/reference hooks. |
|
|
317
340
|
| `components` + `overlayChildren` | Floating overlays anchored to the grid, optionally snapped to cell edges. |
|
|
318
341
|
|
|
342
|
+
Wheel input over the grid remains available when its cell selection is blurred.
|
|
343
|
+
Wheel events originating inside a floating overlay are left to that overlay, so
|
|
344
|
+
scrollable editors and other interactive overlay content keep their native
|
|
345
|
+
scroll behavior.
|
|
346
|
+
|
|
319
347
|
## Development
|
|
320
348
|
|
|
321
349
|
```bash
|
package/dist/cjs/lib.cjs
CHANGED
|
@@ -1434,6 +1434,7 @@ var resizeCellSnapAnchorToNearestEdges = (anchor, rect, handle, columnWidths, ro
|
|
|
1434
1434
|
|
|
1435
1435
|
// src/spreadsheet/spreadsheet.tsx
|
|
1436
1436
|
var jsx_runtime3 = require("react/jsx-runtime");
|
|
1437
|
+
var SPREADSHEET_OVERLAY_SELECTOR = "[data-rsp-overlay]";
|
|
1437
1438
|
var resolveGridLineColor = (probe, colorScheme) => {
|
|
1438
1439
|
const fallback = colorScheme === "dark" ? "#232c3d" : "#e8ebf1";
|
|
1439
1440
|
if (!probe || typeof getComputedStyle !== "function") {
|
|
@@ -2394,6 +2395,12 @@ var Spreadsheet = import_react4.forwardRef(({
|
|
|
2394
2395
|
});
|
|
2395
2396
|
}, [selectionManager, clipboardManager, disableClipboard]);
|
|
2396
2397
|
const handleWheel = import_react4.useCallback((e) => {
|
|
2398
|
+
const target = e.target;
|
|
2399
|
+
const currentTarget = e.currentTarget;
|
|
2400
|
+
const overlay = target instanceof Element ? target.closest(SPREADSHEET_OVERLAY_SELECTOR) : null;
|
|
2401
|
+
if (overlay && currentTarget instanceof Node && currentTarget.contains(overlay)) {
|
|
2402
|
+
return;
|
|
2403
|
+
}
|
|
2397
2404
|
e.preventDefault();
|
|
2398
2405
|
e.stopPropagation();
|
|
2399
2406
|
setViewport((previous) => ({
|
|
@@ -3119,6 +3126,7 @@ var Spreadsheet = import_react4.forwardRef(({
|
|
|
3119
3126
|
const isTextBox = child.component === "RichText";
|
|
3120
3127
|
const isMinimized = Boolean(child.minimized && !isTextBox);
|
|
3121
3128
|
return /* @__PURE__ */ jsx_runtime3.jsxs("div", {
|
|
3129
|
+
"data-rsp-overlay": child.id,
|
|
3122
3130
|
style: {
|
|
3123
3131
|
position: "absolute",
|
|
3124
3132
|
width: child.width,
|
|
@@ -3312,9 +3320,24 @@ var import_formula_engine = require("@ricsam/formula-engine");
|
|
|
3312
3320
|
class WorkbookSelectionManager {
|
|
3313
3321
|
managers = new Map;
|
|
3314
3322
|
lastFocusedManager = null;
|
|
3323
|
+
lastSelections = [];
|
|
3324
|
+
selectionsByView = new Map;
|
|
3315
3325
|
listeners = new Set;
|
|
3316
3326
|
cleanupFunctions = new Map;
|
|
3317
3327
|
pendingRangeFocus = null;
|
|
3328
|
+
applyingRangeFocus = new Set;
|
|
3329
|
+
getViewKey(context) {
|
|
3330
|
+
return JSON.stringify([context.workbookName, context.sheetName, context.viewId ?? null]);
|
|
3331
|
+
}
|
|
3332
|
+
cloneSmArea(area) {
|
|
3333
|
+
return {
|
|
3334
|
+
start: { row: area.start.row, col: area.start.col },
|
|
3335
|
+
end: {
|
|
3336
|
+
row: area.end.row.type === "infinity" ? { type: "infinity" } : { type: "number", value: area.end.row.value },
|
|
3337
|
+
col: area.end.col.type === "infinity" ? { type: "infinity" } : { type: "number", value: area.end.col.value }
|
|
3338
|
+
}
|
|
3339
|
+
};
|
|
3340
|
+
}
|
|
3318
3341
|
smAreaToSpreadsheetRange(area) {
|
|
3319
3342
|
const rowEnd = area.end.row.type === "infinity" ? { type: "infinity", sign: "positive" } : { type: "number", value: area.end.row.value };
|
|
3320
3343
|
const colEnd = area.end.col.type === "infinity" ? { type: "infinity", sign: "positive" } : { type: "number", value: area.end.col.value };
|
|
@@ -3345,43 +3368,69 @@ class WorkbookSelectionManager {
|
|
|
3345
3368
|
range: this.smAreaToSpreadsheetRange(this.spreadsheetRangeToSmArea(address.range))
|
|
3346
3369
|
};
|
|
3347
3370
|
}
|
|
3348
|
-
|
|
3349
|
-
return
|
|
3371
|
+
areasToRangeAddresses(areas, context) {
|
|
3372
|
+
return areas.map((area) => ({
|
|
3373
|
+
workbookName: context.workbookName,
|
|
3374
|
+
sheetName: context.sheetName,
|
|
3375
|
+
range: this.smAreaToSpreadsheetRange(area)
|
|
3376
|
+
}));
|
|
3377
|
+
}
|
|
3378
|
+
rememberSelections(manager, context, areas = manager.getState().selections) {
|
|
3379
|
+
const cachedAreas = areas.map((area) => this.cloneSmArea(area));
|
|
3380
|
+
this.selectionsByView.set(this.getViewKey(context), cachedAreas);
|
|
3381
|
+
this.lastFocusedManager = { sm: manager, context };
|
|
3382
|
+
this.lastSelections = this.areasToRangeAddresses(cachedAreas, context);
|
|
3383
|
+
}
|
|
3384
|
+
matchesContext(address, context, viewId) {
|
|
3385
|
+
return address.workbookName === context.workbookName && address.sheetName === context.sheetName && (viewId === undefined || context.viewId === viewId);
|
|
3350
3386
|
}
|
|
3351
3387
|
applyRangeFocus(manager, context, request) {
|
|
3352
3388
|
const selection = this.spreadsheetRangeToSmArea(request.address.range);
|
|
3353
|
-
|
|
3354
|
-
|
|
3389
|
+
this.applyingRangeFocus.add(manager);
|
|
3390
|
+
try {
|
|
3391
|
+
manager.replaceSelections([selection]);
|
|
3392
|
+
manager.focus();
|
|
3393
|
+
} finally {
|
|
3394
|
+
this.applyingRangeFocus.delete(manager);
|
|
3395
|
+
}
|
|
3355
3396
|
manager.revealRange(selection, request.options);
|
|
3356
|
-
this.
|
|
3397
|
+
this.rememberSelections(manager, context, [selection]);
|
|
3357
3398
|
this.emitChange();
|
|
3358
3399
|
}
|
|
3359
3400
|
applyPendingRangeFocusOnMount(manager, context) {
|
|
3360
3401
|
const pending = this.pendingRangeFocus;
|
|
3361
|
-
if (!pending || !this.matchesContext(pending.address, context))
|
|
3402
|
+
if (!pending || !this.matchesContext(pending.address, context, pending.options.viewId))
|
|
3362
3403
|
return;
|
|
3363
3404
|
queueMicrotask(() => {
|
|
3364
3405
|
if (this.pendingRangeFocus !== pending)
|
|
3365
3406
|
return;
|
|
3366
3407
|
const mountedContext = this.managers.get(manager);
|
|
3367
|
-
if (!mountedContext || !this.matchesContext(pending.address, mountedContext))
|
|
3408
|
+
if (!mountedContext || !this.matchesContext(pending.address, mountedContext, pending.options.viewId)) {
|
|
3368
3409
|
return;
|
|
3410
|
+
}
|
|
3369
3411
|
this.pendingRangeFocus = null;
|
|
3370
3412
|
this.applyRangeFocus(manager, mountedContext, pending);
|
|
3371
3413
|
});
|
|
3372
3414
|
}
|
|
3373
3415
|
add(manager, context) {
|
|
3374
|
-
|
|
3416
|
+
const registeredContext = { ...context };
|
|
3417
|
+
this.managers.set(manager, registeredContext);
|
|
3418
|
+
const cachedSelections = this.selectionsByView.get(this.getViewKey(registeredContext));
|
|
3419
|
+
if (cachedSelections && manager.getState().selections.length === 0) {
|
|
3420
|
+
manager.replaceSelections(cachedSelections);
|
|
3421
|
+
}
|
|
3375
3422
|
const focusCleanup = manager.observeStateChange((state) => state.hasFocus, (hasFocus) => {
|
|
3376
|
-
if (hasFocus) {
|
|
3377
|
-
this.
|
|
3423
|
+
if (hasFocus && !this.applyingRangeFocus.has(manager)) {
|
|
3424
|
+
this.rememberSelections(manager, registeredContext);
|
|
3378
3425
|
this.emitChange();
|
|
3379
3426
|
}
|
|
3380
3427
|
});
|
|
3381
3428
|
const selectionCleanup = manager.observeStateChange((state) => JSON.stringify(state.selections), (selectionsJson) => {
|
|
3429
|
+
if (this.applyingRangeFocus.has(manager))
|
|
3430
|
+
return;
|
|
3382
3431
|
const selections = JSON.parse(selectionsJson);
|
|
3383
3432
|
if (selections && selections.length > 0) {
|
|
3384
|
-
this.
|
|
3433
|
+
this.rememberSelections(manager, registeredContext, selections);
|
|
3385
3434
|
this.emitChange();
|
|
3386
3435
|
}
|
|
3387
3436
|
});
|
|
@@ -3391,27 +3440,19 @@ class WorkbookSelectionManager {
|
|
|
3391
3440
|
this.managers.delete(manager);
|
|
3392
3441
|
this.cleanupFunctions.delete(manager);
|
|
3393
3442
|
if (this.lastFocusedManager?.sm === manager) {
|
|
3443
|
+
this.rememberSelections(manager, registeredContext);
|
|
3394
3444
|
this.lastFocusedManager = null;
|
|
3395
3445
|
}
|
|
3396
3446
|
};
|
|
3397
3447
|
this.cleanupFunctions.set(manager, cleanup);
|
|
3398
|
-
this.applyPendingRangeFocusOnMount(manager,
|
|
3448
|
+
this.applyPendingRangeFocusOnMount(manager, registeredContext);
|
|
3399
3449
|
return cleanup;
|
|
3400
3450
|
}
|
|
3401
3451
|
getLastFocusedSelectionManager() {
|
|
3402
3452
|
return this.lastFocusedManager;
|
|
3403
3453
|
}
|
|
3404
3454
|
getSelections() {
|
|
3405
|
-
|
|
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
|
-
}));
|
|
3455
|
+
return this.lastSelections.map((selection) => this.cloneRangeAddress(selection));
|
|
3415
3456
|
}
|
|
3416
3457
|
getLastSelection() {
|
|
3417
3458
|
const selections = this.getSelections();
|
|
@@ -3460,35 +3501,37 @@ class WorkbookSelectionManager {
|
|
|
3460
3501
|
return `[${workbookName}]${sheetRef}!${rangeStr}`;
|
|
3461
3502
|
}
|
|
3462
3503
|
blur() {
|
|
3463
|
-
this.managers.forEach((
|
|
3504
|
+
this.managers.forEach((_context, manager) => {
|
|
3464
3505
|
manager.clearSelections();
|
|
3465
3506
|
manager.blur();
|
|
3466
3507
|
});
|
|
3508
|
+
this.selectionsByView.clear();
|
|
3467
3509
|
this.lastFocusedManager = null;
|
|
3510
|
+
this.lastSelections = [];
|
|
3468
3511
|
this.emitChange();
|
|
3469
3512
|
}
|
|
3470
|
-
focusWorkbook(workbookName) {
|
|
3513
|
+
focusWorkbook(workbookName, viewId) {
|
|
3471
3514
|
for (const [manager, context] of this.managers) {
|
|
3472
|
-
if (context.workbookName === workbookName) {
|
|
3515
|
+
if (context.workbookName === workbookName && (viewId === undefined || context.viewId === viewId)) {
|
|
3473
3516
|
manager.focus();
|
|
3474
|
-
this.
|
|
3517
|
+
this.rememberSelections(manager, context);
|
|
3475
3518
|
this.emitChange();
|
|
3476
3519
|
return;
|
|
3477
3520
|
}
|
|
3478
3521
|
}
|
|
3479
3522
|
}
|
|
3480
|
-
focusSheet(workbookName, sheetName) {
|
|
3523
|
+
focusSheet(workbookName, sheetName, viewId) {
|
|
3481
3524
|
for (const [manager, context] of this.managers) {
|
|
3482
|
-
if (context.workbookName === workbookName && context.sheetName === sheetName) {
|
|
3525
|
+
if (context.workbookName === workbookName && context.sheetName === sheetName && (viewId === undefined || context.viewId === viewId)) {
|
|
3483
3526
|
manager.focus();
|
|
3484
|
-
this.
|
|
3527
|
+
this.rememberSelections(manager, context);
|
|
3485
3528
|
this.emitChange();
|
|
3486
3529
|
return true;
|
|
3487
3530
|
}
|
|
3488
3531
|
}
|
|
3489
3532
|
return false;
|
|
3490
3533
|
}
|
|
3491
|
-
focusCell(workbookName, sheetName, row, col) {
|
|
3534
|
+
focusCell(workbookName, sheetName, row, col, options = {}) {
|
|
3492
3535
|
return this.focusRange({
|
|
3493
3536
|
workbookName,
|
|
3494
3537
|
sheetName,
|
|
@@ -3499,7 +3542,7 @@ class WorkbookSelectionManager {
|
|
|
3499
3542
|
col: { type: "number", value: col }
|
|
3500
3543
|
}
|
|
3501
3544
|
}
|
|
3502
|
-
});
|
|
3545
|
+
}, options);
|
|
3503
3546
|
}
|
|
3504
3547
|
focusRange(address, options = {}) {
|
|
3505
3548
|
const request = {
|
|
@@ -3507,7 +3550,7 @@ class WorkbookSelectionManager {
|
|
|
3507
3550
|
options: { ...options }
|
|
3508
3551
|
};
|
|
3509
3552
|
for (const [manager, context] of this.managers) {
|
|
3510
|
-
if (!this.matchesContext(request.address, context))
|
|
3553
|
+
if (!this.matchesContext(request.address, context, request.options.viewId))
|
|
3511
3554
|
continue;
|
|
3512
3555
|
this.pendingRangeFocus = null;
|
|
3513
3556
|
this.applyRangeFocus(manager, context, request);
|
|
@@ -3516,6 +3559,9 @@ class WorkbookSelectionManager {
|
|
|
3516
3559
|
this.pendingRangeFocus = request;
|
|
3517
3560
|
return false;
|
|
3518
3561
|
}
|
|
3562
|
+
cancelPendingFocusRange() {
|
|
3563
|
+
this.pendingRangeFocus = null;
|
|
3564
|
+
}
|
|
3519
3565
|
emitChange() {
|
|
3520
3566
|
const currentSelection = this.getLastSelection();
|
|
3521
3567
|
const selections = currentSelection ? [currentSelection] : [];
|
|
@@ -3742,6 +3788,7 @@ function FormulaSheet({
|
|
|
3742
3788
|
verboseErrors = false,
|
|
3743
3789
|
selection,
|
|
3744
3790
|
selectionManager,
|
|
3791
|
+
viewId,
|
|
3745
3792
|
customCellStyle,
|
|
3746
3793
|
customCellRenderer,
|
|
3747
3794
|
onCellDataChangeError = queueCellEditError,
|
|
@@ -3913,7 +3960,8 @@ function FormulaSheet({
|
|
|
3913
3960
|
if (selectionManager) {
|
|
3914
3961
|
const workbookManagerCleanup = selectionManager.add(spreadsheetSelectionManager, {
|
|
3915
3962
|
workbookName,
|
|
3916
|
-
sheetName
|
|
3963
|
+
sheetName,
|
|
3964
|
+
...viewId === undefined ? {} : { viewId }
|
|
3917
3965
|
});
|
|
3918
3966
|
cleanups.push(workbookManagerCleanup);
|
|
3919
3967
|
}
|
|
@@ -3934,6 +3982,7 @@ function FormulaSheet({
|
|
|
3934
3982
|
selectionManager,
|
|
3935
3983
|
sheetName,
|
|
3936
3984
|
showFormulas,
|
|
3985
|
+
viewId,
|
|
3937
3986
|
workbookName
|
|
3938
3987
|
]);
|
|
3939
3988
|
if (!sheet) {
|
|
@@ -4167,6 +4216,7 @@ function FormulaWorkbook({
|
|
|
4167
4216
|
onActiveSheetChange,
|
|
4168
4217
|
verboseErrors = false,
|
|
4169
4218
|
selectionManager,
|
|
4219
|
+
viewId,
|
|
4170
4220
|
components,
|
|
4171
4221
|
sheetOverlayChildren,
|
|
4172
4222
|
sheetSelectedOverlayIds,
|
|
@@ -4334,6 +4384,7 @@ function FormulaWorkbook({
|
|
|
4334
4384
|
engine,
|
|
4335
4385
|
verboseErrors,
|
|
4336
4386
|
selectionManager,
|
|
4387
|
+
viewId,
|
|
4337
4388
|
...rest,
|
|
4338
4389
|
components,
|
|
4339
4390
|
overlayChildren: activeSheetOverlayChildren,
|
|
@@ -4510,4 +4561,4 @@ var useClickAway = (ref, callback) => {
|
|
|
4510
4561
|
}, [ref, callback]);
|
|
4511
4562
|
};
|
|
4512
4563
|
|
|
4513
|
-
//# debugId=
|
|
4564
|
+
//# debugId=64201658718498BD64756E2164756E21
|