@odoo/o-spreadsheet 18.1.0-alpha.0 → 18.1.0-alpha.2
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 +1455 -883
- package/dist/o-spreadsheet.d.ts +343 -255
- package/dist/o-spreadsheet.esm.js +1455 -883
- package/dist/o-spreadsheet.iife.js +1455 -883
- package/dist/o-spreadsheet.iife.min.js +586 -558
- package/dist/o_spreadsheet.xml +308 -240
- package/package.json +12 -12
|
@@ -2,15 +2,121 @@
|
|
|
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.0-alpha.
|
|
6
|
-
* @date 2024-
|
|
7
|
-
* @hash
|
|
5
|
+
* @version 18.1.0-alpha.2
|
|
6
|
+
* @date 2024-10-24T08:53:21.828Z
|
|
7
|
+
* @hash 2a01250
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
'use strict';
|
|
11
11
|
|
|
12
12
|
var owl = require('@odoo/owl');
|
|
13
13
|
|
|
14
|
+
function createActions(menuItems) {
|
|
15
|
+
return menuItems.map(createAction).sort((a, b) => a.sequence - b.sequence);
|
|
16
|
+
}
|
|
17
|
+
let nextItemId = 1;
|
|
18
|
+
function createAction(item) {
|
|
19
|
+
const name = item.name;
|
|
20
|
+
const children = item.children;
|
|
21
|
+
const description = item.description;
|
|
22
|
+
const icon = item.icon;
|
|
23
|
+
const secondaryIcon = item.secondaryIcon;
|
|
24
|
+
const itemId = item.id || nextItemId++;
|
|
25
|
+
return {
|
|
26
|
+
id: itemId.toString(),
|
|
27
|
+
name: typeof name === "function" ? name : () => name,
|
|
28
|
+
isVisible: item.isVisible ? item.isVisible : () => true,
|
|
29
|
+
isEnabled: item.isEnabled ? item.isEnabled : () => true,
|
|
30
|
+
isActive: item.isActive,
|
|
31
|
+
execute: item.execute,
|
|
32
|
+
children: children
|
|
33
|
+
? (env) => {
|
|
34
|
+
return children
|
|
35
|
+
.map((child) => (typeof child === "function" ? child(env) : child))
|
|
36
|
+
.flat()
|
|
37
|
+
.map(createAction);
|
|
38
|
+
}
|
|
39
|
+
: () => [],
|
|
40
|
+
isReadonlyAllowed: item.isReadonlyAllowed || false,
|
|
41
|
+
separator: item.separator || false,
|
|
42
|
+
icon: typeof icon === "function" ? icon : () => icon || "",
|
|
43
|
+
iconColor: item.iconColor,
|
|
44
|
+
secondaryIcon: typeof secondaryIcon === "function" ? secondaryIcon : () => secondaryIcon || "",
|
|
45
|
+
description: typeof description === "function" ? description : () => description || "",
|
|
46
|
+
textColor: item.textColor,
|
|
47
|
+
sequence: item.sequence || 0,
|
|
48
|
+
onStartHover: item.onStartHover,
|
|
49
|
+
onStopHover: item.onStopHover,
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Registry
|
|
55
|
+
*
|
|
56
|
+
* The Registry class is basically just a mapping from a string key to an object.
|
|
57
|
+
* It is really not much more than an object. It is however useful for the
|
|
58
|
+
* following reasons:
|
|
59
|
+
*
|
|
60
|
+
* 1. it let us react and execute code when someone add something to the registry
|
|
61
|
+
* (for example, the FunctionRegistry subclass this for this purpose)
|
|
62
|
+
* 2. it throws an error when the get operation fails
|
|
63
|
+
* 3. it provides a chained API to add items to the registry.
|
|
64
|
+
*/
|
|
65
|
+
class Registry {
|
|
66
|
+
content = {};
|
|
67
|
+
/**
|
|
68
|
+
* Add an item to the registry
|
|
69
|
+
*
|
|
70
|
+
* Note that this also returns the registry, so another add method call can
|
|
71
|
+
* be chained
|
|
72
|
+
*/
|
|
73
|
+
add(key, value) {
|
|
74
|
+
this.content[key] = value;
|
|
75
|
+
return this;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Get an item from the registry
|
|
79
|
+
*/
|
|
80
|
+
get(key) {
|
|
81
|
+
/**
|
|
82
|
+
* Note: key in {} is ~12 times slower than {}[key].
|
|
83
|
+
* So, we check the absence of key only when the direct access returns
|
|
84
|
+
* a falsy value. It's done to ensure that the registry can contains falsy values
|
|
85
|
+
*/
|
|
86
|
+
const content = this.content[key];
|
|
87
|
+
if (!content) {
|
|
88
|
+
if (!(key in this.content)) {
|
|
89
|
+
throw new Error(`Cannot find ${key} in this registry!`);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
return content;
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Check if the key is already in the registry
|
|
96
|
+
*/
|
|
97
|
+
contains(key) {
|
|
98
|
+
return key in this.content;
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Get a list of all elements in the registry
|
|
102
|
+
*/
|
|
103
|
+
getAll() {
|
|
104
|
+
return Object.values(this.content);
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Get a list of all keys in the registry
|
|
108
|
+
*/
|
|
109
|
+
getKeys() {
|
|
110
|
+
return Object.keys(this.content);
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Remove an item from the registry
|
|
114
|
+
*/
|
|
115
|
+
remove(key) {
|
|
116
|
+
delete this.content[key];
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
14
120
|
const CANVAS_SHIFT = 0.5;
|
|
15
121
|
// Colors
|
|
16
122
|
const HIGHLIGHT_COLOR = "#37A850";
|
|
@@ -225,6 +331,7 @@ const DEFAULT_SCORECARD_BASELINE_MODE = "difference";
|
|
|
225
331
|
const DEFAULT_SCORECARD_BASELINE_COLOR_UP = "#43C5B1";
|
|
226
332
|
const DEFAULT_SCORECARD_BASELINE_COLOR_DOWN = "#EA6175";
|
|
227
333
|
const LINE_FILL_TRANSPARENCY = 0.4;
|
|
334
|
+
const DEFAULT_WINDOW_SIZE = 2;
|
|
228
335
|
// session
|
|
229
336
|
const DEBOUNCE_TIME = 200;
|
|
230
337
|
const MESSAGE_VERSION = 1;
|
|
@@ -270,7 +377,7 @@ const PIVOT_TABLE_CONFIG = {
|
|
|
270
377
|
bandedRows: true,
|
|
271
378
|
bandedColumns: false,
|
|
272
379
|
styleId: "TableStyleMedium5",
|
|
273
|
-
automaticAutofill:
|
|
380
|
+
automaticAutofill: false,
|
|
274
381
|
};
|
|
275
382
|
const DEFAULT_CURRENCY = {
|
|
276
383
|
symbol: "$",
|
|
@@ -486,7 +593,7 @@ function buildSheetLink(sheetId) {
|
|
|
486
593
|
*/
|
|
487
594
|
function parseSheetUrl(sheetLink) {
|
|
488
595
|
if (sheetLink.startsWith(O_SPREADSHEET_LINK_PREFIX)) {
|
|
489
|
-
return sheetLink.
|
|
596
|
+
return sheetLink.slice(O_SPREADSHEET_LINK_PREFIX.length);
|
|
490
597
|
}
|
|
491
598
|
throw new Error(`${sheetLink} is not a valid sheet link`);
|
|
492
599
|
}
|
|
@@ -3051,8 +3158,7 @@ const getNumberRegex = memoize(function getNumberRegex(locale) {
|
|
|
3051
3158
|
const p2 = pMinus + pNumber + pCurrencyFormat;
|
|
3052
3159
|
const p3 = pCurrencyFormat + pMinus + pNumber;
|
|
3053
3160
|
const pNumberExp = "^(?:(?:" + [p1, p2, p3].join(")|(?:") + "))$";
|
|
3054
|
-
|
|
3055
|
-
return numberRegexp;
|
|
3161
|
+
return new RegExp(pNumberExp, "i");
|
|
3056
3162
|
});
|
|
3057
3163
|
/**
|
|
3058
3164
|
* Return true if the argument is a "number string".
|
|
@@ -5726,8 +5832,7 @@ function computeCachedTextWidth(context, text) {
|
|
|
5726
5832
|
textWidthCache[font] = {};
|
|
5727
5833
|
}
|
|
5728
5834
|
if (textWidthCache[font][text] === undefined) {
|
|
5729
|
-
|
|
5730
|
-
textWidthCache[font][text] = textWidth;
|
|
5835
|
+
textWidthCache[font][text] = context.measureText(text).width;
|
|
5731
5836
|
}
|
|
5732
5837
|
return textWidthCache[font][text];
|
|
5733
5838
|
}
|
|
@@ -5970,136 +6075,22 @@ function drawDecoratedText(context, text, position, underline = false, strikethr
|
|
|
5970
6075
|
* https://stackoverflow.com/questions/105034/create-guid-uuid-in-javascript
|
|
5971
6076
|
* */
|
|
5972
6077
|
class UuidGenerator {
|
|
5973
|
-
isFastIdStrategy = false;
|
|
5974
|
-
fastIdStart = 0;
|
|
5975
|
-
setIsFastStrategy(isFast) {
|
|
5976
|
-
this.isFastIdStrategy = isFast;
|
|
5977
|
-
}
|
|
5978
6078
|
uuidv4() {
|
|
5979
|
-
|
|
5980
|
-
|
|
5981
|
-
return String(this.fastIdStart);
|
|
5982
|
-
//@ts-ignore
|
|
5983
|
-
}
|
|
5984
|
-
else if (window.crypto && window.crypto.getRandomValues) {
|
|
6079
|
+
//@ts-ignore
|
|
6080
|
+
if (window.crypto && window.crypto.getRandomValues) {
|
|
5985
6081
|
//@ts-ignore
|
|
5986
6082
|
return ([1e7] + -1e3 + -4e3 + -8e3 + -1e11).replace(/[018]/g, (c) => (c ^ (crypto.getRandomValues(new Uint8Array(1))[0] & (15 >> (c / 4)))).toString(16));
|
|
5987
6083
|
}
|
|
5988
6084
|
else {
|
|
5989
6085
|
// mainly for jest and other browsers that do not have the crypto functionality
|
|
5990
6086
|
return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function (c) {
|
|
5991
|
-
|
|
6087
|
+
const r = (Math.random() * 16) | 0, v = c === "x" ? r : (r & 0x3) | 0x8;
|
|
5992
6088
|
return v.toString(16);
|
|
5993
6089
|
});
|
|
5994
6090
|
}
|
|
5995
6091
|
}
|
|
5996
6092
|
}
|
|
5997
6093
|
|
|
5998
|
-
function createActions(menuItems) {
|
|
5999
|
-
return menuItems.map(createAction).sort((a, b) => a.sequence - b.sequence);
|
|
6000
|
-
}
|
|
6001
|
-
const uuidGenerator$1 = new UuidGenerator();
|
|
6002
|
-
function createAction(item) {
|
|
6003
|
-
const name = item.name;
|
|
6004
|
-
const children = item.children;
|
|
6005
|
-
const description = item.description;
|
|
6006
|
-
const icon = item.icon;
|
|
6007
|
-
const secondaryIcon = item.secondaryIcon;
|
|
6008
|
-
return {
|
|
6009
|
-
id: item.id || uuidGenerator$1.uuidv4(),
|
|
6010
|
-
name: typeof name === "function" ? name : () => name,
|
|
6011
|
-
isVisible: item.isVisible ? item.isVisible : () => true,
|
|
6012
|
-
isEnabled: item.isEnabled ? item.isEnabled : () => true,
|
|
6013
|
-
isActive: item.isActive,
|
|
6014
|
-
execute: item.execute,
|
|
6015
|
-
children: children
|
|
6016
|
-
? (env) => {
|
|
6017
|
-
return children
|
|
6018
|
-
.map((child) => (typeof child === "function" ? child(env) : child))
|
|
6019
|
-
.flat()
|
|
6020
|
-
.map(createAction);
|
|
6021
|
-
}
|
|
6022
|
-
: () => [],
|
|
6023
|
-
isReadonlyAllowed: item.isReadonlyAllowed || false,
|
|
6024
|
-
separator: item.separator || false,
|
|
6025
|
-
icon: typeof icon === "function" ? icon : () => icon || "",
|
|
6026
|
-
iconColor: item.iconColor,
|
|
6027
|
-
secondaryIcon: typeof secondaryIcon === "function" ? secondaryIcon : () => secondaryIcon || "",
|
|
6028
|
-
description: typeof description === "function" ? description : () => description || "",
|
|
6029
|
-
textColor: item.textColor,
|
|
6030
|
-
sequence: item.sequence || 0,
|
|
6031
|
-
onStartHover: item.onStartHover,
|
|
6032
|
-
onStopHover: item.onStopHover,
|
|
6033
|
-
};
|
|
6034
|
-
}
|
|
6035
|
-
|
|
6036
|
-
/**
|
|
6037
|
-
* Registry
|
|
6038
|
-
*
|
|
6039
|
-
* The Registry class is basically just a mapping from a string key to an object.
|
|
6040
|
-
* It is really not much more than an object. It is however useful for the
|
|
6041
|
-
* following reasons:
|
|
6042
|
-
*
|
|
6043
|
-
* 1. it let us react and execute code when someone add something to the registry
|
|
6044
|
-
* (for example, the FunctionRegistry subclass this for this purpose)
|
|
6045
|
-
* 2. it throws an error when the get operation fails
|
|
6046
|
-
* 3. it provides a chained API to add items to the registry.
|
|
6047
|
-
*/
|
|
6048
|
-
class Registry {
|
|
6049
|
-
content = {};
|
|
6050
|
-
/**
|
|
6051
|
-
* Add an item to the registry
|
|
6052
|
-
*
|
|
6053
|
-
* Note that this also returns the registry, so another add method call can
|
|
6054
|
-
* be chained
|
|
6055
|
-
*/
|
|
6056
|
-
add(key, value) {
|
|
6057
|
-
this.content[key] = value;
|
|
6058
|
-
return this;
|
|
6059
|
-
}
|
|
6060
|
-
/**
|
|
6061
|
-
* Get an item from the registry
|
|
6062
|
-
*/
|
|
6063
|
-
get(key) {
|
|
6064
|
-
/**
|
|
6065
|
-
* Note: key in {} is ~12 times slower than {}[key].
|
|
6066
|
-
* So, we check the absence of key only when the direct access returns
|
|
6067
|
-
* a falsy value. It's done to ensure that the registry can contains falsy values
|
|
6068
|
-
*/
|
|
6069
|
-
const content = this.content[key];
|
|
6070
|
-
if (!content) {
|
|
6071
|
-
if (!(key in this.content)) {
|
|
6072
|
-
throw new Error(`Cannot find ${key} in this registry!`);
|
|
6073
|
-
}
|
|
6074
|
-
}
|
|
6075
|
-
return content;
|
|
6076
|
-
}
|
|
6077
|
-
/**
|
|
6078
|
-
* Check if the key is already in the registry
|
|
6079
|
-
*/
|
|
6080
|
-
contains(key) {
|
|
6081
|
-
return key in this.content;
|
|
6082
|
-
}
|
|
6083
|
-
/**
|
|
6084
|
-
* Get a list of all elements in the registry
|
|
6085
|
-
*/
|
|
6086
|
-
getAll() {
|
|
6087
|
-
return Object.values(this.content);
|
|
6088
|
-
}
|
|
6089
|
-
/**
|
|
6090
|
-
* Get a list of all keys in the registry
|
|
6091
|
-
*/
|
|
6092
|
-
getKeys() {
|
|
6093
|
-
return Object.keys(this.content);
|
|
6094
|
-
}
|
|
6095
|
-
/**
|
|
6096
|
-
* Remove an item from the registry
|
|
6097
|
-
*/
|
|
6098
|
-
remove(key) {
|
|
6099
|
-
delete this.content[key];
|
|
6100
|
-
}
|
|
6101
|
-
}
|
|
6102
|
-
|
|
6103
6094
|
function getClipboardDataPositions(sheetId, zones) {
|
|
6104
6095
|
const lefts = new Set(zones.map((z) => z.left));
|
|
6105
6096
|
const rights = new Set(zones.map((z) => z.right));
|
|
@@ -6590,10 +6581,9 @@ function localizeNumberLiteral(literal, locale) {
|
|
|
6590
6581
|
return literal;
|
|
6591
6582
|
}
|
|
6592
6583
|
const decimalNumberRegex = getDecimalNumberRegex(DEFAULT_LOCALE);
|
|
6593
|
-
|
|
6584
|
+
return literal.replace(decimalNumberRegex, (match) => {
|
|
6594
6585
|
return match.replace(".", locale.decimalSeparator);
|
|
6595
6586
|
});
|
|
6596
|
-
return localized;
|
|
6597
6587
|
}
|
|
6598
6588
|
/**
|
|
6599
6589
|
* Change a literal string from its canonical form (en_US locale) to the given locale. Also convert date string.
|
|
@@ -7078,6 +7068,21 @@ function predictLinearValues(Y, X, newX, computeIntercept) {
|
|
|
7078
7068
|
});
|
|
7079
7069
|
return newY.length === newX.length ? newY : transposeMatrix(newY);
|
|
7080
7070
|
}
|
|
7071
|
+
function getMovingAverageValues(dataset, windowSize = DEFAULT_WINDOW_SIZE) {
|
|
7072
|
+
const values = [];
|
|
7073
|
+
// Fill the starting values with null until we have a full window
|
|
7074
|
+
for (let i = 0; i < windowSize - 1; i++) {
|
|
7075
|
+
values.push(null);
|
|
7076
|
+
}
|
|
7077
|
+
for (let i = 0; i <= dataset.length - windowSize; i++) {
|
|
7078
|
+
let sum = 0;
|
|
7079
|
+
for (let j = i; j < i + windowSize; j++) {
|
|
7080
|
+
sum += dataset[j];
|
|
7081
|
+
}
|
|
7082
|
+
values.push(sum / windowSize);
|
|
7083
|
+
}
|
|
7084
|
+
return values;
|
|
7085
|
+
}
|
|
7081
7086
|
|
|
7082
7087
|
const PREVIOUS_VALUE = "(previous)";
|
|
7083
7088
|
const NEXT_VALUE = "(next)";
|
|
@@ -7613,8 +7618,7 @@ function getMaxObjectId(o) {
|
|
|
7613
7618
|
return 0;
|
|
7614
7619
|
}
|
|
7615
7620
|
const nums = keys.map((id) => parseInt(id, 10));
|
|
7616
|
-
|
|
7617
|
-
return max;
|
|
7621
|
+
return Math.max(...nums);
|
|
7618
7622
|
}
|
|
7619
7623
|
const ALL_PERIODS = {
|
|
7620
7624
|
year: _t("Year"),
|
|
@@ -8454,31 +8458,30 @@ class TableClipboardHandler extends AbstractCellClipboardHandler {
|
|
|
8454
8458
|
for (let col of columnsIndexes) {
|
|
8455
8459
|
const position = { col, row, sheetId };
|
|
8456
8460
|
const table = this.getters.getTable(position);
|
|
8457
|
-
if (!table
|
|
8461
|
+
if (!table) {
|
|
8458
8462
|
tableCellsInRow.push({});
|
|
8459
8463
|
continue;
|
|
8460
8464
|
}
|
|
8461
8465
|
const coreTable = this.getters.getCoreTable(position);
|
|
8462
8466
|
const tableZone = coreTable?.range.zone;
|
|
8467
|
+
let copiedTable = undefined;
|
|
8463
8468
|
// Copy whole table
|
|
8464
|
-
if (
|
|
8465
|
-
|
|
8466
|
-
|
|
8467
|
-
|
|
8468
|
-
|
|
8469
|
-
|
|
8470
|
-
|
|
8471
|
-
|
|
8472
|
-
|
|
8473
|
-
|
|
8474
|
-
type: coreTable.type,
|
|
8475
|
-
},
|
|
8476
|
-
});
|
|
8477
|
-
}
|
|
8478
|
-
// Copy only style of cell
|
|
8479
|
-
else if (table) {
|
|
8480
|
-
tableCellsInRow.push({ style: this.getTableStyleToCopy(position) });
|
|
8469
|
+
if (!copiedTablesIds.has(table.id) &&
|
|
8470
|
+
coreTable &&
|
|
8471
|
+
tableZone &&
|
|
8472
|
+
zones.some((z) => isZoneInside(tableZone, z))) {
|
|
8473
|
+
copiedTablesIds.add(table.id);
|
|
8474
|
+
copiedTable = {
|
|
8475
|
+
range: coreTable.range.rangeData,
|
|
8476
|
+
config: coreTable.config,
|
|
8477
|
+
type: coreTable.type,
|
|
8478
|
+
};
|
|
8481
8479
|
}
|
|
8480
|
+
tableCellsInRow.push({
|
|
8481
|
+
table: copiedTable,
|
|
8482
|
+
style: this.getTableStyleToCopy(position),
|
|
8483
|
+
isWholeTableCopied: copiedTablesIds.has(table.id),
|
|
8484
|
+
});
|
|
8482
8485
|
}
|
|
8483
8486
|
}
|
|
8484
8487
|
return {
|
|
@@ -8559,11 +8562,14 @@ class TableClipboardHandler extends AbstractCellClipboardHandler {
|
|
|
8559
8562
|
tableType: tableCell.table.type,
|
|
8560
8563
|
});
|
|
8561
8564
|
}
|
|
8562
|
-
// Do not paste table style if we're inside another table
|
|
8563
8565
|
// We cannot check for dynamic tables, because at this point the paste can have changed the evaluation, and the
|
|
8564
8566
|
// dynamic tables are not yet computed
|
|
8565
|
-
if (
|
|
8566
|
-
|
|
8567
|
+
if (this.getters.getCoreTable(position) || options?.pasteOption === "asValue") {
|
|
8568
|
+
return;
|
|
8569
|
+
}
|
|
8570
|
+
if ((!options?.pasteOption && !tableCell.isWholeTableCopied) ||
|
|
8571
|
+
options?.pasteOption === "onlyFormat") {
|
|
8572
|
+
if (tableCell.style?.style) {
|
|
8567
8573
|
this.dispatch("UPDATE_CELL", { ...position, style: tableCell.style.style });
|
|
8568
8574
|
}
|
|
8569
8575
|
if (tableCell.style?.border) {
|
|
@@ -9245,11 +9251,10 @@ function getChartPositionAtCenterOfViewport(getters, chartSize) {
|
|
|
9245
9251
|
const { x, y } = getters.getMainViewportCoordinates();
|
|
9246
9252
|
const { scrollX, scrollY } = getters.getActiveSheetScrollInfo();
|
|
9247
9253
|
const { width, height } = getters.getVisibleRect(getters.getActiveMainViewport());
|
|
9248
|
-
|
|
9254
|
+
return {
|
|
9249
9255
|
x: x + scrollX + Math.max(0, (width - chartSize.width) / 2),
|
|
9250
9256
|
y: y + scrollY + Math.max(0, (height - chartSize.height) / 2),
|
|
9251
9257
|
}; // Position at the center of the scrollable viewport
|
|
9252
|
-
return position;
|
|
9253
9258
|
}
|
|
9254
9259
|
function getChartAxisTitleRuntime(design) {
|
|
9255
9260
|
if (design?.title?.text) {
|
|
@@ -9283,6 +9288,49 @@ function getDefinedAxis(definition) {
|
|
|
9283
9288
|
useLeftAxis ||= !useRightAxis;
|
|
9284
9289
|
return { useLeftAxis, useRightAxis };
|
|
9285
9290
|
}
|
|
9291
|
+
function getChartAxis(definition, position, type, options) {
|
|
9292
|
+
const { useLeftAxis, useRightAxis } = getDefinedAxis(definition);
|
|
9293
|
+
if ((position === "left" && !useLeftAxis) || (position === "right" && !useRightAxis)) {
|
|
9294
|
+
return undefined;
|
|
9295
|
+
}
|
|
9296
|
+
const fontColor = chartFontColor(definition.background);
|
|
9297
|
+
let design;
|
|
9298
|
+
if (position === "bottom") {
|
|
9299
|
+
design = definition.axesDesign?.x;
|
|
9300
|
+
}
|
|
9301
|
+
else if (position === "left") {
|
|
9302
|
+
design = definition.axesDesign?.y;
|
|
9303
|
+
}
|
|
9304
|
+
else {
|
|
9305
|
+
design = definition.axesDesign?.y1;
|
|
9306
|
+
}
|
|
9307
|
+
if (type === "values") {
|
|
9308
|
+
const displayGridLines = position === "left" || (position === "right" && !useLeftAxis);
|
|
9309
|
+
return {
|
|
9310
|
+
position: position,
|
|
9311
|
+
title: getChartAxisTitleRuntime(design),
|
|
9312
|
+
grid: {
|
|
9313
|
+
display: displayGridLines,
|
|
9314
|
+
},
|
|
9315
|
+
beginAtZero: true,
|
|
9316
|
+
stacked: options?.stacked,
|
|
9317
|
+
ticks: {
|
|
9318
|
+
color: fontColor,
|
|
9319
|
+
callback: formatTickValue(options),
|
|
9320
|
+
},
|
|
9321
|
+
};
|
|
9322
|
+
}
|
|
9323
|
+
else {
|
|
9324
|
+
return {
|
|
9325
|
+
ticks: {
|
|
9326
|
+
padding: 5,
|
|
9327
|
+
color: fontColor,
|
|
9328
|
+
},
|
|
9329
|
+
stacked: options?.stacked,
|
|
9330
|
+
title: getChartAxisTitleRuntime(design),
|
|
9331
|
+
};
|
|
9332
|
+
}
|
|
9333
|
+
}
|
|
9286
9334
|
function computeChartPadding({ displayTitle, displayLegend, }) {
|
|
9287
9335
|
let top = 25;
|
|
9288
9336
|
if (displayTitle) {
|
|
@@ -9323,7 +9371,7 @@ function getFullTrendingLineDataSet(dataset, config, data) {
|
|
|
9323
9371
|
return {
|
|
9324
9372
|
...dataset,
|
|
9325
9373
|
type: "line",
|
|
9326
|
-
xAxisID: TREND_LINE_XAXIS_ID,
|
|
9374
|
+
xAxisID: config.type !== "trailingMovingAverage" ? TREND_LINE_XAXIS_ID : "x",
|
|
9327
9375
|
label: dataset.label ? _t("Trend line for %s", dataset.label) : "",
|
|
9328
9376
|
data,
|
|
9329
9377
|
order: -1,
|
|
@@ -9365,10 +9413,19 @@ function interpolateData(config, values, labels, newLabels) {
|
|
|
9365
9413
|
case "logarithmic": {
|
|
9366
9414
|
return predictLinearValues([values], logM([labels]), logM([newLabels]), true)[0];
|
|
9367
9415
|
}
|
|
9416
|
+
case "trailingMovingAverage": {
|
|
9417
|
+
return getMovingAverageValues(values, config.window);
|
|
9418
|
+
}
|
|
9368
9419
|
default:
|
|
9369
9420
|
return [];
|
|
9370
9421
|
}
|
|
9371
9422
|
}
|
|
9423
|
+
function formatChartDatasetValue(axisFormats, locale) {
|
|
9424
|
+
return (value, axisId) => {
|
|
9425
|
+
const format = axisId ? axisFormats?.[axisId] : undefined;
|
|
9426
|
+
return formatTickValue({ format, locale })(value);
|
|
9427
|
+
};
|
|
9428
|
+
}
|
|
9372
9429
|
function formatTickValue(localeFormat) {
|
|
9373
9430
|
return (value) => {
|
|
9374
9431
|
value = Number(value);
|
|
@@ -9404,70 +9461,115 @@ const chartShowValuesPlugin = {
|
|
|
9404
9461
|
ctx.save();
|
|
9405
9462
|
ctx.textAlign = "center";
|
|
9406
9463
|
ctx.textBaseline = "middle";
|
|
9407
|
-
ctx.
|
|
9408
|
-
|
|
9409
|
-
|
|
9410
|
-
|
|
9411
|
-
|
|
9412
|
-
|
|
9413
|
-
|
|
9414
|
-
|
|
9415
|
-
|
|
9416
|
-
|
|
9417
|
-
|
|
9418
|
-
|
|
9419
|
-
|
|
9420
|
-
const midRadius = (innerRadius + outerRadius) / 2;
|
|
9421
|
-
const x = bar.x + midRadius * Math.cos(midAngle);
|
|
9422
|
-
const y = bar.y + midRadius * Math.sin(midAngle) + 7;
|
|
9423
|
-
ctx.fillStyle = chartFontColor(bar.options.backgroundColor);
|
|
9424
|
-
ctx.strokeStyle = chartFontColor(ctx.fillStyle);
|
|
9425
|
-
const value = options.callback(dataset._parsed[i]);
|
|
9426
|
-
ctx.strokeText(value, x, y);
|
|
9427
|
-
ctx.fillText(value, x, y);
|
|
9428
|
-
}
|
|
9429
|
-
break;
|
|
9430
|
-
}
|
|
9431
|
-
case "bar":
|
|
9432
|
-
case "line": {
|
|
9433
|
-
const yOffset = dataset.type === "bar" && !options.horizontal ? 0 : 3;
|
|
9434
|
-
for (let i = 0; i < dataset._parsed.length; i++) {
|
|
9435
|
-
const point = dataset.data[i];
|
|
9436
|
-
const value = options.horizontal ? dataset._parsed[i].x : dataset._parsed[i].y;
|
|
9437
|
-
const displayedValue = options.callback(value - 0);
|
|
9438
|
-
let xPosition = 0, yPosition = 0;
|
|
9439
|
-
if (options.horizontal) {
|
|
9440
|
-
yPosition = point.y;
|
|
9441
|
-
if (value < 0) {
|
|
9442
|
-
ctx.textAlign = "right";
|
|
9443
|
-
xPosition = point.x - yOffset;
|
|
9444
|
-
}
|
|
9445
|
-
else {
|
|
9446
|
-
ctx.textAlign = "left";
|
|
9447
|
-
xPosition = point.x + yOffset;
|
|
9448
|
-
}
|
|
9449
|
-
}
|
|
9450
|
-
else {
|
|
9451
|
-
xPosition = point.x;
|
|
9452
|
-
if (value < 0) {
|
|
9453
|
-
ctx.textBaseline = "top";
|
|
9454
|
-
yPosition = point.y + yOffset;
|
|
9455
|
-
}
|
|
9456
|
-
else {
|
|
9457
|
-
ctx.textBaseline = "bottom";
|
|
9458
|
-
yPosition = point.y - yOffset;
|
|
9459
|
-
}
|
|
9460
|
-
}
|
|
9461
|
-
ctx.strokeText(displayedValue, xPosition, yPosition);
|
|
9462
|
-
ctx.fillText(displayedValue, xPosition, yPosition);
|
|
9463
|
-
}
|
|
9464
|
-
break;
|
|
9465
|
-
}
|
|
9466
|
-
}
|
|
9467
|
-
});
|
|
9464
|
+
ctx.miterLimit = 1; // Avoid sharp artifacts on strokeText
|
|
9465
|
+
switch (chart.config.type) {
|
|
9466
|
+
case "pie":
|
|
9467
|
+
case "doughnut":
|
|
9468
|
+
drawPieChartValues(chart, options, ctx);
|
|
9469
|
+
break;
|
|
9470
|
+
case "bar":
|
|
9471
|
+
case "line":
|
|
9472
|
+
options.horizontal
|
|
9473
|
+
? drawHorizontalBarChartValues(chart, options, ctx)
|
|
9474
|
+
: drawLineOrBarChartValues(chart, options, ctx);
|
|
9475
|
+
break;
|
|
9476
|
+
}
|
|
9468
9477
|
ctx.restore();
|
|
9469
9478
|
},
|
|
9470
9479
|
};
|
|
9480
|
+
function drawTextWithBackground(text, x, y, ctx) {
|
|
9481
|
+
ctx.lineWidth = 3; // Stroke the text with a big lineWidth width to have some kind of background
|
|
9482
|
+
ctx.strokeText(text, x, y);
|
|
9483
|
+
ctx.lineWidth = 1;
|
|
9484
|
+
ctx.fillText(text, x, y);
|
|
9485
|
+
}
|
|
9486
|
+
function drawLineOrBarChartValues(chart, options, ctx) {
|
|
9487
|
+
const yMax = chart.chartArea.bottom;
|
|
9488
|
+
const yMin = chart.chartArea.top;
|
|
9489
|
+
const textsPositions = {};
|
|
9490
|
+
for (const dataset of chart._metasets) {
|
|
9491
|
+
if (dataset.xAxisID === TREND_LINE_XAXIS_ID) {
|
|
9492
|
+
return; // ignore trend lines
|
|
9493
|
+
}
|
|
9494
|
+
for (let i = 0; i < dataset._parsed.length; i++) {
|
|
9495
|
+
const value = dataset._parsed[i].y;
|
|
9496
|
+
const displayValue = options.callback(value - 0, dataset.yAxisID);
|
|
9497
|
+
const point = dataset.data[i];
|
|
9498
|
+
const xPosition = point.x;
|
|
9499
|
+
let yPosition = 0;
|
|
9500
|
+
if (chart.config.type === "line") {
|
|
9501
|
+
yPosition = point.y - 10;
|
|
9502
|
+
}
|
|
9503
|
+
else {
|
|
9504
|
+
yPosition = value < 0 ? point.y - point.height / 2 : point.y + point.height / 2;
|
|
9505
|
+
}
|
|
9506
|
+
yPosition = Math.min(yPosition, yMax);
|
|
9507
|
+
yPosition = Math.max(yPosition, yMin);
|
|
9508
|
+
// Avoid overlapping texts with same X
|
|
9509
|
+
if (!textsPositions[xPosition]) {
|
|
9510
|
+
textsPositions[xPosition] = [];
|
|
9511
|
+
}
|
|
9512
|
+
for (const otherPosition of textsPositions[xPosition] || []) {
|
|
9513
|
+
if (Math.abs(otherPosition - yPosition) < 13) {
|
|
9514
|
+
yPosition = otherPosition - 13;
|
|
9515
|
+
}
|
|
9516
|
+
}
|
|
9517
|
+
textsPositions[xPosition].push(yPosition);
|
|
9518
|
+
ctx.fillStyle = point.options.backgroundColor;
|
|
9519
|
+
ctx.strokeStyle = options.background || "#ffffff";
|
|
9520
|
+
drawTextWithBackground(displayValue, xPosition, yPosition, ctx);
|
|
9521
|
+
}
|
|
9522
|
+
}
|
|
9523
|
+
}
|
|
9524
|
+
function drawHorizontalBarChartValues(chart, options, ctx) {
|
|
9525
|
+
const xMax = chart.chartArea.right;
|
|
9526
|
+
const xMin = chart.chartArea.left;
|
|
9527
|
+
const textsPositions = {};
|
|
9528
|
+
for (const dataset of chart._metasets) {
|
|
9529
|
+
if (dataset.xAxisID === TREND_LINE_XAXIS_ID) {
|
|
9530
|
+
return; // ignore trend lines
|
|
9531
|
+
}
|
|
9532
|
+
for (let i = 0; i < dataset._parsed.length; i++) {
|
|
9533
|
+
const value = dataset._parsed[i].x;
|
|
9534
|
+
const displayValue = options.callback(value - 0, dataset.xAxisID);
|
|
9535
|
+
const point = dataset.data[i];
|
|
9536
|
+
const yPosition = point.y;
|
|
9537
|
+
let xPosition = value < 0 ? point.x + point.width / 2 : point.x - point.width / 2;
|
|
9538
|
+
xPosition = Math.min(xPosition, xMax);
|
|
9539
|
+
xPosition = Math.max(xPosition, xMin);
|
|
9540
|
+
// Avoid overlapping texts with same Y
|
|
9541
|
+
if (!textsPositions[yPosition]) {
|
|
9542
|
+
textsPositions[yPosition] = [];
|
|
9543
|
+
}
|
|
9544
|
+
const textWidth = computeTextWidth(ctx, displayValue, { fontSize: 12 }, "px");
|
|
9545
|
+
for (const otherPosition of textsPositions[yPosition]) {
|
|
9546
|
+
if (Math.abs(otherPosition - xPosition) < textWidth) {
|
|
9547
|
+
xPosition = otherPosition + textWidth + 3;
|
|
9548
|
+
}
|
|
9549
|
+
}
|
|
9550
|
+
textsPositions[yPosition].push(xPosition);
|
|
9551
|
+
ctx.fillStyle = point.options.backgroundColor;
|
|
9552
|
+
ctx.strokeStyle = options.background || "#ffffff";
|
|
9553
|
+
drawTextWithBackground(displayValue, xPosition, yPosition, ctx);
|
|
9554
|
+
}
|
|
9555
|
+
}
|
|
9556
|
+
}
|
|
9557
|
+
function drawPieChartValues(chart, options, ctx) {
|
|
9558
|
+
for (const dataset of chart._metasets) {
|
|
9559
|
+
for (let i = 0; i < dataset._parsed.length; i++) {
|
|
9560
|
+
const bar = dataset.data[i];
|
|
9561
|
+
const { startAngle, endAngle, innerRadius, outerRadius } = bar;
|
|
9562
|
+
const midAngle = (startAngle + endAngle) / 2;
|
|
9563
|
+
const midRadius = (innerRadius + outerRadius) / 2;
|
|
9564
|
+
const x = bar.x + midRadius * Math.cos(midAngle);
|
|
9565
|
+
const y = bar.y + midRadius * Math.sin(midAngle) + 7;
|
|
9566
|
+
ctx.fillStyle = chartFontColor(options.background);
|
|
9567
|
+
ctx.strokeStyle = options.background || "#ffffff";
|
|
9568
|
+
const value = options.callback(dataset._parsed[i]);
|
|
9569
|
+
drawTextWithBackground(value, x, y, ctx);
|
|
9570
|
+
}
|
|
9571
|
+
}
|
|
9572
|
+
}
|
|
9471
9573
|
|
|
9472
9574
|
/** This is a chartJS plugin that will draw connector lines between the bars of a Waterfall chart */
|
|
9473
9575
|
const waterfallLinesPlugin = {
|
|
@@ -10245,8 +10347,7 @@ function getHtmlContentFromPattern(pattern, value, highlightColor, className) {
|
|
|
10245
10347
|
value = value.slice(index + 1);
|
|
10246
10348
|
}
|
|
10247
10349
|
pendingHtmlContent.push({ value });
|
|
10248
|
-
|
|
10249
|
-
return htmlContent;
|
|
10350
|
+
return pendingHtmlContent.filter((content) => content.value);
|
|
10250
10351
|
}
|
|
10251
10352
|
|
|
10252
10353
|
//------------------------------------------------------------------------------
|
|
@@ -10650,7 +10751,7 @@ const MINVERSE = {
|
|
|
10650
10751
|
assertSquareMatrix(_t("The argument square_matrix must have the same number of columns and rows."), _matrix);
|
|
10651
10752
|
const { inverted } = invertMatrix(_matrix);
|
|
10652
10753
|
if (!inverted) {
|
|
10653
|
-
|
|
10754
|
+
return new EvaluationError(_t("The matrix is not invertible."));
|
|
10654
10755
|
}
|
|
10655
10756
|
return inverted;
|
|
10656
10757
|
},
|
|
@@ -10729,7 +10830,7 @@ function getSumXAndY(arrayX, arrayY, cb) {
|
|
|
10729
10830
|
}
|
|
10730
10831
|
}
|
|
10731
10832
|
if (!validPairFound) {
|
|
10732
|
-
|
|
10833
|
+
return new EvaluationError(_t("The arguments array_x and array_y must contain at least one pair of numbers."));
|
|
10733
10834
|
}
|
|
10734
10835
|
return result;
|
|
10735
10836
|
}
|
|
@@ -10810,7 +10911,7 @@ const TOCOL = {
|
|
|
10810
10911
|
.flat()
|
|
10811
10912
|
.filter(shouldKeepValue(_ignore));
|
|
10812
10913
|
if (result.length === 0) {
|
|
10813
|
-
|
|
10914
|
+
return new NotAvailableError(_t("No results for the given arguments of TOCOL."));
|
|
10814
10915
|
}
|
|
10815
10916
|
return [result];
|
|
10816
10917
|
},
|
|
@@ -10831,7 +10932,7 @@ const TOROW = {
|
|
|
10831
10932
|
.filter(shouldKeepValue(_ignore))
|
|
10832
10933
|
.map((item) => [item]);
|
|
10833
10934
|
if (result.length === 0 || result[0].length === 0) {
|
|
10834
|
-
|
|
10935
|
+
return new NotAvailableError(_t("No results for the given arguments of TOROW."));
|
|
10835
10936
|
}
|
|
10836
10937
|
return result;
|
|
10837
10938
|
},
|
|
@@ -11391,7 +11492,7 @@ const DECIMAL = {
|
|
|
11391
11492
|
* Return error if 'value' is positive.
|
|
11392
11493
|
* Remove '-?' in the next regex to catch this error.
|
|
11393
11494
|
*/
|
|
11394
|
-
assert(() =>
|
|
11495
|
+
assert(() => DECIMAL_REPRESENTATION.test(_value), _t("The value (%s) must be a valid base %s representation.", _value, _base.toString()));
|
|
11395
11496
|
const deci = parseInt(_value, _base);
|
|
11396
11497
|
assert(() => !isNaN(deci), _t("The value (%s) must be a valid base %s representation.", _value, _base.toString()));
|
|
11397
11498
|
return deci;
|
|
@@ -11659,7 +11760,7 @@ const PRODUCT = {
|
|
|
11659
11760
|
count += 1;
|
|
11660
11761
|
}
|
|
11661
11762
|
if (isEvaluationError(f)) {
|
|
11662
|
-
|
|
11763
|
+
return j;
|
|
11663
11764
|
}
|
|
11664
11765
|
}
|
|
11665
11766
|
}
|
|
@@ -12162,9 +12263,8 @@ function covariance(dataY, dataX, isSample) {
|
|
|
12162
12263
|
}
|
|
12163
12264
|
function variance(args, isSample, textAs0, locale) {
|
|
12164
12265
|
let count = 0;
|
|
12165
|
-
let sum = 0;
|
|
12166
12266
|
const reduceFunction = textAs0 ? reduceNumbersTextAs0 : reduceNumbers;
|
|
12167
|
-
sum = reduceFunction(args, (acc, a) => {
|
|
12267
|
+
const sum = reduceFunction(args, (acc, a) => {
|
|
12168
12268
|
count += 1;
|
|
12169
12269
|
return acc + a;
|
|
12170
12270
|
}, 0, locale);
|
|
@@ -12561,7 +12661,7 @@ const MATTHEWS = {
|
|
|
12561
12661
|
const flatY = dataY.flat();
|
|
12562
12662
|
assertSameNumberOfElements(flatX, flatY);
|
|
12563
12663
|
if (flatX.length === 0) {
|
|
12564
|
-
|
|
12664
|
+
return new EvaluationError(_t("[[FUNCTION_NAME]] expects non-empty ranges for both parameters."));
|
|
12565
12665
|
}
|
|
12566
12666
|
const n = flatX.length;
|
|
12567
12667
|
let trueN = 0, trueP = 0, falseP = 0, falseN = 0;
|
|
@@ -13361,9 +13461,8 @@ function getMatchingCells(database, field, criteria, locale) {
|
|
|
13361
13461
|
// 4 - return for each database row corresponding, the cells corresponding to the field parameter
|
|
13362
13462
|
const fieldCol = database[index];
|
|
13363
13463
|
// Example continuation:: fieldCol = ["C", "j", "k", 7]
|
|
13364
|
-
const matchingCells = [...matchingRows].map((x) => fieldCol[x + 1]);
|
|
13365
13464
|
// Example continuation:: matchingCells = ["j", 7]
|
|
13366
|
-
return
|
|
13465
|
+
return [...matchingRows].map((x) => fieldCol[x + 1]);
|
|
13367
13466
|
}
|
|
13368
13467
|
const databaseArgs = [
|
|
13369
13468
|
arg("database (range)", _t("The array or range containing the data to consider, structured in such a way that the first row contains the labels for each column's values.")),
|
|
@@ -14548,7 +14647,7 @@ const FILTER = {
|
|
|
14548
14647
|
}
|
|
14549
14648
|
}
|
|
14550
14649
|
if (!result.length) {
|
|
14551
|
-
|
|
14650
|
+
return new NotAvailableError(_t("No match found in FILTER evaluation"));
|
|
14552
14651
|
}
|
|
14553
14652
|
return mode === "row" ? transposeMatrix(result) : result;
|
|
14554
14653
|
},
|
|
@@ -17246,7 +17345,7 @@ function mapParentFunction(tokens) {
|
|
|
17246
17345
|
argsTokens[argPosition].push({ value: token.value, type: token.type });
|
|
17247
17346
|
}
|
|
17248
17347
|
}
|
|
17249
|
-
|
|
17348
|
+
return tokens.map((token, i) => {
|
|
17250
17349
|
if (!["SPACE", "LEFT_PAREN"].includes(token.type)) {
|
|
17251
17350
|
functionStarted = "";
|
|
17252
17351
|
}
|
|
@@ -17284,7 +17383,6 @@ function mapParentFunction(tokens) {
|
|
|
17284
17383
|
}
|
|
17285
17384
|
return token;
|
|
17286
17385
|
});
|
|
17287
|
-
return res;
|
|
17288
17386
|
}
|
|
17289
17387
|
/**
|
|
17290
17388
|
* Parse the list of tokens that compose the arguments of a function to
|
|
@@ -17740,7 +17838,7 @@ const IFS = {
|
|
|
17740
17838
|
return result;
|
|
17741
17839
|
}
|
|
17742
17840
|
}
|
|
17743
|
-
|
|
17841
|
+
return new EvaluationError(_t("No match."));
|
|
17744
17842
|
},
|
|
17745
17843
|
isExported: true,
|
|
17746
17844
|
};
|
|
@@ -17843,7 +17941,7 @@ function assertDomainLength(domain) {
|
|
|
17843
17941
|
throw new EvaluationError(_t("Function PIVOT takes an even number of arguments."));
|
|
17844
17942
|
}
|
|
17845
17943
|
}
|
|
17846
|
-
function addPivotDependencies(evalContext, coreDefinition) {
|
|
17944
|
+
function addPivotDependencies(evalContext, coreDefinition, forMeasures) {
|
|
17847
17945
|
//TODO This function can be very costly when used with PIVOT.VALUE and PIVOT.HEADER
|
|
17848
17946
|
const dependencies = [];
|
|
17849
17947
|
if (coreDefinition.type === "SPREADSHEET" && coreDefinition.dataSet) {
|
|
@@ -17855,7 +17953,7 @@ function addPivotDependencies(evalContext, coreDefinition) {
|
|
|
17855
17953
|
}
|
|
17856
17954
|
dependencies.push(range);
|
|
17857
17955
|
}
|
|
17858
|
-
for (const measure of
|
|
17956
|
+
for (const measure of forMeasures) {
|
|
17859
17957
|
if (measure.computedBy) {
|
|
17860
17958
|
const formula = evalContext.getters.getMeasureCompiledFormula(measure);
|
|
17861
17959
|
dependencies.push(...formula.dependencies.filter((range) => !range.invalidXc));
|
|
@@ -17904,8 +18002,8 @@ const ADDRESS = {
|
|
|
17904
18002
|
let cellReference;
|
|
17905
18003
|
if (_useA1Notation) {
|
|
17906
18004
|
const rangePart = {
|
|
17907
|
-
rowFixed: [1, 2].includes(_absoluteRelativeMode)
|
|
17908
|
-
colFixed: [1, 3].includes(_absoluteRelativeMode)
|
|
18005
|
+
rowFixed: [1, 2].includes(_absoluteRelativeMode),
|
|
18006
|
+
colFixed: [1, 3].includes(_absoluteRelativeMode),
|
|
17909
18007
|
};
|
|
17910
18008
|
cellReference = toXC(colNumber - 1, rowNumber - 1, rangePart);
|
|
17911
18009
|
}
|
|
@@ -17931,7 +18029,7 @@ const COLUMN = {
|
|
|
17931
18029
|
],
|
|
17932
18030
|
compute: function (cellReference) {
|
|
17933
18031
|
if (isEvaluationError(cellReference?.value)) {
|
|
17934
|
-
|
|
18032
|
+
return cellReference;
|
|
17935
18033
|
}
|
|
17936
18034
|
const column = cellReference === undefined
|
|
17937
18035
|
? this.__originCellPosition?.col
|
|
@@ -17949,7 +18047,7 @@ const COLUMNS = {
|
|
|
17949
18047
|
args: [arg("range (meta)", _t("The range whose column count will be returned."))],
|
|
17950
18048
|
compute: function (range) {
|
|
17951
18049
|
if (isEvaluationError(range?.value)) {
|
|
17952
|
-
|
|
18050
|
+
return range;
|
|
17953
18051
|
}
|
|
17954
18052
|
const zone = toZone(range.value);
|
|
17955
18053
|
return zone.right - zone.left + 1;
|
|
@@ -18029,11 +18127,11 @@ const INDIRECT = {
|
|
|
18029
18127
|
compute: function (reference, useA1Notation = { value: true }) {
|
|
18030
18128
|
let _reference = reference?.value?.toString();
|
|
18031
18129
|
if (!_reference) {
|
|
18032
|
-
|
|
18130
|
+
return new InvalidReferenceError(_t("Reference should be defined."));
|
|
18033
18131
|
}
|
|
18034
18132
|
const _useA1Notation = toBoolean(useA1Notation);
|
|
18035
18133
|
if (!_useA1Notation) {
|
|
18036
|
-
|
|
18134
|
+
return new EvaluationError(_t("R1C1 notation is not supported."));
|
|
18037
18135
|
}
|
|
18038
18136
|
const sheetId = this.__originSheetId;
|
|
18039
18137
|
const originPosition = this.__originCellPosition;
|
|
@@ -18045,7 +18143,7 @@ const INDIRECT = {
|
|
|
18045
18143
|
}
|
|
18046
18144
|
const range = this.getters.getRangeFromSheetXC(sheetId, _reference);
|
|
18047
18145
|
if (range === undefined || range.invalidXc || range.invalidSheetName) {
|
|
18048
|
-
|
|
18146
|
+
return new InvalidReferenceError();
|
|
18049
18147
|
}
|
|
18050
18148
|
if (originPosition) {
|
|
18051
18149
|
this.addDependencies?.(originPosition, [range]);
|
|
@@ -18153,7 +18251,7 @@ const ROW = {
|
|
|
18153
18251
|
],
|
|
18154
18252
|
compute: function (cellReference) {
|
|
18155
18253
|
if (isEvaluationError(cellReference?.value)) {
|
|
18156
|
-
|
|
18254
|
+
return cellReference;
|
|
18157
18255
|
}
|
|
18158
18256
|
const row = cellReference === undefined
|
|
18159
18257
|
? this.__originCellPosition?.row
|
|
@@ -18171,7 +18269,7 @@ const ROWS = {
|
|
|
18171
18269
|
args: [arg("range (meta)", _t("The range whose row count will be returned."))],
|
|
18172
18270
|
compute: function (range) {
|
|
18173
18271
|
if (isEvaluationError(range?.value)) {
|
|
18174
|
-
|
|
18272
|
+
return range;
|
|
18175
18273
|
}
|
|
18176
18274
|
const zone = toZone(range.value);
|
|
18177
18275
|
return zone.bottom - zone.top + 1;
|
|
@@ -18288,7 +18386,7 @@ const PIVOT_VALUE = {
|
|
|
18288
18386
|
assertDomainLength(domainArgs);
|
|
18289
18387
|
const pivot = this.getters.getPivot(pivotId);
|
|
18290
18388
|
const coreDefinition = this.getters.getPivotCoreDefinition(pivotId);
|
|
18291
|
-
addPivotDependencies(this, coreDefinition);
|
|
18389
|
+
addPivotDependencies(this, coreDefinition, coreDefinition.measures.filter((m) => m.id === _measure));
|
|
18292
18390
|
pivot.init({ reload: pivot.needsReevaluation });
|
|
18293
18391
|
const error = pivot.assertIsValid({ throwOnError: false });
|
|
18294
18392
|
if (error) {
|
|
@@ -18318,7 +18416,7 @@ const PIVOT_HEADER = {
|
|
|
18318
18416
|
assertDomainLength(domainArgs);
|
|
18319
18417
|
const pivot = this.getters.getPivot(_pivotId);
|
|
18320
18418
|
const coreDefinition = this.getters.getPivotCoreDefinition(_pivotId);
|
|
18321
|
-
addPivotDependencies(this, coreDefinition);
|
|
18419
|
+
addPivotDependencies(this, coreDefinition, []);
|
|
18322
18420
|
pivot.init({ reload: pivot.needsReevaluation });
|
|
18323
18421
|
const error = pivot.assertIsValid({ throwOnError: false });
|
|
18324
18422
|
if (error) {
|
|
@@ -18358,18 +18456,18 @@ const PIVOT = {
|
|
|
18358
18456
|
const _pivotFormulaId = toString(pivotFormulaId);
|
|
18359
18457
|
const _rowCount = toNumber(rowCount, this.locale);
|
|
18360
18458
|
if (_rowCount < 0) {
|
|
18361
|
-
|
|
18459
|
+
return new EvaluationError(_t("The number of rows must be positive."));
|
|
18362
18460
|
}
|
|
18363
18461
|
const _columnCount = toNumber(columnCount, this.locale);
|
|
18364
18462
|
if (_columnCount < 0) {
|
|
18365
|
-
|
|
18463
|
+
return new EvaluationError(_t("The number of columns must be positive."));
|
|
18366
18464
|
}
|
|
18367
18465
|
const _includeColumnHeaders = toBoolean(includeColumnHeaders);
|
|
18368
18466
|
const _includedTotal = toBoolean(includeTotal);
|
|
18369
18467
|
const pivotId = getPivotId(_pivotFormulaId, this.getters);
|
|
18370
18468
|
const pivot = this.getters.getPivot(pivotId);
|
|
18371
18469
|
const coreDefinition = this.getters.getPivotCoreDefinition(pivotId);
|
|
18372
|
-
addPivotDependencies(this, coreDefinition);
|
|
18470
|
+
addPivotDependencies(this, coreDefinition, coreDefinition.measures);
|
|
18373
18471
|
pivot.init({ reload: pivot.needsReevaluation });
|
|
18374
18472
|
const error = pivot.assertIsValid({ throwOnError: false });
|
|
18375
18473
|
if (error) {
|
|
@@ -18561,6 +18659,12 @@ const EQ = {
|
|
|
18561
18659
|
arg("value2 (any)", _t("The value to test against value1 for equality.")),
|
|
18562
18660
|
],
|
|
18563
18661
|
compute: function (value1, value2) {
|
|
18662
|
+
if (isEvaluationError(value1?.value)) {
|
|
18663
|
+
return value1;
|
|
18664
|
+
}
|
|
18665
|
+
if (isEvaluationError(value2?.value)) {
|
|
18666
|
+
return value2;
|
|
18667
|
+
}
|
|
18564
18668
|
let _value1 = isEmpty(value1) ? getNeutral[typeof value2?.value] : value1?.value;
|
|
18565
18669
|
let _value2 = isEmpty(value2) ? getNeutral[typeof value1?.value] : value2?.value;
|
|
18566
18670
|
if (typeof _value1 === "string") {
|
|
@@ -18569,27 +18673,21 @@ const EQ = {
|
|
|
18569
18673
|
if (typeof _value2 === "string") {
|
|
18570
18674
|
_value2 = _value2.toUpperCase();
|
|
18571
18675
|
}
|
|
18572
|
-
|
|
18573
|
-
throw value1;
|
|
18574
|
-
}
|
|
18575
|
-
if (isEvaluationError(_value2)) {
|
|
18576
|
-
throw value2;
|
|
18577
|
-
}
|
|
18578
|
-
return _value1 === _value2;
|
|
18676
|
+
return { value: _value1 === _value2 };
|
|
18579
18677
|
},
|
|
18580
18678
|
};
|
|
18581
18679
|
// -----------------------------------------------------------------------------
|
|
18582
18680
|
// GT
|
|
18583
18681
|
// -----------------------------------------------------------------------------
|
|
18584
18682
|
function applyRelationalOperator(value1, value2, cb) {
|
|
18585
|
-
|
|
18586
|
-
|
|
18587
|
-
if (isEvaluationError(_value1)) {
|
|
18588
|
-
throw value1;
|
|
18683
|
+
if (isEvaluationError(value1?.value)) {
|
|
18684
|
+
return value1;
|
|
18589
18685
|
}
|
|
18590
|
-
if (isEvaluationError(
|
|
18591
|
-
|
|
18686
|
+
if (isEvaluationError(value2?.value)) {
|
|
18687
|
+
return value2;
|
|
18592
18688
|
}
|
|
18689
|
+
let _value1 = isEmpty(value1) ? getNeutral[typeof value2?.value] : value1?.value;
|
|
18690
|
+
let _value2 = isEmpty(value2) ? getNeutral[typeof value1?.value] : value2?.value;
|
|
18593
18691
|
if (typeof _value1 !== "number") {
|
|
18594
18692
|
_value1 = toString(_value1).toUpperCase();
|
|
18595
18693
|
}
|
|
@@ -18599,12 +18697,12 @@ function applyRelationalOperator(value1, value2, cb) {
|
|
|
18599
18697
|
const tV1 = typeof _value1;
|
|
18600
18698
|
const tV2 = typeof _value2;
|
|
18601
18699
|
if (tV1 === "string" && tV2 === "number") {
|
|
18602
|
-
return true;
|
|
18700
|
+
return { value: true };
|
|
18603
18701
|
}
|
|
18604
18702
|
if (tV2 === "string" && tV1 === "number") {
|
|
18605
|
-
return false;
|
|
18703
|
+
return { value: false };
|
|
18606
18704
|
}
|
|
18607
|
-
return cb(_value1, _value2);
|
|
18705
|
+
return { value: cb(_value1, _value2) };
|
|
18608
18706
|
}
|
|
18609
18707
|
const GT = {
|
|
18610
18708
|
description: _t("Strictly greater than."),
|
|
@@ -18643,7 +18741,11 @@ const LT = {
|
|
|
18643
18741
|
arg("value2 (any)", _t("The second value.")),
|
|
18644
18742
|
],
|
|
18645
18743
|
compute: function (value1, value2) {
|
|
18646
|
-
|
|
18744
|
+
const result = GTE.compute.bind(this)(value1, value2);
|
|
18745
|
+
if (isEvaluationError(result.value)) {
|
|
18746
|
+
return result;
|
|
18747
|
+
}
|
|
18748
|
+
return { value: !result.value };
|
|
18647
18749
|
},
|
|
18648
18750
|
};
|
|
18649
18751
|
// -----------------------------------------------------------------------------
|
|
@@ -18656,7 +18758,11 @@ const LTE = {
|
|
|
18656
18758
|
arg("value2 (any)", _t("The second value.")),
|
|
18657
18759
|
],
|
|
18658
18760
|
compute: function (value1, value2) {
|
|
18659
|
-
|
|
18761
|
+
const result = GT.compute.bind(this)(value1, value2);
|
|
18762
|
+
if (isEvaluationError(result.value)) {
|
|
18763
|
+
return result;
|
|
18764
|
+
}
|
|
18765
|
+
return { value: !result.value };
|
|
18660
18766
|
},
|
|
18661
18767
|
};
|
|
18662
18768
|
// -----------------------------------------------------------------------------
|
|
@@ -18701,7 +18807,11 @@ const NE = {
|
|
|
18701
18807
|
arg("value2 (any)", _t("The value to test against value1 for inequality.")),
|
|
18702
18808
|
],
|
|
18703
18809
|
compute: function (value1, value2) {
|
|
18704
|
-
|
|
18810
|
+
const result = EQ.compute.bind(this)(value1, value2);
|
|
18811
|
+
if (isEvaluationError(result.value)) {
|
|
18812
|
+
return result;
|
|
18813
|
+
}
|
|
18814
|
+
return { value: !result.value };
|
|
18705
18815
|
},
|
|
18706
18816
|
};
|
|
18707
18817
|
// -----------------------------------------------------------------------------
|
|
@@ -19860,13 +19970,12 @@ function cssPropertiesToCss(attributes) {
|
|
|
19860
19970
|
}
|
|
19861
19971
|
function getElementMargins(el) {
|
|
19862
19972
|
const style = window.getComputedStyle(el);
|
|
19863
|
-
|
|
19973
|
+
return {
|
|
19864
19974
|
top: parseInt(style.marginTop, 10) || 0,
|
|
19865
19975
|
bottom: parseInt(style.marginBottom, 10) || 0,
|
|
19866
19976
|
left: parseInt(style.marginLeft, 10) || 0,
|
|
19867
19977
|
right: parseInt(style.marginRight, 10) || 0,
|
|
19868
19978
|
};
|
|
19869
|
-
return margins;
|
|
19870
19979
|
}
|
|
19871
19980
|
|
|
19872
19981
|
const macRegex = /Mac/i;
|
|
@@ -20165,7 +20274,7 @@ class ContentEditableHelper {
|
|
|
20165
20274
|
let startNode = this.findChildAtCharacterIndex(start);
|
|
20166
20275
|
let endNode = this.findChildAtCharacterIndex(end);
|
|
20167
20276
|
range.setStart(startNode.node, startNode.offset);
|
|
20168
|
-
|
|
20277
|
+
range.setEnd(endNode.node, endNode.offset);
|
|
20169
20278
|
}
|
|
20170
20279
|
}
|
|
20171
20280
|
/**
|
|
@@ -20663,7 +20772,7 @@ class Composer extends owl.Component {
|
|
|
20663
20772
|
"Alt+Enter": this.processNewLineEvent,
|
|
20664
20773
|
"Ctrl+Enter": this.processNewLineEvent,
|
|
20665
20774
|
Escape: this.processEscapeKey,
|
|
20666
|
-
F2: () =>
|
|
20775
|
+
F2: (ev) => this.toggleEditionMode(ev),
|
|
20667
20776
|
F4: (ev) => this.processF4Key(ev),
|
|
20668
20777
|
Tab: (ev) => this.processTabKey(ev, "right"),
|
|
20669
20778
|
"Shift+Tab": (ev) => this.processTabKey(ev, "left"),
|
|
@@ -20782,6 +20891,11 @@ class Composer extends owl.Component {
|
|
|
20782
20891
|
this.props.composerStore.cycleReferences();
|
|
20783
20892
|
this.processContent();
|
|
20784
20893
|
}
|
|
20894
|
+
toggleEditionMode(ev) {
|
|
20895
|
+
ev.stopPropagation();
|
|
20896
|
+
this.props.composerStore.toggleEditionMode();
|
|
20897
|
+
this.processContent();
|
|
20898
|
+
}
|
|
20785
20899
|
processNumpadDecimal(ev) {
|
|
20786
20900
|
ev.stopPropagation();
|
|
20787
20901
|
ev.preventDefault();
|
|
@@ -21007,7 +21121,13 @@ class Composer extends owl.Component {
|
|
|
21007
21121
|
break;
|
|
21008
21122
|
case "REFERENCE":
|
|
21009
21123
|
const { xc, sheetName } = splitReference(token.value);
|
|
21010
|
-
result.push({
|
|
21124
|
+
result.push({
|
|
21125
|
+
value: token.value,
|
|
21126
|
+
color: this.rangeColor(xc, sheetName) || "#000",
|
|
21127
|
+
class: tokenAtCursor === token && this.props.composerStore.editionMode === "selecting"
|
|
21128
|
+
? "text-decoration-underline"
|
|
21129
|
+
: undefined,
|
|
21130
|
+
});
|
|
21011
21131
|
break;
|
|
21012
21132
|
case "SYMBOL":
|
|
21013
21133
|
const value = token.value;
|
|
@@ -21578,6 +21698,7 @@ function insertTokenAfterArgSeparator(tokenAtCursor, value) {
|
|
|
21578
21698
|
// replace the whole token
|
|
21579
21699
|
start = tokenAtCursor.start;
|
|
21580
21700
|
}
|
|
21701
|
+
this.composer.stopComposerRangeSelection();
|
|
21581
21702
|
this.composer.changeComposerCursorSelection(start, end);
|
|
21582
21703
|
this.composer.replaceComposerCursorSelection(value);
|
|
21583
21704
|
}
|
|
@@ -21595,6 +21716,7 @@ function insertTokenAfterLeftParenthesis(tokenAtCursor, value) {
|
|
|
21595
21716
|
// replace the whole token
|
|
21596
21717
|
start = tokenAtCursor.start;
|
|
21597
21718
|
}
|
|
21719
|
+
this.composer.stopComposerRangeSelection();
|
|
21598
21720
|
this.composer.changeComposerCursorSelection(start, end);
|
|
21599
21721
|
this.composer.replaceComposerCursorSelection(value);
|
|
21600
21722
|
}
|
|
@@ -21728,9 +21850,13 @@ autoCompleteProviders.add("pivot_group_fields", {
|
|
|
21728
21850
|
const colFields = columns.map((groupBy) => groupBy.nameWithGranularity);
|
|
21729
21851
|
const rowFields = rows.map((groupBy) => groupBy.nameWithGranularity);
|
|
21730
21852
|
const proposals = [];
|
|
21731
|
-
|
|
21853
|
+
let previousGroupBy = ["ARG_SEPARATOR", "SPACE"].includes(tokenAtCursor.type)
|
|
21732
21854
|
? argGroupBys.at(-1)
|
|
21733
21855
|
: argGroupBys.at(-2);
|
|
21856
|
+
const isPositionalSupported = supportedPivotPositionalFormulaRegistry.get(pivot.type);
|
|
21857
|
+
if (isPositionalSupported && previousGroupBy?.startsWith("#")) {
|
|
21858
|
+
previousGroupBy = previousGroupBy.slice(1);
|
|
21859
|
+
}
|
|
21734
21860
|
if (previousGroupBy === undefined) {
|
|
21735
21861
|
proposals.push(colFields[0]);
|
|
21736
21862
|
proposals.push(rowFields[0]);
|
|
@@ -21752,7 +21878,7 @@ autoCompleteProviders.add("pivot_group_fields", {
|
|
|
21752
21878
|
return field ? makeFieldProposal(field, granularity) : undefined;
|
|
21753
21879
|
})
|
|
21754
21880
|
.concat(groupBys.map((groupBy) => {
|
|
21755
|
-
if (!
|
|
21881
|
+
if (!isPositionalSupported) {
|
|
21756
21882
|
return undefined;
|
|
21757
21883
|
}
|
|
21758
21884
|
const fieldName = groupBy.split(":")[0];
|
|
@@ -21761,13 +21887,12 @@ autoCompleteProviders.add("pivot_group_fields", {
|
|
|
21761
21887
|
return undefined;
|
|
21762
21888
|
}
|
|
21763
21889
|
const positionalFieldArg = `"#${groupBy}"`;
|
|
21764
|
-
|
|
21890
|
+
return {
|
|
21765
21891
|
text: positionalFieldArg,
|
|
21766
21892
|
description: _t("%s (positional)", field.string) + (field.help ? ` (${field.help})` : ""),
|
|
21767
21893
|
htmlContent: [{ value: positionalFieldArg, color: tokenColors.STRING }],
|
|
21768
21894
|
fuzzySearchKey: field.string + positionalFieldArg, // search on translated name and on technical name
|
|
21769
21895
|
};
|
|
21770
|
-
return positionalProposal;
|
|
21771
21896
|
}))
|
|
21772
21897
|
.filter(isDefined);
|
|
21773
21898
|
},
|
|
@@ -22036,7 +22161,7 @@ function parseLiteral(content, locale) {
|
|
|
22036
22161
|
return internalDate.value;
|
|
22037
22162
|
}
|
|
22038
22163
|
if (isBoolean(content)) {
|
|
22039
|
-
return content.toUpperCase() === "TRUE"
|
|
22164
|
+
return content.toUpperCase() === "TRUE";
|
|
22040
22165
|
}
|
|
22041
22166
|
return content;
|
|
22042
22167
|
}
|
|
@@ -22187,6 +22312,27 @@ autofillModifiersRegistry
|
|
|
22187
22312
|
tooltip: content ? { props: { content: tooltipValue } } : undefined,
|
|
22188
22313
|
};
|
|
22189
22314
|
},
|
|
22315
|
+
})
|
|
22316
|
+
.add("DATE_INCREMENT_MODIFIER", {
|
|
22317
|
+
apply: (rule, data, getters) => {
|
|
22318
|
+
const date = toJsDate(rule.current, getters.getLocale());
|
|
22319
|
+
date.setFullYear(date.getFullYear() + rule.increment.years || 0);
|
|
22320
|
+
date.setMonth(date.getMonth() + rule.increment.months || 0);
|
|
22321
|
+
date.setDate(date.getDate() + rule.increment.days || 0);
|
|
22322
|
+
const value = jsDateToNumber(date);
|
|
22323
|
+
rule.current = value;
|
|
22324
|
+
const locale = getters.getLocale();
|
|
22325
|
+
const tooltipValue = formatValue(value, { format: data.cell?.format, locale });
|
|
22326
|
+
return {
|
|
22327
|
+
cellData: {
|
|
22328
|
+
border: data.border,
|
|
22329
|
+
style: data.cell && data.cell.style,
|
|
22330
|
+
format: data.cell && data.cell.format,
|
|
22331
|
+
content: value.toString(),
|
|
22332
|
+
},
|
|
22333
|
+
tooltip: value ? { props: { content: tooltipValue } } : undefined,
|
|
22334
|
+
};
|
|
22335
|
+
},
|
|
22190
22336
|
})
|
|
22191
22337
|
.add("COPY_MODIFIER", {
|
|
22192
22338
|
apply: (rule, data, getters) => {
|
|
@@ -22267,7 +22413,9 @@ function getGroup(cell, cells, filter) {
|
|
|
22267
22413
|
if (x === cell) {
|
|
22268
22414
|
found = true;
|
|
22269
22415
|
}
|
|
22270
|
-
const cellValue = x === undefined || x.isFormula
|
|
22416
|
+
const cellValue = x === undefined || x.isFormula
|
|
22417
|
+
? undefined
|
|
22418
|
+
: evaluateLiteral(x, { locale: DEFAULT_LOCALE, format: x.format });
|
|
22271
22419
|
if (cellValue && filter(cellValue)) {
|
|
22272
22420
|
group.push(cellValue);
|
|
22273
22421
|
}
|
|
@@ -22303,6 +22451,71 @@ function calculateIncrementBasedOnGroup(group) {
|
|
|
22303
22451
|
}
|
|
22304
22452
|
return increment;
|
|
22305
22453
|
}
|
|
22454
|
+
/**
|
|
22455
|
+
* Iterates on a list of date intervals.
|
|
22456
|
+
* if every interval is the same, return the interval
|
|
22457
|
+
* Otherwise return undefined
|
|
22458
|
+
*
|
|
22459
|
+
*/
|
|
22460
|
+
function getEqualInterval(intervals) {
|
|
22461
|
+
if (intervals.length < 2) {
|
|
22462
|
+
return intervals[0] || { years: 0, months: 0, days: 0 };
|
|
22463
|
+
}
|
|
22464
|
+
const equal = intervals.every((interval) => interval.years === intervals[0].years &&
|
|
22465
|
+
interval.months === intervals[0].months &&
|
|
22466
|
+
interval.days === intervals[0].days);
|
|
22467
|
+
return equal ? intervals[0] : undefined;
|
|
22468
|
+
}
|
|
22469
|
+
/**
|
|
22470
|
+
* Based on a group of dates, calculate the increment that should be applied
|
|
22471
|
+
* to the next date.
|
|
22472
|
+
*
|
|
22473
|
+
* This will compute the date difference in calendar terms (years, months, days)
|
|
22474
|
+
* In order to make abstraction of leap years and months with different number of days.
|
|
22475
|
+
*
|
|
22476
|
+
* In case the dates are not equidistant in calendar terms, no rule can be extrapolated
|
|
22477
|
+
* In case of equidistant dates, we either have in that order:
|
|
22478
|
+
* - exact date interval (e.g. +n year OR +n month OR +n day) in which case we increment by the same interval
|
|
22479
|
+
* - exact day interval (e.g. +n days) in which case we increment by the same day interval
|
|
22480
|
+
* - equidistant dates but not the same interval, in which case we return increment of the same interval
|
|
22481
|
+
*
|
|
22482
|
+
* */
|
|
22483
|
+
function calculateDateIncrementBasedOnGroup(group) {
|
|
22484
|
+
if (group.length < 2) {
|
|
22485
|
+
return 1;
|
|
22486
|
+
}
|
|
22487
|
+
const jsDates = group.map((date) => toJsDate(date, DEFAULT_LOCALE));
|
|
22488
|
+
const datesIntervals = getDateIntervals(jsDates);
|
|
22489
|
+
const datesEquidistantInterval = getEqualInterval(datesIntervals);
|
|
22490
|
+
if (datesEquidistantInterval === undefined) {
|
|
22491
|
+
// dates are not equidistant in terms of years, months or days, thus no rule can be extrapolated
|
|
22492
|
+
return undefined;
|
|
22493
|
+
}
|
|
22494
|
+
// The dates are apart by an exact interval of years, months or days
|
|
22495
|
+
// but not a combination of them
|
|
22496
|
+
const exactDateInterval = Object.values(datesEquidistantInterval).filter((value) => value !== 0).length === 1;
|
|
22497
|
+
const isSameDay = Object.values(datesEquidistantInterval).every((el) => el === 0); // handles time values (strict decimals)
|
|
22498
|
+
if (!exactDateInterval || isSameDay) {
|
|
22499
|
+
const timeIntervals = jsDates
|
|
22500
|
+
.map((date, index) => {
|
|
22501
|
+
if (index === 0) {
|
|
22502
|
+
return 0;
|
|
22503
|
+
}
|
|
22504
|
+
const previous = jsDates[index - 1];
|
|
22505
|
+
return Math.floor(date.getTime()) - Math.floor(previous.getTime());
|
|
22506
|
+
})
|
|
22507
|
+
.slice(1);
|
|
22508
|
+
const equidistantDates = timeIntervals.every((interval) => interval === timeIntervals[0]);
|
|
22509
|
+
if (equidistantDates) {
|
|
22510
|
+
return group.length * (group[1] - group[0]);
|
|
22511
|
+
}
|
|
22512
|
+
}
|
|
22513
|
+
return {
|
|
22514
|
+
years: datesEquidistantInterval.years * group.length,
|
|
22515
|
+
months: datesEquidistantInterval.months * group.length,
|
|
22516
|
+
days: datesEquidistantInterval.days * group.length,
|
|
22517
|
+
};
|
|
22518
|
+
}
|
|
22306
22519
|
autofillRulesRegistry
|
|
22307
22520
|
.add("simple_value_copy", {
|
|
22308
22521
|
condition: (cell, cells) => {
|
|
@@ -22350,12 +22563,47 @@ autofillRulesRegistry
|
|
|
22350
22563
|
return { type: "FORMULA_MODIFIER", increment: cells.length, current: 0 };
|
|
22351
22564
|
},
|
|
22352
22565
|
sequence: 30,
|
|
22566
|
+
})
|
|
22567
|
+
.add("increment_dates", {
|
|
22568
|
+
condition: (cell, cells) => {
|
|
22569
|
+
return (!cell.isFormula &&
|
|
22570
|
+
evaluateLiteral(cell, { locale: DEFAULT_LOCALE }).type === CellValueType.number &&
|
|
22571
|
+
!!cell.format &&
|
|
22572
|
+
isDateTimeFormat(cell.format));
|
|
22573
|
+
},
|
|
22574
|
+
generateRule: (cell, cells) => {
|
|
22575
|
+
const group = getGroup(cell, cells, (evaluatedCell) => evaluatedCell.type === CellValueType.number &&
|
|
22576
|
+
!!evaluatedCell.format &&
|
|
22577
|
+
isDateTimeFormat(evaluatedCell.format)).map((cell) => Number(cell.value));
|
|
22578
|
+
const increment = calculateDateIncrementBasedOnGroup(group);
|
|
22579
|
+
if (increment === undefined) {
|
|
22580
|
+
return { type: "COPY_MODIFIER" };
|
|
22581
|
+
}
|
|
22582
|
+
/** requires to detect the current date (requires to be an integer value with the right format)
|
|
22583
|
+
* detect if year or if month or if day then extrapolate increment required (+1 month, +1 year + 1 day)
|
|
22584
|
+
*/
|
|
22585
|
+
const evaluation = evaluateLiteral(cell, { locale: DEFAULT_LOCALE });
|
|
22586
|
+
if (typeof increment === "object") {
|
|
22587
|
+
return {
|
|
22588
|
+
type: "DATE_INCREMENT_MODIFIER",
|
|
22589
|
+
increment,
|
|
22590
|
+
current: evaluation.type === CellValueType.number ? evaluation.value : 0,
|
|
22591
|
+
};
|
|
22592
|
+
}
|
|
22593
|
+
return {
|
|
22594
|
+
type: "INCREMENT_MODIFIER",
|
|
22595
|
+
increment,
|
|
22596
|
+
current: evaluation.type === CellValueType.number ? evaluation.value : 0,
|
|
22597
|
+
};
|
|
22598
|
+
},
|
|
22599
|
+
sequence: 25,
|
|
22353
22600
|
})
|
|
22354
22601
|
.add("increment_number", {
|
|
22355
22602
|
condition: (cell) => !cell.isFormula &&
|
|
22356
22603
|
evaluateLiteral(cell, { locale: DEFAULT_LOCALE }).type === CellValueType.number,
|
|
22357
22604
|
generateRule: (cell, cells) => {
|
|
22358
|
-
const group = getGroup(cell, cells, (evaluatedCell) => evaluatedCell.type === CellValueType.number
|
|
22605
|
+
const group = getGroup(cell, cells, (evaluatedCell) => evaluatedCell.type === CellValueType.number &&
|
|
22606
|
+
!isDateTimeFormat(evaluatedCell.format || "")).map((cell) => Number(cell.value));
|
|
22359
22607
|
const increment = calculateIncrementBasedOnGroup(group);
|
|
22360
22608
|
const evaluation = evaluateLiteral(cell, { locale: DEFAULT_LOCALE });
|
|
22361
22609
|
return {
|
|
@@ -22366,6 +22614,37 @@ autofillRulesRegistry
|
|
|
22366
22614
|
},
|
|
22367
22615
|
sequence: 40,
|
|
22368
22616
|
});
|
|
22617
|
+
/**
|
|
22618
|
+
* Returns the date intervals between consecutive dates of an array
|
|
22619
|
+
* in the format of { years: number, months: number, days: number }
|
|
22620
|
+
*
|
|
22621
|
+
* The split is necessary to make abstraction of leap years and
|
|
22622
|
+
* months with different number of days.
|
|
22623
|
+
*
|
|
22624
|
+
* @param dates
|
|
22625
|
+
*/
|
|
22626
|
+
function getDateIntervals(dates) {
|
|
22627
|
+
if (dates.length < 2) {
|
|
22628
|
+
return [{ years: 0, months: 0, days: 0 }];
|
|
22629
|
+
}
|
|
22630
|
+
const res = dates.map((date, index) => {
|
|
22631
|
+
if (index === 0) {
|
|
22632
|
+
return { years: 0, months: 0, days: 0 };
|
|
22633
|
+
}
|
|
22634
|
+
const previous = DateTime.fromTimestamp(dates[index - 1].getTime());
|
|
22635
|
+
const years = getTimeDifferenceInWholeYears(previous, date);
|
|
22636
|
+
const months = getTimeDifferenceInWholeMonths(previous, date) % 12;
|
|
22637
|
+
previous.setFullYear(previous.getFullYear() + years);
|
|
22638
|
+
previous.setMonth(previous.getMonth() + months);
|
|
22639
|
+
const days = getTimeDifferenceInWholeDays(previous, date);
|
|
22640
|
+
return {
|
|
22641
|
+
years,
|
|
22642
|
+
months,
|
|
22643
|
+
days,
|
|
22644
|
+
};
|
|
22645
|
+
});
|
|
22646
|
+
return res.slice(1);
|
|
22647
|
+
}
|
|
22369
22648
|
|
|
22370
22649
|
const cellPopoverRegistry = new Registry();
|
|
22371
22650
|
|
|
@@ -22782,6 +23061,7 @@ const XLSX_CHART_TYPES = [
|
|
|
22782
23061
|
"surface3DChart",
|
|
22783
23062
|
"bubbleChart",
|
|
22784
23063
|
"comboChart",
|
|
23064
|
+
"radarChart",
|
|
22785
23065
|
];
|
|
22786
23066
|
|
|
22787
23067
|
/** In XLSX color format (no #) */
|
|
@@ -23322,7 +23602,7 @@ const CHART_TYPE_CONVERSION_MAP = {
|
|
|
23322
23602
|
lineChart: "line",
|
|
23323
23603
|
line3DChart: undefined,
|
|
23324
23604
|
stockChart: undefined,
|
|
23325
|
-
radarChart:
|
|
23605
|
+
radarChart: "radar",
|
|
23326
23606
|
scatterChart: "scatter",
|
|
23327
23607
|
pieChart: "pie",
|
|
23328
23608
|
pie3DChart: undefined,
|
|
@@ -24735,7 +25015,7 @@ const TABLE_STYLE_CATEGORIES = {
|
|
|
24735
25015
|
custom: _t("Custom"),
|
|
24736
25016
|
};
|
|
24737
25017
|
const DEFAULT_TABLE_CONFIG = {
|
|
24738
|
-
hasFilters:
|
|
25018
|
+
hasFilters: false,
|
|
24739
25019
|
totalRow: false,
|
|
24740
25020
|
firstColumn: false,
|
|
24741
25021
|
lastColumn: false,
|
|
@@ -25404,12 +25684,11 @@ class XlsxBaseExtractor {
|
|
|
25404
25684
|
* Get the list of all the XLSX files in the XLSX file structure
|
|
25405
25685
|
*/
|
|
25406
25686
|
getListOfXMLFiles() {
|
|
25407
|
-
|
|
25687
|
+
return Object.entries(this.xlsxFileStructure)
|
|
25408
25688
|
.filter(([key]) => key !== "images")
|
|
25409
25689
|
.map(([_, value]) => value)
|
|
25410
25690
|
.flat()
|
|
25411
25691
|
.filter(isDefined);
|
|
25412
|
-
return XMLFiles;
|
|
25413
25692
|
}
|
|
25414
25693
|
/**
|
|
25415
25694
|
* Return an array containing the return value of the given function applied to all the XML elements
|
|
@@ -25573,13 +25852,12 @@ class XlsxBaseExtractor {
|
|
|
25573
25852
|
rgb = this.extractAttr(colorElement, "rgb")?.asString();
|
|
25574
25853
|
rgb = rgb === DEFAULT_SYSTEM_COLOR ? undefined : rgb;
|
|
25575
25854
|
}
|
|
25576
|
-
|
|
25855
|
+
return {
|
|
25577
25856
|
rgb: rgb || defaultColor,
|
|
25578
25857
|
auto: this.extractAttr(colorElement, "auto")?.asBool(),
|
|
25579
25858
|
indexed: this.extractAttr(colorElement, "indexed")?.asNum(),
|
|
25580
25859
|
tint: this.extractAttr(colorElement, "tint")?.asNum(),
|
|
25581
25860
|
};
|
|
25582
|
-
return color;
|
|
25583
25861
|
}
|
|
25584
25862
|
/**
|
|
25585
25863
|
* Returns the xml file targeted by a relationship.
|
|
@@ -26171,7 +26449,7 @@ class XlsxSheetExtractor extends XlsxBaseExtractor {
|
|
|
26171
26449
|
hyperlinks: this.extractHyperLinks(sheetElement),
|
|
26172
26450
|
tables: this.extractTables(sheetElement),
|
|
26173
26451
|
pivotTables: this.extractPivotTables(),
|
|
26174
|
-
isVisible: sheetWorkbookInfo.state === "visible"
|
|
26452
|
+
isVisible: sheetWorkbookInfo.state === "visible",
|
|
26175
26453
|
};
|
|
26176
26454
|
})[0];
|
|
26177
26455
|
}
|
|
@@ -26242,8 +26520,7 @@ class XlsxSheetExtractor extends XlsxBaseExtractor {
|
|
|
26242
26520
|
const figures = this.mapOnElements({ parent: worksheet, query: "drawing" }, (drawingElement) => {
|
|
26243
26521
|
const drawingId = this.extractAttr(drawingElement, "r:id", { required: true })?.asString();
|
|
26244
26522
|
const drawingFile = this.getTargetXmlFile(this.relationships[drawingId]);
|
|
26245
|
-
|
|
26246
|
-
return figures;
|
|
26523
|
+
return new XlsxFigureExtractor(drawingFile, this.xlsxFileStructure, this.warningManager).extractFigures();
|
|
26247
26524
|
})[0];
|
|
26248
26525
|
return figures || [];
|
|
26249
26526
|
}
|
|
@@ -26261,8 +26538,7 @@ class XlsxSheetExtractor extends XlsxBaseExtractor {
|
|
|
26261
26538
|
.filter((relationship) => relationship.type.endsWith("pivotTable"))
|
|
26262
26539
|
.map((pivotRelationship) => {
|
|
26263
26540
|
const pivotFile = this.getTargetXmlFile(pivotRelationship);
|
|
26264
|
-
|
|
26265
|
-
return pivot;
|
|
26541
|
+
return new XlsxPivotExtractor(pivotFile, this.xlsxFileStructure, this.warningManager).getPivotTable();
|
|
26266
26542
|
});
|
|
26267
26543
|
}
|
|
26268
26544
|
catch (e) {
|
|
@@ -26661,8 +26937,7 @@ class XlsxReader {
|
|
|
26661
26937
|
}
|
|
26662
26938
|
convertXlsx() {
|
|
26663
26939
|
const xlsxData = this.getXlsxData();
|
|
26664
|
-
|
|
26665
|
-
return convertedData;
|
|
26940
|
+
return this.convertImportedData(xlsxData);
|
|
26666
26941
|
}
|
|
26667
26942
|
// ---------------------------------------------------------------------------
|
|
26668
26943
|
// Parsing XMLs
|
|
@@ -27253,6 +27528,20 @@ migrationStepRegistry
|
|
|
27253
27528
|
}
|
|
27254
27529
|
return data;
|
|
27255
27530
|
},
|
|
27531
|
+
})
|
|
27532
|
+
.add("migration_22", {
|
|
27533
|
+
// "tables are no longer inserted with filters by default",
|
|
27534
|
+
versionFrom: "22",
|
|
27535
|
+
migrate(data) {
|
|
27536
|
+
for (const sheet of data.sheets || []) {
|
|
27537
|
+
for (const table of sheet.tables || []) {
|
|
27538
|
+
if (!table.config) {
|
|
27539
|
+
table.config = { ...DEFAULT_TABLE_CONFIG, hasFilters: true };
|
|
27540
|
+
}
|
|
27541
|
+
}
|
|
27542
|
+
}
|
|
27543
|
+
return data;
|
|
27544
|
+
},
|
|
27256
27545
|
});
|
|
27257
27546
|
function fixOverlappingFilters(data) {
|
|
27258
27547
|
for (let sheet of data.sheets || []) {
|
|
@@ -27280,7 +27569,7 @@ function fixOverlappingFilters(data) {
|
|
|
27280
27569
|
* a breaking change is made in the way the state is handled, and an upgrade
|
|
27281
27570
|
* function should be defined
|
|
27282
27571
|
*/
|
|
27283
|
-
const CURRENT_VERSION =
|
|
27572
|
+
const CURRENT_VERSION = 23;
|
|
27284
27573
|
const INITIAL_SHEET_ID = "Sheet1";
|
|
27285
27574
|
/**
|
|
27286
27575
|
* This function tries to load anything that could look like a valid
|
|
@@ -27293,7 +27582,7 @@ function load(data, verboseImport) {
|
|
|
27293
27582
|
if (!data) {
|
|
27294
27583
|
return createEmptyWorkbookData();
|
|
27295
27584
|
}
|
|
27296
|
-
console.
|
|
27585
|
+
console.debug("### Loading data ###");
|
|
27297
27586
|
const start = performance.now();
|
|
27298
27587
|
if (data["[Content_Types].xml"]) {
|
|
27299
27588
|
const reader = new XlsxReader(data);
|
|
@@ -27307,13 +27596,13 @@ function load(data, verboseImport) {
|
|
|
27307
27596
|
// apply migrations, if needed
|
|
27308
27597
|
if ("version" in data) {
|
|
27309
27598
|
if (data.version < CURRENT_VERSION) {
|
|
27310
|
-
console.
|
|
27599
|
+
console.debug("Migrating data from version", data.version);
|
|
27311
27600
|
data = migrate(data);
|
|
27312
27601
|
}
|
|
27313
27602
|
}
|
|
27314
27603
|
data = repairData(data);
|
|
27315
|
-
console.
|
|
27316
|
-
console.
|
|
27604
|
+
console.debug("Data loaded in", performance.now() - start, "ms");
|
|
27605
|
+
console.debug("###");
|
|
27317
27606
|
return data;
|
|
27318
27607
|
}
|
|
27319
27608
|
// -----------------------------------------------------------------------------
|
|
@@ -27343,7 +27632,7 @@ function migrate(data) {
|
|
|
27343
27632
|
for (let i = index; i < steps.length; i++) {
|
|
27344
27633
|
data = steps[i].migrate(data);
|
|
27345
27634
|
}
|
|
27346
|
-
console.
|
|
27635
|
+
console.debug("Data migrated in", performance.now() - start, "ms");
|
|
27347
27636
|
return data;
|
|
27348
27637
|
}
|
|
27349
27638
|
/**
|
|
@@ -27525,7 +27814,7 @@ function createEmptySheet(sheetId, name) {
|
|
|
27525
27814
|
};
|
|
27526
27815
|
}
|
|
27527
27816
|
function createEmptyWorkbookData(sheetName = "Sheet1") {
|
|
27528
|
-
|
|
27817
|
+
return {
|
|
27529
27818
|
version: CURRENT_VERSION,
|
|
27530
27819
|
sheets: [createEmptySheet(INITIAL_SHEET_ID, sheetName)],
|
|
27531
27820
|
styles: {},
|
|
@@ -27538,7 +27827,6 @@ function createEmptyWorkbookData(sheetName = "Sheet1") {
|
|
|
27538
27827
|
pivotNextId: 1,
|
|
27539
27828
|
customTableStyles: {},
|
|
27540
27829
|
};
|
|
27541
|
-
return data;
|
|
27542
27830
|
}
|
|
27543
27831
|
function createEmptyExcelSheet(sheetId, name) {
|
|
27544
27832
|
return {
|
|
@@ -27876,9 +28164,9 @@ function truncateLabel(label) {
|
|
|
27876
28164
|
/**
|
|
27877
28165
|
* Get a default chart js configuration
|
|
27878
28166
|
*/
|
|
27879
|
-
function getDefaultChartJsRuntime(chart, labels, fontColor, {
|
|
28167
|
+
function getDefaultChartJsRuntime(chart, labels, fontColor, { axisFormats, locale, truncateLabels = true, horizontalChart }) {
|
|
27880
28168
|
const chartTitle = chart.title.text ? chart.title : { ...chart.title, content: "" };
|
|
27881
|
-
const
|
|
28169
|
+
const chartOptions = {
|
|
27882
28170
|
// https://www.chartjs.org/docs/latest/general/responsive.html
|
|
27883
28171
|
responsive: true, // will resize when its container is resized
|
|
27884
28172
|
maintainAspectRatio: false, // doesn't maintain the aspect ration (width/height =2 by default) so the user has the choice of the exact layout
|
|
@@ -27922,11 +28210,13 @@ function getDefaultChartJsRuntime(chart, labels, fontColor, { format, locale, tr
|
|
|
27922
28210
|
const xLabel = tooltipItem.dataset?.label || tooltipItem.label;
|
|
27923
28211
|
// tooltipItem.parsed can be an object or a number for pie charts
|
|
27924
28212
|
let yLabel = horizontalChart ? tooltipItem.parsed.x : tooltipItem.parsed.y;
|
|
27925
|
-
if (
|
|
28213
|
+
if (yLabel === undefined || yLabel === null) {
|
|
27926
28214
|
yLabel = tooltipItem.parsed;
|
|
27927
28215
|
}
|
|
27928
|
-
const
|
|
27929
|
-
|
|
28216
|
+
const axisId = horizontalChart
|
|
28217
|
+
? tooltipItem.dataset.xAxisID
|
|
28218
|
+
: tooltipItem.dataset.yAxisID;
|
|
28219
|
+
const yLabelStr = formatChartDatasetValue(axisFormats, locale)(yLabel, axisId);
|
|
27930
28220
|
return xLabel ? `${xLabel}: ${yLabelStr}` : yLabelStr;
|
|
27931
28221
|
},
|
|
27932
28222
|
},
|
|
@@ -27935,7 +28225,7 @@ function getDefaultChartJsRuntime(chart, labels, fontColor, { format, locale, tr
|
|
|
27935
28225
|
};
|
|
27936
28226
|
return {
|
|
27937
28227
|
type: chart.type,
|
|
27938
|
-
options,
|
|
28228
|
+
options: chartOptions,
|
|
27939
28229
|
data: {
|
|
27940
28230
|
labels: truncateLabels ? labels.map(truncateLabel) : labels,
|
|
27941
28231
|
datasets: [],
|
|
@@ -27994,7 +28284,8 @@ function getChartLabelValues(getters, dataSets, labelRange) {
|
|
|
27994
28284
|
* Get the format to apply to the the dataset values. This format is defined as the first format
|
|
27995
28285
|
* found in the dataset ranges that isn't a date format.
|
|
27996
28286
|
*/
|
|
27997
|
-
function getChartDatasetFormat(getters,
|
|
28287
|
+
function getChartDatasetFormat(getters, allDataSets, axis) {
|
|
28288
|
+
const dataSets = allDataSets.filter((ds) => (axis === "right") === !!ds.rightYAxis);
|
|
27998
28289
|
for (const ds of dataSets) {
|
|
27999
28290
|
const formatsInDataset = getters.getRangeFormats(ds.dataRange);
|
|
28000
28291
|
const format = formatsInDataset.find((f) => f !== undefined && !isDateTimeFormat(f));
|
|
@@ -28248,12 +28539,16 @@ function createBarChartRuntime(chart, getters) {
|
|
|
28248
28539
|
if (chart.aggregated) {
|
|
28249
28540
|
({ labels, dataSetsValues } = aggregateDataForLabels(labels, dataSetsValues));
|
|
28250
28541
|
}
|
|
28251
|
-
const
|
|
28542
|
+
const leftAxisFormat = getChartDatasetFormat(getters, chart.dataSets, "left");
|
|
28543
|
+
const rightAxisFormat = getChartDatasetFormat(getters, chart.dataSets, "right");
|
|
28252
28544
|
const locale = getters.getLocale();
|
|
28253
|
-
const localeFormat = { format: dataSetFormat, locale };
|
|
28254
28545
|
const fontColor = chartFontColor(chart.background);
|
|
28546
|
+
const axisFormats = chart.horizontal
|
|
28547
|
+
? { x: leftAxisFormat || rightAxisFormat }
|
|
28548
|
+
: { y: leftAxisFormat, y1: rightAxisFormat };
|
|
28255
28549
|
const config = getDefaultChartJsRuntime(chart, labels, fontColor, {
|
|
28256
|
-
|
|
28550
|
+
locale,
|
|
28551
|
+
axisFormats,
|
|
28257
28552
|
horizontalChart: chart.horizontal,
|
|
28258
28553
|
});
|
|
28259
28554
|
const legend = {
|
|
@@ -28274,51 +28569,27 @@ function createBarChartRuntime(chart, getters) {
|
|
|
28274
28569
|
};
|
|
28275
28570
|
config.options.indexAxis = chart.horizontal ? "y" : "x";
|
|
28276
28571
|
config.options.scales = {};
|
|
28277
|
-
const
|
|
28278
|
-
const
|
|
28279
|
-
|
|
28280
|
-
|
|
28281
|
-
|
|
28282
|
-
|
|
28283
|
-
},
|
|
28284
|
-
};
|
|
28285
|
-
const xAxis = chart.horizontal ? valuesAxis : labelsAxis;
|
|
28286
|
-
const yAxis = chart.horizontal ? labelsAxis : valuesAxis;
|
|
28287
|
-
const { useLeftAxis, useRightAxis } = getDefinedAxis(chart.getDefinition());
|
|
28288
|
-
config.options.scales.x = { ...xAxis, title: getChartAxisTitleRuntime(chart.axesDesign?.x) };
|
|
28289
|
-
if (useLeftAxis) {
|
|
28290
|
-
config.options.scales.y = {
|
|
28291
|
-
...yAxis,
|
|
28292
|
-
position: "left",
|
|
28293
|
-
title: getChartAxisTitleRuntime(chart.axesDesign?.y),
|
|
28294
|
-
};
|
|
28295
|
-
}
|
|
28296
|
-
if (useRightAxis) {
|
|
28297
|
-
config.options.scales.y1 = {
|
|
28298
|
-
...yAxis,
|
|
28299
|
-
position: "right",
|
|
28300
|
-
title: getChartAxisTitleRuntime(chart.axesDesign?.y1),
|
|
28301
|
-
};
|
|
28572
|
+
const definition = chart.getDefinition();
|
|
28573
|
+
const options = { stacked: chart.stacked, locale };
|
|
28574
|
+
if (chart.horizontal) {
|
|
28575
|
+
const format = leftAxisFormat || rightAxisFormat;
|
|
28576
|
+
config.options.scales.x = getChartAxis(definition, "bottom", "values", { ...options, format });
|
|
28577
|
+
config.options.scales.y = getChartAxis(definition, "left", "labels", options);
|
|
28302
28578
|
}
|
|
28303
|
-
|
|
28304
|
-
|
|
28305
|
-
|
|
28306
|
-
|
|
28307
|
-
|
|
28308
|
-
|
|
28309
|
-
}
|
|
28310
|
-
if (useRightAxis) {
|
|
28311
|
-
// @ts-ignore chart.js type is broken
|
|
28312
|
-
config.options.scales.y1.stacked = true;
|
|
28313
|
-
}
|
|
28579
|
+
else {
|
|
28580
|
+
config.options.scales.x = getChartAxis(definition, "bottom", "labels", options);
|
|
28581
|
+
const leftAxisOptions = { ...options, format: leftAxisFormat };
|
|
28582
|
+
config.options.scales.y = getChartAxis(definition, "left", "values", leftAxisOptions);
|
|
28583
|
+
const rightAxisOptions = { ...options, format: rightAxisFormat };
|
|
28584
|
+
config.options.scales.y1 = getChartAxis(definition, "right", "values", rightAxisOptions);
|
|
28314
28585
|
}
|
|
28586
|
+
config.options.scales = removeFalsyAttributes(config.options.scales);
|
|
28315
28587
|
config.options.plugins.chartShowValuesPlugin = {
|
|
28316
28588
|
showValues: chart.showValues,
|
|
28317
28589
|
background: chart.background,
|
|
28318
28590
|
horizontal: chart.horizontal,
|
|
28319
|
-
callback:
|
|
28591
|
+
callback: formatChartDatasetValue(axisFormats, locale),
|
|
28320
28592
|
};
|
|
28321
|
-
const definition = chart.getDefinition();
|
|
28322
28593
|
const colors = getChartColorsGenerator(definition, dataSetsValues.length);
|
|
28323
28594
|
const trendDatasets = [];
|
|
28324
28595
|
for (const index in dataSetsValues) {
|
|
@@ -28333,12 +28604,10 @@ function createBarChartRuntime(chart, getters) {
|
|
|
28333
28604
|
};
|
|
28334
28605
|
config.data.datasets.push(dataset);
|
|
28335
28606
|
if (definition.dataSets?.[index]?.label) {
|
|
28336
|
-
|
|
28337
|
-
dataset.label = label;
|
|
28338
|
-
}
|
|
28339
|
-
if (definition.dataSets?.[index]?.yAxisId && !chart.horizontal) {
|
|
28340
|
-
dataset["yAxisID"] = definition.dataSets[index].yAxisId;
|
|
28607
|
+
dataset.label = definition.dataSets[index].label;
|
|
28341
28608
|
}
|
|
28609
|
+
dataset.yAxisID = chart.horizontal ? "y" : definition.dataSets[index].yAxisId || "y";
|
|
28610
|
+
dataset.xAxisID = "x";
|
|
28342
28611
|
const trend = definition.dataSets?.[index].trend;
|
|
28343
28612
|
if (!trend?.display || chart.horizontal) {
|
|
28344
28613
|
continue;
|
|
@@ -28354,7 +28623,7 @@ function createBarChartRuntime(chart, getters) {
|
|
|
28354
28623
|
*/
|
|
28355
28624
|
const maxLength = Math.max(...trendDatasets.map((trendDataset) => trendDataset.data.length));
|
|
28356
28625
|
config.options.scales[TREND_LINE_XAXIS_ID] = {
|
|
28357
|
-
...
|
|
28626
|
+
...config.options.scales.x,
|
|
28358
28627
|
labels: Array(maxLength).fill(""),
|
|
28359
28628
|
offset: false,
|
|
28360
28629
|
display: false,
|
|
@@ -28363,13 +28632,10 @@ function createBarChartRuntime(chart, getters) {
|
|
|
28363
28632
|
* datasets to ensure the way we distinguish the originals and trendLine datasets after
|
|
28364
28633
|
*/
|
|
28365
28634
|
trendDatasets.forEach((x) => config.data.datasets.push(x));
|
|
28366
|
-
const originalTooltipTitle = config.options.plugins.tooltip.callbacks.title;
|
|
28367
28635
|
config.options.plugins.tooltip.callbacks.title = function (tooltipItems) {
|
|
28368
|
-
|
|
28369
|
-
|
|
28370
|
-
|
|
28371
|
-
}
|
|
28372
|
-
return "";
|
|
28636
|
+
return tooltipItems.some((item) => item.dataset.xAxisID !== TREND_LINE_XAXIS_ID)
|
|
28637
|
+
? undefined
|
|
28638
|
+
: "";
|
|
28373
28639
|
};
|
|
28374
28640
|
}
|
|
28375
28641
|
return { chartJsConfig: config, background: chart.background || BACKGROUND_CHART_COLOR };
|
|
@@ -28644,8 +28910,10 @@ function createLineOrScatterChartRuntime(chart, getters) {
|
|
|
28644
28910
|
}
|
|
28645
28911
|
const locale = getters.getLocale();
|
|
28646
28912
|
const truncateLabels = axisType === "category";
|
|
28647
|
-
const
|
|
28648
|
-
const
|
|
28913
|
+
const leftAxisFormat = getChartDatasetFormat(getters, chart.dataSets, "left");
|
|
28914
|
+
const rightAxisFormat = getChartDatasetFormat(getters, chart.dataSets, "right");
|
|
28915
|
+
const axisFormats = { y: leftAxisFormat, y1: rightAxisFormat };
|
|
28916
|
+
const options = { locale, truncateLabels, axisFormats };
|
|
28649
28917
|
const fontColor = chartFontColor(chart.background);
|
|
28650
28918
|
const config = getDefaultChartJsRuntime(chart, labels, fontColor, options);
|
|
28651
28919
|
const legend = {
|
|
@@ -28675,52 +28943,18 @@ function createLineOrScatterChartRuntime(chart, getters) {
|
|
|
28675
28943
|
displayLegend: chart.legendPosition === "top",
|
|
28676
28944
|
}),
|
|
28677
28945
|
};
|
|
28678
|
-
const
|
|
28679
|
-
|
|
28680
|
-
padding: 5,
|
|
28681
|
-
color: fontColor,
|
|
28682
|
-
},
|
|
28683
|
-
title: getChartAxisTitleRuntime(chart.axesDesign?.x),
|
|
28684
|
-
};
|
|
28946
|
+
const definition = chart.getDefinition();
|
|
28947
|
+
const stacked = "stacked" in chart && chart.stacked;
|
|
28685
28948
|
config.options.scales = {
|
|
28686
|
-
x:
|
|
28687
|
-
|
|
28688
|
-
|
|
28689
|
-
beginAtZero: true, // the origin of the y axis is always zero
|
|
28690
|
-
ticks: {
|
|
28691
|
-
color: fontColor,
|
|
28692
|
-
callback: formatTickValue(options),
|
|
28693
|
-
},
|
|
28949
|
+
x: getChartAxis(definition, "bottom", "labels", { locale }),
|
|
28950
|
+
y: getChartAxis(definition, "left", "values", { locale, stacked, format: leftAxisFormat }),
|
|
28951
|
+
y1: getChartAxis(definition, "right", "values", { locale, stacked, format: rightAxisFormat }),
|
|
28694
28952
|
};
|
|
28695
|
-
|
|
28696
|
-
if (useLeftAxis) {
|
|
28697
|
-
config.options.scales.y = {
|
|
28698
|
-
...yAxis,
|
|
28699
|
-
position: "left",
|
|
28700
|
-
title: getChartAxisTitleRuntime(chart.axesDesign?.y),
|
|
28701
|
-
};
|
|
28702
|
-
}
|
|
28703
|
-
if (useRightAxis) {
|
|
28704
|
-
config.options.scales.y1 = {
|
|
28705
|
-
...yAxis,
|
|
28706
|
-
position: "right",
|
|
28707
|
-
title: getChartAxisTitleRuntime(chart.axesDesign?.y1),
|
|
28708
|
-
};
|
|
28709
|
-
}
|
|
28710
|
-
if ("stacked" in chart && chart.stacked) {
|
|
28711
|
-
if (useLeftAxis) {
|
|
28712
|
-
// @ts-ignore chart.js type is broken
|
|
28713
|
-
config.options.scales.y.stacked = true;
|
|
28714
|
-
}
|
|
28715
|
-
if (useRightAxis) {
|
|
28716
|
-
// @ts-ignore chart.js type is broken
|
|
28717
|
-
config.options.scales.y1.stacked = true;
|
|
28718
|
-
}
|
|
28719
|
-
}
|
|
28953
|
+
config.options.scales = removeFalsyAttributes(config.options.scales);
|
|
28720
28954
|
config.options.plugins.chartShowValuesPlugin = {
|
|
28721
28955
|
showValues: chart.showValues,
|
|
28722
28956
|
background: chart.background,
|
|
28723
|
-
callback:
|
|
28957
|
+
callback: formatChartDatasetValue(axisFormats, locale),
|
|
28724
28958
|
};
|
|
28725
28959
|
if (chart.dataSetsHaveTitle &&
|
|
28726
28960
|
dataSetsValues[0] &&
|
|
@@ -28739,7 +28973,6 @@ function createLineOrScatterChartRuntime(chart, getters) {
|
|
|
28739
28973
|
else if (axisType === "linear") {
|
|
28740
28974
|
config.options.scales.x.type = "linear";
|
|
28741
28975
|
config.options.scales.x.ticks.callback = (value) => formatValue(value, { format: labelFormat, locale });
|
|
28742
|
-
config.options.plugins.tooltip.callbacks.title = () => "";
|
|
28743
28976
|
config.options.plugins.tooltip.callbacks.label = (tooltipItem) => {
|
|
28744
28977
|
const dataSetPoint = dataSetsValues[tooltipItem.datasetIndex].data[tooltipItem.dataIndex];
|
|
28745
28978
|
let label = tooltipItem.label || labelValues.values[tooltipItem.dataIndex];
|
|
@@ -28747,7 +28980,7 @@ function createLineOrScatterChartRuntime(chart, getters) {
|
|
|
28747
28980
|
label = toNumber(label, locale);
|
|
28748
28981
|
}
|
|
28749
28982
|
const formattedX = formatValue(label, { locale, format: labelFormat });
|
|
28750
|
-
const formattedY = formatValue(dataSetPoint, { locale, format:
|
|
28983
|
+
const formattedY = formatValue(dataSetPoint, { locale, format: leftAxisFormat });
|
|
28751
28984
|
const dataSetTitle = tooltipItem.dataset.label;
|
|
28752
28985
|
return formattedX
|
|
28753
28986
|
? `${dataSetTitle}: (${formattedX}, ${formattedY})`
|
|
@@ -28757,7 +28990,6 @@ function createLineOrScatterChartRuntime(chart, getters) {
|
|
|
28757
28990
|
const areaChart = "fillArea" in chart ? chart.fillArea : false;
|
|
28758
28991
|
const stackedChart = "stacked" in chart ? chart.stacked : false;
|
|
28759
28992
|
const cumulative = "cumulative" in chart ? chart.cumulative : false;
|
|
28760
|
-
const definition = chart.getDefinition();
|
|
28761
28993
|
const colors = getChartColorsGenerator(definition, dataSetsValues.length);
|
|
28762
28994
|
for (let [index, { label, data }] of dataSetsValues.entries()) {
|
|
28763
28995
|
const color = colors.next();
|
|
@@ -28795,12 +29027,9 @@ function createLineOrScatterChartRuntime(chart, getters) {
|
|
|
28795
29027
|
const trendDatasets = [];
|
|
28796
29028
|
for (const [index, dataset] of config.data.datasets.entries()) {
|
|
28797
29029
|
if (definition.dataSets?.[index]?.label) {
|
|
28798
|
-
|
|
28799
|
-
dataset.label = label;
|
|
28800
|
-
}
|
|
28801
|
-
if (definition.dataSets?.[index]?.yAxisId) {
|
|
28802
|
-
dataset["yAxisID"] = definition.dataSets[index].yAxisId;
|
|
29030
|
+
dataset.label = definition.dataSets[index].label;
|
|
28803
29031
|
}
|
|
29032
|
+
dataset["yAxisID"] = definition.dataSets[index].yAxisId || "y";
|
|
28804
29033
|
const trend = definition.dataSets?.[index].trend;
|
|
28805
29034
|
if (!trend?.display) {
|
|
28806
29035
|
continue;
|
|
@@ -28817,7 +29046,7 @@ function createLineOrScatterChartRuntime(chart, getters) {
|
|
|
28817
29046
|
* set so that the second axis points match the classical x axis
|
|
28818
29047
|
*/
|
|
28819
29048
|
config.options.scales[TREND_LINE_XAXIS_ID] = {
|
|
28820
|
-
...
|
|
29049
|
+
...config.options.scales.x,
|
|
28821
29050
|
type: "category",
|
|
28822
29051
|
labels: range(0, maxLength).map((x) => x.toString()),
|
|
28823
29052
|
offset: false,
|
|
@@ -28827,22 +29056,15 @@ function createLineOrScatterChartRuntime(chart, getters) {
|
|
|
28827
29056
|
* distinguish the originals and trendLine datasets after
|
|
28828
29057
|
*/
|
|
28829
29058
|
trendDatasets.forEach((x) => config.data.datasets.push(x));
|
|
28830
|
-
const originalTooltipTitle = config.options.plugins.tooltip.callbacks.title;
|
|
28831
|
-
config.options.plugins.tooltip.callbacks.title = function (tooltipItems) {
|
|
28832
|
-
if (tooltipItems.some((item) => item.dataset.xAxisID !== TREND_LINE_XAXIS_ID)) {
|
|
28833
|
-
// @ts-expect-error
|
|
28834
|
-
return originalTooltipTitle?.(tooltipItems);
|
|
28835
|
-
}
|
|
28836
|
-
return "";
|
|
28837
|
-
};
|
|
28838
29059
|
}
|
|
29060
|
+
config.options.plugins.tooltip.callbacks.title = function (tooltipItems) {
|
|
29061
|
+
const displayTooltipTitle = axisType !== "linear" &&
|
|
29062
|
+
tooltipItems.some((item) => item.dataset.xAxisID !== TREND_LINE_XAXIS_ID);
|
|
29063
|
+
return displayTooltipTitle ? undefined : "";
|
|
29064
|
+
};
|
|
28839
29065
|
return {
|
|
28840
29066
|
chartJsConfig: config,
|
|
28841
29067
|
background: chart.background || BACKGROUND_CHART_COLOR,
|
|
28842
|
-
dataSetsValues,
|
|
28843
|
-
labelValues,
|
|
28844
|
-
dataSetFormat,
|
|
28845
|
-
labelFormat,
|
|
28846
29068
|
};
|
|
28847
29069
|
}
|
|
28848
29070
|
|
|
@@ -28900,6 +29122,7 @@ class ComboChart extends AbstractChart {
|
|
|
28900
29122
|
ranges.push({
|
|
28901
29123
|
...this.dataSetDesign?.[i],
|
|
28902
29124
|
dataRange: this.getters.getRangeString(dataSet.dataRange, targetSheetId || this.sheetId),
|
|
29125
|
+
type: this.dataSetDesign?.[i]?.type ?? (i ? "line" : "bar"),
|
|
28903
29126
|
});
|
|
28904
29127
|
}
|
|
28905
29128
|
return {
|
|
@@ -28945,9 +29168,13 @@ class ComboChart extends AbstractChart {
|
|
|
28945
29168
|
return new ComboChart(definition, this.sheetId, this.getters);
|
|
28946
29169
|
}
|
|
28947
29170
|
static getDefinitionFromContextCreation(context) {
|
|
29171
|
+
const dataSets = (context.range ?? []).map((ds, index) => ({
|
|
29172
|
+
...ds,
|
|
29173
|
+
type: index ? "line" : "bar",
|
|
29174
|
+
}));
|
|
28948
29175
|
return {
|
|
28949
29176
|
background: context.background,
|
|
28950
|
-
dataSets
|
|
29177
|
+
dataSets,
|
|
28951
29178
|
dataSetsHaveTitle: context.dataSetsHaveTitle ?? false,
|
|
28952
29179
|
aggregated: context.aggregated,
|
|
28953
29180
|
legendPosition: context.legendPosition ?? "top",
|
|
@@ -28970,10 +29197,8 @@ class ComboChart extends AbstractChart {
|
|
|
28970
29197
|
}
|
|
28971
29198
|
}
|
|
28972
29199
|
function createComboChartRuntime(chart, getters) {
|
|
28973
|
-
const mainDataSetFormat = chart.dataSets
|
|
28974
|
-
|
|
28975
|
-
: undefined;
|
|
28976
|
-
const lineDataSetsFormat = getChartDatasetFormat(getters, chart.dataSets.slice(1));
|
|
29200
|
+
const mainDataSetFormat = getChartDatasetFormat(getters, chart.dataSets, "left");
|
|
29201
|
+
const lineDataSetsFormat = getChartDatasetFormat(getters, chart.dataSets, "right");
|
|
28977
29202
|
const locale = getters.getLocale();
|
|
28978
29203
|
const labelValues = getChartLabelValues(getters, chart.dataSets, chart.labelRange);
|
|
28979
29204
|
let labels = labelValues.formattedValues;
|
|
@@ -28987,12 +29212,11 @@ function createComboChartRuntime(chart, getters) {
|
|
|
28987
29212
|
if (chart.aggregated) {
|
|
28988
29213
|
({ labels, dataSetsValues } = aggregateDataForLabels(labels, dataSetsValues));
|
|
28989
29214
|
}
|
|
28990
|
-
const localeFormat = { format: mainDataSetFormat, locale };
|
|
28991
29215
|
const fontColor = chartFontColor(chart.background);
|
|
28992
|
-
const
|
|
29216
|
+
const axisFormats = { y: mainDataSetFormat, y1: lineDataSetsFormat };
|
|
29217
|
+
const config = getDefaultChartJsRuntime(chart, labels, fontColor, { locale, axisFormats });
|
|
28993
29218
|
const legend = {
|
|
28994
29219
|
labels: { color: fontColor },
|
|
28995
|
-
reverse: true,
|
|
28996
29220
|
};
|
|
28997
29221
|
if (chart.legendPosition === "none") {
|
|
28998
29222
|
legend.display = false;
|
|
@@ -29007,52 +29231,17 @@ function createComboChartRuntime(chart, getters) {
|
|
|
29007
29231
|
displayLegend: chart.legendPosition === "top",
|
|
29008
29232
|
}),
|
|
29009
29233
|
};
|
|
29234
|
+
const definition = chart.getDefinition();
|
|
29010
29235
|
config.options.scales = {
|
|
29011
|
-
x: {
|
|
29012
|
-
|
|
29013
|
-
|
|
29014
|
-
color: fontColor,
|
|
29015
|
-
},
|
|
29016
|
-
title: getChartAxisTitleRuntime(chart.axesDesign?.x),
|
|
29017
|
-
},
|
|
29236
|
+
x: getChartAxis(definition, "bottom", "labels", { locale }),
|
|
29237
|
+
y: getChartAxis(definition, "left", "values", { locale, format: mainDataSetFormat }),
|
|
29238
|
+
y1: getChartAxis(definition, "right", "values", { locale, format: lineDataSetsFormat }),
|
|
29018
29239
|
};
|
|
29019
|
-
|
|
29020
|
-
beginAtZero: true, // the origin of the y axis is always zero
|
|
29021
|
-
ticks: {
|
|
29022
|
-
color: fontColor,
|
|
29023
|
-
callback: formatTickValue({ format: mainDataSetFormat, locale }),
|
|
29024
|
-
},
|
|
29025
|
-
};
|
|
29026
|
-
const rightVerticalAxis = {
|
|
29027
|
-
beginAtZero: true, // the origin of the y axis is always zero
|
|
29028
|
-
ticks: {
|
|
29029
|
-
color: fontColor,
|
|
29030
|
-
callback: formatTickValue({ format: lineDataSetsFormat, locale }),
|
|
29031
|
-
},
|
|
29032
|
-
};
|
|
29033
|
-
const definition = chart.getDefinition();
|
|
29034
|
-
const { useLeftAxis, useRightAxis } = getDefinedAxis(definition);
|
|
29035
|
-
if (useLeftAxis) {
|
|
29036
|
-
config.options.scales.y = {
|
|
29037
|
-
...leftVerticalAxis,
|
|
29038
|
-
position: "left",
|
|
29039
|
-
title: getChartAxisTitleRuntime(chart.axesDesign?.y),
|
|
29040
|
-
};
|
|
29041
|
-
}
|
|
29042
|
-
if (useRightAxis) {
|
|
29043
|
-
config.options.scales.y1 = {
|
|
29044
|
-
...rightVerticalAxis,
|
|
29045
|
-
position: "right",
|
|
29046
|
-
grid: {
|
|
29047
|
-
display: false,
|
|
29048
|
-
},
|
|
29049
|
-
title: getChartAxisTitleRuntime(chart.axesDesign?.y1),
|
|
29050
|
-
};
|
|
29051
|
-
}
|
|
29240
|
+
config.options.scales = removeFalsyAttributes(config.options.scales);
|
|
29052
29241
|
config.options.plugins.chartShowValuesPlugin = {
|
|
29053
29242
|
showValues: chart.showValues,
|
|
29054
29243
|
background: chart.background,
|
|
29055
|
-
callback:
|
|
29244
|
+
callback: formatChartDatasetValue(axisFormats, locale),
|
|
29056
29245
|
};
|
|
29057
29246
|
const colors = getChartColorsGenerator(definition, dataSetsValues.length);
|
|
29058
29247
|
let maxLength = 0;
|
|
@@ -29060,14 +29249,15 @@ function createComboChartRuntime(chart, getters) {
|
|
|
29060
29249
|
for (let [index, { label, data }] of dataSetsValues.entries()) {
|
|
29061
29250
|
const design = definition.dataSets[index];
|
|
29062
29251
|
const color = colors.next();
|
|
29252
|
+
const type = design?.type ?? "line";
|
|
29063
29253
|
const dataset = {
|
|
29064
29254
|
label: design?.label ?? label,
|
|
29065
29255
|
data,
|
|
29066
29256
|
borderColor: color,
|
|
29067
29257
|
backgroundColor: color,
|
|
29068
29258
|
yAxisID: design?.yAxisId ?? "y",
|
|
29069
|
-
type
|
|
29070
|
-
order:
|
|
29259
|
+
type,
|
|
29260
|
+
order: type === "bar" ? dataSetsValues.length + index : index,
|
|
29071
29261
|
};
|
|
29072
29262
|
config.data.datasets.push(dataset);
|
|
29073
29263
|
const trend = definition.dataSets?.[index].trend;
|
|
@@ -29094,13 +29284,10 @@ function createComboChartRuntime(chart, getters) {
|
|
|
29094
29284
|
* distinguish the originals and trendLine datasets after
|
|
29095
29285
|
*/
|
|
29096
29286
|
trendDatasets.forEach((x) => config.data.datasets.push(x));
|
|
29097
|
-
const originalTooltipTitle = config.options.plugins.tooltip.callbacks.title;
|
|
29098
29287
|
config.options.plugins.tooltip.callbacks.title = function (tooltipItems) {
|
|
29099
|
-
|
|
29100
|
-
|
|
29101
|
-
|
|
29102
|
-
}
|
|
29103
|
-
return "";
|
|
29288
|
+
return tooltipItems.some((item) => item.dataset.xAxisID !== TREND_LINE_XAXIS_ID)
|
|
29289
|
+
? undefined
|
|
29290
|
+
: "";
|
|
29104
29291
|
};
|
|
29105
29292
|
}
|
|
29106
29293
|
return { chartJsConfig: config, background: chart.background || BACKGROUND_CHART_COLOR };
|
|
@@ -29680,7 +29867,7 @@ function createPieChartRuntime(chart, getters) {
|
|
|
29680
29867
|
({ labels, dataSetsValues } = aggregateDataForLabels(labels, dataSetsValues));
|
|
29681
29868
|
}
|
|
29682
29869
|
({ dataSetsValues, labels } = filterNegativeValues(labels, dataSetsValues));
|
|
29683
|
-
const dataSetFormat = getChartDatasetFormat(getters, chart.dataSets);
|
|
29870
|
+
const dataSetFormat = getChartDatasetFormat(getters, chart.dataSets, "left");
|
|
29684
29871
|
const locale = getters.getLocale();
|
|
29685
29872
|
const config = getPieConfiguration(chart, labels, { format: dataSetFormat, locale });
|
|
29686
29873
|
const dataSetsLength = Math.max(0, ...dataSetsValues.map((ds) => ds?.data?.length ?? 0));
|
|
@@ -29835,7 +30022,199 @@ function createPyramidChartRuntime(chart, getters) {
|
|
|
29835
30022
|
return tooltipLabelCallback(tooltipItem);
|
|
29836
30023
|
};
|
|
29837
30024
|
const callback = config.options.plugins.chartShowValuesPlugin.callback;
|
|
29838
|
-
config.options.plugins.chartShowValuesPlugin.callback = (x) => callback(Math.abs(x));
|
|
30025
|
+
config.options.plugins.chartShowValuesPlugin.callback = (x, axisId) => callback(Math.abs(x), axisId);
|
|
30026
|
+
return { chartJsConfig: config, background: chart.background || BACKGROUND_CHART_COLOR };
|
|
30027
|
+
}
|
|
30028
|
+
|
|
30029
|
+
class RadarChart extends AbstractChart {
|
|
30030
|
+
dataSets;
|
|
30031
|
+
labelRange;
|
|
30032
|
+
background;
|
|
30033
|
+
legendPosition;
|
|
30034
|
+
stacked;
|
|
30035
|
+
aggregated;
|
|
30036
|
+
type = "radar";
|
|
30037
|
+
dataSetsHaveTitle;
|
|
30038
|
+
dataSetDesign;
|
|
30039
|
+
fillArea;
|
|
30040
|
+
constructor(definition, sheetId, getters) {
|
|
30041
|
+
super(definition, sheetId, getters);
|
|
30042
|
+
this.dataSets = createDataSets(getters, definition.dataSets, sheetId, definition.dataSetsHaveTitle);
|
|
30043
|
+
this.labelRange = createValidRange(getters, sheetId, definition.labelRange);
|
|
30044
|
+
this.background = definition.background;
|
|
30045
|
+
this.legendPosition = definition.legendPosition;
|
|
30046
|
+
this.stacked = definition.stacked;
|
|
30047
|
+
this.aggregated = definition.aggregated;
|
|
30048
|
+
this.dataSetsHaveTitle = definition.dataSetsHaveTitle;
|
|
30049
|
+
this.dataSetDesign = definition.dataSets;
|
|
30050
|
+
this.fillArea = definition.fillArea;
|
|
30051
|
+
}
|
|
30052
|
+
static transformDefinition(definition, executed) {
|
|
30053
|
+
return transformChartDefinitionWithDataSetsWithZone(definition, executed);
|
|
30054
|
+
}
|
|
30055
|
+
static validateChartDefinition(validator, definition) {
|
|
30056
|
+
return validator.checkValidations(definition, checkDataset, checkLabelRange);
|
|
30057
|
+
}
|
|
30058
|
+
static getDefinitionFromContextCreation(context) {
|
|
30059
|
+
return {
|
|
30060
|
+
background: context.background,
|
|
30061
|
+
dataSets: context.range ?? [],
|
|
30062
|
+
dataSetsHaveTitle: context.dataSetsHaveTitle ?? false,
|
|
30063
|
+
stacked: context.stacked ?? false,
|
|
30064
|
+
aggregated: context.aggregated ?? false,
|
|
30065
|
+
legendPosition: context.legendPosition ?? "top",
|
|
30066
|
+
title: context.title || { text: "" },
|
|
30067
|
+
type: "radar",
|
|
30068
|
+
labelRange: context.auxiliaryRange || undefined,
|
|
30069
|
+
fillArea: context.fillArea ?? false,
|
|
30070
|
+
};
|
|
30071
|
+
}
|
|
30072
|
+
getContextCreation() {
|
|
30073
|
+
const range = [];
|
|
30074
|
+
for (const [i, dataSet] of this.dataSets.entries()) {
|
|
30075
|
+
range.push({
|
|
30076
|
+
...this.dataSetDesign?.[i],
|
|
30077
|
+
dataRange: this.getters.getRangeString(dataSet.dataRange, this.sheetId),
|
|
30078
|
+
});
|
|
30079
|
+
}
|
|
30080
|
+
return {
|
|
30081
|
+
...this,
|
|
30082
|
+
range,
|
|
30083
|
+
auxiliaryRange: this.labelRange
|
|
30084
|
+
? this.getters.getRangeString(this.labelRange, this.sheetId)
|
|
30085
|
+
: undefined,
|
|
30086
|
+
};
|
|
30087
|
+
}
|
|
30088
|
+
copyForSheetId(sheetId) {
|
|
30089
|
+
const dataSets = copyDataSetsWithNewSheetId(this.sheetId, sheetId, this.dataSets);
|
|
30090
|
+
const labelRange = copyLabelRangeWithNewSheetId(this.sheetId, sheetId, this.labelRange);
|
|
30091
|
+
const definition = this.getDefinitionWithSpecificDataSets(dataSets, labelRange, sheetId);
|
|
30092
|
+
return new RadarChart(definition, sheetId, this.getters);
|
|
30093
|
+
}
|
|
30094
|
+
copyInSheetId(sheetId) {
|
|
30095
|
+
const definition = this.getDefinitionWithSpecificDataSets(this.dataSets, this.labelRange, sheetId);
|
|
30096
|
+
return new RadarChart(definition, sheetId, this.getters);
|
|
30097
|
+
}
|
|
30098
|
+
getDefinition() {
|
|
30099
|
+
return this.getDefinitionWithSpecificDataSets(this.dataSets, this.labelRange);
|
|
30100
|
+
}
|
|
30101
|
+
getDefinitionWithSpecificDataSets(dataSets, labelRange, targetSheetId) {
|
|
30102
|
+
const ranges = [];
|
|
30103
|
+
for (const [i, dataSet] of dataSets.entries()) {
|
|
30104
|
+
ranges.push({
|
|
30105
|
+
...this.dataSetDesign?.[i],
|
|
30106
|
+
dataRange: this.getters.getRangeString(dataSet.dataRange, targetSheetId || this.sheetId),
|
|
30107
|
+
});
|
|
30108
|
+
}
|
|
30109
|
+
return {
|
|
30110
|
+
type: "radar",
|
|
30111
|
+
dataSetsHaveTitle: dataSets.length ? Boolean(dataSets[0].labelCell) : false,
|
|
30112
|
+
background: this.background,
|
|
30113
|
+
dataSets: ranges,
|
|
30114
|
+
legendPosition: this.legendPosition,
|
|
30115
|
+
labelRange: labelRange
|
|
30116
|
+
? this.getters.getRangeString(labelRange, targetSheetId || this.sheetId)
|
|
30117
|
+
: undefined,
|
|
30118
|
+
title: this.title,
|
|
30119
|
+
stacked: this.stacked,
|
|
30120
|
+
aggregated: this.aggregated,
|
|
30121
|
+
fillArea: this.fillArea,
|
|
30122
|
+
};
|
|
30123
|
+
}
|
|
30124
|
+
getDefinitionForExcel() {
|
|
30125
|
+
if (this.aggregated) {
|
|
30126
|
+
return undefined;
|
|
30127
|
+
}
|
|
30128
|
+
const dataSets = this.dataSets
|
|
30129
|
+
.map((ds) => toExcelDataset(this.getters, ds))
|
|
30130
|
+
.filter((ds) => ds.range !== "" && ds.range !== CellErrorType.InvalidReference);
|
|
30131
|
+
const labelRange = toExcelLabelRange(this.getters, this.labelRange, shouldRemoveFirstLabel(this.labelRange, this.dataSets[0], this.dataSetsHaveTitle));
|
|
30132
|
+
const definition = this.getDefinition();
|
|
30133
|
+
return {
|
|
30134
|
+
...definition,
|
|
30135
|
+
backgroundColor: toXlsxHexColor(this.background || BACKGROUND_CHART_COLOR),
|
|
30136
|
+
fontColor: toXlsxHexColor(chartFontColor(this.background)),
|
|
30137
|
+
dataSets,
|
|
30138
|
+
labelRange,
|
|
30139
|
+
};
|
|
30140
|
+
}
|
|
30141
|
+
updateRanges(applyChange) {
|
|
30142
|
+
const { dataSets, labelRange, isStale } = updateChartRangesWithDataSets(this.getters, applyChange, this.dataSets, this.labelRange);
|
|
30143
|
+
if (!isStale) {
|
|
30144
|
+
return this;
|
|
30145
|
+
}
|
|
30146
|
+
const definition = this.getDefinitionWithSpecificDataSets(dataSets, labelRange);
|
|
30147
|
+
return new RadarChart(definition, this.sheetId, this.getters);
|
|
30148
|
+
}
|
|
30149
|
+
}
|
|
30150
|
+
function createRadarChartRuntime(chart, getters) {
|
|
30151
|
+
const definition = chart.getDefinition();
|
|
30152
|
+
const labelValues = getChartLabelValues(getters, chart.dataSets, chart.labelRange);
|
|
30153
|
+
let labels = labelValues.formattedValues;
|
|
30154
|
+
let dataSetsValues = getChartDatasetValues(getters, chart.dataSets);
|
|
30155
|
+
if (chart.dataSetsHaveTitle &&
|
|
30156
|
+
dataSetsValues[0] &&
|
|
30157
|
+
labels.length > dataSetsValues[0].data.length) {
|
|
30158
|
+
labels.shift();
|
|
30159
|
+
}
|
|
30160
|
+
({ labels, dataSetsValues } = filterEmptyDataPoints(labels, dataSetsValues));
|
|
30161
|
+
if (chart.aggregated) {
|
|
30162
|
+
({ labels, dataSetsValues } = aggregateDataForLabels(labels, dataSetsValues));
|
|
30163
|
+
}
|
|
30164
|
+
const leftAxisFormat = getChartDatasetFormat(getters, chart.dataSets, "left");
|
|
30165
|
+
const rightAxisFormat = getChartDatasetFormat(getters, chart.dataSets, "right");
|
|
30166
|
+
const axisFormats = { y: leftAxisFormat, y1: rightAxisFormat };
|
|
30167
|
+
const locale = getters.getLocale();
|
|
30168
|
+
const fontColor = chartFontColor(chart.background);
|
|
30169
|
+
const config = getDefaultChartJsRuntime(chart, labels, fontColor, {
|
|
30170
|
+
axisFormats,
|
|
30171
|
+
locale,
|
|
30172
|
+
});
|
|
30173
|
+
const legend = {
|
|
30174
|
+
labels: { color: fontColor },
|
|
30175
|
+
};
|
|
30176
|
+
if ((!chart.labelRange && chart.dataSets.length === 1) || chart.legendPosition === "none") {
|
|
30177
|
+
legend.display = false;
|
|
30178
|
+
}
|
|
30179
|
+
else {
|
|
30180
|
+
legend.position = chart.legendPosition;
|
|
30181
|
+
}
|
|
30182
|
+
const fill = definition.fillArea ?? false;
|
|
30183
|
+
if (!fill) {
|
|
30184
|
+
legend.labels["boxHeight"] = 0;
|
|
30185
|
+
}
|
|
30186
|
+
config.options.plugins.legend = { ...config.options.plugins?.legend, ...legend };
|
|
30187
|
+
config.options.plugins.tooltip = {
|
|
30188
|
+
...config.options.plugins?.tooltip,
|
|
30189
|
+
callbacks: {
|
|
30190
|
+
label: function (tooltipItem) {
|
|
30191
|
+
const xLabel = tooltipItem.dataset?.label || tooltipItem.label;
|
|
30192
|
+
const yLabel = tooltipItem.parsed.r;
|
|
30193
|
+
return xLabel ? `${xLabel}: ${yLabel}` : yLabel.toString();
|
|
30194
|
+
},
|
|
30195
|
+
},
|
|
30196
|
+
};
|
|
30197
|
+
config.options.layout = {
|
|
30198
|
+
padding: { left: 20, right: 20, top: chart.title ? 10 : 25, bottom: 10 },
|
|
30199
|
+
};
|
|
30200
|
+
const colorGenerator = getChartColorsGenerator(definition, dataSetsValues.length);
|
|
30201
|
+
for (let i = 0; i < dataSetsValues.length; i++) {
|
|
30202
|
+
let { label, data } = dataSetsValues[i];
|
|
30203
|
+
if (definition.dataSets?.[i]?.label) {
|
|
30204
|
+
label = definition.dataSets[i].label;
|
|
30205
|
+
}
|
|
30206
|
+
const borderColor = colorGenerator.next();
|
|
30207
|
+
const dataset = {
|
|
30208
|
+
label,
|
|
30209
|
+
data,
|
|
30210
|
+
borderColor,
|
|
30211
|
+
};
|
|
30212
|
+
if (fill) {
|
|
30213
|
+
dataset.backgroundColor = setColorAlpha(borderColor, 0.3);
|
|
30214
|
+
dataset["fill"] = true;
|
|
30215
|
+
}
|
|
30216
|
+
config.data.datasets.push(dataset);
|
|
30217
|
+
}
|
|
29839
30218
|
return { chartJsConfig: config, background: chart.background || BACKGROUND_CHART_COLOR };
|
|
29840
30219
|
}
|
|
29841
30220
|
|
|
@@ -30226,7 +30605,8 @@ function createWaterfallChartRuntime(chart, getters) {
|
|
|
30226
30605
|
if (chart.showSubTotals) {
|
|
30227
30606
|
labels.push(_t("Subtotal"));
|
|
30228
30607
|
}
|
|
30229
|
-
const dataSetFormat = getChartDatasetFormat(getters, chart.dataSets)
|
|
30608
|
+
const dataSetFormat = getChartDatasetFormat(getters, chart.dataSets, "left") ||
|
|
30609
|
+
getChartDatasetFormat(getters, chart.dataSets, "right");
|
|
30230
30610
|
const locale = getters.getLocale();
|
|
30231
30611
|
const dataSeriesLabels = dataSetsValues.map((dataSet) => dataSet.label);
|
|
30232
30612
|
const config = getWaterfallConfiguration(chart, labels, dataSeriesLabels, {
|
|
@@ -30360,6 +30740,15 @@ chartRegistry.add("pyramid", {
|
|
|
30360
30740
|
getChartDefinitionFromContextCreation: PyramidChart.getDefinitionFromContextCreation,
|
|
30361
30741
|
sequence: 80,
|
|
30362
30742
|
});
|
|
30743
|
+
chartRegistry.add("radar", {
|
|
30744
|
+
match: (type) => type === "radar",
|
|
30745
|
+
createChart: (definition, sheetId, getters) => new RadarChart(definition, sheetId, getters),
|
|
30746
|
+
getChartRuntime: createRadarChartRuntime,
|
|
30747
|
+
validateChartDefinition: RadarChart.validateChartDefinition,
|
|
30748
|
+
transformDefinition: RadarChart.transformDefinition,
|
|
30749
|
+
getChartDefinitionFromContextCreation: RadarChart.getDefinitionFromContextCreation,
|
|
30750
|
+
sequence: 80,
|
|
30751
|
+
});
|
|
30363
30752
|
const chartComponentRegistry = new Registry();
|
|
30364
30753
|
chartComponentRegistry.add("line", ChartJsComponent);
|
|
30365
30754
|
chartComponentRegistry.add("bar", ChartJsComponent);
|
|
@@ -30370,6 +30759,7 @@ chartComponentRegistry.add("scatter", ChartJsComponent);
|
|
|
30370
30759
|
chartComponentRegistry.add("scorecard", ScorecardChart);
|
|
30371
30760
|
chartComponentRegistry.add("waterfall", ChartJsComponent);
|
|
30372
30761
|
chartComponentRegistry.add("pyramid", ChartJsComponent);
|
|
30762
|
+
chartComponentRegistry.add("radar", ChartJsComponent);
|
|
30373
30763
|
const chartCategories = {
|
|
30374
30764
|
line: _t("Line"),
|
|
30375
30765
|
column: _t("Column"),
|
|
@@ -30511,6 +30901,24 @@ chartSubtypeRegistry
|
|
|
30511
30901
|
chartType: "pyramid",
|
|
30512
30902
|
category: "misc",
|
|
30513
30903
|
preview: "o-spreadsheet-ChartPreview.POPULATION_PYRAMID_CHART",
|
|
30904
|
+
})
|
|
30905
|
+
.add("radar", {
|
|
30906
|
+
matcher: (definition) => definition.type === "radar" && !definition.fillArea,
|
|
30907
|
+
displayName: _t("Radar"),
|
|
30908
|
+
chartSubtype: "radar",
|
|
30909
|
+
chartType: "radar",
|
|
30910
|
+
subtypeDefinition: { fillArea: false },
|
|
30911
|
+
category: "misc",
|
|
30912
|
+
preview: "o-spreadsheet-ChartPreview.RADAR_CHART",
|
|
30913
|
+
})
|
|
30914
|
+
.add("filled_radar", {
|
|
30915
|
+
matcher: (definition) => definition.type === "radar" && !!definition.fillArea,
|
|
30916
|
+
displayName: _t("Filled Radar"),
|
|
30917
|
+
chartType: "radar",
|
|
30918
|
+
chartSubtype: "filled_radar",
|
|
30919
|
+
subtypeDefinition: { fillArea: true },
|
|
30920
|
+
category: "misc",
|
|
30921
|
+
preview: "o-spreadsheet-ChartPreview.FILLED_RADAR_CHART",
|
|
30514
30922
|
});
|
|
30515
30923
|
|
|
30516
30924
|
/**
|
|
@@ -30578,11 +30986,10 @@ function centerFigurePosition(getters, size) {
|
|
|
30578
30986
|
const rect = getters.getVisibleRect(getters.getActiveMainViewport());
|
|
30579
30987
|
const scrollableViewportWidth = Math.min(rect.width, dim.width - offsetCorrectionX);
|
|
30580
30988
|
const scrollableViewportHeight = Math.min(rect.height, dim.height - offsetCorrectionY);
|
|
30581
|
-
|
|
30989
|
+
return {
|
|
30582
30990
|
x: offsetCorrectionX + scrollX + Math.max(0, (scrollableViewportWidth - size.width) / 2),
|
|
30583
30991
|
y: offsetCorrectionY + scrollY + Math.max(0, (scrollableViewportHeight - size.height) / 2),
|
|
30584
30992
|
}; // Position at the center of the scrollable viewport
|
|
30585
|
-
return position;
|
|
30586
30993
|
}
|
|
30587
30994
|
function getMaxFigureSize(getters, figureSize) {
|
|
30588
30995
|
const size = deepCopy(figureSize);
|
|
@@ -31155,14 +31562,13 @@ class PopoverPositionContext {
|
|
|
31155
31562
|
const shouldRenderAtBottom = this.shouldRenderAtBottom(elDims.height);
|
|
31156
31563
|
const shouldRenderAtRight = this.shouldRenderAtRight(elDims.width);
|
|
31157
31564
|
verticalOffset = shouldRenderAtBottom ? verticalOffset : -verticalOffset;
|
|
31158
|
-
|
|
31565
|
+
return {
|
|
31159
31566
|
top: this.getTopCoordinate(actualHeight, shouldRenderAtBottom) -
|
|
31160
31567
|
this.spreadsheetOffset.y -
|
|
31161
31568
|
verticalOffset +
|
|
31162
31569
|
"px",
|
|
31163
31570
|
left: this.getLeftCoordinate(actualWidth, shouldRenderAtRight) - this.spreadsheetOffset.x + "px",
|
|
31164
31571
|
};
|
|
31165
|
-
return cssProperties;
|
|
31166
31572
|
}
|
|
31167
31573
|
getCurrentPosition(elDims) {
|
|
31168
31574
|
const shouldRenderAtBottom = this.shouldRenderAtBottom(elDims.height);
|
|
@@ -32390,7 +32796,7 @@ function getSmartChartDefinition(zone, getters) {
|
|
|
32390
32796
|
* Create a table on the selected zone, with UI warnings to the user if the creation fails.
|
|
32391
32797
|
* If a single cell is selected, expand the selection to non-empty adjacent cells to create a table.
|
|
32392
32798
|
*/
|
|
32393
|
-
function interactiveCreateTable(env, sheetId, tableConfig) {
|
|
32799
|
+
function interactiveCreateTable(env, sheetId, tableConfig = DEFAULT_TABLE_CONFIG) {
|
|
32394
32800
|
let target = env.model.getters.getSelectedZones();
|
|
32395
32801
|
let isDynamic = env.model.getters.canCreateDynamicTableOnZones(sheetId, target);
|
|
32396
32802
|
if (target.length === 1 && !isDynamic && getZoneArea(target[0]) === 1) {
|
|
@@ -33449,7 +33855,7 @@ function getColumnsNumber(env) {
|
|
|
33449
33855
|
}
|
|
33450
33856
|
|
|
33451
33857
|
const pivotProperties = {
|
|
33452
|
-
name: _t("
|
|
33858
|
+
name: _t("See pivot properties"),
|
|
33453
33859
|
execute(env) {
|
|
33454
33860
|
const position = env.model.getters.getActivePosition();
|
|
33455
33861
|
const pivotId = env.model.getters.getPivotIdFromPosition(position);
|
|
@@ -33603,8 +34009,7 @@ cellMenuRegistry
|
|
|
33603
34009
|
})
|
|
33604
34010
|
.add("pivot_properties", {
|
|
33605
34011
|
...pivotProperties,
|
|
33606
|
-
sequence:
|
|
33607
|
-
separator: true,
|
|
34012
|
+
sequence: 170,
|
|
33608
34013
|
});
|
|
33609
34014
|
|
|
33610
34015
|
const sortRange = {
|
|
@@ -35296,6 +35701,7 @@ const CHECK_SVG = /*xml*/ `
|
|
|
35296
35701
|
<path fill='none' stroke='#FFF' stroke-linecap='round' stroke-linejoin='round' stroke-width='3' d='m6 10 3 3 6-6'/>
|
|
35297
35702
|
</svg>
|
|
35298
35703
|
`;
|
|
35704
|
+
const CHECKBOX_WIDTH = 14;
|
|
35299
35705
|
css /* scss */ `
|
|
35300
35706
|
label.o-checkbox {
|
|
35301
35707
|
input {
|
|
@@ -35303,8 +35709,8 @@ css /* scss */ `
|
|
|
35303
35709
|
-webkit-appearance: none;
|
|
35304
35710
|
-moz-appearance: none;
|
|
35305
35711
|
border-radius: 0;
|
|
35306
|
-
width:
|
|
35307
|
-
height:
|
|
35712
|
+
width: ${CHECKBOX_WIDTH}px;
|
|
35713
|
+
height: ${CHECKBOX_WIDTH}px;
|
|
35308
35714
|
vertical-align: top;
|
|
35309
35715
|
box-sizing: border-box;
|
|
35310
35716
|
outline: none;
|
|
@@ -35350,6 +35756,7 @@ class Section extends owl.Component {
|
|
|
35350
35756
|
static template = "o_spreadsheet.Section";
|
|
35351
35757
|
static props = {
|
|
35352
35758
|
class: { type: String, optional: true },
|
|
35759
|
+
title: { type: String, optional: true },
|
|
35353
35760
|
slots: Object,
|
|
35354
35761
|
};
|
|
35355
35762
|
}
|
|
@@ -36079,17 +36486,11 @@ class BarConfigPanel extends GenericChartConfigPanel {
|
|
|
36079
36486
|
stacked,
|
|
36080
36487
|
});
|
|
36081
36488
|
}
|
|
36082
|
-
onUpdateAggregated(aggregated) {
|
|
36083
|
-
this.props.updateChart(this.props.figureId, {
|
|
36084
|
-
aggregated,
|
|
36085
|
-
});
|
|
36086
|
-
}
|
|
36087
36489
|
}
|
|
36088
36490
|
|
|
36089
36491
|
css /* scss */ `
|
|
36090
36492
|
.o_side_panel_collapsible_title {
|
|
36091
36493
|
font-size: 16px;
|
|
36092
|
-
font-weight: bold;
|
|
36093
36494
|
cursor: pointer;
|
|
36094
36495
|
padding: 6px 0px 6px 6px !important;
|
|
36095
36496
|
|
|
@@ -36126,49 +36527,40 @@ class SidePanelCollapsible extends owl.Component {
|
|
|
36126
36527
|
static template = "o-spreadsheet-SidePanelCollapsible";
|
|
36127
36528
|
static props = {
|
|
36128
36529
|
slots: Object,
|
|
36530
|
+
title: { type: String, optional: true },
|
|
36129
36531
|
collapsedAtInit: { type: Boolean, optional: true },
|
|
36130
36532
|
class: { type: String, optional: true },
|
|
36131
36533
|
};
|
|
36132
36534
|
currentId = (CURRENT_COLLAPSIBLE_ID++).toString();
|
|
36133
36535
|
}
|
|
36134
36536
|
|
|
36135
|
-
const CIRCLE_SVG = /*xml*/ `
|
|
36136
|
-
<svg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'>
|
|
36137
|
-
<circle r="2" fill="#FFF"/>
|
|
36138
|
-
</svg>
|
|
36139
|
-
`;
|
|
36140
36537
|
css /* scss */ `
|
|
36141
|
-
.o-
|
|
36142
|
-
|
|
36143
|
-
|
|
36144
|
-
-
|
|
36145
|
-
|
|
36146
|
-
|
|
36147
|
-
height: 14px;
|
|
36148
|
-
border: 1px solid ${GRAY_300};
|
|
36149
|
-
box-sizing: border-box;
|
|
36150
|
-
outline: none;
|
|
36151
|
-
border-radius: 8px;
|
|
36152
|
-
|
|
36153
|
-
&:checked {
|
|
36154
|
-
background: url("data:image/svg+xml,${encodeURIComponent(CIRCLE_SVG)}");
|
|
36155
|
-
background-color: ${ACTION_COLOR};
|
|
36538
|
+
.o-badge-selection {
|
|
36539
|
+
gap: 1px;
|
|
36540
|
+
button.o-button {
|
|
36541
|
+
border-radius: 0;
|
|
36542
|
+
&.selected {
|
|
36543
|
+
color: ${GRAY_900};
|
|
36156
36544
|
border-color: ${ACTION_COLOR};
|
|
36545
|
+
background: ${BADGE_SELECTED_COLOR};
|
|
36546
|
+
font-weight: 600;
|
|
36547
|
+
}
|
|
36548
|
+
|
|
36549
|
+
&:first-child {
|
|
36550
|
+
border-radius: 4px 0 0 4px;
|
|
36551
|
+
}
|
|
36552
|
+
&:last-child {
|
|
36553
|
+
border-radius: 0 4px 4px 0;
|
|
36157
36554
|
}
|
|
36158
36555
|
}
|
|
36159
36556
|
}
|
|
36160
36557
|
`;
|
|
36161
|
-
class
|
|
36162
|
-
static template = "o-spreadsheet.
|
|
36558
|
+
class BadgeSelection extends owl.Component {
|
|
36559
|
+
static template = "o-spreadsheet.BadgeSelection";
|
|
36163
36560
|
static props = {
|
|
36164
36561
|
choices: Array,
|
|
36165
36562
|
onChange: Function,
|
|
36166
|
-
selectedValue:
|
|
36167
|
-
name: String,
|
|
36168
|
-
direction: { type: String, optional: true },
|
|
36169
|
-
};
|
|
36170
|
-
static defaultProps = {
|
|
36171
|
-
direction: "horizontal",
|
|
36563
|
+
selectedValue: String,
|
|
36172
36564
|
};
|
|
36173
36565
|
}
|
|
36174
36566
|
|
|
@@ -36713,86 +37105,6 @@ class ColorPickerWidget extends owl.Component {
|
|
|
36713
37105
|
}
|
|
36714
37106
|
}
|
|
36715
37107
|
|
|
36716
|
-
const TRANSPARENT_BACKGROUND_SVG = /*xml*/ `
|
|
36717
|
-
<svg xmlns="http://www.w3.org/2000/svg" width="10" height="10">
|
|
36718
|
-
<path fill="#d9d9d9" d="M5 5h5v5H5zH0V0h5"/>
|
|
36719
|
-
</svg>
|
|
36720
|
-
`;
|
|
36721
|
-
css /* scss */ `
|
|
36722
|
-
.o-round-color-picker-button {
|
|
36723
|
-
width: 18px;
|
|
36724
|
-
height: 18px;
|
|
36725
|
-
cursor: pointer;
|
|
36726
|
-
border: 1px solid ${GRAY_300};
|
|
36727
|
-
background-position: 1px 1px;
|
|
36728
|
-
background-image: url("data:image/svg+xml,${encodeURIComponent(TRANSPARENT_BACKGROUND_SVG)}");
|
|
36729
|
-
}
|
|
36730
|
-
`;
|
|
36731
|
-
class RoundColorPicker extends owl.Component {
|
|
36732
|
-
static template = "o-spreadsheet.RoundColorPicker";
|
|
36733
|
-
static components = { ColorPickerWidget, Section, ColorPicker };
|
|
36734
|
-
static props = {
|
|
36735
|
-
currentColor: { type: String, optional: true },
|
|
36736
|
-
title: { type: String, optional: true },
|
|
36737
|
-
onColorPicked: Function,
|
|
36738
|
-
};
|
|
36739
|
-
colorPickerButtonRef = owl.useRef("colorPickerButton");
|
|
36740
|
-
state;
|
|
36741
|
-
setup() {
|
|
36742
|
-
this.state = owl.useState({ pickerOpened: false });
|
|
36743
|
-
owl.useExternalListener(window, "click", this.closePicker);
|
|
36744
|
-
}
|
|
36745
|
-
closePicker() {
|
|
36746
|
-
this.state.pickerOpened = false;
|
|
36747
|
-
}
|
|
36748
|
-
togglePicker() {
|
|
36749
|
-
this.state.pickerOpened = !this.state.pickerOpened;
|
|
36750
|
-
}
|
|
36751
|
-
onColorPicked(color) {
|
|
36752
|
-
this.props.onColorPicked(color);
|
|
36753
|
-
this.state.pickerOpened = false;
|
|
36754
|
-
}
|
|
36755
|
-
get colorPickerAnchorRect() {
|
|
36756
|
-
const button = this.colorPickerButtonRef.el;
|
|
36757
|
-
return getBoundingRectAsPOJO(button);
|
|
36758
|
-
}
|
|
36759
|
-
get buttonStyle() {
|
|
36760
|
-
return cssPropertiesToCss({
|
|
36761
|
-
background: this.props.currentColor,
|
|
36762
|
-
});
|
|
36763
|
-
}
|
|
36764
|
-
}
|
|
36765
|
-
|
|
36766
|
-
css /* scss */ `
|
|
36767
|
-
.o-badge-selection {
|
|
36768
|
-
gap: 1px;
|
|
36769
|
-
button.o-button {
|
|
36770
|
-
border-radius: 0;
|
|
36771
|
-
&.selected {
|
|
36772
|
-
color: ${GRAY_900};
|
|
36773
|
-
border-color: ${ACTION_COLOR};
|
|
36774
|
-
background: ${BADGE_SELECTED_COLOR};
|
|
36775
|
-
font-weight: 600;
|
|
36776
|
-
}
|
|
36777
|
-
|
|
36778
|
-
&:first-child {
|
|
36779
|
-
border-radius: 4px 0 0 4px;
|
|
36780
|
-
}
|
|
36781
|
-
&:last-child {
|
|
36782
|
-
border-radius: 0 4px 4px 0;
|
|
36783
|
-
}
|
|
36784
|
-
}
|
|
36785
|
-
}
|
|
36786
|
-
`;
|
|
36787
|
-
class BadgeSelection extends owl.Component {
|
|
36788
|
-
static template = "o-spreadsheet.BadgeSelection";
|
|
36789
|
-
static props = {
|
|
36790
|
-
choices: Array,
|
|
36791
|
-
onChange: Function,
|
|
36792
|
-
selectedValue: String,
|
|
36793
|
-
};
|
|
36794
|
-
}
|
|
36795
|
-
|
|
36796
37108
|
css /* scss */ `
|
|
36797
37109
|
.o-chart-title-designer {
|
|
36798
37110
|
> span {
|
|
@@ -36947,8 +37259,7 @@ class AxisDesignEditor extends owl.Component {
|
|
|
36947
37259
|
this.props.updateChart(this.props.figureId, { axesDesign });
|
|
36948
37260
|
}
|
|
36949
37261
|
updateAxisEditor(ev) {
|
|
36950
|
-
|
|
36951
|
-
this.state.currentAxis = axis;
|
|
37262
|
+
this.state.currentAxis = ev.target.value;
|
|
36952
37263
|
}
|
|
36953
37264
|
getAxisTitle() {
|
|
36954
37265
|
const axesDesign = this.props.definition.axesDesign ?? {};
|
|
@@ -36967,6 +37278,56 @@ class AxisDesignEditor extends owl.Component {
|
|
|
36967
37278
|
}
|
|
36968
37279
|
}
|
|
36969
37280
|
|
|
37281
|
+
const TRANSPARENT_BACKGROUND_SVG = /*xml*/ `
|
|
37282
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="10" height="10">
|
|
37283
|
+
<path fill="#d9d9d9" d="M5 5h5v5H5zH0V0h5"/>
|
|
37284
|
+
</svg>
|
|
37285
|
+
`;
|
|
37286
|
+
css /* scss */ `
|
|
37287
|
+
.o-round-color-picker-button {
|
|
37288
|
+
width: 18px;
|
|
37289
|
+
height: 18px;
|
|
37290
|
+
cursor: pointer;
|
|
37291
|
+
border: 1px solid ${GRAY_300};
|
|
37292
|
+
background-position: 1px 1px;
|
|
37293
|
+
background-image: url("data:image/svg+xml,${encodeURIComponent(TRANSPARENT_BACKGROUND_SVG)}");
|
|
37294
|
+
}
|
|
37295
|
+
`;
|
|
37296
|
+
class RoundColorPicker extends owl.Component {
|
|
37297
|
+
static template = "o-spreadsheet.RoundColorPicker";
|
|
37298
|
+
static components = { Section, ColorPicker };
|
|
37299
|
+
static props = {
|
|
37300
|
+
currentColor: { type: String, optional: true },
|
|
37301
|
+
title: { type: String, optional: true },
|
|
37302
|
+
onColorPicked: Function,
|
|
37303
|
+
};
|
|
37304
|
+
colorPickerButtonRef = owl.useRef("colorPickerButton");
|
|
37305
|
+
state;
|
|
37306
|
+
setup() {
|
|
37307
|
+
this.state = owl.useState({ pickerOpened: false });
|
|
37308
|
+
owl.useExternalListener(window, "click", this.closePicker);
|
|
37309
|
+
}
|
|
37310
|
+
closePicker() {
|
|
37311
|
+
this.state.pickerOpened = false;
|
|
37312
|
+
}
|
|
37313
|
+
togglePicker() {
|
|
37314
|
+
this.state.pickerOpened = !this.state.pickerOpened;
|
|
37315
|
+
}
|
|
37316
|
+
onColorPicked(color) {
|
|
37317
|
+
this.props.onColorPicked(color);
|
|
37318
|
+
this.state.pickerOpened = false;
|
|
37319
|
+
}
|
|
37320
|
+
get colorPickerAnchorRect() {
|
|
37321
|
+
const button = this.colorPickerButtonRef.el;
|
|
37322
|
+
return getBoundingRectAsPOJO(button);
|
|
37323
|
+
}
|
|
37324
|
+
get buttonStyle() {
|
|
37325
|
+
return cssPropertiesToCss({
|
|
37326
|
+
background: this.props.currentColor,
|
|
37327
|
+
});
|
|
37328
|
+
}
|
|
37329
|
+
}
|
|
37330
|
+
|
|
36970
37331
|
class GeneralDesignEditor extends owl.Component {
|
|
36971
37332
|
static template = "o-spreadsheet-GeneralDesignEditor";
|
|
36972
37333
|
static components = {
|
|
@@ -37031,58 +37392,75 @@ class GeneralDesignEditor extends owl.Component {
|
|
|
37031
37392
|
}
|
|
37032
37393
|
}
|
|
37033
37394
|
|
|
37034
|
-
|
|
37035
|
-
|
|
37395
|
+
const CIRCLE_SVG = /*xml*/ `
|
|
37396
|
+
<svg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'>
|
|
37397
|
+
<circle r="2" fill="#FFF"/>
|
|
37398
|
+
</svg>
|
|
37399
|
+
`;
|
|
37400
|
+
css /* scss */ `
|
|
37401
|
+
.o-radio {
|
|
37402
|
+
input {
|
|
37403
|
+
appearance: none;
|
|
37404
|
+
-webkit-appearance: none;
|
|
37405
|
+
-moz-appearance: none;
|
|
37406
|
+
width: 14px;
|
|
37407
|
+
height: 14px;
|
|
37408
|
+
border: 1px solid ${GRAY_300};
|
|
37409
|
+
box-sizing: border-box;
|
|
37410
|
+
outline: none;
|
|
37411
|
+
border-radius: 8px;
|
|
37412
|
+
|
|
37413
|
+
&:checked {
|
|
37414
|
+
background: url("data:image/svg+xml,${encodeURIComponent(CIRCLE_SVG)}");
|
|
37415
|
+
background-color: ${ACTION_COLOR};
|
|
37416
|
+
border-color: ${ACTION_COLOR};
|
|
37417
|
+
}
|
|
37418
|
+
}
|
|
37419
|
+
}
|
|
37420
|
+
`;
|
|
37421
|
+
class RadioSelection extends owl.Component {
|
|
37422
|
+
static template = "o-spreadsheet.RadioSelection";
|
|
37423
|
+
static props = {
|
|
37424
|
+
choices: Array,
|
|
37425
|
+
onChange: Function,
|
|
37426
|
+
selectedValue: { optional: false },
|
|
37427
|
+
name: String,
|
|
37428
|
+
direction: { type: String, optional: true },
|
|
37429
|
+
};
|
|
37430
|
+
static defaultProps = {
|
|
37431
|
+
direction: "horizontal",
|
|
37432
|
+
};
|
|
37433
|
+
}
|
|
37434
|
+
|
|
37435
|
+
class SeriesDesignEditor extends owl.Component {
|
|
37436
|
+
static template = "o-spreadsheet-SeriesDesignEditor";
|
|
37036
37437
|
static components = {
|
|
37037
|
-
GeneralDesignEditor,
|
|
37038
37438
|
SidePanelCollapsible,
|
|
37039
37439
|
Section,
|
|
37040
|
-
AxisDesignEditor,
|
|
37041
37440
|
RoundColorPicker,
|
|
37042
|
-
Checkbox,
|
|
37043
|
-
RadioSelection,
|
|
37044
37441
|
};
|
|
37045
37442
|
static props = {
|
|
37046
37443
|
figureId: String,
|
|
37047
37444
|
definition: Object,
|
|
37048
|
-
canUpdateChart: Function,
|
|
37049
37445
|
updateChart: Function,
|
|
37446
|
+
canUpdateChart: Function,
|
|
37447
|
+
slots: { type: Object, optional: true },
|
|
37050
37448
|
};
|
|
37051
|
-
axisChoices = CHART_AXIS_CHOICES;
|
|
37052
37449
|
state = owl.useState({ index: 0 });
|
|
37053
|
-
get axesList() {
|
|
37054
|
-
const { useLeftAxis, useRightAxis } = getDefinedAxis(this.props.definition);
|
|
37055
|
-
let axes = [{ id: "x", name: _t("Horizontal axis") }];
|
|
37056
|
-
if (useLeftAxis) {
|
|
37057
|
-
axes.push({ id: "y", name: useRightAxis ? _t("Left axis") : _t("Vertical axis") });
|
|
37058
|
-
}
|
|
37059
|
-
if (useRightAxis) {
|
|
37060
|
-
axes.push({ id: "y1", name: useLeftAxis ? _t("Right axis") : _t("Vertical axis") });
|
|
37061
|
-
}
|
|
37062
|
-
return axes;
|
|
37063
|
-
}
|
|
37064
|
-
updateLegendPosition(ev) {
|
|
37065
|
-
this.props.updateChart(this.props.figureId, {
|
|
37066
|
-
legendPosition: ev.target.value,
|
|
37067
|
-
});
|
|
37068
|
-
}
|
|
37069
37450
|
getDataSeries() {
|
|
37070
|
-
|
|
37451
|
+
const runtime = this.env.model.getters.getChartRuntime(this.props.figureId);
|
|
37452
|
+
if (!runtime || !("chartJsConfig" in runtime)) {
|
|
37453
|
+
return [];
|
|
37454
|
+
}
|
|
37455
|
+
return runtime.chartJsConfig.data.datasets.map((d) => d.label);
|
|
37071
37456
|
}
|
|
37072
37457
|
updateSerieEditor(ev) {
|
|
37073
|
-
|
|
37074
|
-
const selectedIndex = ev.target.selectedIndex;
|
|
37075
|
-
const runtime = this.env.model.getters.getChartRuntime(chartId);
|
|
37076
|
-
if (!runtime) {
|
|
37077
|
-
return;
|
|
37078
|
-
}
|
|
37079
|
-
this.state.index = selectedIndex;
|
|
37458
|
+
this.state.index = ev.target.selectedIndex;
|
|
37080
37459
|
}
|
|
37081
37460
|
updateDataSeriesColor(color) {
|
|
37082
|
-
const dataSets =
|
|
37083
|
-
if (!dataSets?.[this.state.index])
|
|
37461
|
+
const dataSets = this.props.definition.dataSets;
|
|
37462
|
+
if (!dataSets?.[this.state.index])
|
|
37084
37463
|
return;
|
|
37085
|
-
}
|
|
37086
37464
|
dataSets[this.state.index] = {
|
|
37087
37465
|
...dataSets[this.state.index],
|
|
37088
37466
|
backgroundColor: color,
|
|
@@ -37091,71 +37469,87 @@ class ChartWithAxisDesignPanel extends owl.Component {
|
|
|
37091
37469
|
}
|
|
37092
37470
|
getDataSerieColor() {
|
|
37093
37471
|
const dataSets = this.props.definition.dataSets;
|
|
37094
|
-
if (!dataSets?.[this.state.index])
|
|
37472
|
+
if (!dataSets?.[this.state.index])
|
|
37095
37473
|
return "";
|
|
37096
|
-
}
|
|
37097
37474
|
const color = dataSets[this.state.index].backgroundColor;
|
|
37098
|
-
return color
|
|
37475
|
+
return color
|
|
37476
|
+
? toHex(color)
|
|
37477
|
+
: getNthColor(this.state.index, getColorsPalette(this.props.definition.dataSets.length));
|
|
37099
37478
|
}
|
|
37100
|
-
|
|
37101
|
-
const
|
|
37102
|
-
|
|
37479
|
+
updateDataSeriesLabel(ev) {
|
|
37480
|
+
const label = ev.target.value;
|
|
37481
|
+
const dataSets = this.props.definition.dataSets;
|
|
37482
|
+
if (!dataSets?.[this.state.index])
|
|
37103
37483
|
return;
|
|
37104
|
-
}
|
|
37105
37484
|
dataSets[this.state.index] = {
|
|
37106
37485
|
...dataSets[this.state.index],
|
|
37107
|
-
|
|
37486
|
+
label,
|
|
37108
37487
|
};
|
|
37109
37488
|
this.props.updateChart(this.props.figureId, { dataSets });
|
|
37110
37489
|
}
|
|
37111
|
-
|
|
37490
|
+
getDataSerieLabel() {
|
|
37112
37491
|
const dataSets = this.props.definition.dataSets;
|
|
37113
|
-
|
|
37114
|
-
return "left";
|
|
37115
|
-
}
|
|
37116
|
-
return dataSets[this.state.index].yAxisId === "y1" ? "right" : "left";
|
|
37117
|
-
}
|
|
37118
|
-
get canHaveTwoVerticalAxis() {
|
|
37119
|
-
return "horizontal" in this.props.definition ? !this.props.definition.horizontal : true;
|
|
37492
|
+
return dataSets[this.state.index]?.label || this.getDataSeries()[this.state.index];
|
|
37120
37493
|
}
|
|
37121
|
-
|
|
37122
|
-
|
|
37494
|
+
}
|
|
37495
|
+
|
|
37496
|
+
class SeriesWithAxisDesignEditor extends owl.Component {
|
|
37497
|
+
static template = "o-spreadsheet-SeriesWithAxisDesignEditor";
|
|
37498
|
+
static components = {
|
|
37499
|
+
SeriesDesignEditor,
|
|
37500
|
+
Checkbox,
|
|
37501
|
+
RadioSelection,
|
|
37502
|
+
Section,
|
|
37503
|
+
RoundColorPicker,
|
|
37504
|
+
};
|
|
37505
|
+
static props = {
|
|
37506
|
+
figureId: String,
|
|
37507
|
+
definition: Object,
|
|
37508
|
+
canUpdateChart: Function,
|
|
37509
|
+
updateChart: Function,
|
|
37510
|
+
slots: { type: Object, optional: true },
|
|
37511
|
+
};
|
|
37512
|
+
axisChoices = CHART_AXIS_CHOICES;
|
|
37513
|
+
updateDataSeriesAxis(index, axis) {
|
|
37123
37514
|
const dataSets = [...this.props.definition.dataSets];
|
|
37124
|
-
if (!dataSets?.[
|
|
37515
|
+
if (!dataSets?.[index]) {
|
|
37125
37516
|
return;
|
|
37126
37517
|
}
|
|
37127
|
-
dataSets[
|
|
37128
|
-
...dataSets[
|
|
37129
|
-
|
|
37518
|
+
dataSets[index] = {
|
|
37519
|
+
...dataSets[index],
|
|
37520
|
+
yAxisId: axis === "left" ? "y" : "y1",
|
|
37130
37521
|
};
|
|
37131
37522
|
this.props.updateChart(this.props.figureId, { dataSets });
|
|
37132
37523
|
}
|
|
37133
|
-
|
|
37524
|
+
getDataSerieAxis(index) {
|
|
37134
37525
|
const dataSets = this.props.definition.dataSets;
|
|
37135
|
-
|
|
37526
|
+
if (!dataSets?.[index]) {
|
|
37527
|
+
return "left";
|
|
37528
|
+
}
|
|
37529
|
+
return dataSets[index].yAxisId === "y1" ? "right" : "left";
|
|
37136
37530
|
}
|
|
37137
|
-
|
|
37138
|
-
this.props.
|
|
37531
|
+
get canHaveTwoVerticalAxis() {
|
|
37532
|
+
return !("horizontal" in this.props.definition && this.props.definition.horizontal);
|
|
37139
37533
|
}
|
|
37140
|
-
toggleDataTrend(display) {
|
|
37534
|
+
toggleDataTrend(index, display) {
|
|
37141
37535
|
const dataSets = [...this.props.definition.dataSets];
|
|
37142
|
-
if (!dataSets?.[
|
|
37536
|
+
if (!dataSets?.[index]) {
|
|
37143
37537
|
return;
|
|
37144
37538
|
}
|
|
37145
|
-
dataSets[
|
|
37146
|
-
...dataSets[
|
|
37539
|
+
dataSets[index] = {
|
|
37540
|
+
...dataSets[index],
|
|
37147
37541
|
trend: {
|
|
37148
37542
|
type: "polynomial",
|
|
37149
37543
|
order: 1,
|
|
37150
|
-
...dataSets[
|
|
37544
|
+
...dataSets[index].trend,
|
|
37151
37545
|
display,
|
|
37152
37546
|
},
|
|
37153
37547
|
};
|
|
37154
37548
|
this.props.updateChart(this.props.figureId, { dataSets });
|
|
37155
37549
|
}
|
|
37156
|
-
getTrendLineConfiguration() {
|
|
37550
|
+
getTrendLineConfiguration(index) {
|
|
37157
37551
|
const dataSets = this.props.definition.dataSets;
|
|
37158
|
-
return dataSets?.[
|
|
37552
|
+
return dataSets?.[index]?.trend;
|
|
37159
37553
|
}
|
|
37160
37554
|
getTrendType(config) {
|
|
37161
37555
|
if (!config) {
|
|
@@ -37163,7 +37557,7 @@ class ChartWithAxisDesignPanel extends owl.Component {
|
|
|
37163
37557
|
}
|
|
37164
37558
|
return config.type === "polynomial" && config.order === 1 ? "linear" : config.type;
|
|
37165
37559
|
}
|
|
37166
|
-
onChangeTrendType(ev) {
|
|
37560
|
+
onChangeTrendType(index, ev) {
|
|
37167
37561
|
const type = ev.target.value;
|
|
37168
37562
|
let config;
|
|
37169
37563
|
switch (type) {
|
|
@@ -37176,37 +37570,59 @@ class ChartWithAxisDesignPanel extends owl.Component {
|
|
|
37176
37570
|
break;
|
|
37177
37571
|
case "exponential":
|
|
37178
37572
|
case "logarithmic":
|
|
37573
|
+
case "trailingMovingAverage":
|
|
37179
37574
|
config = { type };
|
|
37180
37575
|
break;
|
|
37181
37576
|
default:
|
|
37182
37577
|
return;
|
|
37183
37578
|
}
|
|
37184
|
-
this.updateTrendLineValue(config);
|
|
37579
|
+
this.updateTrendLineValue(index, config);
|
|
37185
37580
|
}
|
|
37186
|
-
onChangePolynomialDegree(ev) {
|
|
37581
|
+
onChangePolynomialDegree(index, ev) {
|
|
37187
37582
|
const element = ev.target;
|
|
37188
37583
|
const order = parseInt(element.value || "1");
|
|
37189
37584
|
if (order < 2) {
|
|
37190
|
-
element.value = `${this.getTrendLineConfiguration()?.order ?? 2}`;
|
|
37585
|
+
element.value = `${this.getTrendLineConfiguration(index)?.order ?? 2}`;
|
|
37191
37586
|
return;
|
|
37192
37587
|
}
|
|
37193
|
-
this.updateTrendLineValue({ order });
|
|
37588
|
+
this.updateTrendLineValue(index, { order });
|
|
37589
|
+
}
|
|
37590
|
+
get defaultWindowSize() {
|
|
37591
|
+
return DEFAULT_WINDOW_SIZE;
|
|
37592
|
+
}
|
|
37593
|
+
onChangeMovingAverageWindow(index, ev) {
|
|
37594
|
+
const element = ev.target;
|
|
37595
|
+
let window = parseInt(element.value) || DEFAULT_WINDOW_SIZE;
|
|
37596
|
+
if (window <= 1) {
|
|
37597
|
+
window = DEFAULT_WINDOW_SIZE;
|
|
37598
|
+
}
|
|
37599
|
+
this.updateTrendLineValue(index, { window });
|
|
37194
37600
|
}
|
|
37195
|
-
|
|
37196
|
-
|
|
37601
|
+
getDataSerieColor(index) {
|
|
37602
|
+
const dataSets = this.props.definition.dataSets;
|
|
37603
|
+
if (!dataSets?.[index])
|
|
37604
|
+
return "";
|
|
37605
|
+
const color = dataSets[index].backgroundColor;
|
|
37606
|
+
return color
|
|
37607
|
+
? toHex(color)
|
|
37608
|
+
: getNthColor(index, getColorsPalette(this.props.definition.dataSets.length));
|
|
37609
|
+
}
|
|
37610
|
+
getTrendLineColor(index) {
|
|
37611
|
+
return (this.getTrendLineConfiguration(index)?.color ??
|
|
37612
|
+
setColorAlpha(this.getDataSerieColor(index), 0.5));
|
|
37197
37613
|
}
|
|
37198
|
-
updateTrendLineColor(color) {
|
|
37199
|
-
this.updateTrendLineValue({ color });
|
|
37614
|
+
updateTrendLineColor(index, color) {
|
|
37615
|
+
this.updateTrendLineValue(index, { color });
|
|
37200
37616
|
}
|
|
37201
|
-
updateTrendLineValue(config) {
|
|
37617
|
+
updateTrendLineValue(index, config) {
|
|
37202
37618
|
const dataSets = [...this.props.definition.dataSets];
|
|
37203
|
-
if (!dataSets?.[
|
|
37619
|
+
if (!dataSets?.[index]) {
|
|
37204
37620
|
return;
|
|
37205
37621
|
}
|
|
37206
|
-
dataSets[
|
|
37207
|
-
...dataSets[
|
|
37622
|
+
dataSets[index] = {
|
|
37623
|
+
...dataSets[index],
|
|
37208
37624
|
trend: {
|
|
37209
|
-
...dataSets[
|
|
37625
|
+
...dataSets[index].trend,
|
|
37210
37626
|
...config,
|
|
37211
37627
|
},
|
|
37212
37628
|
};
|
|
@@ -37214,6 +37630,70 @@ class ChartWithAxisDesignPanel extends owl.Component {
|
|
|
37214
37630
|
}
|
|
37215
37631
|
}
|
|
37216
37632
|
|
|
37633
|
+
class ChartWithAxisDesignPanel extends owl.Component {
|
|
37634
|
+
static template = "o-spreadsheet-ChartWithAxisDesignPanel";
|
|
37635
|
+
static components = {
|
|
37636
|
+
GeneralDesignEditor,
|
|
37637
|
+
SidePanelCollapsible,
|
|
37638
|
+
Section,
|
|
37639
|
+
AxisDesignEditor,
|
|
37640
|
+
Checkbox,
|
|
37641
|
+
SeriesWithAxisDesignEditor,
|
|
37642
|
+
};
|
|
37643
|
+
static props = {
|
|
37644
|
+
figureId: String,
|
|
37645
|
+
definition: Object,
|
|
37646
|
+
canUpdateChart: Function,
|
|
37647
|
+
updateChart: Function,
|
|
37648
|
+
};
|
|
37649
|
+
get axesList() {
|
|
37650
|
+
const { useLeftAxis, useRightAxis } = getDefinedAxis(this.props.definition);
|
|
37651
|
+
let axes = [{ id: "x", name: _t("Horizontal axis") }];
|
|
37652
|
+
if (useLeftAxis) {
|
|
37653
|
+
axes.push({ id: "y", name: useRightAxis ? _t("Left axis") : _t("Vertical axis") });
|
|
37654
|
+
}
|
|
37655
|
+
if (useRightAxis) {
|
|
37656
|
+
axes.push({ id: "y1", name: useLeftAxis ? _t("Right axis") : _t("Vertical axis") });
|
|
37657
|
+
}
|
|
37658
|
+
return axes;
|
|
37659
|
+
}
|
|
37660
|
+
updateLegendPosition(ev) {
|
|
37661
|
+
this.props.updateChart(this.props.figureId, {
|
|
37662
|
+
legendPosition: ev.target.value,
|
|
37663
|
+
});
|
|
37664
|
+
}
|
|
37665
|
+
}
|
|
37666
|
+
|
|
37667
|
+
class ComboChartDesignPanel extends ChartWithAxisDesignPanel {
|
|
37668
|
+
static template = "o-spreadsheet-ComboChartDesignPanel";
|
|
37669
|
+
static components = {
|
|
37670
|
+
...ChartWithAxisDesignPanel.components,
|
|
37671
|
+
RadioSelection,
|
|
37672
|
+
};
|
|
37673
|
+
seriesTypeChoices = [
|
|
37674
|
+
{ value: "bar", label: _t("Bar") },
|
|
37675
|
+
{ value: "line", label: _t("Line") },
|
|
37676
|
+
];
|
|
37677
|
+
updateDataSeriesType(index, type) {
|
|
37678
|
+
const dataSets = [...this.props.definition.dataSets];
|
|
37679
|
+
if (!dataSets?.[index]) {
|
|
37680
|
+
return;
|
|
37681
|
+
}
|
|
37682
|
+
dataSets[index] = {
|
|
37683
|
+
...dataSets[index],
|
|
37684
|
+
type,
|
|
37685
|
+
};
|
|
37686
|
+
this.props.updateChart(this.props.figureId, { dataSets });
|
|
37687
|
+
}
|
|
37688
|
+
getDataSeriesType(index) {
|
|
37689
|
+
const dataSets = this.props.definition.dataSets;
|
|
37690
|
+
if (!dataSets?.[index]) {
|
|
37691
|
+
return "bar";
|
|
37692
|
+
}
|
|
37693
|
+
return dataSets[index].type ?? "line";
|
|
37694
|
+
}
|
|
37695
|
+
}
|
|
37696
|
+
|
|
37217
37697
|
class GaugeChartConfigPanel extends owl.Component {
|
|
37218
37698
|
static template = "o-spreadsheet-GaugeChartConfigPanel";
|
|
37219
37699
|
static components = { ChartErrorSection, ChartDataSeries };
|
|
@@ -37393,11 +37873,6 @@ class LineConfigPanel extends GenericChartConfigPanel {
|
|
|
37393
37873
|
stacked,
|
|
37394
37874
|
});
|
|
37395
37875
|
}
|
|
37396
|
-
onUpdateAggregated(aggregated) {
|
|
37397
|
-
this.props.updateChart(this.props.figureId, {
|
|
37398
|
-
aggregated,
|
|
37399
|
-
});
|
|
37400
|
-
}
|
|
37401
37876
|
onUpdateCumulative(cumulative) {
|
|
37402
37877
|
this.props.updateChart(this.props.figureId, {
|
|
37403
37878
|
cumulative,
|
|
@@ -37425,6 +37900,27 @@ class PieChartDesignPanel extends owl.Component {
|
|
|
37425
37900
|
}
|
|
37426
37901
|
}
|
|
37427
37902
|
|
|
37903
|
+
class RadarChartDesignPanel extends owl.Component {
|
|
37904
|
+
static template = "o-spreadsheet-RadarChartDesignPanel";
|
|
37905
|
+
static components = {
|
|
37906
|
+
GeneralDesignEditor,
|
|
37907
|
+
SeriesDesignEditor,
|
|
37908
|
+
Section,
|
|
37909
|
+
Checkbox,
|
|
37910
|
+
};
|
|
37911
|
+
static props = {
|
|
37912
|
+
figureId: String,
|
|
37913
|
+
definition: Object,
|
|
37914
|
+
canUpdateChart: Function,
|
|
37915
|
+
updateChart: Function,
|
|
37916
|
+
};
|
|
37917
|
+
updateLegendPosition(ev) {
|
|
37918
|
+
this.props.updateChart(this.props.figureId, {
|
|
37919
|
+
legendPosition: ev.target.value,
|
|
37920
|
+
});
|
|
37921
|
+
}
|
|
37922
|
+
}
|
|
37923
|
+
|
|
37428
37924
|
class ScatterConfigPanel extends GenericChartConfigPanel {
|
|
37429
37925
|
static template = "o-spreadsheet-ScatterConfigPanel";
|
|
37430
37926
|
get canTreatLabelsAsText() {
|
|
@@ -37619,9 +38115,6 @@ class WaterfallChartDesignPanel extends owl.Component {
|
|
|
37619
38115
|
verticalAxisPosition: value,
|
|
37620
38116
|
});
|
|
37621
38117
|
}
|
|
37622
|
-
updateShowValues(showValues) {
|
|
37623
|
-
this.props.updateChart(this.props.figureId, { showValues });
|
|
37624
|
-
}
|
|
37625
38118
|
}
|
|
37626
38119
|
|
|
37627
38120
|
const chartSidePanelComponentRegistry = new Registry();
|
|
@@ -37640,7 +38133,7 @@ chartSidePanelComponentRegistry
|
|
|
37640
38133
|
})
|
|
37641
38134
|
.add("combo", {
|
|
37642
38135
|
configuration: GenericChartConfigPanel,
|
|
37643
|
-
design:
|
|
38136
|
+
design: ComboChartDesignPanel,
|
|
37644
38137
|
})
|
|
37645
38138
|
.add("pie", {
|
|
37646
38139
|
configuration: GenericChartConfigPanel,
|
|
@@ -37661,6 +38154,10 @@ chartSidePanelComponentRegistry
|
|
|
37661
38154
|
.add("pyramid", {
|
|
37662
38155
|
configuration: GenericChartConfigPanel,
|
|
37663
38156
|
design: ChartWithAxisDesignPanel,
|
|
38157
|
+
})
|
|
38158
|
+
.add("radar", {
|
|
38159
|
+
configuration: GenericChartConfigPanel,
|
|
38160
|
+
design: RadarChartDesignPanel,
|
|
37664
38161
|
});
|
|
37665
38162
|
|
|
37666
38163
|
css /* scss */ `
|
|
@@ -37926,6 +38423,7 @@ class AbstractComposerStore extends SpreadsheetStore {
|
|
|
37926
38423
|
"stopComposerRangeSelection",
|
|
37927
38424
|
"cancelEdition",
|
|
37928
38425
|
"cycleReferences",
|
|
38426
|
+
"toggleEditionMode",
|
|
37929
38427
|
"changeComposerCursorSelection",
|
|
37930
38428
|
"replaceComposerCursorSelection",
|
|
37931
38429
|
];
|
|
@@ -38081,6 +38579,42 @@ class AbstractComposerStore extends SpreadsheetStore {
|
|
|
38081
38579
|
}
|
|
38082
38580
|
this.setCurrentContent(updated.content, updated.selection);
|
|
38083
38581
|
}
|
|
38582
|
+
toggleEditionMode() {
|
|
38583
|
+
if (this.editionMode === "inactive")
|
|
38584
|
+
return;
|
|
38585
|
+
const start = Math.min(this.selectionStart, this.selectionEnd);
|
|
38586
|
+
const end = Math.max(this.selectionStart, this.selectionEnd);
|
|
38587
|
+
const refToken = [...this.currentTokens]
|
|
38588
|
+
.reverse()
|
|
38589
|
+
.find((tk) => tk.end >= start && end >= tk.start && tk.type === "REFERENCE");
|
|
38590
|
+
if (this.editionMode === "editing" && refToken) {
|
|
38591
|
+
const currentSheetId = this.getters.getActiveSheetId();
|
|
38592
|
+
const { sheetName, xc } = splitReference(refToken.value);
|
|
38593
|
+
const sheetId = this.getters.getSheetIdByName(sheetName);
|
|
38594
|
+
if (sheetId && sheetId !== currentSheetId) {
|
|
38595
|
+
this.model.dispatch("ACTIVATE_SHEET", { sheetIdFrom: currentSheetId, sheetIdTo: sheetId });
|
|
38596
|
+
}
|
|
38597
|
+
// move cursor to the right part of the token
|
|
38598
|
+
this.selectionStart = this.selectionEnd = refToken.end;
|
|
38599
|
+
const zone = this.getters.getRangeFromSheetXC(this.sheetId, xc).zone;
|
|
38600
|
+
this.captureSelection(zone);
|
|
38601
|
+
this.editionMode = "selecting";
|
|
38602
|
+
}
|
|
38603
|
+
else {
|
|
38604
|
+
this.editionMode = "editing";
|
|
38605
|
+
}
|
|
38606
|
+
}
|
|
38607
|
+
captureSelection(zone, col, row) {
|
|
38608
|
+
this.model.selection.capture(this, {
|
|
38609
|
+
cell: { col: col || zone.left, row: row || zone.right },
|
|
38610
|
+
zone,
|
|
38611
|
+
}, {
|
|
38612
|
+
handleEvent: this.handleEvent.bind(this),
|
|
38613
|
+
release: () => {
|
|
38614
|
+
this._stopEdition();
|
|
38615
|
+
},
|
|
38616
|
+
});
|
|
38617
|
+
}
|
|
38084
38618
|
isSelectionValid(length, start, end) {
|
|
38085
38619
|
return start >= 0 && start <= length && end >= 0 && end <= length;
|
|
38086
38620
|
}
|
|
@@ -38119,12 +38653,7 @@ class AbstractComposerStore extends SpreadsheetStore {
|
|
|
38119
38653
|
this.setContent(str || this.initialContent, selection);
|
|
38120
38654
|
this.colorIndexByRange = {};
|
|
38121
38655
|
const zone = positionToZone({ col: this.col, row: this.row });
|
|
38122
|
-
this.
|
|
38123
|
-
handleEvent: this.handleEvent.bind(this),
|
|
38124
|
-
release: () => {
|
|
38125
|
-
this._stopEdition();
|
|
38126
|
-
},
|
|
38127
|
-
});
|
|
38656
|
+
this.captureSelection(zone, col, row);
|
|
38128
38657
|
}
|
|
38129
38658
|
_stopEdition() {
|
|
38130
38659
|
if (this.editionMode !== "inactive") {
|
|
@@ -38831,13 +39360,6 @@ class DOMDndHelper {
|
|
|
38831
39360
|
return;
|
|
38832
39361
|
this.edgeScrollIntervalId = window.setInterval(() => {
|
|
38833
39362
|
const offset = direction * 3;
|
|
38834
|
-
let newPosition = this.currentMousePosition + offset;
|
|
38835
|
-
if (newPosition < Math.min(this.container.start, this.minPosition)) {
|
|
38836
|
-
newPosition = Math.min(this.container.start, this.minPosition);
|
|
38837
|
-
}
|
|
38838
|
-
else if (newPosition > Math.max(this.container.end, this.maxPosition)) {
|
|
38839
|
-
newPosition = Math.max(this.container.end, this.maxPosition);
|
|
38840
|
-
}
|
|
38841
39363
|
this.container.scroll += offset;
|
|
38842
39364
|
}, 5);
|
|
38843
39365
|
}
|
|
@@ -39014,7 +39536,6 @@ css /* scss */ `
|
|
|
39014
39536
|
width: 142px;
|
|
39015
39537
|
.o-cf-preview-description-rule {
|
|
39016
39538
|
margin-bottom: 4px;
|
|
39017
|
-
font-weight: 600;
|
|
39018
39539
|
max-height: 2.8em;
|
|
39019
39540
|
line-height: 1.4em;
|
|
39020
39541
|
}
|
|
@@ -39486,7 +40007,7 @@ class ConditionalFormattingEditor extends owl.Component {
|
|
|
39486
40007
|
setColorScaleColor(target, color) {
|
|
39487
40008
|
const point = this.state.rules.colorScale[target];
|
|
39488
40009
|
if (point) {
|
|
39489
|
-
point.color = Number.parseInt(color.
|
|
40010
|
+
point.color = Number.parseInt(color.slice(1), 16);
|
|
39490
40011
|
}
|
|
39491
40012
|
this.closeMenus();
|
|
39492
40013
|
}
|
|
@@ -39597,7 +40118,7 @@ class ConditionalFormattingEditor extends owl.Component {
|
|
|
39597
40118
|
return [this.state.rules.dataBar.rangeValues || ""];
|
|
39598
40119
|
}
|
|
39599
40120
|
updateDataBarColor(color) {
|
|
39600
|
-
this.state.rules.dataBar.color = Number.parseInt(color.
|
|
40121
|
+
this.state.rules.dataBar.color = Number.parseInt(color.slice(1), 16);
|
|
39601
40122
|
}
|
|
39602
40123
|
onDataBarRangeUpdate(ranges) {
|
|
39603
40124
|
this.state.rules.dataBar.rangeValues = ranges[0];
|
|
@@ -41010,10 +41531,11 @@ class FindAndReplaceStore extends SpreadsheetStore {
|
|
|
41010
41531
|
activeSheetMatches = [];
|
|
41011
41532
|
specificRangeMatches = [];
|
|
41012
41533
|
currentSearchRegex = null;
|
|
41013
|
-
isSearchDirty = false;
|
|
41014
41534
|
initialShowFormulaState;
|
|
41015
41535
|
preserveSelectedMatchIndex = false;
|
|
41016
41536
|
irreplaceableMatchCount = 0;
|
|
41537
|
+
isSearchDirty = false;
|
|
41538
|
+
shouldFinalizeUpdateSelection = false;
|
|
41017
41539
|
notificationStore = this.get(NotificationStore);
|
|
41018
41540
|
// fixme: why do we make selectedMatchIndex on top of a selected
|
|
41019
41541
|
// property in the matches?
|
|
@@ -41059,10 +41581,13 @@ class FindAndReplaceStore extends SpreadsheetStore {
|
|
|
41059
41581
|
this.updateSearchOptions({ searchFormulas: showFormula });
|
|
41060
41582
|
}
|
|
41061
41583
|
selectPreviousMatch() {
|
|
41062
|
-
this.selectNextCell(Direction.previous
|
|
41584
|
+
this.selectNextCell(Direction.previous, {
|
|
41585
|
+
jumpToMatchSheet: true,
|
|
41586
|
+
updateSelection: true,
|
|
41587
|
+
});
|
|
41063
41588
|
}
|
|
41064
41589
|
selectNextMatch() {
|
|
41065
|
-
this.selectNextCell(Direction.next);
|
|
41590
|
+
this.selectNextCell(Direction.next, { jumpToMatchSheet: true, updateSelection: true });
|
|
41066
41591
|
}
|
|
41067
41592
|
handle(cmd) {
|
|
41068
41593
|
switch (cmd.type) {
|
|
@@ -41079,8 +41604,11 @@ class FindAndReplaceStore extends SpreadsheetStore {
|
|
|
41079
41604
|
case "ADD_COLUMNS_ROWS":
|
|
41080
41605
|
case "EVALUATE_CELLS":
|
|
41081
41606
|
case "UPDATE_CELL":
|
|
41607
|
+
this.isSearchDirty = true;
|
|
41608
|
+
break;
|
|
41082
41609
|
case "ACTIVATE_SHEET":
|
|
41083
41610
|
this.isSearchDirty = true;
|
|
41611
|
+
this.shouldFinalizeUpdateSelection = true;
|
|
41084
41612
|
break;
|
|
41085
41613
|
case "REPLACE_SEARCH":
|
|
41086
41614
|
for (const match of cmd.matches) {
|
|
@@ -41095,7 +41623,11 @@ class FindAndReplaceStore extends SpreadsheetStore {
|
|
|
41095
41623
|
}
|
|
41096
41624
|
finalize() {
|
|
41097
41625
|
if (this.isSearchDirty) {
|
|
41098
|
-
this.refreshSearch(
|
|
41626
|
+
this.refreshSearch({
|
|
41627
|
+
jumpToMatchSheet: false,
|
|
41628
|
+
updateSelection: this.shouldFinalizeUpdateSelection,
|
|
41629
|
+
});
|
|
41630
|
+
this.shouldFinalizeUpdateSelection = false;
|
|
41099
41631
|
this.isSearchDirty = false;
|
|
41100
41632
|
}
|
|
41101
41633
|
}
|
|
@@ -41122,17 +41654,17 @@ class FindAndReplaceStore extends SpreadsheetStore {
|
|
|
41122
41654
|
}
|
|
41123
41655
|
this.toSearch = toSearch;
|
|
41124
41656
|
this.currentSearchRegex = getSearchRegex(this.toSearch, this.searchOptions);
|
|
41125
|
-
this.refreshSearch();
|
|
41657
|
+
this.refreshSearch({ jumpToMatchSheet: true, updateSelection: true });
|
|
41126
41658
|
}
|
|
41127
41659
|
/**
|
|
41128
41660
|
* refresh the matches according to the current search options
|
|
41129
41661
|
*/
|
|
41130
|
-
refreshSearch(
|
|
41662
|
+
refreshSearch(options) {
|
|
41131
41663
|
if (!this.preserveSelectedMatchIndex) {
|
|
41132
41664
|
this.selectedMatchIndex = null;
|
|
41133
41665
|
}
|
|
41134
41666
|
this.findMatches();
|
|
41135
|
-
this.selectNextCell(Direction.current,
|
|
41667
|
+
this.selectNextCell(Direction.current, options);
|
|
41136
41668
|
}
|
|
41137
41669
|
getSheetsInSearchOrder() {
|
|
41138
41670
|
switch (this.searchOptions.searchScope) {
|
|
@@ -41202,7 +41734,7 @@ class FindAndReplaceStore extends SpreadsheetStore {
|
|
|
41202
41734
|
* It is also used to keep coherence between the selected searchMatch
|
|
41203
41735
|
* and selectedMatchIndex.
|
|
41204
41736
|
*/
|
|
41205
|
-
selectNextCell(indexChange,
|
|
41737
|
+
selectNextCell(indexChange, options) {
|
|
41206
41738
|
const matches = this.searchMatches;
|
|
41207
41739
|
if (!matches.length) {
|
|
41208
41740
|
this.selectedMatchIndex = null;
|
|
@@ -41228,7 +41760,7 @@ class FindAndReplaceStore extends SpreadsheetStore {
|
|
|
41228
41760
|
this.selectedMatchIndex = nextIndex;
|
|
41229
41761
|
const selectedMatch = matches[nextIndex];
|
|
41230
41762
|
// Switch to the sheet where the match is located
|
|
41231
|
-
if (jumpToMatchSheet && this.getters.getActiveSheetId() !== selectedMatch.sheetId) {
|
|
41763
|
+
if (options.jumpToMatchSheet && this.getters.getActiveSheetId() !== selectedMatch.sheetId) {
|
|
41232
41764
|
// We set `preserveSelectedMatchIndex` to true to avoid resetting the selected search
|
|
41233
41765
|
// index in the `refreshSearch` function when a new sheet is activated. The reason being
|
|
41234
41766
|
// that, when we automatically go back to previous sheet while performing a search, the
|
|
@@ -41244,7 +41776,9 @@ class FindAndReplaceStore extends SpreadsheetStore {
|
|
|
41244
41776
|
}
|
|
41245
41777
|
// we want grid selection to capture the selection stream
|
|
41246
41778
|
this.model.selection.getBackToDefault();
|
|
41247
|
-
|
|
41779
|
+
if (options.updateSelection) {
|
|
41780
|
+
this.model.selection.selectCell(selectedMatch.col, selectedMatch.row);
|
|
41781
|
+
}
|
|
41248
41782
|
}
|
|
41249
41783
|
/**
|
|
41250
41784
|
* Replace the value of the currently selected match
|
|
@@ -41259,7 +41793,7 @@ class FindAndReplaceStore extends SpreadsheetStore {
|
|
|
41259
41793
|
matches: [this.searchMatches[this.selectedMatchIndex]],
|
|
41260
41794
|
searchOptions: this.searchOptions,
|
|
41261
41795
|
});
|
|
41262
|
-
this.selectNextCell(Direction.next);
|
|
41796
|
+
this.selectNextCell(Direction.next, { jumpToMatchSheet: true, updateSelection: true });
|
|
41263
41797
|
}
|
|
41264
41798
|
/**
|
|
41265
41799
|
* Apply the replace function to all the matches one time.
|
|
@@ -41441,6 +41975,14 @@ class FindAndReplacePanel extends owl.Component {
|
|
|
41441
41975
|
owl.onMounted(() => this.searchInput.el?.focus());
|
|
41442
41976
|
owl.onWillUnmount(() => this.updateSearchContent.stopDebounce());
|
|
41443
41977
|
this.updateSearchContent = debounce(this.store.updateSearchContent, 200);
|
|
41978
|
+
owl.useExternalListener(window, "keydown", (ev) => {
|
|
41979
|
+
const code = keyboardEventToShortcutString(ev);
|
|
41980
|
+
if (code === "Ctrl+F" || code === "Ctrl+H") {
|
|
41981
|
+
this.searchInput.el?.focus();
|
|
41982
|
+
ev.preventDefault();
|
|
41983
|
+
ev.stopPropagation();
|
|
41984
|
+
}
|
|
41985
|
+
}, { capture: true });
|
|
41444
41986
|
}
|
|
41445
41987
|
onFocusSearch() {
|
|
41446
41988
|
this.updateDataRange();
|
|
@@ -42016,7 +42558,6 @@ function createMeasureAutoComplete(pivot, forComputedMeasure) {
|
|
|
42016
42558
|
sequence: 0,
|
|
42017
42559
|
autoSelectFirstProposal: true,
|
|
42018
42560
|
getProposals(tokenAtCursor) {
|
|
42019
|
-
// return []
|
|
42020
42561
|
const measureProposals = pivot.measures
|
|
42021
42562
|
.filter((m) => m !== forComputedMeasure)
|
|
42022
42563
|
.map((measure) => {
|
|
@@ -42819,8 +43360,9 @@ const EMPTY_PIVOT_CELL = { type: "EMPTY" };
|
|
|
42819
43360
|
* This function converts a list of data entry into a spreadsheet pivot table.
|
|
42820
43361
|
*/
|
|
42821
43362
|
function dataEntriesToSpreadsheetPivotTable(dataEntries, definition) {
|
|
43363
|
+
const measureIds = definition.measures.filter((measure) => !measure.isHidden).map((m) => m.id);
|
|
42822
43364
|
const columnsTree = dataEntriesToColumnsTree(dataEntries, definition.columns, 0);
|
|
42823
|
-
computeWidthOfColumnsNodes(columnsTree,
|
|
43365
|
+
computeWidthOfColumnsNodes(columnsTree, measureIds.length);
|
|
42824
43366
|
const cols = columnsTreeToColumns(columnsTree, definition);
|
|
42825
43367
|
const rows = dataEntriesToRows(dataEntries, 0, definition.rows, [], []);
|
|
42826
43368
|
// Add the total row
|
|
@@ -42829,7 +43371,6 @@ function dataEntriesToSpreadsheetPivotTable(dataEntries, definition) {
|
|
|
42829
43371
|
values: [],
|
|
42830
43372
|
indent: 0,
|
|
42831
43373
|
});
|
|
42832
|
-
const measureIds = definition.measures.filter((measure) => !measure.isHidden).map((m) => m.id);
|
|
42833
43374
|
const fieldsType = {};
|
|
42834
43375
|
for (const columns of definition.columns) {
|
|
42835
43376
|
fieldsType[columns.fieldName] = columns.type;
|
|
@@ -43590,20 +44131,22 @@ pivotRegistry.add("SPREADSHEET", {
|
|
|
43590
44131
|
onIterationEndEvaluation: (pivot) => pivot.markAsDirtyForEvaluation(),
|
|
43591
44132
|
dateGranularities: [...dateGranularities],
|
|
43592
44133
|
datetimeGranularities: [...dateGranularities, "hour_number", "minute_number", "second_number"],
|
|
43593
|
-
isMeasureCandidate: (field) => !["
|
|
44134
|
+
isMeasureCandidate: (field) => !["datetime", "boolean"].includes(field.type),
|
|
43594
44135
|
isGroupable: () => true,
|
|
43595
44136
|
});
|
|
43596
44137
|
|
|
43597
44138
|
class PivotSidePanelStore extends SpreadsheetStore {
|
|
43598
44139
|
pivotId;
|
|
43599
44140
|
mutators = ["reset", "deferUpdates", "applyUpdate", "discardPendingUpdate", "update"];
|
|
43600
|
-
updatesAreDeferred
|
|
44141
|
+
updatesAreDeferred;
|
|
43601
44142
|
draft = null;
|
|
43602
44143
|
notification = this.get(NotificationStore);
|
|
43603
44144
|
alreadyNotified = false;
|
|
43604
44145
|
constructor(get, pivotId) {
|
|
43605
44146
|
super(get);
|
|
43606
44147
|
this.pivotId = pivotId;
|
|
44148
|
+
this.updatesAreDeferred =
|
|
44149
|
+
this.getters.getPivotCoreDefinition(this.pivotId).deferUpdates ?? false;
|
|
43607
44150
|
}
|
|
43608
44151
|
handle(cmd) {
|
|
43609
44152
|
switch (cmd.type) {
|
|
@@ -43691,10 +44234,14 @@ class PivotSidePanelStore extends SpreadsheetStore {
|
|
|
43691
44234
|
this.draft = null;
|
|
43692
44235
|
}
|
|
43693
44236
|
deferUpdates(shouldDefer) {
|
|
43694
|
-
this.updatesAreDeferred = shouldDefer;
|
|
43695
44237
|
if (shouldDefer === false && this.draft) {
|
|
44238
|
+
this.draft.deferUpdates = false;
|
|
43696
44239
|
this.applyUpdate();
|
|
43697
44240
|
}
|
|
44241
|
+
else {
|
|
44242
|
+
this.update({ deferUpdates: shouldDefer });
|
|
44243
|
+
}
|
|
44244
|
+
this.updatesAreDeferred = shouldDefer;
|
|
43698
44245
|
}
|
|
43699
44246
|
applyUpdate() {
|
|
43700
44247
|
if (this.draft) {
|
|
@@ -43952,7 +44499,7 @@ class RemoveDuplicatesPanel extends owl.Component {
|
|
|
43952
44499
|
return colLabel;
|
|
43953
44500
|
}
|
|
43954
44501
|
get isEveryColumnSelected() {
|
|
43955
|
-
return Object.values(this.state.columns).every((value) => value
|
|
44502
|
+
return Object.values(this.state.columns).every((value) => value);
|
|
43956
44503
|
}
|
|
43957
44504
|
get errorMessages() {
|
|
43958
44505
|
const cancelledReasons = this.env.model.canDispatch("REMOVE_DUPLICATES", {
|
|
@@ -44052,8 +44599,7 @@ class SettingsPanel extends owl.Component {
|
|
|
44052
44599
|
const currentLocale = this.currentLocale;
|
|
44053
44600
|
const localeInLoadedLocales = this.loadedLocales.find((l) => l.code === currentLocale.code);
|
|
44054
44601
|
if (!localeInLoadedLocales) {
|
|
44055
|
-
|
|
44056
|
-
return locales;
|
|
44602
|
+
return [...this.loadedLocales, currentLocale].sort((a, b) => a.name.localeCompare(b.name));
|
|
44057
44603
|
}
|
|
44058
44604
|
else if (!deepEquals(currentLocale, localeInLoadedLocales)) {
|
|
44059
44605
|
const index = this.loadedLocales.indexOf(localeInLoadedLocales);
|
|
@@ -45947,10 +46493,9 @@ class GridComposer extends owl.Component {
|
|
|
45947
46493
|
});
|
|
45948
46494
|
}
|
|
45949
46495
|
get focus() {
|
|
45950
|
-
|
|
46496
|
+
return this.composerFocusStore.activeComposer === this.composerInterface
|
|
45951
46497
|
? this.composerFocusStore.focusMode
|
|
45952
46498
|
: "inactive";
|
|
45953
|
-
return focus;
|
|
45954
46499
|
}
|
|
45955
46500
|
get composerProps() {
|
|
45956
46501
|
const { width, height } = this.env.model.getters.getSheetViewDimensionWithHeaders();
|
|
@@ -46119,13 +46664,10 @@ class GridCellIcon extends owl.Component {
|
|
|
46119
46664
|
}
|
|
46120
46665
|
}
|
|
46121
46666
|
|
|
46122
|
-
const CHECKBOX_WIDTH = 15;
|
|
46123
46667
|
const MARGIN = (GRID_ICON_EDGE_LENGTH - CHECKBOX_WIDTH) / 2;
|
|
46124
46668
|
css /* scss */ `
|
|
46125
46669
|
.o-dv-checkbox {
|
|
46126
46670
|
box-sizing: border-box !important;
|
|
46127
|
-
width: ${CHECKBOX_WIDTH}px;
|
|
46128
|
-
height: ${CHECKBOX_WIDTH}px;
|
|
46129
46671
|
accent-color: #808080;
|
|
46130
46672
|
margin: ${MARGIN}px;
|
|
46131
46673
|
/** required to prevent the checkbox position to be sensible to the font-size (affects Firefox) */
|
|
@@ -46134,13 +46676,15 @@ css /* scss */ `
|
|
|
46134
46676
|
`;
|
|
46135
46677
|
class DataValidationCheckbox extends owl.Component {
|
|
46136
46678
|
static template = "o-spreadsheet-DataValidationCheckbox";
|
|
46679
|
+
static components = {
|
|
46680
|
+
Checkbox,
|
|
46681
|
+
};
|
|
46137
46682
|
static props = {
|
|
46138
46683
|
cellPosition: Object,
|
|
46139
46684
|
};
|
|
46140
|
-
onCheckboxChange(
|
|
46141
|
-
const newValue = ev.target.checked;
|
|
46685
|
+
onCheckboxChange(value) {
|
|
46142
46686
|
const { sheetId, col, row } = this.props.cellPosition;
|
|
46143
|
-
const cellContent =
|
|
46687
|
+
const cellContent = value ? "TRUE" : "FALSE";
|
|
46144
46688
|
this.env.model.dispatch("UPDATE_CELL", { sheetId, col, row, content: cellContent });
|
|
46145
46689
|
}
|
|
46146
46690
|
get checkBoxValue() {
|
|
@@ -46954,7 +47498,12 @@ class GridAddRowsFooter extends owl.Component {
|
|
|
46954
47498
|
class PaintFormatStore extends SpreadsheetStore {
|
|
46955
47499
|
mutators = ["activate", "cancel", "pasteFormat"];
|
|
46956
47500
|
highlightStore = this.get(HighlightStore);
|
|
46957
|
-
|
|
47501
|
+
clipboardHandlers = [
|
|
47502
|
+
new CellClipboardHandler(this.getters, this.model.dispatch),
|
|
47503
|
+
new BorderClipboardHandler(this.getters, this.model.dispatch),
|
|
47504
|
+
new TableClipboardHandler(this.getters, this.model.dispatch),
|
|
47505
|
+
new ConditionalFormatClipboardHandler(this.getters, this.model.dispatch),
|
|
47506
|
+
];
|
|
46958
47507
|
status = "inactive";
|
|
46959
47508
|
copiedData;
|
|
46960
47509
|
constructor(get) {
|
|
@@ -46964,6 +47513,13 @@ class PaintFormatStore extends SpreadsheetStore {
|
|
|
46964
47513
|
this.highlightStore.unRegister(this);
|
|
46965
47514
|
});
|
|
46966
47515
|
}
|
|
47516
|
+
handle(cmd) {
|
|
47517
|
+
switch (cmd.type) {
|
|
47518
|
+
case "PAINT_FORMAT":
|
|
47519
|
+
this.paintFormat(cmd.sheetId, cmd.target);
|
|
47520
|
+
break;
|
|
47521
|
+
}
|
|
47522
|
+
}
|
|
46967
47523
|
activate(args) {
|
|
46968
47524
|
this.copiedData = this.copyFormats();
|
|
46969
47525
|
this.status = args.persistent ? "persistent" : "oneOff";
|
|
@@ -46973,16 +47529,7 @@ class PaintFormatStore extends SpreadsheetStore {
|
|
|
46973
47529
|
this.copiedData = undefined;
|
|
46974
47530
|
}
|
|
46975
47531
|
pasteFormat(target) {
|
|
46976
|
-
|
|
46977
|
-
const sheetId = this.getters.getActiveSheetId();
|
|
46978
|
-
this.cellClipboardHandler.paste({ zones: target, sheetId }, this.copiedData, {
|
|
46979
|
-
isCutOperation: false,
|
|
46980
|
-
pasteOption: "onlyFormat",
|
|
46981
|
-
});
|
|
46982
|
-
}
|
|
46983
|
-
if (this.status === "oneOff") {
|
|
46984
|
-
this.cancel();
|
|
46985
|
-
}
|
|
47532
|
+
this.model.dispatch("PAINT_FORMAT", { target, sheetId: this.getters.getActiveSheetId() });
|
|
46986
47533
|
}
|
|
46987
47534
|
get isActive() {
|
|
46988
47535
|
return this.status !== "inactive";
|
|
@@ -46990,7 +47537,24 @@ class PaintFormatStore extends SpreadsheetStore {
|
|
|
46990
47537
|
copyFormats() {
|
|
46991
47538
|
const sheetId = this.getters.getActiveSheetId();
|
|
46992
47539
|
const zones = this.getters.getSelectedZones();
|
|
46993
|
-
|
|
47540
|
+
const copiedData = {};
|
|
47541
|
+
for (const handler of this.clipboardHandlers) {
|
|
47542
|
+
Object.assign(copiedData, handler.copy(getClipboardDataPositions(sheetId, zones)));
|
|
47543
|
+
}
|
|
47544
|
+
return copiedData;
|
|
47545
|
+
}
|
|
47546
|
+
paintFormat(sheetId, target) {
|
|
47547
|
+
if (this.copiedData) {
|
|
47548
|
+
for (const handler of this.clipboardHandlers) {
|
|
47549
|
+
handler.paste({ zones: target, sheetId }, this.copiedData, {
|
|
47550
|
+
isCutOperation: false,
|
|
47551
|
+
pasteOption: "onlyFormat",
|
|
47552
|
+
});
|
|
47553
|
+
}
|
|
47554
|
+
}
|
|
47555
|
+
if (this.status === "oneOff") {
|
|
47556
|
+
this.cancel();
|
|
47557
|
+
}
|
|
46994
47558
|
}
|
|
46995
47559
|
get highlights() {
|
|
46996
47560
|
const data = this.copiedData;
|
|
@@ -47368,7 +47932,7 @@ class AbstractResizer extends owl.Component {
|
|
|
47368
47932
|
if (index < 0) {
|
|
47369
47933
|
return;
|
|
47370
47934
|
}
|
|
47371
|
-
if (this.state.waitingForMove
|
|
47935
|
+
if (this.state.waitingForMove) {
|
|
47372
47936
|
if (!this.env.model.getters.isGridSelectionActive()) {
|
|
47373
47937
|
this._selectElement(index, false);
|
|
47374
47938
|
}
|
|
@@ -48927,7 +49491,7 @@ class SidePanelStore extends SpreadsheetStore {
|
|
|
48927
49491
|
}
|
|
48928
49492
|
open(componentTag, panelProps = {}) {
|
|
48929
49493
|
const state = this.computeState(componentTag, panelProps);
|
|
48930
|
-
if (state.isOpen
|
|
49494
|
+
if (!state.isOpen) {
|
|
48931
49495
|
return;
|
|
48932
49496
|
}
|
|
48933
49497
|
if (this.isOpen && componentTag !== this.componentTag) {
|
|
@@ -49266,6 +49830,8 @@ class Grid extends owl.Component {
|
|
|
49266
49830
|
},
|
|
49267
49831
|
"Ctrl+D": async () => this.env.model.dispatch("COPY_PASTE_CELLS_ABOVE"),
|
|
49268
49832
|
"Ctrl+R": async () => this.env.model.dispatch("COPY_PASTE_CELLS_ON_LEFT"),
|
|
49833
|
+
"Ctrl+H": () => this.sidePanel.open("FindAndReplace", {}),
|
|
49834
|
+
"Ctrl+F": () => this.sidePanel.open("FindAndReplace", {}),
|
|
49269
49835
|
"Ctrl+Shift+E": () => this.setHorizontalAlign("center"),
|
|
49270
49836
|
"Ctrl+Shift+L": () => this.setHorizontalAlign("left"),
|
|
49271
49837
|
"Ctrl+Shift+R": () => this.setHorizontalAlign("right"),
|
|
@@ -49673,31 +50239,6 @@ class Grid extends owl.Component {
|
|
|
49673
50239
|
}
|
|
49674
50240
|
}
|
|
49675
50241
|
|
|
49676
|
-
/** @odoo-module */
|
|
49677
|
-
class EditableName extends owl.Component {
|
|
49678
|
-
static template = "o-spreadsheet-EditableName";
|
|
49679
|
-
static props = {
|
|
49680
|
-
name: String,
|
|
49681
|
-
displayName: String,
|
|
49682
|
-
onChanged: Function,
|
|
49683
|
-
};
|
|
49684
|
-
state;
|
|
49685
|
-
setup() {
|
|
49686
|
-
this.state = owl.useState({
|
|
49687
|
-
isEditing: false,
|
|
49688
|
-
name: "",
|
|
49689
|
-
});
|
|
49690
|
-
}
|
|
49691
|
-
rename() {
|
|
49692
|
-
this.state.isEditing = true;
|
|
49693
|
-
this.state.name = this.props.name;
|
|
49694
|
-
}
|
|
49695
|
-
save() {
|
|
49696
|
-
this.props.onChanged(this.state.name.trim());
|
|
49697
|
-
this.state.isEditing = false;
|
|
49698
|
-
}
|
|
49699
|
-
}
|
|
49700
|
-
|
|
49701
50242
|
/**
|
|
49702
50243
|
* BasePlugin
|
|
49703
50244
|
*
|
|
@@ -49798,12 +50339,10 @@ class BasePlugin {
|
|
|
49798
50339
|
*/
|
|
49799
50340
|
class CorePlugin extends BasePlugin {
|
|
49800
50341
|
getters;
|
|
49801
|
-
|
|
49802
|
-
constructor({ getters, stateObserver, range, dispatch, canDispatch, uuidGenerator, }) {
|
|
50342
|
+
constructor({ getters, stateObserver, range, dispatch, canDispatch }) {
|
|
49803
50343
|
super(stateObserver, dispatch, canDispatch);
|
|
49804
50344
|
range.addRangeProvider(this.adaptRanges.bind(this));
|
|
49805
50345
|
this.getters = getters;
|
|
49806
|
-
this.uuidGenerator = uuidGenerator;
|
|
49807
50346
|
}
|
|
49808
50347
|
// ---------------------------------------------------------------------------
|
|
49809
50348
|
// Import/Export
|
|
@@ -53617,7 +54156,7 @@ class SheetPlugin extends CorePlugin {
|
|
|
53617
54156
|
this.sheetIdsMapName[sheet.name] = sheet.id;
|
|
53618
54157
|
}
|
|
53619
54158
|
for (let sheetData of data.sheets) {
|
|
53620
|
-
const name = sheetData.name ||
|
|
54159
|
+
const name = sheetData.name || "Sheet" + (Object.keys(this.sheets).length + 1);
|
|
53621
54160
|
const { colNumber, rowNumber } = this.getImportedSheetSize(sheetData);
|
|
53622
54161
|
const sheet = {
|
|
53623
54162
|
id: sheetData.id,
|
|
@@ -54301,6 +54840,7 @@ class SheetPlugin extends CorePlugin {
|
|
|
54301
54840
|
}
|
|
54302
54841
|
}
|
|
54303
54842
|
|
|
54843
|
+
let nextTableId = 1;
|
|
54304
54844
|
class TablePlugin extends CorePlugin {
|
|
54305
54845
|
static getters = ["getCoreTable", "getCoreTables", "getCoreTableMatchingTopLeft"];
|
|
54306
54846
|
tables = {};
|
|
@@ -54368,7 +54908,7 @@ class TablePlugin extends CorePlugin {
|
|
|
54368
54908
|
const union = this.getters.getRangesUnion(ranges);
|
|
54369
54909
|
const mergesInTarget = this.getters.getMergesInZone(cmd.sheetId, union.zone);
|
|
54370
54910
|
this.dispatch("REMOVE_MERGE", { sheetId: cmd.sheetId, target: mergesInTarget });
|
|
54371
|
-
const id =
|
|
54911
|
+
const id = `${nextTableId++}`;
|
|
54372
54912
|
const config = cmd.config || DEFAULT_TABLE_CONFIG;
|
|
54373
54913
|
const newTable = cmd.tableType === "dynamic"
|
|
54374
54914
|
? this.createDynamicTable(id, union, config)
|
|
@@ -54521,7 +55061,7 @@ class TablePlugin extends CorePlugin {
|
|
|
54521
55061
|
filters = [];
|
|
54522
55062
|
for (const i of range(zone.left, zone.right + 1)) {
|
|
54523
55063
|
const filterZone = { ...zone, left: i, right: i };
|
|
54524
|
-
const uid =
|
|
55064
|
+
const uid = `${nextTableId++}`;
|
|
54525
55065
|
filters.push(this.createFilterFromZone(uid, tableRange.sheetId, filterZone, config));
|
|
54526
55066
|
}
|
|
54527
55067
|
}
|
|
@@ -54586,7 +55126,7 @@ class TablePlugin extends CorePlugin {
|
|
|
54586
55126
|
? table.filters.find((f) => f.col === i)
|
|
54587
55127
|
: undefined;
|
|
54588
55128
|
const filterZone = { ...tableZone, left: i, right: i };
|
|
54589
|
-
const filterId = oldFilter?.id ||
|
|
55129
|
+
const filterId = oldFilter?.id || `${nextTableId++}`;
|
|
54590
55130
|
filters.push(this.createFilterFromZone(filterId, tableRange.sheetId, filterZone, config));
|
|
54591
55131
|
}
|
|
54592
55132
|
}
|
|
@@ -54687,7 +55227,7 @@ class TablePlugin extends CorePlugin {
|
|
|
54687
55227
|
if (filters.length < zoneToDimension(tableZone).numberOfCols) {
|
|
54688
55228
|
for (let col = tableZone.left; col <= tableZone.right; col++) {
|
|
54689
55229
|
if (!filters.find((filter) => filter.col === col)) {
|
|
54690
|
-
const uid =
|
|
55230
|
+
const uid = `${nextTableId++}`;
|
|
54691
55231
|
const filterZone = { ...tableZone, left: col, right: col };
|
|
54692
55232
|
filters.push(this.createFilterFromZone(uid, sheetId, filterZone, table.config));
|
|
54693
55233
|
}
|
|
@@ -54703,7 +55243,7 @@ class TablePlugin extends CorePlugin {
|
|
|
54703
55243
|
import(data) {
|
|
54704
55244
|
for (const sheet of data.sheets) {
|
|
54705
55245
|
for (const tableData of sheet.tables || []) {
|
|
54706
|
-
const uuid =
|
|
55246
|
+
const uuid = `${nextTableId++}`;
|
|
54707
55247
|
const tableConfig = tableData.config || DEFAULT_TABLE_CONFIG;
|
|
54708
55248
|
const range = this.getters.getRangeFromSheetXC(sheet.id, tableData.range);
|
|
54709
55249
|
const tableType = tableData.type || "static";
|
|
@@ -55235,7 +55775,7 @@ class PivotCorePlugin extends CorePlugin {
|
|
|
55235
55775
|
case "DUPLICATE_PIVOT": {
|
|
55236
55776
|
const { pivotId, newPivotId } = cmd;
|
|
55237
55777
|
const pivot = deepCopy(this.getPivotCore(pivotId).definition);
|
|
55238
|
-
pivot.name =
|
|
55778
|
+
pivot.name = cmd.duplicatedPivotName ?? pivot.name + " (copy)";
|
|
55239
55779
|
this.addPivot(newPivotId, pivot);
|
|
55240
55780
|
break;
|
|
55241
55781
|
}
|
|
@@ -55275,7 +55815,7 @@ class PivotCorePlugin extends CorePlugin {
|
|
|
55275
55815
|
return `(#${formulaId}) ${this.getPivotName(pivotId)}`;
|
|
55276
55816
|
}
|
|
55277
55817
|
getPivotName(pivotId) {
|
|
55278
|
-
return
|
|
55818
|
+
return this.getPivotCore(pivotId).definition.name;
|
|
55279
55819
|
}
|
|
55280
55820
|
/**
|
|
55281
55821
|
* Returns the pivot core definition of the pivot with the given id.
|
|
@@ -56917,7 +57457,7 @@ class Evaluator {
|
|
|
56917
57457
|
cellsToCompute.addMany(arrayFormulasPositions);
|
|
56918
57458
|
cellsToCompute.addMany(this.getCellsDependingOn(arrayFormulasPositions));
|
|
56919
57459
|
this.evaluate(cellsToCompute);
|
|
56920
|
-
console.
|
|
57460
|
+
console.debug("evaluate Cells", performance.now() - start, "ms");
|
|
56921
57461
|
}
|
|
56922
57462
|
getArrayFormulasImpactedByChangesOf(positions) {
|
|
56923
57463
|
const impactedPositions = this.createEmptyPositionSet();
|
|
@@ -56961,7 +57501,7 @@ class Evaluator {
|
|
|
56961
57501
|
const start = performance.now();
|
|
56962
57502
|
this.evaluatedCells = new PositionMap();
|
|
56963
57503
|
this.evaluate(this.getAllCells());
|
|
56964
|
-
console.
|
|
57504
|
+
console.debug("evaluate all cells", performance.now() - start, "ms");
|
|
56965
57505
|
}
|
|
56966
57506
|
evaluateFormulaResult(sheetId, formulaString) {
|
|
56967
57507
|
const compiledFormula = compile(formulaString);
|
|
@@ -57967,8 +58507,7 @@ class EvaluationConditionalFormatPlugin extends UIPlugin {
|
|
|
57967
58507
|
.map((cell) => cell.value);
|
|
57968
58508
|
switch (threshold.type) {
|
|
57969
58509
|
case "value":
|
|
57970
|
-
|
|
57971
|
-
return result;
|
|
58510
|
+
return functionName === "max" ? largeMax(rangeValues) : largeMin(rangeValues);
|
|
57972
58511
|
case "number":
|
|
57973
58512
|
return Number(threshold.value);
|
|
57974
58513
|
case "percentage":
|
|
@@ -59052,7 +59591,7 @@ function withPivotPresentationLayer (PivotClass) {
|
|
|
59052
59591
|
const ranking = {};
|
|
59053
59592
|
const mainDimension = getFieldDimensionType(this, fieldNameWithGranularity);
|
|
59054
59593
|
const secondaryDimension = mainDimension === "row" ? "column" : "row";
|
|
59055
|
-
let pivotCells = this.getPivotValueCells();
|
|
59594
|
+
let pivotCells = this.getPivotValueCells(measure.id);
|
|
59056
59595
|
if (mainDimension === "column") {
|
|
59057
59596
|
// Transpose the pivot cells so we can do the same operations on the columns as on the rows
|
|
59058
59597
|
// This means that we need to transpose back the ranking at the end
|
|
@@ -59096,7 +59635,7 @@ function withPivotPresentationLayer (PivotClass) {
|
|
|
59096
59635
|
const cellsRunningTotals = {};
|
|
59097
59636
|
const mainDimension = getFieldDimensionType(this, fieldNameWithGranularity);
|
|
59098
59637
|
const secondaryDimension = mainDimension === "row" ? "column" : "row";
|
|
59099
|
-
let pivotCells = this.getPivotValueCells();
|
|
59638
|
+
let pivotCells = this.getPivotValueCells(measure.id);
|
|
59100
59639
|
if (mainDimension === "column") {
|
|
59101
59640
|
// Transpose the pivot cells so we can do the same operations on the columns as on the rows
|
|
59102
59641
|
// This means that we need to transpose back the totals at the end
|
|
@@ -59172,13 +59711,12 @@ function withPivotPresentationLayer (PivotClass) {
|
|
|
59172
59711
|
throw new NotAvailableError();
|
|
59173
59712
|
}
|
|
59174
59713
|
const comparedValue = this._getPivotCellValueAndFormat(measure.id, comparedDomain);
|
|
59175
|
-
|
|
59176
|
-
return comparedValueNumber;
|
|
59714
|
+
return this.strictMeasureValueToNumber(comparedValue);
|
|
59177
59715
|
}
|
|
59178
|
-
getPivotValueCells() {
|
|
59716
|
+
getPivotValueCells(measureId) {
|
|
59179
59717
|
return this.getTableStructure()
|
|
59180
59718
|
.getPivotCells()
|
|
59181
|
-
.map((col) => col.filter((cell) => cell.type === "VALUE"))
|
|
59719
|
+
.map((col) => col.filter((cell) => cell.type === "VALUE" && cell.measure === measureId))
|
|
59182
59720
|
.filter((col) => col.length > 0);
|
|
59183
59721
|
}
|
|
59184
59722
|
measureValueToNumber(result) {
|
|
@@ -60707,7 +61245,7 @@ class Session extends EventBus {
|
|
|
60707
61245
|
this.onMessageReceived(message);
|
|
60708
61246
|
}
|
|
60709
61247
|
this.isReplayingInitialRevisions = false;
|
|
60710
|
-
console.
|
|
61248
|
+
console.debug("Replayed", numberOfCommands, "commands in", performance.now() - start, "ms");
|
|
60711
61249
|
}
|
|
60712
61250
|
/**
|
|
60713
61251
|
* Notify the server that the user client left the collaborative session
|
|
@@ -60879,7 +61417,6 @@ class Session extends EventBus {
|
|
|
60879
61417
|
if (this.waitingAck) {
|
|
60880
61418
|
return;
|
|
60881
61419
|
}
|
|
60882
|
-
this.waitingAck = true;
|
|
60883
61420
|
this.sendPendingMessage();
|
|
60884
61421
|
}
|
|
60885
61422
|
/**
|
|
@@ -60916,6 +61453,7 @@ class Session extends EventBus {
|
|
|
60916
61453
|
throw new Error(`Trying to send a new revision while replaying initial revision. This can lead to endless dispatches every time the spreadsheet is open.
|
|
60917
61454
|
${JSON.stringify(message)}`);
|
|
60918
61455
|
}
|
|
61456
|
+
this.waitingAck = true;
|
|
60919
61457
|
this.transportService.sendMessage({
|
|
60920
61458
|
...message,
|
|
60921
61459
|
serverRevisionId: this.serverRevisionId,
|
|
@@ -60946,6 +61484,7 @@ class Session extends EventBus {
|
|
|
60946
61484
|
case "REMOTE_REVISION":
|
|
60947
61485
|
case "REVISION_REDONE":
|
|
60948
61486
|
case "REVISION_UNDONE":
|
|
61487
|
+
case "SNAPSHOT_CREATED":
|
|
60949
61488
|
return this.processedRevisions.has(message.nextRevisionId);
|
|
60950
61489
|
default:
|
|
60951
61490
|
return false;
|
|
@@ -61074,8 +61613,7 @@ class CollaborativePlugin extends UIPlugin {
|
|
|
61074
61613
|
}
|
|
61075
61614
|
const color = client.color;
|
|
61076
61615
|
/* Cell background */
|
|
61077
|
-
|
|
61078
|
-
ctx.fillStyle = cellBackgroundColor;
|
|
61616
|
+
ctx.fillStyle = `${color}10`;
|
|
61079
61617
|
ctx.lineWidth = 4 * thinLineWidth;
|
|
61080
61618
|
ctx.strokeStyle = color;
|
|
61081
61619
|
ctx.globalCompositeOperation = "multiply";
|
|
@@ -61442,8 +61980,7 @@ class HeaderVisibilityUIPlugin extends UIPlugin {
|
|
|
61442
61980
|
exportForExcel(data) {
|
|
61443
61981
|
for (const sheetData of data.sheets) {
|
|
61444
61982
|
for (const [row, rowData] of Object.entries(sheetData.rows)) {
|
|
61445
|
-
|
|
61446
|
-
rowData.isHidden = isHidden;
|
|
61983
|
+
rowData.isHidden = this.isRowHidden(sheetData.id, Number(row));
|
|
61447
61984
|
}
|
|
61448
61985
|
}
|
|
61449
61986
|
}
|
|
@@ -61503,12 +62040,13 @@ class InsertPivotPlugin extends UIPlugin {
|
|
|
61503
62040
|
this.dispatch("DUPLICATE_PIVOT", {
|
|
61504
62041
|
pivotId,
|
|
61505
62042
|
newPivotId,
|
|
62043
|
+
duplicatedPivotName: _t("%s (copy)", this.getters.getPivotCoreDefinition(pivotId).name),
|
|
61506
62044
|
});
|
|
61507
62045
|
const activeSheetId = this.getters.getActiveSheetId();
|
|
61508
62046
|
const position = this.getters.getSheetIds().indexOf(activeSheetId) + 1;
|
|
61509
62047
|
const formulaId = this.getters.getPivotFormulaId(newPivotId);
|
|
61510
62048
|
const newPivotName = this.getters.getPivotName(newPivotId);
|
|
61511
|
-
this.dispatch("CREATE_SHEET", {
|
|
62049
|
+
const result = this.dispatch("CREATE_SHEET", {
|
|
61512
62050
|
sheetId: newSheetId,
|
|
61513
62051
|
name: this.getPivotDuplicateSheetName(_t("%(newPivotName)s (Pivot #%(formulaId)s)", {
|
|
61514
62052
|
newPivotName,
|
|
@@ -61516,20 +62054,19 @@ class InsertPivotPlugin extends UIPlugin {
|
|
|
61516
62054
|
})),
|
|
61517
62055
|
position,
|
|
61518
62056
|
});
|
|
61519
|
-
|
|
61520
|
-
|
|
61521
|
-
|
|
61522
|
-
|
|
61523
|
-
|
|
61524
|
-
content: `=PIVOT(${formulaId})`,
|
|
61525
|
-
});
|
|
62057
|
+
if (result.isSuccessful) {
|
|
62058
|
+
this.dispatch("ACTIVATE_SHEET", { sheetIdFrom: activeSheetId, sheetIdTo: newSheetId });
|
|
62059
|
+
const pivot = this.getters.getPivot(pivotId);
|
|
62060
|
+
this.insertPivotWithTable(newSheetId, 0, 0, newPivotId, pivot.getTableStructure().export(), "dynamic");
|
|
62061
|
+
}
|
|
61526
62062
|
}
|
|
61527
62063
|
getPivotDuplicateSheetName(pivotName) {
|
|
61528
62064
|
let i = 1;
|
|
61529
62065
|
const names = this.getters.getSheetIds().map((id) => this.getters.getSheetName(id));
|
|
61530
|
-
|
|
62066
|
+
const sanitizedName = pivotName.replace(new RegExp(FORBIDDEN_IN_EXCEL_REGEX, "g"), " ");
|
|
62067
|
+
let name = sanitizedName;
|
|
61531
62068
|
while (names.includes(name)) {
|
|
61532
|
-
name = `${
|
|
62069
|
+
name = `${sanitizedName} (${i})`;
|
|
61533
62070
|
i++;
|
|
61534
62071
|
}
|
|
61535
62072
|
return name;
|
|
@@ -61968,7 +62505,6 @@ class SheetUIPlugin extends UIPlugin {
|
|
|
61968
62505
|
if (!isEqual(zone, newZone)) {
|
|
61969
62506
|
hasExpanded = true;
|
|
61970
62507
|
zone = newZone;
|
|
61971
|
-
continue;
|
|
61972
62508
|
}
|
|
61973
62509
|
} while (hasExpanded);
|
|
61974
62510
|
return zone;
|
|
@@ -63061,7 +63597,7 @@ class ClipboardPlugin extends UIPlugin {
|
|
|
63061
63597
|
case "ADD_COLUMNS_ROWS": {
|
|
63062
63598
|
this.status = "invisible";
|
|
63063
63599
|
// If we add a col/row inside or before the cut area, we invalidate the clipboard
|
|
63064
|
-
if (this._isCutOperation
|
|
63600
|
+
if (!this._isCutOperation || cmd.sheetId !== this.copiedData?.sheetId) {
|
|
63065
63601
|
return;
|
|
63066
63602
|
}
|
|
63067
63603
|
const isClipboardDirty = this.isColRowDirtyingClipboard(cmd.position === "before" ? cmd.base : cmd.base + 1, cmd.dimension);
|
|
@@ -63073,7 +63609,7 @@ class ClipboardPlugin extends UIPlugin {
|
|
|
63073
63609
|
case "REMOVE_COLUMNS_ROWS": {
|
|
63074
63610
|
this.status = "invisible";
|
|
63075
63611
|
// If we remove a col/row inside or before the cut area, we invalidate the clipboard
|
|
63076
|
-
if (this._isCutOperation
|
|
63612
|
+
if (!this._isCutOperation || cmd.sheetId !== this.copiedData?.sheetId) {
|
|
63077
63613
|
return;
|
|
63078
63614
|
}
|
|
63079
63615
|
for (let el of cmd.elements) {
|
|
@@ -63095,7 +63631,7 @@ class ClipboardPlugin extends UIPlugin {
|
|
|
63095
63631
|
break;
|
|
63096
63632
|
}
|
|
63097
63633
|
case "DELETE_SHEET":
|
|
63098
|
-
if (this._isCutOperation
|
|
63634
|
+
if (!this._isCutOperation) {
|
|
63099
63635
|
return;
|
|
63100
63636
|
}
|
|
63101
63637
|
if (this.originSheetId === cmd.sheetId) {
|
|
@@ -63988,8 +64524,7 @@ class GridSelectionPlugin extends UIPlugin {
|
|
|
63988
64524
|
this.setSelectionMixin({ zone, cell: { col, row } }, [zone]);
|
|
63989
64525
|
}
|
|
63990
64526
|
setActiveSheet(id) {
|
|
63991
|
-
|
|
63992
|
-
this.activeSheet = sheet;
|
|
64527
|
+
this.activeSheet = this.getters.getSheet(id);
|
|
63993
64528
|
}
|
|
63994
64529
|
activateNextSheet(direction) {
|
|
63995
64530
|
const sheetIds = this.getters.getSheetIds();
|
|
@@ -64683,9 +65218,6 @@ class SheetViewPlugin extends UIPlugin {
|
|
|
64683
65218
|
case "UNFREEZE_COLUMNS_ROWS":
|
|
64684
65219
|
this.resetViewports(this.getters.getActiveSheetId());
|
|
64685
65220
|
break;
|
|
64686
|
-
case "DELETE_SHEET":
|
|
64687
|
-
this.sheetsWithDirtyViewports.delete(cmd.sheetId);
|
|
64688
|
-
break;
|
|
64689
65221
|
case "SCROLL_TO_CELL":
|
|
64690
65222
|
this.refreshViewport(this.getters.getActiveSheetId(), { col: cmd.col, row: cmd.row });
|
|
64691
65223
|
break;
|
|
@@ -66637,10 +67169,9 @@ css /* scss */ `
|
|
|
66637
67169
|
user-select: none;
|
|
66638
67170
|
color: ${TEXT_BODY};
|
|
66639
67171
|
|
|
66640
|
-
.o-
|
|
67172
|
+
.o-sidePanelTitle {
|
|
66641
67173
|
line-height: 20px;
|
|
66642
67174
|
font-size: 16px;
|
|
66643
|
-
font-weight: 600;
|
|
66644
67175
|
}
|
|
66645
67176
|
|
|
66646
67177
|
.o-sidePanelHeader {
|
|
@@ -66725,6 +67256,10 @@ css /* scss */ `
|
|
|
66725
67256
|
}
|
|
66726
67257
|
}
|
|
66727
67258
|
}
|
|
67259
|
+
|
|
67260
|
+
.o-fw-bold {
|
|
67261
|
+
font-weight: 500;
|
|
67262
|
+
}
|
|
66728
67263
|
`;
|
|
66729
67264
|
class SidePanel extends owl.Component {
|
|
66730
67265
|
static template = "o-spreadsheet-SidePanel";
|
|
@@ -67556,8 +68091,7 @@ class WebClipboardWrapper {
|
|
|
67556
68091
|
for (const item of clipboardItems) {
|
|
67557
68092
|
for (const type of item.types) {
|
|
67558
68093
|
const blob = await item.getType(type);
|
|
67559
|
-
|
|
67560
|
-
clipboardContent[type] = text;
|
|
68094
|
+
clipboardContent[type] = await blob.text();
|
|
67561
68095
|
}
|
|
67562
68096
|
}
|
|
67563
68097
|
return { status: "ok", content: clipboardContent };
|
|
@@ -67869,7 +68403,6 @@ class Spreadsheet extends owl.Component {
|
|
|
67869
68403
|
spreadsheetRef = owl.useRef("spreadsheet");
|
|
67870
68404
|
spreadsheetRect = useSpreadsheetRect();
|
|
67871
68405
|
_focusGrid;
|
|
67872
|
-
keyDownMapping;
|
|
67873
68406
|
isViewportTooSmall = false;
|
|
67874
68407
|
notificationStore;
|
|
67875
68408
|
composerFocusStore;
|
|
@@ -67893,10 +68426,6 @@ class Spreadsheet extends owl.Component {
|
|
|
67893
68426
|
this.notificationStore = useStore(NotificationStore);
|
|
67894
68427
|
this.composerFocusStore = useStore(ComposerFocusStore);
|
|
67895
68428
|
this.sidePanel = useStore(SidePanelStore);
|
|
67896
|
-
this.keyDownMapping = {
|
|
67897
|
-
"CTRL+H": () => this.sidePanel.toggle("FindAndReplace", {}),
|
|
67898
|
-
"CTRL+F": () => this.sidePanel.toggle("FindAndReplace", {}),
|
|
67899
|
-
};
|
|
67900
68429
|
const fileStore = this.model.config.external.fileStore;
|
|
67901
68430
|
owl.useSubEnv({
|
|
67902
68431
|
model: this.model,
|
|
@@ -67996,20 +68525,6 @@ class Spreadsheet extends owl.Component {
|
|
|
67996
68525
|
}
|
|
67997
68526
|
this._focusGrid();
|
|
67998
68527
|
}
|
|
67999
|
-
onKeydown(ev) {
|
|
68000
|
-
let keyDownString = "";
|
|
68001
|
-
if (isCtrlKey(ev)) {
|
|
68002
|
-
keyDownString += "CTRL+";
|
|
68003
|
-
}
|
|
68004
|
-
keyDownString += ev.key.toUpperCase();
|
|
68005
|
-
let handler = this.keyDownMapping[keyDownString];
|
|
68006
|
-
if (handler) {
|
|
68007
|
-
ev.preventDefault();
|
|
68008
|
-
ev.stopPropagation();
|
|
68009
|
-
handler();
|
|
68010
|
-
return;
|
|
68011
|
-
}
|
|
68012
|
-
}
|
|
68013
68528
|
get gridHeight() {
|
|
68014
68529
|
const { height } = this.env.model.getters.getSheetViewDimension();
|
|
68015
68530
|
return height;
|
|
@@ -69424,10 +69939,10 @@ class SelectionStreamProcessorImpl {
|
|
|
69424
69939
|
getNextCellPosition(currentPosition, dimension, direction) {
|
|
69425
69940
|
const dimOfInterest = dimension === "cols" ? "col" : "row";
|
|
69426
69941
|
const startingPosition = { ...currentPosition };
|
|
69427
|
-
|
|
69428
|
-
|
|
69429
|
-
|
|
69430
|
-
|
|
69942
|
+
startingPosition[dimOfInterest] =
|
|
69943
|
+
dimension === "cols"
|
|
69944
|
+
? this.getNextAvailableCol(direction, startingPosition.col, startingPosition.row)
|
|
69945
|
+
: this.getNextAvailableRow(direction, startingPosition.col, startingPosition.row);
|
|
69431
69946
|
return { col: startingPosition.col, row: startingPosition.row };
|
|
69432
69947
|
}
|
|
69433
69948
|
getPosition() {
|
|
@@ -69551,6 +70066,8 @@ function createChart(chart, chartSheetIndex, data) {
|
|
|
69551
70066
|
case "pie":
|
|
69552
70067
|
plot = addDoughnutChart(chart.data, chartSheetIndex, data, { holeSize: 0 });
|
|
69553
70068
|
break;
|
|
70069
|
+
case "radar":
|
|
70070
|
+
plot = addRadarChart(chart.data);
|
|
69554
70071
|
}
|
|
69555
70072
|
let position = "t";
|
|
69556
70073
|
switch (chart.data.legendPosition) {
|
|
@@ -70009,6 +70526,53 @@ function addScatterChart(chart) {
|
|
|
70009
70526
|
`
|
|
70010
70527
|
: ""}`;
|
|
70011
70528
|
}
|
|
70529
|
+
function addRadarChart(chart) {
|
|
70530
|
+
const dataSetsColors = chart.dataSets.map((ds) => ds.backgroundColor ?? "");
|
|
70531
|
+
const colors = new ColorGenerator(chart.dataSets.length, dataSetsColors);
|
|
70532
|
+
const dataSetsNodes = [];
|
|
70533
|
+
for (const [dsIndex, dataset] of Object.entries(chart.dataSets)) {
|
|
70534
|
+
const color = toXlsxHexColor(colors.next());
|
|
70535
|
+
const dataShapeProperty = shapeProperty({
|
|
70536
|
+
line: {
|
|
70537
|
+
width: 2.5,
|
|
70538
|
+
style: "solid",
|
|
70539
|
+
color,
|
|
70540
|
+
},
|
|
70541
|
+
});
|
|
70542
|
+
const dataSetNode = escapeXml /*xml*/ `
|
|
70543
|
+
<c:ser>
|
|
70544
|
+
<c:idx val="${dsIndex}"/>
|
|
70545
|
+
<c:order val="${dsIndex}"/>
|
|
70546
|
+
<c:smooth val="0"/>
|
|
70547
|
+
<c:marker>
|
|
70548
|
+
<c:symbol val="circle" />
|
|
70549
|
+
<c:size val="5"/>
|
|
70550
|
+
${shapeProperty({ backgroundColor: color, line: { color } })}
|
|
70551
|
+
</c:marker>
|
|
70552
|
+
${extractDataSetLabel(dataset.label)}
|
|
70553
|
+
${dataShapeProperty}
|
|
70554
|
+
${chart.labelRange ? escapeXml `<c:cat>${stringRef(chart.labelRange)}</c:cat>` : ""} <!-- x-coordinate values -->
|
|
70555
|
+
<c:val> <!-- x-coordinate values -->
|
|
70556
|
+
${numberRef(dataset.range)}
|
|
70557
|
+
</c:val>
|
|
70558
|
+
</c:ser>
|
|
70559
|
+
`;
|
|
70560
|
+
dataSetsNodes.push(dataSetNode);
|
|
70561
|
+
}
|
|
70562
|
+
return escapeXml /*xml*/ `
|
|
70563
|
+
${escapeXml /*xml*/ `
|
|
70564
|
+
<c:radarChart>
|
|
70565
|
+
<c:radarStyle val="marker"/>
|
|
70566
|
+
<c:varyColors val="0"/>
|
|
70567
|
+
${joinXmlNodes(dataSetsNodes)}
|
|
70568
|
+
<c:axId val="${catAxId}" />
|
|
70569
|
+
<c:axId val="${valAxId}" />
|
|
70570
|
+
</c:radarChart>
|
|
70571
|
+
${addAx("b", "c:catAx", catAxId, valAxId, chart.axesDesign?.x?.title, chart.fontColor)}
|
|
70572
|
+
${addAx("l", "c:valAx", valAxId, catAxId, chart.axesDesign?.y?.title, chart.fontColor)}
|
|
70573
|
+
`}
|
|
70574
|
+
`;
|
|
70575
|
+
}
|
|
70012
70576
|
function addDoughnutChart(chart, chartSheetIndex, data, { holeSize } = { holeSize: 50 }) {
|
|
70013
70577
|
const maxLength = largeMax(chart.dataSets.map((ds) => getRangeSize(ds.range, chartSheetIndex, data)));
|
|
70014
70578
|
const colors = new ColorGenerator(maxLength);
|
|
@@ -70882,6 +71446,15 @@ function addTableColumns(table, sheetData) {
|
|
|
70882
71446
|
["id", i + 1], // id cannot be 0
|
|
70883
71447
|
["name", colName],
|
|
70884
71448
|
];
|
|
71449
|
+
if (table.config.totalRow) {
|
|
71450
|
+
// Note: To be 100% complete, we could also add a `totalsRowLabel` attribute for total strings, and a tag
|
|
71451
|
+
// `<totalsRowFormula>` for the formula of the total. But those doesn't seem to be mandatory for Excel.
|
|
71452
|
+
const colTotalXc = toXC(tableZone.left + i, tableZone.bottom);
|
|
71453
|
+
const colTotalContent = sheetData.cells[colTotalXc]?.content;
|
|
71454
|
+
if (colTotalContent?.startsWith("=")) {
|
|
71455
|
+
colAttributes.push(["totalsRowFunction", "custom"]);
|
|
71456
|
+
}
|
|
71457
|
+
}
|
|
70885
71458
|
columns.push(escapeXml /*xml*/ `<tableColumn ${formatAttributes(colAttributes)}/>`);
|
|
70886
71459
|
}
|
|
70887
71460
|
return escapeXml /*xml*/ `
|
|
@@ -70976,8 +71549,9 @@ function addRows(construct, data, sheet) {
|
|
|
70976
71549
|
}
|
|
70977
71550
|
else if (cell.content && cell.content !== "") {
|
|
70978
71551
|
const isTableHeader = isCellTableHeader(c, r, sheet);
|
|
71552
|
+
const isTableTotal = isCellTableTotal(c, r, sheet);
|
|
70979
71553
|
const isPlainText = !!(cell.format && isTextFormat(data.formats[cell.format]));
|
|
70980
|
-
({ attrs: additionalAttrs, node: cellNode } = addContent(cell.content, construct.sharedStrings, isTableHeader || isPlainText));
|
|
71554
|
+
({ attrs: additionalAttrs, node: cellNode } = addContent(cell.content, construct.sharedStrings, isTableHeader || isTableTotal || isPlainText));
|
|
70981
71555
|
}
|
|
70982
71556
|
attributes.push(...additionalAttrs);
|
|
70983
71557
|
// prettier-ignore
|
|
@@ -71011,6 +71585,16 @@ function isCellTableHeader(col, row, sheet) {
|
|
|
71011
71585
|
return isInside(col, row, headerZone);
|
|
71012
71586
|
});
|
|
71013
71587
|
}
|
|
71588
|
+
function isCellTableTotal(col, row, sheet) {
|
|
71589
|
+
return sheet.tables.some((table) => {
|
|
71590
|
+
if (!table.config.totalRow) {
|
|
71591
|
+
return false;
|
|
71592
|
+
}
|
|
71593
|
+
const zone = toZone(table.range);
|
|
71594
|
+
const totalZone = { ...zone, top: zone.bottom };
|
|
71595
|
+
return isInside(col, row, totalZone);
|
|
71596
|
+
});
|
|
71597
|
+
}
|
|
71014
71598
|
function addHyperlinks(construct, data, sheetIndex) {
|
|
71015
71599
|
const sheet = data.sheets[sheetIndex];
|
|
71016
71600
|
const cells = sheet.cells;
|
|
@@ -71096,25 +71680,23 @@ function addSheetViews(sheet) {
|
|
|
71096
71680
|
["showGridLines", sheet.areGridLinesVisible ? 1 : 0],
|
|
71097
71681
|
["workbookViewId", 0],
|
|
71098
71682
|
];
|
|
71099
|
-
|
|
71683
|
+
return escapeXml /*xml*/ `
|
|
71100
71684
|
<sheetViews>
|
|
71101
71685
|
<sheetView ${formatAttributes(sheetViewAttrs)}>
|
|
71102
71686
|
${splitPanes}
|
|
71103
71687
|
</sheetView>
|
|
71104
71688
|
</sheetViews>
|
|
71105
71689
|
`;
|
|
71106
|
-
return sheetView;
|
|
71107
71690
|
}
|
|
71108
71691
|
function addSheetProperties(sheet) {
|
|
71109
71692
|
if (!sheet.color) {
|
|
71110
71693
|
return "";
|
|
71111
71694
|
}
|
|
71112
|
-
|
|
71695
|
+
return escapeXml /*xml*/ `
|
|
71113
71696
|
<sheetPr>
|
|
71114
71697
|
<tabColor ${formatAttributes([["rgb", toXlsxHexColor(sheet.color)]])} />
|
|
71115
71698
|
</sheetPr>
|
|
71116
71699
|
`;
|
|
71117
|
-
return sheetView;
|
|
71118
71700
|
}
|
|
71119
71701
|
|
|
71120
71702
|
/**
|
|
@@ -71427,9 +72009,7 @@ var Status;
|
|
|
71427
72009
|
})(Status || (Status = {}));
|
|
71428
72010
|
class Model extends EventBus {
|
|
71429
72011
|
corePlugins = [];
|
|
71430
|
-
featurePlugins = [];
|
|
71431
72012
|
statefulUIPlugins = [];
|
|
71432
|
-
coreViewsPlugins = [];
|
|
71433
72013
|
range;
|
|
71434
72014
|
session;
|
|
71435
72015
|
/**
|
|
@@ -71474,7 +72054,7 @@ class Model extends EventBus {
|
|
|
71474
72054
|
coreHandlers = [];
|
|
71475
72055
|
constructor(data = {}, config = {}, stateUpdateMessages = [], uuidGenerator = new UuidGenerator(), verboseImport = false) {
|
|
71476
72056
|
const start = performance.now();
|
|
71477
|
-
console.
|
|
72057
|
+
console.debug("##### Model creation #####");
|
|
71478
72058
|
super();
|
|
71479
72059
|
setDefaultTranslationMethod();
|
|
71480
72060
|
stateUpdateMessages = repairInitialMessages(data, stateUpdateMessages);
|
|
@@ -71501,7 +72081,6 @@ class Model extends EventBus {
|
|
|
71501
72081
|
isReadonly: () => this.config.mode === "readonly" || this.config.mode === "dashboard",
|
|
71502
72082
|
isDashboard: () => this.config.mode === "dashboard",
|
|
71503
72083
|
};
|
|
71504
|
-
this.uuidGenerator.setIsFastStrategy(true);
|
|
71505
72084
|
// Initiate stream processor
|
|
71506
72085
|
this.selection = new SelectionStreamProcessorImpl(this.getters);
|
|
71507
72086
|
this.coreHandlers.push(this.range);
|
|
@@ -71516,7 +72095,6 @@ class Model extends EventBus {
|
|
|
71516
72095
|
this.session.loadInitialMessages(stateUpdateMessages);
|
|
71517
72096
|
for (let Plugin of coreViewsPluginRegistry.getAll()) {
|
|
71518
72097
|
const plugin = this.setupUiPlugin(Plugin);
|
|
71519
|
-
this.coreViewsPlugins.push(plugin);
|
|
71520
72098
|
this.handlers.push(plugin);
|
|
71521
72099
|
this.uiHandlers.push(plugin);
|
|
71522
72100
|
this.coreHandlers.push(plugin);
|
|
@@ -71529,11 +72107,9 @@ class Model extends EventBus {
|
|
|
71529
72107
|
}
|
|
71530
72108
|
for (let Plugin of featurePluginRegistry.getAll()) {
|
|
71531
72109
|
const plugin = this.setupUiPlugin(Plugin);
|
|
71532
|
-
this.featurePlugins.push(plugin);
|
|
71533
72110
|
this.handlers.push(plugin);
|
|
71534
72111
|
this.uiHandlers.push(plugin);
|
|
71535
72112
|
}
|
|
71536
|
-
this.uuidGenerator.setIsFastStrategy(false);
|
|
71537
72113
|
// starting plugins
|
|
71538
72114
|
this.dispatch("START");
|
|
71539
72115
|
// Model should be the last permanent subscriber in the list since he should render
|
|
@@ -71547,16 +72123,16 @@ class Model extends EventBus {
|
|
|
71547
72123
|
this.joinSession();
|
|
71548
72124
|
if (config.snapshotRequested) {
|
|
71549
72125
|
const startSnapshot = performance.now();
|
|
71550
|
-
console.
|
|
72126
|
+
console.debug("Snapshot requested");
|
|
71551
72127
|
this.session.snapshot(this.exportData());
|
|
71552
72128
|
this.garbageCollectExternalResources();
|
|
71553
|
-
console.
|
|
72129
|
+
console.debug("Snapshot taken in", performance.now() - startSnapshot, "ms");
|
|
71554
72130
|
}
|
|
71555
72131
|
// mark all models as "raw", so they will not be turned into reactive objects
|
|
71556
72132
|
// by owl, since we do not rely on reactivity
|
|
71557
72133
|
owl.markRaw(this);
|
|
71558
|
-
console.
|
|
71559
|
-
console.
|
|
72134
|
+
console.debug("Model created in", performance.now() - start, "ms");
|
|
72135
|
+
console.debug("######");
|
|
71560
72136
|
}
|
|
71561
72137
|
joinSession() {
|
|
71562
72138
|
this.session.join(this.config.client);
|
|
@@ -71615,7 +72191,7 @@ class Model extends EventBus {
|
|
|
71615
72191
|
this.finalize();
|
|
71616
72192
|
}
|
|
71617
72193
|
setupSession(revisionId) {
|
|
71618
|
-
|
|
72194
|
+
return new Session(buildRevisionLog({
|
|
71619
72195
|
initialRevisionId: revisionId,
|
|
71620
72196
|
recordChanges: this.state.recordChanges.bind(this.state),
|
|
71621
72197
|
dispatch: (command) => {
|
|
@@ -71628,7 +72204,6 @@ class Model extends EventBus {
|
|
|
71628
72204
|
this.isReplayingCommand = false;
|
|
71629
72205
|
},
|
|
71630
72206
|
}), this.config.transportService, revisionId);
|
|
71631
|
-
return session;
|
|
71632
72207
|
}
|
|
71633
72208
|
setupSessionEvents() {
|
|
71634
72209
|
this.session.on("remote-revision-received", this, this.onRemoteRevisionReceived);
|
|
@@ -71681,7 +72256,6 @@ class Model extends EventBus {
|
|
|
71681
72256
|
range: this.range,
|
|
71682
72257
|
dispatch: this.dispatchFromCorePlugin,
|
|
71683
72258
|
canDispatch: this.canDispatch,
|
|
71684
|
-
uuidGenerator: this.uuidGenerator,
|
|
71685
72259
|
custom: this.config.custom,
|
|
71686
72260
|
external: this.config.external,
|
|
71687
72261
|
};
|
|
@@ -71722,8 +72296,7 @@ class Model extends EventBus {
|
|
|
71722
72296
|
return results;
|
|
71723
72297
|
}
|
|
71724
72298
|
checkDispatchAllowedLocalCommand(command) {
|
|
71725
|
-
|
|
71726
|
-
return results;
|
|
72299
|
+
return this.uiHandlers.map((handler) => handler.allowDispatch(command));
|
|
71727
72300
|
}
|
|
71728
72301
|
finalize() {
|
|
71729
72302
|
this.status = 3 /* Status.Finalizing */;
|
|
@@ -71780,7 +72353,7 @@ class Model extends EventBus {
|
|
|
71780
72353
|
this.finalize();
|
|
71781
72354
|
const time = performance.now() - start;
|
|
71782
72355
|
if (time > 5) {
|
|
71783
|
-
console.
|
|
72356
|
+
console.debug(type, time, "ms");
|
|
71784
72357
|
}
|
|
71785
72358
|
});
|
|
71786
72359
|
this.session.save(command, commands, changes);
|
|
@@ -72083,7 +72656,6 @@ const components = {
|
|
|
72083
72656
|
PivotDimensionOrder,
|
|
72084
72657
|
PivotDimension,
|
|
72085
72658
|
PivotLayoutConfigurator,
|
|
72086
|
-
EditableName,
|
|
72087
72659
|
PivotDeferUpdate,
|
|
72088
72660
|
PivotTitleSection,
|
|
72089
72661
|
CogWheelMenu,
|
|
@@ -72175,6 +72747,6 @@ exports.tokenColors = tokenColors;
|
|
|
72175
72747
|
exports.tokenize = tokenize;
|
|
72176
72748
|
|
|
72177
72749
|
|
|
72178
|
-
__info__.version = "18.1.0-alpha.
|
|
72179
|
-
__info__.date = "2024-
|
|
72180
|
-
__info__.hash = "
|
|
72750
|
+
__info__.version = "18.1.0-alpha.2";
|
|
72751
|
+
__info__.date = "2024-10-24T08:53:21.828Z";
|
|
72752
|
+
__info__.hash = "2a01250";
|