@odoo/o-spreadsheet 17.4.0-alpha.8 → 17.4.0
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 +2014 -1176
- package/dist/o-spreadsheet.d.ts +145 -55
- package/dist/o-spreadsheet.esm.js +2014 -1176
- package/dist/o-spreadsheet.iife.js +2014 -1176
- package/dist/o-spreadsheet.iife.min.js +347 -347
- package/dist/o_spreadsheet.xml +71 -10
- package/package.json +3 -3
- package/readme.md +2 -1
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* This file is generated by o-spreadsheet build tools. Do not edit it.
|
|
4
4
|
* @see https://github.com/odoo/o-spreadsheet
|
|
5
|
-
* @version 17.4.0
|
|
6
|
-
* @date 2024-
|
|
7
|
-
* @hash
|
|
5
|
+
* @version 17.4.0
|
|
6
|
+
* @date 2024-07-12T04:41:51.675Z
|
|
7
|
+
* @hash f8e8543
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
'use strict';
|
|
@@ -592,7 +592,7 @@ function getAddHeaderStartIndex(position, base) {
|
|
|
592
592
|
/**
|
|
593
593
|
* Compares two objects.
|
|
594
594
|
*/
|
|
595
|
-
function deepEquals(o1, o2
|
|
595
|
+
function deepEquals(o1, o2) {
|
|
596
596
|
if (o1 === o2)
|
|
597
597
|
return true;
|
|
598
598
|
if ((o1 && !o2) || (o2 && !o1))
|
|
@@ -608,17 +608,13 @@ function deepEquals(o1, o2, ignoreFunctions) {
|
|
|
608
608
|
}
|
|
609
609
|
}
|
|
610
610
|
for (const key in o1) {
|
|
611
|
-
|
|
612
|
-
if (typeOfO1Key !== typeof o2[key])
|
|
611
|
+
if (typeof o1[key] !== typeof o2[key])
|
|
613
612
|
return false;
|
|
614
|
-
if (
|
|
615
|
-
if (!deepEquals(o1[key], o2[key]
|
|
613
|
+
if (typeof o1[key] === "object") {
|
|
614
|
+
if (!deepEquals(o1[key], o2[key]))
|
|
616
615
|
return false;
|
|
617
616
|
}
|
|
618
617
|
else {
|
|
619
|
-
if (ignoreFunctions && typeOfO1Key === "function") {
|
|
620
|
-
continue;
|
|
621
|
-
}
|
|
622
618
|
if (o1[key] !== o2[key])
|
|
623
619
|
return false;
|
|
624
620
|
}
|
|
@@ -1156,31 +1152,15 @@ function numberToLetters(n) {
|
|
|
1156
1152
|
return numberToLetters(Math.floor(n / 26) - 1) + numberToLetters(n % 26);
|
|
1157
1153
|
}
|
|
1158
1154
|
}
|
|
1159
|
-
const LETTERS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
|
1160
|
-
const LETTERS_NUMBER_MAPPING = {};
|
|
1161
|
-
for (const letter of LETTERS) {
|
|
1162
|
-
const colIndex = letter.charCodeAt(0) - 64;
|
|
1163
|
-
LETTERS_NUMBER_MAPPING[letter] = colIndex;
|
|
1164
|
-
LETTERS_NUMBER_MAPPING[letter.toLowerCase()] = colIndex;
|
|
1165
|
-
}
|
|
1166
|
-
/**
|
|
1167
|
-
* Convert a string (describing a column) to its number value.
|
|
1168
|
-
*
|
|
1169
|
-
* Examples:
|
|
1170
|
-
* 'A' => 0
|
|
1171
|
-
* 'Z' => 25
|
|
1172
|
-
* 'AA' => 26
|
|
1173
|
-
*/
|
|
1174
1155
|
function lettersToNumber(letters) {
|
|
1175
|
-
let result =
|
|
1156
|
+
let result = 0;
|
|
1176
1157
|
const l = letters.length;
|
|
1177
|
-
let
|
|
1178
|
-
|
|
1179
|
-
const
|
|
1180
|
-
result
|
|
1181
|
-
pow *= 26;
|
|
1158
|
+
for (let i = 0; i < l; i++) {
|
|
1159
|
+
const charCode = letters.charCodeAt(i);
|
|
1160
|
+
const colIndex = charCode >= 65 && charCode <= 90 ? charCode - 64 : charCode - 96;
|
|
1161
|
+
result = result * 26 + colIndex;
|
|
1182
1162
|
}
|
|
1183
|
-
return result;
|
|
1163
|
+
return result - 1;
|
|
1184
1164
|
}
|
|
1185
1165
|
function isCharALetter(char) {
|
|
1186
1166
|
return (char >= "A" && char <= "Z") || (char >= "a" && char <= "z");
|
|
@@ -1199,37 +1179,27 @@ function isCharADigit(char) {
|
|
|
1199
1179
|
*/
|
|
1200
1180
|
function toCartesian(xc) {
|
|
1201
1181
|
xc = xc.trim();
|
|
1202
|
-
let
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
}
|
|
1211
|
-
numberPartStart = i;
|
|
1212
|
-
}
|
|
1213
|
-
// Number part
|
|
1214
|
-
if (!isCharADigit(char)) {
|
|
1215
|
-
if (char === "$" && i === numberPartStart) {
|
|
1216
|
-
continue;
|
|
1217
|
-
}
|
|
1218
|
-
throw new Error(`Invalid cell description: ${xc}`);
|
|
1219
|
-
}
|
|
1182
|
+
let letterPart = "";
|
|
1183
|
+
let numberPart = "";
|
|
1184
|
+
let i = 0;
|
|
1185
|
+
// Process letter part
|
|
1186
|
+
if (xc[i] === "$")
|
|
1187
|
+
i++;
|
|
1188
|
+
while (i < xc.length && isCharALetter(xc[i])) {
|
|
1189
|
+
letterPart += xc[i++];
|
|
1220
1190
|
}
|
|
1221
|
-
if (
|
|
1191
|
+
if (letterPart.length === 0 || letterPart.length > 3) {
|
|
1192
|
+
// limit to max 3 letters for performance reasons
|
|
1222
1193
|
throw new Error(`Invalid cell description: ${xc}`);
|
|
1223
1194
|
}
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
numberPart.length > 7) {
|
|
1195
|
+
// Process number part
|
|
1196
|
+
if (xc[i] === "$")
|
|
1197
|
+
i++;
|
|
1198
|
+
while (i < xc.length && isCharADigit(xc[i])) {
|
|
1199
|
+
numberPart += xc[i++];
|
|
1200
|
+
}
|
|
1201
|
+
if (i !== xc.length || numberPart.length === 0 || numberPart.length > 7) {
|
|
1202
|
+
// limit to max 7 numbers for performance reasons
|
|
1233
1203
|
throw new Error(`Invalid cell description: ${xc}`);
|
|
1234
1204
|
}
|
|
1235
1205
|
const col = lettersToNumber(letterPart);
|
|
@@ -1831,7 +1801,7 @@ const getFormulaNumberRegex = memoize(function getFormulaNumberRegex(decimalSepa
|
|
|
1831
1801
|
});
|
|
1832
1802
|
const getNumberRegex = memoize(function getNumberRegex(locale) {
|
|
1833
1803
|
const decimalSeparator = escapeRegExp(locale.decimalSeparator);
|
|
1834
|
-
const thousandsSeparator = escapeRegExp(locale.thousandsSeparator);
|
|
1804
|
+
const thousandsSeparator = escapeRegExp(locale.thousandsSeparator || "");
|
|
1835
1805
|
const pIntegerAndDecimals = `(?:\\d+(?:${thousandsSeparator}\\d{3,})*(?:${decimalSeparator}\\d*)?)`; // pattern that match integer number with or without decimal digits
|
|
1836
1806
|
const pOnlyDecimals = `(?:${decimalSeparator}\\d+)`; // pattern that match only expression with decimal digits
|
|
1837
1807
|
const pScientificFormat = "(?:e(?:\\+|-)?\\d+)?"; // pattern that match scientific format between zero and one time (should be placed before pPercentFormat)
|
|
@@ -1864,7 +1834,7 @@ function isNumber(value, locale) {
|
|
|
1864
1834
|
return getNumberRegex(locale).test(value.trim());
|
|
1865
1835
|
}
|
|
1866
1836
|
const getInvaluableSymbolsRegexp = memoize(function getInvaluableSymbolsRegexp(locale) {
|
|
1867
|
-
return new RegExp(`[\$€${escapeRegExp(locale.thousandsSeparator)}]`, "g");
|
|
1837
|
+
return new RegExp(`[\$€${escapeRegExp(locale.thousandsSeparator || "")}]`, "g");
|
|
1868
1838
|
});
|
|
1869
1839
|
/**
|
|
1870
1840
|
* Convert a string into a number. It assumes that the string actually represents
|
|
@@ -2667,11 +2637,11 @@ function generateMatrix(nColumns, nRows, callback) {
|
|
|
2667
2637
|
}
|
|
2668
2638
|
return returned;
|
|
2669
2639
|
}
|
|
2670
|
-
function matrixMap(matrix,
|
|
2640
|
+
function matrixMap(matrix, callback) {
|
|
2671
2641
|
if (matrix.length === 0) {
|
|
2672
2642
|
return [];
|
|
2673
2643
|
}
|
|
2674
|
-
return generateMatrix(matrix.length, matrix[0].length, (col, row) =>
|
|
2644
|
+
return generateMatrix(matrix.length, matrix[0].length, (col, row) => callback(matrix[col][row]));
|
|
2675
2645
|
}
|
|
2676
2646
|
function matrixForEach(matrix, fn) {
|
|
2677
2647
|
const numberOfCols = matrix.length;
|
|
@@ -2771,6 +2741,9 @@ function getPredicate(descr, locale) {
|
|
|
2771
2741
|
* If the character is a special regular expression character, it is escaped with "\\".
|
|
2772
2742
|
*/
|
|
2773
2743
|
const wildcardToRegExp = memoize(function wildcardToRegExp(operand) {
|
|
2744
|
+
if (operand === "*") {
|
|
2745
|
+
return /.+/;
|
|
2746
|
+
}
|
|
2774
2747
|
let exp = "";
|
|
2775
2748
|
let predecessor = "";
|
|
2776
2749
|
for (let char of operand) {
|
|
@@ -2794,9 +2767,9 @@ const wildcardToRegExp = memoize(function wildcardToRegExp(operand) {
|
|
|
2794
2767
|
}
|
|
2795
2768
|
return new RegExp("^" + exp + "$", "i");
|
|
2796
2769
|
});
|
|
2797
|
-
function evaluatePredicate(value, criterion) {
|
|
2770
|
+
function evaluatePredicate(value = "", criterion) {
|
|
2798
2771
|
const { operator, operand } = criterion;
|
|
2799
|
-
if (
|
|
2772
|
+
if (operand === undefined || value === null || operand === null) {
|
|
2800
2773
|
return false;
|
|
2801
2774
|
}
|
|
2802
2775
|
if (typeof operand === "number" && operator === "=") {
|
|
@@ -5888,19 +5861,22 @@ class TokenizingChars {
|
|
|
5888
5861
|
}
|
|
5889
5862
|
|
|
5890
5863
|
function isValidLocale(locale) {
|
|
5891
|
-
if (!
|
|
5892
|
-
typeof locale
|
|
5893
|
-
typeof locale.
|
|
5894
|
-
typeof locale.code === "string" &&
|
|
5895
|
-
typeof locale.thousandsSeparator === "string" &&
|
|
5896
|
-
typeof locale.decimalSeparator === "string" &&
|
|
5897
|
-
typeof locale.dateFormat === "string" &&
|
|
5898
|
-
typeof locale.timeFormat === "string" &&
|
|
5899
|
-
typeof locale.formulaArgSeparator === "string")) {
|
|
5864
|
+
if (!locale ||
|
|
5865
|
+
typeof locale !== "object" ||
|
|
5866
|
+
!(!locale.thousandsSeparator || typeof locale.thousandsSeparator === "string")) {
|
|
5900
5867
|
return false;
|
|
5901
5868
|
}
|
|
5902
|
-
|
|
5903
|
-
|
|
5869
|
+
for (const property of [
|
|
5870
|
+
"code",
|
|
5871
|
+
"name",
|
|
5872
|
+
"decimalSeparator",
|
|
5873
|
+
"dateFormat",
|
|
5874
|
+
"timeFormat",
|
|
5875
|
+
"formulaArgSeparator",
|
|
5876
|
+
]) {
|
|
5877
|
+
if (!locale[property] || typeof locale[property] !== "string") {
|
|
5878
|
+
return false;
|
|
5879
|
+
}
|
|
5904
5880
|
}
|
|
5905
5881
|
if (locale.formulaArgSeparator === locale.decimalSeparator) {
|
|
5906
5882
|
return false;
|
|
@@ -5998,7 +5974,10 @@ function canonicalizeNumberLiteral(content, locale) {
|
|
|
5998
5974
|
if (locale.decimalSeparator === "." || !isNumber(content, locale)) {
|
|
5999
5975
|
return content;
|
|
6000
5976
|
}
|
|
6001
|
-
|
|
5977
|
+
if (locale.thousandsSeparator) {
|
|
5978
|
+
content = content.replaceAll(locale.thousandsSeparator, "");
|
|
5979
|
+
}
|
|
5980
|
+
return content.replace(locale.decimalSeparator, ".");
|
|
6002
5981
|
}
|
|
6003
5982
|
/**
|
|
6004
5983
|
* Change a content string from the given locale to its canonical form (en_US locale). Also convert date string.
|
|
@@ -6295,24 +6274,6 @@ const dayOfMonthAdapter = {
|
|
|
6295
6274
|
return `${normalizedValue}`;
|
|
6296
6275
|
},
|
|
6297
6276
|
};
|
|
6298
|
-
/**
|
|
6299
|
-
* Normalized value: "2/2023" for week 2 of 2023
|
|
6300
|
-
*/
|
|
6301
|
-
const weekAdapter = {
|
|
6302
|
-
normalizeFunctionValue(value) {
|
|
6303
|
-
const [week, year] = toString(value).split("/");
|
|
6304
|
-
return `${Number(week)}/${Number(year)}`;
|
|
6305
|
-
},
|
|
6306
|
-
toValueAndFormat(normalizedValue, locale) {
|
|
6307
|
-
const [week, year] = normalizedValue.split("/");
|
|
6308
|
-
return {
|
|
6309
|
-
value: _t("W%(week)s %(year)s", { week, year }),
|
|
6310
|
-
};
|
|
6311
|
-
},
|
|
6312
|
-
toFunctionValue(normalizedValue) {
|
|
6313
|
-
return `"${normalizedValue}"`;
|
|
6314
|
-
},
|
|
6315
|
-
};
|
|
6316
6277
|
/**
|
|
6317
6278
|
* normalizes iso week number
|
|
6318
6279
|
*/
|
|
@@ -6334,25 +6295,6 @@ const isoWeekNumberAdapter = {
|
|
|
6334
6295
|
return `${normalizedValue}`;
|
|
6335
6296
|
},
|
|
6336
6297
|
};
|
|
6337
|
-
/**
|
|
6338
|
-
* normalized month value is a string formatted as "MM/yyyy" (luxon format)
|
|
6339
|
-
* e.g. "01/2020" for January 2020
|
|
6340
|
-
*/
|
|
6341
|
-
const monthAdapter = {
|
|
6342
|
-
normalizeFunctionValue(value) {
|
|
6343
|
-
const date = toNumber(value, DEFAULT_LOCALE);
|
|
6344
|
-
return formatValue(date, { locale: DEFAULT_LOCALE, format: "mm/yyyy" });
|
|
6345
|
-
},
|
|
6346
|
-
toValueAndFormat(normalizedValue) {
|
|
6347
|
-
return {
|
|
6348
|
-
value: toNumber(normalizedValue, DEFAULT_LOCALE),
|
|
6349
|
-
format: "mmmm yyyy",
|
|
6350
|
-
};
|
|
6351
|
-
},
|
|
6352
|
-
toFunctionValue(normalizedValue) {
|
|
6353
|
-
return `"${normalizedValue}"`;
|
|
6354
|
-
},
|
|
6355
|
-
};
|
|
6356
6298
|
/**
|
|
6357
6299
|
* normalizes month number
|
|
6358
6300
|
*/
|
|
@@ -6374,25 +6316,6 @@ const monthNumberAdapter = {
|
|
|
6374
6316
|
return `${normalizedValue}`;
|
|
6375
6317
|
},
|
|
6376
6318
|
};
|
|
6377
|
-
/**
|
|
6378
|
-
* normalized quarter value is "quarter/year"
|
|
6379
|
-
* e.g. "1/2020" for Q1 2020
|
|
6380
|
-
*/
|
|
6381
|
-
const quarterAdapter = {
|
|
6382
|
-
normalizeFunctionValue(value) {
|
|
6383
|
-
const [quarter, year] = toString(value).split("/");
|
|
6384
|
-
return `${quarter}/${year}`;
|
|
6385
|
-
},
|
|
6386
|
-
toValueAndFormat(normalizedValue) {
|
|
6387
|
-
const [quarter, year] = normalizedValue.split("/");
|
|
6388
|
-
return {
|
|
6389
|
-
value: _t("Q%(quarter)s %(year)s", { quarter, year }),
|
|
6390
|
-
};
|
|
6391
|
-
},
|
|
6392
|
-
toFunctionValue(normalizedValue) {
|
|
6393
|
-
return `"${normalizedValue}"`;
|
|
6394
|
-
},
|
|
6395
|
-
};
|
|
6396
6319
|
/**
|
|
6397
6320
|
* normalizes quarter number
|
|
6398
6321
|
*/
|
|
@@ -6406,7 +6329,7 @@ const quarterNumberAdapter = {
|
|
|
6406
6329
|
},
|
|
6407
6330
|
toValueAndFormat(normalizedValue) {
|
|
6408
6331
|
return {
|
|
6409
|
-
value:
|
|
6332
|
+
value: _t("Q%(quarter_number)s", { quarter_number: normalizedValue }),
|
|
6410
6333
|
format: "0",
|
|
6411
6334
|
};
|
|
6412
6335
|
},
|
|
@@ -6456,15 +6379,11 @@ function nullHandlerDecorator(adapter) {
|
|
|
6456
6379
|
}
|
|
6457
6380
|
pivotTimeAdapterRegistry
|
|
6458
6381
|
.add("day", nullHandlerDecorator(dayAdapter))
|
|
6459
|
-
.add("week", nullHandlerDecorator(weekAdapter))
|
|
6460
|
-
.add("month", nullHandlerDecorator(monthAdapter))
|
|
6461
|
-
.add("quarter", nullHandlerDecorator(quarterAdapter))
|
|
6462
6382
|
.add("year", nullHandlerDecorator(yearAdapter))
|
|
6463
6383
|
.add("day_of_month", nullHandlerDecorator(dayOfMonthAdapter))
|
|
6464
6384
|
.add("iso_week_number", nullHandlerDecorator(isoWeekNumberAdapter))
|
|
6465
6385
|
.add("month_number", nullHandlerDecorator(monthNumberAdapter))
|
|
6466
|
-
.add("quarter_number", nullHandlerDecorator(quarterNumberAdapter))
|
|
6467
|
-
.add("year_number", nullHandlerDecorator(yearAdapter));
|
|
6386
|
+
.add("quarter_number", nullHandlerDecorator(quarterNumberAdapter));
|
|
6468
6387
|
|
|
6469
6388
|
const AGGREGATOR_NAMES = {
|
|
6470
6389
|
count: _t("Count"),
|
|
@@ -6540,11 +6459,10 @@ function getMaxObjectId(o) {
|
|
|
6540
6459
|
}
|
|
6541
6460
|
const ALL_PERIODS = {
|
|
6542
6461
|
year: _t("Year"),
|
|
6543
|
-
quarter: _t("Quarter"),
|
|
6544
|
-
month: _t("Month"),
|
|
6545
|
-
week: _t("Week"),
|
|
6462
|
+
quarter: _t("Quarter & Year"),
|
|
6463
|
+
month: _t("Month & Year"),
|
|
6464
|
+
week: _t("Week & Year"),
|
|
6546
6465
|
day: _t("Day"),
|
|
6547
|
-
year_number: _t("Year"),
|
|
6548
6466
|
quarter_number: _t("Quarter"),
|
|
6549
6467
|
month_number: _t("Month"),
|
|
6550
6468
|
iso_week_number: _t("Week"),
|
|
@@ -6695,7 +6613,7 @@ class CellClipboardHandler extends AbstractCellClipboardHandler {
|
|
|
6695
6613
|
const pivotId = this.getters.getPivotIdFromPosition(position);
|
|
6696
6614
|
const spreader = this.getters.getArrayFormulaSpreadingOn(position);
|
|
6697
6615
|
if (pivotId) {
|
|
6698
|
-
if (!deepEquals(spreader, position) || !isCopyingOneCell) {
|
|
6616
|
+
if (spreader && (!deepEquals(spreader, position) || !isCopyingOneCell)) {
|
|
6699
6617
|
const pivotCell = this.getters.getPivotCellFromPosition(position);
|
|
6700
6618
|
const formulaPivotId = this.getters.getPivotFormulaId(pivotId);
|
|
6701
6619
|
const pivotFormula = createPivotFormula(formulaPivotId, pivotCell);
|
|
@@ -7500,6 +7418,146 @@ function transformRangeData(range, executed) {
|
|
|
7500
7418
|
return undefined;
|
|
7501
7419
|
}
|
|
7502
7420
|
|
|
7421
|
+
var State;
|
|
7422
|
+
(function (State) {
|
|
7423
|
+
/**
|
|
7424
|
+
* Initial state.
|
|
7425
|
+
* Expecting any reference for the left part of a range
|
|
7426
|
+
* e.g. "A1", "1", "A", "Sheet1!A1", "Sheet1!A"
|
|
7427
|
+
*/
|
|
7428
|
+
State[State["LeftRef"] = 0] = "LeftRef";
|
|
7429
|
+
/**
|
|
7430
|
+
* Expecting any reference for the right part of a range
|
|
7431
|
+
* e.g. "A1", "1", "A", "Sheet1!A1", "Sheet1!A"
|
|
7432
|
+
*/
|
|
7433
|
+
State[State["RightRef"] = 1] = "RightRef";
|
|
7434
|
+
/**
|
|
7435
|
+
* Expecting the separator without any constraint on the right part
|
|
7436
|
+
*/
|
|
7437
|
+
State[State["Separator"] = 2] = "Separator";
|
|
7438
|
+
/**
|
|
7439
|
+
* Expecting the separator for a full column range
|
|
7440
|
+
*/
|
|
7441
|
+
State[State["FullColumnSeparator"] = 3] = "FullColumnSeparator";
|
|
7442
|
+
/**
|
|
7443
|
+
* Expecting the separator for a full row range
|
|
7444
|
+
*/
|
|
7445
|
+
State[State["FullRowSeparator"] = 4] = "FullRowSeparator";
|
|
7446
|
+
/**
|
|
7447
|
+
* Expecting the right part of a full column range
|
|
7448
|
+
* e.g. "1", "A1"
|
|
7449
|
+
*/
|
|
7450
|
+
State[State["RightColumnRef"] = 5] = "RightColumnRef";
|
|
7451
|
+
/**
|
|
7452
|
+
* Expecting the right part of a full row range
|
|
7453
|
+
* e.g. "A", "A1"
|
|
7454
|
+
*/
|
|
7455
|
+
State[State["RightRowRef"] = 6] = "RightRowRef";
|
|
7456
|
+
/**
|
|
7457
|
+
* Final state. A range has been matched
|
|
7458
|
+
*/
|
|
7459
|
+
State[State["Found"] = 7] = "Found";
|
|
7460
|
+
})(State || (State = {}));
|
|
7461
|
+
const goTo = (state, guard = () => true) => [
|
|
7462
|
+
{
|
|
7463
|
+
goTo: state,
|
|
7464
|
+
guard,
|
|
7465
|
+
},
|
|
7466
|
+
];
|
|
7467
|
+
const goToMulti = (state, guard = () => true) => ({
|
|
7468
|
+
goTo: state,
|
|
7469
|
+
guard,
|
|
7470
|
+
});
|
|
7471
|
+
const machine = {
|
|
7472
|
+
[State.LeftRef]: {
|
|
7473
|
+
REFERENCE: goTo(State.Separator),
|
|
7474
|
+
NUMBER: goTo(State.FullRowSeparator),
|
|
7475
|
+
SYMBOL: [
|
|
7476
|
+
goToMulti(State.FullColumnSeparator, (token) => isColReference(token.value)),
|
|
7477
|
+
goToMulti(State.FullRowSeparator, (token) => isRowReference(token.value)),
|
|
7478
|
+
],
|
|
7479
|
+
},
|
|
7480
|
+
[State.FullColumnSeparator]: {
|
|
7481
|
+
SPACE: goTo(State.FullColumnSeparator),
|
|
7482
|
+
OPERATOR: goTo(State.RightColumnRef, (token) => token.value === ":"),
|
|
7483
|
+
},
|
|
7484
|
+
[State.FullRowSeparator]: {
|
|
7485
|
+
SPACE: goTo(State.FullRowSeparator),
|
|
7486
|
+
OPERATOR: goTo(State.RightRowRef, (token) => token.value === ":"),
|
|
7487
|
+
},
|
|
7488
|
+
[State.Separator]: {
|
|
7489
|
+
SPACE: goTo(State.Separator),
|
|
7490
|
+
OPERATOR: goTo(State.RightRef, (token) => token.value === ":"),
|
|
7491
|
+
},
|
|
7492
|
+
[State.RightRef]: {
|
|
7493
|
+
SPACE: goTo(State.RightRef),
|
|
7494
|
+
NUMBER: goTo(State.Found),
|
|
7495
|
+
REFERENCE: goTo(State.Found, (token) => isSingleCellReference(token.value)),
|
|
7496
|
+
SYMBOL: goTo(State.Found, (token) => isColHeader(token.value) || isRowHeader(token.value)),
|
|
7497
|
+
},
|
|
7498
|
+
[State.RightColumnRef]: {
|
|
7499
|
+
SPACE: goTo(State.RightColumnRef),
|
|
7500
|
+
SYMBOL: goTo(State.Found, (token) => isColHeader(token.value)),
|
|
7501
|
+
REFERENCE: goTo(State.Found, (token) => isSingleCellReference(token.value)),
|
|
7502
|
+
},
|
|
7503
|
+
[State.RightRowRef]: {
|
|
7504
|
+
SPACE: goTo(State.RightRowRef),
|
|
7505
|
+
NUMBER: goTo(State.Found),
|
|
7506
|
+
REFERENCE: goTo(State.Found, (token) => isSingleCellReference(token.value)),
|
|
7507
|
+
SYMBOL: goTo(State.Found, (token) => isRowHeader(token.value)),
|
|
7508
|
+
},
|
|
7509
|
+
[State.Found]: {},
|
|
7510
|
+
};
|
|
7511
|
+
/**
|
|
7512
|
+
* Check if the list of tokens starts with a sequence of tokens representing
|
|
7513
|
+
* a range.
|
|
7514
|
+
* If a range is found, the sequence is removed from the list and is returned
|
|
7515
|
+
* as a single token.
|
|
7516
|
+
*/
|
|
7517
|
+
function matchReference(tokens) {
|
|
7518
|
+
let head = 0;
|
|
7519
|
+
let transitions = machine[State.LeftRef];
|
|
7520
|
+
let matchedTokens = "";
|
|
7521
|
+
while (transitions !== undefined) {
|
|
7522
|
+
const token = tokens[head++];
|
|
7523
|
+
if (!token) {
|
|
7524
|
+
return null;
|
|
7525
|
+
}
|
|
7526
|
+
const transition = transitions[token.type]?.find((transition) => transition.guard(token));
|
|
7527
|
+
const nextState = transition ? transition.goTo : undefined;
|
|
7528
|
+
switch (nextState) {
|
|
7529
|
+
case undefined:
|
|
7530
|
+
return null;
|
|
7531
|
+
case State.Found:
|
|
7532
|
+
matchedTokens += token.value;
|
|
7533
|
+
tokens.splice(0, head);
|
|
7534
|
+
return {
|
|
7535
|
+
type: "REFERENCE",
|
|
7536
|
+
value: matchedTokens,
|
|
7537
|
+
};
|
|
7538
|
+
default:
|
|
7539
|
+
transitions = machine[nextState];
|
|
7540
|
+
matchedTokens += token.value;
|
|
7541
|
+
break;
|
|
7542
|
+
}
|
|
7543
|
+
}
|
|
7544
|
+
return null;
|
|
7545
|
+
}
|
|
7546
|
+
/**
|
|
7547
|
+
* Take the result of the tokenizer and transform it to be usable in the
|
|
7548
|
+
* manipulations of range
|
|
7549
|
+
*
|
|
7550
|
+
* @param formula
|
|
7551
|
+
*/
|
|
7552
|
+
function rangeTokenize(formula, locale = DEFAULT_LOCALE) {
|
|
7553
|
+
const tokens = tokenize(formula, locale);
|
|
7554
|
+
const result = [];
|
|
7555
|
+
while (tokens.length) {
|
|
7556
|
+
result.push(matchReference(tokens) || tokens.shift());
|
|
7557
|
+
}
|
|
7558
|
+
return result;
|
|
7559
|
+
}
|
|
7560
|
+
|
|
7503
7561
|
const functionRegex = /[a-zA-Z0-9\_]+(\.[a-zA-Z0-9\_]+)*/;
|
|
7504
7562
|
const UNARY_OPERATORS_PREFIX = ["-", "+"];
|
|
7505
7563
|
const UNARY_OPERATORS_POSTFIX = ["%"];
|
|
@@ -7648,7 +7706,7 @@ function parseExpression(tokens, parent_priority = 0) {
|
|
|
7648
7706
|
* Parse an expression (as a string) into an AST.
|
|
7649
7707
|
*/
|
|
7650
7708
|
function parse(str) {
|
|
7651
|
-
return parseTokens(
|
|
7709
|
+
return parseTokens(rangeTokenize(str));
|
|
7652
7710
|
}
|
|
7653
7711
|
function parseTokens(tokens) {
|
|
7654
7712
|
tokens = tokens.filter((x) => x.type !== "SPACE");
|
|
@@ -7784,146 +7842,6 @@ function rightOperandToFormula(operationAST) {
|
|
|
7784
7842
|
return needParenthesis ? `(${astToFormula(rightOperation)})` : astToFormula(rightOperation);
|
|
7785
7843
|
}
|
|
7786
7844
|
|
|
7787
|
-
var State;
|
|
7788
|
-
(function (State) {
|
|
7789
|
-
/**
|
|
7790
|
-
* Initial state.
|
|
7791
|
-
* Expecting any reference for the left part of a range
|
|
7792
|
-
* e.g. "A1", "1", "A", "Sheet1!A1", "Sheet1!A"
|
|
7793
|
-
*/
|
|
7794
|
-
State[State["LeftRef"] = 0] = "LeftRef";
|
|
7795
|
-
/**
|
|
7796
|
-
* Expecting any reference for the right part of a range
|
|
7797
|
-
* e.g. "A1", "1", "A", "Sheet1!A1", "Sheet1!A"
|
|
7798
|
-
*/
|
|
7799
|
-
State[State["RightRef"] = 1] = "RightRef";
|
|
7800
|
-
/**
|
|
7801
|
-
* Expecting the separator without any constraint on the right part
|
|
7802
|
-
*/
|
|
7803
|
-
State[State["Separator"] = 2] = "Separator";
|
|
7804
|
-
/**
|
|
7805
|
-
* Expecting the separator for a full column range
|
|
7806
|
-
*/
|
|
7807
|
-
State[State["FullColumnSeparator"] = 3] = "FullColumnSeparator";
|
|
7808
|
-
/**
|
|
7809
|
-
* Expecting the separator for a full row range
|
|
7810
|
-
*/
|
|
7811
|
-
State[State["FullRowSeparator"] = 4] = "FullRowSeparator";
|
|
7812
|
-
/**
|
|
7813
|
-
* Expecting the right part of a full column range
|
|
7814
|
-
* e.g. "1", "A1"
|
|
7815
|
-
*/
|
|
7816
|
-
State[State["RightColumnRef"] = 5] = "RightColumnRef";
|
|
7817
|
-
/**
|
|
7818
|
-
* Expecting the right part of a full row range
|
|
7819
|
-
* e.g. "A", "A1"
|
|
7820
|
-
*/
|
|
7821
|
-
State[State["RightRowRef"] = 6] = "RightRowRef";
|
|
7822
|
-
/**
|
|
7823
|
-
* Final state. A range has been matched
|
|
7824
|
-
*/
|
|
7825
|
-
State[State["Found"] = 7] = "Found";
|
|
7826
|
-
})(State || (State = {}));
|
|
7827
|
-
const goTo = (state, guard = () => true) => [
|
|
7828
|
-
{
|
|
7829
|
-
goTo: state,
|
|
7830
|
-
guard,
|
|
7831
|
-
},
|
|
7832
|
-
];
|
|
7833
|
-
const goToMulti = (state, guard = () => true) => ({
|
|
7834
|
-
goTo: state,
|
|
7835
|
-
guard,
|
|
7836
|
-
});
|
|
7837
|
-
const machine = {
|
|
7838
|
-
[State.LeftRef]: {
|
|
7839
|
-
REFERENCE: goTo(State.Separator),
|
|
7840
|
-
NUMBER: goTo(State.FullRowSeparator),
|
|
7841
|
-
SYMBOL: [
|
|
7842
|
-
goToMulti(State.FullColumnSeparator, (token) => isColReference(token.value)),
|
|
7843
|
-
goToMulti(State.FullRowSeparator, (token) => isRowReference(token.value)),
|
|
7844
|
-
],
|
|
7845
|
-
},
|
|
7846
|
-
[State.FullColumnSeparator]: {
|
|
7847
|
-
SPACE: goTo(State.FullColumnSeparator),
|
|
7848
|
-
OPERATOR: goTo(State.RightColumnRef, (token) => token.value === ":"),
|
|
7849
|
-
},
|
|
7850
|
-
[State.FullRowSeparator]: {
|
|
7851
|
-
SPACE: goTo(State.FullRowSeparator),
|
|
7852
|
-
OPERATOR: goTo(State.RightRowRef, (token) => token.value === ":"),
|
|
7853
|
-
},
|
|
7854
|
-
[State.Separator]: {
|
|
7855
|
-
SPACE: goTo(State.Separator),
|
|
7856
|
-
OPERATOR: goTo(State.RightRef, (token) => token.value === ":"),
|
|
7857
|
-
},
|
|
7858
|
-
[State.RightRef]: {
|
|
7859
|
-
SPACE: goTo(State.RightRef),
|
|
7860
|
-
NUMBER: goTo(State.Found),
|
|
7861
|
-
REFERENCE: goTo(State.Found, (token) => isSingleCellReference(token.value)),
|
|
7862
|
-
SYMBOL: goTo(State.Found, (token) => isColHeader(token.value) || isRowHeader(token.value)),
|
|
7863
|
-
},
|
|
7864
|
-
[State.RightColumnRef]: {
|
|
7865
|
-
SPACE: goTo(State.RightColumnRef),
|
|
7866
|
-
SYMBOL: goTo(State.Found, (token) => isColHeader(token.value)),
|
|
7867
|
-
REFERENCE: goTo(State.Found, (token) => isSingleCellReference(token.value)),
|
|
7868
|
-
},
|
|
7869
|
-
[State.RightRowRef]: {
|
|
7870
|
-
SPACE: goTo(State.RightRowRef),
|
|
7871
|
-
NUMBER: goTo(State.Found),
|
|
7872
|
-
REFERENCE: goTo(State.Found, (token) => isSingleCellReference(token.value)),
|
|
7873
|
-
SYMBOL: goTo(State.Found, (token) => isRowHeader(token.value)),
|
|
7874
|
-
},
|
|
7875
|
-
[State.Found]: {},
|
|
7876
|
-
};
|
|
7877
|
-
/**
|
|
7878
|
-
* Check if the list of tokens starts with a sequence of tokens representing
|
|
7879
|
-
* a range.
|
|
7880
|
-
* If a range is found, the sequence is removed from the list and is returned
|
|
7881
|
-
* as a single token.
|
|
7882
|
-
*/
|
|
7883
|
-
function matchReference(tokens) {
|
|
7884
|
-
let head = 0;
|
|
7885
|
-
let transitions = machine[State.LeftRef];
|
|
7886
|
-
let matchedTokens = "";
|
|
7887
|
-
while (transitions !== undefined) {
|
|
7888
|
-
const token = tokens[head++];
|
|
7889
|
-
if (!token) {
|
|
7890
|
-
return null;
|
|
7891
|
-
}
|
|
7892
|
-
const transition = transitions[token.type]?.find((transition) => transition.guard(token));
|
|
7893
|
-
const nextState = transition ? transition.goTo : undefined;
|
|
7894
|
-
switch (nextState) {
|
|
7895
|
-
case undefined:
|
|
7896
|
-
return null;
|
|
7897
|
-
case State.Found:
|
|
7898
|
-
matchedTokens += token.value;
|
|
7899
|
-
tokens.splice(0, head);
|
|
7900
|
-
return {
|
|
7901
|
-
type: "REFERENCE",
|
|
7902
|
-
value: matchedTokens,
|
|
7903
|
-
};
|
|
7904
|
-
default:
|
|
7905
|
-
transitions = machine[nextState];
|
|
7906
|
-
matchedTokens += token.value;
|
|
7907
|
-
break;
|
|
7908
|
-
}
|
|
7909
|
-
}
|
|
7910
|
-
return null;
|
|
7911
|
-
}
|
|
7912
|
-
/**
|
|
7913
|
-
* Take the result of the tokenizer and transform it to be usable in the
|
|
7914
|
-
* manipulations of range
|
|
7915
|
-
*
|
|
7916
|
-
* @param formula
|
|
7917
|
-
*/
|
|
7918
|
-
function rangeTokenize(formula, locale = DEFAULT_LOCALE) {
|
|
7919
|
-
const tokens = tokenize(formula, locale);
|
|
7920
|
-
const result = [];
|
|
7921
|
-
while (tokens.length) {
|
|
7922
|
-
result.push(matchReference(tokens) || tokens.shift());
|
|
7923
|
-
}
|
|
7924
|
-
return result;
|
|
7925
|
-
}
|
|
7926
|
-
|
|
7927
7845
|
/**
|
|
7928
7846
|
* Add the following information on tokens:
|
|
7929
7847
|
* - length
|
|
@@ -8154,8 +8072,8 @@ function detectLink(value) {
|
|
|
8154
8072
|
|
|
8155
8073
|
function evaluateLiteral(literalCell, localeFormat) {
|
|
8156
8074
|
const value = localeFormat.format === PLAIN_TEXT_FORMAT ? literalCell.content : literalCell.parsedValue;
|
|
8157
|
-
const
|
|
8158
|
-
return createEvaluatedCell(
|
|
8075
|
+
const functionResult = { value, format: localeFormat.format };
|
|
8076
|
+
return createEvaluatedCell(functionResult, localeFormat.locale);
|
|
8159
8077
|
}
|
|
8160
8078
|
function parseLiteral(content, locale) {
|
|
8161
8079
|
if (content.startsWith("=")) {
|
|
@@ -8176,13 +8094,13 @@ function parseLiteral(content, locale) {
|
|
|
8176
8094
|
}
|
|
8177
8095
|
return content;
|
|
8178
8096
|
}
|
|
8179
|
-
function createEvaluatedCell(
|
|
8180
|
-
const link = detectLink(
|
|
8097
|
+
function createEvaluatedCell(functionResult, locale = DEFAULT_LOCALE, cell) {
|
|
8098
|
+
const link = detectLink(functionResult.value);
|
|
8181
8099
|
if (!link) {
|
|
8182
|
-
return _createEvaluatedCell(
|
|
8100
|
+
return _createEvaluatedCell(functionResult, locale, cell);
|
|
8183
8101
|
}
|
|
8184
8102
|
const value = parseLiteral(link.label, locale);
|
|
8185
|
-
const format =
|
|
8103
|
+
const format = functionResult.format ||
|
|
8186
8104
|
(typeof value === "number"
|
|
8187
8105
|
? detectDateFormat(link.label, locale) || detectNumberFormat(link.label)
|
|
8188
8106
|
: undefined);
|
|
@@ -8195,8 +8113,8 @@ function createEvaluatedCell(fPayload, locale = DEFAULT_LOCALE, cell) {
|
|
|
8195
8113
|
link,
|
|
8196
8114
|
};
|
|
8197
8115
|
}
|
|
8198
|
-
function _createEvaluatedCell(
|
|
8199
|
-
let { value, format, message } =
|
|
8116
|
+
function _createEvaluatedCell(functionResult, locale, cell) {
|
|
8117
|
+
let { value, format, message } = functionResult;
|
|
8200
8118
|
format = cell?.format || format;
|
|
8201
8119
|
const formattedValue = formatValue(value, { format, locale });
|
|
8202
8120
|
if (isEvaluationError(value)) {
|
|
@@ -8484,9 +8402,11 @@ const ChartTerms = {
|
|
|
8484
8402
|
BackgroundColor: _t("Background color"),
|
|
8485
8403
|
StackedBarChart: _t("Stacked bar chart"),
|
|
8486
8404
|
StackedLineChart: _t("Stacked line chart"),
|
|
8405
|
+
StackedAreaChart: _t("Stacked area chart"),
|
|
8487
8406
|
CumulativeData: _t("Cumulative data"),
|
|
8488
8407
|
TreatLabelsAsText: _t("Treat labels as text"),
|
|
8489
8408
|
AggregatedChart: _t("Aggregate"),
|
|
8409
|
+
ShowValues: _t("Show values"),
|
|
8490
8410
|
Errors: {
|
|
8491
8411
|
Unexpected: _t("The chart definition is invalid for an unknown reason"),
|
|
8492
8412
|
// BASIC CHART ERRORS (LINE | BAR | PIE)
|
|
@@ -10328,162 +10248,6 @@ class ComposerFocusStore extends SpreadsheetStore {
|
|
|
10328
10248
|
}
|
|
10329
10249
|
}
|
|
10330
10250
|
|
|
10331
|
-
/** This is a chartJS plugin that will draw connector lines between the bars of a Waterfall chart */
|
|
10332
|
-
const waterfallLinesPlugin = {
|
|
10333
|
-
id: "waterfallLinesPlugin",
|
|
10334
|
-
beforeDraw(chart, args, options) {
|
|
10335
|
-
if (!options.showConnectorLines) {
|
|
10336
|
-
return;
|
|
10337
|
-
}
|
|
10338
|
-
// Note: private properties are not in the typing of chartJS (and some of the existing types are missing properties)
|
|
10339
|
-
// so we don't type anything in this file
|
|
10340
|
-
const drawData = chart._metasets?.[0]?.data;
|
|
10341
|
-
if (!drawData) {
|
|
10342
|
-
return;
|
|
10343
|
-
}
|
|
10344
|
-
const ctx = chart.ctx;
|
|
10345
|
-
ctx.save();
|
|
10346
|
-
ctx.setLineDash([3, 2]);
|
|
10347
|
-
for (let i = 0; i < drawData.length; i++) {
|
|
10348
|
-
const bar = drawData[i];
|
|
10349
|
-
if (bar.height === 0) {
|
|
10350
|
-
continue;
|
|
10351
|
-
}
|
|
10352
|
-
const nextBar = getNextNonEmptyBar(drawData, i);
|
|
10353
|
-
if (!nextBar) {
|
|
10354
|
-
break;
|
|
10355
|
-
}
|
|
10356
|
-
const rect = getBarElementRect(bar);
|
|
10357
|
-
const nextBarRect = getBarElementRect(nextBar);
|
|
10358
|
-
const rawBarValues = bar.$context.raw;
|
|
10359
|
-
const value = rawBarValues[1] - rawBarValues[0];
|
|
10360
|
-
const lineY = Math.round(value < 0 ? rect.bottom - 1 : rect.top);
|
|
10361
|
-
const lineStart = Math.round(rect.right);
|
|
10362
|
-
const lineEnd = Math.round(nextBarRect.left);
|
|
10363
|
-
ctx.strokeStyle = "#999";
|
|
10364
|
-
ctx.beginPath();
|
|
10365
|
-
ctx.moveTo(lineStart + 1, lineY + 0.5);
|
|
10366
|
-
ctx.lineTo(lineEnd, lineY + 0.5);
|
|
10367
|
-
ctx.stroke();
|
|
10368
|
-
}
|
|
10369
|
-
ctx.restore();
|
|
10370
|
-
},
|
|
10371
|
-
};
|
|
10372
|
-
function getBarElementRect(bar) {
|
|
10373
|
-
const flipped = bar.base < bar.y; // Bar are flipped for negative values in the dataset
|
|
10374
|
-
return {
|
|
10375
|
-
left: bar.x - bar.width / 2,
|
|
10376
|
-
right: bar.x + bar.width / 2,
|
|
10377
|
-
bottom: flipped ? bar.base + bar.height : bar.y + bar.height,
|
|
10378
|
-
top: flipped ? bar.base : bar.y,
|
|
10379
|
-
};
|
|
10380
|
-
}
|
|
10381
|
-
function getNextNonEmptyBar(bars, startIndex) {
|
|
10382
|
-
return bars.find((bar, i) => i > startIndex && bar.height !== 0);
|
|
10383
|
-
}
|
|
10384
|
-
|
|
10385
|
-
// @ts-ignore
|
|
10386
|
-
window.Chart?.register(waterfallLinesPlugin);
|
|
10387
|
-
class ChartJsComponent extends owl.Component {
|
|
10388
|
-
static template = "o-spreadsheet-ChartJsComponent";
|
|
10389
|
-
static props = {
|
|
10390
|
-
figure: Object,
|
|
10391
|
-
};
|
|
10392
|
-
canvas = owl.useRef("graphContainer");
|
|
10393
|
-
chart;
|
|
10394
|
-
currentRuntime;
|
|
10395
|
-
get background() {
|
|
10396
|
-
return this.chartRuntime.background;
|
|
10397
|
-
}
|
|
10398
|
-
get canvasStyle() {
|
|
10399
|
-
return `background-color: ${this.background}`;
|
|
10400
|
-
}
|
|
10401
|
-
get chartRuntime() {
|
|
10402
|
-
const runtime = this.env.model.getters.getChartRuntime(this.props.figure.id);
|
|
10403
|
-
if (!("chartJsConfig" in runtime)) {
|
|
10404
|
-
throw new Error("Unsupported chart runtime");
|
|
10405
|
-
}
|
|
10406
|
-
return runtime;
|
|
10407
|
-
}
|
|
10408
|
-
setup() {
|
|
10409
|
-
owl.onMounted(() => {
|
|
10410
|
-
const runtime = this.chartRuntime;
|
|
10411
|
-
this.currentRuntime = runtime;
|
|
10412
|
-
// Note: chartJS modify the runtime in place, so it's important to give it a copy
|
|
10413
|
-
this.createChart(deepCopy(runtime.chartJsConfig));
|
|
10414
|
-
});
|
|
10415
|
-
owl.onWillUnmount(() => this.chart?.destroy());
|
|
10416
|
-
owl.useEffect(() => {
|
|
10417
|
-
const runtime = this.chartRuntime;
|
|
10418
|
-
if (!deepEquals(runtime, this.currentRuntime, "ignoreFunctions")) {
|
|
10419
|
-
if (runtime.chartJsConfig.type !== this.currentRuntime.chartJsConfig.type) {
|
|
10420
|
-
this.chart?.destroy();
|
|
10421
|
-
this.createChart(deepCopy(runtime.chartJsConfig));
|
|
10422
|
-
}
|
|
10423
|
-
else {
|
|
10424
|
-
this.updateChartJs(deepCopy(runtime));
|
|
10425
|
-
}
|
|
10426
|
-
this.currentRuntime = runtime;
|
|
10427
|
-
}
|
|
10428
|
-
});
|
|
10429
|
-
}
|
|
10430
|
-
createChart(chartData) {
|
|
10431
|
-
const canvas = this.canvas.el;
|
|
10432
|
-
const ctx = canvas.getContext("2d");
|
|
10433
|
-
// @ts-ignore
|
|
10434
|
-
this.chart = new window.Chart(ctx, chartData);
|
|
10435
|
-
}
|
|
10436
|
-
updateChartJs(chartRuntime) {
|
|
10437
|
-
const chartData = chartRuntime.chartJsConfig;
|
|
10438
|
-
if (chartData.data && chartData.data.datasets) {
|
|
10439
|
-
this.chart.data = chartData.data;
|
|
10440
|
-
if (chartData.options?.plugins?.title) {
|
|
10441
|
-
this.chart.config.options.plugins.title = chartData.options.plugins.title;
|
|
10442
|
-
}
|
|
10443
|
-
}
|
|
10444
|
-
else {
|
|
10445
|
-
this.chart.data.datasets = [];
|
|
10446
|
-
}
|
|
10447
|
-
this.chart.config.options = chartData.options;
|
|
10448
|
-
this.chart.update();
|
|
10449
|
-
}
|
|
10450
|
-
}
|
|
10451
|
-
|
|
10452
|
-
/**
|
|
10453
|
-
* AbstractChart is the class from which every Chart should inherit.
|
|
10454
|
-
* The role of this class is to maintain the state of each chart.
|
|
10455
|
-
*/
|
|
10456
|
-
class AbstractChart {
|
|
10457
|
-
sheetId;
|
|
10458
|
-
title;
|
|
10459
|
-
getters;
|
|
10460
|
-
constructor(definition, sheetId, getters) {
|
|
10461
|
-
this.title = definition.title;
|
|
10462
|
-
this.sheetId = sheetId;
|
|
10463
|
-
this.getters = getters;
|
|
10464
|
-
}
|
|
10465
|
-
/**
|
|
10466
|
-
* Validate the chart definition given as arguments. This function will be
|
|
10467
|
-
* called from allowDispatch function
|
|
10468
|
-
*/
|
|
10469
|
-
static validateChartDefinition(validator, definition) {
|
|
10470
|
-
throw new Error("This method should be implemented by sub class");
|
|
10471
|
-
}
|
|
10472
|
-
/**
|
|
10473
|
-
* Get a new chart definition transformed with the executed command. This
|
|
10474
|
-
* functions will be called during operational transform process
|
|
10475
|
-
*/
|
|
10476
|
-
static transformDefinition(definition, executed) {
|
|
10477
|
-
throw new Error("This method should be implemented by sub class");
|
|
10478
|
-
}
|
|
10479
|
-
/**
|
|
10480
|
-
* Get an empty definition based on the given context
|
|
10481
|
-
*/
|
|
10482
|
-
static getDefinitionFromContextCreation(context) {
|
|
10483
|
-
throw new Error("This method should be implemented by sub class");
|
|
10484
|
-
}
|
|
10485
|
-
}
|
|
10486
|
-
|
|
10487
10251
|
/**
|
|
10488
10252
|
* This file contains helpers that are common to different charts (mainly
|
|
10489
10253
|
* line, bar and pie charts)
|
|
@@ -10806,6 +10570,238 @@ function getDefinedAxis(definition) {
|
|
|
10806
10570
|
return { useLeftAxis, useRightAxis };
|
|
10807
10571
|
}
|
|
10808
10572
|
|
|
10573
|
+
/** This is a chartJS plugin that will draw the values of each data next to the point/bar/pie slice */
|
|
10574
|
+
const chartShowValuesPlugin = {
|
|
10575
|
+
id: "chartShowValuesPlugin",
|
|
10576
|
+
afterDatasetsDraw(chart, args, options) {
|
|
10577
|
+
if (!options.showValues) {
|
|
10578
|
+
return;
|
|
10579
|
+
}
|
|
10580
|
+
const drawData = chart._metasets?.[0]?.data;
|
|
10581
|
+
if (!drawData) {
|
|
10582
|
+
return;
|
|
10583
|
+
}
|
|
10584
|
+
const ctx = chart.ctx;
|
|
10585
|
+
ctx.save();
|
|
10586
|
+
ctx.textAlign = "center";
|
|
10587
|
+
ctx.textBaseline = "middle";
|
|
10588
|
+
ctx.fillStyle = chartFontColor(options.background);
|
|
10589
|
+
ctx.strokeStyle = chartFontColor(ctx.fillStyle);
|
|
10590
|
+
chart._metasets.forEach(function (dataset) {
|
|
10591
|
+
switch (dataset.type) {
|
|
10592
|
+
case "doughnut":
|
|
10593
|
+
case "pie": {
|
|
10594
|
+
for (let i = 0; i < dataset._parsed.length; i++) {
|
|
10595
|
+
const bar = dataset.data[i];
|
|
10596
|
+
const { startAngle, endAngle, innerRadius, outerRadius } = bar;
|
|
10597
|
+
const midAngle = (startAngle + endAngle) / 2;
|
|
10598
|
+
const midRadius = (innerRadius + outerRadius) / 2;
|
|
10599
|
+
const x = bar.x + midRadius * Math.cos(midAngle);
|
|
10600
|
+
const y = bar.y + midRadius * Math.sin(midAngle) + 7;
|
|
10601
|
+
ctx.fillStyle = chartFontColor(bar.options.backgroundColor);
|
|
10602
|
+
ctx.strokeStyle = chartFontColor(ctx.fillStyle);
|
|
10603
|
+
const value = options.callback(dataset._parsed[i]);
|
|
10604
|
+
ctx.strokeText(value, x, y);
|
|
10605
|
+
ctx.fillText(value, x, y);
|
|
10606
|
+
}
|
|
10607
|
+
break;
|
|
10608
|
+
}
|
|
10609
|
+
case "bar":
|
|
10610
|
+
case "line": {
|
|
10611
|
+
const yOffset = dataset.type === "bar" && !options.horizontal ? 0 : 3;
|
|
10612
|
+
for (let i = 0; i < dataset._parsed.length; i++) {
|
|
10613
|
+
const point = dataset.data[i];
|
|
10614
|
+
const value = options.horizontal ? dataset._parsed[i].x : dataset._parsed[i].y;
|
|
10615
|
+
const displayedValue = options.callback(value - 0);
|
|
10616
|
+
let xPosition = 0, yPosition = 0;
|
|
10617
|
+
if (options.horizontal) {
|
|
10618
|
+
yPosition = point.y;
|
|
10619
|
+
if (value < 0) {
|
|
10620
|
+
ctx.textAlign = "right";
|
|
10621
|
+
xPosition = point.x - yOffset;
|
|
10622
|
+
}
|
|
10623
|
+
else {
|
|
10624
|
+
ctx.textAlign = "left";
|
|
10625
|
+
xPosition = point.x + yOffset;
|
|
10626
|
+
}
|
|
10627
|
+
}
|
|
10628
|
+
else {
|
|
10629
|
+
xPosition = point.x;
|
|
10630
|
+
if (value < 0) {
|
|
10631
|
+
ctx.textBaseline = "top";
|
|
10632
|
+
yPosition = point.y + yOffset;
|
|
10633
|
+
}
|
|
10634
|
+
else {
|
|
10635
|
+
ctx.textBaseline = "bottom";
|
|
10636
|
+
yPosition = point.y - yOffset;
|
|
10637
|
+
}
|
|
10638
|
+
}
|
|
10639
|
+
ctx.strokeText(displayedValue, xPosition, yPosition);
|
|
10640
|
+
ctx.fillText(displayedValue, xPosition, yPosition);
|
|
10641
|
+
}
|
|
10642
|
+
break;
|
|
10643
|
+
}
|
|
10644
|
+
}
|
|
10645
|
+
});
|
|
10646
|
+
ctx.restore();
|
|
10647
|
+
},
|
|
10648
|
+
};
|
|
10649
|
+
|
|
10650
|
+
/** This is a chartJS plugin that will draw connector lines between the bars of a Waterfall chart */
|
|
10651
|
+
const waterfallLinesPlugin = {
|
|
10652
|
+
id: "waterfallLinesPlugin",
|
|
10653
|
+
beforeDraw(chart, args, options) {
|
|
10654
|
+
if (!options.showConnectorLines) {
|
|
10655
|
+
return;
|
|
10656
|
+
}
|
|
10657
|
+
// Note: private properties are not in the typing of chartJS (and some of the existing types are missing properties)
|
|
10658
|
+
// so we don't type anything in this file
|
|
10659
|
+
const drawData = chart._metasets?.[0]?.data;
|
|
10660
|
+
if (!drawData) {
|
|
10661
|
+
return;
|
|
10662
|
+
}
|
|
10663
|
+
const ctx = chart.ctx;
|
|
10664
|
+
ctx.save();
|
|
10665
|
+
ctx.setLineDash([3, 2]);
|
|
10666
|
+
for (let i = 0; i < drawData.length; i++) {
|
|
10667
|
+
const bar = drawData[i];
|
|
10668
|
+
if (bar.height === 0) {
|
|
10669
|
+
continue;
|
|
10670
|
+
}
|
|
10671
|
+
const nextBar = getNextNonEmptyBar(drawData, i);
|
|
10672
|
+
if (!nextBar) {
|
|
10673
|
+
break;
|
|
10674
|
+
}
|
|
10675
|
+
const rect = getBarElementRect(bar);
|
|
10676
|
+
const nextBarRect = getBarElementRect(nextBar);
|
|
10677
|
+
const rawBarValues = bar.$context.raw;
|
|
10678
|
+
const value = rawBarValues[1] - rawBarValues[0];
|
|
10679
|
+
const lineY = Math.round(value < 0 ? rect.bottom - 1 : rect.top);
|
|
10680
|
+
const lineStart = Math.round(rect.right);
|
|
10681
|
+
const lineEnd = Math.round(nextBarRect.left);
|
|
10682
|
+
ctx.strokeStyle = "#999";
|
|
10683
|
+
ctx.beginPath();
|
|
10684
|
+
ctx.moveTo(lineStart + 1, lineY + 0.5);
|
|
10685
|
+
ctx.lineTo(lineEnd, lineY + 0.5);
|
|
10686
|
+
ctx.stroke();
|
|
10687
|
+
}
|
|
10688
|
+
ctx.restore();
|
|
10689
|
+
},
|
|
10690
|
+
};
|
|
10691
|
+
function getBarElementRect(bar) {
|
|
10692
|
+
const flipped = bar.base < bar.y; // Bar are flipped for negative values in the dataset
|
|
10693
|
+
return {
|
|
10694
|
+
left: bar.x - bar.width / 2,
|
|
10695
|
+
right: bar.x + bar.width / 2,
|
|
10696
|
+
bottom: flipped ? bar.base + bar.height : bar.y + bar.height,
|
|
10697
|
+
top: flipped ? bar.base : bar.y,
|
|
10698
|
+
};
|
|
10699
|
+
}
|
|
10700
|
+
function getNextNonEmptyBar(bars, startIndex) {
|
|
10701
|
+
return bars.find((bar, i) => i > startIndex && bar.height !== 0);
|
|
10702
|
+
}
|
|
10703
|
+
|
|
10704
|
+
window.Chart?.register(waterfallLinesPlugin);
|
|
10705
|
+
window.Chart?.register(chartShowValuesPlugin);
|
|
10706
|
+
class ChartJsComponent extends owl.Component {
|
|
10707
|
+
static template = "o-spreadsheet-ChartJsComponent";
|
|
10708
|
+
static props = {
|
|
10709
|
+
figure: Object,
|
|
10710
|
+
};
|
|
10711
|
+
canvas = owl.useRef("graphContainer");
|
|
10712
|
+
chart;
|
|
10713
|
+
currentRuntime;
|
|
10714
|
+
get background() {
|
|
10715
|
+
return this.chartRuntime.background;
|
|
10716
|
+
}
|
|
10717
|
+
get canvasStyle() {
|
|
10718
|
+
return `background-color: ${this.background}`;
|
|
10719
|
+
}
|
|
10720
|
+
get chartRuntime() {
|
|
10721
|
+
const runtime = this.env.model.getters.getChartRuntime(this.props.figure.id);
|
|
10722
|
+
if (!("chartJsConfig" in runtime)) {
|
|
10723
|
+
throw new Error("Unsupported chart runtime");
|
|
10724
|
+
}
|
|
10725
|
+
return runtime;
|
|
10726
|
+
}
|
|
10727
|
+
setup() {
|
|
10728
|
+
owl.onMounted(() => {
|
|
10729
|
+
const runtime = this.chartRuntime;
|
|
10730
|
+
this.currentRuntime = runtime;
|
|
10731
|
+
// Note: chartJS modify the runtime in place, so it's important to give it a copy
|
|
10732
|
+
this.createChart(deepCopy(runtime.chartJsConfig));
|
|
10733
|
+
});
|
|
10734
|
+
owl.onWillUnmount(() => this.chart?.destroy());
|
|
10735
|
+
owl.useEffect(() => {
|
|
10736
|
+
const runtime = this.chartRuntime;
|
|
10737
|
+
if (runtime !== this.currentRuntime) {
|
|
10738
|
+
if (runtime.chartJsConfig.type !== this.currentRuntime.chartJsConfig.type) {
|
|
10739
|
+
this.chart?.destroy();
|
|
10740
|
+
this.createChart(deepCopy(runtime.chartJsConfig));
|
|
10741
|
+
}
|
|
10742
|
+
else {
|
|
10743
|
+
this.updateChartJs(deepCopy(runtime));
|
|
10744
|
+
}
|
|
10745
|
+
this.currentRuntime = runtime;
|
|
10746
|
+
}
|
|
10747
|
+
});
|
|
10748
|
+
}
|
|
10749
|
+
createChart(chartData) {
|
|
10750
|
+
const canvas = this.canvas.el;
|
|
10751
|
+
const ctx = canvas.getContext("2d");
|
|
10752
|
+
this.chart = new window.Chart(ctx, chartData);
|
|
10753
|
+
}
|
|
10754
|
+
updateChartJs(chartRuntime) {
|
|
10755
|
+
const chartData = chartRuntime.chartJsConfig;
|
|
10756
|
+
if (chartData.data && chartData.data.datasets) {
|
|
10757
|
+
this.chart.data = chartData.data;
|
|
10758
|
+
if (chartData.options?.plugins?.title) {
|
|
10759
|
+
this.chart.config.options.plugins.title = chartData.options.plugins.title;
|
|
10760
|
+
}
|
|
10761
|
+
}
|
|
10762
|
+
else {
|
|
10763
|
+
this.chart.data.datasets = [];
|
|
10764
|
+
}
|
|
10765
|
+
this.chart.config.options = chartData.options;
|
|
10766
|
+
this.chart.update();
|
|
10767
|
+
}
|
|
10768
|
+
}
|
|
10769
|
+
|
|
10770
|
+
/**
|
|
10771
|
+
* AbstractChart is the class from which every Chart should inherit.
|
|
10772
|
+
* The role of this class is to maintain the state of each chart.
|
|
10773
|
+
*/
|
|
10774
|
+
class AbstractChart {
|
|
10775
|
+
sheetId;
|
|
10776
|
+
title;
|
|
10777
|
+
getters;
|
|
10778
|
+
constructor(definition, sheetId, getters) {
|
|
10779
|
+
this.title = definition.title;
|
|
10780
|
+
this.sheetId = sheetId;
|
|
10781
|
+
this.getters = getters;
|
|
10782
|
+
}
|
|
10783
|
+
/**
|
|
10784
|
+
* Validate the chart definition given as arguments. This function will be
|
|
10785
|
+
* called from allowDispatch function
|
|
10786
|
+
*/
|
|
10787
|
+
static validateChartDefinition(validator, definition) {
|
|
10788
|
+
throw new Error("This method should be implemented by sub class");
|
|
10789
|
+
}
|
|
10790
|
+
/**
|
|
10791
|
+
* Get a new chart definition transformed with the executed command. This
|
|
10792
|
+
* functions will be called during operational transform process
|
|
10793
|
+
*/
|
|
10794
|
+
static transformDefinition(definition, executed) {
|
|
10795
|
+
throw new Error("This method should be implemented by sub class");
|
|
10796
|
+
}
|
|
10797
|
+
/**
|
|
10798
|
+
* Get an empty definition based on the given context
|
|
10799
|
+
*/
|
|
10800
|
+
static getDefinitionFromContextCreation(context) {
|
|
10801
|
+
throw new Error("This method should be implemented by sub class");
|
|
10802
|
+
}
|
|
10803
|
+
}
|
|
10804
|
+
|
|
10809
10805
|
function getBaselineText(baseline, keyValue, baselineMode, humanize, locale) {
|
|
10810
10806
|
if (!baseline) {
|
|
10811
10807
|
return "";
|
|
@@ -18844,7 +18840,7 @@ const INDEX = {
|
|
|
18844
18840
|
isExported: true,
|
|
18845
18841
|
};
|
|
18846
18842
|
// -----------------------------------------------------------------------------
|
|
18847
|
-
//
|
|
18843
|
+
// INDIRECT
|
|
18848
18844
|
// -----------------------------------------------------------------------------
|
|
18849
18845
|
const INDIRECT = {
|
|
18850
18846
|
description: _t("Returns the content of a cell, specified by a string."),
|
|
@@ -19182,7 +19178,7 @@ const PIVOT = {
|
|
|
19182
19178
|
arg("include_column_titles (boolean, default=TRUE)", _t("Whether to include the column titles or not.")),
|
|
19183
19179
|
arg("column_count (number, optional)", _t("number of columns")),
|
|
19184
19180
|
],
|
|
19185
|
-
compute: function (pivotFormulaId, rowCount = { value:
|
|
19181
|
+
compute: function (pivotFormulaId, rowCount = { value: 10000 }, includeTotal = { value: true }, includeColumnHeaders = { value: true }, columnCount = { value: Number.MAX_VALUE }) {
|
|
19186
19182
|
const _pivotFormulaId = toString(pivotFormulaId);
|
|
19187
19183
|
const _rowCount = toNumber(rowCount, this.locale);
|
|
19188
19184
|
if (_rowCount < 0) {
|
|
@@ -19239,6 +19235,79 @@ const PIVOT = {
|
|
|
19239
19235
|
return result;
|
|
19240
19236
|
},
|
|
19241
19237
|
};
|
|
19238
|
+
//--------------------------------------------------------------------------
|
|
19239
|
+
// OFFSET
|
|
19240
|
+
//--------------------------------------------------------------------------
|
|
19241
|
+
const OFFSET = {
|
|
19242
|
+
description: _t("Returns a range reference shifted by a specified number of rows and columns from a starting cell reference."),
|
|
19243
|
+
args: [
|
|
19244
|
+
arg("cell_reference (meta)", _t("The starting point from which to count the offset rows and columns.")),
|
|
19245
|
+
arg("offset_rows (number)", _t("The number of rows to offset by.")),
|
|
19246
|
+
arg("offset_columns (number)", _t("The number of columns to offset by.")),
|
|
19247
|
+
arg("height (number, default='height of cell_reference')", _t("The number of rows of the range to return starting at the offset target.")),
|
|
19248
|
+
arg("width (number, default='width of cell_reference')", _t("The number of columns of the range to return starting at the offset target.")),
|
|
19249
|
+
],
|
|
19250
|
+
compute: function (cellReference, offsetRows, offsetColumns, height, width) {
|
|
19251
|
+
if (isEvaluationError(cellReference?.value)) {
|
|
19252
|
+
return cellReference;
|
|
19253
|
+
}
|
|
19254
|
+
const _cellReference = cellReference?.value;
|
|
19255
|
+
if (!_cellReference) {
|
|
19256
|
+
throw new Error("In this context, the function OFFSET needs to have a cell or range in parameter.");
|
|
19257
|
+
}
|
|
19258
|
+
const zone = toZone(_cellReference);
|
|
19259
|
+
let offsetHeight = zone.bottom - zone.top + 1;
|
|
19260
|
+
let offsetWidth = zone.right - zone.left + 1;
|
|
19261
|
+
if (height) {
|
|
19262
|
+
const _height = toNumber(height, this.locale);
|
|
19263
|
+
assertPositive(_t("Height value is %(_height)s. It should be greater than or equal to 1.", { _height }), _height);
|
|
19264
|
+
offsetHeight = _height;
|
|
19265
|
+
}
|
|
19266
|
+
if (width) {
|
|
19267
|
+
const _width = toNumber(width, this.locale);
|
|
19268
|
+
assertPositive(_t("Width value is %(_width)s. It should be greater than or equal to 1.", { _width }), _width);
|
|
19269
|
+
offsetWidth = _width;
|
|
19270
|
+
}
|
|
19271
|
+
const { sheetName } = splitReference(_cellReference);
|
|
19272
|
+
const sheetId = (sheetName && this.getters.getSheetIdByName(sheetName)) || this.getters.getActiveSheetId();
|
|
19273
|
+
const _offsetRows = toNumber(offsetRows, this.locale);
|
|
19274
|
+
const _offsetColumns = toNumber(offsetColumns, this.locale);
|
|
19275
|
+
let originPosition;
|
|
19276
|
+
const __originCellXC = this.__originCellXC?.();
|
|
19277
|
+
if (__originCellXC) {
|
|
19278
|
+
const cellZone = toZone(__originCellXC);
|
|
19279
|
+
originPosition = {
|
|
19280
|
+
sheetId: this.__originSheetId,
|
|
19281
|
+
col: cellZone.left,
|
|
19282
|
+
row: cellZone.top,
|
|
19283
|
+
};
|
|
19284
|
+
this.updateDependencies?.(originPosition);
|
|
19285
|
+
}
|
|
19286
|
+
const startingCol = zone.left + _offsetColumns;
|
|
19287
|
+
const startingRow = zone.top + _offsetRows;
|
|
19288
|
+
if (startingCol < 0 || startingRow < 0) {
|
|
19289
|
+
return new InvalidReferenceError(_t("OFFSET evaluates to an out of bounds range."));
|
|
19290
|
+
}
|
|
19291
|
+
const dependencyZone = {
|
|
19292
|
+
left: startingCol,
|
|
19293
|
+
top: startingRow,
|
|
19294
|
+
right: startingCol + offsetWidth - 1,
|
|
19295
|
+
bottom: startingRow + offsetHeight - 1,
|
|
19296
|
+
};
|
|
19297
|
+
const range = this.getters.getRangeFromZone(this.__originSheetId, dependencyZone);
|
|
19298
|
+
if (range.invalidXc || range.invalidSheetName) {
|
|
19299
|
+
return new InvalidReferenceError();
|
|
19300
|
+
}
|
|
19301
|
+
if (originPosition) {
|
|
19302
|
+
this.addDependencies?.(originPosition, [range]);
|
|
19303
|
+
}
|
|
19304
|
+
return generateMatrix(offsetWidth, offsetHeight, (col, row) => this.getters.getEvaluatedCell({
|
|
19305
|
+
sheetId,
|
|
19306
|
+
col: startingCol + col,
|
|
19307
|
+
row: startingRow + row,
|
|
19308
|
+
}));
|
|
19309
|
+
},
|
|
19310
|
+
};
|
|
19242
19311
|
|
|
19243
19312
|
var lookup = /*#__PURE__*/Object.freeze({
|
|
19244
19313
|
__proto__: null,
|
|
@@ -19250,6 +19319,7 @@ var lookup = /*#__PURE__*/Object.freeze({
|
|
|
19250
19319
|
INDIRECT: INDIRECT,
|
|
19251
19320
|
LOOKUP: LOOKUP,
|
|
19252
19321
|
MATCH: MATCH,
|
|
19322
|
+
OFFSET: OFFSET,
|
|
19253
19323
|
PIVOT: PIVOT,
|
|
19254
19324
|
PIVOT_HEADER: PIVOT_HEADER,
|
|
19255
19325
|
PIVOT_VALUE: PIVOT_VALUE,
|
|
@@ -19532,6 +19602,320 @@ var operators = /*#__PURE__*/Object.freeze({
|
|
|
19532
19602
|
UPLUS: UPLUS
|
|
19533
19603
|
});
|
|
19534
19604
|
|
|
19605
|
+
const transformFromFactor = (factor) => ({
|
|
19606
|
+
transform: (x) => x * factor,
|
|
19607
|
+
inverseTransform: (x) => x / factor,
|
|
19608
|
+
});
|
|
19609
|
+
const standard = { transform: (x) => x, inverseTransform: (x) => x };
|
|
19610
|
+
const ANG2M = 1e-10;
|
|
19611
|
+
const IN2M = 0.0254;
|
|
19612
|
+
const PICAPT2M = IN2M / 72;
|
|
19613
|
+
const FT2M = 0.3048;
|
|
19614
|
+
const YD2M = 0.9144;
|
|
19615
|
+
const MI2M = 1609.34;
|
|
19616
|
+
const NMI2M = 1852;
|
|
19617
|
+
const LY2M = 9.46073047258e15;
|
|
19618
|
+
const UNITS = {
|
|
19619
|
+
// WEIGHT UNITs : Standard = gramme
|
|
19620
|
+
g: { ...standard, category: "weight" },
|
|
19621
|
+
u: { ...transformFromFactor(1.66053e-24), category: "weight" },
|
|
19622
|
+
grain: { ...transformFromFactor(0.0647989), category: "weight" },
|
|
19623
|
+
ozm: { ...transformFromFactor(28.3495), category: "weight" },
|
|
19624
|
+
lbm: { ...transformFromFactor(453.592), category: "weight" },
|
|
19625
|
+
stone: { ...transformFromFactor(6350.29), category: "weight" },
|
|
19626
|
+
sg: { ...transformFromFactor(14593.90294), category: "weight" },
|
|
19627
|
+
cwt: { ...transformFromFactor(45359.237), category: "weight" },
|
|
19628
|
+
uk_cwt: { ...transformFromFactor(50802.3), category: "weight" },
|
|
19629
|
+
ton: { ...transformFromFactor(907184.74), category: "weight" },
|
|
19630
|
+
uk_ton: { ...transformFromFactor(1016046.9), category: "weight" },
|
|
19631
|
+
// DISTANCE UNITS : Standard = meter
|
|
19632
|
+
m: { ...standard, category: "distance" },
|
|
19633
|
+
km: { ...transformFromFactor(1000), category: "distance" },
|
|
19634
|
+
ang: { ...transformFromFactor(ANG2M), category: "distance" },
|
|
19635
|
+
Picapt: { ...transformFromFactor(PICAPT2M), category: "distance" },
|
|
19636
|
+
pica: { ...transformFromFactor(IN2M / 6), category: "distance" },
|
|
19637
|
+
in: { ...transformFromFactor(IN2M), category: "distance" },
|
|
19638
|
+
ft: { ...transformFromFactor(FT2M), category: "distance" },
|
|
19639
|
+
yd: { ...transformFromFactor(YD2M), category: "distance" },
|
|
19640
|
+
ell: { ...transformFromFactor(1.143), category: "distance" },
|
|
19641
|
+
mi: { ...transformFromFactor(MI2M), category: "distance" },
|
|
19642
|
+
survey_mi: { ...transformFromFactor(1609.34), category: "distance" },
|
|
19643
|
+
Nmi: { ...transformFromFactor(NMI2M), category: "distance" },
|
|
19644
|
+
ly: { ...transformFromFactor(LY2M), category: "distance" },
|
|
19645
|
+
parsec: { ...transformFromFactor(3.0856775814914e16), category: "distance" },
|
|
19646
|
+
// TIME UNITS : Standard = second
|
|
19647
|
+
sec: { ...standard, category: "time" },
|
|
19648
|
+
min: { ...transformFromFactor(60), category: "time" },
|
|
19649
|
+
hr: { ...transformFromFactor(3600), category: "time" },
|
|
19650
|
+
day: { ...transformFromFactor(86400), category: "time" },
|
|
19651
|
+
yr: { ...transformFromFactor(31556952), category: "time" },
|
|
19652
|
+
// PRESSURE UNITS : Standard = Pascal
|
|
19653
|
+
Pa: { ...standard, category: "pressure" },
|
|
19654
|
+
bar: { ...transformFromFactor(100000), category: "pressure" },
|
|
19655
|
+
mmHg: { ...transformFromFactor(133.322), category: "pressure" },
|
|
19656
|
+
Torr: { ...transformFromFactor(133.322), category: "pressure" },
|
|
19657
|
+
psi: { ...transformFromFactor(6894.76), category: "pressure" },
|
|
19658
|
+
atm: { ...transformFromFactor(101325), category: "pressure" },
|
|
19659
|
+
// FORCE UNITS : Standard = Newton
|
|
19660
|
+
N: { ...standard, category: "force" },
|
|
19661
|
+
dyn: { ...transformFromFactor(1e-5), category: "force" },
|
|
19662
|
+
pond: { ...transformFromFactor(0.00980665), category: "force" },
|
|
19663
|
+
lbf: { ...transformFromFactor(4.44822), category: "force" },
|
|
19664
|
+
// ENERGY UNITS : Standard = Joule
|
|
19665
|
+
J: { ...standard, category: "energy" },
|
|
19666
|
+
eV: { ...transformFromFactor(1.60218e-19), category: "energy" },
|
|
19667
|
+
e: { ...transformFromFactor(1e-7), category: "energy" },
|
|
19668
|
+
flb: { ...transformFromFactor(1.3558179483), category: "energy" },
|
|
19669
|
+
c: { ...transformFromFactor(4.184), category: "energy" },
|
|
19670
|
+
cal: { ...transformFromFactor(4.1868), category: "energy" },
|
|
19671
|
+
BTU: { ...transformFromFactor(1055.06), category: "energy" },
|
|
19672
|
+
Wh: { ...transformFromFactor(3600), category: "energy" },
|
|
19673
|
+
HPh: { ...transformFromFactor(2684520), category: "energy" },
|
|
19674
|
+
// POWER UNITS : Standard = Watt
|
|
19675
|
+
W: { ...standard, category: "power" },
|
|
19676
|
+
PS: { ...transformFromFactor(735.499), category: "power" },
|
|
19677
|
+
HP: { ...transformFromFactor(745.7), category: "power" },
|
|
19678
|
+
// MAGNETISM UNITS : Standard = Tesla
|
|
19679
|
+
T: { ...standard, category: "magnetism" },
|
|
19680
|
+
ga: { ...transformFromFactor(1e-4), category: "magnetism" },
|
|
19681
|
+
// TEMPERATURE UNITS : Standard = Kelvin
|
|
19682
|
+
K: { ...standard, category: "temperature" },
|
|
19683
|
+
C: {
|
|
19684
|
+
transform: (T) => T + 273.15,
|
|
19685
|
+
inverseTransform: (T) => T - 273.15,
|
|
19686
|
+
category: "temperature",
|
|
19687
|
+
},
|
|
19688
|
+
F: {
|
|
19689
|
+
transform: (T) => ((T - 32) * 5) / 9 + 273.15,
|
|
19690
|
+
inverseTransform: (T) => ((T - 273.15) * 9) / 5 + 32,
|
|
19691
|
+
category: "temperature",
|
|
19692
|
+
},
|
|
19693
|
+
Rank: { ...transformFromFactor(5 / 9), category: "temperature" },
|
|
19694
|
+
Reau: {
|
|
19695
|
+
transform: (T) => T * 1.25 + 273.15,
|
|
19696
|
+
inverseTransform: (T) => (T - 273.15) / 1.25,
|
|
19697
|
+
category: "temperature",
|
|
19698
|
+
},
|
|
19699
|
+
// VOLUME UNITS : Standard = cubic meter
|
|
19700
|
+
"m^3": { ...standard, category: "volume", order: 3 },
|
|
19701
|
+
"ang^3": { ...transformFromFactor(Math.pow(ANG2M, 3)), category: "volume", order: 3 },
|
|
19702
|
+
"Picapt^3": { ...transformFromFactor(Math.pow(PICAPT2M, 3)), category: "volume", order: 3 },
|
|
19703
|
+
tsp: { ...transformFromFactor(4.92892e-6), category: "volume" },
|
|
19704
|
+
tspm: { ...transformFromFactor(5e-6), category: "volume" },
|
|
19705
|
+
tbs: { ...transformFromFactor(1.4786764825785619e-5), category: "volume" },
|
|
19706
|
+
"in^3": { ...transformFromFactor(Math.pow(IN2M, 3)), category: "volume", order: 3 },
|
|
19707
|
+
oz: { ...transformFromFactor(2.95735295625e-5), category: "volume" },
|
|
19708
|
+
cup: { ...transformFromFactor(0.000237), category: "volume" },
|
|
19709
|
+
pt: { ...transformFromFactor(0.0004731765), category: "volume" },
|
|
19710
|
+
uk_pt: { ...transformFromFactor(0.000568261), category: "volume" },
|
|
19711
|
+
qt: { ...transformFromFactor(0.0009463529), category: "volume" },
|
|
19712
|
+
l: { ...transformFromFactor(1e-3), category: "volume" },
|
|
19713
|
+
uk_qt: { ...transformFromFactor(0.0011365225), category: "volume" },
|
|
19714
|
+
gal: { ...transformFromFactor(0.0037854118), category: "volume" },
|
|
19715
|
+
uk_gal: { ...transformFromFactor(0.00454609), category: "volume" },
|
|
19716
|
+
"ft^3": { ...transformFromFactor(Math.pow(FT2M, 3)), category: "volume", order: 3 },
|
|
19717
|
+
bushel: { ...transformFromFactor(0.0352390704), category: "volume" },
|
|
19718
|
+
barrel: { ...transformFromFactor(0.158987295), category: "volume" },
|
|
19719
|
+
"yd^3": { ...transformFromFactor(Math.pow(YD2M, 3)), category: "volume", order: 3 },
|
|
19720
|
+
MTON: { ...transformFromFactor(1.13267386368), category: "volume" },
|
|
19721
|
+
GRT: { ...transformFromFactor(2.83168), category: "volume" },
|
|
19722
|
+
"mi^3": { ...transformFromFactor(Math.pow(MI2M, 3)), category: "volume", order: 3 },
|
|
19723
|
+
"Nmi^3": { ...transformFromFactor(Math.pow(NMI2M, 3)), category: "volume", order: 3 },
|
|
19724
|
+
"ly^3": { ...transformFromFactor(Math.pow(LY2M, 3)), category: "volume", order: 3 },
|
|
19725
|
+
// AREA UNITS : Standard = square meter
|
|
19726
|
+
"m^2": { ...standard, category: "area", order: 2 },
|
|
19727
|
+
"ang^2": { ...transformFromFactor(Math.pow(ANG2M, 2)), category: "area", order: 2 },
|
|
19728
|
+
"Picapt^2": { ...transformFromFactor(Math.pow(PICAPT2M, 2)), category: "area", order: 2 },
|
|
19729
|
+
"in^2": { ...transformFromFactor(Math.pow(IN2M, 2)), category: "area", order: 2 },
|
|
19730
|
+
"ft^2": { ...transformFromFactor(Math.pow(FT2M, 2)), category: "area", order: 2 },
|
|
19731
|
+
"yd^2": { ...transformFromFactor(Math.pow(YD2M, 2)), category: "area", order: 2 },
|
|
19732
|
+
ar: { ...transformFromFactor(100), category: "area" },
|
|
19733
|
+
Morgen: { ...transformFromFactor(2500), category: "area" },
|
|
19734
|
+
uk_acre: { ...transformFromFactor(4046.8564224), category: "area" },
|
|
19735
|
+
us_acre: { ...transformFromFactor(4046.8726098743), category: "area" },
|
|
19736
|
+
ha: { ...transformFromFactor(1e4), category: "area" },
|
|
19737
|
+
"mi^2": { ...transformFromFactor(Math.pow(MI2M, 2)), category: "area", order: 2 },
|
|
19738
|
+
"Nmi^2": { ...transformFromFactor(Math.pow(NMI2M, 2)), category: "area", order: 2 },
|
|
19739
|
+
"ly^2": { ...transformFromFactor(Math.pow(LY2M, 2)), category: "area", order: 2 },
|
|
19740
|
+
// INFORMATION UNITS : Standard = bit
|
|
19741
|
+
bit: { ...standard, category: "information" },
|
|
19742
|
+
byte: { ...transformFromFactor(8), category: "information" },
|
|
19743
|
+
// SPEED UNITS : Standard = m/s
|
|
19744
|
+
"m/s": { ...standard, category: "speed" },
|
|
19745
|
+
"m/hr": { ...transformFromFactor(1 / 3600), category: "speed" },
|
|
19746
|
+
"km/hr": { ...transformFromFactor(1 / 3.6), category: "speed" },
|
|
19747
|
+
mph: { ...transformFromFactor(0.44704), category: "speed" },
|
|
19748
|
+
kn: { ...transformFromFactor(0.5144444444), category: "speed" },
|
|
19749
|
+
admkn: { ...transformFromFactor(0.5147733333), category: "speed" },
|
|
19750
|
+
};
|
|
19751
|
+
const UNITS_ALIASES = {
|
|
19752
|
+
shweight: "cwt",
|
|
19753
|
+
lcwt: "uk_cwt",
|
|
19754
|
+
hweight: "uk_cwt",
|
|
19755
|
+
LTON: "uk_ton",
|
|
19756
|
+
brton: "uk_ton",
|
|
19757
|
+
pc: "parsec",
|
|
19758
|
+
Pica: "Picapt",
|
|
19759
|
+
d: "day",
|
|
19760
|
+
mn: "min",
|
|
19761
|
+
s: "sec",
|
|
19762
|
+
p: "Pa",
|
|
19763
|
+
at: "atm",
|
|
19764
|
+
dy: "dyn",
|
|
19765
|
+
ev: "eV",
|
|
19766
|
+
hh: "HPh",
|
|
19767
|
+
wh: "Wh",
|
|
19768
|
+
btu: "BTU",
|
|
19769
|
+
h: "HP",
|
|
19770
|
+
cel: "C",
|
|
19771
|
+
fah: "F",
|
|
19772
|
+
kel: "K",
|
|
19773
|
+
us_pt: "pt",
|
|
19774
|
+
L: "l",
|
|
19775
|
+
lt: "l",
|
|
19776
|
+
ang3: "ang^3",
|
|
19777
|
+
ft3: "ft^3",
|
|
19778
|
+
in3: "in^3",
|
|
19779
|
+
ly3: "ly^3",
|
|
19780
|
+
m3: "m^3",
|
|
19781
|
+
mi3: "mi^3",
|
|
19782
|
+
yd3: "yd^3",
|
|
19783
|
+
Nmi3: "Nmi^3",
|
|
19784
|
+
Picapt3: "Picapt^3",
|
|
19785
|
+
"Pica^3": "Picapt^3",
|
|
19786
|
+
Pica3: "Picapt^3",
|
|
19787
|
+
regton: "GRT",
|
|
19788
|
+
ang2: "ang^2",
|
|
19789
|
+
ft2: "ft^2",
|
|
19790
|
+
in2: "in^2",
|
|
19791
|
+
ly2: "ly^2",
|
|
19792
|
+
m2: "m^2",
|
|
19793
|
+
mi2: "mi^2",
|
|
19794
|
+
Nmi2: "Nmi^2",
|
|
19795
|
+
Picapt2: "Picapt^2",
|
|
19796
|
+
"Pica^2": "Picapt^2",
|
|
19797
|
+
Pica2: "Picapt^2",
|
|
19798
|
+
yd2: "yd^2",
|
|
19799
|
+
"m/h": "m/hr",
|
|
19800
|
+
"m/sec": "m/s",
|
|
19801
|
+
};
|
|
19802
|
+
const UNIT_PREFIXES = {
|
|
19803
|
+
"": 1,
|
|
19804
|
+
Y: 1e24,
|
|
19805
|
+
Z: 1e21,
|
|
19806
|
+
E: 1e18,
|
|
19807
|
+
P: 1e15,
|
|
19808
|
+
T: 1e12,
|
|
19809
|
+
G: 1e9,
|
|
19810
|
+
M: 1e6,
|
|
19811
|
+
k: 1e3,
|
|
19812
|
+
h: 1e2,
|
|
19813
|
+
da: 1e1,
|
|
19814
|
+
e: 1e1,
|
|
19815
|
+
d: 1e-1,
|
|
19816
|
+
c: 1e-2,
|
|
19817
|
+
m: 1e-3,
|
|
19818
|
+
u: 1e-6,
|
|
19819
|
+
n: 1e-9,
|
|
19820
|
+
p: 1e-12,
|
|
19821
|
+
f: 1e-15,
|
|
19822
|
+
a: 1e-18,
|
|
19823
|
+
z: 1e-21,
|
|
19824
|
+
y: 1e-21,
|
|
19825
|
+
Yi: Math.pow(2, 80),
|
|
19826
|
+
Zi: Math.pow(2, 70),
|
|
19827
|
+
Ei: Math.pow(2, 60),
|
|
19828
|
+
Pi: Math.pow(2, 50),
|
|
19829
|
+
Ti: Math.pow(2, 40),
|
|
19830
|
+
Gi: Math.pow(2, 30),
|
|
19831
|
+
Mi: Math.pow(2, 20),
|
|
19832
|
+
ki: Math.pow(2, 10),
|
|
19833
|
+
};
|
|
19834
|
+
const TRANSLATED_CATEGORIES = {
|
|
19835
|
+
weight: _t("Weight"),
|
|
19836
|
+
distance: _t("Distance"),
|
|
19837
|
+
time: _t("Time"),
|
|
19838
|
+
pressure: _t("Pressure"),
|
|
19839
|
+
force: _t("Force"),
|
|
19840
|
+
energy: _t("Energy"),
|
|
19841
|
+
power: _t("Power"),
|
|
19842
|
+
magnetism: _t("Magnetism"),
|
|
19843
|
+
temperature: _t("Temperature"),
|
|
19844
|
+
volume: _t("Volume"),
|
|
19845
|
+
area: _t("Area"),
|
|
19846
|
+
information: _t("Information"),
|
|
19847
|
+
speed: _t("Speed"),
|
|
19848
|
+
};
|
|
19849
|
+
function getTranslatedCategory(key) {
|
|
19850
|
+
return TRANSLATED_CATEGORIES[key] ?? "";
|
|
19851
|
+
}
|
|
19852
|
+
function getTransformation(key) {
|
|
19853
|
+
for (const [prefix, value] of Object.entries(UNIT_PREFIXES)) {
|
|
19854
|
+
if (prefix && !key.startsWith(prefix))
|
|
19855
|
+
continue;
|
|
19856
|
+
const _key = key.slice(prefix.length);
|
|
19857
|
+
let conversion = UNITS[_key];
|
|
19858
|
+
if (!conversion && UNITS_ALIASES[_key]) {
|
|
19859
|
+
conversion = UNITS[UNITS_ALIASES[_key]];
|
|
19860
|
+
}
|
|
19861
|
+
if (conversion) {
|
|
19862
|
+
return {
|
|
19863
|
+
...conversion,
|
|
19864
|
+
factor: conversion.order ? Math.pow(value, conversion.order) : value,
|
|
19865
|
+
};
|
|
19866
|
+
}
|
|
19867
|
+
}
|
|
19868
|
+
return;
|
|
19869
|
+
}
|
|
19870
|
+
|
|
19871
|
+
// -----------------------------------------------------------------------------
|
|
19872
|
+
// CONVERT
|
|
19873
|
+
// -----------------------------------------------------------------------------
|
|
19874
|
+
const CONVERT = {
|
|
19875
|
+
description: _t("Converts a numeric value to a different unit of measure."),
|
|
19876
|
+
args: [
|
|
19877
|
+
arg("value (number)", _t("the numeric value in start_unit to convert to end_unit")),
|
|
19878
|
+
arg("start_unit (string)", _t("The starting unit, the unit currently assigned to value")),
|
|
19879
|
+
arg("end_unit (string)", _t("The unit of measure into which to convert value")),
|
|
19880
|
+
],
|
|
19881
|
+
compute: function (value, startUnit, endUnit) {
|
|
19882
|
+
const _value = toNumber(value, this.locale);
|
|
19883
|
+
const _startUnit = toString(startUnit);
|
|
19884
|
+
const _endUnit = toString(endUnit);
|
|
19885
|
+
const startConversion = getTransformation(_startUnit);
|
|
19886
|
+
const endConversion = getTransformation(_endUnit);
|
|
19887
|
+
if (!startConversion) {
|
|
19888
|
+
return {
|
|
19889
|
+
value: CellErrorType.GenericError,
|
|
19890
|
+
message: _t("Invalid units of measure ('%s')", _startUnit),
|
|
19891
|
+
};
|
|
19892
|
+
}
|
|
19893
|
+
if (!endConversion) {
|
|
19894
|
+
return {
|
|
19895
|
+
value: CellErrorType.GenericError,
|
|
19896
|
+
message: _t("Invalid units of measure ('%s')", _endUnit),
|
|
19897
|
+
};
|
|
19898
|
+
}
|
|
19899
|
+
if (startConversion.category !== endConversion.category) {
|
|
19900
|
+
return {
|
|
19901
|
+
value: CellErrorType.GenericError,
|
|
19902
|
+
message: _t("Incompatible units of measure ('%s' vs '%s')", getTranslatedCategory(startConversion.category), getTranslatedCategory(endConversion.category)),
|
|
19903
|
+
};
|
|
19904
|
+
}
|
|
19905
|
+
return {
|
|
19906
|
+
value: endConversion.inverseTransform(startConversion.factor * startConversion.transform(_value)) /
|
|
19907
|
+
endConversion.factor,
|
|
19908
|
+
format: value?.format,
|
|
19909
|
+
};
|
|
19910
|
+
},
|
|
19911
|
+
isExported: true,
|
|
19912
|
+
};
|
|
19913
|
+
|
|
19914
|
+
var parser = /*#__PURE__*/Object.freeze({
|
|
19915
|
+
__proto__: null,
|
|
19916
|
+
CONVERT: CONVERT
|
|
19917
|
+
});
|
|
19918
|
+
|
|
19535
19919
|
const DEFAULT_STARTING_AT = 1;
|
|
19536
19920
|
/** Regex matching all the words in a string */
|
|
19537
19921
|
const wordRegex = /[A-Za-zÀ-ÖØ-öø-ÿ]+/g;
|
|
@@ -19949,6 +20333,7 @@ const categories = [
|
|
|
19949
20333
|
{ name: _t("Text"), functions: text },
|
|
19950
20334
|
{ name: _t("Engineering"), functions: engineering },
|
|
19951
20335
|
{ name: _t("Web"), functions: web },
|
|
20336
|
+
{ name: _t("Parser"), functions: parser },
|
|
19952
20337
|
];
|
|
19953
20338
|
const functionNameRegex = /^[A-Z0-9\_\.]+$/;
|
|
19954
20339
|
class FunctionRegistry extends Registry {
|
|
@@ -19960,41 +20345,127 @@ class FunctionRegistry extends Registry {
|
|
|
19960
20345
|
}
|
|
19961
20346
|
const descr = addMetaInfoFromArg(addDescr);
|
|
19962
20347
|
validateArguments(descr.args);
|
|
19963
|
-
this.mapping[name] =
|
|
20348
|
+
this.mapping[name] = createComputeFunction(descr, name);
|
|
19964
20349
|
super.add(name, descr);
|
|
19965
20350
|
return this;
|
|
19966
20351
|
}
|
|
19967
20352
|
}
|
|
19968
|
-
|
|
19969
|
-
|
|
20353
|
+
const functionRegistry = new FunctionRegistry();
|
|
20354
|
+
for (let category of categories) {
|
|
20355
|
+
const fns = category.functions;
|
|
20356
|
+
for (let name in fns) {
|
|
20357
|
+
const addDescr = fns[name];
|
|
20358
|
+
addDescr.category = addDescr.category || category.name;
|
|
20359
|
+
name = name.replace(/_/g, ".");
|
|
20360
|
+
functionRegistry.add(name, { isExported: false, ...addDescr });
|
|
20361
|
+
}
|
|
20362
|
+
}
|
|
20363
|
+
const notAvailableError = new NotAvailableError(_t("Array arguments to [[FUNCTION_NAME]] are of different size."));
|
|
20364
|
+
function createComputeFunction(descr, functionName) {
|
|
20365
|
+
function runtimeCompute(...args) {
|
|
20366
|
+
try {
|
|
20367
|
+
return vectorizedCompute.apply(this, args);
|
|
20368
|
+
}
|
|
20369
|
+
catch (e) {
|
|
20370
|
+
return handleError(e, functionName);
|
|
20371
|
+
}
|
|
20372
|
+
}
|
|
20373
|
+
function vectorizedCompute(...args) {
|
|
20374
|
+
let countVectorizableCol = 1;
|
|
20375
|
+
let countVectorizableRow = 1;
|
|
20376
|
+
let vectorizableColLimit = Infinity;
|
|
20377
|
+
let vectorizableRowLimit = Infinity;
|
|
20378
|
+
let vectorArgsType = undefined;
|
|
20379
|
+
//#region Compute vectorisation limits
|
|
19970
20380
|
for (let i = 0; i < args.length; i++) {
|
|
19971
20381
|
const argDefinition = descr.args[descr.getArgToFocus(i + 1) - 1];
|
|
19972
20382
|
const arg = args[i];
|
|
19973
20383
|
if (isMatrix(arg) && !argDefinition.acceptMatrix) {
|
|
19974
|
-
if
|
|
19975
|
-
|
|
20384
|
+
// if argDefinition does not accept a matrix but arg is still a matrix
|
|
20385
|
+
// --> triggers the arguments vectorization
|
|
20386
|
+
const nColumns = arg.length;
|
|
20387
|
+
const nRows = arg[0].length;
|
|
20388
|
+
if (nColumns !== 1 || nRows !== 1) {
|
|
20389
|
+
vectorArgsType ??= new Array(args.length);
|
|
20390
|
+
if (nColumns !== 1 && nRows !== 1) {
|
|
20391
|
+
vectorArgsType[i] = "matrix";
|
|
20392
|
+
countVectorizableCol = Math.max(countVectorizableCol, nColumns);
|
|
20393
|
+
countVectorizableRow = Math.max(countVectorizableRow, nRows);
|
|
20394
|
+
vectorizableColLimit = Math.min(vectorizableColLimit, nColumns);
|
|
20395
|
+
vectorizableRowLimit = Math.min(vectorizableRowLimit, nRows);
|
|
20396
|
+
}
|
|
20397
|
+
else if (nColumns !== 1) {
|
|
20398
|
+
vectorArgsType[i] = "horizontal";
|
|
20399
|
+
countVectorizableCol = Math.max(countVectorizableCol, nColumns);
|
|
20400
|
+
vectorizableColLimit = Math.min(vectorizableColLimit, nColumns);
|
|
20401
|
+
}
|
|
20402
|
+
else if (nRows !== 1) {
|
|
20403
|
+
vectorArgsType[i] = "vertical";
|
|
20404
|
+
countVectorizableRow = Math.max(countVectorizableRow, nRows);
|
|
20405
|
+
vectorizableRowLimit = Math.min(vectorizableRowLimit, nRows);
|
|
20406
|
+
}
|
|
20407
|
+
}
|
|
20408
|
+
else {
|
|
20409
|
+
args[i] = arg[0][0];
|
|
19976
20410
|
}
|
|
19977
|
-
args[i] = arg[0][0];
|
|
19978
20411
|
}
|
|
19979
20412
|
if (!isMatrix(arg) && argDefinition.acceptMatrixOnly) {
|
|
19980
20413
|
throw new BadExpressionError(_t("Function [[FUNCTION_NAME]] expects the parameter '%s' to be reference to a cell or range.", (i + 1).toString()));
|
|
19981
20414
|
}
|
|
19982
20415
|
}
|
|
19983
|
-
|
|
20416
|
+
//#endregion
|
|
20417
|
+
if (countVectorizableCol === 1 && countVectorizableRow === 1) {
|
|
20418
|
+
// either this function is not vectorized or it ends up with a 1x1 dimension
|
|
20419
|
+
return computeFunctionToObject.apply(this, args);
|
|
20420
|
+
}
|
|
20421
|
+
const getArgOffset = (i, j) => args.map((arg, index) => {
|
|
20422
|
+
switch (vectorArgsType?.[index]) {
|
|
20423
|
+
case "matrix":
|
|
20424
|
+
return arg[i][j];
|
|
20425
|
+
case "horizontal":
|
|
20426
|
+
return arg[i][0];
|
|
20427
|
+
case "vertical":
|
|
20428
|
+
return arg[0][j];
|
|
20429
|
+
case undefined:
|
|
20430
|
+
return arg;
|
|
20431
|
+
}
|
|
20432
|
+
});
|
|
20433
|
+
return generateMatrix(countVectorizableCol, countVectorizableRow, (col, row) => {
|
|
20434
|
+
if (col > vectorizableColLimit - 1 || row > vectorizableRowLimit - 1) {
|
|
20435
|
+
return notAvailableError;
|
|
20436
|
+
}
|
|
20437
|
+
const singleCellComputeResult = computeFunctionToObject.apply(this, getArgOffset(col, row));
|
|
20438
|
+
// In the case where the user tries to vectorize arguments of an array formula, we will get an
|
|
20439
|
+
// array for every combination of the vectorized arguments, which will lead to a 3D matrix and
|
|
20440
|
+
// we won't be able to return the values.
|
|
20441
|
+
// In this case, we keep the first element of each spreading part, just as Excel does, and
|
|
20442
|
+
// create an array with these parts.
|
|
20443
|
+
// For exemple, we have MUNIT(x) that return an unitary matrix of x*x. If we use it with a
|
|
20444
|
+
// range, like MUNIT(A1:A2), we will get two unitary matrices (one for the value in A1 and one
|
|
20445
|
+
// for the value in A2). In this case, we will simply take the first value of each matrix and
|
|
20446
|
+
// return the array [First value of MUNIT(A1), First value of MUNIT(A2)].
|
|
20447
|
+
return isMatrix(singleCellComputeResult)
|
|
20448
|
+
? singleCellComputeResult[0][0]
|
|
20449
|
+
: singleCellComputeResult;
|
|
20450
|
+
});
|
|
19984
20451
|
}
|
|
19985
|
-
|
|
19986
|
-
|
|
19987
|
-
|
|
19988
|
-
|
|
19989
|
-
|
|
19990
|
-
|
|
20452
|
+
function computeFunctionToObject(...args) {
|
|
20453
|
+
const result = descr.compute.apply(this, args);
|
|
20454
|
+
if (!isMatrix(result)) {
|
|
20455
|
+
if (typeof result === "object" && result !== null && "value" in result) {
|
|
20456
|
+
replaceFunctionNamePlaceholder(result, functionName);
|
|
20457
|
+
return result;
|
|
20458
|
+
}
|
|
20459
|
+
return { value: result };
|
|
19991
20460
|
}
|
|
19992
|
-
|
|
19993
|
-
|
|
20461
|
+
if (typeof result[0][0] === "object" && result[0][0] !== null && "value" in result[0][0]) {
|
|
20462
|
+
matrixForEach(result, (result) => replaceFunctionNamePlaceholder(result, functionName));
|
|
20463
|
+
return result;
|
|
19994
20464
|
}
|
|
19995
|
-
|
|
20465
|
+
return matrixMap(result, (row) => ({ value: row }));
|
|
20466
|
+
}
|
|
20467
|
+
return runtimeCompute;
|
|
19996
20468
|
}
|
|
19997
|
-
const implementationErrorMessage = _t("An unexpected error occurred. Submit a support ticket at odoo.com/help.");
|
|
19998
20469
|
function handleError(e, functionName) {
|
|
19999
20470
|
// the error could be an user error (instance of EvaluationError)
|
|
20000
20471
|
// or a javascript error (instance of Error)
|
|
@@ -20013,42 +20484,16 @@ function hasStringValue(obj) {
|
|
|
20013
20484
|
return (obj?.value !== undefined &&
|
|
20014
20485
|
typeof obj.value === "string");
|
|
20015
20486
|
}
|
|
20016
|
-
function
|
|
20017
|
-
return (obj?.message !== undefined &&
|
|
20018
|
-
typeof obj.message === "string");
|
|
20019
|
-
}
|
|
20020
|
-
function addResultHandling(compute, functionName) {
|
|
20021
|
-
return function computeWithResultHandling(...args) {
|
|
20022
|
-
const result = compute.apply(this, args);
|
|
20023
|
-
if (!isMatrix(result)) {
|
|
20024
|
-
if (typeof result === "object" && result !== null && "value" in result) {
|
|
20025
|
-
replaceFunctionNamePlaceholder(result, functionName);
|
|
20026
|
-
return result;
|
|
20027
|
-
}
|
|
20028
|
-
return { value: result };
|
|
20029
|
-
}
|
|
20030
|
-
if (typeof result[0][0] === "object" && result[0][0] !== null && "value" in result[0][0]) {
|
|
20031
|
-
matrixForEach(result, (result) => replaceFunctionNamePlaceholder(result, functionName));
|
|
20032
|
-
return result;
|
|
20033
|
-
}
|
|
20034
|
-
return matrixMap(result, (row) => ({ value: row }));
|
|
20035
|
-
};
|
|
20036
|
-
}
|
|
20037
|
-
function replaceFunctionNamePlaceholder(fPayload, functionName) {
|
|
20487
|
+
function replaceFunctionNamePlaceholder(functionResult, functionName) {
|
|
20038
20488
|
// for performance reasons: change in place and only if needed
|
|
20039
|
-
if (
|
|
20040
|
-
|
|
20489
|
+
if (functionResult.message?.includes("[[FUNCTION_NAME]]")) {
|
|
20490
|
+
functionResult.message = functionResult.message.replace("[[FUNCTION_NAME]]", functionName);
|
|
20041
20491
|
}
|
|
20042
20492
|
}
|
|
20043
|
-
const
|
|
20044
|
-
|
|
20045
|
-
|
|
20046
|
-
|
|
20047
|
-
const addDescr = fns[name];
|
|
20048
|
-
addDescr.category = addDescr.category || category.name;
|
|
20049
|
-
name = name.replace(/_/g, ".");
|
|
20050
|
-
functionRegistry.add(name, { isExported: false, ...addDescr });
|
|
20051
|
-
}
|
|
20493
|
+
const implementationErrorMessage = _t("An unexpected error occurred. Submit a support ticket at odoo.com/help.");
|
|
20494
|
+
function hasStringMessage(obj) {
|
|
20495
|
+
return (obj?.message !== undefined &&
|
|
20496
|
+
typeof obj.message === "string");
|
|
20052
20497
|
}
|
|
20053
20498
|
|
|
20054
20499
|
autoCompleteProviders.add("functions", {
|
|
@@ -21497,6 +21942,7 @@ function indentCode(code) {
|
|
|
21497
21942
|
|
|
21498
21943
|
const functions$1 = functionRegistry.content;
|
|
21499
21944
|
const OPERATOR_MAP = {
|
|
21945
|
+
// export for test
|
|
21500
21946
|
"=": "EQ",
|
|
21501
21947
|
"+": "ADD",
|
|
21502
21948
|
"-": "MINUS",
|
|
@@ -21511,6 +21957,7 @@ const OPERATOR_MAP = {
|
|
|
21511
21957
|
"&": "CONCATENATE",
|
|
21512
21958
|
};
|
|
21513
21959
|
const UNARY_OPERATOR_MAP = {
|
|
21960
|
+
// export for test
|
|
21514
21961
|
"-": "UMINUS",
|
|
21515
21962
|
"+": "UPLUS",
|
|
21516
21963
|
"%": "UNARY.PERCENT",
|
|
@@ -21581,7 +22028,7 @@ function compileTokens(tokens) {
|
|
|
21581
22028
|
}
|
|
21582
22029
|
/**
|
|
21583
22030
|
* This function compiles all the information extracted by the parser into an
|
|
21584
|
-
* executable code for the evaluation of the cells content. It uses a
|
|
22031
|
+
* executable code for the evaluation of the cells content. It uses a cache to
|
|
21585
22032
|
* not reevaluate identical code structures.
|
|
21586
22033
|
*
|
|
21587
22034
|
* The function is sensitive to parameter “isMeta”. This
|
|
@@ -22799,8 +23246,7 @@ function truncateLabel(label) {
|
|
|
22799
23246
|
/**
|
|
22800
23247
|
* Get a default chart js configuration
|
|
22801
23248
|
*/
|
|
22802
|
-
function getDefaultChartJsRuntime(chart, labels, fontColor,
|
|
22803
|
-
const { format, locale, truncateLabels, horizontalChart } = args;
|
|
23249
|
+
function getDefaultChartJsRuntime(chart, labels, fontColor, { format, locale, truncateLabels = true, horizontalChart, }) {
|
|
22804
23250
|
const chartTitle = chart.title.text ? chart.title : { ...chart.title, content: "" };
|
|
22805
23251
|
const options = {
|
|
22806
23252
|
// https://www.chartjs.org/docs/latest/general/responsive.html
|
|
@@ -22949,14 +23395,17 @@ function getChartDatasetValues(getters, dataSets) {
|
|
|
22949
23395
|
}
|
|
22950
23396
|
return datasetValues;
|
|
22951
23397
|
}
|
|
22952
|
-
/**
|
|
22953
|
-
|
|
22954
|
-
|
|
23398
|
+
/**
|
|
23399
|
+
* If the chart is a stacked area chart, we want to fill until the next dataset.
|
|
23400
|
+
* If the chart is a simple area chart, we want to fill until the origin (bottom axis).
|
|
23401
|
+
*
|
|
23402
|
+
* See https://www.chartjs.org/docs/latest/charts/area.html#filling-modes
|
|
23403
|
+
*/
|
|
23404
|
+
function getFillingMode(index, stackedChart) {
|
|
23405
|
+
if (!stackedChart) {
|
|
22955
23406
|
return "origin";
|
|
22956
23407
|
}
|
|
22957
|
-
|
|
22958
|
-
return index - 1;
|
|
22959
|
-
}
|
|
23408
|
+
return index === 0 ? "origin" : "-1";
|
|
22960
23409
|
}
|
|
22961
23410
|
function chartToImage(runtime, figure, type) {
|
|
22962
23411
|
// wrap the canvas in a div with a fixed size because chart.js would
|
|
@@ -22973,7 +23422,6 @@ function chartToImage(runtime, figure, type) {
|
|
|
22973
23422
|
if ("chartJsConfig" in runtime) {
|
|
22974
23423
|
const config = deepCopy(runtime.chartJsConfig);
|
|
22975
23424
|
config.plugins = [backgroundColorChartJSPlugin];
|
|
22976
|
-
// @ts-ignore
|
|
22977
23425
|
const chart = new window.Chart(canvas, config);
|
|
22978
23426
|
const imgContent = chart.toBase64Image();
|
|
22979
23427
|
chart.destroy();
|
|
@@ -23023,6 +23471,7 @@ class BarChart extends AbstractChart {
|
|
|
23023
23471
|
dataSetDesign;
|
|
23024
23472
|
axesDesign;
|
|
23025
23473
|
horizontal;
|
|
23474
|
+
showValues;
|
|
23026
23475
|
constructor(definition, sheetId, getters) {
|
|
23027
23476
|
super(definition, sheetId, getters);
|
|
23028
23477
|
this.dataSets = createDataSets(getters, definition.dataSets, sheetId, definition.dataSetsHaveTitle);
|
|
@@ -23035,6 +23484,7 @@ class BarChart extends AbstractChart {
|
|
|
23035
23484
|
this.dataSetDesign = definition.dataSets;
|
|
23036
23485
|
this.axesDesign = definition.axesDesign;
|
|
23037
23486
|
this.horizontal = definition.horizontal;
|
|
23487
|
+
this.showValues = definition.showValues;
|
|
23038
23488
|
}
|
|
23039
23489
|
static transformDefinition(definition, executed) {
|
|
23040
23490
|
return transformChartDefinitionWithDataSetsWithZone(definition, executed);
|
|
@@ -23054,6 +23504,7 @@ class BarChart extends AbstractChart {
|
|
|
23054
23504
|
type: "bar",
|
|
23055
23505
|
labelRange: context.auxiliaryRange || undefined,
|
|
23056
23506
|
axesDesign: context.axesDesign,
|
|
23507
|
+
showValues: context.showValues,
|
|
23057
23508
|
};
|
|
23058
23509
|
}
|
|
23059
23510
|
getContextCreation() {
|
|
@@ -23107,6 +23558,7 @@ class BarChart extends AbstractChart {
|
|
|
23107
23558
|
aggregated: this.aggregated,
|
|
23108
23559
|
axesDesign: this.axesDesign,
|
|
23109
23560
|
horizontal: this.horizontal,
|
|
23561
|
+
showValues: this.showValues,
|
|
23110
23562
|
};
|
|
23111
23563
|
}
|
|
23112
23564
|
getDefinitionForExcel() {
|
|
@@ -23156,22 +23608,23 @@ function getBarConfiguration(chart, labels, localeFormat) {
|
|
|
23156
23608
|
padding: { left: 20, right: 20, top: chart.title ? 10 : 25, bottom: 10 },
|
|
23157
23609
|
};
|
|
23158
23610
|
config.options.indexAxis = chart.horizontal ? "y" : "x";
|
|
23611
|
+
const formatCallback = (value) => {
|
|
23612
|
+
value = Number(value);
|
|
23613
|
+
if (isNaN(value))
|
|
23614
|
+
return value;
|
|
23615
|
+
const { locale, format } = localeFormat;
|
|
23616
|
+
return formatValue(value, {
|
|
23617
|
+
locale,
|
|
23618
|
+
format: !format && Math.abs(value) >= 1000 ? "#,##" : format,
|
|
23619
|
+
});
|
|
23620
|
+
};
|
|
23159
23621
|
config.options.scales = {};
|
|
23160
23622
|
const labelsAxis = { ticks: { padding: 5, color: fontColor } };
|
|
23161
23623
|
const valuesAxis = {
|
|
23162
23624
|
beginAtZero: true, // the origin of the y axis is always zero
|
|
23163
23625
|
ticks: {
|
|
23164
23626
|
color: fontColor,
|
|
23165
|
-
callback:
|
|
23166
|
-
value = Number(value);
|
|
23167
|
-
if (isNaN(value))
|
|
23168
|
-
return value;
|
|
23169
|
-
const { locale, format } = localeFormat;
|
|
23170
|
-
return formatValue(value, {
|
|
23171
|
-
locale,
|
|
23172
|
-
format: !format && Math.abs(value) >= 1000 ? "#,##" : format,
|
|
23173
|
-
});
|
|
23174
|
-
},
|
|
23627
|
+
callback: formatCallback,
|
|
23175
23628
|
},
|
|
23176
23629
|
};
|
|
23177
23630
|
const xAxis = chart.horizontal ? valuesAxis : labelsAxis;
|
|
@@ -23204,6 +23657,12 @@ function getBarConfiguration(chart, labels, localeFormat) {
|
|
|
23204
23657
|
config.options.scales.y1.stacked = true;
|
|
23205
23658
|
}
|
|
23206
23659
|
}
|
|
23660
|
+
config.options.plugins.chartShowValuesPlugin = {
|
|
23661
|
+
showValues: chart.showValues,
|
|
23662
|
+
background: chart.background,
|
|
23663
|
+
horizontal: chart.horizontal,
|
|
23664
|
+
callback: formatCallback,
|
|
23665
|
+
};
|
|
23207
23666
|
return config;
|
|
23208
23667
|
}
|
|
23209
23668
|
function createBarChartRuntime(chart, getters) {
|
|
@@ -23251,467 +23710,6 @@ function createBarChartRuntime(chart, getters) {
|
|
|
23251
23710
|
return { chartJsConfig: config, background: chart.background || BACKGROUND_CHART_COLOR };
|
|
23252
23711
|
}
|
|
23253
23712
|
|
|
23254
|
-
class ComboChart extends AbstractChart {
|
|
23255
|
-
dataSets;
|
|
23256
|
-
labelRange;
|
|
23257
|
-
background;
|
|
23258
|
-
legendPosition;
|
|
23259
|
-
aggregated;
|
|
23260
|
-
dataSetsHaveTitle;
|
|
23261
|
-
dataSetDesign;
|
|
23262
|
-
axesDesign;
|
|
23263
|
-
type = "combo";
|
|
23264
|
-
constructor(definition, sheetId, getters) {
|
|
23265
|
-
super(definition, sheetId, getters);
|
|
23266
|
-
this.dataSets = createDataSets(getters, definition.dataSets, sheetId, definition.dataSetsHaveTitle);
|
|
23267
|
-
this.labelRange = createValidRange(getters, sheetId, definition.labelRange);
|
|
23268
|
-
this.background = definition.background;
|
|
23269
|
-
this.legendPosition = definition.legendPosition;
|
|
23270
|
-
this.aggregated = definition.aggregated;
|
|
23271
|
-
this.dataSetsHaveTitle = definition.dataSetsHaveTitle;
|
|
23272
|
-
this.dataSetDesign = definition.dataSets;
|
|
23273
|
-
this.axesDesign = definition.axesDesign;
|
|
23274
|
-
}
|
|
23275
|
-
static transformDefinition(definition, executed) {
|
|
23276
|
-
return transformChartDefinitionWithDataSetsWithZone(definition, executed);
|
|
23277
|
-
}
|
|
23278
|
-
static validateChartDefinition(validator, definition) {
|
|
23279
|
-
return validator.checkValidations(definition, checkDataset, checkLabelRange);
|
|
23280
|
-
}
|
|
23281
|
-
getContextCreation() {
|
|
23282
|
-
const range = [];
|
|
23283
|
-
for (const [i, dataSet] of this.dataSets.entries()) {
|
|
23284
|
-
range.push({
|
|
23285
|
-
...this.dataSetDesign?.[i],
|
|
23286
|
-
dataRange: this.getters.getRangeString(dataSet.dataRange, this.sheetId),
|
|
23287
|
-
});
|
|
23288
|
-
}
|
|
23289
|
-
return {
|
|
23290
|
-
...this,
|
|
23291
|
-
range,
|
|
23292
|
-
auxiliaryRange: this.labelRange
|
|
23293
|
-
? this.getters.getRangeString(this.labelRange, this.sheetId)
|
|
23294
|
-
: undefined,
|
|
23295
|
-
};
|
|
23296
|
-
}
|
|
23297
|
-
getDefinition() {
|
|
23298
|
-
return this.getDefinitionWithSpecificDataSets(this.dataSets, this.labelRange);
|
|
23299
|
-
}
|
|
23300
|
-
getDefinitionWithSpecificDataSets(dataSets, labelRange, targetSheetId) {
|
|
23301
|
-
const ranges = [];
|
|
23302
|
-
for (const [i, dataSet] of dataSets.entries()) {
|
|
23303
|
-
ranges.push({
|
|
23304
|
-
...this.dataSetDesign?.[i],
|
|
23305
|
-
dataRange: this.getters.getRangeString(dataSet.dataRange, targetSheetId || this.sheetId),
|
|
23306
|
-
});
|
|
23307
|
-
}
|
|
23308
|
-
return {
|
|
23309
|
-
type: "combo",
|
|
23310
|
-
dataSetsHaveTitle: dataSets.length ? Boolean(dataSets[0].labelCell) : false,
|
|
23311
|
-
background: this.background,
|
|
23312
|
-
dataSets: ranges,
|
|
23313
|
-
legendPosition: this.legendPosition,
|
|
23314
|
-
labelRange: labelRange
|
|
23315
|
-
? this.getters.getRangeString(labelRange, targetSheetId || this.sheetId)
|
|
23316
|
-
: undefined,
|
|
23317
|
-
title: this.title,
|
|
23318
|
-
aggregated: this.aggregated,
|
|
23319
|
-
axesDesign: this.axesDesign,
|
|
23320
|
-
};
|
|
23321
|
-
}
|
|
23322
|
-
getDefinitionForExcel() {
|
|
23323
|
-
// Excel does not support aggregating labels
|
|
23324
|
-
if (this.aggregated) {
|
|
23325
|
-
return undefined;
|
|
23326
|
-
}
|
|
23327
|
-
const dataSets = this.dataSets
|
|
23328
|
-
.map((ds) => toExcelDataset(this.getters, ds))
|
|
23329
|
-
.filter((ds) => ds.range !== "" && ds.range !== CellErrorType.InvalidReference);
|
|
23330
|
-
const labelRange = toExcelLabelRange(this.getters, this.labelRange, shouldRemoveFirstLabel(this.labelRange, this.dataSets[0], this.dataSetsHaveTitle));
|
|
23331
|
-
const definition = this.getDefinition();
|
|
23332
|
-
return {
|
|
23333
|
-
...definition,
|
|
23334
|
-
backgroundColor: toXlsxHexColor(this.background || BACKGROUND_CHART_COLOR),
|
|
23335
|
-
fontColor: toXlsxHexColor(chartFontColor(this.background)),
|
|
23336
|
-
dataSets,
|
|
23337
|
-
labelRange,
|
|
23338
|
-
verticalAxis: getDefinedAxis(definition),
|
|
23339
|
-
};
|
|
23340
|
-
}
|
|
23341
|
-
updateRanges(applyChange) {
|
|
23342
|
-
const { dataSets, labelRange, isStale } = updateChartRangesWithDataSets(this.getters, applyChange, this.dataSets, this.labelRange);
|
|
23343
|
-
if (!isStale) {
|
|
23344
|
-
return this;
|
|
23345
|
-
}
|
|
23346
|
-
const definition = this.getDefinitionWithSpecificDataSets(dataSets, labelRange);
|
|
23347
|
-
return new ComboChart(definition, this.sheetId, this.getters);
|
|
23348
|
-
}
|
|
23349
|
-
static getDefinitionFromContextCreation(context) {
|
|
23350
|
-
return {
|
|
23351
|
-
background: context.background,
|
|
23352
|
-
dataSets: context.range ?? [],
|
|
23353
|
-
dataSetsHaveTitle: context.dataSetsHaveTitle ?? false,
|
|
23354
|
-
aggregated: context.aggregated,
|
|
23355
|
-
legendPosition: context.legendPosition ?? "top",
|
|
23356
|
-
title: context.title || { text: "" },
|
|
23357
|
-
labelRange: context.auxiliaryRange || undefined,
|
|
23358
|
-
type: "combo",
|
|
23359
|
-
axesDesign: context.axesDesign,
|
|
23360
|
-
};
|
|
23361
|
-
}
|
|
23362
|
-
copyForSheetId(sheetId) {
|
|
23363
|
-
const dataSets = copyDataSetsWithNewSheetId(this.sheetId, sheetId, this.dataSets);
|
|
23364
|
-
const labelRange = copyLabelRangeWithNewSheetId(this.sheetId, sheetId, this.labelRange);
|
|
23365
|
-
const definition = this.getDefinitionWithSpecificDataSets(dataSets, labelRange, sheetId);
|
|
23366
|
-
return new ComboChart(definition, sheetId, this.getters);
|
|
23367
|
-
}
|
|
23368
|
-
copyInSheetId(sheetId) {
|
|
23369
|
-
const definition = this.getDefinitionWithSpecificDataSets(this.dataSets, this.labelRange, sheetId);
|
|
23370
|
-
return new ComboChart(definition, sheetId, this.getters);
|
|
23371
|
-
}
|
|
23372
|
-
}
|
|
23373
|
-
function createComboChartRuntime(chart, getters) {
|
|
23374
|
-
const mainDataSetFormat = chart.dataSets.length
|
|
23375
|
-
? getChartDatasetFormat(getters, [chart.dataSets[0]])
|
|
23376
|
-
: undefined;
|
|
23377
|
-
const lineDataSetsFormat = getChartDatasetFormat(getters, chart.dataSets.slice(1));
|
|
23378
|
-
const locale = getters.getLocale();
|
|
23379
|
-
const labelValues = getChartLabelValues(getters, chart.dataSets, chart.labelRange);
|
|
23380
|
-
let labels = labelValues.formattedValues;
|
|
23381
|
-
let dataSetsValues = getChartDatasetValues(getters, chart.dataSets);
|
|
23382
|
-
if (chart.dataSetsHaveTitle &&
|
|
23383
|
-
dataSetsValues[0] &&
|
|
23384
|
-
labels.length > dataSetsValues[0].data.length) {
|
|
23385
|
-
labels.shift();
|
|
23386
|
-
}
|
|
23387
|
-
({ labels, dataSetsValues } = filterEmptyDataPoints(labels, dataSetsValues));
|
|
23388
|
-
if (chart.aggregated) {
|
|
23389
|
-
({ labels, dataSetsValues } = aggregateDataForLabels(labels, dataSetsValues));
|
|
23390
|
-
}
|
|
23391
|
-
const localeFormat = { format: mainDataSetFormat, locale };
|
|
23392
|
-
const fontColor = chartFontColor(chart.background);
|
|
23393
|
-
const config = getDefaultChartJsRuntime(chart, labels, fontColor, localeFormat);
|
|
23394
|
-
const legend = {
|
|
23395
|
-
labels: { color: fontColor },
|
|
23396
|
-
reverse: true,
|
|
23397
|
-
};
|
|
23398
|
-
if ((!chart.labelRange && chart.dataSets.length === 1) || chart.legendPosition === "none") {
|
|
23399
|
-
legend.display = false;
|
|
23400
|
-
}
|
|
23401
|
-
else {
|
|
23402
|
-
legend.position = chart.legendPosition;
|
|
23403
|
-
}
|
|
23404
|
-
config.options.plugins.legend = { ...config.options.plugins?.legend, ...legend };
|
|
23405
|
-
config.options.layout = {
|
|
23406
|
-
padding: { left: 20, right: 20, top: chart.title ? 10 : 25, bottom: 10 },
|
|
23407
|
-
};
|
|
23408
|
-
config.options.scales = {
|
|
23409
|
-
x: {
|
|
23410
|
-
ticks: {
|
|
23411
|
-
padding: 5,
|
|
23412
|
-
color: fontColor,
|
|
23413
|
-
},
|
|
23414
|
-
title: getChartAxisTitleRuntime(chart.axesDesign?.x),
|
|
23415
|
-
},
|
|
23416
|
-
};
|
|
23417
|
-
const formatCallback = (format) => {
|
|
23418
|
-
return (value) => {
|
|
23419
|
-
value = Number(value);
|
|
23420
|
-
if (isNaN(value))
|
|
23421
|
-
return value;
|
|
23422
|
-
const { locale } = localeFormat;
|
|
23423
|
-
return formatValue(value, {
|
|
23424
|
-
locale,
|
|
23425
|
-
format: !format && Math.abs(value) >= 1000 ? "#,##" : format,
|
|
23426
|
-
});
|
|
23427
|
-
};
|
|
23428
|
-
};
|
|
23429
|
-
const leftVerticalAxis = {
|
|
23430
|
-
beginAtZero: true, // the origin of the y axis is always zero
|
|
23431
|
-
ticks: {
|
|
23432
|
-
color: fontColor,
|
|
23433
|
-
callback: formatCallback(mainDataSetFormat),
|
|
23434
|
-
},
|
|
23435
|
-
};
|
|
23436
|
-
const rightVerticalAxis = {
|
|
23437
|
-
beginAtZero: true, // the origin of the y axis is always zero
|
|
23438
|
-
ticks: {
|
|
23439
|
-
color: fontColor,
|
|
23440
|
-
callback: formatCallback(lineDataSetsFormat),
|
|
23441
|
-
},
|
|
23442
|
-
};
|
|
23443
|
-
const definition = chart.getDefinition();
|
|
23444
|
-
const { useLeftAxis, useRightAxis } = getDefinedAxis(definition);
|
|
23445
|
-
if (useLeftAxis) {
|
|
23446
|
-
config.options.scales.y = {
|
|
23447
|
-
...leftVerticalAxis,
|
|
23448
|
-
position: "left",
|
|
23449
|
-
title: getChartAxisTitleRuntime(chart.axesDesign?.y),
|
|
23450
|
-
};
|
|
23451
|
-
}
|
|
23452
|
-
if (useRightAxis) {
|
|
23453
|
-
config.options.scales.y1 = {
|
|
23454
|
-
...rightVerticalAxis,
|
|
23455
|
-
position: "right",
|
|
23456
|
-
grid: {
|
|
23457
|
-
display: false,
|
|
23458
|
-
},
|
|
23459
|
-
title: getChartAxisTitleRuntime(chart.axesDesign?.y1),
|
|
23460
|
-
};
|
|
23461
|
-
}
|
|
23462
|
-
const colors = new ColorGenerator();
|
|
23463
|
-
for (let [index, { label, data }] of dataSetsValues.entries()) {
|
|
23464
|
-
const design = definition.dataSets[index];
|
|
23465
|
-
const color = colors.next();
|
|
23466
|
-
const dataset = {
|
|
23467
|
-
label: design?.label ?? label,
|
|
23468
|
-
data,
|
|
23469
|
-
borderColor: design?.backgroundColor ?? color,
|
|
23470
|
-
backgroundColor: design.backgroundColor ?? color,
|
|
23471
|
-
yAxisID: design?.yAxisId ?? "y",
|
|
23472
|
-
type: index === 0 ? "bar" : "line",
|
|
23473
|
-
order: -index,
|
|
23474
|
-
};
|
|
23475
|
-
config.data.datasets.push(dataset);
|
|
23476
|
-
}
|
|
23477
|
-
return { chartJsConfig: config, background: chart.background || BACKGROUND_CHART_COLOR };
|
|
23478
|
-
}
|
|
23479
|
-
|
|
23480
|
-
function isDataRangeValid(definition) {
|
|
23481
|
-
return definition.dataRange && !rangeReference.test(definition.dataRange)
|
|
23482
|
-
? "InvalidGaugeDataRange" /* CommandResult.InvalidGaugeDataRange */
|
|
23483
|
-
: "Success" /* CommandResult.Success */;
|
|
23484
|
-
}
|
|
23485
|
-
function checkRangeLimits(check, batchValidations) {
|
|
23486
|
-
return batchValidations((definition) => {
|
|
23487
|
-
if (definition.sectionRule) {
|
|
23488
|
-
return check(definition.sectionRule.rangeMin, "rangeMin");
|
|
23489
|
-
}
|
|
23490
|
-
return "Success" /* CommandResult.Success */;
|
|
23491
|
-
}, (definition) => {
|
|
23492
|
-
if (definition.sectionRule) {
|
|
23493
|
-
return check(definition.sectionRule.rangeMax, "rangeMax");
|
|
23494
|
-
}
|
|
23495
|
-
return "Success" /* CommandResult.Success */;
|
|
23496
|
-
});
|
|
23497
|
-
}
|
|
23498
|
-
function checkInflectionPointsValue(check, batchValidations) {
|
|
23499
|
-
return batchValidations((definition) => {
|
|
23500
|
-
if (definition.sectionRule) {
|
|
23501
|
-
return check(definition.sectionRule.lowerInflectionPoint.value, "lowerInflectionPointValue");
|
|
23502
|
-
}
|
|
23503
|
-
return "Success" /* CommandResult.Success */;
|
|
23504
|
-
}, (definition) => {
|
|
23505
|
-
if (definition.sectionRule) {
|
|
23506
|
-
return check(definition.sectionRule.upperInflectionPoint.value, "upperInflectionPointValue");
|
|
23507
|
-
}
|
|
23508
|
-
return "Success" /* CommandResult.Success */;
|
|
23509
|
-
});
|
|
23510
|
-
}
|
|
23511
|
-
function checkRangeMinBiggerThanRangeMax(definition) {
|
|
23512
|
-
if (definition.sectionRule) {
|
|
23513
|
-
if (Number(definition.sectionRule.rangeMin) >= Number(definition.sectionRule.rangeMax)) {
|
|
23514
|
-
return "GaugeRangeMinBiggerThanRangeMax" /* CommandResult.GaugeRangeMinBiggerThanRangeMax */;
|
|
23515
|
-
}
|
|
23516
|
-
}
|
|
23517
|
-
return "Success" /* CommandResult.Success */;
|
|
23518
|
-
}
|
|
23519
|
-
function checkEmpty(value, valueName) {
|
|
23520
|
-
if (value === "") {
|
|
23521
|
-
switch (valueName) {
|
|
23522
|
-
case "rangeMin":
|
|
23523
|
-
return "EmptyGaugeRangeMin" /* CommandResult.EmptyGaugeRangeMin */;
|
|
23524
|
-
case "rangeMax":
|
|
23525
|
-
return "EmptyGaugeRangeMax" /* CommandResult.EmptyGaugeRangeMax */;
|
|
23526
|
-
}
|
|
23527
|
-
}
|
|
23528
|
-
return "Success" /* CommandResult.Success */;
|
|
23529
|
-
}
|
|
23530
|
-
function checkNaN(value, valueName) {
|
|
23531
|
-
if (isNaN(value)) {
|
|
23532
|
-
switch (valueName) {
|
|
23533
|
-
case "rangeMin":
|
|
23534
|
-
return "GaugeRangeMinNaN" /* CommandResult.GaugeRangeMinNaN */;
|
|
23535
|
-
case "rangeMax":
|
|
23536
|
-
return "GaugeRangeMaxNaN" /* CommandResult.GaugeRangeMaxNaN */;
|
|
23537
|
-
case "lowerInflectionPointValue":
|
|
23538
|
-
return "GaugeLowerInflectionPointNaN" /* CommandResult.GaugeLowerInflectionPointNaN */;
|
|
23539
|
-
case "upperInflectionPointValue":
|
|
23540
|
-
return "GaugeUpperInflectionPointNaN" /* CommandResult.GaugeUpperInflectionPointNaN */;
|
|
23541
|
-
}
|
|
23542
|
-
}
|
|
23543
|
-
return "Success" /* CommandResult.Success */;
|
|
23544
|
-
}
|
|
23545
|
-
class GaugeChart extends AbstractChart {
|
|
23546
|
-
dataRange;
|
|
23547
|
-
sectionRule;
|
|
23548
|
-
background;
|
|
23549
|
-
type = "gauge";
|
|
23550
|
-
constructor(definition, sheetId, getters) {
|
|
23551
|
-
super(definition, sheetId, getters);
|
|
23552
|
-
this.dataRange = createValidRange(this.getters, this.sheetId, definition.dataRange);
|
|
23553
|
-
this.sectionRule = definition.sectionRule;
|
|
23554
|
-
this.background = definition.background;
|
|
23555
|
-
}
|
|
23556
|
-
static validateChartDefinition(validator, definition) {
|
|
23557
|
-
return validator.checkValidations(definition, isDataRangeValid, validator.chainValidations(checkRangeLimits(checkEmpty, validator.batchValidations), checkRangeLimits(checkNaN, validator.batchValidations), checkRangeMinBiggerThanRangeMax), validator.chainValidations(checkInflectionPointsValue(checkNaN, validator.batchValidations)));
|
|
23558
|
-
}
|
|
23559
|
-
static transformDefinition(definition, executed) {
|
|
23560
|
-
let dataRangeZone;
|
|
23561
|
-
if (definition.dataRange) {
|
|
23562
|
-
dataRangeZone = transformZone(toUnboundedZone(definition.dataRange), executed);
|
|
23563
|
-
}
|
|
23564
|
-
return {
|
|
23565
|
-
...definition,
|
|
23566
|
-
dataRange: dataRangeZone ? zoneToXc(dataRangeZone) : undefined,
|
|
23567
|
-
};
|
|
23568
|
-
}
|
|
23569
|
-
static getDefinitionFromContextCreation(context) {
|
|
23570
|
-
return {
|
|
23571
|
-
background: context.background,
|
|
23572
|
-
title: context.title || { text: "" },
|
|
23573
|
-
type: "gauge",
|
|
23574
|
-
dataRange: context.range ? context.range[0].dataRange : undefined,
|
|
23575
|
-
sectionRule: {
|
|
23576
|
-
colors: {
|
|
23577
|
-
lowerColor: DEFAULT_GAUGE_LOWER_COLOR,
|
|
23578
|
-
middleColor: DEFAULT_GAUGE_MIDDLE_COLOR,
|
|
23579
|
-
upperColor: DEFAULT_GAUGE_UPPER_COLOR,
|
|
23580
|
-
},
|
|
23581
|
-
rangeMin: "0",
|
|
23582
|
-
rangeMax: "100",
|
|
23583
|
-
lowerInflectionPoint: {
|
|
23584
|
-
type: "percentage",
|
|
23585
|
-
value: "15",
|
|
23586
|
-
},
|
|
23587
|
-
upperInflectionPoint: {
|
|
23588
|
-
type: "percentage",
|
|
23589
|
-
value: "40",
|
|
23590
|
-
},
|
|
23591
|
-
},
|
|
23592
|
-
};
|
|
23593
|
-
}
|
|
23594
|
-
copyForSheetId(sheetId) {
|
|
23595
|
-
const dataRange = copyLabelRangeWithNewSheetId(this.sheetId, sheetId, this.dataRange);
|
|
23596
|
-
const definition = this.getDefinitionWithSpecificRanges(dataRange, sheetId);
|
|
23597
|
-
return new GaugeChart(definition, sheetId, this.getters);
|
|
23598
|
-
}
|
|
23599
|
-
copyInSheetId(sheetId) {
|
|
23600
|
-
const definition = this.getDefinitionWithSpecificRanges(this.dataRange, sheetId);
|
|
23601
|
-
return new GaugeChart(definition, sheetId, this.getters);
|
|
23602
|
-
}
|
|
23603
|
-
getDefinition() {
|
|
23604
|
-
return this.getDefinitionWithSpecificRanges(this.dataRange);
|
|
23605
|
-
}
|
|
23606
|
-
getDefinitionWithSpecificRanges(dataRange, targetSheetId) {
|
|
23607
|
-
return {
|
|
23608
|
-
background: this.background,
|
|
23609
|
-
sectionRule: this.sectionRule,
|
|
23610
|
-
title: this.title,
|
|
23611
|
-
type: "gauge",
|
|
23612
|
-
dataRange: dataRange
|
|
23613
|
-
? this.getters.getRangeString(dataRange, targetSheetId || this.sheetId)
|
|
23614
|
-
: undefined,
|
|
23615
|
-
};
|
|
23616
|
-
}
|
|
23617
|
-
getDefinitionForExcel() {
|
|
23618
|
-
// This kind of graph is not exportable in Excel
|
|
23619
|
-
return undefined;
|
|
23620
|
-
}
|
|
23621
|
-
getContextCreation() {
|
|
23622
|
-
return {
|
|
23623
|
-
...this,
|
|
23624
|
-
range: this.dataRange
|
|
23625
|
-
? [{ dataRange: this.getters.getRangeString(this.dataRange, this.sheetId) }]
|
|
23626
|
-
: undefined,
|
|
23627
|
-
};
|
|
23628
|
-
}
|
|
23629
|
-
updateRanges(applyChange) {
|
|
23630
|
-
const range = adaptChartRange(this.dataRange, applyChange);
|
|
23631
|
-
if (this.dataRange === range) {
|
|
23632
|
-
return this;
|
|
23633
|
-
}
|
|
23634
|
-
const definition = this.getDefinitionWithSpecificRanges(range);
|
|
23635
|
-
return new GaugeChart(definition, this.sheetId, this.getters);
|
|
23636
|
-
}
|
|
23637
|
-
}
|
|
23638
|
-
function createGaugeChartRuntime(chart, getters) {
|
|
23639
|
-
const locale = getters.getLocale();
|
|
23640
|
-
const chartColors = chart.sectionRule.colors;
|
|
23641
|
-
let gaugeValue = undefined;
|
|
23642
|
-
let formattedValue = undefined;
|
|
23643
|
-
let format = undefined;
|
|
23644
|
-
const dataRange = chart.dataRange;
|
|
23645
|
-
if (dataRange !== undefined) {
|
|
23646
|
-
const cell = getters.getEvaluatedCell({
|
|
23647
|
-
sheetId: dataRange.sheetId,
|
|
23648
|
-
col: dataRange.zone.left,
|
|
23649
|
-
row: dataRange.zone.top,
|
|
23650
|
-
});
|
|
23651
|
-
if (cell.type === CellValueType.number) {
|
|
23652
|
-
gaugeValue = cell.value;
|
|
23653
|
-
formattedValue = cell.formattedValue;
|
|
23654
|
-
format = cell.format;
|
|
23655
|
-
}
|
|
23656
|
-
}
|
|
23657
|
-
const minValue = Number(chart.sectionRule.rangeMin);
|
|
23658
|
-
const maxValue = Number(chart.sectionRule.rangeMax);
|
|
23659
|
-
const lowerPoint = chart.sectionRule.lowerInflectionPoint;
|
|
23660
|
-
const upperPoint = chart.sectionRule.upperInflectionPoint;
|
|
23661
|
-
const lowerPointValue = getSectionThresholdValue(lowerPoint, minValue, maxValue);
|
|
23662
|
-
const upperPointValue = getSectionThresholdValue(upperPoint, minValue, maxValue);
|
|
23663
|
-
const inflectionValues = [];
|
|
23664
|
-
const colors = [];
|
|
23665
|
-
if (lowerPointValue !== undefined) {
|
|
23666
|
-
inflectionValues.push({
|
|
23667
|
-
value: lowerPointValue,
|
|
23668
|
-
label: formatValue(lowerPointValue, { locale, format }),
|
|
23669
|
-
});
|
|
23670
|
-
colors.push(chartColors.lowerColor);
|
|
23671
|
-
}
|
|
23672
|
-
if (upperPointValue !== undefined && upperPointValue !== lowerPointValue) {
|
|
23673
|
-
inflectionValues.push({
|
|
23674
|
-
value: upperPointValue,
|
|
23675
|
-
label: formatValue(upperPointValue, { locale, format }),
|
|
23676
|
-
});
|
|
23677
|
-
colors.push(chartColors.middleColor);
|
|
23678
|
-
}
|
|
23679
|
-
if (upperPointValue !== undefined &&
|
|
23680
|
-
lowerPointValue !== undefined &&
|
|
23681
|
-
lowerPointValue > upperPointValue) {
|
|
23682
|
-
inflectionValues.reverse();
|
|
23683
|
-
colors.reverse();
|
|
23684
|
-
}
|
|
23685
|
-
colors.push(chartColors.upperColor);
|
|
23686
|
-
return {
|
|
23687
|
-
background: getters.getStyleOfSingleCellChart(chart.background, dataRange).background,
|
|
23688
|
-
title: chart.title ?? { text: "" },
|
|
23689
|
-
minValue: {
|
|
23690
|
-
value: minValue,
|
|
23691
|
-
label: formatValue(minValue, { locale, format }),
|
|
23692
|
-
},
|
|
23693
|
-
maxValue: {
|
|
23694
|
-
value: maxValue,
|
|
23695
|
-
label: formatValue(maxValue, { locale, format }),
|
|
23696
|
-
},
|
|
23697
|
-
gaugeValue: gaugeValue !== undefined && formattedValue
|
|
23698
|
-
? { value: gaugeValue, label: formattedValue }
|
|
23699
|
-
: undefined,
|
|
23700
|
-
inflectionValues,
|
|
23701
|
-
colors,
|
|
23702
|
-
};
|
|
23703
|
-
}
|
|
23704
|
-
function getSectionThresholdValue(threshold, minValue, maxValue) {
|
|
23705
|
-
if (threshold.value === "" || isNaN(Number(threshold.value))) {
|
|
23706
|
-
return undefined;
|
|
23707
|
-
}
|
|
23708
|
-
const numberValue = Number(threshold.value);
|
|
23709
|
-
const value = threshold.type === "number"
|
|
23710
|
-
? numberValue
|
|
23711
|
-
: minValue + ((maxValue - minValue) * numberValue) / 100;
|
|
23712
|
-
return clip(value, minValue, maxValue);
|
|
23713
|
-
}
|
|
23714
|
-
|
|
23715
23713
|
const UNIT_LENGTH = {
|
|
23716
23714
|
second: 1000,
|
|
23717
23715
|
minute: 1000 * 60,
|
|
@@ -23894,13 +23892,11 @@ function canBeLinearChart(labelRange, getters) {
|
|
|
23894
23892
|
}
|
|
23895
23893
|
let missingTimeAdapterAlreadyWarned = false;
|
|
23896
23894
|
function isLuxonTimeAdapterInstalled() {
|
|
23897
|
-
// @ts-ignore
|
|
23898
23895
|
if (!window.Chart) {
|
|
23899
23896
|
return false;
|
|
23900
23897
|
}
|
|
23901
23898
|
// @ts-ignore
|
|
23902
23899
|
const adapter = new window.Chart._adapters._date({});
|
|
23903
|
-
// @ts-ignore
|
|
23904
23900
|
const isInstalled = adapter._id === "luxon";
|
|
23905
23901
|
if (!isInstalled && !missingTimeAdapterAlreadyWarned) {
|
|
23906
23902
|
missingTimeAdapterAlreadyWarned = true;
|
|
@@ -23917,9 +23913,7 @@ function getLineOrScatterConfiguration(chart, labels, options) {
|
|
|
23917
23913
|
generateLabels(chart) {
|
|
23918
23914
|
// color the legend labels with the dataset color, without any transparency
|
|
23919
23915
|
const { data } = chart;
|
|
23920
|
-
|
|
23921
|
-
const labels = window.Chart.defaults.plugins.legend.labels
|
|
23922
|
-
.generateLabels(chart);
|
|
23916
|
+
const labels = window.Chart.defaults.plugins.legend.labels.generateLabels(chart);
|
|
23923
23917
|
for (const [index, label] of labels.entries()) {
|
|
23924
23918
|
label.fillStyle = data.datasets[index].borderColor;
|
|
23925
23919
|
}
|
|
@@ -23946,151 +23940,628 @@ function getLineOrScatterConfiguration(chart, labels, options) {
|
|
|
23946
23940
|
title: getChartAxisTitleRuntime(chart.axesDesign?.x),
|
|
23947
23941
|
},
|
|
23948
23942
|
};
|
|
23949
|
-
const
|
|
23943
|
+
const formatCallback = (value) => {
|
|
23944
|
+
value = Number(value);
|
|
23945
|
+
if (isNaN(value))
|
|
23946
|
+
return value;
|
|
23947
|
+
const { locale, format } = options;
|
|
23948
|
+
return formatValue(value, {
|
|
23949
|
+
locale,
|
|
23950
|
+
format: !format && Math.abs(value) >= 1000 ? "#,##" : format,
|
|
23951
|
+
});
|
|
23952
|
+
};
|
|
23953
|
+
const yAxis = {
|
|
23954
|
+
beginAtZero: true, // the origin of the y axis is always zero
|
|
23955
|
+
ticks: {
|
|
23956
|
+
color: fontColor,
|
|
23957
|
+
callback: formatCallback,
|
|
23958
|
+
},
|
|
23959
|
+
};
|
|
23960
|
+
const { useLeftAxis, useRightAxis } = getDefinedAxis(chart.getDefinition());
|
|
23961
|
+
if (useLeftAxis) {
|
|
23962
|
+
config.options.scales.y = {
|
|
23963
|
+
...yAxis,
|
|
23964
|
+
position: "left",
|
|
23965
|
+
title: getChartAxisTitleRuntime(chart.axesDesign?.y),
|
|
23966
|
+
};
|
|
23967
|
+
}
|
|
23968
|
+
if (useRightAxis) {
|
|
23969
|
+
config.options.scales.y1 = {
|
|
23970
|
+
...yAxis,
|
|
23971
|
+
position: "right",
|
|
23972
|
+
title: getChartAxisTitleRuntime(chart.axesDesign?.y1),
|
|
23973
|
+
};
|
|
23974
|
+
}
|
|
23975
|
+
if ("stacked" in chart && chart.stacked) {
|
|
23976
|
+
if (useLeftAxis) {
|
|
23977
|
+
// @ts-ignore chart.js type is broken
|
|
23978
|
+
config.options.scales.y.stacked = true;
|
|
23979
|
+
}
|
|
23980
|
+
if (useRightAxis) {
|
|
23981
|
+
// @ts-ignore chart.js type is broken
|
|
23982
|
+
config.options.scales.y1.stacked = true;
|
|
23983
|
+
}
|
|
23984
|
+
}
|
|
23985
|
+
config.options.plugins.chartShowValuesPlugin = {
|
|
23986
|
+
showValues: chart.showValues,
|
|
23987
|
+
background: chart.background,
|
|
23988
|
+
callback: formatCallback,
|
|
23989
|
+
};
|
|
23990
|
+
return config;
|
|
23991
|
+
}
|
|
23992
|
+
function createLineOrScatterChartRuntime(chart, getters) {
|
|
23993
|
+
const axisType = getChartAxisType(chart, getters);
|
|
23994
|
+
const labelValues = getChartLabelValues(getters, chart.dataSets, chart.labelRange);
|
|
23995
|
+
let labels = axisType === "linear" ? labelValues.values : labelValues.formattedValues;
|
|
23996
|
+
let dataSetsValues = getChartDatasetValues(getters, chart.dataSets);
|
|
23997
|
+
if (chart.dataSetsHaveTitle &&
|
|
23998
|
+
dataSetsValues[0] &&
|
|
23999
|
+
labels.length > dataSetsValues[0].data.length) {
|
|
24000
|
+
labels.shift();
|
|
24001
|
+
}
|
|
24002
|
+
({ labels, dataSetsValues } = filterEmptyDataPoints(labels, dataSetsValues));
|
|
24003
|
+
if (axisType === "time") {
|
|
24004
|
+
({ labels, dataSetsValues } = fixEmptyLabelsForDateCharts(labels, dataSetsValues));
|
|
24005
|
+
}
|
|
24006
|
+
if (chart.aggregated) {
|
|
24007
|
+
({ labels, dataSetsValues } = aggregateDataForLabels(labels, dataSetsValues));
|
|
24008
|
+
}
|
|
24009
|
+
const locale = getters.getLocale();
|
|
24010
|
+
const truncateLabels = axisType === "category";
|
|
24011
|
+
const dataSetFormat = getChartDatasetFormat(getters, chart.dataSets);
|
|
24012
|
+
const options = { format: dataSetFormat, locale, truncateLabels };
|
|
24013
|
+
const config = getLineOrScatterConfiguration(chart, labels, options);
|
|
24014
|
+
const labelFormat = getChartLabelFormat(getters, chart.labelRange);
|
|
24015
|
+
if (axisType === "time") {
|
|
24016
|
+
const axis = {
|
|
24017
|
+
type: "time",
|
|
24018
|
+
time: getChartTimeOptions(labels, labelFormat, locale),
|
|
24019
|
+
};
|
|
24020
|
+
Object.assign(config.options.scales.x, axis);
|
|
24021
|
+
config.options.scales.x.ticks.maxTicksLimit = 15;
|
|
24022
|
+
}
|
|
24023
|
+
else if (axisType === "linear") {
|
|
24024
|
+
config.options.scales.x.type = "linear";
|
|
24025
|
+
config.options.scales.x.ticks.callback = (value) => formatValue(value, { format: labelFormat, locale });
|
|
24026
|
+
config.options.plugins.tooltip.callbacks.title = (tooltipItem) => {
|
|
24027
|
+
return formatValue(tooltipItem[0].parsed.x || tooltipItem[0].label, {
|
|
24028
|
+
locale,
|
|
24029
|
+
format: labelFormat,
|
|
24030
|
+
});
|
|
24031
|
+
};
|
|
24032
|
+
}
|
|
24033
|
+
const areaChart = "fillArea" in chart ? chart.fillArea : false;
|
|
24034
|
+
const stackedChart = "stacked" in chart ? chart.stacked : false;
|
|
24035
|
+
const cumulative = "cumulative" in chart ? chart.cumulative : false;
|
|
24036
|
+
const colors = new ColorGenerator();
|
|
24037
|
+
const definition = chart.getDefinition();
|
|
24038
|
+
for (let [index, { label, data }] of dataSetsValues.entries()) {
|
|
24039
|
+
const color = colors.next();
|
|
24040
|
+
let backgroundRGBA = colorToRGBA(color);
|
|
24041
|
+
if (areaChart) {
|
|
24042
|
+
backgroundRGBA.a = LINE_FILL_TRANSPARENCY;
|
|
24043
|
+
}
|
|
24044
|
+
if (cumulative) {
|
|
24045
|
+
let accumulator = 0;
|
|
24046
|
+
data = data.map((value) => {
|
|
24047
|
+
if (!isNaN(value)) {
|
|
24048
|
+
accumulator += parseFloat(value);
|
|
24049
|
+
return accumulator;
|
|
24050
|
+
}
|
|
24051
|
+
return value;
|
|
24052
|
+
});
|
|
24053
|
+
}
|
|
24054
|
+
if (["linear", "time"].includes(axisType)) {
|
|
24055
|
+
// Replace empty string labels by undefined to make sure chartJS doesn't decide that "" is the same as 0
|
|
24056
|
+
data = data.map((y, index) => ({ x: labels[index] || undefined, y }));
|
|
24057
|
+
}
|
|
24058
|
+
const backgroundColor = rgbaToHex(backgroundRGBA);
|
|
24059
|
+
const dataset = {
|
|
24060
|
+
label,
|
|
24061
|
+
data,
|
|
24062
|
+
tension: 0, // 0 -> render straight lines, which is much faster
|
|
24063
|
+
borderColor: color,
|
|
24064
|
+
backgroundColor,
|
|
24065
|
+
pointBackgroundColor: color,
|
|
24066
|
+
fill: areaChart ? getFillingMode(index, stackedChart) : false,
|
|
24067
|
+
};
|
|
24068
|
+
config.data.datasets.push(dataset);
|
|
24069
|
+
}
|
|
24070
|
+
for (const [index, dataset] of config.data.datasets.entries()) {
|
|
24071
|
+
if (definition.dataSets?.[index]?.backgroundColor) {
|
|
24072
|
+
const color = definition.dataSets[index].backgroundColor;
|
|
24073
|
+
dataset.backgroundColor = color;
|
|
24074
|
+
dataset.borderColor = color;
|
|
24075
|
+
//@ts-ignore
|
|
24076
|
+
dataset.pointBackgroundColor = color;
|
|
24077
|
+
}
|
|
24078
|
+
if (definition.dataSets?.[index]?.label) {
|
|
24079
|
+
const label = definition.dataSets[index].label;
|
|
24080
|
+
dataset.label = label;
|
|
24081
|
+
}
|
|
24082
|
+
if (definition.dataSets?.[index]?.yAxisId) {
|
|
24083
|
+
dataset["yAxisID"] = definition.dataSets[index].yAxisId;
|
|
24084
|
+
}
|
|
24085
|
+
}
|
|
24086
|
+
return {
|
|
24087
|
+
chartJsConfig: config,
|
|
24088
|
+
background: chart.background || BACKGROUND_CHART_COLOR,
|
|
24089
|
+
dataSetsValues,
|
|
24090
|
+
labelValues,
|
|
24091
|
+
dataSetFormat,
|
|
24092
|
+
labelFormat,
|
|
24093
|
+
};
|
|
24094
|
+
}
|
|
24095
|
+
|
|
24096
|
+
class ComboChart extends AbstractChart {
|
|
24097
|
+
dataSets;
|
|
24098
|
+
labelRange;
|
|
24099
|
+
background;
|
|
24100
|
+
legendPosition;
|
|
24101
|
+
aggregated;
|
|
24102
|
+
dataSetsHaveTitle;
|
|
24103
|
+
dataSetDesign;
|
|
24104
|
+
axesDesign;
|
|
24105
|
+
type = "combo";
|
|
24106
|
+
showValues;
|
|
24107
|
+
constructor(definition, sheetId, getters) {
|
|
24108
|
+
super(definition, sheetId, getters);
|
|
24109
|
+
this.dataSets = createDataSets(getters, definition.dataSets, sheetId, definition.dataSetsHaveTitle);
|
|
24110
|
+
this.labelRange = createValidRange(getters, sheetId, definition.labelRange);
|
|
24111
|
+
this.background = definition.background;
|
|
24112
|
+
this.legendPosition = definition.legendPosition;
|
|
24113
|
+
this.aggregated = definition.aggregated;
|
|
24114
|
+
this.dataSetsHaveTitle = definition.dataSetsHaveTitle;
|
|
24115
|
+
this.dataSetDesign = definition.dataSets;
|
|
24116
|
+
this.axesDesign = definition.axesDesign;
|
|
24117
|
+
this.showValues = definition.showValues;
|
|
24118
|
+
}
|
|
24119
|
+
static transformDefinition(definition, executed) {
|
|
24120
|
+
return transformChartDefinitionWithDataSetsWithZone(definition, executed);
|
|
24121
|
+
}
|
|
24122
|
+
static validateChartDefinition(validator, definition) {
|
|
24123
|
+
return validator.checkValidations(definition, checkDataset, checkLabelRange);
|
|
24124
|
+
}
|
|
24125
|
+
getContextCreation() {
|
|
24126
|
+
const range = [];
|
|
24127
|
+
for (const [i, dataSet] of this.dataSets.entries()) {
|
|
24128
|
+
range.push({
|
|
24129
|
+
...this.dataSetDesign?.[i],
|
|
24130
|
+
dataRange: this.getters.getRangeString(dataSet.dataRange, this.sheetId),
|
|
24131
|
+
});
|
|
24132
|
+
}
|
|
24133
|
+
return {
|
|
24134
|
+
...this,
|
|
24135
|
+
range,
|
|
24136
|
+
auxiliaryRange: this.labelRange
|
|
24137
|
+
? this.getters.getRangeString(this.labelRange, this.sheetId)
|
|
24138
|
+
: undefined,
|
|
24139
|
+
};
|
|
24140
|
+
}
|
|
24141
|
+
getDefinition() {
|
|
24142
|
+
return this.getDefinitionWithSpecificDataSets(this.dataSets, this.labelRange);
|
|
24143
|
+
}
|
|
24144
|
+
getDefinitionWithSpecificDataSets(dataSets, labelRange, targetSheetId) {
|
|
24145
|
+
const ranges = [];
|
|
24146
|
+
for (const [i, dataSet] of dataSets.entries()) {
|
|
24147
|
+
ranges.push({
|
|
24148
|
+
...this.dataSetDesign?.[i],
|
|
24149
|
+
dataRange: this.getters.getRangeString(dataSet.dataRange, targetSheetId || this.sheetId),
|
|
24150
|
+
});
|
|
24151
|
+
}
|
|
24152
|
+
return {
|
|
24153
|
+
type: "combo",
|
|
24154
|
+
dataSetsHaveTitle: dataSets.length ? Boolean(dataSets[0].labelCell) : false,
|
|
24155
|
+
background: this.background,
|
|
24156
|
+
dataSets: ranges,
|
|
24157
|
+
legendPosition: this.legendPosition,
|
|
24158
|
+
labelRange: labelRange
|
|
24159
|
+
? this.getters.getRangeString(labelRange, targetSheetId || this.sheetId)
|
|
24160
|
+
: undefined,
|
|
24161
|
+
title: this.title,
|
|
24162
|
+
aggregated: this.aggregated,
|
|
24163
|
+
axesDesign: this.axesDesign,
|
|
24164
|
+
showValues: this.showValues,
|
|
24165
|
+
};
|
|
24166
|
+
}
|
|
24167
|
+
getDefinitionForExcel() {
|
|
24168
|
+
// Excel does not support aggregating labels
|
|
24169
|
+
if (this.aggregated) {
|
|
24170
|
+
return undefined;
|
|
24171
|
+
}
|
|
24172
|
+
const dataSets = this.dataSets
|
|
24173
|
+
.map((ds) => toExcelDataset(this.getters, ds))
|
|
24174
|
+
.filter((ds) => ds.range !== "" && ds.range !== CellErrorType.InvalidReference);
|
|
24175
|
+
const labelRange = toExcelLabelRange(this.getters, this.labelRange, shouldRemoveFirstLabel(this.labelRange, this.dataSets[0], this.dataSetsHaveTitle));
|
|
24176
|
+
const definition = this.getDefinition();
|
|
24177
|
+
return {
|
|
24178
|
+
...definition,
|
|
24179
|
+
backgroundColor: toXlsxHexColor(this.background || BACKGROUND_CHART_COLOR),
|
|
24180
|
+
fontColor: toXlsxHexColor(chartFontColor(this.background)),
|
|
24181
|
+
dataSets,
|
|
24182
|
+
labelRange,
|
|
24183
|
+
verticalAxis: getDefinedAxis(definition),
|
|
24184
|
+
};
|
|
24185
|
+
}
|
|
24186
|
+
updateRanges(applyChange) {
|
|
24187
|
+
const { dataSets, labelRange, isStale } = updateChartRangesWithDataSets(this.getters, applyChange, this.dataSets, this.labelRange);
|
|
24188
|
+
if (!isStale) {
|
|
24189
|
+
return this;
|
|
24190
|
+
}
|
|
24191
|
+
const definition = this.getDefinitionWithSpecificDataSets(dataSets, labelRange);
|
|
24192
|
+
return new ComboChart(definition, this.sheetId, this.getters);
|
|
24193
|
+
}
|
|
24194
|
+
static getDefinitionFromContextCreation(context) {
|
|
24195
|
+
return {
|
|
24196
|
+
background: context.background,
|
|
24197
|
+
dataSets: context.range ?? [],
|
|
24198
|
+
dataSetsHaveTitle: context.dataSetsHaveTitle ?? false,
|
|
24199
|
+
aggregated: context.aggregated,
|
|
24200
|
+
legendPosition: context.legendPosition ?? "top",
|
|
24201
|
+
title: context.title || { text: "" },
|
|
24202
|
+
labelRange: context.auxiliaryRange || undefined,
|
|
24203
|
+
type: "combo",
|
|
24204
|
+
axesDesign: context.axesDesign,
|
|
24205
|
+
showValues: context.showValues,
|
|
24206
|
+
};
|
|
24207
|
+
}
|
|
24208
|
+
copyForSheetId(sheetId) {
|
|
24209
|
+
const dataSets = copyDataSetsWithNewSheetId(this.sheetId, sheetId, this.dataSets);
|
|
24210
|
+
const labelRange = copyLabelRangeWithNewSheetId(this.sheetId, sheetId, this.labelRange);
|
|
24211
|
+
const definition = this.getDefinitionWithSpecificDataSets(dataSets, labelRange, sheetId);
|
|
24212
|
+
return new ComboChart(definition, sheetId, this.getters);
|
|
24213
|
+
}
|
|
24214
|
+
copyInSheetId(sheetId) {
|
|
24215
|
+
const definition = this.getDefinitionWithSpecificDataSets(this.dataSets, this.labelRange, sheetId);
|
|
24216
|
+
return new ComboChart(definition, sheetId, this.getters);
|
|
24217
|
+
}
|
|
24218
|
+
}
|
|
24219
|
+
function createComboChartRuntime(chart, getters) {
|
|
24220
|
+
const mainDataSetFormat = chart.dataSets.length
|
|
24221
|
+
? getChartDatasetFormat(getters, [chart.dataSets[0]])
|
|
24222
|
+
: undefined;
|
|
24223
|
+
const lineDataSetsFormat = getChartDatasetFormat(getters, chart.dataSets.slice(1));
|
|
24224
|
+
const locale = getters.getLocale();
|
|
24225
|
+
const labelValues = getChartLabelValues(getters, chart.dataSets, chart.labelRange);
|
|
24226
|
+
let labels = labelValues.formattedValues;
|
|
24227
|
+
let dataSetsValues = getChartDatasetValues(getters, chart.dataSets);
|
|
24228
|
+
if (chart.dataSetsHaveTitle &&
|
|
24229
|
+
dataSetsValues[0] &&
|
|
24230
|
+
labels.length > dataSetsValues[0].data.length) {
|
|
24231
|
+
labels.shift();
|
|
24232
|
+
}
|
|
24233
|
+
({ labels, dataSetsValues } = filterEmptyDataPoints(labels, dataSetsValues));
|
|
24234
|
+
if (chart.aggregated) {
|
|
24235
|
+
({ labels, dataSetsValues } = aggregateDataForLabels(labels, dataSetsValues));
|
|
24236
|
+
}
|
|
24237
|
+
const localeFormat = { format: mainDataSetFormat, locale };
|
|
24238
|
+
const fontColor = chartFontColor(chart.background);
|
|
24239
|
+
const config = getDefaultChartJsRuntime(chart, labels, fontColor, localeFormat);
|
|
24240
|
+
const legend = {
|
|
24241
|
+
labels: { color: fontColor },
|
|
24242
|
+
reverse: true,
|
|
24243
|
+
};
|
|
24244
|
+
if ((!chart.labelRange && chart.dataSets.length === 1) || chart.legendPosition === "none") {
|
|
24245
|
+
legend.display = false;
|
|
24246
|
+
}
|
|
24247
|
+
else {
|
|
24248
|
+
legend.position = chart.legendPosition;
|
|
24249
|
+
}
|
|
24250
|
+
config.options.plugins.legend = { ...config.options.plugins?.legend, ...legend };
|
|
24251
|
+
config.options.layout = {
|
|
24252
|
+
padding: { left: 20, right: 20, top: chart.title ? 10 : 25, bottom: 10 },
|
|
24253
|
+
};
|
|
24254
|
+
config.options.scales = {
|
|
24255
|
+
x: {
|
|
24256
|
+
ticks: {
|
|
24257
|
+
padding: 5,
|
|
24258
|
+
color: fontColor,
|
|
24259
|
+
},
|
|
24260
|
+
title: getChartAxisTitleRuntime(chart.axesDesign?.x),
|
|
24261
|
+
},
|
|
24262
|
+
};
|
|
24263
|
+
const formatCallback = (format) => {
|
|
24264
|
+
return (value) => {
|
|
24265
|
+
value = Number(value);
|
|
24266
|
+
if (isNaN(value))
|
|
24267
|
+
return value;
|
|
24268
|
+
const { locale } = localeFormat;
|
|
24269
|
+
return formatValue(value, {
|
|
24270
|
+
locale,
|
|
24271
|
+
format: !format && Math.abs(value) >= 1000 ? "#,##" : format,
|
|
24272
|
+
});
|
|
24273
|
+
};
|
|
24274
|
+
};
|
|
24275
|
+
const leftVerticalAxis = {
|
|
24276
|
+
beginAtZero: true, // the origin of the y axis is always zero
|
|
24277
|
+
ticks: {
|
|
24278
|
+
color: fontColor,
|
|
24279
|
+
callback: formatCallback(mainDataSetFormat),
|
|
24280
|
+
},
|
|
24281
|
+
};
|
|
24282
|
+
const rightVerticalAxis = {
|
|
23950
24283
|
beginAtZero: true, // the origin of the y axis is always zero
|
|
23951
24284
|
ticks: {
|
|
23952
24285
|
color: fontColor,
|
|
23953
|
-
callback: (
|
|
23954
|
-
value = Number(value);
|
|
23955
|
-
if (isNaN(value))
|
|
23956
|
-
return value;
|
|
23957
|
-
const { locale, format } = options;
|
|
23958
|
-
return formatValue(value, {
|
|
23959
|
-
locale,
|
|
23960
|
-
format: !format && Math.abs(value) >= 1000 ? "#,##" : format,
|
|
23961
|
-
});
|
|
23962
|
-
},
|
|
24286
|
+
callback: formatCallback(lineDataSetsFormat),
|
|
23963
24287
|
},
|
|
23964
24288
|
};
|
|
23965
|
-
const
|
|
24289
|
+
const definition = chart.getDefinition();
|
|
24290
|
+
const { useLeftAxis, useRightAxis } = getDefinedAxis(definition);
|
|
23966
24291
|
if (useLeftAxis) {
|
|
23967
24292
|
config.options.scales.y = {
|
|
23968
|
-
...
|
|
24293
|
+
...leftVerticalAxis,
|
|
23969
24294
|
position: "left",
|
|
23970
24295
|
title: getChartAxisTitleRuntime(chart.axesDesign?.y),
|
|
23971
24296
|
};
|
|
23972
24297
|
}
|
|
23973
24298
|
if (useRightAxis) {
|
|
23974
24299
|
config.options.scales.y1 = {
|
|
23975
|
-
...
|
|
24300
|
+
...rightVerticalAxis,
|
|
23976
24301
|
position: "right",
|
|
24302
|
+
grid: {
|
|
24303
|
+
display: false,
|
|
24304
|
+
},
|
|
23977
24305
|
title: getChartAxisTitleRuntime(chart.axesDesign?.y1),
|
|
23978
24306
|
};
|
|
23979
24307
|
}
|
|
23980
|
-
|
|
23981
|
-
|
|
23982
|
-
|
|
23983
|
-
|
|
24308
|
+
config.options.plugins.chartShowValuesPlugin = {
|
|
24309
|
+
showValues: chart.showValues,
|
|
24310
|
+
background: chart.background,
|
|
24311
|
+
callback: formatCallback(mainDataSetFormat),
|
|
24312
|
+
};
|
|
24313
|
+
const colors = new ColorGenerator();
|
|
24314
|
+
for (let [index, { label, data }] of dataSetsValues.entries()) {
|
|
24315
|
+
const design = definition.dataSets[index];
|
|
24316
|
+
const color = colors.next();
|
|
24317
|
+
const dataset = {
|
|
24318
|
+
label: design?.label ?? label,
|
|
24319
|
+
data,
|
|
24320
|
+
borderColor: design?.backgroundColor ?? color,
|
|
24321
|
+
backgroundColor: design.backgroundColor ?? color,
|
|
24322
|
+
yAxisID: design?.yAxisId ?? "y",
|
|
24323
|
+
type: index === 0 ? "bar" : "line",
|
|
24324
|
+
order: -index,
|
|
24325
|
+
};
|
|
24326
|
+
config.data.datasets.push(dataset);
|
|
24327
|
+
}
|
|
24328
|
+
return { chartJsConfig: config, background: chart.background || BACKGROUND_CHART_COLOR };
|
|
24329
|
+
}
|
|
24330
|
+
|
|
24331
|
+
function isDataRangeValid(definition) {
|
|
24332
|
+
return definition.dataRange && !rangeReference.test(definition.dataRange)
|
|
24333
|
+
? "InvalidGaugeDataRange" /* CommandResult.InvalidGaugeDataRange */
|
|
24334
|
+
: "Success" /* CommandResult.Success */;
|
|
24335
|
+
}
|
|
24336
|
+
function checkRangeLimits(check, batchValidations) {
|
|
24337
|
+
return batchValidations((definition) => {
|
|
24338
|
+
if (definition.sectionRule) {
|
|
24339
|
+
return check(definition.sectionRule.rangeMin, "rangeMin");
|
|
23984
24340
|
}
|
|
23985
|
-
|
|
23986
|
-
|
|
23987
|
-
|
|
24341
|
+
return "Success" /* CommandResult.Success */;
|
|
24342
|
+
}, (definition) => {
|
|
24343
|
+
if (definition.sectionRule) {
|
|
24344
|
+
return check(definition.sectionRule.rangeMax, "rangeMax");
|
|
24345
|
+
}
|
|
24346
|
+
return "Success" /* CommandResult.Success */;
|
|
24347
|
+
});
|
|
24348
|
+
}
|
|
24349
|
+
function checkInflectionPointsValue(check, batchValidations) {
|
|
24350
|
+
return batchValidations((definition) => {
|
|
24351
|
+
if (definition.sectionRule) {
|
|
24352
|
+
return check(definition.sectionRule.lowerInflectionPoint.value, "lowerInflectionPointValue");
|
|
24353
|
+
}
|
|
24354
|
+
return "Success" /* CommandResult.Success */;
|
|
24355
|
+
}, (definition) => {
|
|
24356
|
+
if (definition.sectionRule) {
|
|
24357
|
+
return check(definition.sectionRule.upperInflectionPoint.value, "upperInflectionPointValue");
|
|
24358
|
+
}
|
|
24359
|
+
return "Success" /* CommandResult.Success */;
|
|
24360
|
+
});
|
|
24361
|
+
}
|
|
24362
|
+
function checkRangeMinBiggerThanRangeMax(definition) {
|
|
24363
|
+
if (definition.sectionRule) {
|
|
24364
|
+
if (Number(definition.sectionRule.rangeMin) >= Number(definition.sectionRule.rangeMax)) {
|
|
24365
|
+
return "GaugeRangeMinBiggerThanRangeMax" /* CommandResult.GaugeRangeMinBiggerThanRangeMax */;
|
|
23988
24366
|
}
|
|
23989
24367
|
}
|
|
23990
|
-
return
|
|
24368
|
+
return "Success" /* CommandResult.Success */;
|
|
23991
24369
|
}
|
|
23992
|
-
function
|
|
23993
|
-
|
|
23994
|
-
|
|
23995
|
-
|
|
23996
|
-
|
|
23997
|
-
|
|
23998
|
-
|
|
23999
|
-
|
|
24000
|
-
labels.shift();
|
|
24370
|
+
function checkEmpty(value, valueName) {
|
|
24371
|
+
if (value === "") {
|
|
24372
|
+
switch (valueName) {
|
|
24373
|
+
case "rangeMin":
|
|
24374
|
+
return "EmptyGaugeRangeMin" /* CommandResult.EmptyGaugeRangeMin */;
|
|
24375
|
+
case "rangeMax":
|
|
24376
|
+
return "EmptyGaugeRangeMax" /* CommandResult.EmptyGaugeRangeMax */;
|
|
24377
|
+
}
|
|
24001
24378
|
}
|
|
24002
|
-
|
|
24003
|
-
|
|
24004
|
-
|
|
24379
|
+
return "Success" /* CommandResult.Success */;
|
|
24380
|
+
}
|
|
24381
|
+
function checkNaN(value, valueName) {
|
|
24382
|
+
if (isNaN(value)) {
|
|
24383
|
+
switch (valueName) {
|
|
24384
|
+
case "rangeMin":
|
|
24385
|
+
return "GaugeRangeMinNaN" /* CommandResult.GaugeRangeMinNaN */;
|
|
24386
|
+
case "rangeMax":
|
|
24387
|
+
return "GaugeRangeMaxNaN" /* CommandResult.GaugeRangeMaxNaN */;
|
|
24388
|
+
case "lowerInflectionPointValue":
|
|
24389
|
+
return "GaugeLowerInflectionPointNaN" /* CommandResult.GaugeLowerInflectionPointNaN */;
|
|
24390
|
+
case "upperInflectionPointValue":
|
|
24391
|
+
return "GaugeUpperInflectionPointNaN" /* CommandResult.GaugeUpperInflectionPointNaN */;
|
|
24392
|
+
}
|
|
24005
24393
|
}
|
|
24006
|
-
|
|
24007
|
-
|
|
24394
|
+
return "Success" /* CommandResult.Success */;
|
|
24395
|
+
}
|
|
24396
|
+
class GaugeChart extends AbstractChart {
|
|
24397
|
+
dataRange;
|
|
24398
|
+
sectionRule;
|
|
24399
|
+
background;
|
|
24400
|
+
type = "gauge";
|
|
24401
|
+
constructor(definition, sheetId, getters) {
|
|
24402
|
+
super(definition, sheetId, getters);
|
|
24403
|
+
this.dataRange = createValidRange(this.getters, this.sheetId, definition.dataRange);
|
|
24404
|
+
this.sectionRule = definition.sectionRule;
|
|
24405
|
+
this.background = definition.background;
|
|
24008
24406
|
}
|
|
24009
|
-
|
|
24010
|
-
|
|
24011
|
-
|
|
24012
|
-
|
|
24013
|
-
|
|
24014
|
-
|
|
24015
|
-
|
|
24016
|
-
|
|
24017
|
-
|
|
24018
|
-
|
|
24407
|
+
static validateChartDefinition(validator, definition) {
|
|
24408
|
+
return validator.checkValidations(definition, isDataRangeValid, validator.chainValidations(checkRangeLimits(checkEmpty, validator.batchValidations), checkRangeLimits(checkNaN, validator.batchValidations), checkRangeMinBiggerThanRangeMax), validator.chainValidations(checkInflectionPointsValue(checkNaN, validator.batchValidations)));
|
|
24409
|
+
}
|
|
24410
|
+
static transformDefinition(definition, executed) {
|
|
24411
|
+
let dataRangeZone;
|
|
24412
|
+
if (definition.dataRange) {
|
|
24413
|
+
dataRangeZone = transformZone(toUnboundedZone(definition.dataRange), executed);
|
|
24414
|
+
}
|
|
24415
|
+
return {
|
|
24416
|
+
...definition,
|
|
24417
|
+
dataRange: dataRangeZone ? zoneToXc(dataRangeZone) : undefined,
|
|
24019
24418
|
};
|
|
24020
|
-
Object.assign(config.options.scales.x, axis);
|
|
24021
|
-
config.options.scales.x.ticks.maxTicksLimit = 15;
|
|
24022
24419
|
}
|
|
24023
|
-
|
|
24024
|
-
|
|
24025
|
-
|
|
24026
|
-
|
|
24027
|
-
|
|
24028
|
-
|
|
24029
|
-
|
|
24030
|
-
|
|
24420
|
+
static getDefinitionFromContextCreation(context) {
|
|
24421
|
+
return {
|
|
24422
|
+
background: context.background,
|
|
24423
|
+
title: context.title || { text: "" },
|
|
24424
|
+
type: "gauge",
|
|
24425
|
+
dataRange: context.range ? context.range[0].dataRange : undefined,
|
|
24426
|
+
sectionRule: {
|
|
24427
|
+
colors: {
|
|
24428
|
+
lowerColor: DEFAULT_GAUGE_LOWER_COLOR,
|
|
24429
|
+
middleColor: DEFAULT_GAUGE_MIDDLE_COLOR,
|
|
24430
|
+
upperColor: DEFAULT_GAUGE_UPPER_COLOR,
|
|
24431
|
+
},
|
|
24432
|
+
rangeMin: "0",
|
|
24433
|
+
rangeMax: "100",
|
|
24434
|
+
lowerInflectionPoint: {
|
|
24435
|
+
type: "percentage",
|
|
24436
|
+
value: "15",
|
|
24437
|
+
},
|
|
24438
|
+
upperInflectionPoint: {
|
|
24439
|
+
type: "percentage",
|
|
24440
|
+
value: "40",
|
|
24441
|
+
},
|
|
24442
|
+
},
|
|
24031
24443
|
};
|
|
24032
24444
|
}
|
|
24033
|
-
|
|
24034
|
-
|
|
24035
|
-
|
|
24036
|
-
|
|
24037
|
-
|
|
24038
|
-
|
|
24039
|
-
|
|
24040
|
-
|
|
24041
|
-
|
|
24042
|
-
|
|
24043
|
-
|
|
24044
|
-
|
|
24045
|
-
|
|
24046
|
-
|
|
24047
|
-
|
|
24048
|
-
|
|
24049
|
-
|
|
24050
|
-
|
|
24051
|
-
|
|
24052
|
-
|
|
24053
|
-
|
|
24054
|
-
return value;
|
|
24055
|
-
});
|
|
24056
|
-
}
|
|
24057
|
-
const backgroundColor = rgbaToHex(backgroundRGBA);
|
|
24058
|
-
const dataset = {
|
|
24059
|
-
label,
|
|
24060
|
-
data,
|
|
24061
|
-
tension: 0, // 0 -> render straight lines, which is much faster
|
|
24062
|
-
borderColor: color,
|
|
24063
|
-
backgroundColor,
|
|
24064
|
-
pointBackgroundColor: color,
|
|
24065
|
-
fill: stacked ? getFillingMode(index) : false,
|
|
24445
|
+
copyForSheetId(sheetId) {
|
|
24446
|
+
const dataRange = copyLabelRangeWithNewSheetId(this.sheetId, sheetId, this.dataRange);
|
|
24447
|
+
const definition = this.getDefinitionWithSpecificRanges(dataRange, sheetId);
|
|
24448
|
+
return new GaugeChart(definition, sheetId, this.getters);
|
|
24449
|
+
}
|
|
24450
|
+
copyInSheetId(sheetId) {
|
|
24451
|
+
const definition = this.getDefinitionWithSpecificRanges(this.dataRange, sheetId);
|
|
24452
|
+
return new GaugeChart(definition, sheetId, this.getters);
|
|
24453
|
+
}
|
|
24454
|
+
getDefinition() {
|
|
24455
|
+
return this.getDefinitionWithSpecificRanges(this.dataRange);
|
|
24456
|
+
}
|
|
24457
|
+
getDefinitionWithSpecificRanges(dataRange, targetSheetId) {
|
|
24458
|
+
return {
|
|
24459
|
+
background: this.background,
|
|
24460
|
+
sectionRule: this.sectionRule,
|
|
24461
|
+
title: this.title,
|
|
24462
|
+
type: "gauge",
|
|
24463
|
+
dataRange: dataRange
|
|
24464
|
+
? this.getters.getRangeString(dataRange, targetSheetId || this.sheetId)
|
|
24465
|
+
: undefined,
|
|
24066
24466
|
};
|
|
24067
|
-
config.data.datasets.push(dataset);
|
|
24068
24467
|
}
|
|
24069
|
-
|
|
24070
|
-
|
|
24071
|
-
|
|
24072
|
-
|
|
24073
|
-
|
|
24074
|
-
|
|
24075
|
-
|
|
24076
|
-
|
|
24077
|
-
|
|
24078
|
-
|
|
24079
|
-
|
|
24468
|
+
getDefinitionForExcel() {
|
|
24469
|
+
// This kind of graph is not exportable in Excel
|
|
24470
|
+
return undefined;
|
|
24471
|
+
}
|
|
24472
|
+
getContextCreation() {
|
|
24473
|
+
return {
|
|
24474
|
+
...this,
|
|
24475
|
+
range: this.dataRange
|
|
24476
|
+
? [{ dataRange: this.getters.getRangeString(this.dataRange, this.sheetId) }]
|
|
24477
|
+
: undefined,
|
|
24478
|
+
};
|
|
24479
|
+
}
|
|
24480
|
+
updateRanges(applyChange) {
|
|
24481
|
+
const range = adaptChartRange(this.dataRange, applyChange);
|
|
24482
|
+
if (this.dataRange === range) {
|
|
24483
|
+
return this;
|
|
24080
24484
|
}
|
|
24081
|
-
|
|
24082
|
-
|
|
24485
|
+
const definition = this.getDefinitionWithSpecificRanges(range);
|
|
24486
|
+
return new GaugeChart(definition, this.sheetId, this.getters);
|
|
24487
|
+
}
|
|
24488
|
+
}
|
|
24489
|
+
function createGaugeChartRuntime(chart, getters) {
|
|
24490
|
+
const locale = getters.getLocale();
|
|
24491
|
+
const chartColors = chart.sectionRule.colors;
|
|
24492
|
+
let gaugeValue = undefined;
|
|
24493
|
+
let formattedValue = undefined;
|
|
24494
|
+
let format = undefined;
|
|
24495
|
+
const dataRange = chart.dataRange;
|
|
24496
|
+
if (dataRange !== undefined) {
|
|
24497
|
+
const cell = getters.getEvaluatedCell({
|
|
24498
|
+
sheetId: dataRange.sheetId,
|
|
24499
|
+
col: dataRange.zone.left,
|
|
24500
|
+
row: dataRange.zone.top,
|
|
24501
|
+
});
|
|
24502
|
+
if (cell.type === CellValueType.number) {
|
|
24503
|
+
gaugeValue = cell.value;
|
|
24504
|
+
formattedValue = cell.formattedValue;
|
|
24505
|
+
format = cell.format;
|
|
24083
24506
|
}
|
|
24084
24507
|
}
|
|
24508
|
+
const minValue = Number(chart.sectionRule.rangeMin);
|
|
24509
|
+
const maxValue = Number(chart.sectionRule.rangeMax);
|
|
24510
|
+
const lowerPoint = chart.sectionRule.lowerInflectionPoint;
|
|
24511
|
+
const upperPoint = chart.sectionRule.upperInflectionPoint;
|
|
24512
|
+
const lowerPointValue = getSectionThresholdValue(lowerPoint, minValue, maxValue);
|
|
24513
|
+
const upperPointValue = getSectionThresholdValue(upperPoint, minValue, maxValue);
|
|
24514
|
+
const inflectionValues = [];
|
|
24515
|
+
const colors = [];
|
|
24516
|
+
if (lowerPointValue !== undefined) {
|
|
24517
|
+
inflectionValues.push({
|
|
24518
|
+
value: lowerPointValue,
|
|
24519
|
+
label: formatValue(lowerPointValue, { locale, format }),
|
|
24520
|
+
});
|
|
24521
|
+
colors.push(chartColors.lowerColor);
|
|
24522
|
+
}
|
|
24523
|
+
if (upperPointValue !== undefined && upperPointValue !== lowerPointValue) {
|
|
24524
|
+
inflectionValues.push({
|
|
24525
|
+
value: upperPointValue,
|
|
24526
|
+
label: formatValue(upperPointValue, { locale, format }),
|
|
24527
|
+
});
|
|
24528
|
+
colors.push(chartColors.middleColor);
|
|
24529
|
+
}
|
|
24530
|
+
if (upperPointValue !== undefined &&
|
|
24531
|
+
lowerPointValue !== undefined &&
|
|
24532
|
+
lowerPointValue > upperPointValue) {
|
|
24533
|
+
inflectionValues.reverse();
|
|
24534
|
+
colors.reverse();
|
|
24535
|
+
}
|
|
24536
|
+
colors.push(chartColors.upperColor);
|
|
24085
24537
|
return {
|
|
24086
|
-
|
|
24087
|
-
|
|
24088
|
-
|
|
24089
|
-
|
|
24090
|
-
|
|
24091
|
-
|
|
24538
|
+
background: getters.getStyleOfSingleCellChart(chart.background, dataRange).background,
|
|
24539
|
+
title: chart.title ?? { text: "" },
|
|
24540
|
+
minValue: {
|
|
24541
|
+
value: minValue,
|
|
24542
|
+
label: formatValue(minValue, { locale, format }),
|
|
24543
|
+
},
|
|
24544
|
+
maxValue: {
|
|
24545
|
+
value: maxValue,
|
|
24546
|
+
label: formatValue(maxValue, { locale, format }),
|
|
24547
|
+
},
|
|
24548
|
+
gaugeValue: gaugeValue !== undefined && formattedValue
|
|
24549
|
+
? { value: gaugeValue, label: formattedValue }
|
|
24550
|
+
: undefined,
|
|
24551
|
+
inflectionValues,
|
|
24552
|
+
colors,
|
|
24092
24553
|
};
|
|
24093
24554
|
}
|
|
24555
|
+
function getSectionThresholdValue(threshold, minValue, maxValue) {
|
|
24556
|
+
if (threshold.value === "" || isNaN(Number(threshold.value))) {
|
|
24557
|
+
return undefined;
|
|
24558
|
+
}
|
|
24559
|
+
const numberValue = Number(threshold.value);
|
|
24560
|
+
const value = threshold.type === "number"
|
|
24561
|
+
? numberValue
|
|
24562
|
+
: minValue + ((maxValue - minValue) * numberValue) / 100;
|
|
24563
|
+
return clip(value, minValue, maxValue);
|
|
24564
|
+
}
|
|
24094
24565
|
|
|
24095
24566
|
class LineChart extends AbstractChart {
|
|
24096
24567
|
dataSets;
|
|
@@ -24105,6 +24576,8 @@ class LineChart extends AbstractChart {
|
|
|
24105
24576
|
cumulative;
|
|
24106
24577
|
dataSetDesign;
|
|
24107
24578
|
axesDesign;
|
|
24579
|
+
fillArea;
|
|
24580
|
+
showValues;
|
|
24108
24581
|
constructor(definition, sheetId, getters) {
|
|
24109
24582
|
super(definition, sheetId, getters);
|
|
24110
24583
|
this.dataSets = createDataSets(this.getters, definition.dataSets, sheetId, definition.dataSetsHaveTitle);
|
|
@@ -24118,6 +24591,8 @@ class LineChart extends AbstractChart {
|
|
|
24118
24591
|
this.cumulative = definition.cumulative;
|
|
24119
24592
|
this.dataSetDesign = definition.dataSets;
|
|
24120
24593
|
this.axesDesign = definition.axesDesign;
|
|
24594
|
+
this.fillArea = definition.fillArea;
|
|
24595
|
+
this.showValues = definition.showValues;
|
|
24121
24596
|
}
|
|
24122
24597
|
static validateChartDefinition(validator, definition) {
|
|
24123
24598
|
return validator.checkValidations(definition, checkDataset, checkLabelRange);
|
|
@@ -24139,6 +24614,8 @@ class LineChart extends AbstractChart {
|
|
|
24139
24614
|
aggregated: context.aggregated ?? false,
|
|
24140
24615
|
cumulative: context.cumulative ?? false,
|
|
24141
24616
|
axesDesign: context.axesDesign,
|
|
24617
|
+
fillArea: context.fillArea,
|
|
24618
|
+
showValues: context.showValues,
|
|
24142
24619
|
};
|
|
24143
24620
|
}
|
|
24144
24621
|
getDefinition() {
|
|
@@ -24167,6 +24644,8 @@ class LineChart extends AbstractChart {
|
|
|
24167
24644
|
aggregated: this.aggregated,
|
|
24168
24645
|
cumulative: this.cumulative,
|
|
24169
24646
|
axesDesign: this.axesDesign,
|
|
24647
|
+
fillArea: this.fillArea,
|
|
24648
|
+
showValues: this.showValues,
|
|
24170
24649
|
};
|
|
24171
24650
|
}
|
|
24172
24651
|
getContextCreation() {
|
|
@@ -24222,10 +24701,6 @@ class LineChart extends AbstractChart {
|
|
|
24222
24701
|
return new LineChart(definition, sheetId, this.getters);
|
|
24223
24702
|
}
|
|
24224
24703
|
}
|
|
24225
|
-
function createLineChartRuntime(chart, getters) {
|
|
24226
|
-
const { chartJsConfig, background } = createLineOrScatterChartRuntime(chart, getters);
|
|
24227
|
-
return { chartJsConfig, background };
|
|
24228
|
-
}
|
|
24229
24704
|
|
|
24230
24705
|
class PieChart extends AbstractChart {
|
|
24231
24706
|
dataSets;
|
|
@@ -24236,6 +24711,7 @@ class PieChart extends AbstractChart {
|
|
|
24236
24711
|
aggregated;
|
|
24237
24712
|
dataSetsHaveTitle;
|
|
24238
24713
|
isDoughnut;
|
|
24714
|
+
showValues;
|
|
24239
24715
|
constructor(definition, sheetId, getters) {
|
|
24240
24716
|
super(definition, sheetId, getters);
|
|
24241
24717
|
this.dataSets = createDataSets(getters, definition.dataSets, sheetId, definition.dataSetsHaveTitle);
|
|
@@ -24245,6 +24721,7 @@ class PieChart extends AbstractChart {
|
|
|
24245
24721
|
this.aggregated = definition.aggregated;
|
|
24246
24722
|
this.dataSetsHaveTitle = definition.dataSetsHaveTitle;
|
|
24247
24723
|
this.isDoughnut = definition.isDoughnut;
|
|
24724
|
+
this.showValues = definition.showValues;
|
|
24248
24725
|
}
|
|
24249
24726
|
static transformDefinition(definition, executed) {
|
|
24250
24727
|
return transformChartDefinitionWithDataSetsWithZone(definition, executed);
|
|
@@ -24263,6 +24740,7 @@ class PieChart extends AbstractChart {
|
|
|
24263
24740
|
labelRange: context.auxiliaryRange || undefined,
|
|
24264
24741
|
aggregated: context.aggregated ?? false,
|
|
24265
24742
|
isDoughnut: false,
|
|
24743
|
+
showValues: context.showValues,
|
|
24266
24744
|
};
|
|
24267
24745
|
}
|
|
24268
24746
|
getDefinition() {
|
|
@@ -24294,6 +24772,7 @@ class PieChart extends AbstractChart {
|
|
|
24294
24772
|
title: this.title,
|
|
24295
24773
|
aggregated: this.aggregated,
|
|
24296
24774
|
isDoughnut: this.isDoughnut,
|
|
24775
|
+
showValues: this.showValues,
|
|
24297
24776
|
};
|
|
24298
24777
|
}
|
|
24299
24778
|
copyForSheetId(sheetId) {
|
|
@@ -24361,6 +24840,7 @@ function getPieConfiguration(chart, labels, localeFormat) {
|
|
|
24361
24840
|
const yLabelStr = formatValue(yLabel, { format: toolTipFormat, locale });
|
|
24362
24841
|
return xLabel ? `${xLabel}: ${yLabelStr} (${percentage}%)` : `${yLabelStr} (${percentage}%)`;
|
|
24363
24842
|
};
|
|
24843
|
+
config.options.plugins.chartShowValuesPlugin = { showValues: chart.showValues };
|
|
24364
24844
|
return config;
|
|
24365
24845
|
}
|
|
24366
24846
|
function getPieColors(colors, dataSetsValues) {
|
|
@@ -24446,6 +24926,7 @@ class PyramidChart extends AbstractChart {
|
|
|
24446
24926
|
axesDesign;
|
|
24447
24927
|
horizontal = true;
|
|
24448
24928
|
stacked = true;
|
|
24929
|
+
showValues;
|
|
24449
24930
|
constructor(definition, sheetId, getters) {
|
|
24450
24931
|
super(definition, sheetId, getters);
|
|
24451
24932
|
this.dataSets = createDataSets(getters, definition.dataSets, sheetId, definition.dataSetsHaveTitle).slice(0, 2);
|
|
@@ -24456,6 +24937,7 @@ class PyramidChart extends AbstractChart {
|
|
|
24456
24937
|
this.dataSetsHaveTitle = definition.dataSetsHaveTitle;
|
|
24457
24938
|
this.dataSetDesign = definition.dataSets;
|
|
24458
24939
|
this.axesDesign = definition.axesDesign;
|
|
24940
|
+
this.showValues = definition.showValues;
|
|
24459
24941
|
}
|
|
24460
24942
|
static transformDefinition(definition, executed) {
|
|
24461
24943
|
return transformChartDefinitionWithDataSetsWithZone(definition, executed);
|
|
@@ -24476,6 +24958,7 @@ class PyramidChart extends AbstractChart {
|
|
|
24476
24958
|
axesDesign: context.axesDesign,
|
|
24477
24959
|
horizontal: true,
|
|
24478
24960
|
stacked: true,
|
|
24961
|
+
showValues: context.showValues,
|
|
24479
24962
|
};
|
|
24480
24963
|
}
|
|
24481
24964
|
getContextCreation() {
|
|
@@ -24529,6 +25012,7 @@ class PyramidChart extends AbstractChart {
|
|
|
24529
25012
|
axesDesign: this.axesDesign,
|
|
24530
25013
|
horizontal: true,
|
|
24531
25014
|
stacked: true,
|
|
25015
|
+
showValues: this.showValues,
|
|
24532
25016
|
};
|
|
24533
25017
|
}
|
|
24534
25018
|
getDefinitionForExcel() {
|
|
@@ -24563,6 +25047,8 @@ function createPyramidChartRuntime(chart, getters) {
|
|
|
24563
25047
|
const tooltipItem = { ...item, parsed: { y: item.parsed.y, x: Math.abs(item.parsed.x) } };
|
|
24564
25048
|
return tooltipLabelCallback(tooltipItem);
|
|
24565
25049
|
};
|
|
25050
|
+
const callback = config.options.plugins.chartShowValuesPlugin.callback;
|
|
25051
|
+
config.options.plugins.chartShowValuesPlugin.callback = (x) => callback(Math.abs(x));
|
|
24566
25052
|
return { chartJsConfig: config, background: chart.background || BACKGROUND_CHART_COLOR };
|
|
24567
25053
|
}
|
|
24568
25054
|
|
|
@@ -24577,6 +25063,7 @@ class ScatterChart extends AbstractChart {
|
|
|
24577
25063
|
dataSetsHaveTitle;
|
|
24578
25064
|
dataSetDesign;
|
|
24579
25065
|
axesDesign;
|
|
25066
|
+
showValues;
|
|
24580
25067
|
constructor(definition, sheetId, getters) {
|
|
24581
25068
|
super(definition, sheetId, getters);
|
|
24582
25069
|
this.dataSets = createDataSets(this.getters, definition.dataSets, sheetId, definition.dataSetsHaveTitle);
|
|
@@ -24588,6 +25075,7 @@ class ScatterChart extends AbstractChart {
|
|
|
24588
25075
|
this.dataSetsHaveTitle = definition.dataSetsHaveTitle;
|
|
24589
25076
|
this.dataSetDesign = definition.dataSets;
|
|
24590
25077
|
this.axesDesign = definition.axesDesign;
|
|
25078
|
+
this.showValues = definition.showValues;
|
|
24591
25079
|
}
|
|
24592
25080
|
static validateChartDefinition(validator, definition) {
|
|
24593
25081
|
return validator.checkValidations(definition, checkDataset, checkLabelRange);
|
|
@@ -24607,6 +25095,7 @@ class ScatterChart extends AbstractChart {
|
|
|
24607
25095
|
labelRange: context.auxiliaryRange || undefined,
|
|
24608
25096
|
aggregated: context.aggregated ?? false,
|
|
24609
25097
|
axesDesign: context.axesDesign,
|
|
25098
|
+
showValues: context.showValues,
|
|
24610
25099
|
};
|
|
24611
25100
|
}
|
|
24612
25101
|
getDefinition() {
|
|
@@ -24633,6 +25122,7 @@ class ScatterChart extends AbstractChart {
|
|
|
24633
25122
|
labelsAsText: this.labelsAsText,
|
|
24634
25123
|
aggregated: this.aggregated,
|
|
24635
25124
|
axesDesign: this.axesDesign,
|
|
25125
|
+
showValues: this.showValues,
|
|
24636
25126
|
};
|
|
24637
25127
|
}
|
|
24638
25128
|
getContextCreation() {
|
|
@@ -24733,6 +25223,7 @@ class WaterfallChart extends AbstractChart {
|
|
|
24733
25223
|
subTotalValuesColor;
|
|
24734
25224
|
dataSetDesign;
|
|
24735
25225
|
axesDesign;
|
|
25226
|
+
showValues;
|
|
24736
25227
|
constructor(definition, sheetId, getters) {
|
|
24737
25228
|
super(definition, sheetId, getters);
|
|
24738
25229
|
this.dataSets = createDataSets(getters, definition.dataSets, sheetId, definition.dataSetsHaveTitle);
|
|
@@ -24750,6 +25241,7 @@ class WaterfallChart extends AbstractChart {
|
|
|
24750
25241
|
this.firstValueAsSubtotal = definition.firstValueAsSubtotal;
|
|
24751
25242
|
this.dataSetDesign = definition.dataSets;
|
|
24752
25243
|
this.axesDesign = definition.axesDesign;
|
|
25244
|
+
this.showValues = definition.showValues;
|
|
24753
25245
|
}
|
|
24754
25246
|
static transformDefinition(definition, executed) {
|
|
24755
25247
|
return transformChartDefinitionWithDataSetsWithZone(definition, executed);
|
|
@@ -24772,6 +25264,7 @@ class WaterfallChart extends AbstractChart {
|
|
|
24772
25264
|
showConnectorLines: context.showConnectorLines ?? true,
|
|
24773
25265
|
firstValueAsSubtotal: context.firstValueAsSubtotal ?? false,
|
|
24774
25266
|
axesDesign: context.axesDesign,
|
|
25267
|
+
showValues: context.showValues,
|
|
24775
25268
|
};
|
|
24776
25269
|
}
|
|
24777
25270
|
getContextCreation() {
|
|
@@ -24830,6 +25323,7 @@ class WaterfallChart extends AbstractChart {
|
|
|
24830
25323
|
subTotalValuesColor: this.subTotalValuesColor,
|
|
24831
25324
|
firstValueAsSubtotal: this.firstValueAsSubtotal,
|
|
24832
25325
|
axesDesign: this.axesDesign,
|
|
25326
|
+
showValues: this.showValues,
|
|
24833
25327
|
};
|
|
24834
25328
|
}
|
|
24835
25329
|
getDefinitionForExcel() {
|
|
@@ -24927,6 +25421,10 @@ function getWaterfallConfiguration(chart, labels, dataSeriesLabels, localeFormat
|
|
|
24927
25421
|
},
|
|
24928
25422
|
};
|
|
24929
25423
|
config.options.plugins.waterfallLinesPlugin = { showConnectorLines: chart.showConnectorLines };
|
|
25424
|
+
config.options.plugins.chartShowValuesPlugin = {
|
|
25425
|
+
showValues: chart.showValues,
|
|
25426
|
+
background: chart.background,
|
|
25427
|
+
};
|
|
24930
25428
|
return config;
|
|
24931
25429
|
}
|
|
24932
25430
|
function createWaterfallChartRuntime(chart, getters) {
|
|
@@ -25019,7 +25517,7 @@ chartRegistry.add("combo", {
|
|
|
25019
25517
|
chartRegistry.add("line", {
|
|
25020
25518
|
match: (type) => type === "line",
|
|
25021
25519
|
createChart: (definition, sheetId, getters) => new LineChart(definition, sheetId, getters),
|
|
25022
|
-
getChartRuntime:
|
|
25520
|
+
getChartRuntime: createLineOrScatterChartRuntime,
|
|
25023
25521
|
validateChartDefinition: LineChart.validateChartDefinition,
|
|
25024
25522
|
transformDefinition: LineChart.transformDefinition,
|
|
25025
25523
|
getChartDefinitionFromContextCreation: LineChart.getDefinitionFromContextCreation,
|
|
@@ -25093,27 +25591,46 @@ const chartCategories = {
|
|
|
25093
25591
|
line: _t("Line"),
|
|
25094
25592
|
column: _t("Column"),
|
|
25095
25593
|
bar: _t("Bar"),
|
|
25594
|
+
area: _t("Area"),
|
|
25096
25595
|
pie: _t("Pie"),
|
|
25097
25596
|
misc: _t("Miscellaneous"),
|
|
25098
25597
|
};
|
|
25099
25598
|
const chartSubtypeRegistry = new Registry();
|
|
25100
25599
|
chartSubtypeRegistry
|
|
25101
25600
|
.add("line", {
|
|
25102
|
-
matcher: (definition) => definition.type === "line" && !definition.stacked,
|
|
25601
|
+
matcher: (definition) => definition.type === "line" && !definition.stacked && !definition.fillArea,
|
|
25103
25602
|
displayName: _t("Line"),
|
|
25104
25603
|
chartType: "line",
|
|
25105
25604
|
chartSubtype: "line",
|
|
25106
|
-
subtypeDefinition: { stacked: false },
|
|
25605
|
+
subtypeDefinition: { stacked: false, fillArea: false },
|
|
25107
25606
|
category: "line",
|
|
25108
25607
|
preview: "o-spreadsheet-ChartPreview.LINE_CHART",
|
|
25109
25608
|
})
|
|
25110
25609
|
.add("stacked_line", {
|
|
25111
|
-
matcher: (definition) => definition.type === "line" && definition.stacked,
|
|
25610
|
+
matcher: (definition) => definition.type === "line" && !definition.fillArea && !!definition.stacked,
|
|
25112
25611
|
displayName: _t("Stacked Line"),
|
|
25113
25612
|
chartType: "line",
|
|
25114
25613
|
chartSubtype: "stacked_line",
|
|
25115
|
-
subtypeDefinition: { stacked: true },
|
|
25614
|
+
subtypeDefinition: { stacked: true, fillArea: false },
|
|
25116
25615
|
category: "line",
|
|
25616
|
+
preview: "o-spreadsheet-ChartPreview.STACKED_LINE_CHART",
|
|
25617
|
+
})
|
|
25618
|
+
.add("area", {
|
|
25619
|
+
matcher: (definition) => definition.type === "line" && !definition.stacked && !!definition.fillArea,
|
|
25620
|
+
displayName: _t("Area"),
|
|
25621
|
+
chartType: "line",
|
|
25622
|
+
chartSubtype: "area",
|
|
25623
|
+
subtypeDefinition: { stacked: false, fillArea: true },
|
|
25624
|
+
category: "area",
|
|
25625
|
+
preview: "o-spreadsheet-ChartPreview.AREA_CHART",
|
|
25626
|
+
})
|
|
25627
|
+
.add("stacked_area", {
|
|
25628
|
+
matcher: (definition) => definition.type === "line" && definition.stacked && !!definition.fillArea,
|
|
25629
|
+
displayName: _t("Stacked Area"),
|
|
25630
|
+
chartType: "line",
|
|
25631
|
+
chartSubtype: "stacked_area",
|
|
25632
|
+
subtypeDefinition: { stacked: true, fillArea: true },
|
|
25633
|
+
category: "area",
|
|
25117
25634
|
preview: "o-spreadsheet-ChartPreview.STACKED_AREA_CHART",
|
|
25118
25635
|
})
|
|
25119
25636
|
.add("scatter", {
|
|
@@ -25537,7 +26054,7 @@ class MenuItemRegistry extends Registry {
|
|
|
25537
26054
|
* @param path Path of items to add this subitem
|
|
25538
26055
|
* @param value Subitem to add
|
|
25539
26056
|
*/
|
|
25540
|
-
addChild(key, path, value) {
|
|
26057
|
+
addChild(key, path, value, options = { force: false }) {
|
|
25541
26058
|
if (typeof value !== "function" && value.id === undefined) {
|
|
25542
26059
|
value.id = key;
|
|
25543
26060
|
}
|
|
@@ -25559,6 +26076,19 @@ class MenuItemRegistry extends Registry {
|
|
|
25559
26076
|
if (!node.children) {
|
|
25560
26077
|
node.children = [];
|
|
25561
26078
|
}
|
|
26079
|
+
const children = node.children;
|
|
26080
|
+
if (!children || typeof children === "function") {
|
|
26081
|
+
throw new Error(`${path} is either not a node or it's dynamically computed`);
|
|
26082
|
+
}
|
|
26083
|
+
if ("id" in value) {
|
|
26084
|
+
const valueIndex = children.findIndex((elt) => "id" in elt && elt.id === value.id);
|
|
26085
|
+
if (valueIndex > -1) {
|
|
26086
|
+
if (!options.force)
|
|
26087
|
+
throw new Error(`A child with the id "${value.id}" already exists.`);
|
|
26088
|
+
node.children.splice(valueIndex, 1, value);
|
|
26089
|
+
return this;
|
|
26090
|
+
}
|
|
26091
|
+
}
|
|
25562
26092
|
node.children.push(value);
|
|
25563
26093
|
return this;
|
|
25564
26094
|
}
|
|
@@ -27851,6 +28381,23 @@ const CREATE_PIVOT = (env) => {
|
|
|
27851
28381
|
env.openSidePanel("PivotSidePanel", { pivotId });
|
|
27852
28382
|
}
|
|
27853
28383
|
};
|
|
28384
|
+
const REINSERT_PIVOT_CHILDREN = (env) => env.model.getters.getPivotIds().map((pivotId, index) => ({
|
|
28385
|
+
id: `reinsert_pivot_${env.model.getters.getPivotFormulaId(pivotId)}`,
|
|
28386
|
+
name: env.model.getters.getPivotDisplayName(pivotId),
|
|
28387
|
+
sequence: index,
|
|
28388
|
+
execute: (env) => {
|
|
28389
|
+
const zone = env.model.getters.getSelectedZone();
|
|
28390
|
+
const table = env.model.getters.getPivot(pivotId).getTableStructure().export();
|
|
28391
|
+
env.model.dispatch("INSERT_PIVOT_WITH_TABLE", {
|
|
28392
|
+
pivotId,
|
|
28393
|
+
table,
|
|
28394
|
+
col: zone.left,
|
|
28395
|
+
row: zone.top,
|
|
28396
|
+
sheetId: env.model.getters.getActiveSheetId(),
|
|
28397
|
+
});
|
|
28398
|
+
env.model.dispatch("REFRESH_PIVOT", { id: pivotId });
|
|
28399
|
+
},
|
|
28400
|
+
}));
|
|
27854
28401
|
//------------------------------------------------------------------------------
|
|
27855
28402
|
// Image
|
|
27856
28403
|
//------------------------------------------------------------------------------
|
|
@@ -28715,12 +29262,21 @@ const splitToColumns = {
|
|
|
28715
29262
|
isEnabled: (env) => env.model.getters.isSingleColSelected(),
|
|
28716
29263
|
icon: "o-spreadsheet-Icon.SPLIT_TEXT",
|
|
28717
29264
|
};
|
|
29265
|
+
const reinsertPivotMenu = {
|
|
29266
|
+
id: "reinsert_pivot",
|
|
29267
|
+
name: _t("Re-insert pivot"),
|
|
29268
|
+
sequence: 1020,
|
|
29269
|
+
icon: "o-spreadsheet-Icon.INSERT_PIVOT",
|
|
29270
|
+
children: [REINSERT_PIVOT_CHILDREN],
|
|
29271
|
+
isVisible: (env) => env.model.getters.getPivotIds().length > 0,
|
|
29272
|
+
};
|
|
28718
29273
|
|
|
28719
29274
|
var ACTION_DATA = /*#__PURE__*/Object.freeze({
|
|
28720
29275
|
__proto__: null,
|
|
28721
29276
|
createRemoveFilter: createRemoveFilter,
|
|
28722
29277
|
createRemoveFilterTool: createRemoveFilterTool,
|
|
28723
29278
|
dataCleanup: dataCleanup,
|
|
29279
|
+
reinsertPivotMenu: reinsertPivotMenu,
|
|
28724
29280
|
removeDuplicates: removeDuplicates,
|
|
28725
29281
|
sortAscending: sortAscending,
|
|
28726
29282
|
sortDescending: sortDescending,
|
|
@@ -30201,7 +30757,8 @@ topbarMenuRegistry
|
|
|
30201
30757
|
: undefined,
|
|
30202
30758
|
};
|
|
30203
30759
|
});
|
|
30204
|
-
})
|
|
30760
|
+
})
|
|
30761
|
+
.addChild("reinsert_pivot", ["data"], reinsertPivotMenu);
|
|
30205
30762
|
|
|
30206
30763
|
class OTRegistry extends Registry {
|
|
30207
30764
|
/**
|
|
@@ -31686,7 +32243,7 @@ class ChartTitle extends owl.Component {
|
|
|
31686
32243
|
static template = "o-spreadsheet.ChartTitle";
|
|
31687
32244
|
static components = { Section, ColorPickerWidget };
|
|
31688
32245
|
static props = {
|
|
31689
|
-
title: String,
|
|
32246
|
+
title: { type: String, optional: true },
|
|
31690
32247
|
updateTitle: Function,
|
|
31691
32248
|
name: { type: String, optional: true },
|
|
31692
32249
|
toggleItalic: { type: Function, optional: true },
|
|
@@ -31695,6 +32252,9 @@ class ChartTitle extends owl.Component {
|
|
|
31695
32252
|
updateColor: { type: Function, optional: true },
|
|
31696
32253
|
style: { type: Object, optional: true },
|
|
31697
32254
|
};
|
|
32255
|
+
static defaultProps = {
|
|
32256
|
+
title: "",
|
|
32257
|
+
};
|
|
31698
32258
|
openedEl = null;
|
|
31699
32259
|
setup() {
|
|
31700
32260
|
owl.useExternalListener(window, "click", this.onExternalClick);
|
|
@@ -31890,6 +32450,7 @@ class ChartWithAxisDesignPanel extends owl.Component {
|
|
|
31890
32450
|
Section,
|
|
31891
32451
|
AxisDesignEditor,
|
|
31892
32452
|
RoundColorPicker,
|
|
32453
|
+
Checkbox,
|
|
31893
32454
|
};
|
|
31894
32455
|
static props = {
|
|
31895
32456
|
figureId: String,
|
|
@@ -31982,6 +32543,12 @@ class ChartWithAxisDesignPanel extends owl.Component {
|
|
|
31982
32543
|
const dataSets = this.props.definition.dataSets;
|
|
31983
32544
|
return dataSets[this.state.index]?.label || this.getDataSeries()[this.state.index];
|
|
31984
32545
|
}
|
|
32546
|
+
get showValuesLabel() {
|
|
32547
|
+
return ChartTerms.ShowValues;
|
|
32548
|
+
}
|
|
32549
|
+
updateShowValues(showValues) {
|
|
32550
|
+
this.props.updateChart(this.props.figureId, { showValues });
|
|
32551
|
+
}
|
|
31985
32552
|
}
|
|
31986
32553
|
|
|
31987
32554
|
class GaugeChartConfigPanel extends owl.Component {
|
|
@@ -32131,6 +32698,10 @@ class LineConfigPanel extends GenericChartConfigPanel {
|
|
|
32131
32698
|
}
|
|
32132
32699
|
return false;
|
|
32133
32700
|
}
|
|
32701
|
+
get stackedLabel() {
|
|
32702
|
+
const definition = this.props.definition;
|
|
32703
|
+
return definition.fillArea ? this.chartTerms.StackedAreaChart : this.chartTerms.StackedBarChart;
|
|
32704
|
+
}
|
|
32134
32705
|
getLabelRangeOptions() {
|
|
32135
32706
|
const options = super.getLabelRangeOptions();
|
|
32136
32707
|
if (this.canTreatLabelsAsText) {
|
|
@@ -32170,6 +32741,7 @@ class PieChartDesignPanel extends owl.Component {
|
|
|
32170
32741
|
static components = {
|
|
32171
32742
|
GeneralDesignEditor,
|
|
32172
32743
|
Section,
|
|
32744
|
+
Checkbox,
|
|
32173
32745
|
};
|
|
32174
32746
|
static props = {
|
|
32175
32747
|
figureId: String,
|
|
@@ -32182,6 +32754,9 @@ class PieChartDesignPanel extends owl.Component {
|
|
|
32182
32754
|
legendPosition: ev.target.value,
|
|
32183
32755
|
});
|
|
32184
32756
|
}
|
|
32757
|
+
get showValuesLabel() {
|
|
32758
|
+
return ChartTerms.ShowValues;
|
|
32759
|
+
}
|
|
32185
32760
|
}
|
|
32186
32761
|
|
|
32187
32762
|
class ScatterConfigPanel extends GenericChartConfigPanel {
|
|
@@ -32376,6 +32951,12 @@ class WaterfallChartDesignPanel extends owl.Component {
|
|
|
32376
32951
|
verticalAxisPosition: ev.target.value,
|
|
32377
32952
|
});
|
|
32378
32953
|
}
|
|
32954
|
+
get showValuesLabel() {
|
|
32955
|
+
return ChartTerms.ShowValues;
|
|
32956
|
+
}
|
|
32957
|
+
updateShowValues(showValues) {
|
|
32958
|
+
this.props.updateChart(this.props.figureId, { showValues });
|
|
32959
|
+
}
|
|
32379
32960
|
}
|
|
32380
32961
|
|
|
32381
32962
|
const chartSidePanelComponentRegistry = new Registry();
|
|
@@ -32636,6 +33217,11 @@ class ChartPanel extends owl.Component {
|
|
|
32636
33217
|
}
|
|
32637
33218
|
}
|
|
32638
33219
|
|
|
33220
|
+
/**
|
|
33221
|
+
* Registry to draw icons on cells
|
|
33222
|
+
*/
|
|
33223
|
+
const iconsOnCellRegistry = new Registry();
|
|
33224
|
+
|
|
32639
33225
|
css /* scss */ `
|
|
32640
33226
|
.o-spreadsheet {
|
|
32641
33227
|
.o-icon {
|
|
@@ -32671,49 +33257,47 @@ const FROWN = '<svg class="o-cf-icon frown" width="10" height="10" focusable="fa
|
|
|
32671
33257
|
const GREEN_DOT = '<svg class="o-cf-icon green-dot" width="10" height="10" focusable="false" viewBox="0 0 512 512"><path fill="#6AA84F" d="M256 8C119 8 8 119 8 256s111 248 248 248 248-111 248-248S393 8 256 8z"></path></svg>';
|
|
32672
33258
|
const YELLOW_DOT = '<svg class="o-cf-icon yellow-dot" width="10" height="10" focusable="false" viewBox="0 0 512 512"><path fill="#F0AD4E" d="M256 8C119 8 8 119 8 256s111 248 248 248 248-111 248-248S393 8 256 8z"></path></svg>';
|
|
32673
33259
|
const RED_DOT = '<svg class="o-cf-icon red-dot" width="10" height="10" focusable="false" viewBox="0 0 512 512"><path fill="#E06666" d="M256 8C119 8 8 119 8 256s111 248 248 248 248-111 248-248S393 8 256 8z"></path></svg>';
|
|
32674
|
-
function
|
|
33260
|
+
function getIconSrc(svg) {
|
|
32675
33261
|
/** We have to add xmlns, as it's not added by owl in the canvas */
|
|
32676
33262
|
svg = `<svg xmlns="http://www.w3.org/2000/svg" ${svg.slice(4)}`;
|
|
32677
|
-
|
|
32678
|
-
image.src = "data:image/svg+xml; charset=utf8, " + encodeURIComponent(svg);
|
|
32679
|
-
return image;
|
|
33263
|
+
return "data:image/svg+xml; charset=utf8, " + encodeURIComponent(svg);
|
|
32680
33264
|
}
|
|
32681
33265
|
const ICONS = {
|
|
32682
33266
|
arrowGood: {
|
|
32683
33267
|
template: "ARROW_UP",
|
|
32684
|
-
img:
|
|
33268
|
+
img: getIconSrc(ARROW_UP),
|
|
32685
33269
|
},
|
|
32686
33270
|
arrowNeutral: {
|
|
32687
33271
|
template: "ARROW_RIGHT",
|
|
32688
|
-
img:
|
|
33272
|
+
img: getIconSrc(ARROW_RIGHT),
|
|
32689
33273
|
},
|
|
32690
33274
|
arrowBad: {
|
|
32691
33275
|
template: "ARROW_DOWN",
|
|
32692
|
-
img:
|
|
33276
|
+
img: getIconSrc(ARROW_DOWN),
|
|
32693
33277
|
},
|
|
32694
33278
|
smileyGood: {
|
|
32695
33279
|
template: "SMILE",
|
|
32696
|
-
img:
|
|
33280
|
+
img: getIconSrc(SMILE),
|
|
32697
33281
|
},
|
|
32698
33282
|
smileyNeutral: {
|
|
32699
33283
|
template: "MEH",
|
|
32700
|
-
img:
|
|
33284
|
+
img: getIconSrc(MEH),
|
|
32701
33285
|
},
|
|
32702
33286
|
smileyBad: {
|
|
32703
33287
|
template: "FROWN",
|
|
32704
|
-
img:
|
|
33288
|
+
img: getIconSrc(FROWN),
|
|
32705
33289
|
},
|
|
32706
33290
|
dotGood: {
|
|
32707
33291
|
template: "GREEN_DOT",
|
|
32708
|
-
img:
|
|
33292
|
+
img: getIconSrc(GREEN_DOT),
|
|
32709
33293
|
},
|
|
32710
33294
|
dotNeutral: {
|
|
32711
33295
|
template: "YELLOW_DOT",
|
|
32712
|
-
img:
|
|
33296
|
+
img: getIconSrc(YELLOW_DOT),
|
|
32713
33297
|
},
|
|
32714
33298
|
dotBad: {
|
|
32715
33299
|
template: "RED_DOT",
|
|
32716
|
-
img:
|
|
33300
|
+
img: getIconSrc(RED_DOT),
|
|
32717
33301
|
},
|
|
32718
33302
|
};
|
|
32719
33303
|
const ICON_SETS = {
|
|
@@ -32733,6 +33317,12 @@ const ICON_SETS = {
|
|
|
32733
33317
|
bad: "dotBad",
|
|
32734
33318
|
},
|
|
32735
33319
|
};
|
|
33320
|
+
iconsOnCellRegistry.add("conditional_formatting", (getters, position) => {
|
|
33321
|
+
const icon = getters.getConditionalIcon(position);
|
|
33322
|
+
if (icon) {
|
|
33323
|
+
return ICONS[icon].img;
|
|
33324
|
+
}
|
|
33325
|
+
});
|
|
32736
33326
|
|
|
32737
33327
|
css /* scss */ `
|
|
32738
33328
|
.o-icon-picker {
|
|
@@ -33081,21 +33671,25 @@ function useHoveredElement(ref) {
|
|
|
33081
33671
|
|
|
33082
33672
|
function useHighlightsOnHover(ref, highlightProvider) {
|
|
33083
33673
|
const hoverState = useHoveredElement(ref);
|
|
33084
|
-
const stores = useStoreProvider();
|
|
33085
33674
|
useHighlights({
|
|
33086
33675
|
get highlights() {
|
|
33087
33676
|
return hoverState.hovered ? highlightProvider.highlights : [];
|
|
33088
33677
|
},
|
|
33089
33678
|
});
|
|
33090
|
-
owl.useEffect(() => {
|
|
33091
|
-
stores.trigger("store-updated");
|
|
33092
|
-
}, () => [hoverState.hovered]);
|
|
33093
33679
|
}
|
|
33094
33680
|
function useHighlights(highlightProvider) {
|
|
33681
|
+
const stores = useStoreProvider();
|
|
33095
33682
|
const store = useLocalStore(HighlightStore);
|
|
33096
33683
|
owl.onMounted(() => {
|
|
33097
33684
|
store.register(highlightProvider);
|
|
33098
33685
|
});
|
|
33686
|
+
let currentHighlights = highlightProvider.highlights;
|
|
33687
|
+
owl.useEffect((highlights) => {
|
|
33688
|
+
if (!deepEquals(highlights, currentHighlights)) {
|
|
33689
|
+
currentHighlights = highlights;
|
|
33690
|
+
stores.trigger("store-updated");
|
|
33691
|
+
}
|
|
33692
|
+
}, () => [highlightProvider.highlights]);
|
|
33099
33693
|
}
|
|
33100
33694
|
|
|
33101
33695
|
css /* scss */ `
|
|
@@ -34567,6 +35161,8 @@ class FindAndReplaceStore extends SpreadsheetStore {
|
|
|
34567
35161
|
isSearchDirty = false;
|
|
34568
35162
|
initialShowFormulaState;
|
|
34569
35163
|
preserveSelectedMatchIndex = false;
|
|
35164
|
+
irreplaceableMatchCount = 0;
|
|
35165
|
+
notificationStore = this.get(NotificationStore);
|
|
34570
35166
|
// fixme: why do we make selectedMatchIndex on top of a selected
|
|
34571
35167
|
// property in the matches?
|
|
34572
35168
|
selectedMatchIndex = null;
|
|
@@ -34576,7 +35172,7 @@ class FindAndReplaceStore extends SpreadsheetStore {
|
|
|
34576
35172
|
matchCase: false,
|
|
34577
35173
|
exactMatch: false,
|
|
34578
35174
|
searchFormulas: false,
|
|
34579
|
-
searchScope: "
|
|
35175
|
+
searchScope: "activeSheet",
|
|
34580
35176
|
specificRange: undefined,
|
|
34581
35177
|
};
|
|
34582
35178
|
updateSearchContent = debounce(this._updateSearchContent.bind(this), 200);
|
|
@@ -34643,6 +35239,11 @@ class FindAndReplaceStore extends SpreadsheetStore {
|
|
|
34643
35239
|
for (const match of cmd.matches) {
|
|
34644
35240
|
this.replaceMatch(match, cmd.searchString, cmd.replaceWith, cmd.searchOptions);
|
|
34645
35241
|
}
|
|
35242
|
+
if (this.irreplaceableMatchCount > 0) {
|
|
35243
|
+
this.showReplaceWarningMessage(cmd.matches.length, this.irreplaceableMatchCount);
|
|
35244
|
+
}
|
|
35245
|
+
this.irreplaceableMatchCount = 0;
|
|
35246
|
+
break;
|
|
34646
35247
|
}
|
|
34647
35248
|
}
|
|
34648
35249
|
finalize() {
|
|
@@ -34824,12 +35425,36 @@ class FindAndReplaceStore extends SpreadsheetStore {
|
|
|
34824
35425
|
searchOptions: this.searchOptions,
|
|
34825
35426
|
});
|
|
34826
35427
|
}
|
|
35428
|
+
/**
|
|
35429
|
+
* Show a warning message based on the number of matches replaced and irreplaceable.
|
|
35430
|
+
*/
|
|
35431
|
+
showReplaceWarningMessage(totalMatches, irreplaceableMatches) {
|
|
35432
|
+
const replaceableMatches = totalMatches - irreplaceableMatches;
|
|
35433
|
+
if (replaceableMatches === 0) {
|
|
35434
|
+
this.notificationStore.notifyUser({
|
|
35435
|
+
type: "warning",
|
|
35436
|
+
sticky: false,
|
|
35437
|
+
text: _t("Match(es) cannot be replaced as they are part of a formula."),
|
|
35438
|
+
});
|
|
35439
|
+
}
|
|
35440
|
+
else {
|
|
35441
|
+
this.notificationStore.notifyUser({
|
|
35442
|
+
type: "warning",
|
|
35443
|
+
sticky: false,
|
|
35444
|
+
text: _t("%(replaceable_count)s match(es) replaced. %(irreplaceable_count)s match(es) cannot be replaced as they are part of a formula.", {
|
|
35445
|
+
replaceable_count: replaceableMatches,
|
|
35446
|
+
irreplaceable_count: irreplaceableMatches,
|
|
35447
|
+
}),
|
|
35448
|
+
});
|
|
35449
|
+
}
|
|
35450
|
+
}
|
|
34827
35451
|
replaceMatch(selectedMatch, searchString, replaceWith, searchOptions) {
|
|
34828
35452
|
const cell = this.getters.getCell(selectedMatch);
|
|
34829
35453
|
if (!cell?.content) {
|
|
34830
35454
|
return;
|
|
34831
35455
|
}
|
|
34832
35456
|
if (cell?.isFormula && !searchOptions.searchFormulas) {
|
|
35457
|
+
this.irreplaceableMatchCount++;
|
|
34833
35458
|
return;
|
|
34834
35459
|
}
|
|
34835
35460
|
const searchRegex = getSearchRegex(searchString, searchOptions);
|
|
@@ -35506,9 +36131,11 @@ class PivotTitleSection extends owl.Component {
|
|
|
35506
36131
|
}
|
|
35507
36132
|
duplicatePivot() {
|
|
35508
36133
|
const newPivotId = this.env.model.uuidGenerator.uuidv4();
|
|
35509
|
-
const
|
|
36134
|
+
const newSheetId = this.env.model.uuidGenerator.uuidv4();
|
|
36135
|
+
const result = this.env.model.dispatch("DUPLICATE_PIVOT_IN_NEW_SHEET", {
|
|
35510
36136
|
pivotId: this.props.pivotId,
|
|
35511
36137
|
newPivotId,
|
|
36138
|
+
newSheetId,
|
|
35512
36139
|
});
|
|
35513
36140
|
const text = result.isSuccessful ? _t("Pivot duplicated.") : _t("Pivot duplication failed");
|
|
35514
36141
|
const type = result.isSuccessful ? "success" : "danger";
|
|
@@ -36043,7 +36670,7 @@ function createDate(dimension, value, locale) {
|
|
|
36043
36670
|
if (typeof value === "number" || typeof value === "string") {
|
|
36044
36671
|
const date = toJsDate(value, locale);
|
|
36045
36672
|
switch (granularity) {
|
|
36046
|
-
case "
|
|
36673
|
+
case "year":
|
|
36047
36674
|
number = date.getFullYear();
|
|
36048
36675
|
break;
|
|
36049
36676
|
case "quarter_number":
|
|
@@ -36071,7 +36698,7 @@ function createDate(dimension, value, locale) {
|
|
|
36071
36698
|
* This map is used to cache the different values of a pivot date value
|
|
36072
36699
|
* 43_831 -> 01/01/2012
|
|
36073
36700
|
* Example: {
|
|
36074
|
-
*
|
|
36701
|
+
* year: {
|
|
36075
36702
|
* set: { 43_831 },
|
|
36076
36703
|
* values: { '43_831': 2012 }
|
|
36077
36704
|
* },
|
|
@@ -36098,7 +36725,7 @@ function createDate(dimension, value, locale) {
|
|
|
36098
36725
|
* }
|
|
36099
36726
|
*/
|
|
36100
36727
|
const MAP_VALUE_DIMENSION_DATE = {
|
|
36101
|
-
|
|
36728
|
+
year: {
|
|
36102
36729
|
set: new Set(),
|
|
36103
36730
|
values: {},
|
|
36104
36731
|
},
|
|
@@ -36483,7 +37110,7 @@ pivotRegistry.add("SPREADSHEET", {
|
|
|
36483
37110
|
externalData: false,
|
|
36484
37111
|
onIterationEndEvaluation: (pivot) => pivot.markAsDirtyForEvaluation(),
|
|
36485
37112
|
granularities: [
|
|
36486
|
-
"
|
|
37113
|
+
"year",
|
|
36487
37114
|
"quarter_number",
|
|
36488
37115
|
"month_number",
|
|
36489
37116
|
"iso_week_number",
|
|
@@ -36893,7 +37520,13 @@ class SettingsPanel extends owl.Component {
|
|
|
36893
37520
|
}
|
|
36894
37521
|
async loadLocales() {
|
|
36895
37522
|
this.loadedLocales = (await this.env.loadLocales())
|
|
36896
|
-
.filter(
|
|
37523
|
+
.filter((locale) => {
|
|
37524
|
+
const isValid = isValidLocale(locale);
|
|
37525
|
+
if (!isValid) {
|
|
37526
|
+
console.warn(`Invalid locale: ${locale["code"]} ${locale}`);
|
|
37527
|
+
}
|
|
37528
|
+
return isValid;
|
|
37529
|
+
})
|
|
36897
37530
|
.sort((a, b) => a.name.localeCompare(b.name));
|
|
36898
37531
|
}
|
|
36899
37532
|
get numberFormatPreview() {
|
|
@@ -37952,6 +38585,9 @@ class TopBarComponentRegistry extends Registry {
|
|
|
37952
38585
|
const component = { ...value, id: this.uuidGenerator.uuidv4() };
|
|
37953
38586
|
return super.add(name, component);
|
|
37954
38587
|
}
|
|
38588
|
+
getAllOrdered() {
|
|
38589
|
+
return this.getAll().sort((a, b) => a.sequence - b.sequence);
|
|
38590
|
+
}
|
|
37955
38591
|
}
|
|
37956
38592
|
const topbarComponentRegistry = new TopBarComponentRegistry();
|
|
37957
38593
|
|
|
@@ -40840,21 +41476,22 @@ class GridRenderer {
|
|
|
40840
41476
|
isError: (cell.type === CellValueType.error && !!cell.message) ||
|
|
40841
41477
|
this.getters.isDataValidationInvalid(position),
|
|
40842
41478
|
};
|
|
40843
|
-
|
|
40844
|
-
|
|
40845
|
-
}
|
|
40846
|
-
/** Icon CF */
|
|
40847
|
-
const cfIcon = this.getters.getConditionalIcon(position);
|
|
41479
|
+
/** Icon */
|
|
41480
|
+
const iconSrc = this.getters.getCellIconSrc(position);
|
|
40848
41481
|
const fontSizePX = computeTextFontSizeInPixels(box.style);
|
|
40849
|
-
const iconBoxWidth =
|
|
40850
|
-
if (
|
|
41482
|
+
const iconBoxWidth = iconSrc ? MIN_CF_ICON_MARGIN + fontSizePX : 0;
|
|
41483
|
+
if (iconSrc) {
|
|
41484
|
+
const imageHtmlElement = loadIconImage(iconSrc);
|
|
40851
41485
|
box.image = {
|
|
40852
41486
|
type: "icon",
|
|
40853
41487
|
size: fontSizePX,
|
|
40854
41488
|
clipIcon: { x: box.x, y: box.y, width: Math.min(iconBoxWidth, width), height },
|
|
40855
|
-
image:
|
|
41489
|
+
image: imageHtmlElement,
|
|
40856
41490
|
};
|
|
40857
41491
|
}
|
|
41492
|
+
if (cell.type === CellValueType.empty || this.getters.isCellValidCheckbox(position)) {
|
|
41493
|
+
return box;
|
|
41494
|
+
}
|
|
40858
41495
|
/** Filter Header or data validation icon */
|
|
40859
41496
|
box.hasIcon = this.getters.doesCellHaveGridIcon(position);
|
|
40860
41497
|
const headerIconWidth = box.hasIcon ? GRID_ICON_EDGE_LENGTH + GRID_ICON_MARGIN : 0;
|
|
@@ -40873,7 +41510,7 @@ class GridRenderer {
|
|
|
40873
41510
|
};
|
|
40874
41511
|
/** ClipRect */
|
|
40875
41512
|
const isOverflowing = contentWidth > width || fontSizePX > height;
|
|
40876
|
-
if (
|
|
41513
|
+
if (iconSrc || box.hasIcon) {
|
|
40877
41514
|
box.clipRect = {
|
|
40878
41515
|
x: box.x + iconBoxWidth,
|
|
40879
41516
|
y: box.y,
|
|
@@ -40984,6 +41621,11 @@ class GridRenderer {
|
|
|
40984
41621
|
return boxes;
|
|
40985
41622
|
}
|
|
40986
41623
|
}
|
|
41624
|
+
const loadIconImage = memoize(function loadIconImage(src) {
|
|
41625
|
+
const image = new Image();
|
|
41626
|
+
image.src = src;
|
|
41627
|
+
return image;
|
|
41628
|
+
});
|
|
40987
41629
|
|
|
40988
41630
|
function useGridDrawing(refName, model, canvasSize) {
|
|
40989
41631
|
const canvasRef = owl.useRef(refName);
|
|
@@ -42056,6 +42698,7 @@ class Grid extends owl.Component {
|
|
|
42056
42698
|
return;
|
|
42057
42699
|
}
|
|
42058
42700
|
if (clipboardData.types.indexOf(ClipboardMIMEType.PlainText) > -1) {
|
|
42701
|
+
ev.preventDefault();
|
|
42059
42702
|
const content = clipboardData.getData(ClipboardMIMEType.PlainText);
|
|
42060
42703
|
const target = this.env.model.getters.getSelectedZones();
|
|
42061
42704
|
const clipboardString = this.env.model.getters.getClipboardTextContent();
|
|
@@ -42322,6 +42965,8 @@ const NON_RETROCOMPATIBLE_FUNCTIONS = [
|
|
|
42322
42965
|
"BITOR",
|
|
42323
42966
|
"BITRSHIFT",
|
|
42324
42967
|
"BITXOR",
|
|
42968
|
+
"BYCOL",
|
|
42969
|
+
"BYROW",
|
|
42325
42970
|
"CEILING.MATH",
|
|
42326
42971
|
"CEILING.PRECISE",
|
|
42327
42972
|
"CHISQ.DIST",
|
|
@@ -42329,6 +42974,8 @@ const NON_RETROCOMPATIBLE_FUNCTIONS = [
|
|
|
42329
42974
|
"CHISQ.INV",
|
|
42330
42975
|
"CHISQ.INV.RT",
|
|
42331
42976
|
"CHISQ.TEST",
|
|
42977
|
+
"CHOOSECOLS",
|
|
42978
|
+
"CHOOSEROWS",
|
|
42332
42979
|
"COMBINA",
|
|
42333
42980
|
"CONCAT",
|
|
42334
42981
|
"CONFIDENCE.NORM",
|
|
@@ -42341,14 +42988,17 @@ const NON_RETROCOMPATIBLE_FUNCTIONS = [
|
|
|
42341
42988
|
"CSCH",
|
|
42342
42989
|
"DAYS",
|
|
42343
42990
|
"DECIMAL",
|
|
42991
|
+
"DROP",
|
|
42344
42992
|
"ERF.PRECISE",
|
|
42345
42993
|
"ERFC.PRECISE",
|
|
42994
|
+
"EXPAND",
|
|
42346
42995
|
"EXPON.DIST",
|
|
42347
42996
|
"F.DIST",
|
|
42348
42997
|
"F.DIST.RT",
|
|
42349
42998
|
"F.INV",
|
|
42350
42999
|
"F.INV.RT",
|
|
42351
43000
|
"F.TEST",
|
|
43001
|
+
"FIELDVALUE",
|
|
42352
43002
|
"FILTERXML",
|
|
42353
43003
|
"FLOOR.MATH",
|
|
42354
43004
|
"FLOOR.PRECISE",
|
|
@@ -42363,6 +43013,7 @@ const NON_RETROCOMPATIBLE_FUNCTIONS = [
|
|
|
42363
43013
|
"GAMMA.INV",
|
|
42364
43014
|
"GAMMALN.PRECISE",
|
|
42365
43015
|
"GAUSS",
|
|
43016
|
+
"HSTACK",
|
|
42366
43017
|
"HYPGEOM.DIST",
|
|
42367
43018
|
"IFNA",
|
|
42368
43019
|
"IFS",
|
|
@@ -42375,9 +43026,14 @@ const NON_RETROCOMPATIBLE_FUNCTIONS = [
|
|
|
42375
43026
|
"IMSINH",
|
|
42376
43027
|
"IMTAN",
|
|
42377
43028
|
"ISFORMULA",
|
|
43029
|
+
"ISOMITTED",
|
|
42378
43030
|
"ISOWEEKNUM",
|
|
43031
|
+
"LAMBDA",
|
|
43032
|
+
"LET",
|
|
42379
43033
|
"LOGNORM.DIST",
|
|
42380
43034
|
"LOGNORM.INV",
|
|
43035
|
+
"MAKEARRAY",
|
|
43036
|
+
"MAP",
|
|
42381
43037
|
"MAXIFS",
|
|
42382
43038
|
"MINIFS",
|
|
42383
43039
|
"MODE.MULT",
|
|
@@ -42397,17 +43053,26 @@ const NON_RETROCOMPATIBLE_FUNCTIONS = [
|
|
|
42397
43053
|
"PERMUTATIONA",
|
|
42398
43054
|
"PHI",
|
|
42399
43055
|
"POISSON.DIST",
|
|
43056
|
+
"PQSOURCE",
|
|
43057
|
+
"PYTHON_STR",
|
|
43058
|
+
"PYTHON_TYPE",
|
|
43059
|
+
"PYTHON_TYPENAME",
|
|
42400
43060
|
"QUARTILE.EXC",
|
|
42401
43061
|
"QUARTILE.INC",
|
|
42402
43062
|
"QUERYSTRING",
|
|
43063
|
+
"RANDARRAY",
|
|
42403
43064
|
"RANK.AVG",
|
|
42404
43065
|
"RANK.EQ",
|
|
43066
|
+
"REDUCE",
|
|
42405
43067
|
"RRI",
|
|
43068
|
+
"SCAN",
|
|
42406
43069
|
"SEC",
|
|
42407
43070
|
"SECH",
|
|
43071
|
+
"SEQUENCE",
|
|
42408
43072
|
"SHEET",
|
|
42409
43073
|
"SHEETS",
|
|
42410
43074
|
"SKEW.P",
|
|
43075
|
+
"SORTBY",
|
|
42411
43076
|
"STDEV.P",
|
|
42412
43077
|
"STDEV.S",
|
|
42413
43078
|
"SWITCH",
|
|
@@ -42417,13 +43082,24 @@ const NON_RETROCOMPATIBLE_FUNCTIONS = [
|
|
|
42417
43082
|
"T.INV",
|
|
42418
43083
|
"T.INV.2T",
|
|
42419
43084
|
"T.TEST",
|
|
43085
|
+
"TAKE",
|
|
43086
|
+
"TEXTAFTER",
|
|
43087
|
+
"TEXTBEFORE",
|
|
42420
43088
|
"TEXTJOIN",
|
|
43089
|
+
"TEXTSPLIT",
|
|
43090
|
+
"TOCOL",
|
|
43091
|
+
"TOROW",
|
|
42421
43092
|
"UNICHAR",
|
|
42422
43093
|
"UNICODE",
|
|
43094
|
+
"UNIQUE",
|
|
42423
43095
|
"VAR.P",
|
|
42424
43096
|
"VAR.S",
|
|
43097
|
+
"VSTACK",
|
|
42425
43098
|
"WEBSERVICE",
|
|
42426
43099
|
"WEIBULL.DIST",
|
|
43100
|
+
"WRAPCOLS",
|
|
43101
|
+
"WRAPROWS",
|
|
43102
|
+
"XLOOKUP",
|
|
42427
43103
|
"XOR",
|
|
42428
43104
|
"Z.TEST",
|
|
42429
43105
|
];
|
|
@@ -45607,7 +46283,7 @@ function getRelationFile(file, xmls) {
|
|
|
45607
46283
|
return relsFile;
|
|
45608
46284
|
}
|
|
45609
46285
|
|
|
45610
|
-
const EXCEL_IMPORT_VERSION =
|
|
46286
|
+
const EXCEL_IMPORT_VERSION = 17;
|
|
45611
46287
|
class XlsxReader {
|
|
45612
46288
|
warningManager;
|
|
45613
46289
|
xmls;
|
|
@@ -45739,7 +46415,7 @@ function normalizeV9(formula) {
|
|
|
45739
46415
|
* a breaking change is made in the way the state is handled, and an upgrade
|
|
45740
46416
|
* function should be defined
|
|
45741
46417
|
*/
|
|
45742
|
-
const CURRENT_VERSION =
|
|
46418
|
+
const CURRENT_VERSION = 17;
|
|
45743
46419
|
const INITIAL_SHEET_ID = "Sheet1";
|
|
45744
46420
|
/**
|
|
45745
46421
|
* This function tries to load anything that could look like a valid
|
|
@@ -46686,6 +47362,21 @@ class BordersPlugin extends CorePlugin {
|
|
|
46686
47362
|
return [];
|
|
46687
47363
|
return Object.keys(sheetBorders).map((index) => parseInt(index, 10));
|
|
46688
47364
|
}
|
|
47365
|
+
/**
|
|
47366
|
+
* Get all the rows which contains at least a border
|
|
47367
|
+
*/
|
|
47368
|
+
getRowsWithBorders(sheetId) {
|
|
47369
|
+
const sheetBorders = this.borders[sheetId]?.filter(isDefined);
|
|
47370
|
+
if (!sheetBorders)
|
|
47371
|
+
return [];
|
|
47372
|
+
const rowsWithBorders = new Set();
|
|
47373
|
+
for (const rowBorders of sheetBorders) {
|
|
47374
|
+
for (const rowBorder in rowBorders) {
|
|
47375
|
+
rowsWithBorders.add(parseInt(rowBorder, 10));
|
|
47376
|
+
}
|
|
47377
|
+
}
|
|
47378
|
+
return Array.from(rowsWithBorders);
|
|
47379
|
+
}
|
|
46689
47380
|
/**
|
|
46690
47381
|
* Get the range of all the rows in the sheet
|
|
46691
47382
|
*/
|
|
@@ -46735,7 +47426,7 @@ class BordersPlugin extends CorePlugin {
|
|
|
46735
47426
|
destructive: false,
|
|
46736
47427
|
});
|
|
46737
47428
|
}
|
|
46738
|
-
this.
|
|
47429
|
+
this.getRowsWithBorders(sheetId)
|
|
46739
47430
|
.filter((row) => row >= start)
|
|
46740
47431
|
.sort((a, b) => (delta < 0 ? a - b : b - a)) // start by the end when moving up
|
|
46741
47432
|
.forEach((row) => {
|
|
@@ -49061,10 +49752,12 @@ class MergePlugin extends CorePlugin {
|
|
|
49061
49752
|
if (!sheetMap)
|
|
49062
49753
|
return [];
|
|
49063
49754
|
const mergeIds = new Set();
|
|
49064
|
-
for (
|
|
49065
|
-
|
|
49066
|
-
|
|
49067
|
-
|
|
49755
|
+
for (let col = zone.left; col <= zone.right; col++) {
|
|
49756
|
+
for (let row = zone.top; row <= zone.bottom; row++) {
|
|
49757
|
+
const mergeId = sheetMap[col]?.[row];
|
|
49758
|
+
if (mergeId) {
|
|
49759
|
+
mergeIds.add(mergeId);
|
|
49760
|
+
}
|
|
49068
49761
|
}
|
|
49069
49762
|
}
|
|
49070
49763
|
return Array.from(mergeIds)
|
|
@@ -52932,7 +53625,13 @@ class FormulaDependencyGraph {
|
|
|
52932
53625
|
const queue = Array.from(ranges).reverse();
|
|
52933
53626
|
while (queue.length > 0) {
|
|
52934
53627
|
const range = queue.pop();
|
|
52935
|
-
|
|
53628
|
+
const zone = range.zone;
|
|
53629
|
+
const sheetId = range.sheetId;
|
|
53630
|
+
for (let col = zone.left; col <= zone.right; col++) {
|
|
53631
|
+
for (let row = zone.top; row <= zone.bottom; row++) {
|
|
53632
|
+
visited.add({ sheetId, col, row });
|
|
53633
|
+
}
|
|
53634
|
+
}
|
|
52936
53635
|
const impactedPositions = this.rTree.search(range).map((dep) => dep.data);
|
|
52937
53636
|
const nextInQueue = {};
|
|
52938
53637
|
for (const position of impactedPositions) {
|
|
@@ -52948,7 +53647,16 @@ class FormulaDependencyGraph {
|
|
|
52948
53647
|
queue.push(...zones.map((zone) => ({ sheetId, zone })));
|
|
52949
53648
|
}
|
|
52950
53649
|
}
|
|
52951
|
-
|
|
53650
|
+
// remove initial ranges
|
|
53651
|
+
for (const range of ranges) {
|
|
53652
|
+
const zone = range.zone;
|
|
53653
|
+
const sheetId = range.sheetId;
|
|
53654
|
+
for (let col = zone.left; col <= zone.right; col++) {
|
|
53655
|
+
for (let row = zone.top; row <= zone.bottom; row++) {
|
|
53656
|
+
visited.delete({ sheetId, col, row });
|
|
53657
|
+
}
|
|
53658
|
+
}
|
|
53659
|
+
}
|
|
52952
53660
|
return visited;
|
|
52953
53661
|
}
|
|
52954
53662
|
}
|
|
@@ -53079,7 +53787,7 @@ class PositionSet {
|
|
|
53079
53787
|
return this.sheets[position.sheetId].getValue(position) === 1;
|
|
53080
53788
|
}
|
|
53081
53789
|
clear() {
|
|
53082
|
-
const insertions = this
|
|
53790
|
+
const insertions = [...this];
|
|
53083
53791
|
this.insertions = [];
|
|
53084
53792
|
for (const sheetId in this.sheets) {
|
|
53085
53793
|
this.sheets[sheetId].clear();
|
|
@@ -53423,6 +54131,7 @@ class Evaluator {
|
|
|
53423
54131
|
}
|
|
53424
54132
|
finally {
|
|
53425
54133
|
this.cellsBeingComputed.delete(cellId);
|
|
54134
|
+
this.nextPositionsToUpdate.delete(position);
|
|
53426
54135
|
}
|
|
53427
54136
|
}
|
|
53428
54137
|
computeAndSave(position) {
|
|
@@ -53449,8 +54158,33 @@ class Evaluator {
|
|
|
53449
54158
|
forEachSpreadPositionInMatrix(nbColumns, nbRows,
|
|
53450
54159
|
// thanks to the isMatrix check above, we know that formulaReturn is MatrixFunctionReturn
|
|
53451
54160
|
this.spreadValues(formulaPosition, formulaReturn));
|
|
54161
|
+
this.invalidatePositionsDependingOnSpread(formulaPosition, nbColumns, nbRows);
|
|
53452
54162
|
return createEvaluatedCell(nullValueToZeroValue(formulaReturn[0][0]), this.getters.getLocale(), cellData);
|
|
53453
54163
|
}
|
|
54164
|
+
invalidatePositionsDependingOnSpread(arrayFormulaPosition, nbColumns, nbRows) {
|
|
54165
|
+
// the result matrix is split in 2 zones to exclude the array formula position
|
|
54166
|
+
const top = arrayFormulaPosition.row;
|
|
54167
|
+
const left = arrayFormulaPosition.col;
|
|
54168
|
+
const bottom = top + nbRows - 1;
|
|
54169
|
+
const leftColumnZone = {
|
|
54170
|
+
top: top + 1,
|
|
54171
|
+
bottom,
|
|
54172
|
+
left,
|
|
54173
|
+
right: left,
|
|
54174
|
+
};
|
|
54175
|
+
const rightPartZone = {
|
|
54176
|
+
top,
|
|
54177
|
+
bottom,
|
|
54178
|
+
left: left + 1,
|
|
54179
|
+
right: left + nbColumns - 1,
|
|
54180
|
+
};
|
|
54181
|
+
const sheetId = arrayFormulaPosition.sheetId;
|
|
54182
|
+
const invalidatedPositions = this.formulaDependencies().getCellsDependingOn([
|
|
54183
|
+
{ sheetId, zone: rightPartZone },
|
|
54184
|
+
{ sheetId, zone: leftColumnZone },
|
|
54185
|
+
]);
|
|
54186
|
+
this.nextPositionsToUpdate.addMany(invalidatedPositions);
|
|
54187
|
+
}
|
|
53454
54188
|
assertSheetHasEnoughSpaceToSpreadFormulaResult({ sheetId, col, row }, matrixResult) {
|
|
53455
54189
|
const numberOfCols = this.getters.getNumberCols(sheetId);
|
|
53456
54190
|
const numberOfRows = this.getters.getNumberRows(sheetId);
|
|
@@ -53481,14 +54215,15 @@ class Evaluator {
|
|
|
53481
54215
|
}
|
|
53482
54216
|
updateSpreadRelation({ sheetId, col, row, }) {
|
|
53483
54217
|
const arrayFormulaPosition = { sheetId, col, row };
|
|
53484
|
-
|
|
54218
|
+
const updateSpreadRelation = (i, j) => {
|
|
53485
54219
|
const position = { sheetId, col: i + col, row: j + row };
|
|
53486
54220
|
this.spreadingRelations.addRelation({ resultPosition: position, arrayFormulaPosition });
|
|
53487
54221
|
};
|
|
54222
|
+
return updateSpreadRelation;
|
|
53488
54223
|
}
|
|
53489
54224
|
checkCollision(formulaPosition) {
|
|
53490
54225
|
const { sheetId, col, row } = formulaPosition;
|
|
53491
|
-
|
|
54226
|
+
const checkCollision = (i, j) => {
|
|
53492
54227
|
const position = { sheetId: sheetId, col: i + col, row: j + row };
|
|
53493
54228
|
const rawCell = this.getters.getCell(position);
|
|
53494
54229
|
if (rawCell?.content ||
|
|
@@ -53498,17 +54233,16 @@ class Evaluator {
|
|
|
53498
54233
|
}
|
|
53499
54234
|
this.blockedArrayFormulas.delete(formulaPosition);
|
|
53500
54235
|
};
|
|
54236
|
+
return checkCollision;
|
|
53501
54237
|
}
|
|
53502
54238
|
spreadValues({ sheetId, col, row }, matrixResult) {
|
|
53503
|
-
|
|
54239
|
+
const spreadValues = (i, j) => {
|
|
53504
54240
|
const position = { sheetId, col: i + col, row: j + row };
|
|
53505
54241
|
const cell = this.getters.getCell(position);
|
|
53506
54242
|
const evaluatedCell = createEvaluatedCell(nullValueToZeroValue(matrixResult[i][j]), this.getters.getLocale(), cell);
|
|
53507
54243
|
this.evaluatedCells.set(position, evaluatedCell);
|
|
53508
|
-
// check if formula dependencies present in the spread zone
|
|
53509
|
-
// if so, they need to be recomputed
|
|
53510
|
-
this.nextPositionsToUpdate.addMany(this.getCellsDependingOn([position]));
|
|
53511
54244
|
};
|
|
54245
|
+
return spreadValues;
|
|
53512
54246
|
}
|
|
53513
54247
|
invalidateSpreading(position) {
|
|
53514
54248
|
if (!this.spreadingRelations.isArrayFormula(position)) {
|
|
@@ -53563,12 +54297,12 @@ function forEachSpreadPositionInMatrix(nbColumns, nbRows, callback) {
|
|
|
53563
54297
|
* rather than appearing empty. This indicates that the
|
|
53564
54298
|
* cell is the result of a non-empty content.
|
|
53565
54299
|
*/
|
|
53566
|
-
function nullValueToZeroValue(
|
|
53567
|
-
if (
|
|
53568
|
-
// '
|
|
53569
|
-
return { ...
|
|
54300
|
+
function nullValueToZeroValue(functionResult) {
|
|
54301
|
+
if (functionResult.value === null || functionResult.value === undefined) {
|
|
54302
|
+
// 'functionResult.value === undefined' is supposed to never happen, it's a safety net for javascript use
|
|
54303
|
+
return { ...functionResult, value: 0 };
|
|
53570
54304
|
}
|
|
53571
|
-
return
|
|
54305
|
+
return functionResult;
|
|
53572
54306
|
}
|
|
53573
54307
|
function updateEvalContextAndExecute(compiledFormula, compilationParams, sheetId, cellId) {
|
|
53574
54308
|
compilationParams.evalContext.__originCellXC = lazy(() => {
|
|
@@ -55043,7 +55777,7 @@ class PivotUIPlugin extends UIPlugin {
|
|
|
55043
55777
|
getPivotIdFromPosition(position) {
|
|
55044
55778
|
const cell = this.getters.getCorrespondingFormulaCell(position);
|
|
55045
55779
|
if (cell && cell.isFormula) {
|
|
55046
|
-
const pivotFunction = this.getFirstPivotFunction(cell.compiledFormula.tokens);
|
|
55780
|
+
const pivotFunction = this.getFirstPivotFunction(position.sheetId, cell.compiledFormula.tokens);
|
|
55047
55781
|
if (pivotFunction) {
|
|
55048
55782
|
const pivotId = pivotFunction.args[0]?.toString();
|
|
55049
55783
|
return pivotId && this.getters.getPivotId(pivotId);
|
|
@@ -55054,12 +55788,12 @@ class PivotUIPlugin extends UIPlugin {
|
|
|
55054
55788
|
isSpillPivotFormula(position) {
|
|
55055
55789
|
const cell = this.getters.getCorrespondingFormulaCell(position);
|
|
55056
55790
|
if (cell && cell.isFormula) {
|
|
55057
|
-
const pivotFunction = this.getFirstPivotFunction(cell.compiledFormula.tokens);
|
|
55791
|
+
const pivotFunction = this.getFirstPivotFunction(position.sheetId, cell.compiledFormula.tokens);
|
|
55058
55792
|
return pivotFunction?.functionName === "PIVOT";
|
|
55059
55793
|
}
|
|
55060
55794
|
return false;
|
|
55061
55795
|
}
|
|
55062
|
-
getFirstPivotFunction(tokens) {
|
|
55796
|
+
getFirstPivotFunction(sheetId, tokens) {
|
|
55063
55797
|
const pivotFunction = getFirstPivotFunction(tokens);
|
|
55064
55798
|
if (!pivotFunction) {
|
|
55065
55799
|
return undefined;
|
|
@@ -55075,7 +55809,7 @@ class PivotUIPlugin extends UIPlugin {
|
|
|
55075
55809
|
return argAst.value;
|
|
55076
55810
|
}
|
|
55077
55811
|
const argsString = astToFormula(argAst);
|
|
55078
|
-
return this.getters.evaluateFormula(
|
|
55812
|
+
return this.getters.evaluateFormula(sheetId, argsString);
|
|
55079
55813
|
});
|
|
55080
55814
|
return { functionName, args: evaluatedArgs };
|
|
55081
55815
|
}
|
|
@@ -55098,7 +55832,7 @@ class PivotUIPlugin extends UIPlugin {
|
|
|
55098
55832
|
return EMPTY_PIVOT_CELL;
|
|
55099
55833
|
}
|
|
55100
55834
|
const mainPosition = this.getters.getCellPosition(cell.id);
|
|
55101
|
-
const result = this.getters.getFirstPivotFunction(cell.compiledFormula.tokens);
|
|
55835
|
+
const result = this.getters.getFirstPivotFunction(position.sheetId, cell.compiledFormula.tokens);
|
|
55102
55836
|
if (!result) {
|
|
55103
55837
|
return EMPTY_PIVOT_CELL;
|
|
55104
55838
|
}
|
|
@@ -57136,6 +57870,12 @@ class InsertPivotPlugin extends UIPlugin {
|
|
|
57136
57870
|
case "INSERT_NEW_PIVOT":
|
|
57137
57871
|
this.insertNewPivot(cmd.pivotId, cmd.newSheetId);
|
|
57138
57872
|
break;
|
|
57873
|
+
case "DUPLICATE_PIVOT_IN_NEW_SHEET":
|
|
57874
|
+
this.duplicatePivotInNewSheet(cmd.pivotId, cmd.newPivotId, cmd.newSheetId);
|
|
57875
|
+
break;
|
|
57876
|
+
case "INSERT_PIVOT_WITH_TABLE":
|
|
57877
|
+
this.insertPivotWithTable(cmd.sheetId, cmd.col, cmd.row, cmd.pivotId, cmd.table);
|
|
57878
|
+
break;
|
|
57139
57879
|
}
|
|
57140
57880
|
}
|
|
57141
57881
|
insertNewPivot(pivotId, sheetId) {
|
|
@@ -57161,7 +57901,7 @@ class InsertPivotPlugin extends UIPlugin {
|
|
|
57161
57901
|
const formulaId = this.getters.getPivotFormulaId(pivotId);
|
|
57162
57902
|
this.dispatch("CREATE_SHEET", {
|
|
57163
57903
|
sheetId,
|
|
57164
|
-
name: _t("Pivot #%s", formulaId),
|
|
57904
|
+
name: _t("Pivot #%(formulaId)s", { formulaId }),
|
|
57165
57905
|
position,
|
|
57166
57906
|
});
|
|
57167
57907
|
this.dispatch("ACTIVATE_SHEET", {
|
|
@@ -57175,6 +57915,92 @@ class InsertPivotPlugin extends UIPlugin {
|
|
|
57175
57915
|
content: `=PIVOT(${formulaId})`,
|
|
57176
57916
|
});
|
|
57177
57917
|
}
|
|
57918
|
+
duplicatePivotInNewSheet(pivotId, newPivotId, newSheetId) {
|
|
57919
|
+
this.dispatch("DUPLICATE_PIVOT", {
|
|
57920
|
+
pivotId,
|
|
57921
|
+
newPivotId,
|
|
57922
|
+
});
|
|
57923
|
+
const activeSheetId = this.getters.getActiveSheetId();
|
|
57924
|
+
const position = this.getters.getSheetIds().indexOf(activeSheetId) + 1;
|
|
57925
|
+
const formulaId = this.getters.getPivotFormulaId(newPivotId);
|
|
57926
|
+
const newPivotName = this.getters.getPivotName(newPivotId);
|
|
57927
|
+
this.dispatch("CREATE_SHEET", {
|
|
57928
|
+
sheetId: newSheetId,
|
|
57929
|
+
name: this.getPivotDuplicateSheetName(_t("%(newPivotName)s (Pivot #%(formulaId)s)", {
|
|
57930
|
+
newPivotName,
|
|
57931
|
+
formulaId,
|
|
57932
|
+
})),
|
|
57933
|
+
position,
|
|
57934
|
+
});
|
|
57935
|
+
this.dispatch("ACTIVATE_SHEET", { sheetIdFrom: activeSheetId, sheetIdTo: newSheetId });
|
|
57936
|
+
this.dispatch("UPDATE_CELL", {
|
|
57937
|
+
sheetId: newSheetId,
|
|
57938
|
+
col: 0,
|
|
57939
|
+
row: 0,
|
|
57940
|
+
content: `=PIVOT(${formulaId})`,
|
|
57941
|
+
});
|
|
57942
|
+
}
|
|
57943
|
+
getPivotDuplicateSheetName(pivotName) {
|
|
57944
|
+
let i = 1;
|
|
57945
|
+
const names = this.getters.getSheetIds().map((id) => this.getters.getSheetName(id));
|
|
57946
|
+
let name = pivotName;
|
|
57947
|
+
while (names.includes(name)) {
|
|
57948
|
+
name = `${pivotName} (${i})`;
|
|
57949
|
+
i++;
|
|
57950
|
+
}
|
|
57951
|
+
return name;
|
|
57952
|
+
}
|
|
57953
|
+
insertPivotWithTable(sheetId, col, row, pivotId, table) {
|
|
57954
|
+
const { cols, rows, measures, fieldsType } = table;
|
|
57955
|
+
const pivotTable = new SpreadsheetPivotTable(cols, rows, measures, fieldsType || {});
|
|
57956
|
+
this.resizeSheet(sheetId, col, row, pivotTable);
|
|
57957
|
+
const pivotFormulaId = this.getters.getPivotFormulaId(pivotId);
|
|
57958
|
+
this.dispatch("UPDATE_CELL", {
|
|
57959
|
+
sheetId,
|
|
57960
|
+
col,
|
|
57961
|
+
row,
|
|
57962
|
+
content: `=PIVOT("${pivotFormulaId}")`,
|
|
57963
|
+
});
|
|
57964
|
+
const zone = {
|
|
57965
|
+
left: col,
|
|
57966
|
+
right: col,
|
|
57967
|
+
top: row,
|
|
57968
|
+
bottom: row,
|
|
57969
|
+
};
|
|
57970
|
+
const numberOfHeaders = pivotTable.columns.length - 1;
|
|
57971
|
+
this.dispatch("CREATE_TABLE", {
|
|
57972
|
+
tableType: "dynamic",
|
|
57973
|
+
sheetId,
|
|
57974
|
+
ranges: [this.getters.getRangeDataFromZone(sheetId, zone)],
|
|
57975
|
+
config: { ...PIVOT_TABLE_CONFIG, numberOfHeaders },
|
|
57976
|
+
});
|
|
57977
|
+
}
|
|
57978
|
+
resizeSheet(sheetId, col, row, table) {
|
|
57979
|
+
const colLimit = table.getNumberOfDataColumns() + 1; // +1 for the Top-Left
|
|
57980
|
+
const numberCols = this.getters.getNumberCols(sheetId);
|
|
57981
|
+
const deltaCol = numberCols - col;
|
|
57982
|
+
if (deltaCol < colLimit) {
|
|
57983
|
+
this.dispatch("ADD_COLUMNS_ROWS", {
|
|
57984
|
+
dimension: "COL",
|
|
57985
|
+
base: numberCols - 1,
|
|
57986
|
+
sheetId: sheetId,
|
|
57987
|
+
quantity: colLimit - deltaCol,
|
|
57988
|
+
position: "after",
|
|
57989
|
+
});
|
|
57990
|
+
}
|
|
57991
|
+
const rowLimit = table.columns.length + table.rows.length;
|
|
57992
|
+
const numberRows = this.getters.getNumberRows(sheetId);
|
|
57993
|
+
const deltaRow = numberRows - row;
|
|
57994
|
+
if (deltaRow < rowLimit) {
|
|
57995
|
+
this.dispatch("ADD_COLUMNS_ROWS", {
|
|
57996
|
+
dimension: "ROW",
|
|
57997
|
+
base: numberRows - 1,
|
|
57998
|
+
sheetId: sheetId,
|
|
57999
|
+
quantity: rowLimit - deltaRow,
|
|
58000
|
+
position: "after",
|
|
58001
|
+
});
|
|
58002
|
+
}
|
|
58003
|
+
}
|
|
57178
58004
|
}
|
|
57179
58005
|
|
|
57180
58006
|
class SortPlugin extends UIPlugin {
|
|
@@ -57355,6 +58181,7 @@ class SheetUIPlugin extends UIPlugin {
|
|
|
57355
58181
|
static getters = [
|
|
57356
58182
|
"doesCellHaveGridIcon",
|
|
57357
58183
|
"getCellWidth",
|
|
58184
|
+
"getCellIconSrc",
|
|
57358
58185
|
"getTextWidth",
|
|
57359
58186
|
"getCellText",
|
|
57360
58187
|
"getCellMultiLineText",
|
|
@@ -57405,7 +58232,7 @@ class SheetUIPlugin extends UIPlugin {
|
|
|
57405
58232
|
const multiLineText = splitTextToWidth(this.ctx, content, style, undefined);
|
|
57406
58233
|
contentWidth += Math.max(...multiLineText.map((line) => computeTextWidth(this.ctx, line, style)));
|
|
57407
58234
|
}
|
|
57408
|
-
const icon = this.getters.
|
|
58235
|
+
const icon = this.getters.getCellIconSrc(position);
|
|
57409
58236
|
if (icon) {
|
|
57410
58237
|
contentWidth += computeIconWidth(style);
|
|
57411
58238
|
}
|
|
@@ -57422,6 +58249,16 @@ class SheetUIPlugin extends UIPlugin {
|
|
|
57422
58249
|
}
|
|
57423
58250
|
return contentWidth;
|
|
57424
58251
|
}
|
|
58252
|
+
getCellIconSrc(position) {
|
|
58253
|
+
const callbacks = iconsOnCellRegistry.getAll();
|
|
58254
|
+
for (const callback of callbacks) {
|
|
58255
|
+
const imageSrc = callback(this.getters, position);
|
|
58256
|
+
if (imageSrc) {
|
|
58257
|
+
return imageSrc;
|
|
58258
|
+
}
|
|
58259
|
+
}
|
|
58260
|
+
return undefined;
|
|
58261
|
+
}
|
|
57425
58262
|
getTextWidth(text, style) {
|
|
57426
58263
|
return computeTextWidth(this.ctx, text, style);
|
|
57427
58264
|
}
|
|
@@ -61197,7 +62034,7 @@ class BottomBarSheet extends owl.Component {
|
|
|
61197
62034
|
this.DOMFocusableElementStore.focus();
|
|
61198
62035
|
}
|
|
61199
62036
|
}
|
|
61200
|
-
|
|
62037
|
+
onMouseEventSheetName(ev) {
|
|
61201
62038
|
if (this.state.isEditing)
|
|
61202
62039
|
ev.stopPropagation();
|
|
61203
62040
|
}
|
|
@@ -62925,7 +63762,7 @@ class TopBar extends owl.Component {
|
|
|
62925
63762
|
}
|
|
62926
63763
|
get topbarComponents() {
|
|
62927
63764
|
return topbarComponentRegistry
|
|
62928
|
-
.
|
|
63765
|
+
.getAllOrdered()
|
|
62929
63766
|
.filter((item) => !item.isVisible || item.isVisible(this.env));
|
|
62930
63767
|
}
|
|
62931
63768
|
onExternalClick(ev) {
|
|
@@ -67338,6 +68175,7 @@ const registries = {
|
|
|
67338
68175
|
linkMenuRegistry,
|
|
67339
68176
|
functionRegistry,
|
|
67340
68177
|
featurePluginRegistry,
|
|
68178
|
+
iconsOnCellRegistry,
|
|
67341
68179
|
statefulUIPluginRegistry,
|
|
67342
68180
|
coreViewsPluginRegistry,
|
|
67343
68181
|
corePluginRegistry,
|
|
@@ -67555,6 +68393,6 @@ exports.tokenColors = tokenColors;
|
|
|
67555
68393
|
exports.tokenize = tokenize;
|
|
67556
68394
|
|
|
67557
68395
|
|
|
67558
|
-
__info__.version = "17.4.0
|
|
67559
|
-
__info__.date = "2024-
|
|
67560
|
-
__info__.hash = "
|
|
68396
|
+
__info__.version = "17.4.0";
|
|
68397
|
+
__info__.date = "2024-07-12T04:41:51.675Z";
|
|
68398
|
+
__info__.hash = "f8e8543";
|