@odoo/o-spreadsheet 18.1.19 → 18.1.20
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/o-spreadsheet.cjs.js +458 -41
- package/dist/o-spreadsheet.d.ts +277 -152
- package/dist/o-spreadsheet.esm.js +458 -41
- package/dist/o-spreadsheet.iife.js +458 -41
- package/dist/o-spreadsheet.iife.min.js +420 -384
- package/dist/o_spreadsheet.xml +60 -13
- package/package.json +1 -1
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* This file is generated by o-spreadsheet build tools. Do not edit it.
|
|
4
4
|
* @see https://github.com/odoo/o-spreadsheet
|
|
5
|
-
* @version 18.1.
|
|
6
|
-
* @date 2025-05-
|
|
7
|
-
* @hash
|
|
5
|
+
* @version 18.1.20
|
|
6
|
+
* @date 2025-05-13T17:52:28.174Z
|
|
7
|
+
* @hash 3e43a46
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
import { useEnv, useSubEnv, onWillUnmount, useComponent, status, Component, useRef, onMounted, useEffect, useState, onPatched, onWillPatch, onWillUpdateProps, useExternalListener, onWillStart, xml, useChildSubEnv, markRaw, toRaw } from '@odoo/owl';
|
|
@@ -3346,7 +3346,7 @@ function isDateAfter(date, dateAfter) {
|
|
|
3346
3346
|
*/
|
|
3347
3347
|
const getFormulaNumberRegex = memoize(function getFormulaNumberRegex(decimalSeparator) {
|
|
3348
3348
|
decimalSeparator = escapeRegExp(decimalSeparator);
|
|
3349
|
-
return new RegExp(`(?:^-?\\d+(?:${decimalSeparator}?\\d*(?:e
|
|
3349
|
+
return new RegExp(`(?:^-?\\d+(?:${decimalSeparator}?\\d*(?:e(\\+|-)?\\d+)?)?|^-?${decimalSeparator}\\d+)(?!\\w|!)`);
|
|
3350
3350
|
});
|
|
3351
3351
|
const getNumberRegex = memoize(function getNumberRegex(locale) {
|
|
3352
3352
|
const decimalSeparator = escapeRegExp(locale.decimalSeparator);
|
|
@@ -6290,6 +6290,13 @@ function getDuplicateSheetName(nameToDuplicate, existingNames) {
|
|
|
6290
6290
|
}
|
|
6291
6291
|
return name;
|
|
6292
6292
|
}
|
|
6293
|
+
function isSheetNameEqual(name1, name2) {
|
|
6294
|
+
if (name1 === undefined || name2 === undefined) {
|
|
6295
|
+
return false;
|
|
6296
|
+
}
|
|
6297
|
+
return (getUnquotedSheetName(name1.trim().toUpperCase()) ===
|
|
6298
|
+
getUnquotedSheetName(name2.trim().toUpperCase()));
|
|
6299
|
+
}
|
|
6293
6300
|
|
|
6294
6301
|
function computeTextLinesHeight(textLineHeight, numberOfLines = 1) {
|
|
6295
6302
|
return numberOfLines * (textLineHeight + MIN_CELL_TEXT_MARGIN) - MIN_CELL_TEXT_MARGIN;
|
|
@@ -8153,10 +8160,9 @@ const AGGREGATOR_NAMES = {
|
|
|
8153
8160
|
avg: _t("Average"),
|
|
8154
8161
|
sum: _t("Sum"),
|
|
8155
8162
|
};
|
|
8156
|
-
const NUMBER_CHAR_AGGREGATORS = ["max", "min", "avg", "sum", "count_distinct", "count"];
|
|
8157
8163
|
const AGGREGATORS_BY_FIELD_TYPE = {
|
|
8158
|
-
integer:
|
|
8159
|
-
char:
|
|
8164
|
+
integer: ["max", "min", "avg", "sum", "count_distinct", "count"],
|
|
8165
|
+
char: ["count_distinct", "count"],
|
|
8160
8166
|
boolean: ["count_distinct", "count", "bool_and", "bool_or"],
|
|
8161
8167
|
};
|
|
8162
8168
|
const AGGREGATORS = {};
|
|
@@ -9508,7 +9514,10 @@ function proxifyStoreMutation(store, callback) {
|
|
|
9508
9514
|
const functionProxy = new Proxy(value, {
|
|
9509
9515
|
// trap the function call
|
|
9510
9516
|
apply(target, thisArg, argArray) {
|
|
9511
|
-
Reflect.apply(target, thisStore, argArray);
|
|
9517
|
+
const res = Reflect.apply(target, thisStore, argArray);
|
|
9518
|
+
if (res === "noStateChange") {
|
|
9519
|
+
return;
|
|
9520
|
+
}
|
|
9512
9521
|
callback();
|
|
9513
9522
|
},
|
|
9514
9523
|
});
|
|
@@ -9530,7 +9539,7 @@ function getDependencyContainer(env) {
|
|
|
9530
9539
|
const ModelStore = createAbstractStore("Model");
|
|
9531
9540
|
|
|
9532
9541
|
class RendererStore {
|
|
9533
|
-
mutators = ["register", "unRegister"];
|
|
9542
|
+
mutators = ["register", "unRegister", "drawLayer"];
|
|
9534
9543
|
renderers = {};
|
|
9535
9544
|
register(renderer) {
|
|
9536
9545
|
if (!renderer.renderingLayers.length) {
|
|
@@ -9550,14 +9559,14 @@ class RendererStore {
|
|
|
9550
9559
|
}
|
|
9551
9560
|
drawLayer(context, layer) {
|
|
9552
9561
|
const renderers = this.renderers[layer];
|
|
9553
|
-
if (
|
|
9554
|
-
|
|
9555
|
-
|
|
9556
|
-
|
|
9557
|
-
|
|
9558
|
-
|
|
9559
|
-
context.ctx.restore();
|
|
9562
|
+
if (renderers) {
|
|
9563
|
+
for (const renderer of renderers) {
|
|
9564
|
+
context.ctx.save();
|
|
9565
|
+
renderer.drawLayer(context, layer);
|
|
9566
|
+
context.ctx.restore();
|
|
9567
|
+
}
|
|
9560
9568
|
}
|
|
9569
|
+
return "noStateChange";
|
|
9561
9570
|
}
|
|
9562
9571
|
}
|
|
9563
9572
|
|
|
@@ -9610,16 +9619,17 @@ class ComposerFocusStore extends SpreadsheetStore {
|
|
|
9610
9619
|
focusComposer(listener, args) {
|
|
9611
9620
|
this.activeComposer = listener;
|
|
9612
9621
|
if (this.getters.isReadonly()) {
|
|
9613
|
-
return;
|
|
9622
|
+
return "noStateChange";
|
|
9614
9623
|
}
|
|
9615
9624
|
this._focusMode = args.focusMode || "contentFocus";
|
|
9616
9625
|
if (this._focusMode !== "inactive") {
|
|
9617
9626
|
this.setComposerContent(args);
|
|
9618
9627
|
}
|
|
9628
|
+
return;
|
|
9619
9629
|
}
|
|
9620
9630
|
focusActiveComposer(args) {
|
|
9621
9631
|
if (this.getters.isReadonly()) {
|
|
9622
|
-
return;
|
|
9632
|
+
return "noStateChange";
|
|
9623
9633
|
}
|
|
9624
9634
|
if (!this.activeComposer) {
|
|
9625
9635
|
throw new Error("No composer is registered");
|
|
@@ -9628,6 +9638,7 @@ class ComposerFocusStore extends SpreadsheetStore {
|
|
|
9628
9638
|
if (this._focusMode !== "inactive") {
|
|
9629
9639
|
this.setComposerContent(args);
|
|
9630
9640
|
}
|
|
9641
|
+
return;
|
|
9631
9642
|
}
|
|
9632
9643
|
/**
|
|
9633
9644
|
* Start the edition or update the content if it's already started.
|
|
@@ -9977,7 +9988,7 @@ function getDefinedAxis(definition) {
|
|
|
9977
9988
|
}
|
|
9978
9989
|
function formatChartDatasetValue(axisFormats, locale) {
|
|
9979
9990
|
return (value, axisId) => {
|
|
9980
|
-
const format =
|
|
9991
|
+
const format = axisFormats?.[axisId];
|
|
9981
9992
|
return formatTickValue({ format, locale })(value);
|
|
9982
9993
|
};
|
|
9983
9994
|
}
|
|
@@ -10141,7 +10152,7 @@ function drawPieChartValues(chart, options, ctx) {
|
|
|
10141
10152
|
const y = bar.y + midRadius * Math.sin(midAngle) + 7;
|
|
10142
10153
|
ctx.fillStyle = chartFontColor(options.background);
|
|
10143
10154
|
ctx.strokeStyle = options.background || "#ffffff";
|
|
10144
|
-
const displayValue = options.callback(value);
|
|
10155
|
+
const displayValue = options.callback(value, "y");
|
|
10145
10156
|
drawTextWithBackground(displayValue, x, y, ctx);
|
|
10146
10157
|
}
|
|
10147
10158
|
}
|
|
@@ -19031,6 +19042,9 @@ const PIVOT_VALUE = {
|
|
|
19031
19042
|
};
|
|
19032
19043
|
}
|
|
19033
19044
|
const domain = pivot.parseArgsToPivotDomain(domainArgs);
|
|
19045
|
+
if (this.getters.getActiveSheetId() === this.__originSheetId) {
|
|
19046
|
+
this.getters.getPivotPresenceTracker(pivotId)?.trackValue(_measure, domain);
|
|
19047
|
+
}
|
|
19034
19048
|
return pivot.getPivotCellValueAndFormat(_measure, domain);
|
|
19035
19049
|
},
|
|
19036
19050
|
};
|
|
@@ -19062,6 +19076,9 @@ const PIVOT_HEADER = {
|
|
|
19062
19076
|
};
|
|
19063
19077
|
}
|
|
19064
19078
|
const domain = pivot.parseArgsToPivotDomain(domainArgs);
|
|
19079
|
+
if (this.getters.getActiveSheetId() === this.__originSheetId) {
|
|
19080
|
+
this.getters.getPivotPresenceTracker(_pivotId)?.trackHeader(domain);
|
|
19081
|
+
}
|
|
19065
19082
|
const lastNode = domain.at(-1);
|
|
19066
19083
|
if (lastNode?.field === "measure") {
|
|
19067
19084
|
return pivot.getPivotMeasureValue(toString(lastNode.value), domain);
|
|
@@ -19284,6 +19301,9 @@ function isEmpty(data) {
|
|
|
19284
19301
|
return data === undefined || data.value === null;
|
|
19285
19302
|
}
|
|
19286
19303
|
const getNeutral = { number: 0, string: "", boolean: false };
|
|
19304
|
+
function areAlmostEqual(value1, value2, epsilon = 2e-16) {
|
|
19305
|
+
return Math.abs(value1 - value2) < epsilon;
|
|
19306
|
+
}
|
|
19287
19307
|
const EQ = {
|
|
19288
19308
|
description: _t("Equal."),
|
|
19289
19309
|
args: [
|
|
@@ -19305,6 +19325,9 @@ const EQ = {
|
|
|
19305
19325
|
if (typeof _value2 === "string") {
|
|
19306
19326
|
_value2 = _value2.toUpperCase();
|
|
19307
19327
|
}
|
|
19328
|
+
if (typeof _value1 === "number" && typeof _value2 === "number") {
|
|
19329
|
+
return { value: areAlmostEqual(_value1, _value2) };
|
|
19330
|
+
}
|
|
19308
19331
|
return { value: _value1 === _value2 };
|
|
19309
19332
|
},
|
|
19310
19333
|
};
|
|
@@ -19344,6 +19367,9 @@ const GT = {
|
|
|
19344
19367
|
],
|
|
19345
19368
|
compute: function (value1, value2) {
|
|
19346
19369
|
return applyRelationalOperator(value1, value2, (v1, v2) => {
|
|
19370
|
+
if (typeof v1 === "number" && typeof v2 === "number") {
|
|
19371
|
+
return !areAlmostEqual(v1, v2) && v1 > v2;
|
|
19372
|
+
}
|
|
19347
19373
|
return v1 > v2;
|
|
19348
19374
|
});
|
|
19349
19375
|
},
|
|
@@ -19359,6 +19385,9 @@ const GTE = {
|
|
|
19359
19385
|
],
|
|
19360
19386
|
compute: function (value1, value2) {
|
|
19361
19387
|
return applyRelationalOperator(value1, value2, (v1, v2) => {
|
|
19388
|
+
if (typeof v1 === "number" && typeof v2 === "number") {
|
|
19389
|
+
return areAlmostEqual(v1, v2) || v1 > v2;
|
|
19390
|
+
}
|
|
19362
19391
|
return v1 >= v2;
|
|
19363
19392
|
});
|
|
19364
19393
|
},
|
|
@@ -20965,7 +20994,7 @@ class AbstractComposerStore extends SpreadsheetStore {
|
|
|
20965
20994
|
.find((token) => {
|
|
20966
20995
|
const { xc, sheetName: sheet } = splitReference(token.value);
|
|
20967
20996
|
const sheetName = sheet || this.getters.getSheetName(this.sheetId);
|
|
20968
|
-
if (this.getters.getSheetName(activeSheetId)
|
|
20997
|
+
if (!isSheetNameEqual(this.getters.getSheetName(activeSheetId), sheetName)) {
|
|
20969
20998
|
return false;
|
|
20970
20999
|
}
|
|
20971
21000
|
const refRange = this.getters.getRangeFromSheetXC(activeSheetId, xc);
|
|
@@ -24576,7 +24605,7 @@ function getRangeSize(reference, defaultSheetIndex, data) {
|
|
|
24576
24605
|
({ xc, sheetName } = splitReference(reference));
|
|
24577
24606
|
let rangeSheetIndex;
|
|
24578
24607
|
if (sheetName) {
|
|
24579
|
-
const index = data.sheets.findIndex((sheet) => sheet.name
|
|
24608
|
+
const index = data.sheets.findIndex((sheet) => isSheetNameEqual(sheet.name, sheetName));
|
|
24580
24609
|
if (index < 0) {
|
|
24581
24610
|
throw new Error("Unable to find a sheet with the name " + sheetName);
|
|
24582
24611
|
}
|
|
@@ -24917,7 +24946,7 @@ function convertFormula(formula, data) {
|
|
|
24917
24946
|
formula = formula.replace(externalReferenceRegex, (match, externalRefId, sheetName, cellRef) => {
|
|
24918
24947
|
externalRefId = Number(externalRefId) - 1;
|
|
24919
24948
|
cellRef = cellRef.replace(/\$/g, "");
|
|
24920
|
-
const sheetIndex = data.externalBooks[externalRefId].sheetNames.findIndex((name) => name
|
|
24949
|
+
const sheetIndex = data.externalBooks[externalRefId].sheetNames.findIndex((name) => isSheetNameEqual(name, sheetName));
|
|
24921
24950
|
if (sheetIndex === -1) {
|
|
24922
24951
|
return match;
|
|
24923
24952
|
}
|
|
@@ -25568,7 +25597,7 @@ function convertPivotTableConfig(pivotTable) {
|
|
|
25568
25597
|
*/
|
|
25569
25598
|
function convertTableFormulaReferences(convertedSheets, xlsxSheets) {
|
|
25570
25599
|
for (let tableSheet of convertedSheets) {
|
|
25571
|
-
const tables = xlsxSheets.find((s) => s.sheetName
|
|
25600
|
+
const tables = xlsxSheets.find((s) => isSheetNameEqual(s.sheetName, tableSheet.name)).tables;
|
|
25572
25601
|
for (let table of tables) {
|
|
25573
25602
|
const tabRef = table.name + "[";
|
|
25574
25603
|
for (let sheet of convertedSheets) {
|
|
@@ -32403,12 +32432,20 @@ class HoveredCellStore extends SpreadsheetStore {
|
|
|
32403
32432
|
}
|
|
32404
32433
|
}
|
|
32405
32434
|
hover(position) {
|
|
32435
|
+
if (position.col === this.col && position.row === this.row) {
|
|
32436
|
+
return "noStateChange";
|
|
32437
|
+
}
|
|
32406
32438
|
this.col = position.col;
|
|
32407
32439
|
this.row = position.row;
|
|
32440
|
+
return;
|
|
32408
32441
|
}
|
|
32409
32442
|
clear() {
|
|
32443
|
+
if (this.col === undefined && this.row === undefined) {
|
|
32444
|
+
return "noStateChange";
|
|
32445
|
+
}
|
|
32410
32446
|
this.col = undefined;
|
|
32411
32447
|
this.row = undefined;
|
|
32448
|
+
return;
|
|
32412
32449
|
}
|
|
32413
32450
|
}
|
|
32414
32451
|
|
|
@@ -32430,7 +32467,11 @@ class CellPopoverStore extends SpreadsheetStore {
|
|
|
32430
32467
|
this.persistentPopover = { col, row, sheetId, type };
|
|
32431
32468
|
}
|
|
32432
32469
|
close() {
|
|
32470
|
+
if (!this.persistentPopover) {
|
|
32471
|
+
return "noStateChange";
|
|
32472
|
+
}
|
|
32433
32473
|
this.persistentPopover = undefined;
|
|
32474
|
+
return;
|
|
32434
32475
|
}
|
|
32435
32476
|
get persistentCellPopover() {
|
|
32436
32477
|
return ((this.persistentPopover && { isOpen: true, ...this.persistentPopover }) || { isOpen: false });
|
|
@@ -40262,10 +40303,18 @@ class ChartPanel extends Component {
|
|
|
40262
40303
|
}
|
|
40263
40304
|
|
|
40264
40305
|
class DOMFocusableElementStore {
|
|
40265
|
-
mutators = ["setFocusableElement"];
|
|
40306
|
+
mutators = ["setFocusableElement", "focus"];
|
|
40266
40307
|
focusableElement = undefined;
|
|
40267
40308
|
setFocusableElement(element) {
|
|
40268
40309
|
this.focusableElement = element;
|
|
40310
|
+
return "noStateChange";
|
|
40311
|
+
}
|
|
40312
|
+
focus() {
|
|
40313
|
+
if (this.focusableElement === document.activeElement) {
|
|
40314
|
+
return "noStateChange";
|
|
40315
|
+
}
|
|
40316
|
+
this.focusableElement?.focus();
|
|
40317
|
+
return;
|
|
40269
40318
|
}
|
|
40270
40319
|
}
|
|
40271
40320
|
|
|
@@ -40855,7 +40904,7 @@ class Composer extends Component {
|
|
|
40855
40904
|
if (document.activeElement === this.contentHelper.el &&
|
|
40856
40905
|
this.props.composerStore.editionMode === "inactive" &&
|
|
40857
40906
|
!this.props.isDefaultFocus) {
|
|
40858
|
-
this.DOMFocusableElementStore.
|
|
40907
|
+
this.DOMFocusableElementStore.focus();
|
|
40859
40908
|
}
|
|
40860
40909
|
});
|
|
40861
40910
|
useEffect(() => {
|
|
@@ -46602,7 +46651,12 @@ class SpreadsheetPivot {
|
|
|
46602
46651
|
entry[field.name] = { value: null, type: CellValueType.empty, formattedValue: "" };
|
|
46603
46652
|
}
|
|
46604
46653
|
else {
|
|
46605
|
-
|
|
46654
|
+
if (field.type === "char") {
|
|
46655
|
+
entry[field.name] = { ...cell, value: cell.formattedValue || null };
|
|
46656
|
+
}
|
|
46657
|
+
else {
|
|
46658
|
+
entry[field.name] = cell;
|
|
46659
|
+
}
|
|
46606
46660
|
}
|
|
46607
46661
|
}
|
|
46608
46662
|
entry["__count"] = { value: 1, type: CellValueType.number, formattedValue: "1" };
|
|
@@ -51631,10 +51685,6 @@ function useGridDrawing(refName, model, canvasSize) {
|
|
|
51631
51685
|
ctx.scale(dpr, dpr);
|
|
51632
51686
|
for (const layer of OrderedLayers()) {
|
|
51633
51687
|
model.drawLayer(renderingContext, layer);
|
|
51634
|
-
// @ts-ignore 'drawLayer' is not declated as a mutator because:
|
|
51635
|
-
// it does not mutate anything. Most importantly it's used
|
|
51636
|
-
// during rendering. Invoking a mutator during rendering would
|
|
51637
|
-
// trigger another rendering, ultimately resulting in an infinite loop.
|
|
51638
51688
|
rendererStore.drawLayer(renderingContext, layer);
|
|
51639
51689
|
}
|
|
51640
51690
|
}
|
|
@@ -52324,7 +52374,7 @@ class Grid extends Component {
|
|
|
52324
52374
|
this.cellPopovers = useStore(CellPopoverStore);
|
|
52325
52375
|
useEffect(() => {
|
|
52326
52376
|
if (!this.sidePanel.isOpen) {
|
|
52327
|
-
this.DOMFocusableElementStore.
|
|
52377
|
+
this.DOMFocusableElementStore.focus();
|
|
52328
52378
|
}
|
|
52329
52379
|
}, () => [this.sidePanel.isOpen]);
|
|
52330
52380
|
useTouchScroll(this.gridRef, this.moveCanvas.bind(this), () => {
|
|
@@ -52534,7 +52584,7 @@ class Grid extends Component {
|
|
|
52534
52584
|
focusDefaultElement() {
|
|
52535
52585
|
if (!this.env.model.getters.getSelectedFigureId() &&
|
|
52536
52586
|
this.composerFocusStore.activeComposer.editionMode === "inactive") {
|
|
52537
|
-
this.DOMFocusableElementStore.
|
|
52587
|
+
this.DOMFocusableElementStore.focus();
|
|
52538
52588
|
}
|
|
52539
52589
|
}
|
|
52540
52590
|
get gridEl() {
|
|
@@ -52879,6 +52929,322 @@ class Grid extends Component {
|
|
|
52879
52929
|
}
|
|
52880
52930
|
}
|
|
52881
52931
|
|
|
52932
|
+
css /* scss */ `
|
|
52933
|
+
.o_pivot_html_renderer {
|
|
52934
|
+
width: 100%;
|
|
52935
|
+
border-collapse: collapse;
|
|
52936
|
+
|
|
52937
|
+
&:hover {
|
|
52938
|
+
cursor: pointer;
|
|
52939
|
+
}
|
|
52940
|
+
|
|
52941
|
+
td,
|
|
52942
|
+
th {
|
|
52943
|
+
border: 1px solid #dee2e6;
|
|
52944
|
+
background-color: #fff;
|
|
52945
|
+
padding: 0.3rem;
|
|
52946
|
+
white-space: nowrap;
|
|
52947
|
+
|
|
52948
|
+
&:hover {
|
|
52949
|
+
filter: brightness(0.9);
|
|
52950
|
+
}
|
|
52951
|
+
}
|
|
52952
|
+
|
|
52953
|
+
td {
|
|
52954
|
+
text-align: right;
|
|
52955
|
+
}
|
|
52956
|
+
|
|
52957
|
+
th {
|
|
52958
|
+
background-color: #f5f5f5;
|
|
52959
|
+
font-weight: bold;
|
|
52960
|
+
color: black;
|
|
52961
|
+
}
|
|
52962
|
+
|
|
52963
|
+
.o_missing_value {
|
|
52964
|
+
color: #46646d;
|
|
52965
|
+
background: #e7f2f6;
|
|
52966
|
+
}
|
|
52967
|
+
}
|
|
52968
|
+
`;
|
|
52969
|
+
class PivotHTMLRenderer extends Component {
|
|
52970
|
+
static template = "o_spreadsheet.PivotHTMLRenderer";
|
|
52971
|
+
static components = { Checkbox };
|
|
52972
|
+
static props = {
|
|
52973
|
+
pivotId: String,
|
|
52974
|
+
onCellClicked: Function,
|
|
52975
|
+
};
|
|
52976
|
+
pivot = this.env.model.getters.getPivot(this.props.pivotId);
|
|
52977
|
+
data = {
|
|
52978
|
+
columns: [],
|
|
52979
|
+
rows: [],
|
|
52980
|
+
values: [],
|
|
52981
|
+
};
|
|
52982
|
+
state = useState({
|
|
52983
|
+
showMissingValuesOnly: false,
|
|
52984
|
+
});
|
|
52985
|
+
setup() {
|
|
52986
|
+
const table = this.pivot.getTableStructure();
|
|
52987
|
+
const formulaId = this.env.model.getters.getPivotFormulaId(this.props.pivotId);
|
|
52988
|
+
this.data = {
|
|
52989
|
+
columns: this._buildColHeaders(formulaId, table),
|
|
52990
|
+
rows: this._buildRowHeaders(formulaId, table),
|
|
52991
|
+
values: this._buildValues(formulaId, table),
|
|
52992
|
+
};
|
|
52993
|
+
}
|
|
52994
|
+
get tracker() {
|
|
52995
|
+
return this.env.model.getters.getPivotPresenceTracker(this.props.pivotId);
|
|
52996
|
+
}
|
|
52997
|
+
// ---------------------------------------------------------------------
|
|
52998
|
+
// Missing values building
|
|
52999
|
+
// ---------------------------------------------------------------------
|
|
53000
|
+
/**
|
|
53001
|
+
* Retrieve the data to display in the Pivot Table
|
|
53002
|
+
* In the case when showMissingValuesOnly is false, the returned value
|
|
53003
|
+
* is the complete data
|
|
53004
|
+
* In the case when showMissingValuesOnly is true, the returned value is
|
|
53005
|
+
* the data which contains only missing values in the rows and cols. In
|
|
53006
|
+
* the rows, we also return the parent rows of rows which contains missing
|
|
53007
|
+
* values, to give context to the user.
|
|
53008
|
+
*
|
|
53009
|
+
*/
|
|
53010
|
+
getTableData() {
|
|
53011
|
+
if (!this.state.showMissingValuesOnly) {
|
|
53012
|
+
return this.data;
|
|
53013
|
+
}
|
|
53014
|
+
const colIndexes = this.getColumnsIndexes();
|
|
53015
|
+
const rowIndexes = this.getRowsIndexes();
|
|
53016
|
+
const columns = this.buildColumnsMissing(colIndexes);
|
|
53017
|
+
const rows = this.buildRowsMissing(rowIndexes);
|
|
53018
|
+
const values = this.buildValuesMissing(colIndexes, rowIndexes);
|
|
53019
|
+
return { columns, rows, values };
|
|
53020
|
+
}
|
|
53021
|
+
/**
|
|
53022
|
+
* Retrieve the parents of the given row
|
|
53023
|
+
* ex:
|
|
53024
|
+
* Australia
|
|
53025
|
+
* January
|
|
53026
|
+
* February
|
|
53027
|
+
* The parent of "January" is "Australia"
|
|
53028
|
+
*/
|
|
53029
|
+
addRecursiveRow(index) {
|
|
53030
|
+
const rows = this.pivot.getTableStructure().rows;
|
|
53031
|
+
const row = [...rows[index].values];
|
|
53032
|
+
if (row.length <= 1) {
|
|
53033
|
+
return [index];
|
|
53034
|
+
}
|
|
53035
|
+
row.pop();
|
|
53036
|
+
const parentRowIndex = rows.findIndex((r) => JSON.stringify(r.values) === JSON.stringify(row));
|
|
53037
|
+
return [index].concat(this.addRecursiveRow(parentRowIndex));
|
|
53038
|
+
}
|
|
53039
|
+
/**
|
|
53040
|
+
* Create the columns to be used, based on the indexes of the columns in
|
|
53041
|
+
* which a missing value is present
|
|
53042
|
+
*
|
|
53043
|
+
*/
|
|
53044
|
+
buildColumnsMissing(indexes) {
|
|
53045
|
+
// columnsMap explode the columns in an array of array of the same
|
|
53046
|
+
// size with the index of each column, repeated 'span' times.
|
|
53047
|
+
// ex:
|
|
53048
|
+
// | A | B |
|
|
53049
|
+
// | 1 | 2 | 3 |
|
|
53050
|
+
// => [
|
|
53051
|
+
// [0, 0, 1]
|
|
53052
|
+
// [0, 1, 2]
|
|
53053
|
+
// ]
|
|
53054
|
+
const columnsMap = [];
|
|
53055
|
+
for (const column of this.data.columns) {
|
|
53056
|
+
const columnMap = [];
|
|
53057
|
+
for (const index in column) {
|
|
53058
|
+
for (let i = 0; i < column[index].span; i++) {
|
|
53059
|
+
columnMap.push(parseInt(index, 10));
|
|
53060
|
+
}
|
|
53061
|
+
}
|
|
53062
|
+
columnsMap.push(columnMap);
|
|
53063
|
+
}
|
|
53064
|
+
// Remove the columns that are not present in indexes
|
|
53065
|
+
for (let i = columnsMap[columnsMap.length - 1].length; i >= 0; i--) {
|
|
53066
|
+
if (!indexes.includes(i)) {
|
|
53067
|
+
for (const columnMap of columnsMap) {
|
|
53068
|
+
columnMap.splice(i, 1);
|
|
53069
|
+
}
|
|
53070
|
+
}
|
|
53071
|
+
}
|
|
53072
|
+
// Build the columns
|
|
53073
|
+
const columns = [];
|
|
53074
|
+
for (const mapIndex in columnsMap) {
|
|
53075
|
+
const column = [];
|
|
53076
|
+
let index = undefined;
|
|
53077
|
+
let span = 1;
|
|
53078
|
+
for (let i = 0; i < columnsMap[mapIndex].length; i++) {
|
|
53079
|
+
if (index !== columnsMap[mapIndex][i]) {
|
|
53080
|
+
if (index !== undefined) {
|
|
53081
|
+
column.push(Object.assign({}, this.data.columns[mapIndex][index], { span }));
|
|
53082
|
+
}
|
|
53083
|
+
index = columnsMap[mapIndex][i];
|
|
53084
|
+
span = 1;
|
|
53085
|
+
}
|
|
53086
|
+
else {
|
|
53087
|
+
span++;
|
|
53088
|
+
}
|
|
53089
|
+
}
|
|
53090
|
+
if (index !== undefined) {
|
|
53091
|
+
column.push(Object.assign({}, this.data.columns[mapIndex][index], { span }));
|
|
53092
|
+
}
|
|
53093
|
+
columns.push(column);
|
|
53094
|
+
}
|
|
53095
|
+
return columns;
|
|
53096
|
+
}
|
|
53097
|
+
/**
|
|
53098
|
+
* Create the rows to be used, based on the indexes of the rows in
|
|
53099
|
+
* which a missing value is present.
|
|
53100
|
+
*/
|
|
53101
|
+
buildRowsMissing(indexes) {
|
|
53102
|
+
return indexes.map((index) => this.data.rows[index]);
|
|
53103
|
+
}
|
|
53104
|
+
/**
|
|
53105
|
+
* Create the value to be used, based on the indexes of the columns and
|
|
53106
|
+
* rows in which a missing value is present.
|
|
53107
|
+
*/
|
|
53108
|
+
buildValuesMissing(colIndexes, rowIndexes) {
|
|
53109
|
+
const values = colIndexes.map(() => []);
|
|
53110
|
+
for (const row of rowIndexes) {
|
|
53111
|
+
for (const col in colIndexes) {
|
|
53112
|
+
values[col].push(this.data.values[colIndexes[col]][row]);
|
|
53113
|
+
}
|
|
53114
|
+
}
|
|
53115
|
+
return values;
|
|
53116
|
+
}
|
|
53117
|
+
getColumnsIndexes() {
|
|
53118
|
+
const indexes = new Set();
|
|
53119
|
+
for (let i = 0; i < this.data.columns.length; i++) {
|
|
53120
|
+
const exploded = [];
|
|
53121
|
+
for (let y = 0; y < this.data.columns[i].length; y++) {
|
|
53122
|
+
for (let x = 0; x < this.data.columns[i][y].span; x++) {
|
|
53123
|
+
exploded.push(this.data.columns[i][y]);
|
|
53124
|
+
}
|
|
53125
|
+
}
|
|
53126
|
+
for (let y = 0; y < exploded.length; y++) {
|
|
53127
|
+
if (exploded[y].isMissing) {
|
|
53128
|
+
indexes.add(y);
|
|
53129
|
+
}
|
|
53130
|
+
}
|
|
53131
|
+
}
|
|
53132
|
+
for (let i = 0; i < this.data.columns[this.data.columns.length - 1].length; i++) {
|
|
53133
|
+
const values = this.data.values[i];
|
|
53134
|
+
if (values.find((x) => x.isMissing)) {
|
|
53135
|
+
indexes.add(i);
|
|
53136
|
+
}
|
|
53137
|
+
}
|
|
53138
|
+
return Array.from(indexes).sort((a, b) => a - b);
|
|
53139
|
+
}
|
|
53140
|
+
getRowsIndexes() {
|
|
53141
|
+
const rowIndexes = new Set();
|
|
53142
|
+
for (let i = 0; i < this.data.rows.length; i++) {
|
|
53143
|
+
if (this.data.rows[i].isMissing) {
|
|
53144
|
+
rowIndexes.add(i);
|
|
53145
|
+
}
|
|
53146
|
+
for (const col of this.data.values) {
|
|
53147
|
+
if (col[i].isMissing) {
|
|
53148
|
+
this.addRecursiveRow(i).forEach((x) => rowIndexes.add(x));
|
|
53149
|
+
}
|
|
53150
|
+
}
|
|
53151
|
+
}
|
|
53152
|
+
return Array.from(rowIndexes).sort((a, b) => a - b);
|
|
53153
|
+
}
|
|
53154
|
+
// ---------------------------------------------------------------------
|
|
53155
|
+
// Data table creation
|
|
53156
|
+
// ---------------------------------------------------------------------
|
|
53157
|
+
_buildColHeaders(id, table) {
|
|
53158
|
+
const headers = [];
|
|
53159
|
+
for (const row of table.columns) {
|
|
53160
|
+
const current = [];
|
|
53161
|
+
for (const cell of row) {
|
|
53162
|
+
const args = [];
|
|
53163
|
+
for (let i = 0; i < cell.fields.length; i++) {
|
|
53164
|
+
args.push({ value: cell.fields[i] }, { value: cell.values[i] });
|
|
53165
|
+
}
|
|
53166
|
+
const domain = this.pivot.parseArgsToPivotDomain(args);
|
|
53167
|
+
const locale = this.env.model.getters.getLocale();
|
|
53168
|
+
if (domain.at(-1)?.field === "measure") {
|
|
53169
|
+
const { value, format } = this.pivot.getPivotMeasureValue(toString(domain.at(-1).value), domain);
|
|
53170
|
+
current.push({
|
|
53171
|
+
formula: `=PIVOT.HEADER(${generatePivotArgs(id, domain).join(",")})`,
|
|
53172
|
+
value: formatValue(value, { format, locale }),
|
|
53173
|
+
span: cell.width,
|
|
53174
|
+
isMissing: !this.tracker?.isHeaderPresent(domain),
|
|
53175
|
+
});
|
|
53176
|
+
}
|
|
53177
|
+
else {
|
|
53178
|
+
const { value, format } = this.pivot.getPivotHeaderValueAndFormat(domain);
|
|
53179
|
+
current.push({
|
|
53180
|
+
formula: `=PIVOT.HEADER(${generatePivotArgs(id, domain).join(",")})`,
|
|
53181
|
+
value: formatValue(value, { format, locale }),
|
|
53182
|
+
span: cell.width,
|
|
53183
|
+
isMissing: !this.tracker?.isHeaderPresent(domain),
|
|
53184
|
+
});
|
|
53185
|
+
}
|
|
53186
|
+
}
|
|
53187
|
+
headers.push(current);
|
|
53188
|
+
}
|
|
53189
|
+
const last = headers[headers.length - 1];
|
|
53190
|
+
headers[headers.length - 1] = last.map((cell) => {
|
|
53191
|
+
if (!cell.isMissing) {
|
|
53192
|
+
cell.style = "color: #756f6f;";
|
|
53193
|
+
}
|
|
53194
|
+
return cell;
|
|
53195
|
+
});
|
|
53196
|
+
return headers;
|
|
53197
|
+
}
|
|
53198
|
+
_buildRowHeaders(id, table) {
|
|
53199
|
+
const headers = [];
|
|
53200
|
+
for (const row of table.rows) {
|
|
53201
|
+
const args = [];
|
|
53202
|
+
for (let i = 0; i < row.fields.length; i++) {
|
|
53203
|
+
args.push({ value: row.fields[i] }, { value: row.values[i] });
|
|
53204
|
+
}
|
|
53205
|
+
const domain = this.pivot.parseArgsToPivotDomain(args);
|
|
53206
|
+
const { value, format } = this.pivot.getPivotHeaderValueAndFormat(domain);
|
|
53207
|
+
const locale = this.env.model.getters.getLocale();
|
|
53208
|
+
const cell = {
|
|
53209
|
+
formula: `=PIVOT.HEADER(${generatePivotArgs(id, domain).join(",")})`,
|
|
53210
|
+
value: formatValue(value, { format, locale }),
|
|
53211
|
+
isMissing: !this.tracker?.isHeaderPresent(domain),
|
|
53212
|
+
};
|
|
53213
|
+
if (row.indent > 1) {
|
|
53214
|
+
cell.style = `padding-left: ${row.indent - 1 * 10}px`;
|
|
53215
|
+
}
|
|
53216
|
+
headers.push(cell);
|
|
53217
|
+
}
|
|
53218
|
+
return headers;
|
|
53219
|
+
}
|
|
53220
|
+
_buildValues(id, table) {
|
|
53221
|
+
const values = [];
|
|
53222
|
+
for (const col of table.columns.at(-1) || []) {
|
|
53223
|
+
const current = [];
|
|
53224
|
+
const measure = toString(col.values[col.values.length - 1]);
|
|
53225
|
+
for (const row of table.rows) {
|
|
53226
|
+
const args = [];
|
|
53227
|
+
for (let i = 0; i < row.fields.length; i++) {
|
|
53228
|
+
args.push({ value: row.fields[i] }, { value: row.values[i] });
|
|
53229
|
+
}
|
|
53230
|
+
for (let i = 0; i < col.fields.length - 1; i++) {
|
|
53231
|
+
args.push({ value: col.fields[i] }, { value: col.values[i] });
|
|
53232
|
+
}
|
|
53233
|
+
const domain = this.pivot.parseArgsToPivotDomain(args);
|
|
53234
|
+
const { value, format } = this.pivot.getPivotCellValueAndFormat(measure, domain);
|
|
53235
|
+
const locale = this.env.model.getters.getLocale();
|
|
53236
|
+
current.push({
|
|
53237
|
+
formula: `=PIVOT.VALUE(${generatePivotArgs(id, domain, measure).join(",")})`,
|
|
53238
|
+
value: formatValue(value, { format, locale }),
|
|
53239
|
+
isMissing: !this.tracker?.isValuePresent(measure, domain),
|
|
53240
|
+
});
|
|
53241
|
+
}
|
|
53242
|
+
values.push(current);
|
|
53243
|
+
}
|
|
53244
|
+
return values;
|
|
53245
|
+
}
|
|
53246
|
+
}
|
|
53247
|
+
|
|
52882
53248
|
/**
|
|
52883
53249
|
* BasePlugin
|
|
52884
53250
|
*
|
|
@@ -56316,7 +56682,7 @@ class RangeAdapter {
|
|
|
56316
56682
|
if (range.sheetId === cmd.sheetId) {
|
|
56317
56683
|
return { changeType: "CHANGE", range };
|
|
56318
56684
|
}
|
|
56319
|
-
if (
|
|
56685
|
+
if (isSheetNameEqual(range.invalidSheetName, cmd.name)) {
|
|
56320
56686
|
const invalidSheetName = undefined;
|
|
56321
56687
|
const sheetId = cmd.sheetId;
|
|
56322
56688
|
const newRange = range.clone({ sheetId, invalidSheetName });
|
|
@@ -56901,7 +57267,7 @@ class SheetPlugin extends CorePlugin {
|
|
|
56901
57267
|
if (name) {
|
|
56902
57268
|
const unquotedName = getUnquotedSheetName(name);
|
|
56903
57269
|
for (const key in this.sheetIdsMapName) {
|
|
56904
|
-
if (key
|
|
57270
|
+
if (isSheetNameEqual(key, unquotedName)) {
|
|
56905
57271
|
return this.sheetIdsMapName[key];
|
|
56906
57272
|
}
|
|
56907
57273
|
}
|
|
@@ -57149,7 +57515,7 @@ class SheetPlugin extends CorePlugin {
|
|
|
57149
57515
|
}
|
|
57150
57516
|
const { orderedSheetIds, sheets } = this;
|
|
57151
57517
|
const name = cmd.name && cmd.name.trim().toLowerCase();
|
|
57152
|
-
if (orderedSheetIds.find((id) => sheets[id]?.name
|
|
57518
|
+
if (orderedSheetIds.find((id) => isSheetNameEqual(sheets[id]?.name, name) && id !== cmd.sheetId)) {
|
|
57153
57519
|
return "DuplicatedSheetName" /* CommandResult.DuplicatedSheetName */;
|
|
57154
57520
|
}
|
|
57155
57521
|
if (FORBIDDEN_SHEETNAME_CHARS_IN_EXCEL_REGEX.test(name)) {
|
|
@@ -66052,6 +66418,55 @@ class HistoryPlugin extends UIPlugin {
|
|
|
66052
66418
|
}
|
|
66053
66419
|
}
|
|
66054
66420
|
|
|
66421
|
+
class PivotPresenceTracker {
|
|
66422
|
+
trackedValues = new Set();
|
|
66423
|
+
domainToArray(domain) {
|
|
66424
|
+
return domain.flatMap((node) => [node.field, toString(node.value)]);
|
|
66425
|
+
}
|
|
66426
|
+
isValuePresent(measure, domain) {
|
|
66427
|
+
const key = JSON.stringify({ measure, domain: this.domainToArray(domain) });
|
|
66428
|
+
return this.trackedValues.has(key);
|
|
66429
|
+
}
|
|
66430
|
+
isHeaderPresent(domain) {
|
|
66431
|
+
const key = JSON.stringify({ domain: this.domainToArray(domain) });
|
|
66432
|
+
return this.trackedValues.has(key);
|
|
66433
|
+
}
|
|
66434
|
+
trackValue(measure, domain) {
|
|
66435
|
+
const key = JSON.stringify({ measure, domain: this.domainToArray(domain) });
|
|
66436
|
+
this.trackedValues.add(key);
|
|
66437
|
+
}
|
|
66438
|
+
trackHeader(domain) {
|
|
66439
|
+
const key = JSON.stringify({ domain: this.domainToArray(domain) });
|
|
66440
|
+
this.trackedValues.add(key);
|
|
66441
|
+
}
|
|
66442
|
+
}
|
|
66443
|
+
|
|
66444
|
+
class PivotPresencePlugin extends UIPlugin {
|
|
66445
|
+
static getters = ["getPivotPresenceTracker"];
|
|
66446
|
+
trackPresencePivotId;
|
|
66447
|
+
tracker;
|
|
66448
|
+
handle(cmd) {
|
|
66449
|
+
switch (cmd.type) {
|
|
66450
|
+
case "PIVOT_START_PRESENCE_TRACKING":
|
|
66451
|
+
this.tracker = new PivotPresenceTracker();
|
|
66452
|
+
this.trackPresencePivotId = cmd.pivotId;
|
|
66453
|
+
break;
|
|
66454
|
+
case "PIVOT_STOP_PRESENCE_TRACKING":
|
|
66455
|
+
this.trackPresencePivotId = undefined;
|
|
66456
|
+
break;
|
|
66457
|
+
}
|
|
66458
|
+
}
|
|
66459
|
+
getPivotPresenceTracker(pivotId) {
|
|
66460
|
+
if (this.trackPresencePivotId !== pivotId) {
|
|
66461
|
+
return undefined;
|
|
66462
|
+
}
|
|
66463
|
+
if (!this.tracker) {
|
|
66464
|
+
throw new Error("Tracker not initialized");
|
|
66465
|
+
}
|
|
66466
|
+
return this.tracker;
|
|
66467
|
+
}
|
|
66468
|
+
}
|
|
66469
|
+
|
|
66055
66470
|
class SplitToColumnsPlugin extends UIPlugin {
|
|
66056
66471
|
static getters = ["getAutomaticSeparator"];
|
|
66057
66472
|
allowDispatch(cmd) {
|
|
@@ -68839,6 +69254,7 @@ const featurePluginRegistry = new Registry()
|
|
|
68839
69254
|
.add("automatic_sum", AutomaticSumPlugin)
|
|
68840
69255
|
.add("format", FormatPlugin)
|
|
68841
69256
|
.add("insert_pivot", InsertPivotPlugin)
|
|
69257
|
+
.add("pivot_presence", PivotPresencePlugin)
|
|
68842
69258
|
.add("split_to_columns", SplitToColumnsPlugin)
|
|
68843
69259
|
.add("collaborative", CollaborativePlugin)
|
|
68844
69260
|
.add("history", HistoryPlugin)
|
|
@@ -69219,11 +69635,11 @@ class BottomBarSheet extends Component {
|
|
|
69219
69635
|
if (ev.key === "Enter") {
|
|
69220
69636
|
ev.preventDefault();
|
|
69221
69637
|
this.stopEdition();
|
|
69222
|
-
this.DOMFocusableElementStore.
|
|
69638
|
+
this.DOMFocusableElementStore.focus();
|
|
69223
69639
|
}
|
|
69224
69640
|
if (ev.key === "Escape") {
|
|
69225
69641
|
this.cancelEdition();
|
|
69226
|
-
this.DOMFocusableElementStore.
|
|
69642
|
+
this.DOMFocusableElementStore.focus();
|
|
69227
69643
|
}
|
|
69228
69644
|
}
|
|
69229
69645
|
onMouseEventSheetName(ev) {
|
|
@@ -75814,6 +76230,7 @@ const components = {
|
|
|
75814
76230
|
PivotDimensionOrder,
|
|
75815
76231
|
PivotDimension,
|
|
75816
76232
|
PivotLayoutConfigurator,
|
|
76233
|
+
PivotHTMLRenderer,
|
|
75817
76234
|
PivotDeferUpdate,
|
|
75818
76235
|
PivotTitleSection,
|
|
75819
76236
|
CogWheelMenu,
|
|
@@ -75863,6 +76280,6 @@ const chartHelpers = { ...CHART_HELPERS, ...CHART_RUNTIME_HELPERS };
|
|
|
75863
76280
|
export { AbstractCellClipboardHandler, AbstractChart, AbstractFigureClipboardHandler, CellErrorType, CommandResult, CorePlugin, DispatchResult, EvaluationError, Model, PivotRuntimeDefinition, Registry, Revision, SPREADSHEET_DIMENSIONS, Spreadsheet, SpreadsheetPivotTable, UIPlugin, __info__, addFunction, addRenderingLayer, astToFormula, chartHelpers, compile, compileTokens, components, constants, convertAstNodes, coreTypes, findCellInNewZone, functionCache, helpers, hooks, invalidateCFEvaluationCommands, invalidateDependenciesCommands, invalidateEvaluationCommands, iterateAstNodes, links, load, parse, parseTokens, readonlyAllowedCommands, registries, setDefaultSheetViewSize, setTranslationMethod, stores, tokenColors, tokenize };
|
|
75864
76281
|
|
|
75865
76282
|
|
|
75866
|
-
__info__.version = "18.1.
|
|
75867
|
-
__info__.date = "2025-05-
|
|
75868
|
-
__info__.hash = "
|
|
76283
|
+
__info__.version = "18.1.20";
|
|
76284
|
+
__info__.date = "2025-05-13T17:52:28.174Z";
|
|
76285
|
+
__info__.hash = "3e43a46";
|