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