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