@odoo/o-spreadsheet 17.4.0-alpha.9 → 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 +2005 -1170
- package/dist/o-spreadsheet.d.ts +136 -52
- package/dist/o-spreadsheet.esm.js +2005 -1170
- package/dist/o-spreadsheet.iife.js +2005 -1170
- 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
|
/**
|
|
@@ -31893,6 +32450,7 @@ class ChartWithAxisDesignPanel extends owl.Component {
|
|
|
31893
32450
|
Section,
|
|
31894
32451
|
AxisDesignEditor,
|
|
31895
32452
|
RoundColorPicker,
|
|
32453
|
+
Checkbox,
|
|
31896
32454
|
};
|
|
31897
32455
|
static props = {
|
|
31898
32456
|
figureId: String,
|
|
@@ -31985,6 +32543,12 @@ class ChartWithAxisDesignPanel extends owl.Component {
|
|
|
31985
32543
|
const dataSets = this.props.definition.dataSets;
|
|
31986
32544
|
return dataSets[this.state.index]?.label || this.getDataSeries()[this.state.index];
|
|
31987
32545
|
}
|
|
32546
|
+
get showValuesLabel() {
|
|
32547
|
+
return ChartTerms.ShowValues;
|
|
32548
|
+
}
|
|
32549
|
+
updateShowValues(showValues) {
|
|
32550
|
+
this.props.updateChart(this.props.figureId, { showValues });
|
|
32551
|
+
}
|
|
31988
32552
|
}
|
|
31989
32553
|
|
|
31990
32554
|
class GaugeChartConfigPanel extends owl.Component {
|
|
@@ -32134,6 +32698,10 @@ class LineConfigPanel extends GenericChartConfigPanel {
|
|
|
32134
32698
|
}
|
|
32135
32699
|
return false;
|
|
32136
32700
|
}
|
|
32701
|
+
get stackedLabel() {
|
|
32702
|
+
const definition = this.props.definition;
|
|
32703
|
+
return definition.fillArea ? this.chartTerms.StackedAreaChart : this.chartTerms.StackedBarChart;
|
|
32704
|
+
}
|
|
32137
32705
|
getLabelRangeOptions() {
|
|
32138
32706
|
const options = super.getLabelRangeOptions();
|
|
32139
32707
|
if (this.canTreatLabelsAsText) {
|
|
@@ -32173,6 +32741,7 @@ class PieChartDesignPanel extends owl.Component {
|
|
|
32173
32741
|
static components = {
|
|
32174
32742
|
GeneralDesignEditor,
|
|
32175
32743
|
Section,
|
|
32744
|
+
Checkbox,
|
|
32176
32745
|
};
|
|
32177
32746
|
static props = {
|
|
32178
32747
|
figureId: String,
|
|
@@ -32185,6 +32754,9 @@ class PieChartDesignPanel extends owl.Component {
|
|
|
32185
32754
|
legendPosition: ev.target.value,
|
|
32186
32755
|
});
|
|
32187
32756
|
}
|
|
32757
|
+
get showValuesLabel() {
|
|
32758
|
+
return ChartTerms.ShowValues;
|
|
32759
|
+
}
|
|
32188
32760
|
}
|
|
32189
32761
|
|
|
32190
32762
|
class ScatterConfigPanel extends GenericChartConfigPanel {
|
|
@@ -32379,6 +32951,12 @@ class WaterfallChartDesignPanel extends owl.Component {
|
|
|
32379
32951
|
verticalAxisPosition: ev.target.value,
|
|
32380
32952
|
});
|
|
32381
32953
|
}
|
|
32954
|
+
get showValuesLabel() {
|
|
32955
|
+
return ChartTerms.ShowValues;
|
|
32956
|
+
}
|
|
32957
|
+
updateShowValues(showValues) {
|
|
32958
|
+
this.props.updateChart(this.props.figureId, { showValues });
|
|
32959
|
+
}
|
|
32382
32960
|
}
|
|
32383
32961
|
|
|
32384
32962
|
const chartSidePanelComponentRegistry = new Registry();
|
|
@@ -32639,6 +33217,11 @@ class ChartPanel extends owl.Component {
|
|
|
32639
33217
|
}
|
|
32640
33218
|
}
|
|
32641
33219
|
|
|
33220
|
+
/**
|
|
33221
|
+
* Registry to draw icons on cells
|
|
33222
|
+
*/
|
|
33223
|
+
const iconsOnCellRegistry = new Registry();
|
|
33224
|
+
|
|
32642
33225
|
css /* scss */ `
|
|
32643
33226
|
.o-spreadsheet {
|
|
32644
33227
|
.o-icon {
|
|
@@ -32674,49 +33257,47 @@ const FROWN = '<svg class="o-cf-icon frown" width="10" height="10" focusable="fa
|
|
|
32674
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>';
|
|
32675
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>';
|
|
32676
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>';
|
|
32677
|
-
function
|
|
33260
|
+
function getIconSrc(svg) {
|
|
32678
33261
|
/** We have to add xmlns, as it's not added by owl in the canvas */
|
|
32679
33262
|
svg = `<svg xmlns="http://www.w3.org/2000/svg" ${svg.slice(4)}`;
|
|
32680
|
-
|
|
32681
|
-
image.src = "data:image/svg+xml; charset=utf8, " + encodeURIComponent(svg);
|
|
32682
|
-
return image;
|
|
33263
|
+
return "data:image/svg+xml; charset=utf8, " + encodeURIComponent(svg);
|
|
32683
33264
|
}
|
|
32684
33265
|
const ICONS = {
|
|
32685
33266
|
arrowGood: {
|
|
32686
33267
|
template: "ARROW_UP",
|
|
32687
|
-
img:
|
|
33268
|
+
img: getIconSrc(ARROW_UP),
|
|
32688
33269
|
},
|
|
32689
33270
|
arrowNeutral: {
|
|
32690
33271
|
template: "ARROW_RIGHT",
|
|
32691
|
-
img:
|
|
33272
|
+
img: getIconSrc(ARROW_RIGHT),
|
|
32692
33273
|
},
|
|
32693
33274
|
arrowBad: {
|
|
32694
33275
|
template: "ARROW_DOWN",
|
|
32695
|
-
img:
|
|
33276
|
+
img: getIconSrc(ARROW_DOWN),
|
|
32696
33277
|
},
|
|
32697
33278
|
smileyGood: {
|
|
32698
33279
|
template: "SMILE",
|
|
32699
|
-
img:
|
|
33280
|
+
img: getIconSrc(SMILE),
|
|
32700
33281
|
},
|
|
32701
33282
|
smileyNeutral: {
|
|
32702
33283
|
template: "MEH",
|
|
32703
|
-
img:
|
|
33284
|
+
img: getIconSrc(MEH),
|
|
32704
33285
|
},
|
|
32705
33286
|
smileyBad: {
|
|
32706
33287
|
template: "FROWN",
|
|
32707
|
-
img:
|
|
33288
|
+
img: getIconSrc(FROWN),
|
|
32708
33289
|
},
|
|
32709
33290
|
dotGood: {
|
|
32710
33291
|
template: "GREEN_DOT",
|
|
32711
|
-
img:
|
|
33292
|
+
img: getIconSrc(GREEN_DOT),
|
|
32712
33293
|
},
|
|
32713
33294
|
dotNeutral: {
|
|
32714
33295
|
template: "YELLOW_DOT",
|
|
32715
|
-
img:
|
|
33296
|
+
img: getIconSrc(YELLOW_DOT),
|
|
32716
33297
|
},
|
|
32717
33298
|
dotBad: {
|
|
32718
33299
|
template: "RED_DOT",
|
|
32719
|
-
img:
|
|
33300
|
+
img: getIconSrc(RED_DOT),
|
|
32720
33301
|
},
|
|
32721
33302
|
};
|
|
32722
33303
|
const ICON_SETS = {
|
|
@@ -32736,6 +33317,12 @@ const ICON_SETS = {
|
|
|
32736
33317
|
bad: "dotBad",
|
|
32737
33318
|
},
|
|
32738
33319
|
};
|
|
33320
|
+
iconsOnCellRegistry.add("conditional_formatting", (getters, position) => {
|
|
33321
|
+
const icon = getters.getConditionalIcon(position);
|
|
33322
|
+
if (icon) {
|
|
33323
|
+
return ICONS[icon].img;
|
|
33324
|
+
}
|
|
33325
|
+
});
|
|
32739
33326
|
|
|
32740
33327
|
css /* scss */ `
|
|
32741
33328
|
.o-icon-picker {
|
|
@@ -33084,21 +33671,25 @@ function useHoveredElement(ref) {
|
|
|
33084
33671
|
|
|
33085
33672
|
function useHighlightsOnHover(ref, highlightProvider) {
|
|
33086
33673
|
const hoverState = useHoveredElement(ref);
|
|
33087
|
-
const stores = useStoreProvider();
|
|
33088
33674
|
useHighlights({
|
|
33089
33675
|
get highlights() {
|
|
33090
33676
|
return hoverState.hovered ? highlightProvider.highlights : [];
|
|
33091
33677
|
},
|
|
33092
33678
|
});
|
|
33093
|
-
owl.useEffect(() => {
|
|
33094
|
-
stores.trigger("store-updated");
|
|
33095
|
-
}, () => [hoverState.hovered]);
|
|
33096
33679
|
}
|
|
33097
33680
|
function useHighlights(highlightProvider) {
|
|
33681
|
+
const stores = useStoreProvider();
|
|
33098
33682
|
const store = useLocalStore(HighlightStore);
|
|
33099
33683
|
owl.onMounted(() => {
|
|
33100
33684
|
store.register(highlightProvider);
|
|
33101
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]);
|
|
33102
33693
|
}
|
|
33103
33694
|
|
|
33104
33695
|
css /* scss */ `
|
|
@@ -34570,6 +35161,8 @@ class FindAndReplaceStore extends SpreadsheetStore {
|
|
|
34570
35161
|
isSearchDirty = false;
|
|
34571
35162
|
initialShowFormulaState;
|
|
34572
35163
|
preserveSelectedMatchIndex = false;
|
|
35164
|
+
irreplaceableMatchCount = 0;
|
|
35165
|
+
notificationStore = this.get(NotificationStore);
|
|
34573
35166
|
// fixme: why do we make selectedMatchIndex on top of a selected
|
|
34574
35167
|
// property in the matches?
|
|
34575
35168
|
selectedMatchIndex = null;
|
|
@@ -34579,7 +35172,7 @@ class FindAndReplaceStore extends SpreadsheetStore {
|
|
|
34579
35172
|
matchCase: false,
|
|
34580
35173
|
exactMatch: false,
|
|
34581
35174
|
searchFormulas: false,
|
|
34582
|
-
searchScope: "
|
|
35175
|
+
searchScope: "activeSheet",
|
|
34583
35176
|
specificRange: undefined,
|
|
34584
35177
|
};
|
|
34585
35178
|
updateSearchContent = debounce(this._updateSearchContent.bind(this), 200);
|
|
@@ -34646,6 +35239,11 @@ class FindAndReplaceStore extends SpreadsheetStore {
|
|
|
34646
35239
|
for (const match of cmd.matches) {
|
|
34647
35240
|
this.replaceMatch(match, cmd.searchString, cmd.replaceWith, cmd.searchOptions);
|
|
34648
35241
|
}
|
|
35242
|
+
if (this.irreplaceableMatchCount > 0) {
|
|
35243
|
+
this.showReplaceWarningMessage(cmd.matches.length, this.irreplaceableMatchCount);
|
|
35244
|
+
}
|
|
35245
|
+
this.irreplaceableMatchCount = 0;
|
|
35246
|
+
break;
|
|
34649
35247
|
}
|
|
34650
35248
|
}
|
|
34651
35249
|
finalize() {
|
|
@@ -34827,12 +35425,36 @@ class FindAndReplaceStore extends SpreadsheetStore {
|
|
|
34827
35425
|
searchOptions: this.searchOptions,
|
|
34828
35426
|
});
|
|
34829
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
|
+
}
|
|
34830
35451
|
replaceMatch(selectedMatch, searchString, replaceWith, searchOptions) {
|
|
34831
35452
|
const cell = this.getters.getCell(selectedMatch);
|
|
34832
35453
|
if (!cell?.content) {
|
|
34833
35454
|
return;
|
|
34834
35455
|
}
|
|
34835
35456
|
if (cell?.isFormula && !searchOptions.searchFormulas) {
|
|
35457
|
+
this.irreplaceableMatchCount++;
|
|
34836
35458
|
return;
|
|
34837
35459
|
}
|
|
34838
35460
|
const searchRegex = getSearchRegex(searchString, searchOptions);
|
|
@@ -35509,9 +36131,11 @@ class PivotTitleSection extends owl.Component {
|
|
|
35509
36131
|
}
|
|
35510
36132
|
duplicatePivot() {
|
|
35511
36133
|
const newPivotId = this.env.model.uuidGenerator.uuidv4();
|
|
35512
|
-
const
|
|
36134
|
+
const newSheetId = this.env.model.uuidGenerator.uuidv4();
|
|
36135
|
+
const result = this.env.model.dispatch("DUPLICATE_PIVOT_IN_NEW_SHEET", {
|
|
35513
36136
|
pivotId: this.props.pivotId,
|
|
35514
36137
|
newPivotId,
|
|
36138
|
+
newSheetId,
|
|
35515
36139
|
});
|
|
35516
36140
|
const text = result.isSuccessful ? _t("Pivot duplicated.") : _t("Pivot duplication failed");
|
|
35517
36141
|
const type = result.isSuccessful ? "success" : "danger";
|
|
@@ -36046,7 +36670,7 @@ function createDate(dimension, value, locale) {
|
|
|
36046
36670
|
if (typeof value === "number" || typeof value === "string") {
|
|
36047
36671
|
const date = toJsDate(value, locale);
|
|
36048
36672
|
switch (granularity) {
|
|
36049
|
-
case "
|
|
36673
|
+
case "year":
|
|
36050
36674
|
number = date.getFullYear();
|
|
36051
36675
|
break;
|
|
36052
36676
|
case "quarter_number":
|
|
@@ -36074,7 +36698,7 @@ function createDate(dimension, value, locale) {
|
|
|
36074
36698
|
* This map is used to cache the different values of a pivot date value
|
|
36075
36699
|
* 43_831 -> 01/01/2012
|
|
36076
36700
|
* Example: {
|
|
36077
|
-
*
|
|
36701
|
+
* year: {
|
|
36078
36702
|
* set: { 43_831 },
|
|
36079
36703
|
* values: { '43_831': 2012 }
|
|
36080
36704
|
* },
|
|
@@ -36101,7 +36725,7 @@ function createDate(dimension, value, locale) {
|
|
|
36101
36725
|
* }
|
|
36102
36726
|
*/
|
|
36103
36727
|
const MAP_VALUE_DIMENSION_DATE = {
|
|
36104
|
-
|
|
36728
|
+
year: {
|
|
36105
36729
|
set: new Set(),
|
|
36106
36730
|
values: {},
|
|
36107
36731
|
},
|
|
@@ -36486,7 +37110,7 @@ pivotRegistry.add("SPREADSHEET", {
|
|
|
36486
37110
|
externalData: false,
|
|
36487
37111
|
onIterationEndEvaluation: (pivot) => pivot.markAsDirtyForEvaluation(),
|
|
36488
37112
|
granularities: [
|
|
36489
|
-
"
|
|
37113
|
+
"year",
|
|
36490
37114
|
"quarter_number",
|
|
36491
37115
|
"month_number",
|
|
36492
37116
|
"iso_week_number",
|
|
@@ -36896,7 +37520,13 @@ class SettingsPanel extends owl.Component {
|
|
|
36896
37520
|
}
|
|
36897
37521
|
async loadLocales() {
|
|
36898
37522
|
this.loadedLocales = (await this.env.loadLocales())
|
|
36899
|
-
.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
|
+
})
|
|
36900
37530
|
.sort((a, b) => a.name.localeCompare(b.name));
|
|
36901
37531
|
}
|
|
36902
37532
|
get numberFormatPreview() {
|
|
@@ -37955,6 +38585,9 @@ class TopBarComponentRegistry extends Registry {
|
|
|
37955
38585
|
const component = { ...value, id: this.uuidGenerator.uuidv4() };
|
|
37956
38586
|
return super.add(name, component);
|
|
37957
38587
|
}
|
|
38588
|
+
getAllOrdered() {
|
|
38589
|
+
return this.getAll().sort((a, b) => a.sequence - b.sequence);
|
|
38590
|
+
}
|
|
37958
38591
|
}
|
|
37959
38592
|
const topbarComponentRegistry = new TopBarComponentRegistry();
|
|
37960
38593
|
|
|
@@ -40843,21 +41476,22 @@ class GridRenderer {
|
|
|
40843
41476
|
isError: (cell.type === CellValueType.error && !!cell.message) ||
|
|
40844
41477
|
this.getters.isDataValidationInvalid(position),
|
|
40845
41478
|
};
|
|
40846
|
-
|
|
40847
|
-
|
|
40848
|
-
}
|
|
40849
|
-
/** Icon CF */
|
|
40850
|
-
const cfIcon = this.getters.getConditionalIcon(position);
|
|
41479
|
+
/** Icon */
|
|
41480
|
+
const iconSrc = this.getters.getCellIconSrc(position);
|
|
40851
41481
|
const fontSizePX = computeTextFontSizeInPixels(box.style);
|
|
40852
|
-
const iconBoxWidth =
|
|
40853
|
-
if (
|
|
41482
|
+
const iconBoxWidth = iconSrc ? MIN_CF_ICON_MARGIN + fontSizePX : 0;
|
|
41483
|
+
if (iconSrc) {
|
|
41484
|
+
const imageHtmlElement = loadIconImage(iconSrc);
|
|
40854
41485
|
box.image = {
|
|
40855
41486
|
type: "icon",
|
|
40856
41487
|
size: fontSizePX,
|
|
40857
41488
|
clipIcon: { x: box.x, y: box.y, width: Math.min(iconBoxWidth, width), height },
|
|
40858
|
-
image:
|
|
41489
|
+
image: imageHtmlElement,
|
|
40859
41490
|
};
|
|
40860
41491
|
}
|
|
41492
|
+
if (cell.type === CellValueType.empty || this.getters.isCellValidCheckbox(position)) {
|
|
41493
|
+
return box;
|
|
41494
|
+
}
|
|
40861
41495
|
/** Filter Header or data validation icon */
|
|
40862
41496
|
box.hasIcon = this.getters.doesCellHaveGridIcon(position);
|
|
40863
41497
|
const headerIconWidth = box.hasIcon ? GRID_ICON_EDGE_LENGTH + GRID_ICON_MARGIN : 0;
|
|
@@ -40876,7 +41510,7 @@ class GridRenderer {
|
|
|
40876
41510
|
};
|
|
40877
41511
|
/** ClipRect */
|
|
40878
41512
|
const isOverflowing = contentWidth > width || fontSizePX > height;
|
|
40879
|
-
if (
|
|
41513
|
+
if (iconSrc || box.hasIcon) {
|
|
40880
41514
|
box.clipRect = {
|
|
40881
41515
|
x: box.x + iconBoxWidth,
|
|
40882
41516
|
y: box.y,
|
|
@@ -40987,6 +41621,11 @@ class GridRenderer {
|
|
|
40987
41621
|
return boxes;
|
|
40988
41622
|
}
|
|
40989
41623
|
}
|
|
41624
|
+
const loadIconImage = memoize(function loadIconImage(src) {
|
|
41625
|
+
const image = new Image();
|
|
41626
|
+
image.src = src;
|
|
41627
|
+
return image;
|
|
41628
|
+
});
|
|
40990
41629
|
|
|
40991
41630
|
function useGridDrawing(refName, model, canvasSize) {
|
|
40992
41631
|
const canvasRef = owl.useRef(refName);
|
|
@@ -42059,6 +42698,7 @@ class Grid extends owl.Component {
|
|
|
42059
42698
|
return;
|
|
42060
42699
|
}
|
|
42061
42700
|
if (clipboardData.types.indexOf(ClipboardMIMEType.PlainText) > -1) {
|
|
42701
|
+
ev.preventDefault();
|
|
42062
42702
|
const content = clipboardData.getData(ClipboardMIMEType.PlainText);
|
|
42063
42703
|
const target = this.env.model.getters.getSelectedZones();
|
|
42064
42704
|
const clipboardString = this.env.model.getters.getClipboardTextContent();
|
|
@@ -42325,6 +42965,8 @@ const NON_RETROCOMPATIBLE_FUNCTIONS = [
|
|
|
42325
42965
|
"BITOR",
|
|
42326
42966
|
"BITRSHIFT",
|
|
42327
42967
|
"BITXOR",
|
|
42968
|
+
"BYCOL",
|
|
42969
|
+
"BYROW",
|
|
42328
42970
|
"CEILING.MATH",
|
|
42329
42971
|
"CEILING.PRECISE",
|
|
42330
42972
|
"CHISQ.DIST",
|
|
@@ -42332,6 +42974,8 @@ const NON_RETROCOMPATIBLE_FUNCTIONS = [
|
|
|
42332
42974
|
"CHISQ.INV",
|
|
42333
42975
|
"CHISQ.INV.RT",
|
|
42334
42976
|
"CHISQ.TEST",
|
|
42977
|
+
"CHOOSECOLS",
|
|
42978
|
+
"CHOOSEROWS",
|
|
42335
42979
|
"COMBINA",
|
|
42336
42980
|
"CONCAT",
|
|
42337
42981
|
"CONFIDENCE.NORM",
|
|
@@ -42344,14 +42988,17 @@ const NON_RETROCOMPATIBLE_FUNCTIONS = [
|
|
|
42344
42988
|
"CSCH",
|
|
42345
42989
|
"DAYS",
|
|
42346
42990
|
"DECIMAL",
|
|
42991
|
+
"DROP",
|
|
42347
42992
|
"ERF.PRECISE",
|
|
42348
42993
|
"ERFC.PRECISE",
|
|
42994
|
+
"EXPAND",
|
|
42349
42995
|
"EXPON.DIST",
|
|
42350
42996
|
"F.DIST",
|
|
42351
42997
|
"F.DIST.RT",
|
|
42352
42998
|
"F.INV",
|
|
42353
42999
|
"F.INV.RT",
|
|
42354
43000
|
"F.TEST",
|
|
43001
|
+
"FIELDVALUE",
|
|
42355
43002
|
"FILTERXML",
|
|
42356
43003
|
"FLOOR.MATH",
|
|
42357
43004
|
"FLOOR.PRECISE",
|
|
@@ -42366,6 +43013,7 @@ const NON_RETROCOMPATIBLE_FUNCTIONS = [
|
|
|
42366
43013
|
"GAMMA.INV",
|
|
42367
43014
|
"GAMMALN.PRECISE",
|
|
42368
43015
|
"GAUSS",
|
|
43016
|
+
"HSTACK",
|
|
42369
43017
|
"HYPGEOM.DIST",
|
|
42370
43018
|
"IFNA",
|
|
42371
43019
|
"IFS",
|
|
@@ -42378,9 +43026,14 @@ const NON_RETROCOMPATIBLE_FUNCTIONS = [
|
|
|
42378
43026
|
"IMSINH",
|
|
42379
43027
|
"IMTAN",
|
|
42380
43028
|
"ISFORMULA",
|
|
43029
|
+
"ISOMITTED",
|
|
42381
43030
|
"ISOWEEKNUM",
|
|
43031
|
+
"LAMBDA",
|
|
43032
|
+
"LET",
|
|
42382
43033
|
"LOGNORM.DIST",
|
|
42383
43034
|
"LOGNORM.INV",
|
|
43035
|
+
"MAKEARRAY",
|
|
43036
|
+
"MAP",
|
|
42384
43037
|
"MAXIFS",
|
|
42385
43038
|
"MINIFS",
|
|
42386
43039
|
"MODE.MULT",
|
|
@@ -42400,17 +43053,26 @@ const NON_RETROCOMPATIBLE_FUNCTIONS = [
|
|
|
42400
43053
|
"PERMUTATIONA",
|
|
42401
43054
|
"PHI",
|
|
42402
43055
|
"POISSON.DIST",
|
|
43056
|
+
"PQSOURCE",
|
|
43057
|
+
"PYTHON_STR",
|
|
43058
|
+
"PYTHON_TYPE",
|
|
43059
|
+
"PYTHON_TYPENAME",
|
|
42403
43060
|
"QUARTILE.EXC",
|
|
42404
43061
|
"QUARTILE.INC",
|
|
42405
43062
|
"QUERYSTRING",
|
|
43063
|
+
"RANDARRAY",
|
|
42406
43064
|
"RANK.AVG",
|
|
42407
43065
|
"RANK.EQ",
|
|
43066
|
+
"REDUCE",
|
|
42408
43067
|
"RRI",
|
|
43068
|
+
"SCAN",
|
|
42409
43069
|
"SEC",
|
|
42410
43070
|
"SECH",
|
|
43071
|
+
"SEQUENCE",
|
|
42411
43072
|
"SHEET",
|
|
42412
43073
|
"SHEETS",
|
|
42413
43074
|
"SKEW.P",
|
|
43075
|
+
"SORTBY",
|
|
42414
43076
|
"STDEV.P",
|
|
42415
43077
|
"STDEV.S",
|
|
42416
43078
|
"SWITCH",
|
|
@@ -42420,13 +43082,24 @@ const NON_RETROCOMPATIBLE_FUNCTIONS = [
|
|
|
42420
43082
|
"T.INV",
|
|
42421
43083
|
"T.INV.2T",
|
|
42422
43084
|
"T.TEST",
|
|
43085
|
+
"TAKE",
|
|
43086
|
+
"TEXTAFTER",
|
|
43087
|
+
"TEXTBEFORE",
|
|
42423
43088
|
"TEXTJOIN",
|
|
43089
|
+
"TEXTSPLIT",
|
|
43090
|
+
"TOCOL",
|
|
43091
|
+
"TOROW",
|
|
42424
43092
|
"UNICHAR",
|
|
42425
43093
|
"UNICODE",
|
|
43094
|
+
"UNIQUE",
|
|
42426
43095
|
"VAR.P",
|
|
42427
43096
|
"VAR.S",
|
|
43097
|
+
"VSTACK",
|
|
42428
43098
|
"WEBSERVICE",
|
|
42429
43099
|
"WEIBULL.DIST",
|
|
43100
|
+
"WRAPCOLS",
|
|
43101
|
+
"WRAPROWS",
|
|
43102
|
+
"XLOOKUP",
|
|
42430
43103
|
"XOR",
|
|
42431
43104
|
"Z.TEST",
|
|
42432
43105
|
];
|
|
@@ -45610,7 +46283,7 @@ function getRelationFile(file, xmls) {
|
|
|
45610
46283
|
return relsFile;
|
|
45611
46284
|
}
|
|
45612
46285
|
|
|
45613
|
-
const EXCEL_IMPORT_VERSION =
|
|
46286
|
+
const EXCEL_IMPORT_VERSION = 17;
|
|
45614
46287
|
class XlsxReader {
|
|
45615
46288
|
warningManager;
|
|
45616
46289
|
xmls;
|
|
@@ -45742,7 +46415,7 @@ function normalizeV9(formula) {
|
|
|
45742
46415
|
* a breaking change is made in the way the state is handled, and an upgrade
|
|
45743
46416
|
* function should be defined
|
|
45744
46417
|
*/
|
|
45745
|
-
const CURRENT_VERSION =
|
|
46418
|
+
const CURRENT_VERSION = 17;
|
|
45746
46419
|
const INITIAL_SHEET_ID = "Sheet1";
|
|
45747
46420
|
/**
|
|
45748
46421
|
* This function tries to load anything that could look like a valid
|
|
@@ -46689,6 +47362,21 @@ class BordersPlugin extends CorePlugin {
|
|
|
46689
47362
|
return [];
|
|
46690
47363
|
return Object.keys(sheetBorders).map((index) => parseInt(index, 10));
|
|
46691
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
|
+
}
|
|
46692
47380
|
/**
|
|
46693
47381
|
* Get the range of all the rows in the sheet
|
|
46694
47382
|
*/
|
|
@@ -46738,7 +47426,7 @@ class BordersPlugin extends CorePlugin {
|
|
|
46738
47426
|
destructive: false,
|
|
46739
47427
|
});
|
|
46740
47428
|
}
|
|
46741
|
-
this.
|
|
47429
|
+
this.getRowsWithBorders(sheetId)
|
|
46742
47430
|
.filter((row) => row >= start)
|
|
46743
47431
|
.sort((a, b) => (delta < 0 ? a - b : b - a)) // start by the end when moving up
|
|
46744
47432
|
.forEach((row) => {
|
|
@@ -49064,10 +49752,12 @@ class MergePlugin extends CorePlugin {
|
|
|
49064
49752
|
if (!sheetMap)
|
|
49065
49753
|
return [];
|
|
49066
49754
|
const mergeIds = new Set();
|
|
49067
|
-
for (
|
|
49068
|
-
|
|
49069
|
-
|
|
49070
|
-
|
|
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
|
+
}
|
|
49071
49761
|
}
|
|
49072
49762
|
}
|
|
49073
49763
|
return Array.from(mergeIds)
|
|
@@ -52935,7 +53625,13 @@ class FormulaDependencyGraph {
|
|
|
52935
53625
|
const queue = Array.from(ranges).reverse();
|
|
52936
53626
|
while (queue.length > 0) {
|
|
52937
53627
|
const range = queue.pop();
|
|
52938
|
-
|
|
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
|
+
}
|
|
52939
53635
|
const impactedPositions = this.rTree.search(range).map((dep) => dep.data);
|
|
52940
53636
|
const nextInQueue = {};
|
|
52941
53637
|
for (const position of impactedPositions) {
|
|
@@ -52951,7 +53647,16 @@ class FormulaDependencyGraph {
|
|
|
52951
53647
|
queue.push(...zones.map((zone) => ({ sheetId, zone })));
|
|
52952
53648
|
}
|
|
52953
53649
|
}
|
|
52954
|
-
|
|
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
|
+
}
|
|
52955
53660
|
return visited;
|
|
52956
53661
|
}
|
|
52957
53662
|
}
|
|
@@ -53082,7 +53787,7 @@ class PositionSet {
|
|
|
53082
53787
|
return this.sheets[position.sheetId].getValue(position) === 1;
|
|
53083
53788
|
}
|
|
53084
53789
|
clear() {
|
|
53085
|
-
const insertions = this
|
|
53790
|
+
const insertions = [...this];
|
|
53086
53791
|
this.insertions = [];
|
|
53087
53792
|
for (const sheetId in this.sheets) {
|
|
53088
53793
|
this.sheets[sheetId].clear();
|
|
@@ -53426,6 +54131,7 @@ class Evaluator {
|
|
|
53426
54131
|
}
|
|
53427
54132
|
finally {
|
|
53428
54133
|
this.cellsBeingComputed.delete(cellId);
|
|
54134
|
+
this.nextPositionsToUpdate.delete(position);
|
|
53429
54135
|
}
|
|
53430
54136
|
}
|
|
53431
54137
|
computeAndSave(position) {
|
|
@@ -53452,8 +54158,33 @@ class Evaluator {
|
|
|
53452
54158
|
forEachSpreadPositionInMatrix(nbColumns, nbRows,
|
|
53453
54159
|
// thanks to the isMatrix check above, we know that formulaReturn is MatrixFunctionReturn
|
|
53454
54160
|
this.spreadValues(formulaPosition, formulaReturn));
|
|
54161
|
+
this.invalidatePositionsDependingOnSpread(formulaPosition, nbColumns, nbRows);
|
|
53455
54162
|
return createEvaluatedCell(nullValueToZeroValue(formulaReturn[0][0]), this.getters.getLocale(), cellData);
|
|
53456
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
|
+
}
|
|
53457
54188
|
assertSheetHasEnoughSpaceToSpreadFormulaResult({ sheetId, col, row }, matrixResult) {
|
|
53458
54189
|
const numberOfCols = this.getters.getNumberCols(sheetId);
|
|
53459
54190
|
const numberOfRows = this.getters.getNumberRows(sheetId);
|
|
@@ -53484,14 +54215,15 @@ class Evaluator {
|
|
|
53484
54215
|
}
|
|
53485
54216
|
updateSpreadRelation({ sheetId, col, row, }) {
|
|
53486
54217
|
const arrayFormulaPosition = { sheetId, col, row };
|
|
53487
|
-
|
|
54218
|
+
const updateSpreadRelation = (i, j) => {
|
|
53488
54219
|
const position = { sheetId, col: i + col, row: j + row };
|
|
53489
54220
|
this.spreadingRelations.addRelation({ resultPosition: position, arrayFormulaPosition });
|
|
53490
54221
|
};
|
|
54222
|
+
return updateSpreadRelation;
|
|
53491
54223
|
}
|
|
53492
54224
|
checkCollision(formulaPosition) {
|
|
53493
54225
|
const { sheetId, col, row } = formulaPosition;
|
|
53494
|
-
|
|
54226
|
+
const checkCollision = (i, j) => {
|
|
53495
54227
|
const position = { sheetId: sheetId, col: i + col, row: j + row };
|
|
53496
54228
|
const rawCell = this.getters.getCell(position);
|
|
53497
54229
|
if (rawCell?.content ||
|
|
@@ -53501,17 +54233,16 @@ class Evaluator {
|
|
|
53501
54233
|
}
|
|
53502
54234
|
this.blockedArrayFormulas.delete(formulaPosition);
|
|
53503
54235
|
};
|
|
54236
|
+
return checkCollision;
|
|
53504
54237
|
}
|
|
53505
54238
|
spreadValues({ sheetId, col, row }, matrixResult) {
|
|
53506
|
-
|
|
54239
|
+
const spreadValues = (i, j) => {
|
|
53507
54240
|
const position = { sheetId, col: i + col, row: j + row };
|
|
53508
54241
|
const cell = this.getters.getCell(position);
|
|
53509
54242
|
const evaluatedCell = createEvaluatedCell(nullValueToZeroValue(matrixResult[i][j]), this.getters.getLocale(), cell);
|
|
53510
54243
|
this.evaluatedCells.set(position, evaluatedCell);
|
|
53511
|
-
// check if formula dependencies present in the spread zone
|
|
53512
|
-
// if so, they need to be recomputed
|
|
53513
|
-
this.nextPositionsToUpdate.addMany(this.getCellsDependingOn([position]));
|
|
53514
54244
|
};
|
|
54245
|
+
return spreadValues;
|
|
53515
54246
|
}
|
|
53516
54247
|
invalidateSpreading(position) {
|
|
53517
54248
|
if (!this.spreadingRelations.isArrayFormula(position)) {
|
|
@@ -53566,12 +54297,12 @@ function forEachSpreadPositionInMatrix(nbColumns, nbRows, callback) {
|
|
|
53566
54297
|
* rather than appearing empty. This indicates that the
|
|
53567
54298
|
* cell is the result of a non-empty content.
|
|
53568
54299
|
*/
|
|
53569
|
-
function nullValueToZeroValue(
|
|
53570
|
-
if (
|
|
53571
|
-
// '
|
|
53572
|
-
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 };
|
|
53573
54304
|
}
|
|
53574
|
-
return
|
|
54305
|
+
return functionResult;
|
|
53575
54306
|
}
|
|
53576
54307
|
function updateEvalContextAndExecute(compiledFormula, compilationParams, sheetId, cellId) {
|
|
53577
54308
|
compilationParams.evalContext.__originCellXC = lazy(() => {
|
|
@@ -57139,6 +57870,12 @@ class InsertPivotPlugin extends UIPlugin {
|
|
|
57139
57870
|
case "INSERT_NEW_PIVOT":
|
|
57140
57871
|
this.insertNewPivot(cmd.pivotId, cmd.newSheetId);
|
|
57141
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;
|
|
57142
57879
|
}
|
|
57143
57880
|
}
|
|
57144
57881
|
insertNewPivot(pivotId, sheetId) {
|
|
@@ -57164,7 +57901,7 @@ class InsertPivotPlugin extends UIPlugin {
|
|
|
57164
57901
|
const formulaId = this.getters.getPivotFormulaId(pivotId);
|
|
57165
57902
|
this.dispatch("CREATE_SHEET", {
|
|
57166
57903
|
sheetId,
|
|
57167
|
-
name: _t("Pivot #%s", formulaId),
|
|
57904
|
+
name: _t("Pivot #%(formulaId)s", { formulaId }),
|
|
57168
57905
|
position,
|
|
57169
57906
|
});
|
|
57170
57907
|
this.dispatch("ACTIVATE_SHEET", {
|
|
@@ -57178,6 +57915,92 @@ class InsertPivotPlugin extends UIPlugin {
|
|
|
57178
57915
|
content: `=PIVOT(${formulaId})`,
|
|
57179
57916
|
});
|
|
57180
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
|
+
}
|
|
57181
58004
|
}
|
|
57182
58005
|
|
|
57183
58006
|
class SortPlugin extends UIPlugin {
|
|
@@ -57358,6 +58181,7 @@ class SheetUIPlugin extends UIPlugin {
|
|
|
57358
58181
|
static getters = [
|
|
57359
58182
|
"doesCellHaveGridIcon",
|
|
57360
58183
|
"getCellWidth",
|
|
58184
|
+
"getCellIconSrc",
|
|
57361
58185
|
"getTextWidth",
|
|
57362
58186
|
"getCellText",
|
|
57363
58187
|
"getCellMultiLineText",
|
|
@@ -57408,7 +58232,7 @@ class SheetUIPlugin extends UIPlugin {
|
|
|
57408
58232
|
const multiLineText = splitTextToWidth(this.ctx, content, style, undefined);
|
|
57409
58233
|
contentWidth += Math.max(...multiLineText.map((line) => computeTextWidth(this.ctx, line, style)));
|
|
57410
58234
|
}
|
|
57411
|
-
const icon = this.getters.
|
|
58235
|
+
const icon = this.getters.getCellIconSrc(position);
|
|
57412
58236
|
if (icon) {
|
|
57413
58237
|
contentWidth += computeIconWidth(style);
|
|
57414
58238
|
}
|
|
@@ -57425,6 +58249,16 @@ class SheetUIPlugin extends UIPlugin {
|
|
|
57425
58249
|
}
|
|
57426
58250
|
return contentWidth;
|
|
57427
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
|
+
}
|
|
57428
58262
|
getTextWidth(text, style) {
|
|
57429
58263
|
return computeTextWidth(this.ctx, text, style);
|
|
57430
58264
|
}
|
|
@@ -61200,7 +62034,7 @@ class BottomBarSheet extends owl.Component {
|
|
|
61200
62034
|
this.DOMFocusableElementStore.focus();
|
|
61201
62035
|
}
|
|
61202
62036
|
}
|
|
61203
|
-
|
|
62037
|
+
onMouseEventSheetName(ev) {
|
|
61204
62038
|
if (this.state.isEditing)
|
|
61205
62039
|
ev.stopPropagation();
|
|
61206
62040
|
}
|
|
@@ -62928,7 +63762,7 @@ class TopBar extends owl.Component {
|
|
|
62928
63762
|
}
|
|
62929
63763
|
get topbarComponents() {
|
|
62930
63764
|
return topbarComponentRegistry
|
|
62931
|
-
.
|
|
63765
|
+
.getAllOrdered()
|
|
62932
63766
|
.filter((item) => !item.isVisible || item.isVisible(this.env));
|
|
62933
63767
|
}
|
|
62934
63768
|
onExternalClick(ev) {
|
|
@@ -67341,6 +68175,7 @@ const registries = {
|
|
|
67341
68175
|
linkMenuRegistry,
|
|
67342
68176
|
functionRegistry,
|
|
67343
68177
|
featurePluginRegistry,
|
|
68178
|
+
iconsOnCellRegistry,
|
|
67344
68179
|
statefulUIPluginRegistry,
|
|
67345
68180
|
coreViewsPluginRegistry,
|
|
67346
68181
|
corePluginRegistry,
|
|
@@ -67558,6 +68393,6 @@ exports.tokenColors = tokenColors;
|
|
|
67558
68393
|
exports.tokenize = tokenize;
|
|
67559
68394
|
|
|
67560
68395
|
|
|
67561
|
-
__info__.version = "17.4.0
|
|
67562
|
-
__info__.date = "2024-
|
|
67563
|
-
__info__.hash = "
|
|
68396
|
+
__info__.version = "17.4.0";
|
|
68397
|
+
__info__.date = "2024-07-12T04:41:51.675Z";
|
|
68398
|
+
__info__.hash = "f8e8543";
|