@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
|
'use strict';
|
|
@@ -3348,7 +3348,7 @@ function isDateAfter(date, dateAfter) {
|
|
|
3348
3348
|
*/
|
|
3349
3349
|
const getFormulaNumberRegex = memoize(function getFormulaNumberRegex(decimalSeparator) {
|
|
3350
3350
|
decimalSeparator = escapeRegExp(decimalSeparator);
|
|
3351
|
-
return new RegExp(`(?:^-?\\d+(?:${decimalSeparator}?\\d*(?:e
|
|
3351
|
+
return new RegExp(`(?:^-?\\d+(?:${decimalSeparator}?\\d*(?:e(\\+|-)?\\d+)?)?|^-?${decimalSeparator}\\d+)(?!\\w|!)`);
|
|
3352
3352
|
});
|
|
3353
3353
|
const getNumberRegex = memoize(function getNumberRegex(locale) {
|
|
3354
3354
|
const decimalSeparator = escapeRegExp(locale.decimalSeparator);
|
|
@@ -6292,6 +6292,13 @@ function getDuplicateSheetName(nameToDuplicate, existingNames) {
|
|
|
6292
6292
|
}
|
|
6293
6293
|
return name;
|
|
6294
6294
|
}
|
|
6295
|
+
function isSheetNameEqual(name1, name2) {
|
|
6296
|
+
if (name1 === undefined || name2 === undefined) {
|
|
6297
|
+
return false;
|
|
6298
|
+
}
|
|
6299
|
+
return (getUnquotedSheetName(name1.trim().toUpperCase()) ===
|
|
6300
|
+
getUnquotedSheetName(name2.trim().toUpperCase()));
|
|
6301
|
+
}
|
|
6295
6302
|
|
|
6296
6303
|
function computeTextLinesHeight(textLineHeight, numberOfLines = 1) {
|
|
6297
6304
|
return numberOfLines * (textLineHeight + MIN_CELL_TEXT_MARGIN) - MIN_CELL_TEXT_MARGIN;
|
|
@@ -8155,10 +8162,9 @@ const AGGREGATOR_NAMES = {
|
|
|
8155
8162
|
avg: _t("Average"),
|
|
8156
8163
|
sum: _t("Sum"),
|
|
8157
8164
|
};
|
|
8158
|
-
const NUMBER_CHAR_AGGREGATORS = ["max", "min", "avg", "sum", "count_distinct", "count"];
|
|
8159
8165
|
const AGGREGATORS_BY_FIELD_TYPE = {
|
|
8160
|
-
integer:
|
|
8161
|
-
char:
|
|
8166
|
+
integer: ["max", "min", "avg", "sum", "count_distinct", "count"],
|
|
8167
|
+
char: ["count_distinct", "count"],
|
|
8162
8168
|
boolean: ["count_distinct", "count", "bool_and", "bool_or"],
|
|
8163
8169
|
};
|
|
8164
8170
|
const AGGREGATORS = {};
|
|
@@ -9510,7 +9516,10 @@ function proxifyStoreMutation(store, callback) {
|
|
|
9510
9516
|
const functionProxy = new Proxy(value, {
|
|
9511
9517
|
// trap the function call
|
|
9512
9518
|
apply(target, thisArg, argArray) {
|
|
9513
|
-
Reflect.apply(target, thisStore, argArray);
|
|
9519
|
+
const res = Reflect.apply(target, thisStore, argArray);
|
|
9520
|
+
if (res === "noStateChange") {
|
|
9521
|
+
return;
|
|
9522
|
+
}
|
|
9514
9523
|
callback();
|
|
9515
9524
|
},
|
|
9516
9525
|
});
|
|
@@ -9532,7 +9541,7 @@ function getDependencyContainer(env) {
|
|
|
9532
9541
|
const ModelStore = createAbstractStore("Model");
|
|
9533
9542
|
|
|
9534
9543
|
class RendererStore {
|
|
9535
|
-
mutators = ["register", "unRegister"];
|
|
9544
|
+
mutators = ["register", "unRegister", "drawLayer"];
|
|
9536
9545
|
renderers = {};
|
|
9537
9546
|
register(renderer) {
|
|
9538
9547
|
if (!renderer.renderingLayers.length) {
|
|
@@ -9552,14 +9561,14 @@ class RendererStore {
|
|
|
9552
9561
|
}
|
|
9553
9562
|
drawLayer(context, layer) {
|
|
9554
9563
|
const renderers = this.renderers[layer];
|
|
9555
|
-
if (
|
|
9556
|
-
|
|
9557
|
-
|
|
9558
|
-
|
|
9559
|
-
|
|
9560
|
-
|
|
9561
|
-
context.ctx.restore();
|
|
9564
|
+
if (renderers) {
|
|
9565
|
+
for (const renderer of renderers) {
|
|
9566
|
+
context.ctx.save();
|
|
9567
|
+
renderer.drawLayer(context, layer);
|
|
9568
|
+
context.ctx.restore();
|
|
9569
|
+
}
|
|
9562
9570
|
}
|
|
9571
|
+
return "noStateChange";
|
|
9563
9572
|
}
|
|
9564
9573
|
}
|
|
9565
9574
|
|
|
@@ -9612,16 +9621,17 @@ class ComposerFocusStore extends SpreadsheetStore {
|
|
|
9612
9621
|
focusComposer(listener, args) {
|
|
9613
9622
|
this.activeComposer = listener;
|
|
9614
9623
|
if (this.getters.isReadonly()) {
|
|
9615
|
-
return;
|
|
9624
|
+
return "noStateChange";
|
|
9616
9625
|
}
|
|
9617
9626
|
this._focusMode = args.focusMode || "contentFocus";
|
|
9618
9627
|
if (this._focusMode !== "inactive") {
|
|
9619
9628
|
this.setComposerContent(args);
|
|
9620
9629
|
}
|
|
9630
|
+
return;
|
|
9621
9631
|
}
|
|
9622
9632
|
focusActiveComposer(args) {
|
|
9623
9633
|
if (this.getters.isReadonly()) {
|
|
9624
|
-
return;
|
|
9634
|
+
return "noStateChange";
|
|
9625
9635
|
}
|
|
9626
9636
|
if (!this.activeComposer) {
|
|
9627
9637
|
throw new Error("No composer is registered");
|
|
@@ -9630,6 +9640,7 @@ class ComposerFocusStore extends SpreadsheetStore {
|
|
|
9630
9640
|
if (this._focusMode !== "inactive") {
|
|
9631
9641
|
this.setComposerContent(args);
|
|
9632
9642
|
}
|
|
9643
|
+
return;
|
|
9633
9644
|
}
|
|
9634
9645
|
/**
|
|
9635
9646
|
* Start the edition or update the content if it's already started.
|
|
@@ -9979,7 +9990,7 @@ function getDefinedAxis(definition) {
|
|
|
9979
9990
|
}
|
|
9980
9991
|
function formatChartDatasetValue(axisFormats, locale) {
|
|
9981
9992
|
return (value, axisId) => {
|
|
9982
|
-
const format =
|
|
9993
|
+
const format = axisFormats?.[axisId];
|
|
9983
9994
|
return formatTickValue({ format, locale })(value);
|
|
9984
9995
|
};
|
|
9985
9996
|
}
|
|
@@ -10143,7 +10154,7 @@ function drawPieChartValues(chart, options, ctx) {
|
|
|
10143
10154
|
const y = bar.y + midRadius * Math.sin(midAngle) + 7;
|
|
10144
10155
|
ctx.fillStyle = chartFontColor(options.background);
|
|
10145
10156
|
ctx.strokeStyle = options.background || "#ffffff";
|
|
10146
|
-
const displayValue = options.callback(value);
|
|
10157
|
+
const displayValue = options.callback(value, "y");
|
|
10147
10158
|
drawTextWithBackground(displayValue, x, y, ctx);
|
|
10148
10159
|
}
|
|
10149
10160
|
}
|
|
@@ -19033,6 +19044,9 @@ const PIVOT_VALUE = {
|
|
|
19033
19044
|
};
|
|
19034
19045
|
}
|
|
19035
19046
|
const domain = pivot.parseArgsToPivotDomain(domainArgs);
|
|
19047
|
+
if (this.getters.getActiveSheetId() === this.__originSheetId) {
|
|
19048
|
+
this.getters.getPivotPresenceTracker(pivotId)?.trackValue(_measure, domain);
|
|
19049
|
+
}
|
|
19036
19050
|
return pivot.getPivotCellValueAndFormat(_measure, domain);
|
|
19037
19051
|
},
|
|
19038
19052
|
};
|
|
@@ -19064,6 +19078,9 @@ const PIVOT_HEADER = {
|
|
|
19064
19078
|
};
|
|
19065
19079
|
}
|
|
19066
19080
|
const domain = pivot.parseArgsToPivotDomain(domainArgs);
|
|
19081
|
+
if (this.getters.getActiveSheetId() === this.__originSheetId) {
|
|
19082
|
+
this.getters.getPivotPresenceTracker(_pivotId)?.trackHeader(domain);
|
|
19083
|
+
}
|
|
19067
19084
|
const lastNode = domain.at(-1);
|
|
19068
19085
|
if (lastNode?.field === "measure") {
|
|
19069
19086
|
return pivot.getPivotMeasureValue(toString(lastNode.value), domain);
|
|
@@ -19286,6 +19303,9 @@ function isEmpty(data) {
|
|
|
19286
19303
|
return data === undefined || data.value === null;
|
|
19287
19304
|
}
|
|
19288
19305
|
const getNeutral = { number: 0, string: "", boolean: false };
|
|
19306
|
+
function areAlmostEqual(value1, value2, epsilon = 2e-16) {
|
|
19307
|
+
return Math.abs(value1 - value2) < epsilon;
|
|
19308
|
+
}
|
|
19289
19309
|
const EQ = {
|
|
19290
19310
|
description: _t("Equal."),
|
|
19291
19311
|
args: [
|
|
@@ -19307,6 +19327,9 @@ const EQ = {
|
|
|
19307
19327
|
if (typeof _value2 === "string") {
|
|
19308
19328
|
_value2 = _value2.toUpperCase();
|
|
19309
19329
|
}
|
|
19330
|
+
if (typeof _value1 === "number" && typeof _value2 === "number") {
|
|
19331
|
+
return { value: areAlmostEqual(_value1, _value2) };
|
|
19332
|
+
}
|
|
19310
19333
|
return { value: _value1 === _value2 };
|
|
19311
19334
|
},
|
|
19312
19335
|
};
|
|
@@ -19346,6 +19369,9 @@ const GT = {
|
|
|
19346
19369
|
],
|
|
19347
19370
|
compute: function (value1, value2) {
|
|
19348
19371
|
return applyRelationalOperator(value1, value2, (v1, v2) => {
|
|
19372
|
+
if (typeof v1 === "number" && typeof v2 === "number") {
|
|
19373
|
+
return !areAlmostEqual(v1, v2) && v1 > v2;
|
|
19374
|
+
}
|
|
19349
19375
|
return v1 > v2;
|
|
19350
19376
|
});
|
|
19351
19377
|
},
|
|
@@ -19361,6 +19387,9 @@ const GTE = {
|
|
|
19361
19387
|
],
|
|
19362
19388
|
compute: function (value1, value2) {
|
|
19363
19389
|
return applyRelationalOperator(value1, value2, (v1, v2) => {
|
|
19390
|
+
if (typeof v1 === "number" && typeof v2 === "number") {
|
|
19391
|
+
return areAlmostEqual(v1, v2) || v1 > v2;
|
|
19392
|
+
}
|
|
19364
19393
|
return v1 >= v2;
|
|
19365
19394
|
});
|
|
19366
19395
|
},
|
|
@@ -20967,7 +20996,7 @@ class AbstractComposerStore extends SpreadsheetStore {
|
|
|
20967
20996
|
.find((token) => {
|
|
20968
20997
|
const { xc, sheetName: sheet } = splitReference(token.value);
|
|
20969
20998
|
const sheetName = sheet || this.getters.getSheetName(this.sheetId);
|
|
20970
|
-
if (this.getters.getSheetName(activeSheetId)
|
|
20999
|
+
if (!isSheetNameEqual(this.getters.getSheetName(activeSheetId), sheetName)) {
|
|
20971
21000
|
return false;
|
|
20972
21001
|
}
|
|
20973
21002
|
const refRange = this.getters.getRangeFromSheetXC(activeSheetId, xc);
|
|
@@ -24578,7 +24607,7 @@ function getRangeSize(reference, defaultSheetIndex, data) {
|
|
|
24578
24607
|
({ xc, sheetName } = splitReference(reference));
|
|
24579
24608
|
let rangeSheetIndex;
|
|
24580
24609
|
if (sheetName) {
|
|
24581
|
-
const index = data.sheets.findIndex((sheet) => sheet.name
|
|
24610
|
+
const index = data.sheets.findIndex((sheet) => isSheetNameEqual(sheet.name, sheetName));
|
|
24582
24611
|
if (index < 0) {
|
|
24583
24612
|
throw new Error("Unable to find a sheet with the name " + sheetName);
|
|
24584
24613
|
}
|
|
@@ -24919,7 +24948,7 @@ function convertFormula(formula, data) {
|
|
|
24919
24948
|
formula = formula.replace(externalReferenceRegex, (match, externalRefId, sheetName, cellRef) => {
|
|
24920
24949
|
externalRefId = Number(externalRefId) - 1;
|
|
24921
24950
|
cellRef = cellRef.replace(/\$/g, "");
|
|
24922
|
-
const sheetIndex = data.externalBooks[externalRefId].sheetNames.findIndex((name) => name
|
|
24951
|
+
const sheetIndex = data.externalBooks[externalRefId].sheetNames.findIndex((name) => isSheetNameEqual(name, sheetName));
|
|
24923
24952
|
if (sheetIndex === -1) {
|
|
24924
24953
|
return match;
|
|
24925
24954
|
}
|
|
@@ -25570,7 +25599,7 @@ function convertPivotTableConfig(pivotTable) {
|
|
|
25570
25599
|
*/
|
|
25571
25600
|
function convertTableFormulaReferences(convertedSheets, xlsxSheets) {
|
|
25572
25601
|
for (let tableSheet of convertedSheets) {
|
|
25573
|
-
const tables = xlsxSheets.find((s) => s.sheetName
|
|
25602
|
+
const tables = xlsxSheets.find((s) => isSheetNameEqual(s.sheetName, tableSheet.name)).tables;
|
|
25574
25603
|
for (let table of tables) {
|
|
25575
25604
|
const tabRef = table.name + "[";
|
|
25576
25605
|
for (let sheet of convertedSheets) {
|
|
@@ -32405,12 +32434,20 @@ class HoveredCellStore extends SpreadsheetStore {
|
|
|
32405
32434
|
}
|
|
32406
32435
|
}
|
|
32407
32436
|
hover(position) {
|
|
32437
|
+
if (position.col === this.col && position.row === this.row) {
|
|
32438
|
+
return "noStateChange";
|
|
32439
|
+
}
|
|
32408
32440
|
this.col = position.col;
|
|
32409
32441
|
this.row = position.row;
|
|
32442
|
+
return;
|
|
32410
32443
|
}
|
|
32411
32444
|
clear() {
|
|
32445
|
+
if (this.col === undefined && this.row === undefined) {
|
|
32446
|
+
return "noStateChange";
|
|
32447
|
+
}
|
|
32412
32448
|
this.col = undefined;
|
|
32413
32449
|
this.row = undefined;
|
|
32450
|
+
return;
|
|
32414
32451
|
}
|
|
32415
32452
|
}
|
|
32416
32453
|
|
|
@@ -32432,7 +32469,11 @@ class CellPopoverStore extends SpreadsheetStore {
|
|
|
32432
32469
|
this.persistentPopover = { col, row, sheetId, type };
|
|
32433
32470
|
}
|
|
32434
32471
|
close() {
|
|
32472
|
+
if (!this.persistentPopover) {
|
|
32473
|
+
return "noStateChange";
|
|
32474
|
+
}
|
|
32435
32475
|
this.persistentPopover = undefined;
|
|
32476
|
+
return;
|
|
32436
32477
|
}
|
|
32437
32478
|
get persistentCellPopover() {
|
|
32438
32479
|
return ((this.persistentPopover && { isOpen: true, ...this.persistentPopover }) || { isOpen: false });
|
|
@@ -40264,10 +40305,18 @@ class ChartPanel extends owl.Component {
|
|
|
40264
40305
|
}
|
|
40265
40306
|
|
|
40266
40307
|
class DOMFocusableElementStore {
|
|
40267
|
-
mutators = ["setFocusableElement"];
|
|
40308
|
+
mutators = ["setFocusableElement", "focus"];
|
|
40268
40309
|
focusableElement = undefined;
|
|
40269
40310
|
setFocusableElement(element) {
|
|
40270
40311
|
this.focusableElement = element;
|
|
40312
|
+
return "noStateChange";
|
|
40313
|
+
}
|
|
40314
|
+
focus() {
|
|
40315
|
+
if (this.focusableElement === document.activeElement) {
|
|
40316
|
+
return "noStateChange";
|
|
40317
|
+
}
|
|
40318
|
+
this.focusableElement?.focus();
|
|
40319
|
+
return;
|
|
40271
40320
|
}
|
|
40272
40321
|
}
|
|
40273
40322
|
|
|
@@ -40857,7 +40906,7 @@ class Composer extends owl.Component {
|
|
|
40857
40906
|
if (document.activeElement === this.contentHelper.el &&
|
|
40858
40907
|
this.props.composerStore.editionMode === "inactive" &&
|
|
40859
40908
|
!this.props.isDefaultFocus) {
|
|
40860
|
-
this.DOMFocusableElementStore.
|
|
40909
|
+
this.DOMFocusableElementStore.focus();
|
|
40861
40910
|
}
|
|
40862
40911
|
});
|
|
40863
40912
|
owl.useEffect(() => {
|
|
@@ -46604,7 +46653,12 @@ class SpreadsheetPivot {
|
|
|
46604
46653
|
entry[field.name] = { value: null, type: CellValueType.empty, formattedValue: "" };
|
|
46605
46654
|
}
|
|
46606
46655
|
else {
|
|
46607
|
-
|
|
46656
|
+
if (field.type === "char") {
|
|
46657
|
+
entry[field.name] = { ...cell, value: cell.formattedValue || null };
|
|
46658
|
+
}
|
|
46659
|
+
else {
|
|
46660
|
+
entry[field.name] = cell;
|
|
46661
|
+
}
|
|
46608
46662
|
}
|
|
46609
46663
|
}
|
|
46610
46664
|
entry["__count"] = { value: 1, type: CellValueType.number, formattedValue: "1" };
|
|
@@ -51633,10 +51687,6 @@ function useGridDrawing(refName, model, canvasSize) {
|
|
|
51633
51687
|
ctx.scale(dpr, dpr);
|
|
51634
51688
|
for (const layer of OrderedLayers()) {
|
|
51635
51689
|
model.drawLayer(renderingContext, layer);
|
|
51636
|
-
// @ts-ignore 'drawLayer' is not declated as a mutator because:
|
|
51637
|
-
// it does not mutate anything. Most importantly it's used
|
|
51638
|
-
// during rendering. Invoking a mutator during rendering would
|
|
51639
|
-
// trigger another rendering, ultimately resulting in an infinite loop.
|
|
51640
51690
|
rendererStore.drawLayer(renderingContext, layer);
|
|
51641
51691
|
}
|
|
51642
51692
|
}
|
|
@@ -52326,7 +52376,7 @@ class Grid extends owl.Component {
|
|
|
52326
52376
|
this.cellPopovers = useStore(CellPopoverStore);
|
|
52327
52377
|
owl.useEffect(() => {
|
|
52328
52378
|
if (!this.sidePanel.isOpen) {
|
|
52329
|
-
this.DOMFocusableElementStore.
|
|
52379
|
+
this.DOMFocusableElementStore.focus();
|
|
52330
52380
|
}
|
|
52331
52381
|
}, () => [this.sidePanel.isOpen]);
|
|
52332
52382
|
useTouchScroll(this.gridRef, this.moveCanvas.bind(this), () => {
|
|
@@ -52536,7 +52586,7 @@ class Grid extends owl.Component {
|
|
|
52536
52586
|
focusDefaultElement() {
|
|
52537
52587
|
if (!this.env.model.getters.getSelectedFigureId() &&
|
|
52538
52588
|
this.composerFocusStore.activeComposer.editionMode === "inactive") {
|
|
52539
|
-
this.DOMFocusableElementStore.
|
|
52589
|
+
this.DOMFocusableElementStore.focus();
|
|
52540
52590
|
}
|
|
52541
52591
|
}
|
|
52542
52592
|
get gridEl() {
|
|
@@ -52881,6 +52931,322 @@ class Grid extends owl.Component {
|
|
|
52881
52931
|
}
|
|
52882
52932
|
}
|
|
52883
52933
|
|
|
52934
|
+
css /* scss */ `
|
|
52935
|
+
.o_pivot_html_renderer {
|
|
52936
|
+
width: 100%;
|
|
52937
|
+
border-collapse: collapse;
|
|
52938
|
+
|
|
52939
|
+
&:hover {
|
|
52940
|
+
cursor: pointer;
|
|
52941
|
+
}
|
|
52942
|
+
|
|
52943
|
+
td,
|
|
52944
|
+
th {
|
|
52945
|
+
border: 1px solid #dee2e6;
|
|
52946
|
+
background-color: #fff;
|
|
52947
|
+
padding: 0.3rem;
|
|
52948
|
+
white-space: nowrap;
|
|
52949
|
+
|
|
52950
|
+
&:hover {
|
|
52951
|
+
filter: brightness(0.9);
|
|
52952
|
+
}
|
|
52953
|
+
}
|
|
52954
|
+
|
|
52955
|
+
td {
|
|
52956
|
+
text-align: right;
|
|
52957
|
+
}
|
|
52958
|
+
|
|
52959
|
+
th {
|
|
52960
|
+
background-color: #f5f5f5;
|
|
52961
|
+
font-weight: bold;
|
|
52962
|
+
color: black;
|
|
52963
|
+
}
|
|
52964
|
+
|
|
52965
|
+
.o_missing_value {
|
|
52966
|
+
color: #46646d;
|
|
52967
|
+
background: #e7f2f6;
|
|
52968
|
+
}
|
|
52969
|
+
}
|
|
52970
|
+
`;
|
|
52971
|
+
class PivotHTMLRenderer extends owl.Component {
|
|
52972
|
+
static template = "o_spreadsheet.PivotHTMLRenderer";
|
|
52973
|
+
static components = { Checkbox };
|
|
52974
|
+
static props = {
|
|
52975
|
+
pivotId: String,
|
|
52976
|
+
onCellClicked: Function,
|
|
52977
|
+
};
|
|
52978
|
+
pivot = this.env.model.getters.getPivot(this.props.pivotId);
|
|
52979
|
+
data = {
|
|
52980
|
+
columns: [],
|
|
52981
|
+
rows: [],
|
|
52982
|
+
values: [],
|
|
52983
|
+
};
|
|
52984
|
+
state = owl.useState({
|
|
52985
|
+
showMissingValuesOnly: false,
|
|
52986
|
+
});
|
|
52987
|
+
setup() {
|
|
52988
|
+
const table = this.pivot.getTableStructure();
|
|
52989
|
+
const formulaId = this.env.model.getters.getPivotFormulaId(this.props.pivotId);
|
|
52990
|
+
this.data = {
|
|
52991
|
+
columns: this._buildColHeaders(formulaId, table),
|
|
52992
|
+
rows: this._buildRowHeaders(formulaId, table),
|
|
52993
|
+
values: this._buildValues(formulaId, table),
|
|
52994
|
+
};
|
|
52995
|
+
}
|
|
52996
|
+
get tracker() {
|
|
52997
|
+
return this.env.model.getters.getPivotPresenceTracker(this.props.pivotId);
|
|
52998
|
+
}
|
|
52999
|
+
// ---------------------------------------------------------------------
|
|
53000
|
+
// Missing values building
|
|
53001
|
+
// ---------------------------------------------------------------------
|
|
53002
|
+
/**
|
|
53003
|
+
* Retrieve the data to display in the Pivot Table
|
|
53004
|
+
* In the case when showMissingValuesOnly is false, the returned value
|
|
53005
|
+
* is the complete data
|
|
53006
|
+
* In the case when showMissingValuesOnly is true, the returned value is
|
|
53007
|
+
* the data which contains only missing values in the rows and cols. In
|
|
53008
|
+
* the rows, we also return the parent rows of rows which contains missing
|
|
53009
|
+
* values, to give context to the user.
|
|
53010
|
+
*
|
|
53011
|
+
*/
|
|
53012
|
+
getTableData() {
|
|
53013
|
+
if (!this.state.showMissingValuesOnly) {
|
|
53014
|
+
return this.data;
|
|
53015
|
+
}
|
|
53016
|
+
const colIndexes = this.getColumnsIndexes();
|
|
53017
|
+
const rowIndexes = this.getRowsIndexes();
|
|
53018
|
+
const columns = this.buildColumnsMissing(colIndexes);
|
|
53019
|
+
const rows = this.buildRowsMissing(rowIndexes);
|
|
53020
|
+
const values = this.buildValuesMissing(colIndexes, rowIndexes);
|
|
53021
|
+
return { columns, rows, values };
|
|
53022
|
+
}
|
|
53023
|
+
/**
|
|
53024
|
+
* Retrieve the parents of the given row
|
|
53025
|
+
* ex:
|
|
53026
|
+
* Australia
|
|
53027
|
+
* January
|
|
53028
|
+
* February
|
|
53029
|
+
* The parent of "January" is "Australia"
|
|
53030
|
+
*/
|
|
53031
|
+
addRecursiveRow(index) {
|
|
53032
|
+
const rows = this.pivot.getTableStructure().rows;
|
|
53033
|
+
const row = [...rows[index].values];
|
|
53034
|
+
if (row.length <= 1) {
|
|
53035
|
+
return [index];
|
|
53036
|
+
}
|
|
53037
|
+
row.pop();
|
|
53038
|
+
const parentRowIndex = rows.findIndex((r) => JSON.stringify(r.values) === JSON.stringify(row));
|
|
53039
|
+
return [index].concat(this.addRecursiveRow(parentRowIndex));
|
|
53040
|
+
}
|
|
53041
|
+
/**
|
|
53042
|
+
* Create the columns to be used, based on the indexes of the columns in
|
|
53043
|
+
* which a missing value is present
|
|
53044
|
+
*
|
|
53045
|
+
*/
|
|
53046
|
+
buildColumnsMissing(indexes) {
|
|
53047
|
+
// columnsMap explode the columns in an array of array of the same
|
|
53048
|
+
// size with the index of each column, repeated 'span' times.
|
|
53049
|
+
// ex:
|
|
53050
|
+
// | A | B |
|
|
53051
|
+
// | 1 | 2 | 3 |
|
|
53052
|
+
// => [
|
|
53053
|
+
// [0, 0, 1]
|
|
53054
|
+
// [0, 1, 2]
|
|
53055
|
+
// ]
|
|
53056
|
+
const columnsMap = [];
|
|
53057
|
+
for (const column of this.data.columns) {
|
|
53058
|
+
const columnMap = [];
|
|
53059
|
+
for (const index in column) {
|
|
53060
|
+
for (let i = 0; i < column[index].span; i++) {
|
|
53061
|
+
columnMap.push(parseInt(index, 10));
|
|
53062
|
+
}
|
|
53063
|
+
}
|
|
53064
|
+
columnsMap.push(columnMap);
|
|
53065
|
+
}
|
|
53066
|
+
// Remove the columns that are not present in indexes
|
|
53067
|
+
for (let i = columnsMap[columnsMap.length - 1].length; i >= 0; i--) {
|
|
53068
|
+
if (!indexes.includes(i)) {
|
|
53069
|
+
for (const columnMap of columnsMap) {
|
|
53070
|
+
columnMap.splice(i, 1);
|
|
53071
|
+
}
|
|
53072
|
+
}
|
|
53073
|
+
}
|
|
53074
|
+
// Build the columns
|
|
53075
|
+
const columns = [];
|
|
53076
|
+
for (const mapIndex in columnsMap) {
|
|
53077
|
+
const column = [];
|
|
53078
|
+
let index = undefined;
|
|
53079
|
+
let span = 1;
|
|
53080
|
+
for (let i = 0; i < columnsMap[mapIndex].length; i++) {
|
|
53081
|
+
if (index !== columnsMap[mapIndex][i]) {
|
|
53082
|
+
if (index !== undefined) {
|
|
53083
|
+
column.push(Object.assign({}, this.data.columns[mapIndex][index], { span }));
|
|
53084
|
+
}
|
|
53085
|
+
index = columnsMap[mapIndex][i];
|
|
53086
|
+
span = 1;
|
|
53087
|
+
}
|
|
53088
|
+
else {
|
|
53089
|
+
span++;
|
|
53090
|
+
}
|
|
53091
|
+
}
|
|
53092
|
+
if (index !== undefined) {
|
|
53093
|
+
column.push(Object.assign({}, this.data.columns[mapIndex][index], { span }));
|
|
53094
|
+
}
|
|
53095
|
+
columns.push(column);
|
|
53096
|
+
}
|
|
53097
|
+
return columns;
|
|
53098
|
+
}
|
|
53099
|
+
/**
|
|
53100
|
+
* Create the rows to be used, based on the indexes of the rows in
|
|
53101
|
+
* which a missing value is present.
|
|
53102
|
+
*/
|
|
53103
|
+
buildRowsMissing(indexes) {
|
|
53104
|
+
return indexes.map((index) => this.data.rows[index]);
|
|
53105
|
+
}
|
|
53106
|
+
/**
|
|
53107
|
+
* Create the value to be used, based on the indexes of the columns and
|
|
53108
|
+
* rows in which a missing value is present.
|
|
53109
|
+
*/
|
|
53110
|
+
buildValuesMissing(colIndexes, rowIndexes) {
|
|
53111
|
+
const values = colIndexes.map(() => []);
|
|
53112
|
+
for (const row of rowIndexes) {
|
|
53113
|
+
for (const col in colIndexes) {
|
|
53114
|
+
values[col].push(this.data.values[colIndexes[col]][row]);
|
|
53115
|
+
}
|
|
53116
|
+
}
|
|
53117
|
+
return values;
|
|
53118
|
+
}
|
|
53119
|
+
getColumnsIndexes() {
|
|
53120
|
+
const indexes = new Set();
|
|
53121
|
+
for (let i = 0; i < this.data.columns.length; i++) {
|
|
53122
|
+
const exploded = [];
|
|
53123
|
+
for (let y = 0; y < this.data.columns[i].length; y++) {
|
|
53124
|
+
for (let x = 0; x < this.data.columns[i][y].span; x++) {
|
|
53125
|
+
exploded.push(this.data.columns[i][y]);
|
|
53126
|
+
}
|
|
53127
|
+
}
|
|
53128
|
+
for (let y = 0; y < exploded.length; y++) {
|
|
53129
|
+
if (exploded[y].isMissing) {
|
|
53130
|
+
indexes.add(y);
|
|
53131
|
+
}
|
|
53132
|
+
}
|
|
53133
|
+
}
|
|
53134
|
+
for (let i = 0; i < this.data.columns[this.data.columns.length - 1].length; i++) {
|
|
53135
|
+
const values = this.data.values[i];
|
|
53136
|
+
if (values.find((x) => x.isMissing)) {
|
|
53137
|
+
indexes.add(i);
|
|
53138
|
+
}
|
|
53139
|
+
}
|
|
53140
|
+
return Array.from(indexes).sort((a, b) => a - b);
|
|
53141
|
+
}
|
|
53142
|
+
getRowsIndexes() {
|
|
53143
|
+
const rowIndexes = new Set();
|
|
53144
|
+
for (let i = 0; i < this.data.rows.length; i++) {
|
|
53145
|
+
if (this.data.rows[i].isMissing) {
|
|
53146
|
+
rowIndexes.add(i);
|
|
53147
|
+
}
|
|
53148
|
+
for (const col of this.data.values) {
|
|
53149
|
+
if (col[i].isMissing) {
|
|
53150
|
+
this.addRecursiveRow(i).forEach((x) => rowIndexes.add(x));
|
|
53151
|
+
}
|
|
53152
|
+
}
|
|
53153
|
+
}
|
|
53154
|
+
return Array.from(rowIndexes).sort((a, b) => a - b);
|
|
53155
|
+
}
|
|
53156
|
+
// ---------------------------------------------------------------------
|
|
53157
|
+
// Data table creation
|
|
53158
|
+
// ---------------------------------------------------------------------
|
|
53159
|
+
_buildColHeaders(id, table) {
|
|
53160
|
+
const headers = [];
|
|
53161
|
+
for (const row of table.columns) {
|
|
53162
|
+
const current = [];
|
|
53163
|
+
for (const cell of row) {
|
|
53164
|
+
const args = [];
|
|
53165
|
+
for (let i = 0; i < cell.fields.length; i++) {
|
|
53166
|
+
args.push({ value: cell.fields[i] }, { value: cell.values[i] });
|
|
53167
|
+
}
|
|
53168
|
+
const domain = this.pivot.parseArgsToPivotDomain(args);
|
|
53169
|
+
const locale = this.env.model.getters.getLocale();
|
|
53170
|
+
if (domain.at(-1)?.field === "measure") {
|
|
53171
|
+
const { value, format } = this.pivot.getPivotMeasureValue(toString(domain.at(-1).value), domain);
|
|
53172
|
+
current.push({
|
|
53173
|
+
formula: `=PIVOT.HEADER(${generatePivotArgs(id, domain).join(",")})`,
|
|
53174
|
+
value: formatValue(value, { format, locale }),
|
|
53175
|
+
span: cell.width,
|
|
53176
|
+
isMissing: !this.tracker?.isHeaderPresent(domain),
|
|
53177
|
+
});
|
|
53178
|
+
}
|
|
53179
|
+
else {
|
|
53180
|
+
const { value, format } = this.pivot.getPivotHeaderValueAndFormat(domain);
|
|
53181
|
+
current.push({
|
|
53182
|
+
formula: `=PIVOT.HEADER(${generatePivotArgs(id, domain).join(",")})`,
|
|
53183
|
+
value: formatValue(value, { format, locale }),
|
|
53184
|
+
span: cell.width,
|
|
53185
|
+
isMissing: !this.tracker?.isHeaderPresent(domain),
|
|
53186
|
+
});
|
|
53187
|
+
}
|
|
53188
|
+
}
|
|
53189
|
+
headers.push(current);
|
|
53190
|
+
}
|
|
53191
|
+
const last = headers[headers.length - 1];
|
|
53192
|
+
headers[headers.length - 1] = last.map((cell) => {
|
|
53193
|
+
if (!cell.isMissing) {
|
|
53194
|
+
cell.style = "color: #756f6f;";
|
|
53195
|
+
}
|
|
53196
|
+
return cell;
|
|
53197
|
+
});
|
|
53198
|
+
return headers;
|
|
53199
|
+
}
|
|
53200
|
+
_buildRowHeaders(id, table) {
|
|
53201
|
+
const headers = [];
|
|
53202
|
+
for (const row of table.rows) {
|
|
53203
|
+
const args = [];
|
|
53204
|
+
for (let i = 0; i < row.fields.length; i++) {
|
|
53205
|
+
args.push({ value: row.fields[i] }, { value: row.values[i] });
|
|
53206
|
+
}
|
|
53207
|
+
const domain = this.pivot.parseArgsToPivotDomain(args);
|
|
53208
|
+
const { value, format } = this.pivot.getPivotHeaderValueAndFormat(domain);
|
|
53209
|
+
const locale = this.env.model.getters.getLocale();
|
|
53210
|
+
const cell = {
|
|
53211
|
+
formula: `=PIVOT.HEADER(${generatePivotArgs(id, domain).join(",")})`,
|
|
53212
|
+
value: formatValue(value, { format, locale }),
|
|
53213
|
+
isMissing: !this.tracker?.isHeaderPresent(domain),
|
|
53214
|
+
};
|
|
53215
|
+
if (row.indent > 1) {
|
|
53216
|
+
cell.style = `padding-left: ${row.indent - 1 * 10}px`;
|
|
53217
|
+
}
|
|
53218
|
+
headers.push(cell);
|
|
53219
|
+
}
|
|
53220
|
+
return headers;
|
|
53221
|
+
}
|
|
53222
|
+
_buildValues(id, table) {
|
|
53223
|
+
const values = [];
|
|
53224
|
+
for (const col of table.columns.at(-1) || []) {
|
|
53225
|
+
const current = [];
|
|
53226
|
+
const measure = toString(col.values[col.values.length - 1]);
|
|
53227
|
+
for (const row of table.rows) {
|
|
53228
|
+
const args = [];
|
|
53229
|
+
for (let i = 0; i < row.fields.length; i++) {
|
|
53230
|
+
args.push({ value: row.fields[i] }, { value: row.values[i] });
|
|
53231
|
+
}
|
|
53232
|
+
for (let i = 0; i < col.fields.length - 1; i++) {
|
|
53233
|
+
args.push({ value: col.fields[i] }, { value: col.values[i] });
|
|
53234
|
+
}
|
|
53235
|
+
const domain = this.pivot.parseArgsToPivotDomain(args);
|
|
53236
|
+
const { value, format } = this.pivot.getPivotCellValueAndFormat(measure, domain);
|
|
53237
|
+
const locale = this.env.model.getters.getLocale();
|
|
53238
|
+
current.push({
|
|
53239
|
+
formula: `=PIVOT.VALUE(${generatePivotArgs(id, domain, measure).join(",")})`,
|
|
53240
|
+
value: formatValue(value, { format, locale }),
|
|
53241
|
+
isMissing: !this.tracker?.isValuePresent(measure, domain),
|
|
53242
|
+
});
|
|
53243
|
+
}
|
|
53244
|
+
values.push(current);
|
|
53245
|
+
}
|
|
53246
|
+
return values;
|
|
53247
|
+
}
|
|
53248
|
+
}
|
|
53249
|
+
|
|
52884
53250
|
/**
|
|
52885
53251
|
* BasePlugin
|
|
52886
53252
|
*
|
|
@@ -56318,7 +56684,7 @@ class RangeAdapter {
|
|
|
56318
56684
|
if (range.sheetId === cmd.sheetId) {
|
|
56319
56685
|
return { changeType: "CHANGE", range };
|
|
56320
56686
|
}
|
|
56321
|
-
if (
|
|
56687
|
+
if (isSheetNameEqual(range.invalidSheetName, cmd.name)) {
|
|
56322
56688
|
const invalidSheetName = undefined;
|
|
56323
56689
|
const sheetId = cmd.sheetId;
|
|
56324
56690
|
const newRange = range.clone({ sheetId, invalidSheetName });
|
|
@@ -56903,7 +57269,7 @@ class SheetPlugin extends CorePlugin {
|
|
|
56903
57269
|
if (name) {
|
|
56904
57270
|
const unquotedName = getUnquotedSheetName(name);
|
|
56905
57271
|
for (const key in this.sheetIdsMapName) {
|
|
56906
|
-
if (key
|
|
57272
|
+
if (isSheetNameEqual(key, unquotedName)) {
|
|
56907
57273
|
return this.sheetIdsMapName[key];
|
|
56908
57274
|
}
|
|
56909
57275
|
}
|
|
@@ -57151,7 +57517,7 @@ class SheetPlugin extends CorePlugin {
|
|
|
57151
57517
|
}
|
|
57152
57518
|
const { orderedSheetIds, sheets } = this;
|
|
57153
57519
|
const name = cmd.name && cmd.name.trim().toLowerCase();
|
|
57154
|
-
if (orderedSheetIds.find((id) => sheets[id]?.name
|
|
57520
|
+
if (orderedSheetIds.find((id) => isSheetNameEqual(sheets[id]?.name, name) && id !== cmd.sheetId)) {
|
|
57155
57521
|
return "DuplicatedSheetName" /* CommandResult.DuplicatedSheetName */;
|
|
57156
57522
|
}
|
|
57157
57523
|
if (FORBIDDEN_SHEETNAME_CHARS_IN_EXCEL_REGEX.test(name)) {
|
|
@@ -66054,6 +66420,55 @@ class HistoryPlugin extends UIPlugin {
|
|
|
66054
66420
|
}
|
|
66055
66421
|
}
|
|
66056
66422
|
|
|
66423
|
+
class PivotPresenceTracker {
|
|
66424
|
+
trackedValues = new Set();
|
|
66425
|
+
domainToArray(domain) {
|
|
66426
|
+
return domain.flatMap((node) => [node.field, toString(node.value)]);
|
|
66427
|
+
}
|
|
66428
|
+
isValuePresent(measure, domain) {
|
|
66429
|
+
const key = JSON.stringify({ measure, domain: this.domainToArray(domain) });
|
|
66430
|
+
return this.trackedValues.has(key);
|
|
66431
|
+
}
|
|
66432
|
+
isHeaderPresent(domain) {
|
|
66433
|
+
const key = JSON.stringify({ domain: this.domainToArray(domain) });
|
|
66434
|
+
return this.trackedValues.has(key);
|
|
66435
|
+
}
|
|
66436
|
+
trackValue(measure, domain) {
|
|
66437
|
+
const key = JSON.stringify({ measure, domain: this.domainToArray(domain) });
|
|
66438
|
+
this.trackedValues.add(key);
|
|
66439
|
+
}
|
|
66440
|
+
trackHeader(domain) {
|
|
66441
|
+
const key = JSON.stringify({ domain: this.domainToArray(domain) });
|
|
66442
|
+
this.trackedValues.add(key);
|
|
66443
|
+
}
|
|
66444
|
+
}
|
|
66445
|
+
|
|
66446
|
+
class PivotPresencePlugin extends UIPlugin {
|
|
66447
|
+
static getters = ["getPivotPresenceTracker"];
|
|
66448
|
+
trackPresencePivotId;
|
|
66449
|
+
tracker;
|
|
66450
|
+
handle(cmd) {
|
|
66451
|
+
switch (cmd.type) {
|
|
66452
|
+
case "PIVOT_START_PRESENCE_TRACKING":
|
|
66453
|
+
this.tracker = new PivotPresenceTracker();
|
|
66454
|
+
this.trackPresencePivotId = cmd.pivotId;
|
|
66455
|
+
break;
|
|
66456
|
+
case "PIVOT_STOP_PRESENCE_TRACKING":
|
|
66457
|
+
this.trackPresencePivotId = undefined;
|
|
66458
|
+
break;
|
|
66459
|
+
}
|
|
66460
|
+
}
|
|
66461
|
+
getPivotPresenceTracker(pivotId) {
|
|
66462
|
+
if (this.trackPresencePivotId !== pivotId) {
|
|
66463
|
+
return undefined;
|
|
66464
|
+
}
|
|
66465
|
+
if (!this.tracker) {
|
|
66466
|
+
throw new Error("Tracker not initialized");
|
|
66467
|
+
}
|
|
66468
|
+
return this.tracker;
|
|
66469
|
+
}
|
|
66470
|
+
}
|
|
66471
|
+
|
|
66057
66472
|
class SplitToColumnsPlugin extends UIPlugin {
|
|
66058
66473
|
static getters = ["getAutomaticSeparator"];
|
|
66059
66474
|
allowDispatch(cmd) {
|
|
@@ -68841,6 +69256,7 @@ const featurePluginRegistry = new Registry()
|
|
|
68841
69256
|
.add("automatic_sum", AutomaticSumPlugin)
|
|
68842
69257
|
.add("format", FormatPlugin)
|
|
68843
69258
|
.add("insert_pivot", InsertPivotPlugin)
|
|
69259
|
+
.add("pivot_presence", PivotPresencePlugin)
|
|
68844
69260
|
.add("split_to_columns", SplitToColumnsPlugin)
|
|
68845
69261
|
.add("collaborative", CollaborativePlugin)
|
|
68846
69262
|
.add("history", HistoryPlugin)
|
|
@@ -69221,11 +69637,11 @@ class BottomBarSheet extends owl.Component {
|
|
|
69221
69637
|
if (ev.key === "Enter") {
|
|
69222
69638
|
ev.preventDefault();
|
|
69223
69639
|
this.stopEdition();
|
|
69224
|
-
this.DOMFocusableElementStore.
|
|
69640
|
+
this.DOMFocusableElementStore.focus();
|
|
69225
69641
|
}
|
|
69226
69642
|
if (ev.key === "Escape") {
|
|
69227
69643
|
this.cancelEdition();
|
|
69228
|
-
this.DOMFocusableElementStore.
|
|
69644
|
+
this.DOMFocusableElementStore.focus();
|
|
69229
69645
|
}
|
|
69230
69646
|
}
|
|
69231
69647
|
onMouseEventSheetName(ev) {
|
|
@@ -75816,6 +76232,7 @@ const components = {
|
|
|
75816
76232
|
PivotDimensionOrder,
|
|
75817
76233
|
PivotDimension,
|
|
75818
76234
|
PivotLayoutConfigurator,
|
|
76235
|
+
PivotHTMLRenderer,
|
|
75819
76236
|
PivotDeferUpdate,
|
|
75820
76237
|
PivotTitleSection,
|
|
75821
76238
|
CogWheelMenu,
|
|
@@ -75909,6 +76326,6 @@ exports.tokenColors = tokenColors;
|
|
|
75909
76326
|
exports.tokenize = tokenize;
|
|
75910
76327
|
|
|
75911
76328
|
|
|
75912
|
-
__info__.version = "18.1.
|
|
75913
|
-
__info__.date = "2025-05-
|
|
75914
|
-
__info__.hash = "
|
|
76329
|
+
__info__.version = "18.1.20";
|
|
76330
|
+
__info__.date = "2025-05-13T17:52:28.174Z";
|
|
76331
|
+
__info__.hash = "3e43a46";
|