@canvas-components/pivot-table 0.1.7 → 0.1.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +431 -96
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +30 -2
- package/dist/index.d.ts +30 -2
- package/dist/index.js +431 -96
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.cjs
CHANGED
|
@@ -2613,22 +2613,6 @@ function createPivotDomHost(container) {
|
|
|
2613
2613
|
}
|
|
2614
2614
|
};
|
|
2615
2615
|
}
|
|
2616
|
-
async function writePivotClipboardText(text, ownerDocument) {
|
|
2617
|
-
const syncCopied = utils.copyTextSync(text, ownerDocument);
|
|
2618
|
-
logPivotClipboard("sync-copy-result", { copied: syncCopied });
|
|
2619
|
-
if (syncCopied) return true;
|
|
2620
|
-
const copied = await utils.copyText(text, ownerDocument);
|
|
2621
|
-
logPivotClipboard("copy-result", { copied });
|
|
2622
|
-
return copied;
|
|
2623
|
-
}
|
|
2624
|
-
function copyPivotClipboardTextSync(text, ownerDocument) {
|
|
2625
|
-
const copied = utils.copyTextSync(text, ownerDocument);
|
|
2626
|
-
logPivotClipboard("sync-copy-result", { copied });
|
|
2627
|
-
return copied;
|
|
2628
|
-
}
|
|
2629
|
-
function logPivotClipboard(stage, detail) {
|
|
2630
|
-
console.info(`[PivotTable:clipboard] ${stage}`, detail);
|
|
2631
|
-
}
|
|
2632
2616
|
|
|
2633
2617
|
// src/adapters/canvas-host.ts
|
|
2634
2618
|
function resizePivotCanvas(canvas, width, height) {
|
|
@@ -2993,7 +2977,7 @@ function renderPivotTable(params) {
|
|
|
2993
2977
|
});
|
|
2994
2978
|
withClip(context, layout.cornerRect, () => {
|
|
2995
2979
|
fillRect(context, layout.cornerRect, theme.cornerBackgroundColor);
|
|
2996
|
-
drawCorner(context, layout, theme);
|
|
2980
|
+
drawCorner(context, layout, theme, params.selection?.textSelection);
|
|
2997
2981
|
});
|
|
2998
2982
|
withClip(context, layout.rowHeaderRect, () => {
|
|
2999
2983
|
layout.rowHeaderCells.forEach((cell) => {
|
|
@@ -3007,7 +2991,9 @@ function renderPivotTable(params) {
|
|
|
3007
2991
|
cell,
|
|
3008
2992
|
theme,
|
|
3009
2993
|
!areaSelection && isHeaderSelected(cell, selectionPathKeys.row),
|
|
3010
|
-
contentRect
|
|
2994
|
+
contentRect,
|
|
2995
|
+
void 0,
|
|
2996
|
+
params.selection?.textSelection
|
|
3011
2997
|
);
|
|
3012
2998
|
});
|
|
3013
2999
|
layout.rowHeaderCells.forEach(
|
|
@@ -3027,7 +3013,10 @@ function renderPivotTable(params) {
|
|
|
3027
3013
|
context,
|
|
3028
3014
|
cell,
|
|
3029
3015
|
theme,
|
|
3030
|
-
!areaSelection && isHeaderSelected(cell, selectionPathKeys.column)
|
|
3016
|
+
!areaSelection && isHeaderSelected(cell, selectionPathKeys.column),
|
|
3017
|
+
cell.rect,
|
|
3018
|
+
void 0,
|
|
3019
|
+
params.selection?.textSelection
|
|
3031
3020
|
)
|
|
3032
3021
|
);
|
|
3033
3022
|
layout.columnHeaderCells.forEach(
|
|
@@ -3062,7 +3051,8 @@ function renderPivotTable(params) {
|
|
|
3062
3051
|
activeNodePathKey
|
|
3063
3052
|
)
|
|
3064
3053
|
},
|
|
3065
|
-
visibleIndicatorMaxima.get(cell.indicatorKey) ?? 0
|
|
3054
|
+
visibleIndicatorMaxima.get(cell.indicatorKey) ?? 0,
|
|
3055
|
+
params.selection?.textSelection
|
|
3066
3056
|
)
|
|
3067
3057
|
);
|
|
3068
3058
|
layout.bodyCells.forEach(
|
|
@@ -3203,7 +3193,7 @@ function drawReportFilterCell(context, cell, theme) {
|
|
|
3203
3193
|
skipRight: true
|
|
3204
3194
|
});
|
|
3205
3195
|
}
|
|
3206
|
-
function drawCorner(context, layout, theme) {
|
|
3196
|
+
function drawCorner(context, layout, theme, textSelection) {
|
|
3207
3197
|
layout.rowFieldHeaderCells.forEach(
|
|
3208
3198
|
(cell) => drawHeaderCell(
|
|
3209
3199
|
context,
|
|
@@ -3211,7 +3201,8 @@ function drawCorner(context, layout, theme) {
|
|
|
3211
3201
|
theme,
|
|
3212
3202
|
false,
|
|
3213
3203
|
cell.rect,
|
|
3214
|
-
theme.cornerBackgroundColor
|
|
3204
|
+
theme.cornerBackgroundColor,
|
|
3205
|
+
textSelection
|
|
3215
3206
|
)
|
|
3216
3207
|
);
|
|
3217
3208
|
if (layout.rowFieldHeaderCells.length === 0) {
|
|
@@ -3228,7 +3219,7 @@ function drawCorner(context, layout, theme) {
|
|
|
3228
3219
|
})
|
|
3229
3220
|
);
|
|
3230
3221
|
}
|
|
3231
|
-
function drawHeaderCell(context, cell, theme, selected, contentRect = cell.rect, backgroundColor) {
|
|
3222
|
+
function drawHeaderCell(context, cell, theme, selected, contentRect = cell.rect, backgroundColor, textSelection) {
|
|
3232
3223
|
const background = backgroundColor ?? (selected ? theme.selectionBackgroundColor : cell.kind === "grandTotal" ? theme.grandTotalBackgroundColor : cell.kind === "subtotal" ? theme.subtotalBackgroundColor : cell.axis === "row" ? theme.rowHeaderBackgroundColor : theme.columnHeaderBackgroundColor);
|
|
3233
3224
|
fillRect(context, cell.rect, background);
|
|
3234
3225
|
const iconPadding = cell.expandable ? 18 : 0;
|
|
@@ -3250,6 +3241,13 @@ function drawHeaderCell(context, cell, theme, selected, contentRect = cell.rect,
|
|
|
3250
3241
|
cell.filterable ? 32 : 10
|
|
3251
3242
|
);
|
|
3252
3243
|
}
|
|
3244
|
+
drawHeaderTextSelectionBackground(
|
|
3245
|
+
context,
|
|
3246
|
+
cell,
|
|
3247
|
+
contentRect,
|
|
3248
|
+
textSelection,
|
|
3249
|
+
theme.fontSize
|
|
3250
|
+
);
|
|
3253
3251
|
drawText(
|
|
3254
3252
|
context,
|
|
3255
3253
|
cell.label,
|
|
@@ -3262,13 +3260,46 @@ function drawHeaderCell(context, cell, theme, selected, contentRect = cell.rect,
|
|
|
3262
3260
|
cell.align ?? "left"
|
|
3263
3261
|
);
|
|
3264
3262
|
}
|
|
3263
|
+
function drawHeaderTextSelectionBackground(context, cell, contentRect, selection, fontSize) {
|
|
3264
|
+
if (!selection) return;
|
|
3265
|
+
const index = selection.fragments.findIndex(
|
|
3266
|
+
(fragment) => fragment.region === "header" && fragment.key === `header:${cell.axis}:${cell.nodeId ?? ""}:${cell.label}`
|
|
3267
|
+
);
|
|
3268
|
+
if (index < 0) return;
|
|
3269
|
+
const range = resolveTextSelectionRange(selection, index, cell.label.length);
|
|
3270
|
+
if (!range) return;
|
|
3271
|
+
const width = context.measureText(cell.label).width;
|
|
3272
|
+
const align = cell.align ?? "left";
|
|
3273
|
+
const origin = align === "right" ? contentRect.x + contentRect.width - 10 - width : align === "center" ? contentRect.x + contentRect.width / 2 - width / 2 : contentRect.x + 10 + (cell.expandable ? 18 : 0);
|
|
3274
|
+
context.save();
|
|
3275
|
+
context.fillStyle = "#99c2ff";
|
|
3276
|
+
context.fillRect(
|
|
3277
|
+
origin + context.measureText(cell.label.slice(0, range.start)).width,
|
|
3278
|
+
contentRect.y + (contentRect.height - fontSize) / 2,
|
|
3279
|
+
context.measureText(cell.label.slice(range.start, range.end)).width,
|
|
3280
|
+
fontSize
|
|
3281
|
+
);
|
|
3282
|
+
context.restore();
|
|
3283
|
+
}
|
|
3284
|
+
function resolveTextSelectionRange(selection, index, length) {
|
|
3285
|
+
const forward = selection.anchorFragmentIndex <= selection.focusFragmentIndex;
|
|
3286
|
+
const startIndex = forward ? selection.anchorFragmentIndex : selection.focusFragmentIndex;
|
|
3287
|
+
const endIndex = forward ? selection.focusFragmentIndex : selection.anchorFragmentIndex;
|
|
3288
|
+
if (index < startIndex || index > endIndex) return null;
|
|
3289
|
+
const start = index === startIndex ? forward ? selection.anchorOffset : selection.focusOffset : 0;
|
|
3290
|
+
const end = index === endIndex ? forward ? selection.focusOffset : selection.anchorOffset : length;
|
|
3291
|
+
return {
|
|
3292
|
+
start: Math.max(0, Math.min(start, length)),
|
|
3293
|
+
end: Math.max(0, Math.min(end, length))
|
|
3294
|
+
};
|
|
3295
|
+
}
|
|
3265
3296
|
function isHeaderSelected(cell, selectedPathKey) {
|
|
3266
3297
|
if (!selectedPathKey || !cell.nodeId) {
|
|
3267
3298
|
return false;
|
|
3268
3299
|
}
|
|
3269
3300
|
return Boolean(isPathRelated2(selectedPathKey, cell.pathKey));
|
|
3270
3301
|
}
|
|
3271
|
-
function drawBodyCell(context, cell, options, theme, state, visibleMaximum) {
|
|
3302
|
+
function drawBodyCell(context, cell, options, theme, state, visibleMaximum, textSelection) {
|
|
3272
3303
|
const indicator = options.indicators.find(
|
|
3273
3304
|
(item) => item.key === cell.indicatorKey
|
|
3274
3305
|
);
|
|
@@ -3298,15 +3329,54 @@ function drawBodyCell(context, cell, options, theme, state, visibleMaximum) {
|
|
|
3298
3329
|
"#3370ff"
|
|
3299
3330
|
);
|
|
3300
3331
|
}
|
|
3332
|
+
const text = resolveBodyDisplayText(cell, indicator);
|
|
3333
|
+
const padding = 10;
|
|
3334
|
+
const align = indicator?.align ?? "right";
|
|
3335
|
+
drawTextSelectionBackground(
|
|
3336
|
+
context,
|
|
3337
|
+
cell,
|
|
3338
|
+
text,
|
|
3339
|
+
align,
|
|
3340
|
+
padding,
|
|
3341
|
+
textSelection,
|
|
3342
|
+
theme.fontSize
|
|
3343
|
+
);
|
|
3301
3344
|
drawText(
|
|
3302
3345
|
context,
|
|
3303
|
-
|
|
3346
|
+
text,
|
|
3304
3347
|
cell.rect,
|
|
3305
3348
|
theme.textColor,
|
|
3306
3349
|
10,
|
|
3307
3350
|
indicator?.align ?? "right"
|
|
3308
3351
|
);
|
|
3309
3352
|
}
|
|
3353
|
+
function drawTextSelectionBackground(context, cell, text, align, padding, selection, fontSize) {
|
|
3354
|
+
if (!selection) return;
|
|
3355
|
+
const index = selection.fragments.findIndex(
|
|
3356
|
+
(fragment) => fragment.rowIndex === cell.rowIndex && fragment.columnIndex === cell.columnIndex && fragment.text === text
|
|
3357
|
+
);
|
|
3358
|
+
if (index < 0) return;
|
|
3359
|
+
const forward = selection.anchorFragmentIndex < selection.focusFragmentIndex || selection.anchorFragmentIndex === selection.focusFragmentIndex && selection.anchorOffset <= selection.focusOffset;
|
|
3360
|
+
const startIndex = forward ? selection.anchorFragmentIndex : selection.focusFragmentIndex;
|
|
3361
|
+
const endIndex = forward ? selection.focusFragmentIndex : selection.anchorFragmentIndex;
|
|
3362
|
+
if (index < startIndex || index > endIndex) return;
|
|
3363
|
+
const start = index === startIndex ? forward ? selection.anchorOffset : selection.focusOffset : 0;
|
|
3364
|
+
const end = index === endIndex ? forward ? selection.focusOffset : selection.anchorOffset : text.length;
|
|
3365
|
+
if (start >= end) return;
|
|
3366
|
+
const availableWidth = Math.max(cell.rect.width - padding * 2, 0);
|
|
3367
|
+
const shown = truncateText(context, text, availableWidth);
|
|
3368
|
+
const textWidth = context.measureText(shown).width;
|
|
3369
|
+
const origin = align === "right" ? cell.rect.x + cell.rect.width - padding - textWidth : align === "center" ? cell.rect.x + cell.rect.width / 2 - textWidth / 2 : cell.rect.x + padding;
|
|
3370
|
+
context.save();
|
|
3371
|
+
context.fillStyle = "#99c2ff";
|
|
3372
|
+
context.fillRect(
|
|
3373
|
+
origin + context.measureText(shown.slice(0, start)).width,
|
|
3374
|
+
cell.rect.y + (cell.rect.height - fontSize) / 2,
|
|
3375
|
+
context.measureText(shown.slice(start, end)).width,
|
|
3376
|
+
fontSize
|
|
3377
|
+
);
|
|
3378
|
+
context.restore();
|
|
3379
|
+
}
|
|
3310
3380
|
function resolveSelectionPathKeys(layout, selection, activeNodePathKey) {
|
|
3311
3381
|
const activeNode = selection?.activeNode;
|
|
3312
3382
|
if (activeNode) {
|
|
@@ -3595,6 +3665,61 @@ function contains(rect, x, y) {
|
|
|
3595
3665
|
return x >= rect.x && x < rect.x + rect.width && y >= rect.y && y < rect.y + rect.height;
|
|
3596
3666
|
}
|
|
3597
3667
|
|
|
3668
|
+
// src/interaction/text-selection.ts
|
|
3669
|
+
function resolvePivotTextOffset(params) {
|
|
3670
|
+
const boundaries = [0];
|
|
3671
|
+
let offset = 0;
|
|
3672
|
+
for (const character of params.text) {
|
|
3673
|
+
offset += character.length;
|
|
3674
|
+
boundaries.push(offset);
|
|
3675
|
+
}
|
|
3676
|
+
const relativeX = params.pointerX - params.textOriginX;
|
|
3677
|
+
if (relativeX <= 0 || boundaries.length === 1) return 0;
|
|
3678
|
+
if (relativeX >= params.measureText(params.text)) return params.text.length;
|
|
3679
|
+
let low = 1;
|
|
3680
|
+
let high = boundaries.length - 1;
|
|
3681
|
+
while (low < high) {
|
|
3682
|
+
const middle = Math.floor((low + high) / 2);
|
|
3683
|
+
const boundary = boundaries[middle] ?? params.text.length;
|
|
3684
|
+
if (params.measureText(params.text.slice(0, boundary)) < relativeX) {
|
|
3685
|
+
low = middle + 1;
|
|
3686
|
+
} else {
|
|
3687
|
+
high = middle;
|
|
3688
|
+
}
|
|
3689
|
+
}
|
|
3690
|
+
const end = boundaries[low] ?? params.text.length;
|
|
3691
|
+
const start = boundaries[Math.max(low - 1, 0)] ?? 0;
|
|
3692
|
+
const startWidth = params.measureText(params.text.slice(0, start));
|
|
3693
|
+
const endWidth = params.measureText(params.text.slice(0, end));
|
|
3694
|
+
return relativeX - startWidth <= endWidth - relativeX ? start : end;
|
|
3695
|
+
}
|
|
3696
|
+
function getPivotSelectedText(selection) {
|
|
3697
|
+
const textSelection = selection.textSelection;
|
|
3698
|
+
if (!textSelection) return null;
|
|
3699
|
+
const forward = textSelection.anchorFragmentIndex < textSelection.focusFragmentIndex || textSelection.anchorFragmentIndex === textSelection.focusFragmentIndex && textSelection.anchorOffset <= textSelection.focusOffset;
|
|
3700
|
+
const startIndex = forward ? textSelection.anchorFragmentIndex : textSelection.focusFragmentIndex;
|
|
3701
|
+
const endIndex = forward ? textSelection.focusFragmentIndex : textSelection.anchorFragmentIndex;
|
|
3702
|
+
const startOffset = forward ? textSelection.anchorOffset : textSelection.focusOffset;
|
|
3703
|
+
const endOffset = forward ? textSelection.focusOffset : textSelection.anchorOffset;
|
|
3704
|
+
const selected = textSelection.fragments.slice(startIndex, endIndex + 1).map((fragment, index) => {
|
|
3705
|
+
const absoluteIndex = startIndex + index;
|
|
3706
|
+
const start = absoluteIndex === startIndex ? startOffset : 0;
|
|
3707
|
+
const end = absoluteIndex === endIndex ? endOffset : fragment.text.length;
|
|
3708
|
+
return {
|
|
3709
|
+
fragment,
|
|
3710
|
+
text: fragment.text.slice(
|
|
3711
|
+
Math.max(0, Math.min(start, fragment.text.length)),
|
|
3712
|
+
Math.max(0, Math.min(end, fragment.text.length))
|
|
3713
|
+
)
|
|
3714
|
+
};
|
|
3715
|
+
});
|
|
3716
|
+
return selected.reduce((result, item, index) => {
|
|
3717
|
+
const previous = selected[index - 1]?.fragment;
|
|
3718
|
+
const separator = previous ? previous.rowIndex === item.fragment.rowIndex ? " " : "\n" : "";
|
|
3719
|
+
return `${result}${separator}${item.text}`;
|
|
3720
|
+
}, "") || null;
|
|
3721
|
+
}
|
|
3722
|
+
|
|
3598
3723
|
// src/features/clipboard/pivot-selection-copy.ts
|
|
3599
3724
|
function createPivotSelectionText(params) {
|
|
3600
3725
|
const activeNode = params.selection.activeNode;
|
|
@@ -3898,6 +4023,8 @@ var PivotTable = class {
|
|
|
3898
4023
|
__publicField(this, "suppressNextClick", false);
|
|
3899
4024
|
__publicField(this, "scrollbarDrag", null);
|
|
3900
4025
|
__publicField(this, "cellSelectionDrag", null);
|
|
4026
|
+
__publicField(this, "textSelectionDrag", null);
|
|
4027
|
+
__publicField(this, "headerTextSelectionDrag", null);
|
|
3901
4028
|
__publicField(this, "rowHeaderSelectionDrag", null);
|
|
3902
4029
|
__publicField(this, "handlePointerMove", (event) => {
|
|
3903
4030
|
if (this.scrollbarDrag) {
|
|
@@ -3918,10 +4045,22 @@ var PivotTable = class {
|
|
|
3918
4045
|
return;
|
|
3919
4046
|
}
|
|
3920
4047
|
if (this.cellSelectionDrag?.pointerId === event.pointerId) {
|
|
4048
|
+
if (this.textSelectionDrag?.pointerId === event.pointerId) {
|
|
4049
|
+
this.updateTextSelectionDrag(event);
|
|
4050
|
+
if (this.textSelectionDrag.dragged) return;
|
|
4051
|
+
}
|
|
3921
4052
|
this.updateCellSelectionDrag(event);
|
|
3922
4053
|
return;
|
|
3923
4054
|
}
|
|
4055
|
+
if (this.headerTextSelectionDrag?.pointerId === event.pointerId) {
|
|
4056
|
+
this.updateHeaderTextSelectionDrag(event);
|
|
4057
|
+
return;
|
|
4058
|
+
}
|
|
3924
4059
|
if (this.rowHeaderSelectionDrag?.pointerId === event.pointerId) {
|
|
4060
|
+
if (this.headerTextSelectionDrag?.pointerId === event.pointerId) {
|
|
4061
|
+
this.updateHeaderTextSelectionDrag(event);
|
|
4062
|
+
if (this.headerTextSelectionDrag.dragged) return;
|
|
4063
|
+
}
|
|
3925
4064
|
this.updateRowHeaderSelectionDrag(event);
|
|
3926
4065
|
return;
|
|
3927
4066
|
}
|
|
@@ -3968,6 +4107,7 @@ var PivotTable = class {
|
|
|
3968
4107
|
this.openReportFilterPanel(hit.reportFilterCell);
|
|
3969
4108
|
return;
|
|
3970
4109
|
}
|
|
4110
|
+
if (hit.reportFilterCell) return;
|
|
3971
4111
|
if (hit.bodyCell) {
|
|
3972
4112
|
this.selectCell(toCellAddress(hit.bodyCell), event.shiftKey);
|
|
3973
4113
|
return;
|
|
@@ -4009,52 +4149,20 @@ var PivotTable = class {
|
|
|
4009
4149
|
this.scheduleRender();
|
|
4010
4150
|
return;
|
|
4011
4151
|
});
|
|
4012
|
-
__publicField(this, "
|
|
4013
|
-
const root = this.host?.root;
|
|
4014
|
-
const activeElement = this.container.ownerDocument.activeElement;
|
|
4015
|
-
const belongsToTable = Boolean(
|
|
4016
|
-
root && (activeElement === root || activeElement instanceof Element && root.contains(activeElement))
|
|
4017
|
-
);
|
|
4152
|
+
__publicField(this, "handleKeyDown", (event) => {
|
|
4018
4153
|
const isCopyShortcut = (event.ctrlKey || event.metaKey) && !event.altKey && (event.key.toLocaleLowerCase() === "c" || event.code === "KeyC");
|
|
4019
4154
|
if (isCopyShortcut) {
|
|
4020
|
-
|
|
4021
|
-
|
|
4022
|
-
|
|
4023
|
-
|
|
4024
|
-
ctrlKey: event.ctrlKey,
|
|
4025
|
-
metaKey: event.metaKey,
|
|
4026
|
-
defaultPrevented: event.defaultPrevented,
|
|
4027
|
-
activeElement: describeElement(activeElement),
|
|
4028
|
-
eventTarget: describeElement(event.target),
|
|
4029
|
-
protocol: this.container.ownerDocument.location?.protocol ?? "unknown",
|
|
4030
|
-
isSecureContext: this.container.ownerDocument.defaultView?.isSecureContext ?? false
|
|
4031
|
-
});
|
|
4032
|
-
}
|
|
4033
|
-
if (!belongsToTable) return;
|
|
4034
|
-
if (isCopyShortcut) {
|
|
4035
|
-
const text = this.createSelectionTsv();
|
|
4036
|
-
const selection = this.core?.getSelection() ?? null;
|
|
4037
|
-
logPivotClipboard("selection-resolved", {
|
|
4038
|
-
hasLayout: Boolean(this.layout),
|
|
4039
|
-
selection,
|
|
4040
|
-
textLength: text.length,
|
|
4041
|
-
textPreview: text.slice(0, 120)
|
|
4042
|
-
});
|
|
4155
|
+
const text = getPivotSelectedText(
|
|
4156
|
+
this.core?.getSelection() ?? {
|
|
4157
|
+
}
|
|
4158
|
+
) ?? this.createSelectionTsv();
|
|
4043
4159
|
if (!text) {
|
|
4044
|
-
logPivotClipboard("copy-aborted", { reason: "empty-selection-text" });
|
|
4045
4160
|
return;
|
|
4046
4161
|
}
|
|
4047
4162
|
event.preventDefault();
|
|
4048
|
-
const
|
|
4049
|
-
|
|
4050
|
-
|
|
4051
|
-
);
|
|
4052
|
-
if (!copied) {
|
|
4053
|
-
void writePivotClipboardText(text, this.container.ownerDocument).then(
|
|
4054
|
-
(fallbackCopied) => logPivotClipboard("fallback-complete", {
|
|
4055
|
-
copied: fallbackCopied
|
|
4056
|
-
})
|
|
4057
|
-
);
|
|
4163
|
+
const ownerDocument = this.host?.canvas.ownerDocument ?? globalThis.document;
|
|
4164
|
+
if (!utils.copyTextSync(text, ownerDocument)) {
|
|
4165
|
+
void utils.copyText(text, ownerDocument);
|
|
4058
4166
|
}
|
|
4059
4167
|
return;
|
|
4060
4168
|
}
|
|
@@ -4067,16 +4175,6 @@ var PivotTable = class {
|
|
|
4067
4175
|
event.preventDefault();
|
|
4068
4176
|
this.selectCell(next, event.shiftKey);
|
|
4069
4177
|
});
|
|
4070
|
-
__publicField(this, "handleCopy", (event) => {
|
|
4071
|
-
const text = this.createSelectionTsv();
|
|
4072
|
-
logPivotClipboard("copy-event", {
|
|
4073
|
-
textLength: text.length,
|
|
4074
|
-
hasClipboardData: Boolean(event.clipboardData)
|
|
4075
|
-
});
|
|
4076
|
-
if (!text) return;
|
|
4077
|
-
event.preventDefault();
|
|
4078
|
-
event.clipboardData?.setData("text/plain", text);
|
|
4079
|
-
});
|
|
4080
4178
|
__publicField(this, "handleWheel", (event) => {
|
|
4081
4179
|
if (!this.layout) return;
|
|
4082
4180
|
const delta = resolvePivotWheelScrollDelta({
|
|
@@ -4148,13 +4246,13 @@ var PivotTable = class {
|
|
|
4148
4246
|
endColumnIndex: hit.bodyCell.columnIndex
|
|
4149
4247
|
}) : "";
|
|
4150
4248
|
if (text) {
|
|
4151
|
-
|
|
4249
|
+
this.copyPivotText(text);
|
|
4152
4250
|
}
|
|
4153
4251
|
},
|
|
4154
4252
|
onCopySelection: selectionRange ? () => {
|
|
4155
4253
|
const text = this.createSelectionTsv();
|
|
4156
4254
|
if (text) {
|
|
4157
|
-
|
|
4255
|
+
this.copyPivotText(text);
|
|
4158
4256
|
}
|
|
4159
4257
|
} : void 0,
|
|
4160
4258
|
customSelection: {
|
|
@@ -4177,7 +4275,7 @@ var PivotTable = class {
|
|
|
4177
4275
|
endColumnIndex: range.endColumn - 1
|
|
4178
4276
|
});
|
|
4179
4277
|
if (text) {
|
|
4180
|
-
|
|
4278
|
+
this.copyPivotText(text);
|
|
4181
4279
|
}
|
|
4182
4280
|
},
|
|
4183
4281
|
onExportCsv: () => downloadBlob(
|
|
@@ -4209,6 +4307,24 @@ var PivotTable = class {
|
|
|
4209
4307
|
const address = toCellAddress(cellHit.bodyCell);
|
|
4210
4308
|
this.host.root.focus({ preventScroll: true });
|
|
4211
4309
|
const selection = this.selectCell(address, event.shiftKey);
|
|
4310
|
+
const text = this.resolveBodyText(cellHit.bodyCell);
|
|
4311
|
+
const textOriginX = this.resolveBodyTextOriginX(cellHit.bodyCell, text);
|
|
4312
|
+
const anchorOffset = text ? resolvePivotTextOffset({
|
|
4313
|
+
text,
|
|
4314
|
+
pointerX: event.clientX - this.host.root.getBoundingClientRect().left,
|
|
4315
|
+
textOriginX,
|
|
4316
|
+
measureText: (value) => this.measurePivotText(value)
|
|
4317
|
+
}) : 0;
|
|
4318
|
+
const anchorFragmentIndex = this.resolveTextFragmentIndex(
|
|
4319
|
+
`body:${cellHit.bodyCell.rowIndex}:${cellHit.bodyCell.columnIndex}`,
|
|
4320
|
+
this.createTextSelectionFragments("column")
|
|
4321
|
+
);
|
|
4322
|
+
this.textSelectionDrag = text ? {
|
|
4323
|
+
pointerId: event.pointerId,
|
|
4324
|
+
anchorFragmentIndex,
|
|
4325
|
+
anchorOffset,
|
|
4326
|
+
dragged: false
|
|
4327
|
+
} : null;
|
|
4212
4328
|
this.cellSelectionDrag = {
|
|
4213
4329
|
pointerId: event.pointerId,
|
|
4214
4330
|
anchor: selection?.anchorCell ?? address,
|
|
@@ -4220,6 +4336,37 @@ var PivotTable = class {
|
|
|
4220
4336
|
this.suppressNextClick = true;
|
|
4221
4337
|
return;
|
|
4222
4338
|
}
|
|
4339
|
+
if (cellHit?.headerCell && !cellHit.onExpandIcon && !cellHit.onFilterIcon && !cellHit.onSortIcon) {
|
|
4340
|
+
const header = cellHit.headerCell;
|
|
4341
|
+
const text = header.label;
|
|
4342
|
+
const rootRect2 = this.host.root.getBoundingClientRect();
|
|
4343
|
+
const originX = header.rect.x + 10 + (header.expandable ? 18 : 0);
|
|
4344
|
+
const fragments = this.createTextSelectionFragments(header.axis);
|
|
4345
|
+
const key = `header:${header.axis}:${header.nodeId ?? ""}:${header.label}`;
|
|
4346
|
+
const anchorFragmentIndex = fragments.findIndex(
|
|
4347
|
+
(fragment) => fragment.key === key
|
|
4348
|
+
);
|
|
4349
|
+
this.host.root.focus({ preventScroll: true });
|
|
4350
|
+
if (header.nodeId) {
|
|
4351
|
+
this.selectNode(header.axis, header.nodeId);
|
|
4352
|
+
}
|
|
4353
|
+
this.textSelectionDrag = null;
|
|
4354
|
+
this.headerTextSelectionDrag = {
|
|
4355
|
+
pointerId: event.pointerId,
|
|
4356
|
+
fragments,
|
|
4357
|
+
anchorFragmentIndex: Math.max(anchorFragmentIndex, 0),
|
|
4358
|
+
anchorOffset: resolvePivotTextOffset({
|
|
4359
|
+
text,
|
|
4360
|
+
pointerX: event.clientX - rootRect2.left,
|
|
4361
|
+
textOriginX: originX,
|
|
4362
|
+
measureText: (value) => this.measurePivotText(value)
|
|
4363
|
+
}),
|
|
4364
|
+
dragged: false
|
|
4365
|
+
};
|
|
4366
|
+
event.preventDefault();
|
|
4367
|
+
this.host.root.setPointerCapture(event.pointerId);
|
|
4368
|
+
return;
|
|
4369
|
+
}
|
|
4223
4370
|
if (cellHit?.region === "rowHeader" && cellHit.headerCell && !cellHit.onExpandIcon && !cellHit.onFilterIcon && !cellHit.onSortIcon) {
|
|
4224
4371
|
const rootRect2 = this.host.root.getBoundingClientRect();
|
|
4225
4372
|
const rowIndex = resolvePivotRowIndexAtY(
|
|
@@ -4269,6 +4416,14 @@ var PivotTable = class {
|
|
|
4269
4416
|
if (!this.host) return;
|
|
4270
4417
|
const endedCellSelection = this.cellSelectionDrag?.pointerId === event.pointerId;
|
|
4271
4418
|
if (endedCellSelection) this.cellSelectionDrag = null;
|
|
4419
|
+
if (this.textSelectionDrag?.pointerId === event.pointerId) {
|
|
4420
|
+
if (this.textSelectionDrag.dragged) this.suppressNextClick = true;
|
|
4421
|
+
this.textSelectionDrag = null;
|
|
4422
|
+
}
|
|
4423
|
+
if (this.headerTextSelectionDrag?.pointerId === event.pointerId) {
|
|
4424
|
+
if (this.headerTextSelectionDrag.dragged) this.suppressNextClick = true;
|
|
4425
|
+
this.headerTextSelectionDrag = null;
|
|
4426
|
+
}
|
|
4272
4427
|
const endedRowHeaderSelection = this.rowHeaderSelectionDrag?.pointerId === event.pointerId;
|
|
4273
4428
|
if (endedRowHeaderSelection) {
|
|
4274
4429
|
if (this.rowHeaderSelectionDrag?.dragged) this.suppressNextClick = true;
|
|
@@ -4300,12 +4455,7 @@ var PivotTable = class {
|
|
|
4300
4455
|
this.host.root.addEventListener("pointermove", this.handlePointerMove);
|
|
4301
4456
|
this.host.root.addEventListener("pointerleave", this.handlePointerLeave);
|
|
4302
4457
|
this.host.root.addEventListener("click", this.handleClick);
|
|
4303
|
-
this.
|
|
4304
|
-
"keydown",
|
|
4305
|
-
this.handleDocumentKeyDown,
|
|
4306
|
-
true
|
|
4307
|
-
);
|
|
4308
|
-
this.host.root.addEventListener("copy", this.handleCopy);
|
|
4458
|
+
this.host.root.addEventListener("keydown", this.handleKeyDown);
|
|
4309
4459
|
this.host.root.addEventListener("wheel", this.handleWheel, {
|
|
4310
4460
|
passive: false
|
|
4311
4461
|
});
|
|
@@ -4531,12 +4681,7 @@ var PivotTable = class {
|
|
|
4531
4681
|
this.handlePointerLeave
|
|
4532
4682
|
);
|
|
4533
4683
|
this.host?.root.removeEventListener("click", this.handleClick);
|
|
4534
|
-
this.
|
|
4535
|
-
"keydown",
|
|
4536
|
-
this.handleDocumentKeyDown,
|
|
4537
|
-
true
|
|
4538
|
-
);
|
|
4539
|
-
this.host?.root.removeEventListener("copy", this.handleCopy);
|
|
4684
|
+
this.host?.root.removeEventListener("keydown", this.handleKeyDown);
|
|
4540
4685
|
this.host?.root.removeEventListener("wheel", this.handleWheel);
|
|
4541
4686
|
this.host?.root.removeEventListener("contextmenu", this.handleContextMenu);
|
|
4542
4687
|
this.host?.root.removeEventListener("pointerdown", this.handlePointerDown);
|
|
@@ -4648,6 +4793,194 @@ var PivotTable = class {
|
|
|
4648
4793
|
activeNode: null
|
|
4649
4794
|
});
|
|
4650
4795
|
}
|
|
4796
|
+
updateTextSelectionDrag(event) {
|
|
4797
|
+
const drag = this.textSelectionDrag;
|
|
4798
|
+
if (!drag || !this.layout || !this.host) return;
|
|
4799
|
+
const rootRect = this.host.root.getBoundingClientRect();
|
|
4800
|
+
const x = event.clientX - rootRect.left;
|
|
4801
|
+
const y = event.clientY - rootRect.top;
|
|
4802
|
+
const hitResult = hitTestPivotLayout(this.layout, x, y);
|
|
4803
|
+
const hit = hitResult?.bodyCell;
|
|
4804
|
+
const header = hitResult?.headerCell;
|
|
4805
|
+
if (!hit && !header) return;
|
|
4806
|
+
const fragments = this.createTextSelectionFragments(
|
|
4807
|
+
header?.axis ?? "column"
|
|
4808
|
+
);
|
|
4809
|
+
const focusKey = header ? `header:${header.axis}:${header.nodeId ?? ""}:${header.label}` : `body:${hit?.rowIndex}:${hit?.columnIndex}`;
|
|
4810
|
+
const focusIndex = this.resolveTextFragmentIndex(focusKey, fragments);
|
|
4811
|
+
const text = header ? header.label : this.resolveBodyText(hit);
|
|
4812
|
+
if (!text || focusIndex < 0) return;
|
|
4813
|
+
const textOriginX = header ? header.rect.x + 10 + (header.expandable ? 18 : 0) : this.resolveBodyTextOriginX(hit, text);
|
|
4814
|
+
const focusOffset = resolvePivotTextOffset({
|
|
4815
|
+
text,
|
|
4816
|
+
pointerX: x,
|
|
4817
|
+
textOriginX,
|
|
4818
|
+
measureText: (value) => this.measurePivotText(value)
|
|
4819
|
+
});
|
|
4820
|
+
if (focusIndex === drag.anchorFragmentIndex && focusOffset === drag.anchorOffset)
|
|
4821
|
+
return;
|
|
4822
|
+
drag.dragged = true;
|
|
4823
|
+
this.setSelection({
|
|
4824
|
+
activeCell: hit ? toCellAddress(hit) : this.core?.getSelection().activeCell ?? null,
|
|
4825
|
+
anchorCell: this.core?.getSelection().anchorCell ?? (hit ? toCellAddress(hit) : null),
|
|
4826
|
+
ranges: this.core?.getSelection().ranges ?? [],
|
|
4827
|
+
activeNode: null,
|
|
4828
|
+
textSelection: {
|
|
4829
|
+
fragments: fragments.map((fragment) => ({
|
|
4830
|
+
rowIndex: fragment.rowIndex ?? -1,
|
|
4831
|
+
columnIndex: fragment.columnIndex ?? -1,
|
|
4832
|
+
text: fragment.text,
|
|
4833
|
+
region: fragment.key.startsWith("header:") ? "header" : "body",
|
|
4834
|
+
key: fragment.key
|
|
4835
|
+
})),
|
|
4836
|
+
anchorFragmentIndex: drag.anchorFragmentIndex,
|
|
4837
|
+
focusFragmentIndex: focusIndex,
|
|
4838
|
+
anchorOffset: drag.anchorOffset,
|
|
4839
|
+
focusOffset
|
|
4840
|
+
}
|
|
4841
|
+
});
|
|
4842
|
+
}
|
|
4843
|
+
updateHeaderTextSelectionDrag(event) {
|
|
4844
|
+
const drag = this.headerTextSelectionDrag;
|
|
4845
|
+
if (!drag || !this.host || !this.layout) return;
|
|
4846
|
+
const rootRect = this.host.root.getBoundingClientRect();
|
|
4847
|
+
const localX = event.clientX - rootRect.left;
|
|
4848
|
+
const localY = event.clientY - rootRect.top;
|
|
4849
|
+
const hit = hitTestPivotLayout(this.layout, localX, localY);
|
|
4850
|
+
const header = hit?.headerCell;
|
|
4851
|
+
const body = hit?.bodyCell;
|
|
4852
|
+
if (!header && !body) return;
|
|
4853
|
+
const key = header ? `header:${header.axis}:${header.nodeId ?? ""}:${header.label}` : `body:${body?.rowIndex}:${body?.columnIndex}`;
|
|
4854
|
+
const focusFragmentIndex = drag.fragments.findIndex(
|
|
4855
|
+
(fragment) => fragment.key === key
|
|
4856
|
+
);
|
|
4857
|
+
if (focusFragmentIndex < 0) return;
|
|
4858
|
+
const focusFragment = drag.fragments[focusFragmentIndex];
|
|
4859
|
+
const focusOffset = resolvePivotTextOffset({
|
|
4860
|
+
text: focusFragment.text,
|
|
4861
|
+
pointerX: localX,
|
|
4862
|
+
textOriginX: focusFragment.originX,
|
|
4863
|
+
measureText: (value) => this.measurePivotText(value)
|
|
4864
|
+
});
|
|
4865
|
+
if (focusFragmentIndex === drag.anchorFragmentIndex && focusOffset === drag.anchorOffset)
|
|
4866
|
+
return;
|
|
4867
|
+
drag.dragged = true;
|
|
4868
|
+
const selection = this.core?.getSelection();
|
|
4869
|
+
if (!selection) return;
|
|
4870
|
+
this.setSelection({
|
|
4871
|
+
...selection,
|
|
4872
|
+
textSelection: {
|
|
4873
|
+
fragments: drag.fragments.map((fragment) => ({
|
|
4874
|
+
rowIndex: fragment.rowIndex ?? -1,
|
|
4875
|
+
columnIndex: fragment.columnIndex ?? -1,
|
|
4876
|
+
text: fragment.text,
|
|
4877
|
+
region: "header",
|
|
4878
|
+
key: fragment.key
|
|
4879
|
+
})),
|
|
4880
|
+
anchorFragmentIndex: drag.anchorFragmentIndex,
|
|
4881
|
+
focusFragmentIndex,
|
|
4882
|
+
anchorOffset: drag.anchorOffset,
|
|
4883
|
+
focusOffset
|
|
4884
|
+
}
|
|
4885
|
+
});
|
|
4886
|
+
}
|
|
4887
|
+
createTextSelectionFragments(axis) {
|
|
4888
|
+
const bodyFragments = (this.layout?.bodyCells ?? []).map((cell) => ({
|
|
4889
|
+
key: `body:${cell.rowIndex}:${cell.columnIndex}`,
|
|
4890
|
+
text: this.resolveBodyText(cell),
|
|
4891
|
+
originX: this.resolveBodyTextOriginX(cell, this.resolveBodyText(cell)),
|
|
4892
|
+
rowIndex: cell.rowIndex,
|
|
4893
|
+
columnIndex: cell.columnIndex
|
|
4894
|
+
})).filter((fragment) => fragment.text.length > 0);
|
|
4895
|
+
if (this.layout) {
|
|
4896
|
+
const columnHeaders = this.createHeaderTextFragments("column");
|
|
4897
|
+
const rowHeaders = this.layout.rowHeaderCells.filter((cell) => cell.label).map((cell) => ({
|
|
4898
|
+
key: `header:${cell.axis}:${cell.nodeId ?? ""}:${cell.label}`,
|
|
4899
|
+
text: cell.label,
|
|
4900
|
+
rowIndex: void 0,
|
|
4901
|
+
columnIndex: void 0,
|
|
4902
|
+
originX: cell.rect.x + 10
|
|
4903
|
+
}));
|
|
4904
|
+
const rows = Array.from(
|
|
4905
|
+
{ length: this.layout.rowSlots.length },
|
|
4906
|
+
(_, rowIndex) => [
|
|
4907
|
+
rowHeaders[rowIndex],
|
|
4908
|
+
...bodyFragments.filter((fragment) => fragment.rowIndex === rowIndex)
|
|
4909
|
+
]
|
|
4910
|
+
).flat();
|
|
4911
|
+
if (axis === "column") {
|
|
4912
|
+
return [...columnHeaders, ...rows].filter(
|
|
4913
|
+
(fragment) => Boolean(fragment)
|
|
4914
|
+
);
|
|
4915
|
+
}
|
|
4916
|
+
const rowHeadersTitle = this.layout.rowFieldHeaderCells.filter((cell) => cell.label).map((cell) => ({
|
|
4917
|
+
key: `header:${cell.axis}:${cell.nodeId ?? ""}:${cell.label}`,
|
|
4918
|
+
text: cell.label,
|
|
4919
|
+
rowIndex: void 0,
|
|
4920
|
+
columnIndex: void 0,
|
|
4921
|
+
originX: cell.rect.x + 10 + (cell.expandable ? 18 : 0)
|
|
4922
|
+
}));
|
|
4923
|
+
return [...rowHeadersTitle, ...columnHeaders, ...rows].filter(
|
|
4924
|
+
(fragment) => Boolean(fragment)
|
|
4925
|
+
);
|
|
4926
|
+
}
|
|
4927
|
+
return [...this.createHeaderTextFragments(axis), ...bodyFragments];
|
|
4928
|
+
}
|
|
4929
|
+
createHeaderTextFragments(axis) {
|
|
4930
|
+
if (!this.layout) return [];
|
|
4931
|
+
return (axis === "column" ? this.layout.columnHeaderCells : [...this.layout.rowHeaderCells, ...this.layout.rowFieldHeaderCells]).filter((cell) => cell.label).map((cell) => ({
|
|
4932
|
+
key: `header:${cell.axis}:${cell.nodeId ?? ""}:${cell.label}`,
|
|
4933
|
+
text: cell.label,
|
|
4934
|
+
rowIndex: void 0,
|
|
4935
|
+
columnIndex: void 0,
|
|
4936
|
+
originX: cell.rect.x + 10 + (cell.expandable ? 18 : 0)
|
|
4937
|
+
}));
|
|
4938
|
+
}
|
|
4939
|
+
resolveBodyText(cell) {
|
|
4940
|
+
const indicator = this.options.indicators.find(
|
|
4941
|
+
(item) => item.key === cell.indicatorKey
|
|
4942
|
+
);
|
|
4943
|
+
if (!indicator || indicator.formatter || typeof cell.value !== "number")
|
|
4944
|
+
return cell.formattedValue;
|
|
4945
|
+
if (indicator.cellType === "percent")
|
|
4946
|
+
return `${(cell.value * 100).toFixed(indicator.precision ?? 2)}%`;
|
|
4947
|
+
if (indicator.cellType === "money")
|
|
4948
|
+
return cell.value.toLocaleString("zh-CN", {
|
|
4949
|
+
style: "currency",
|
|
4950
|
+
currency: "CNY",
|
|
4951
|
+
minimumFractionDigits: indicator.precision ?? 2,
|
|
4952
|
+
maximumFractionDigits: indicator.precision ?? 2
|
|
4953
|
+
});
|
|
4954
|
+
return cell.formattedValue;
|
|
4955
|
+
}
|
|
4956
|
+
createBodyTextFragments() {
|
|
4957
|
+
return (this.layout?.bodyCells ?? []).map((cell) => ({
|
|
4958
|
+
rowIndex: cell.rowIndex,
|
|
4959
|
+
columnIndex: cell.columnIndex,
|
|
4960
|
+
text: this.resolveBodyText(cell)
|
|
4961
|
+
})).filter((fragment) => fragment.text.length > 0);
|
|
4962
|
+
}
|
|
4963
|
+
resolveTextFragmentIndex(key, fragments) {
|
|
4964
|
+
return fragments.findIndex((fragment) => fragment.key === key);
|
|
4965
|
+
}
|
|
4966
|
+
resolveBodyFragmentIndex(cell) {
|
|
4967
|
+
if (!this.layout) return -1;
|
|
4968
|
+
return this.layout.bodyCells.slice(0, this.layout.bodyCells.indexOf(cell) + 1).filter((item) => this.resolveBodyText(item).length > 0).length - 1;
|
|
4969
|
+
}
|
|
4970
|
+
resolveBodyTextOriginX(cell, text) {
|
|
4971
|
+
const indicator = this.options.indicators.find(
|
|
4972
|
+
(item) => item.key === cell.indicatorKey
|
|
4973
|
+
);
|
|
4974
|
+
const width = this.measurePivotText(text);
|
|
4975
|
+
const align = indicator?.align ?? "right";
|
|
4976
|
+
return align === "right" ? cell.rect.x + cell.rect.width - 10 - width : align === "center" ? cell.rect.x + cell.rect.width / 2 - width / 2 : cell.rect.x + 10;
|
|
4977
|
+
}
|
|
4978
|
+
measurePivotText(text) {
|
|
4979
|
+
const context = this.host?.canvas.getContext("2d");
|
|
4980
|
+
if (!context) return text.length * 8;
|
|
4981
|
+
context.font = `${this.options.theme?.fontSize ?? 13}px ${this.options.theme?.fontFamily ?? "sans-serif"}`;
|
|
4982
|
+
return context.measureText(text).width;
|
|
4983
|
+
}
|
|
4651
4984
|
updateRowHeaderSelectionDrag(event) {
|
|
4652
4985
|
const drag = this.rowHeaderSelectionDrag;
|
|
4653
4986
|
if (!drag || !this.layout || !this.host) return;
|
|
@@ -4886,6 +5219,14 @@ var PivotTable = class {
|
|
|
4886
5219
|
selection: this.core.getSelection()
|
|
4887
5220
|
});
|
|
4888
5221
|
}
|
|
5222
|
+
/** 复制表格选区文本;同步复制失败时回退到异步实现。 */
|
|
5223
|
+
copyPivotText(text) {
|
|
5224
|
+
if (!text) return;
|
|
5225
|
+
const ownerDocument = this.host?.canvas.ownerDocument ?? globalThis.document;
|
|
5226
|
+
if (!utils.copyTextSync(text, ownerDocument)) {
|
|
5227
|
+
void utils.copyText(text, ownerDocument);
|
|
5228
|
+
}
|
|
5229
|
+
}
|
|
4889
5230
|
scheduleRender() {
|
|
4890
5231
|
if (this.frameId !== null) return;
|
|
4891
5232
|
this.frameId = requestAnimationFrame(() => {
|
|
@@ -4929,12 +5270,6 @@ var PivotTable = class {
|
|
|
4929
5270
|
});
|
|
4930
5271
|
}
|
|
4931
5272
|
};
|
|
4932
|
-
function describeElement(value) {
|
|
4933
|
-
if (!(value instanceof Element)) return String(value);
|
|
4934
|
-
const id = value.id ? `#${value.id}` : "";
|
|
4935
|
-
const className = typeof value.className === "string" && value.className ? `.${value.className.trim().replace(/\s+/g, ".")}` : "";
|
|
4936
|
-
return `${value.tagName.toLocaleLowerCase()}${id}${className}`;
|
|
4937
|
-
}
|
|
4938
5273
|
function toCellAddress(cell) {
|
|
4939
5274
|
return {
|
|
4940
5275
|
rowNodeId: cell.rowNodeId ?? "",
|