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