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