@odoo/o-spreadsheet 18.0.27 → 18.0.28
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 +456 -39
- package/dist/o-spreadsheet.d.ts +267 -142
- package/dist/o-spreadsheet.esm.js +456 -39
- package/dist/o-spreadsheet.iife.js +456 -39
- package/dist/o-spreadsheet.iife.min.js +301 -265
- 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.0.
|
|
6
|
-
* @date 2025-05-
|
|
7
|
-
* @hash
|
|
5
|
+
* @version 18.0.28
|
|
6
|
+
* @date 2025-05-13T17:53:12.402Z
|
|
7
|
+
* @hash b3088aa
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
'use strict';
|
|
@@ -3182,7 +3182,7 @@ function isDateAfter(date, dateAfter) {
|
|
|
3182
3182
|
*/
|
|
3183
3183
|
const getFormulaNumberRegex = memoize(function getFormulaNumberRegex(decimalSeparator) {
|
|
3184
3184
|
decimalSeparator = escapeRegExp(decimalSeparator);
|
|
3185
|
-
return new RegExp(`(?:^-?\\d+(?:${decimalSeparator}?\\d*(?:e
|
|
3185
|
+
return new RegExp(`(?:^-?\\d+(?:${decimalSeparator}?\\d*(?:e(\\+|-)?\\d+)?)?|^-?${decimalSeparator}\\d+)(?!\\w|!)`);
|
|
3186
3186
|
});
|
|
3187
3187
|
const getNumberRegex = memoize(function getNumberRegex(locale) {
|
|
3188
3188
|
const decimalSeparator = escapeRegExp(locale.decimalSeparator);
|
|
@@ -6123,6 +6123,13 @@ function getDuplicateSheetName(nameToDuplicate, existingNames) {
|
|
|
6123
6123
|
}
|
|
6124
6124
|
return name;
|
|
6125
6125
|
}
|
|
6126
|
+
function isSheetNameEqual(name1, name2) {
|
|
6127
|
+
if (name1 === undefined || name2 === undefined) {
|
|
6128
|
+
return false;
|
|
6129
|
+
}
|
|
6130
|
+
return (getUnquotedSheetName(name1.trim().toUpperCase()) ===
|
|
6131
|
+
getUnquotedSheetName(name2.trim().toUpperCase()));
|
|
6132
|
+
}
|
|
6126
6133
|
|
|
6127
6134
|
function computeTextLinesHeight(textLineHeight, numberOfLines = 1) {
|
|
6128
6135
|
return numberOfLines * (textLineHeight + MIN_CELL_TEXT_MARGIN) - MIN_CELL_TEXT_MARGIN;
|
|
@@ -7976,10 +7983,9 @@ const AGGREGATOR_NAMES = {
|
|
|
7976
7983
|
avg: _t("Average"),
|
|
7977
7984
|
sum: _t("Sum"),
|
|
7978
7985
|
};
|
|
7979
|
-
const NUMBER_CHAR_AGGREGATORS = ["max", "min", "avg", "sum", "count_distinct", "count"];
|
|
7980
7986
|
const AGGREGATORS_BY_FIELD_TYPE = {
|
|
7981
|
-
integer:
|
|
7982
|
-
char:
|
|
7987
|
+
integer: ["max", "min", "avg", "sum", "count_distinct", "count"],
|
|
7988
|
+
char: ["count_distinct", "count"],
|
|
7983
7989
|
boolean: ["count_distinct", "count", "bool_and", "bool_or"],
|
|
7984
7990
|
};
|
|
7985
7991
|
const AGGREGATORS = {};
|
|
@@ -9312,7 +9318,10 @@ function proxifyStoreMutation(store, callback) {
|
|
|
9312
9318
|
const functionProxy = new Proxy(value, {
|
|
9313
9319
|
// trap the function call
|
|
9314
9320
|
apply(target, thisArg, argArray) {
|
|
9315
|
-
Reflect.apply(target, thisStore, argArray);
|
|
9321
|
+
const res = Reflect.apply(target, thisStore, argArray);
|
|
9322
|
+
if (res === "noStateChange") {
|
|
9323
|
+
return;
|
|
9324
|
+
}
|
|
9316
9325
|
callback();
|
|
9317
9326
|
},
|
|
9318
9327
|
});
|
|
@@ -9334,7 +9343,7 @@ function getDependencyContainer(env) {
|
|
|
9334
9343
|
const ModelStore = createAbstractStore("Model");
|
|
9335
9344
|
|
|
9336
9345
|
class RendererStore {
|
|
9337
|
-
mutators = ["register", "unRegister"];
|
|
9346
|
+
mutators = ["register", "unRegister", "drawLayer"];
|
|
9338
9347
|
renderers = {};
|
|
9339
9348
|
register(renderer) {
|
|
9340
9349
|
if (!renderer.renderingLayers.length) {
|
|
@@ -9354,14 +9363,14 @@ class RendererStore {
|
|
|
9354
9363
|
}
|
|
9355
9364
|
drawLayer(context, layer) {
|
|
9356
9365
|
const renderers = this.renderers[layer];
|
|
9357
|
-
if (
|
|
9358
|
-
|
|
9359
|
-
|
|
9360
|
-
|
|
9361
|
-
|
|
9362
|
-
|
|
9363
|
-
context.ctx.restore();
|
|
9366
|
+
if (renderers) {
|
|
9367
|
+
for (const renderer of renderers) {
|
|
9368
|
+
context.ctx.save();
|
|
9369
|
+
renderer.drawLayer(context, layer);
|
|
9370
|
+
context.ctx.restore();
|
|
9371
|
+
}
|
|
9364
9372
|
}
|
|
9373
|
+
return "noStateChange";
|
|
9365
9374
|
}
|
|
9366
9375
|
}
|
|
9367
9376
|
|
|
@@ -9414,16 +9423,17 @@ class ComposerFocusStore extends SpreadsheetStore {
|
|
|
9414
9423
|
focusComposer(listener, args) {
|
|
9415
9424
|
this.activeComposer = listener;
|
|
9416
9425
|
if (this.getters.isReadonly()) {
|
|
9417
|
-
return;
|
|
9426
|
+
return "noStateChange";
|
|
9418
9427
|
}
|
|
9419
9428
|
this._focusMode = args.focusMode || "contentFocus";
|
|
9420
9429
|
if (this._focusMode !== "inactive") {
|
|
9421
9430
|
this.setComposerContent(args);
|
|
9422
9431
|
}
|
|
9432
|
+
return;
|
|
9423
9433
|
}
|
|
9424
9434
|
focusActiveComposer(args) {
|
|
9425
9435
|
if (this.getters.isReadonly()) {
|
|
9426
|
-
return;
|
|
9436
|
+
return "noStateChange";
|
|
9427
9437
|
}
|
|
9428
9438
|
if (!this.activeComposer) {
|
|
9429
9439
|
throw new Error("No composer is registered");
|
|
@@ -9432,6 +9442,7 @@ class ComposerFocusStore extends SpreadsheetStore {
|
|
|
9432
9442
|
if (this._focusMode !== "inactive") {
|
|
9433
9443
|
this.setComposerContent(args);
|
|
9434
9444
|
}
|
|
9445
|
+
return;
|
|
9435
9446
|
}
|
|
9436
9447
|
/**
|
|
9437
9448
|
* Start the edition or update the content if it's already started.
|
|
@@ -11746,7 +11757,7 @@ function getRangeSize(reference, defaultSheetIndex, data) {
|
|
|
11746
11757
|
({ xc, sheetName } = splitReference(reference));
|
|
11747
11758
|
let rangeSheetIndex;
|
|
11748
11759
|
if (sheetName) {
|
|
11749
|
-
const index = data.sheets.findIndex((sheet) => sheet.name
|
|
11760
|
+
const index = data.sheets.findIndex((sheet) => isSheetNameEqual(sheet.name, sheetName));
|
|
11750
11761
|
if (index < 0) {
|
|
11751
11762
|
throw new Error("Unable to find a sheet with the name " + sheetName);
|
|
11752
11763
|
}
|
|
@@ -12087,7 +12098,7 @@ function convertFormula(formula, data) {
|
|
|
12087
12098
|
formula = formula.replace(externalReferenceRegex, (match, externalRefId, sheetName, cellRef) => {
|
|
12088
12099
|
externalRefId = Number(externalRefId) - 1;
|
|
12089
12100
|
cellRef = cellRef.replace(/\$/g, "");
|
|
12090
|
-
const sheetIndex = data.externalBooks[externalRefId].sheetNames.findIndex((name) => name
|
|
12101
|
+
const sheetIndex = data.externalBooks[externalRefId].sheetNames.findIndex((name) => isSheetNameEqual(name, sheetName));
|
|
12091
12102
|
if (sheetIndex === -1) {
|
|
12092
12103
|
return match;
|
|
12093
12104
|
}
|
|
@@ -12750,7 +12761,7 @@ function convertPivotTableConfig(pivotTable) {
|
|
|
12750
12761
|
*/
|
|
12751
12762
|
function convertTableFormulaReferences(convertedSheets, xlsxSheets) {
|
|
12752
12763
|
for (let tableSheet of convertedSheets) {
|
|
12753
|
-
const tables = xlsxSheets.find((s) => s.sheetName
|
|
12764
|
+
const tables = xlsxSheets.find((s) => isSheetNameEqual(s.sheetName, tableSheet.name)).tables;
|
|
12754
12765
|
for (let table of tables) {
|
|
12755
12766
|
const tabRef = table.name + "[";
|
|
12756
12767
|
for (let sheet of convertedSheets) {
|
|
@@ -25073,6 +25084,9 @@ const PIVOT_VALUE = {
|
|
|
25073
25084
|
};
|
|
25074
25085
|
}
|
|
25075
25086
|
const domain = pivot.parseArgsToPivotDomain(domainArgs);
|
|
25087
|
+
if (this.getters.getActiveSheetId() === this.__originSheetId) {
|
|
25088
|
+
this.getters.getPivotPresenceTracker(pivotId)?.trackValue(_measure, domain);
|
|
25089
|
+
}
|
|
25076
25090
|
return pivot.getPivotCellValueAndFormat(_measure, domain);
|
|
25077
25091
|
},
|
|
25078
25092
|
};
|
|
@@ -25104,6 +25118,9 @@ const PIVOT_HEADER = {
|
|
|
25104
25118
|
};
|
|
25105
25119
|
}
|
|
25106
25120
|
const domain = pivot.parseArgsToPivotDomain(domainArgs);
|
|
25121
|
+
if (this.getters.getActiveSheetId() === this.__originSheetId) {
|
|
25122
|
+
this.getters.getPivotPresenceTracker(_pivotId)?.trackHeader(domain);
|
|
25123
|
+
}
|
|
25107
25124
|
const lastNode = domain.at(-1);
|
|
25108
25125
|
if (lastNode?.field === "measure") {
|
|
25109
25126
|
return pivot.getPivotMeasureValue(toString(lastNode.value), domain);
|
|
@@ -25326,6 +25343,9 @@ function isEmpty(data) {
|
|
|
25326
25343
|
return data === undefined || data.value === null;
|
|
25327
25344
|
}
|
|
25328
25345
|
const getNeutral = { number: 0, string: "", boolean: false };
|
|
25346
|
+
function areAlmostEqual(value1, value2, epsilon = 2e-16) {
|
|
25347
|
+
return Math.abs(value1 - value2) < epsilon;
|
|
25348
|
+
}
|
|
25329
25349
|
const EQ = {
|
|
25330
25350
|
description: _t("Equal."),
|
|
25331
25351
|
args: [
|
|
@@ -25341,6 +25361,9 @@ const EQ = {
|
|
|
25341
25361
|
if (typeof _value2 === "string") {
|
|
25342
25362
|
_value2 = _value2.toUpperCase();
|
|
25343
25363
|
}
|
|
25364
|
+
if (typeof _value1 === "number" && typeof _value2 === "number") {
|
|
25365
|
+
return areAlmostEqual(_value1, _value2);
|
|
25366
|
+
}
|
|
25344
25367
|
return _value1 === _value2;
|
|
25345
25368
|
},
|
|
25346
25369
|
};
|
|
@@ -25380,6 +25403,9 @@ const GT = {
|
|
|
25380
25403
|
],
|
|
25381
25404
|
compute: function (value1, value2) {
|
|
25382
25405
|
return applyRelationalOperator(value1, value2, (v1, v2) => {
|
|
25406
|
+
if (typeof v1 === "number" && typeof v2 === "number") {
|
|
25407
|
+
return !areAlmostEqual(v1, v2) && v1 > v2;
|
|
25408
|
+
}
|
|
25383
25409
|
return v1 > v2;
|
|
25384
25410
|
});
|
|
25385
25411
|
},
|
|
@@ -25395,6 +25421,9 @@ const GTE = {
|
|
|
25395
25421
|
],
|
|
25396
25422
|
compute: function (value1, value2) {
|
|
25397
25423
|
return applyRelationalOperator(value1, value2, (v1, v2) => {
|
|
25424
|
+
if (typeof v1 === "number" && typeof v2 === "number") {
|
|
25425
|
+
return areAlmostEqual(v1, v2) || v1 > v2;
|
|
25426
|
+
}
|
|
25398
25427
|
return v1 >= v2;
|
|
25399
25428
|
});
|
|
25400
25429
|
},
|
|
@@ -26517,10 +26546,18 @@ autoCompleteProviders.add("functions", {
|
|
|
26517
26546
|
});
|
|
26518
26547
|
|
|
26519
26548
|
class DOMFocusableElementStore {
|
|
26520
|
-
mutators = ["setFocusableElement"];
|
|
26549
|
+
mutators = ["setFocusableElement", "focus"];
|
|
26521
26550
|
focusableElement = undefined;
|
|
26522
26551
|
setFocusableElement(element) {
|
|
26523
26552
|
this.focusableElement = element;
|
|
26553
|
+
return "noStateChange";
|
|
26554
|
+
}
|
|
26555
|
+
focus() {
|
|
26556
|
+
if (this.focusableElement === document.activeElement) {
|
|
26557
|
+
return "noStateChange";
|
|
26558
|
+
}
|
|
26559
|
+
this.focusableElement?.focus();
|
|
26560
|
+
return;
|
|
26524
26561
|
}
|
|
26525
26562
|
}
|
|
26526
26563
|
|
|
@@ -27363,7 +27400,7 @@ class Composer extends owl.Component {
|
|
|
27363
27400
|
if (document.activeElement === this.contentHelper.el &&
|
|
27364
27401
|
this.props.composerStore.editionMode === "inactive" &&
|
|
27365
27402
|
!this.props.isDefaultFocus) {
|
|
27366
|
-
this.DOMFocusableElementStore.
|
|
27403
|
+
this.DOMFocusableElementStore.focus();
|
|
27367
27404
|
}
|
|
27368
27405
|
});
|
|
27369
27406
|
owl.useEffect(() => {
|
|
@@ -31835,12 +31872,20 @@ class HoveredCellStore extends SpreadsheetStore {
|
|
|
31835
31872
|
}
|
|
31836
31873
|
}
|
|
31837
31874
|
hover(position) {
|
|
31875
|
+
if (position.col === this.col && position.row === this.row) {
|
|
31876
|
+
return "noStateChange";
|
|
31877
|
+
}
|
|
31838
31878
|
this.col = position.col;
|
|
31839
31879
|
this.row = position.row;
|
|
31880
|
+
return;
|
|
31840
31881
|
}
|
|
31841
31882
|
clear() {
|
|
31883
|
+
if (this.col === undefined && this.row === undefined) {
|
|
31884
|
+
return "noStateChange";
|
|
31885
|
+
}
|
|
31842
31886
|
this.col = undefined;
|
|
31843
31887
|
this.row = undefined;
|
|
31888
|
+
return;
|
|
31844
31889
|
}
|
|
31845
31890
|
}
|
|
31846
31891
|
|
|
@@ -31862,7 +31907,11 @@ class CellPopoverStore extends SpreadsheetStore {
|
|
|
31862
31907
|
this.persistentPopover = { col, row, sheetId, type };
|
|
31863
31908
|
}
|
|
31864
31909
|
close() {
|
|
31910
|
+
if (!this.persistentPopover) {
|
|
31911
|
+
return "noStateChange";
|
|
31912
|
+
}
|
|
31865
31913
|
this.persistentPopover = undefined;
|
|
31914
|
+
return;
|
|
31866
31915
|
}
|
|
31867
31916
|
get persistentCellPopover() {
|
|
31868
31917
|
return ((this.persistentPopover && { isOpen: true, ...this.persistentPopover }) || { isOpen: false });
|
|
@@ -39215,7 +39264,7 @@ class AbstractComposerStore extends SpreadsheetStore {
|
|
|
39215
39264
|
.find((token) => {
|
|
39216
39265
|
const { xc, sheetName: sheet } = splitReference(token.value);
|
|
39217
39266
|
const sheetName = sheet || this.getters.getSheetName(this.sheetId);
|
|
39218
|
-
if (this.getters.getSheetName(activeSheetId)
|
|
39267
|
+
if (!isSheetNameEqual(this.getters.getSheetName(activeSheetId), sheetName)) {
|
|
39219
39268
|
return false;
|
|
39220
39269
|
}
|
|
39221
39270
|
const refRange = this.getters.getRangeFromSheetXC(activeSheetId, xc);
|
|
@@ -44512,7 +44561,12 @@ class SpreadsheetPivot {
|
|
|
44512
44561
|
entry[field.name] = { value: null, type: CellValueType.empty, formattedValue: "" };
|
|
44513
44562
|
}
|
|
44514
44563
|
else {
|
|
44515
|
-
|
|
44564
|
+
if (field.type === "char") {
|
|
44565
|
+
entry[field.name] = { ...cell, value: cell.formattedValue || null };
|
|
44566
|
+
}
|
|
44567
|
+
else {
|
|
44568
|
+
entry[field.name] = cell;
|
|
44569
|
+
}
|
|
44516
44570
|
}
|
|
44517
44571
|
}
|
|
44518
44572
|
entry["__count"] = { value: 1, type: CellValueType.number, formattedValue: "1" };
|
|
@@ -49508,10 +49562,6 @@ function useGridDrawing(refName, model, canvasSize) {
|
|
|
49508
49562
|
ctx.scale(dpr, dpr);
|
|
49509
49563
|
for (const layer of OrderedLayers()) {
|
|
49510
49564
|
model.drawLayer(renderingContext, layer);
|
|
49511
|
-
// @ts-ignore 'drawLayer' is not declated as a mutator because:
|
|
49512
|
-
// it does not mutate anything. Most importantly it's used
|
|
49513
|
-
// during rendering. Invoking a mutator during rendering would
|
|
49514
|
-
// trigger another rendering, ultimately resulting in an infinite loop.
|
|
49515
49565
|
rendererStore.drawLayer(renderingContext, layer);
|
|
49516
49566
|
}
|
|
49517
49567
|
}
|
|
@@ -50201,7 +50251,7 @@ class Grid extends owl.Component {
|
|
|
50201
50251
|
this.cellPopovers = useStore(CellPopoverStore);
|
|
50202
50252
|
owl.useEffect(() => {
|
|
50203
50253
|
if (!this.sidePanel.isOpen) {
|
|
50204
|
-
this.DOMFocusableElementStore.
|
|
50254
|
+
this.DOMFocusableElementStore.focus();
|
|
50205
50255
|
}
|
|
50206
50256
|
}, () => [this.sidePanel.isOpen]);
|
|
50207
50257
|
useTouchScroll(this.gridRef, this.moveCanvas.bind(this), () => {
|
|
@@ -50409,7 +50459,7 @@ class Grid extends owl.Component {
|
|
|
50409
50459
|
focusDefaultElement() {
|
|
50410
50460
|
if (!this.env.model.getters.getSelectedFigureId() &&
|
|
50411
50461
|
this.composerFocusStore.activeComposer.editionMode === "inactive") {
|
|
50412
|
-
this.DOMFocusableElementStore.
|
|
50462
|
+
this.DOMFocusableElementStore.focus();
|
|
50413
50463
|
}
|
|
50414
50464
|
}
|
|
50415
50465
|
get gridEl() {
|
|
@@ -50779,6 +50829,322 @@ class EditableName extends owl.Component {
|
|
|
50779
50829
|
}
|
|
50780
50830
|
}
|
|
50781
50831
|
|
|
50832
|
+
css /* scss */ `
|
|
50833
|
+
.o_pivot_html_renderer {
|
|
50834
|
+
width: 100%;
|
|
50835
|
+
border-collapse: collapse;
|
|
50836
|
+
|
|
50837
|
+
&:hover {
|
|
50838
|
+
cursor: pointer;
|
|
50839
|
+
}
|
|
50840
|
+
|
|
50841
|
+
td,
|
|
50842
|
+
th {
|
|
50843
|
+
border: 1px solid #dee2e6;
|
|
50844
|
+
background-color: #fff;
|
|
50845
|
+
padding: 0.3rem;
|
|
50846
|
+
white-space: nowrap;
|
|
50847
|
+
|
|
50848
|
+
&:hover {
|
|
50849
|
+
filter: brightness(0.9);
|
|
50850
|
+
}
|
|
50851
|
+
}
|
|
50852
|
+
|
|
50853
|
+
td {
|
|
50854
|
+
text-align: right;
|
|
50855
|
+
}
|
|
50856
|
+
|
|
50857
|
+
th {
|
|
50858
|
+
background-color: #f5f5f5;
|
|
50859
|
+
font-weight: bold;
|
|
50860
|
+
color: black;
|
|
50861
|
+
}
|
|
50862
|
+
|
|
50863
|
+
.o_missing_value {
|
|
50864
|
+
color: #46646d;
|
|
50865
|
+
background: #e7f2f6;
|
|
50866
|
+
}
|
|
50867
|
+
}
|
|
50868
|
+
`;
|
|
50869
|
+
class PivotHTMLRenderer extends owl.Component {
|
|
50870
|
+
static template = "o_spreadsheet.PivotHTMLRenderer";
|
|
50871
|
+
static components = { Checkbox };
|
|
50872
|
+
static props = {
|
|
50873
|
+
pivotId: String,
|
|
50874
|
+
onCellClicked: Function,
|
|
50875
|
+
};
|
|
50876
|
+
pivot = this.env.model.getters.getPivot(this.props.pivotId);
|
|
50877
|
+
data = {
|
|
50878
|
+
columns: [],
|
|
50879
|
+
rows: [],
|
|
50880
|
+
values: [],
|
|
50881
|
+
};
|
|
50882
|
+
state = owl.useState({
|
|
50883
|
+
showMissingValuesOnly: false,
|
|
50884
|
+
});
|
|
50885
|
+
setup() {
|
|
50886
|
+
const table = this.pivot.getTableStructure();
|
|
50887
|
+
const formulaId = this.env.model.getters.getPivotFormulaId(this.props.pivotId);
|
|
50888
|
+
this.data = {
|
|
50889
|
+
columns: this._buildColHeaders(formulaId, table),
|
|
50890
|
+
rows: this._buildRowHeaders(formulaId, table),
|
|
50891
|
+
values: this._buildValues(formulaId, table),
|
|
50892
|
+
};
|
|
50893
|
+
}
|
|
50894
|
+
get tracker() {
|
|
50895
|
+
return this.env.model.getters.getPivotPresenceTracker(this.props.pivotId);
|
|
50896
|
+
}
|
|
50897
|
+
// ---------------------------------------------------------------------
|
|
50898
|
+
// Missing values building
|
|
50899
|
+
// ---------------------------------------------------------------------
|
|
50900
|
+
/**
|
|
50901
|
+
* Retrieve the data to display in the Pivot Table
|
|
50902
|
+
* In the case when showMissingValuesOnly is false, the returned value
|
|
50903
|
+
* is the complete data
|
|
50904
|
+
* In the case when showMissingValuesOnly is true, the returned value is
|
|
50905
|
+
* the data which contains only missing values in the rows and cols. In
|
|
50906
|
+
* the rows, we also return the parent rows of rows which contains missing
|
|
50907
|
+
* values, to give context to the user.
|
|
50908
|
+
*
|
|
50909
|
+
*/
|
|
50910
|
+
getTableData() {
|
|
50911
|
+
if (!this.state.showMissingValuesOnly) {
|
|
50912
|
+
return this.data;
|
|
50913
|
+
}
|
|
50914
|
+
const colIndexes = this.getColumnsIndexes();
|
|
50915
|
+
const rowIndexes = this.getRowsIndexes();
|
|
50916
|
+
const columns = this.buildColumnsMissing(colIndexes);
|
|
50917
|
+
const rows = this.buildRowsMissing(rowIndexes);
|
|
50918
|
+
const values = this.buildValuesMissing(colIndexes, rowIndexes);
|
|
50919
|
+
return { columns, rows, values };
|
|
50920
|
+
}
|
|
50921
|
+
/**
|
|
50922
|
+
* Retrieve the parents of the given row
|
|
50923
|
+
* ex:
|
|
50924
|
+
* Australia
|
|
50925
|
+
* January
|
|
50926
|
+
* February
|
|
50927
|
+
* The parent of "January" is "Australia"
|
|
50928
|
+
*/
|
|
50929
|
+
addRecursiveRow(index) {
|
|
50930
|
+
const rows = this.pivot.getTableStructure().rows;
|
|
50931
|
+
const row = [...rows[index].values];
|
|
50932
|
+
if (row.length <= 1) {
|
|
50933
|
+
return [index];
|
|
50934
|
+
}
|
|
50935
|
+
row.pop();
|
|
50936
|
+
const parentRowIndex = rows.findIndex((r) => JSON.stringify(r.values) === JSON.stringify(row));
|
|
50937
|
+
return [index].concat(this.addRecursiveRow(parentRowIndex));
|
|
50938
|
+
}
|
|
50939
|
+
/**
|
|
50940
|
+
* Create the columns to be used, based on the indexes of the columns in
|
|
50941
|
+
* which a missing value is present
|
|
50942
|
+
*
|
|
50943
|
+
*/
|
|
50944
|
+
buildColumnsMissing(indexes) {
|
|
50945
|
+
// columnsMap explode the columns in an array of array of the same
|
|
50946
|
+
// size with the index of each column, repeated 'span' times.
|
|
50947
|
+
// ex:
|
|
50948
|
+
// | A | B |
|
|
50949
|
+
// | 1 | 2 | 3 |
|
|
50950
|
+
// => [
|
|
50951
|
+
// [0, 0, 1]
|
|
50952
|
+
// [0, 1, 2]
|
|
50953
|
+
// ]
|
|
50954
|
+
const columnsMap = [];
|
|
50955
|
+
for (const column of this.data.columns) {
|
|
50956
|
+
const columnMap = [];
|
|
50957
|
+
for (const index in column) {
|
|
50958
|
+
for (let i = 0; i < column[index].span; i++) {
|
|
50959
|
+
columnMap.push(parseInt(index, 10));
|
|
50960
|
+
}
|
|
50961
|
+
}
|
|
50962
|
+
columnsMap.push(columnMap);
|
|
50963
|
+
}
|
|
50964
|
+
// Remove the columns that are not present in indexes
|
|
50965
|
+
for (let i = columnsMap[columnsMap.length - 1].length; i >= 0; i--) {
|
|
50966
|
+
if (!indexes.includes(i)) {
|
|
50967
|
+
for (const columnMap of columnsMap) {
|
|
50968
|
+
columnMap.splice(i, 1);
|
|
50969
|
+
}
|
|
50970
|
+
}
|
|
50971
|
+
}
|
|
50972
|
+
// Build the columns
|
|
50973
|
+
const columns = [];
|
|
50974
|
+
for (const mapIndex in columnsMap) {
|
|
50975
|
+
const column = [];
|
|
50976
|
+
let index = undefined;
|
|
50977
|
+
let span = 1;
|
|
50978
|
+
for (let i = 0; i < columnsMap[mapIndex].length; i++) {
|
|
50979
|
+
if (index !== columnsMap[mapIndex][i]) {
|
|
50980
|
+
if (index !== undefined) {
|
|
50981
|
+
column.push(Object.assign({}, this.data.columns[mapIndex][index], { span }));
|
|
50982
|
+
}
|
|
50983
|
+
index = columnsMap[mapIndex][i];
|
|
50984
|
+
span = 1;
|
|
50985
|
+
}
|
|
50986
|
+
else {
|
|
50987
|
+
span++;
|
|
50988
|
+
}
|
|
50989
|
+
}
|
|
50990
|
+
if (index !== undefined) {
|
|
50991
|
+
column.push(Object.assign({}, this.data.columns[mapIndex][index], { span }));
|
|
50992
|
+
}
|
|
50993
|
+
columns.push(column);
|
|
50994
|
+
}
|
|
50995
|
+
return columns;
|
|
50996
|
+
}
|
|
50997
|
+
/**
|
|
50998
|
+
* Create the rows to be used, based on the indexes of the rows in
|
|
50999
|
+
* which a missing value is present.
|
|
51000
|
+
*/
|
|
51001
|
+
buildRowsMissing(indexes) {
|
|
51002
|
+
return indexes.map((index) => this.data.rows[index]);
|
|
51003
|
+
}
|
|
51004
|
+
/**
|
|
51005
|
+
* Create the value to be used, based on the indexes of the columns and
|
|
51006
|
+
* rows in which a missing value is present.
|
|
51007
|
+
*/
|
|
51008
|
+
buildValuesMissing(colIndexes, rowIndexes) {
|
|
51009
|
+
const values = colIndexes.map(() => []);
|
|
51010
|
+
for (const row of rowIndexes) {
|
|
51011
|
+
for (const col in colIndexes) {
|
|
51012
|
+
values[col].push(this.data.values[colIndexes[col]][row]);
|
|
51013
|
+
}
|
|
51014
|
+
}
|
|
51015
|
+
return values;
|
|
51016
|
+
}
|
|
51017
|
+
getColumnsIndexes() {
|
|
51018
|
+
const indexes = new Set();
|
|
51019
|
+
for (let i = 0; i < this.data.columns.length; i++) {
|
|
51020
|
+
const exploded = [];
|
|
51021
|
+
for (let y = 0; y < this.data.columns[i].length; y++) {
|
|
51022
|
+
for (let x = 0; x < this.data.columns[i][y].span; x++) {
|
|
51023
|
+
exploded.push(this.data.columns[i][y]);
|
|
51024
|
+
}
|
|
51025
|
+
}
|
|
51026
|
+
for (let y = 0; y < exploded.length; y++) {
|
|
51027
|
+
if (exploded[y].isMissing) {
|
|
51028
|
+
indexes.add(y);
|
|
51029
|
+
}
|
|
51030
|
+
}
|
|
51031
|
+
}
|
|
51032
|
+
for (let i = 0; i < this.data.columns[this.data.columns.length - 1].length; i++) {
|
|
51033
|
+
const values = this.data.values[i];
|
|
51034
|
+
if (values.find((x) => x.isMissing)) {
|
|
51035
|
+
indexes.add(i);
|
|
51036
|
+
}
|
|
51037
|
+
}
|
|
51038
|
+
return Array.from(indexes).sort((a, b) => a - b);
|
|
51039
|
+
}
|
|
51040
|
+
getRowsIndexes() {
|
|
51041
|
+
const rowIndexes = new Set();
|
|
51042
|
+
for (let i = 0; i < this.data.rows.length; i++) {
|
|
51043
|
+
if (this.data.rows[i].isMissing) {
|
|
51044
|
+
rowIndexes.add(i);
|
|
51045
|
+
}
|
|
51046
|
+
for (const col of this.data.values) {
|
|
51047
|
+
if (col[i].isMissing) {
|
|
51048
|
+
this.addRecursiveRow(i).forEach((x) => rowIndexes.add(x));
|
|
51049
|
+
}
|
|
51050
|
+
}
|
|
51051
|
+
}
|
|
51052
|
+
return Array.from(rowIndexes).sort((a, b) => a - b);
|
|
51053
|
+
}
|
|
51054
|
+
// ---------------------------------------------------------------------
|
|
51055
|
+
// Data table creation
|
|
51056
|
+
// ---------------------------------------------------------------------
|
|
51057
|
+
_buildColHeaders(id, table) {
|
|
51058
|
+
const headers = [];
|
|
51059
|
+
for (const row of table.columns) {
|
|
51060
|
+
const current = [];
|
|
51061
|
+
for (const cell of row) {
|
|
51062
|
+
const args = [];
|
|
51063
|
+
for (let i = 0; i < cell.fields.length; i++) {
|
|
51064
|
+
args.push({ value: cell.fields[i] }, { value: cell.values[i] });
|
|
51065
|
+
}
|
|
51066
|
+
const domain = this.pivot.parseArgsToPivotDomain(args);
|
|
51067
|
+
const locale = this.env.model.getters.getLocale();
|
|
51068
|
+
if (domain.at(-1)?.field === "measure") {
|
|
51069
|
+
const { value, format } = this.pivot.getPivotMeasureValue(toString(domain.at(-1).value), domain);
|
|
51070
|
+
current.push({
|
|
51071
|
+
formula: `=PIVOT.HEADER(${generatePivotArgs(id, domain).join(",")})`,
|
|
51072
|
+
value: formatValue(value, { format, locale }),
|
|
51073
|
+
span: cell.width,
|
|
51074
|
+
isMissing: !this.tracker?.isHeaderPresent(domain),
|
|
51075
|
+
});
|
|
51076
|
+
}
|
|
51077
|
+
else {
|
|
51078
|
+
const { value, format } = this.pivot.getPivotHeaderValueAndFormat(domain);
|
|
51079
|
+
current.push({
|
|
51080
|
+
formula: `=PIVOT.HEADER(${generatePivotArgs(id, domain).join(",")})`,
|
|
51081
|
+
value: formatValue(value, { format, locale }),
|
|
51082
|
+
span: cell.width,
|
|
51083
|
+
isMissing: !this.tracker?.isHeaderPresent(domain),
|
|
51084
|
+
});
|
|
51085
|
+
}
|
|
51086
|
+
}
|
|
51087
|
+
headers.push(current);
|
|
51088
|
+
}
|
|
51089
|
+
const last = headers[headers.length - 1];
|
|
51090
|
+
headers[headers.length - 1] = last.map((cell) => {
|
|
51091
|
+
if (!cell.isMissing) {
|
|
51092
|
+
cell.style = "color: #756f6f;";
|
|
51093
|
+
}
|
|
51094
|
+
return cell;
|
|
51095
|
+
});
|
|
51096
|
+
return headers;
|
|
51097
|
+
}
|
|
51098
|
+
_buildRowHeaders(id, table) {
|
|
51099
|
+
const headers = [];
|
|
51100
|
+
for (const row of table.rows) {
|
|
51101
|
+
const args = [];
|
|
51102
|
+
for (let i = 0; i < row.fields.length; i++) {
|
|
51103
|
+
args.push({ value: row.fields[i] }, { value: row.values[i] });
|
|
51104
|
+
}
|
|
51105
|
+
const domain = this.pivot.parseArgsToPivotDomain(args);
|
|
51106
|
+
const { value, format } = this.pivot.getPivotHeaderValueAndFormat(domain);
|
|
51107
|
+
const locale = this.env.model.getters.getLocale();
|
|
51108
|
+
const cell = {
|
|
51109
|
+
formula: `=PIVOT.HEADER(${generatePivotArgs(id, domain).join(",")})`,
|
|
51110
|
+
value: formatValue(value, { format, locale }),
|
|
51111
|
+
isMissing: !this.tracker?.isHeaderPresent(domain),
|
|
51112
|
+
};
|
|
51113
|
+
if (row.indent > 1) {
|
|
51114
|
+
cell.style = `padding-left: ${row.indent - 1 * 10}px`;
|
|
51115
|
+
}
|
|
51116
|
+
headers.push(cell);
|
|
51117
|
+
}
|
|
51118
|
+
return headers;
|
|
51119
|
+
}
|
|
51120
|
+
_buildValues(id, table) {
|
|
51121
|
+
const values = [];
|
|
51122
|
+
for (const col of table.columns.at(-1) || []) {
|
|
51123
|
+
const current = [];
|
|
51124
|
+
const measure = toString(col.values[col.values.length - 1]);
|
|
51125
|
+
for (const row of table.rows) {
|
|
51126
|
+
const args = [];
|
|
51127
|
+
for (let i = 0; i < row.fields.length; i++) {
|
|
51128
|
+
args.push({ value: row.fields[i] }, { value: row.values[i] });
|
|
51129
|
+
}
|
|
51130
|
+
for (let i = 0; i < col.fields.length - 1; i++) {
|
|
51131
|
+
args.push({ value: col.fields[i] }, { value: col.values[i] });
|
|
51132
|
+
}
|
|
51133
|
+
const domain = this.pivot.parseArgsToPivotDomain(args);
|
|
51134
|
+
const { value, format } = this.pivot.getPivotCellValueAndFormat(measure, domain);
|
|
51135
|
+
const locale = this.env.model.getters.getLocale();
|
|
51136
|
+
current.push({
|
|
51137
|
+
formula: `=PIVOT.VALUE(${generatePivotArgs(id, domain, measure).join(",")})`,
|
|
51138
|
+
value: formatValue(value, { format, locale }),
|
|
51139
|
+
isMissing: !this.tracker?.isValuePresent(measure, domain),
|
|
51140
|
+
});
|
|
51141
|
+
}
|
|
51142
|
+
values.push(current);
|
|
51143
|
+
}
|
|
51144
|
+
return values;
|
|
51145
|
+
}
|
|
51146
|
+
}
|
|
51147
|
+
|
|
50782
51148
|
/**
|
|
50783
51149
|
* BasePlugin
|
|
50784
51150
|
*
|
|
@@ -54287,7 +54653,7 @@ class RangeAdapter {
|
|
|
54287
54653
|
if (range.sheetId === cmd.sheetId) {
|
|
54288
54654
|
return { changeType: "CHANGE", range };
|
|
54289
54655
|
}
|
|
54290
|
-
if (
|
|
54656
|
+
if (isSheetNameEqual(range.invalidSheetName, cmd.name)) {
|
|
54291
54657
|
const invalidSheetName = undefined;
|
|
54292
54658
|
const sheetId = cmd.sheetId;
|
|
54293
54659
|
const newRange = range.clone({ sheetId, invalidSheetName });
|
|
@@ -54872,7 +55238,7 @@ class SheetPlugin extends CorePlugin {
|
|
|
54872
55238
|
if (name) {
|
|
54873
55239
|
const unquotedName = getUnquotedSheetName(name);
|
|
54874
55240
|
for (const key in this.sheetIdsMapName) {
|
|
54875
|
-
if (key
|
|
55241
|
+
if (isSheetNameEqual(key, unquotedName)) {
|
|
54876
55242
|
return this.sheetIdsMapName[key];
|
|
54877
55243
|
}
|
|
54878
55244
|
}
|
|
@@ -55120,7 +55486,7 @@ class SheetPlugin extends CorePlugin {
|
|
|
55120
55486
|
}
|
|
55121
55487
|
const { orderedSheetIds, sheets } = this;
|
|
55122
55488
|
const name = cmd.name && cmd.name.trim().toLowerCase();
|
|
55123
|
-
if (orderedSheetIds.find((id) => sheets[id]?.name
|
|
55489
|
+
if (orderedSheetIds.find((id) => isSheetNameEqual(sheets[id]?.name, name) && id !== cmd.sheetId)) {
|
|
55124
55490
|
return "DuplicatedSheetName" /* CommandResult.DuplicatedSheetName */;
|
|
55125
55491
|
}
|
|
55126
55492
|
if (FORBIDDEN_SHEETNAME_CHARS_IN_EXCEL_REGEX.test(name)) {
|
|
@@ -64007,6 +64373,55 @@ class HistoryPlugin extends UIPlugin {
|
|
|
64007
64373
|
}
|
|
64008
64374
|
}
|
|
64009
64375
|
|
|
64376
|
+
class PivotPresenceTracker {
|
|
64377
|
+
trackedValues = new Set();
|
|
64378
|
+
domainToArray(domain) {
|
|
64379
|
+
return domain.flatMap((node) => [node.field, toString(node.value)]);
|
|
64380
|
+
}
|
|
64381
|
+
isValuePresent(measure, domain) {
|
|
64382
|
+
const key = JSON.stringify({ measure, domain: this.domainToArray(domain) });
|
|
64383
|
+
return this.trackedValues.has(key);
|
|
64384
|
+
}
|
|
64385
|
+
isHeaderPresent(domain) {
|
|
64386
|
+
const key = JSON.stringify({ domain: this.domainToArray(domain) });
|
|
64387
|
+
return this.trackedValues.has(key);
|
|
64388
|
+
}
|
|
64389
|
+
trackValue(measure, domain) {
|
|
64390
|
+
const key = JSON.stringify({ measure, domain: this.domainToArray(domain) });
|
|
64391
|
+
this.trackedValues.add(key);
|
|
64392
|
+
}
|
|
64393
|
+
trackHeader(domain) {
|
|
64394
|
+
const key = JSON.stringify({ domain: this.domainToArray(domain) });
|
|
64395
|
+
this.trackedValues.add(key);
|
|
64396
|
+
}
|
|
64397
|
+
}
|
|
64398
|
+
|
|
64399
|
+
class PivotPresencePlugin extends UIPlugin {
|
|
64400
|
+
static getters = ["getPivotPresenceTracker"];
|
|
64401
|
+
trackPresencePivotId;
|
|
64402
|
+
tracker;
|
|
64403
|
+
handle(cmd) {
|
|
64404
|
+
switch (cmd.type) {
|
|
64405
|
+
case "PIVOT_START_PRESENCE_TRACKING":
|
|
64406
|
+
this.tracker = new PivotPresenceTracker();
|
|
64407
|
+
this.trackPresencePivotId = cmd.pivotId;
|
|
64408
|
+
break;
|
|
64409
|
+
case "PIVOT_STOP_PRESENCE_TRACKING":
|
|
64410
|
+
this.trackPresencePivotId = undefined;
|
|
64411
|
+
break;
|
|
64412
|
+
}
|
|
64413
|
+
}
|
|
64414
|
+
getPivotPresenceTracker(pivotId) {
|
|
64415
|
+
if (this.trackPresencePivotId !== pivotId) {
|
|
64416
|
+
return undefined;
|
|
64417
|
+
}
|
|
64418
|
+
if (!this.tracker) {
|
|
64419
|
+
throw new Error("Tracker not initialized");
|
|
64420
|
+
}
|
|
64421
|
+
return this.tracker;
|
|
64422
|
+
}
|
|
64423
|
+
}
|
|
64424
|
+
|
|
64010
64425
|
class SplitToColumnsPlugin extends UIPlugin {
|
|
64011
64426
|
static getters = ["getAutomaticSeparator"];
|
|
64012
64427
|
allowDispatch(cmd) {
|
|
@@ -66790,6 +67205,7 @@ const featurePluginRegistry = new Registry()
|
|
|
66790
67205
|
.add("automatic_sum", AutomaticSumPlugin)
|
|
66791
67206
|
.add("format", FormatPlugin)
|
|
66792
67207
|
.add("insert_pivot", InsertPivotPlugin)
|
|
67208
|
+
.add("pivot_presence", PivotPresencePlugin)
|
|
66793
67209
|
.add("split_to_columns", SplitToColumnsPlugin)
|
|
66794
67210
|
.add("collaborative", CollaborativePlugin)
|
|
66795
67211
|
.add("history", HistoryPlugin)
|
|
@@ -67169,11 +67585,11 @@ class BottomBarSheet extends owl.Component {
|
|
|
67169
67585
|
if (ev.key === "Enter") {
|
|
67170
67586
|
ev.preventDefault();
|
|
67171
67587
|
this.stopEdition();
|
|
67172
|
-
this.DOMFocusableElementStore.
|
|
67588
|
+
this.DOMFocusableElementStore.focus();
|
|
67173
67589
|
}
|
|
67174
67590
|
if (ev.key === "Escape") {
|
|
67175
67591
|
this.cancelEdition();
|
|
67176
|
-
this.DOMFocusableElementStore.
|
|
67592
|
+
this.DOMFocusableElementStore.focus();
|
|
67177
67593
|
}
|
|
67178
67594
|
}
|
|
67179
67595
|
onMouseEventSheetName(ev) {
|
|
@@ -73783,6 +74199,7 @@ const components = {
|
|
|
73783
74199
|
PivotDimensionOrder,
|
|
73784
74200
|
PivotDimension,
|
|
73785
74201
|
PivotLayoutConfigurator,
|
|
74202
|
+
PivotHTMLRenderer,
|
|
73786
74203
|
EditableName,
|
|
73787
74204
|
PivotDeferUpdate,
|
|
73788
74205
|
PivotTitleSection,
|
|
@@ -73876,6 +74293,6 @@ exports.tokenColors = tokenColors;
|
|
|
73876
74293
|
exports.tokenize = tokenize;
|
|
73877
74294
|
|
|
73878
74295
|
|
|
73879
|
-
__info__.version = "18.0.
|
|
73880
|
-
__info__.date = "2025-05-
|
|
73881
|
-
__info__.hash = "
|
|
74296
|
+
__info__.version = "18.0.28";
|
|
74297
|
+
__info__.date = "2025-05-13T17:53:12.402Z";
|
|
74298
|
+
__info__.hash = "b3088aa";
|