@odoo/o-spreadsheet 18.2.11 → 18.2.12
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 +280 -155
- package/dist/o-spreadsheet.esm.js +458 -41
- package/dist/o-spreadsheet.iife.js +458 -41
- package/dist/o-spreadsheet.iife.min.js +422 -386
- 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.2.
|
|
6
|
-
* @date 2025-05-
|
|
7
|
-
* @hash
|
|
5
|
+
* @version 18.2.12
|
|
6
|
+
* @date 2025-05-13T17:52:23.989Z
|
|
7
|
+
* @hash ba2ba9b
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
'use strict';
|
|
@@ -3359,7 +3359,7 @@ function isDateAfter(date, dateAfter) {
|
|
|
3359
3359
|
*/
|
|
3360
3360
|
const getFormulaNumberRegex = memoize(function getFormulaNumberRegex(decimalSeparator) {
|
|
3361
3361
|
decimalSeparator = escapeRegExp(decimalSeparator);
|
|
3362
|
-
return new RegExp(`(?:^-?\\d+(?:${decimalSeparator}?\\d*(?:e
|
|
3362
|
+
return new RegExp(`(?:^-?\\d+(?:${decimalSeparator}?\\d*(?:e(\\+|-)?\\d+)?)?|^-?${decimalSeparator}\\d+)(?!\\w|!)`);
|
|
3363
3363
|
});
|
|
3364
3364
|
const getNumberRegex = memoize(function getNumberRegex(locale) {
|
|
3365
3365
|
const decimalSeparator = escapeRegExp(locale.decimalSeparator);
|
|
@@ -6301,6 +6301,13 @@ function getDuplicateSheetName(nameToDuplicate, existingNames) {
|
|
|
6301
6301
|
}
|
|
6302
6302
|
return name;
|
|
6303
6303
|
}
|
|
6304
|
+
function isSheetNameEqual(name1, name2) {
|
|
6305
|
+
if (name1 === undefined || name2 === undefined) {
|
|
6306
|
+
return false;
|
|
6307
|
+
}
|
|
6308
|
+
return (getUnquotedSheetName(name1.trim().toUpperCase()) ===
|
|
6309
|
+
getUnquotedSheetName(name2.trim().toUpperCase()));
|
|
6310
|
+
}
|
|
6304
6311
|
|
|
6305
6312
|
function computeTextLinesHeight(textLineHeight, numberOfLines = 1) {
|
|
6306
6313
|
return numberOfLines * (textLineHeight + MIN_CELL_TEXT_MARGIN) - MIN_CELL_TEXT_MARGIN;
|
|
@@ -8164,10 +8171,9 @@ const AGGREGATOR_NAMES = {
|
|
|
8164
8171
|
avg: _t("Average"),
|
|
8165
8172
|
sum: _t("Sum"),
|
|
8166
8173
|
};
|
|
8167
|
-
const NUMBER_CHAR_AGGREGATORS = ["max", "min", "avg", "sum", "count_distinct", "count"];
|
|
8168
8174
|
const AGGREGATORS_BY_FIELD_TYPE = {
|
|
8169
|
-
integer:
|
|
8170
|
-
char:
|
|
8175
|
+
integer: ["max", "min", "avg", "sum", "count_distinct", "count"],
|
|
8176
|
+
char: ["count_distinct", "count"],
|
|
8171
8177
|
boolean: ["count_distinct", "count", "bool_and", "bool_or"],
|
|
8172
8178
|
datetime: ["max", "min", "count_distinct", "count"],
|
|
8173
8179
|
};
|
|
@@ -9520,7 +9526,10 @@ function proxifyStoreMutation(store, callback) {
|
|
|
9520
9526
|
const functionProxy = new Proxy(value, {
|
|
9521
9527
|
// trap the function call
|
|
9522
9528
|
apply(target, thisArg, argArray) {
|
|
9523
|
-
Reflect.apply(target, thisStore, argArray);
|
|
9529
|
+
const res = Reflect.apply(target, thisStore, argArray);
|
|
9530
|
+
if (res === "noStateChange") {
|
|
9531
|
+
return;
|
|
9532
|
+
}
|
|
9524
9533
|
callback();
|
|
9525
9534
|
},
|
|
9526
9535
|
});
|
|
@@ -9542,7 +9551,7 @@ function getDependencyContainer(env) {
|
|
|
9542
9551
|
const ModelStore = createAbstractStore("Model");
|
|
9543
9552
|
|
|
9544
9553
|
class RendererStore {
|
|
9545
|
-
mutators = ["register", "unRegister"];
|
|
9554
|
+
mutators = ["register", "unRegister", "drawLayer"];
|
|
9546
9555
|
renderers = {};
|
|
9547
9556
|
register(renderer) {
|
|
9548
9557
|
if (!renderer.renderingLayers.length) {
|
|
@@ -9562,14 +9571,14 @@ class RendererStore {
|
|
|
9562
9571
|
}
|
|
9563
9572
|
drawLayer(context, layer) {
|
|
9564
9573
|
const renderers = this.renderers[layer];
|
|
9565
|
-
if (
|
|
9566
|
-
|
|
9567
|
-
|
|
9568
|
-
|
|
9569
|
-
|
|
9570
|
-
|
|
9571
|
-
context.ctx.restore();
|
|
9574
|
+
if (renderers) {
|
|
9575
|
+
for (const renderer of renderers) {
|
|
9576
|
+
context.ctx.save();
|
|
9577
|
+
renderer.drawLayer(context, layer);
|
|
9578
|
+
context.ctx.restore();
|
|
9579
|
+
}
|
|
9572
9580
|
}
|
|
9581
|
+
return "noStateChange";
|
|
9573
9582
|
}
|
|
9574
9583
|
}
|
|
9575
9584
|
|
|
@@ -9622,16 +9631,17 @@ class ComposerFocusStore extends SpreadsheetStore {
|
|
|
9622
9631
|
focusComposer(listener, args) {
|
|
9623
9632
|
this.activeComposer = listener;
|
|
9624
9633
|
if (this.getters.isReadonly()) {
|
|
9625
|
-
return;
|
|
9634
|
+
return "noStateChange";
|
|
9626
9635
|
}
|
|
9627
9636
|
this._focusMode = args.focusMode || "contentFocus";
|
|
9628
9637
|
if (this._focusMode !== "inactive") {
|
|
9629
9638
|
this.setComposerContent(args);
|
|
9630
9639
|
}
|
|
9640
|
+
return;
|
|
9631
9641
|
}
|
|
9632
9642
|
focusActiveComposer(args) {
|
|
9633
9643
|
if (this.getters.isReadonly()) {
|
|
9634
|
-
return;
|
|
9644
|
+
return "noStateChange";
|
|
9635
9645
|
}
|
|
9636
9646
|
if (!this.activeComposer) {
|
|
9637
9647
|
throw new Error("No composer is registered");
|
|
@@ -9640,6 +9650,7 @@ class ComposerFocusStore extends SpreadsheetStore {
|
|
|
9640
9650
|
if (this._focusMode !== "inactive") {
|
|
9641
9651
|
this.setComposerContent(args);
|
|
9642
9652
|
}
|
|
9653
|
+
return;
|
|
9643
9654
|
}
|
|
9644
9655
|
/**
|
|
9645
9656
|
* Start the edition or update the content if it's already started.
|
|
@@ -10133,7 +10144,7 @@ function getDefinedAxis(definition) {
|
|
|
10133
10144
|
}
|
|
10134
10145
|
function formatChartDatasetValue(axisFormats, locale) {
|
|
10135
10146
|
return (value, axisId) => {
|
|
10136
|
-
const format =
|
|
10147
|
+
const format = axisFormats?.[axisId];
|
|
10137
10148
|
return formatTickValue({ format, locale })(value);
|
|
10138
10149
|
};
|
|
10139
10150
|
}
|
|
@@ -10307,7 +10318,7 @@ function drawPieChartValues(chart, options, ctx) {
|
|
|
10307
10318
|
const y = bar.y + midRadius * Math.sin(midAngle) + 7;
|
|
10308
10319
|
ctx.fillStyle = chartFontColor(options.background);
|
|
10309
10320
|
ctx.strokeStyle = options.background || "#ffffff";
|
|
10310
|
-
const displayValue = options.callback(value);
|
|
10321
|
+
const displayValue = options.callback(value, "y");
|
|
10311
10322
|
drawTextWithBackground(displayValue, x, y, ctx);
|
|
10312
10323
|
}
|
|
10313
10324
|
}
|
|
@@ -19206,6 +19217,9 @@ const PIVOT_VALUE = {
|
|
|
19206
19217
|
};
|
|
19207
19218
|
}
|
|
19208
19219
|
const domain = pivot.parseArgsToPivotDomain(domainArgs);
|
|
19220
|
+
if (this.getters.getActiveSheetId() === this.__originSheetId) {
|
|
19221
|
+
this.getters.getPivotPresenceTracker(pivotId)?.trackValue(_measure, domain);
|
|
19222
|
+
}
|
|
19209
19223
|
return pivot.getPivotCellValueAndFormat(_measure, domain);
|
|
19210
19224
|
},
|
|
19211
19225
|
};
|
|
@@ -19237,6 +19251,9 @@ const PIVOT_HEADER = {
|
|
|
19237
19251
|
};
|
|
19238
19252
|
}
|
|
19239
19253
|
const domain = pivot.parseArgsToPivotDomain(domainArgs);
|
|
19254
|
+
if (this.getters.getActiveSheetId() === this.__originSheetId) {
|
|
19255
|
+
this.getters.getPivotPresenceTracker(_pivotId)?.trackHeader(domain);
|
|
19256
|
+
}
|
|
19240
19257
|
const lastNode = domain.at(-1);
|
|
19241
19258
|
if (lastNode?.field === "measure") {
|
|
19242
19259
|
return pivot.getPivotMeasureValue(toString(lastNode.value), domain);
|
|
@@ -19459,6 +19476,9 @@ function isEmpty(data) {
|
|
|
19459
19476
|
return data === undefined || data.value === null;
|
|
19460
19477
|
}
|
|
19461
19478
|
const getNeutral = { number: 0, string: "", boolean: false };
|
|
19479
|
+
function areAlmostEqual(value1, value2, epsilon = 2e-16) {
|
|
19480
|
+
return Math.abs(value1 - value2) < epsilon;
|
|
19481
|
+
}
|
|
19462
19482
|
const EQ = {
|
|
19463
19483
|
description: _t("Equal."),
|
|
19464
19484
|
args: [
|
|
@@ -19480,6 +19500,9 @@ const EQ = {
|
|
|
19480
19500
|
if (typeof _value2 === "string") {
|
|
19481
19501
|
_value2 = _value2.toUpperCase();
|
|
19482
19502
|
}
|
|
19503
|
+
if (typeof _value1 === "number" && typeof _value2 === "number") {
|
|
19504
|
+
return { value: areAlmostEqual(_value1, _value2) };
|
|
19505
|
+
}
|
|
19483
19506
|
return { value: _value1 === _value2 };
|
|
19484
19507
|
},
|
|
19485
19508
|
};
|
|
@@ -19519,6 +19542,9 @@ const GT = {
|
|
|
19519
19542
|
],
|
|
19520
19543
|
compute: function (value1, value2) {
|
|
19521
19544
|
return applyRelationalOperator(value1, value2, (v1, v2) => {
|
|
19545
|
+
if (typeof v1 === "number" && typeof v2 === "number") {
|
|
19546
|
+
return !areAlmostEqual(v1, v2) && v1 > v2;
|
|
19547
|
+
}
|
|
19522
19548
|
return v1 > v2;
|
|
19523
19549
|
});
|
|
19524
19550
|
},
|
|
@@ -19534,6 +19560,9 @@ const GTE = {
|
|
|
19534
19560
|
],
|
|
19535
19561
|
compute: function (value1, value2) {
|
|
19536
19562
|
return applyRelationalOperator(value1, value2, (v1, v2) => {
|
|
19563
|
+
if (typeof v1 === "number" && typeof v2 === "number") {
|
|
19564
|
+
return areAlmostEqual(v1, v2) || v1 > v2;
|
|
19565
|
+
}
|
|
19537
19566
|
return v1 >= v2;
|
|
19538
19567
|
});
|
|
19539
19568
|
},
|
|
@@ -21140,7 +21169,7 @@ class AbstractComposerStore extends SpreadsheetStore {
|
|
|
21140
21169
|
.find((token) => {
|
|
21141
21170
|
const { xc, sheetName: sheet } = splitReference(token.value);
|
|
21142
21171
|
const sheetName = sheet || this.getters.getSheetName(this.sheetId);
|
|
21143
|
-
if (this.getters.getSheetName(activeSheetId)
|
|
21172
|
+
if (!isSheetNameEqual(this.getters.getSheetName(activeSheetId), sheetName)) {
|
|
21144
21173
|
return false;
|
|
21145
21174
|
}
|
|
21146
21175
|
const refRange = this.getters.getRangeFromSheetXC(activeSheetId, xc);
|
|
@@ -24602,7 +24631,7 @@ function getRangeSize(reference, defaultSheetIndex, data) {
|
|
|
24602
24631
|
({ xc, sheetName } = splitReference(reference));
|
|
24603
24632
|
let rangeSheetIndex;
|
|
24604
24633
|
if (sheetName) {
|
|
24605
|
-
const index = data.sheets.findIndex((sheet) => sheet.name
|
|
24634
|
+
const index = data.sheets.findIndex((sheet) => isSheetNameEqual(sheet.name, sheetName));
|
|
24606
24635
|
if (index < 0) {
|
|
24607
24636
|
throw new Error("Unable to find a sheet with the name " + sheetName);
|
|
24608
24637
|
}
|
|
@@ -24943,7 +24972,7 @@ function convertFormula(formula, data) {
|
|
|
24943
24972
|
formula = formula.replace(externalReferenceRegex, (match, externalRefId, sheetName, cellRef) => {
|
|
24944
24973
|
externalRefId = Number(externalRefId) - 1;
|
|
24945
24974
|
cellRef = cellRef.replace(/\$/g, "");
|
|
24946
|
-
const sheetIndex = data.externalBooks[externalRefId].sheetNames.findIndex((name) => name
|
|
24975
|
+
const sheetIndex = data.externalBooks[externalRefId].sheetNames.findIndex((name) => isSheetNameEqual(name, sheetName));
|
|
24947
24976
|
if (sheetIndex === -1) {
|
|
24948
24977
|
return match;
|
|
24949
24978
|
}
|
|
@@ -25594,7 +25623,7 @@ function convertPivotTableConfig(pivotTable) {
|
|
|
25594
25623
|
*/
|
|
25595
25624
|
function convertTableFormulaReferences(convertedSheets, xlsxSheets) {
|
|
25596
25625
|
for (let tableSheet of convertedSheets) {
|
|
25597
|
-
const tables = xlsxSheets.find((s) => s.sheetName
|
|
25626
|
+
const tables = xlsxSheets.find((s) => isSheetNameEqual(s.sheetName, tableSheet.name)).tables;
|
|
25598
25627
|
for (let table of tables) {
|
|
25599
25628
|
const tabRef = table.name + "[";
|
|
25600
25629
|
for (let sheet of convertedSheets) {
|
|
@@ -32586,12 +32615,20 @@ class HoveredCellStore extends SpreadsheetStore {
|
|
|
32586
32615
|
}
|
|
32587
32616
|
}
|
|
32588
32617
|
hover(position) {
|
|
32618
|
+
if (position.col === this.col && position.row === this.row) {
|
|
32619
|
+
return "noStateChange";
|
|
32620
|
+
}
|
|
32589
32621
|
this.col = position.col;
|
|
32590
32622
|
this.row = position.row;
|
|
32623
|
+
return;
|
|
32591
32624
|
}
|
|
32592
32625
|
clear() {
|
|
32626
|
+
if (this.col === undefined && this.row === undefined) {
|
|
32627
|
+
return "noStateChange";
|
|
32628
|
+
}
|
|
32593
32629
|
this.col = undefined;
|
|
32594
32630
|
this.row = undefined;
|
|
32631
|
+
return;
|
|
32595
32632
|
}
|
|
32596
32633
|
}
|
|
32597
32634
|
|
|
@@ -32613,7 +32650,11 @@ class CellPopoverStore extends SpreadsheetStore {
|
|
|
32613
32650
|
this.persistentPopover = { col, row, sheetId, type };
|
|
32614
32651
|
}
|
|
32615
32652
|
close() {
|
|
32653
|
+
if (!this.persistentPopover) {
|
|
32654
|
+
return "noStateChange";
|
|
32655
|
+
}
|
|
32616
32656
|
this.persistentPopover = undefined;
|
|
32657
|
+
return;
|
|
32617
32658
|
}
|
|
32618
32659
|
get persistentCellPopover() {
|
|
32619
32660
|
return ((this.persistentPopover && { isOpen: true, ...this.persistentPopover }) || { isOpen: false });
|
|
@@ -40069,10 +40110,18 @@ class GaugeChartConfigPanel extends owl.Component {
|
|
|
40069
40110
|
}
|
|
40070
40111
|
|
|
40071
40112
|
class DOMFocusableElementStore {
|
|
40072
|
-
mutators = ["setFocusableElement"];
|
|
40113
|
+
mutators = ["setFocusableElement", "focus"];
|
|
40073
40114
|
focusableElement = undefined;
|
|
40074
40115
|
setFocusableElement(element) {
|
|
40075
40116
|
this.focusableElement = element;
|
|
40117
|
+
return "noStateChange";
|
|
40118
|
+
}
|
|
40119
|
+
focus() {
|
|
40120
|
+
if (this.focusableElement === document.activeElement) {
|
|
40121
|
+
return "noStateChange";
|
|
40122
|
+
}
|
|
40123
|
+
this.focusableElement?.focus();
|
|
40124
|
+
return;
|
|
40076
40125
|
}
|
|
40077
40126
|
}
|
|
40078
40127
|
|
|
@@ -40660,7 +40709,7 @@ class Composer extends owl.Component {
|
|
|
40660
40709
|
if (document.activeElement === this.contentHelper.el &&
|
|
40661
40710
|
this.props.composerStore.editionMode === "inactive" &&
|
|
40662
40711
|
!this.props.isDefaultFocus) {
|
|
40663
|
-
this.DOMFocusableElementStore.
|
|
40712
|
+
this.DOMFocusableElementStore.focus();
|
|
40664
40713
|
}
|
|
40665
40714
|
});
|
|
40666
40715
|
owl.useEffect(() => {
|
|
@@ -46944,7 +46993,12 @@ class SpreadsheetPivot {
|
|
|
46944
46993
|
entry[field.name] = { value: null, type: CellValueType.empty, formattedValue: "" };
|
|
46945
46994
|
}
|
|
46946
46995
|
else {
|
|
46947
|
-
|
|
46996
|
+
if (field.type === "char") {
|
|
46997
|
+
entry[field.name] = { ...cell, value: cell.formattedValue || null };
|
|
46998
|
+
}
|
|
46999
|
+
else {
|
|
47000
|
+
entry[field.name] = cell;
|
|
47001
|
+
}
|
|
46948
47002
|
}
|
|
46949
47003
|
}
|
|
46950
47004
|
entry["__count"] = { value: 1, type: CellValueType.number, formattedValue: "1" };
|
|
@@ -52082,10 +52136,6 @@ function useGridDrawing(refName, model, canvasSize) {
|
|
|
52082
52136
|
ctx.scale(dpr, dpr);
|
|
52083
52137
|
for (const layer of OrderedLayers()) {
|
|
52084
52138
|
model.drawLayer(renderingContext, layer);
|
|
52085
|
-
// @ts-ignore 'drawLayer' is not declated as a mutator because:
|
|
52086
|
-
// it does not mutate anything. Most importantly it's used
|
|
52087
|
-
// during rendering. Invoking a mutator during rendering would
|
|
52088
|
-
// trigger another rendering, ultimately resulting in an infinite loop.
|
|
52089
52139
|
rendererStore.drawLayer(renderingContext, layer);
|
|
52090
52140
|
}
|
|
52091
52141
|
}
|
|
@@ -52776,7 +52826,7 @@ class Grid extends owl.Component {
|
|
|
52776
52826
|
this.cellPopovers = useStore(CellPopoverStore);
|
|
52777
52827
|
owl.useEffect(() => {
|
|
52778
52828
|
if (!this.sidePanel.isOpen) {
|
|
52779
|
-
this.DOMFocusableElementStore.
|
|
52829
|
+
this.DOMFocusableElementStore.focus();
|
|
52780
52830
|
}
|
|
52781
52831
|
}, () => [this.sidePanel.isOpen]);
|
|
52782
52832
|
useTouchScroll(this.gridRef, this.moveCanvas.bind(this), () => {
|
|
@@ -52986,7 +53036,7 @@ class Grid extends owl.Component {
|
|
|
52986
53036
|
focusDefaultElement() {
|
|
52987
53037
|
if (!this.env.model.getters.getSelectedFigureId() &&
|
|
52988
53038
|
this.composerFocusStore.activeComposer.editionMode === "inactive") {
|
|
52989
|
-
this.DOMFocusableElementStore.
|
|
53039
|
+
this.DOMFocusableElementStore.focus();
|
|
52990
53040
|
}
|
|
52991
53041
|
}
|
|
52992
53042
|
get gridEl() {
|
|
@@ -53331,6 +53381,322 @@ class Grid extends owl.Component {
|
|
|
53331
53381
|
}
|
|
53332
53382
|
}
|
|
53333
53383
|
|
|
53384
|
+
css /* scss */ `
|
|
53385
|
+
.o_pivot_html_renderer {
|
|
53386
|
+
width: 100%;
|
|
53387
|
+
border-collapse: collapse;
|
|
53388
|
+
|
|
53389
|
+
&:hover {
|
|
53390
|
+
cursor: pointer;
|
|
53391
|
+
}
|
|
53392
|
+
|
|
53393
|
+
td,
|
|
53394
|
+
th {
|
|
53395
|
+
border: 1px solid #dee2e6;
|
|
53396
|
+
background-color: #fff;
|
|
53397
|
+
padding: 0.3rem;
|
|
53398
|
+
white-space: nowrap;
|
|
53399
|
+
|
|
53400
|
+
&:hover {
|
|
53401
|
+
filter: brightness(0.9);
|
|
53402
|
+
}
|
|
53403
|
+
}
|
|
53404
|
+
|
|
53405
|
+
td {
|
|
53406
|
+
text-align: right;
|
|
53407
|
+
}
|
|
53408
|
+
|
|
53409
|
+
th {
|
|
53410
|
+
background-color: #f5f5f5;
|
|
53411
|
+
font-weight: bold;
|
|
53412
|
+
color: black;
|
|
53413
|
+
}
|
|
53414
|
+
|
|
53415
|
+
.o_missing_value {
|
|
53416
|
+
color: #46646d;
|
|
53417
|
+
background: #e7f2f6;
|
|
53418
|
+
}
|
|
53419
|
+
}
|
|
53420
|
+
`;
|
|
53421
|
+
class PivotHTMLRenderer extends owl.Component {
|
|
53422
|
+
static template = "o_spreadsheet.PivotHTMLRenderer";
|
|
53423
|
+
static components = { Checkbox };
|
|
53424
|
+
static props = {
|
|
53425
|
+
pivotId: String,
|
|
53426
|
+
onCellClicked: Function,
|
|
53427
|
+
};
|
|
53428
|
+
pivot = this.env.model.getters.getPivot(this.props.pivotId);
|
|
53429
|
+
data = {
|
|
53430
|
+
columns: [],
|
|
53431
|
+
rows: [],
|
|
53432
|
+
values: [],
|
|
53433
|
+
};
|
|
53434
|
+
state = owl.useState({
|
|
53435
|
+
showMissingValuesOnly: false,
|
|
53436
|
+
});
|
|
53437
|
+
setup() {
|
|
53438
|
+
const table = this.pivot.getTableStructure();
|
|
53439
|
+
const formulaId = this.env.model.getters.getPivotFormulaId(this.props.pivotId);
|
|
53440
|
+
this.data = {
|
|
53441
|
+
columns: this._buildColHeaders(formulaId, table),
|
|
53442
|
+
rows: this._buildRowHeaders(formulaId, table),
|
|
53443
|
+
values: this._buildValues(formulaId, table),
|
|
53444
|
+
};
|
|
53445
|
+
}
|
|
53446
|
+
get tracker() {
|
|
53447
|
+
return this.env.model.getters.getPivotPresenceTracker(this.props.pivotId);
|
|
53448
|
+
}
|
|
53449
|
+
// ---------------------------------------------------------------------
|
|
53450
|
+
// Missing values building
|
|
53451
|
+
// ---------------------------------------------------------------------
|
|
53452
|
+
/**
|
|
53453
|
+
* Retrieve the data to display in the Pivot Table
|
|
53454
|
+
* In the case when showMissingValuesOnly is false, the returned value
|
|
53455
|
+
* is the complete data
|
|
53456
|
+
* In the case when showMissingValuesOnly is true, the returned value is
|
|
53457
|
+
* the data which contains only missing values in the rows and cols. In
|
|
53458
|
+
* the rows, we also return the parent rows of rows which contains missing
|
|
53459
|
+
* values, to give context to the user.
|
|
53460
|
+
*
|
|
53461
|
+
*/
|
|
53462
|
+
getTableData() {
|
|
53463
|
+
if (!this.state.showMissingValuesOnly) {
|
|
53464
|
+
return this.data;
|
|
53465
|
+
}
|
|
53466
|
+
const colIndexes = this.getColumnsIndexes();
|
|
53467
|
+
const rowIndexes = this.getRowsIndexes();
|
|
53468
|
+
const columns = this.buildColumnsMissing(colIndexes);
|
|
53469
|
+
const rows = this.buildRowsMissing(rowIndexes);
|
|
53470
|
+
const values = this.buildValuesMissing(colIndexes, rowIndexes);
|
|
53471
|
+
return { columns, rows, values };
|
|
53472
|
+
}
|
|
53473
|
+
/**
|
|
53474
|
+
* Retrieve the parents of the given row
|
|
53475
|
+
* ex:
|
|
53476
|
+
* Australia
|
|
53477
|
+
* January
|
|
53478
|
+
* February
|
|
53479
|
+
* The parent of "January" is "Australia"
|
|
53480
|
+
*/
|
|
53481
|
+
addRecursiveRow(index) {
|
|
53482
|
+
const rows = this.pivot.getTableStructure().rows;
|
|
53483
|
+
const row = [...rows[index].values];
|
|
53484
|
+
if (row.length <= 1) {
|
|
53485
|
+
return [index];
|
|
53486
|
+
}
|
|
53487
|
+
row.pop();
|
|
53488
|
+
const parentRowIndex = rows.findIndex((r) => JSON.stringify(r.values) === JSON.stringify(row));
|
|
53489
|
+
return [index].concat(this.addRecursiveRow(parentRowIndex));
|
|
53490
|
+
}
|
|
53491
|
+
/**
|
|
53492
|
+
* Create the columns to be used, based on the indexes of the columns in
|
|
53493
|
+
* which a missing value is present
|
|
53494
|
+
*
|
|
53495
|
+
*/
|
|
53496
|
+
buildColumnsMissing(indexes) {
|
|
53497
|
+
// columnsMap explode the columns in an array of array of the same
|
|
53498
|
+
// size with the index of each column, repeated 'span' times.
|
|
53499
|
+
// ex:
|
|
53500
|
+
// | A | B |
|
|
53501
|
+
// | 1 | 2 | 3 |
|
|
53502
|
+
// => [
|
|
53503
|
+
// [0, 0, 1]
|
|
53504
|
+
// [0, 1, 2]
|
|
53505
|
+
// ]
|
|
53506
|
+
const columnsMap = [];
|
|
53507
|
+
for (const column of this.data.columns) {
|
|
53508
|
+
const columnMap = [];
|
|
53509
|
+
for (const index in column) {
|
|
53510
|
+
for (let i = 0; i < column[index].span; i++) {
|
|
53511
|
+
columnMap.push(parseInt(index, 10));
|
|
53512
|
+
}
|
|
53513
|
+
}
|
|
53514
|
+
columnsMap.push(columnMap);
|
|
53515
|
+
}
|
|
53516
|
+
// Remove the columns that are not present in indexes
|
|
53517
|
+
for (let i = columnsMap[columnsMap.length - 1].length; i >= 0; i--) {
|
|
53518
|
+
if (!indexes.includes(i)) {
|
|
53519
|
+
for (const columnMap of columnsMap) {
|
|
53520
|
+
columnMap.splice(i, 1);
|
|
53521
|
+
}
|
|
53522
|
+
}
|
|
53523
|
+
}
|
|
53524
|
+
// Build the columns
|
|
53525
|
+
const columns = [];
|
|
53526
|
+
for (const mapIndex in columnsMap) {
|
|
53527
|
+
const column = [];
|
|
53528
|
+
let index = undefined;
|
|
53529
|
+
let span = 1;
|
|
53530
|
+
for (let i = 0; i < columnsMap[mapIndex].length; i++) {
|
|
53531
|
+
if (index !== columnsMap[mapIndex][i]) {
|
|
53532
|
+
if (index !== undefined) {
|
|
53533
|
+
column.push(Object.assign({}, this.data.columns[mapIndex][index], { span }));
|
|
53534
|
+
}
|
|
53535
|
+
index = columnsMap[mapIndex][i];
|
|
53536
|
+
span = 1;
|
|
53537
|
+
}
|
|
53538
|
+
else {
|
|
53539
|
+
span++;
|
|
53540
|
+
}
|
|
53541
|
+
}
|
|
53542
|
+
if (index !== undefined) {
|
|
53543
|
+
column.push(Object.assign({}, this.data.columns[mapIndex][index], { span }));
|
|
53544
|
+
}
|
|
53545
|
+
columns.push(column);
|
|
53546
|
+
}
|
|
53547
|
+
return columns;
|
|
53548
|
+
}
|
|
53549
|
+
/**
|
|
53550
|
+
* Create the rows to be used, based on the indexes of the rows in
|
|
53551
|
+
* which a missing value is present.
|
|
53552
|
+
*/
|
|
53553
|
+
buildRowsMissing(indexes) {
|
|
53554
|
+
return indexes.map((index) => this.data.rows[index]);
|
|
53555
|
+
}
|
|
53556
|
+
/**
|
|
53557
|
+
* Create the value to be used, based on the indexes of the columns and
|
|
53558
|
+
* rows in which a missing value is present.
|
|
53559
|
+
*/
|
|
53560
|
+
buildValuesMissing(colIndexes, rowIndexes) {
|
|
53561
|
+
const values = colIndexes.map(() => []);
|
|
53562
|
+
for (const row of rowIndexes) {
|
|
53563
|
+
for (const col in colIndexes) {
|
|
53564
|
+
values[col].push(this.data.values[colIndexes[col]][row]);
|
|
53565
|
+
}
|
|
53566
|
+
}
|
|
53567
|
+
return values;
|
|
53568
|
+
}
|
|
53569
|
+
getColumnsIndexes() {
|
|
53570
|
+
const indexes = new Set();
|
|
53571
|
+
for (let i = 0; i < this.data.columns.length; i++) {
|
|
53572
|
+
const exploded = [];
|
|
53573
|
+
for (let y = 0; y < this.data.columns[i].length; y++) {
|
|
53574
|
+
for (let x = 0; x < this.data.columns[i][y].span; x++) {
|
|
53575
|
+
exploded.push(this.data.columns[i][y]);
|
|
53576
|
+
}
|
|
53577
|
+
}
|
|
53578
|
+
for (let y = 0; y < exploded.length; y++) {
|
|
53579
|
+
if (exploded[y].isMissing) {
|
|
53580
|
+
indexes.add(y);
|
|
53581
|
+
}
|
|
53582
|
+
}
|
|
53583
|
+
}
|
|
53584
|
+
for (let i = 0; i < this.data.columns[this.data.columns.length - 1].length; i++) {
|
|
53585
|
+
const values = this.data.values[i];
|
|
53586
|
+
if (values.find((x) => x.isMissing)) {
|
|
53587
|
+
indexes.add(i);
|
|
53588
|
+
}
|
|
53589
|
+
}
|
|
53590
|
+
return Array.from(indexes).sort((a, b) => a - b);
|
|
53591
|
+
}
|
|
53592
|
+
getRowsIndexes() {
|
|
53593
|
+
const rowIndexes = new Set();
|
|
53594
|
+
for (let i = 0; i < this.data.rows.length; i++) {
|
|
53595
|
+
if (this.data.rows[i].isMissing) {
|
|
53596
|
+
rowIndexes.add(i);
|
|
53597
|
+
}
|
|
53598
|
+
for (const col of this.data.values) {
|
|
53599
|
+
if (col[i].isMissing) {
|
|
53600
|
+
this.addRecursiveRow(i).forEach((x) => rowIndexes.add(x));
|
|
53601
|
+
}
|
|
53602
|
+
}
|
|
53603
|
+
}
|
|
53604
|
+
return Array.from(rowIndexes).sort((a, b) => a - b);
|
|
53605
|
+
}
|
|
53606
|
+
// ---------------------------------------------------------------------
|
|
53607
|
+
// Data table creation
|
|
53608
|
+
// ---------------------------------------------------------------------
|
|
53609
|
+
_buildColHeaders(id, table) {
|
|
53610
|
+
const headers = [];
|
|
53611
|
+
for (const row of table.columns) {
|
|
53612
|
+
const current = [];
|
|
53613
|
+
for (const cell of row) {
|
|
53614
|
+
const args = [];
|
|
53615
|
+
for (let i = 0; i < cell.fields.length; i++) {
|
|
53616
|
+
args.push({ value: cell.fields[i] }, { value: cell.values[i] });
|
|
53617
|
+
}
|
|
53618
|
+
const domain = this.pivot.parseArgsToPivotDomain(args);
|
|
53619
|
+
const locale = this.env.model.getters.getLocale();
|
|
53620
|
+
if (domain.at(-1)?.field === "measure") {
|
|
53621
|
+
const { value, format } = this.pivot.getPivotMeasureValue(toString(domain.at(-1).value), domain);
|
|
53622
|
+
current.push({
|
|
53623
|
+
formula: `=PIVOT.HEADER(${generatePivotArgs(id, domain).join(",")})`,
|
|
53624
|
+
value: formatValue(value, { format, locale }),
|
|
53625
|
+
span: cell.width,
|
|
53626
|
+
isMissing: !this.tracker?.isHeaderPresent(domain),
|
|
53627
|
+
});
|
|
53628
|
+
}
|
|
53629
|
+
else {
|
|
53630
|
+
const { value, format } = this.pivot.getPivotHeaderValueAndFormat(domain);
|
|
53631
|
+
current.push({
|
|
53632
|
+
formula: `=PIVOT.HEADER(${generatePivotArgs(id, domain).join(",")})`,
|
|
53633
|
+
value: formatValue(value, { format, locale }),
|
|
53634
|
+
span: cell.width,
|
|
53635
|
+
isMissing: !this.tracker?.isHeaderPresent(domain),
|
|
53636
|
+
});
|
|
53637
|
+
}
|
|
53638
|
+
}
|
|
53639
|
+
headers.push(current);
|
|
53640
|
+
}
|
|
53641
|
+
const last = headers[headers.length - 1];
|
|
53642
|
+
headers[headers.length - 1] = last.map((cell) => {
|
|
53643
|
+
if (!cell.isMissing) {
|
|
53644
|
+
cell.style = "color: #756f6f;";
|
|
53645
|
+
}
|
|
53646
|
+
return cell;
|
|
53647
|
+
});
|
|
53648
|
+
return headers;
|
|
53649
|
+
}
|
|
53650
|
+
_buildRowHeaders(id, table) {
|
|
53651
|
+
const headers = [];
|
|
53652
|
+
for (const row of table.rows) {
|
|
53653
|
+
const args = [];
|
|
53654
|
+
for (let i = 0; i < row.fields.length; i++) {
|
|
53655
|
+
args.push({ value: row.fields[i] }, { value: row.values[i] });
|
|
53656
|
+
}
|
|
53657
|
+
const domain = this.pivot.parseArgsToPivotDomain(args);
|
|
53658
|
+
const { value, format } = this.pivot.getPivotHeaderValueAndFormat(domain);
|
|
53659
|
+
const locale = this.env.model.getters.getLocale();
|
|
53660
|
+
const cell = {
|
|
53661
|
+
formula: `=PIVOT.HEADER(${generatePivotArgs(id, domain).join(",")})`,
|
|
53662
|
+
value: formatValue(value, { format, locale }),
|
|
53663
|
+
isMissing: !this.tracker?.isHeaderPresent(domain),
|
|
53664
|
+
};
|
|
53665
|
+
if (row.indent > 1) {
|
|
53666
|
+
cell.style = `padding-left: ${row.indent - 1 * 10}px`;
|
|
53667
|
+
}
|
|
53668
|
+
headers.push(cell);
|
|
53669
|
+
}
|
|
53670
|
+
return headers;
|
|
53671
|
+
}
|
|
53672
|
+
_buildValues(id, table) {
|
|
53673
|
+
const values = [];
|
|
53674
|
+
for (const col of table.columns.at(-1) || []) {
|
|
53675
|
+
const current = [];
|
|
53676
|
+
const measure = toString(col.values[col.values.length - 1]);
|
|
53677
|
+
for (const row of table.rows) {
|
|
53678
|
+
const args = [];
|
|
53679
|
+
for (let i = 0; i < row.fields.length; i++) {
|
|
53680
|
+
args.push({ value: row.fields[i] }, { value: row.values[i] });
|
|
53681
|
+
}
|
|
53682
|
+
for (let i = 0; i < col.fields.length - 1; i++) {
|
|
53683
|
+
args.push({ value: col.fields[i] }, { value: col.values[i] });
|
|
53684
|
+
}
|
|
53685
|
+
const domain = this.pivot.parseArgsToPivotDomain(args);
|
|
53686
|
+
const { value, format } = this.pivot.getPivotCellValueAndFormat(measure, domain);
|
|
53687
|
+
const locale = this.env.model.getters.getLocale();
|
|
53688
|
+
current.push({
|
|
53689
|
+
formula: `=PIVOT.VALUE(${generatePivotArgs(id, domain, measure).join(",")})`,
|
|
53690
|
+
value: formatValue(value, { format, locale }),
|
|
53691
|
+
isMissing: !this.tracker?.isValuePresent(measure, domain),
|
|
53692
|
+
});
|
|
53693
|
+
}
|
|
53694
|
+
values.push(current);
|
|
53695
|
+
}
|
|
53696
|
+
return values;
|
|
53697
|
+
}
|
|
53698
|
+
}
|
|
53699
|
+
|
|
53334
53700
|
/**
|
|
53335
53701
|
* BasePlugin
|
|
53336
53702
|
*
|
|
@@ -56790,7 +57156,7 @@ class RangeAdapter {
|
|
|
56790
57156
|
if (range.sheetId === cmd.sheetId) {
|
|
56791
57157
|
return { changeType: "CHANGE", range };
|
|
56792
57158
|
}
|
|
56793
|
-
if (
|
|
57159
|
+
if (isSheetNameEqual(range.invalidSheetName, cmd.name)) {
|
|
56794
57160
|
const invalidSheetName = undefined;
|
|
56795
57161
|
const sheetId = cmd.sheetId;
|
|
56796
57162
|
const newRange = range.clone({ sheetId, invalidSheetName });
|
|
@@ -57407,7 +57773,7 @@ class SheetPlugin extends CorePlugin {
|
|
|
57407
57773
|
if (name) {
|
|
57408
57774
|
const unquotedName = getUnquotedSheetName(name);
|
|
57409
57775
|
for (const key in this.sheetIdsMapName) {
|
|
57410
|
-
if (key
|
|
57776
|
+
if (isSheetNameEqual(key, unquotedName)) {
|
|
57411
57777
|
return this.sheetIdsMapName[key];
|
|
57412
57778
|
}
|
|
57413
57779
|
}
|
|
@@ -57655,7 +58021,7 @@ class SheetPlugin extends CorePlugin {
|
|
|
57655
58021
|
}
|
|
57656
58022
|
const { orderedSheetIds, sheets } = this;
|
|
57657
58023
|
const name = cmd.name && cmd.name.trim().toLowerCase();
|
|
57658
|
-
if (orderedSheetIds.find((id) => sheets[id]?.name
|
|
58024
|
+
if (orderedSheetIds.find((id) => isSheetNameEqual(sheets[id]?.name, name) && id !== cmd.sheetId)) {
|
|
57659
58025
|
return "DuplicatedSheetName" /* CommandResult.DuplicatedSheetName */;
|
|
57660
58026
|
}
|
|
57661
58027
|
if (FORBIDDEN_SHEETNAME_CHARS_IN_EXCEL_REGEX.test(name)) {
|
|
@@ -66548,6 +66914,55 @@ class HistoryPlugin extends UIPlugin {
|
|
|
66548
66914
|
}
|
|
66549
66915
|
}
|
|
66550
66916
|
|
|
66917
|
+
class PivotPresenceTracker {
|
|
66918
|
+
trackedValues = new Set();
|
|
66919
|
+
domainToArray(domain) {
|
|
66920
|
+
return domain.flatMap((node) => [node.field, toString(node.value)]);
|
|
66921
|
+
}
|
|
66922
|
+
isValuePresent(measure, domain) {
|
|
66923
|
+
const key = JSON.stringify({ measure, domain: this.domainToArray(domain) });
|
|
66924
|
+
return this.trackedValues.has(key);
|
|
66925
|
+
}
|
|
66926
|
+
isHeaderPresent(domain) {
|
|
66927
|
+
const key = JSON.stringify({ domain: this.domainToArray(domain) });
|
|
66928
|
+
return this.trackedValues.has(key);
|
|
66929
|
+
}
|
|
66930
|
+
trackValue(measure, domain) {
|
|
66931
|
+
const key = JSON.stringify({ measure, domain: this.domainToArray(domain) });
|
|
66932
|
+
this.trackedValues.add(key);
|
|
66933
|
+
}
|
|
66934
|
+
trackHeader(domain) {
|
|
66935
|
+
const key = JSON.stringify({ domain: this.domainToArray(domain) });
|
|
66936
|
+
this.trackedValues.add(key);
|
|
66937
|
+
}
|
|
66938
|
+
}
|
|
66939
|
+
|
|
66940
|
+
class PivotPresencePlugin extends UIPlugin {
|
|
66941
|
+
static getters = ["getPivotPresenceTracker"];
|
|
66942
|
+
trackPresencePivotId;
|
|
66943
|
+
tracker;
|
|
66944
|
+
handle(cmd) {
|
|
66945
|
+
switch (cmd.type) {
|
|
66946
|
+
case "PIVOT_START_PRESENCE_TRACKING":
|
|
66947
|
+
this.tracker = new PivotPresenceTracker();
|
|
66948
|
+
this.trackPresencePivotId = cmd.pivotId;
|
|
66949
|
+
break;
|
|
66950
|
+
case "PIVOT_STOP_PRESENCE_TRACKING":
|
|
66951
|
+
this.trackPresencePivotId = undefined;
|
|
66952
|
+
break;
|
|
66953
|
+
}
|
|
66954
|
+
}
|
|
66955
|
+
getPivotPresenceTracker(pivotId) {
|
|
66956
|
+
if (this.trackPresencePivotId !== pivotId) {
|
|
66957
|
+
return undefined;
|
|
66958
|
+
}
|
|
66959
|
+
if (!this.tracker) {
|
|
66960
|
+
throw new Error("Tracker not initialized");
|
|
66961
|
+
}
|
|
66962
|
+
return this.tracker;
|
|
66963
|
+
}
|
|
66964
|
+
}
|
|
66965
|
+
|
|
66551
66966
|
class SplitToColumnsPlugin extends UIPlugin {
|
|
66552
66967
|
static getters = ["getAutomaticSeparator"];
|
|
66553
66968
|
allowDispatch(cmd) {
|
|
@@ -69298,6 +69713,7 @@ const featurePluginRegistry = new Registry()
|
|
|
69298
69713
|
.add("automatic_sum", AutomaticSumPlugin)
|
|
69299
69714
|
.add("format", FormatPlugin)
|
|
69300
69715
|
.add("insert_pivot", InsertPivotPlugin)
|
|
69716
|
+
.add("pivot_presence", PivotPresencePlugin)
|
|
69301
69717
|
.add("split_to_columns", SplitToColumnsPlugin)
|
|
69302
69718
|
.add("collaborative", CollaborativePlugin)
|
|
69303
69719
|
.add("history", HistoryPlugin)
|
|
@@ -69678,11 +70094,11 @@ class BottomBarSheet extends owl.Component {
|
|
|
69678
70094
|
if (ev.key === "Enter") {
|
|
69679
70095
|
ev.preventDefault();
|
|
69680
70096
|
this.stopEdition();
|
|
69681
|
-
this.DOMFocusableElementStore.
|
|
70097
|
+
this.DOMFocusableElementStore.focus();
|
|
69682
70098
|
}
|
|
69683
70099
|
if (ev.key === "Escape") {
|
|
69684
70100
|
this.cancelEdition();
|
|
69685
|
-
this.DOMFocusableElementStore.
|
|
70101
|
+
this.DOMFocusableElementStore.focus();
|
|
69686
70102
|
}
|
|
69687
70103
|
}
|
|
69688
70104
|
onMouseEventSheetName(ev) {
|
|
@@ -76293,6 +76709,7 @@ const components = {
|
|
|
76293
76709
|
PivotDimensionOrder,
|
|
76294
76710
|
PivotDimension,
|
|
76295
76711
|
PivotLayoutConfigurator,
|
|
76712
|
+
PivotHTMLRenderer,
|
|
76296
76713
|
PivotDeferUpdate,
|
|
76297
76714
|
PivotTitleSection,
|
|
76298
76715
|
CogWheelMenu,
|
|
@@ -76387,6 +76804,6 @@ exports.tokenColors = tokenColors;
|
|
|
76387
76804
|
exports.tokenize = tokenize;
|
|
76388
76805
|
|
|
76389
76806
|
|
|
76390
|
-
__info__.version = "18.2.
|
|
76391
|
-
__info__.date = "2025-05-
|
|
76392
|
-
__info__.hash = "
|
|
76807
|
+
__info__.version = "18.2.12";
|
|
76808
|
+
__info__.date = "2025-05-13T17:52:23.989Z";
|
|
76809
|
+
__info__.hash = "ba2ba9b";
|