@odoo/o-spreadsheet 18.1.0-alpha.7 → 18.1.0-alpha.8
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 +1313 -315
- package/dist/o-spreadsheet.d.ts +398 -292
- package/dist/o-spreadsheet.esm.js +1313 -315
- package/dist/o-spreadsheet.iife.js +1313 -315
- package/dist/o-spreadsheet.iife.min.js +511 -515
- package/dist/o_spreadsheet.xml +214 -20
- package/package.json +3 -2
|
@@ -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 18.1.0-alpha.
|
|
6
|
-
* @date 2024-12-
|
|
7
|
-
* @hash
|
|
5
|
+
* @version 18.1.0-alpha.8
|
|
6
|
+
* @date 2024-12-19T07:49:54.421Z
|
|
7
|
+
* @hash 87a1567
|
|
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';
|
|
@@ -265,7 +265,6 @@ const MIN_ROW_HEIGHT = 10;
|
|
|
265
265
|
const MIN_COL_WIDTH = 5;
|
|
266
266
|
const HEADER_HEIGHT = 26;
|
|
267
267
|
const HEADER_WIDTH = 48;
|
|
268
|
-
const TOPBAR_HEIGHT = 63;
|
|
269
268
|
const TOPBAR_TOOLBAR_HEIGHT = 34;
|
|
270
269
|
const BOTTOMBAR_HEIGHT = 36;
|
|
271
270
|
const DEFAULT_CELL_WIDTH = 96;
|
|
@@ -789,6 +788,7 @@ function removeFalsyAttributes(obj) {
|
|
|
789
788
|
* https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions/Character_Classes
|
|
790
789
|
*/
|
|
791
790
|
const whiteSpaceSpecialCharacters = [
|
|
791
|
+
" ",
|
|
792
792
|
"\t",
|
|
793
793
|
"\f",
|
|
794
794
|
"\v",
|
|
@@ -803,17 +803,15 @@ const whiteSpaceSpecialCharacters = [
|
|
|
803
803
|
String.fromCharCode(parseInt("3000", 16)),
|
|
804
804
|
String.fromCharCode(parseInt("feff", 16)),
|
|
805
805
|
];
|
|
806
|
-
const whiteSpaceRegexp = new RegExp(whiteSpaceSpecialCharacters.join("|")
|
|
806
|
+
const whiteSpaceRegexp = new RegExp(whiteSpaceSpecialCharacters.join("|"), "g");
|
|
807
|
+
const newLineRegexp = /(\r\n|\r)/g;
|
|
807
808
|
/**
|
|
808
|
-
* Replace all
|
|
809
|
-
* different newlines types by \n.
|
|
809
|
+
* Replace all different newlines characters by \n
|
|
810
810
|
*/
|
|
811
|
-
function
|
|
811
|
+
function replaceNewLines(text) {
|
|
812
812
|
if (!text)
|
|
813
813
|
return "";
|
|
814
|
-
|
|
815
|
-
return text;
|
|
816
|
-
return text.replace(whiteSpaceRegexp, (match, newLine) => (newLine ? NEWLINE : " "));
|
|
814
|
+
return text.replace(newLineRegexp, NEWLINE);
|
|
817
815
|
}
|
|
818
816
|
/**
|
|
819
817
|
* Determine if the numbers are consecutive.
|
|
@@ -979,7 +977,7 @@ function transpose2dPOJO(pojo) {
|
|
|
979
977
|
|
|
980
978
|
const RBA_REGEX = /rgba?\(|\s+|\)/gi;
|
|
981
979
|
const HEX_MATCH = /^#([A-F\d]{2}){3,4}$/;
|
|
982
|
-
const colors
|
|
980
|
+
const colors = [
|
|
983
981
|
"#eb6d00",
|
|
984
982
|
"#0074d9",
|
|
985
983
|
"#ad8e00",
|
|
@@ -1002,6 +1000,12 @@ const colors$1 = [
|
|
|
1002
1000
|
function colorNumberString(color) {
|
|
1003
1001
|
return toHex(color.toString(16).padStart(6, "0"));
|
|
1004
1002
|
}
|
|
1003
|
+
function colorToNumber(color) {
|
|
1004
|
+
if (typeof color === "number") {
|
|
1005
|
+
return color;
|
|
1006
|
+
}
|
|
1007
|
+
return Number.parseInt(toHex(color).slice(1), 16);
|
|
1008
|
+
}
|
|
1005
1009
|
/**
|
|
1006
1010
|
* Converts any CSS color value to a standardized hex6 value.
|
|
1007
1011
|
* Accepts: hex3, hex6, hex8, rgb[1] and rgba[1].
|
|
@@ -1352,9 +1356,9 @@ const COLORS_XL = [
|
|
|
1352
1356
|
"#056BD9", // Blue #3
|
|
1353
1357
|
"#155193", // Blue #4
|
|
1354
1358
|
"#A76DBC", // Violet #1
|
|
1355
|
-
"#7F4295", // Violet #
|
|
1356
|
-
"#6D2387", // Violet #
|
|
1357
|
-
"#4F1565", // Violet #
|
|
1359
|
+
"#7F4295", // Violet #2
|
|
1360
|
+
"#6D2387", // Violet #3
|
|
1361
|
+
"#4F1565", // Violet #4
|
|
1358
1362
|
"#EA6175", // Red #1
|
|
1359
1363
|
"#CE4257", // Red #2
|
|
1360
1364
|
"#982738", // Red #3
|
|
@@ -1380,6 +1384,81 @@ const COLORS_XL = [
|
|
|
1380
1384
|
"#C08A16", // Yellow #3
|
|
1381
1385
|
"#936A12", // Yellow #4
|
|
1382
1386
|
];
|
|
1387
|
+
// Same as above but with alternating colors
|
|
1388
|
+
const ALTERNATING_COLORS_MD = [
|
|
1389
|
+
"#4EA7F2", // Blue #1
|
|
1390
|
+
"#43C5B1", // Teal #1
|
|
1391
|
+
"#EA6175", // Red #1
|
|
1392
|
+
"#F4A261", // Orange #1
|
|
1393
|
+
"#8481DD", // Purple #1
|
|
1394
|
+
"#FFD86D", // Yellow #1
|
|
1395
|
+
"#3188E6", // Blue #2
|
|
1396
|
+
"#00A78D", // Teal #2
|
|
1397
|
+
"#CE4257", // Red #2
|
|
1398
|
+
"#F48935", // Orange #2
|
|
1399
|
+
"#5752D1", // Purple #2
|
|
1400
|
+
"#FFBC2C", // Yellow #2
|
|
1401
|
+
];
|
|
1402
|
+
const ALTERNATING_COLORS_LG = [
|
|
1403
|
+
"#4EA7F2", // Blue #1
|
|
1404
|
+
"#A76DBC", // Violet #1
|
|
1405
|
+
"#EA6175", // Red #1
|
|
1406
|
+
"#43C5B1", // Teal #1
|
|
1407
|
+
"#F4A261", // Orange #1
|
|
1408
|
+
"#8481DD", // Purple #1
|
|
1409
|
+
"#A4A8B6", // Gray #1
|
|
1410
|
+
"#FFD86D", // Yellow #1
|
|
1411
|
+
"#3188E6", // Blue #2
|
|
1412
|
+
"#7F4295", // Violet #2
|
|
1413
|
+
"#CE4257", // Red #2
|
|
1414
|
+
"#00A78D", // Teal #2
|
|
1415
|
+
"#F48935", // Orange #2
|
|
1416
|
+
"#5752D1", // Purple #2
|
|
1417
|
+
"#7E8290", // Gray #2
|
|
1418
|
+
"#FFBC2C", // Yellow #2
|
|
1419
|
+
"#056BD9", // Blue #3
|
|
1420
|
+
"#6D2387", // Violet #3
|
|
1421
|
+
"#982738", // Red #3
|
|
1422
|
+
"#0E8270", // Teal #3
|
|
1423
|
+
"#BE5D10", // Orange #3
|
|
1424
|
+
"#3A3580", // Purple #3
|
|
1425
|
+
"#545B70", // Gray #3
|
|
1426
|
+
"#C08A16", // Yellow #3
|
|
1427
|
+
];
|
|
1428
|
+
const ALTERNATING_COLORS_XL = [
|
|
1429
|
+
"#4EA7F2", // Blue #1
|
|
1430
|
+
"#A76DBC", // Violet #1
|
|
1431
|
+
"#EA6175", // Red #1
|
|
1432
|
+
"#43C5B1", // Teal #1
|
|
1433
|
+
"#F4A261", // Orange #1
|
|
1434
|
+
"#8481DD", // Purple #1
|
|
1435
|
+
"#A4A8B6", // Grey #1
|
|
1436
|
+
"#FFD86D", // Yellow #1
|
|
1437
|
+
"#3188E6", // Blue #2
|
|
1438
|
+
"#7F4295", // Violet #2
|
|
1439
|
+
"#CE4257", // Red #2
|
|
1440
|
+
"#00A78D", // Teal #2
|
|
1441
|
+
"#F48935", // Orange #2
|
|
1442
|
+
"#5752D1", // Purple #2
|
|
1443
|
+
"#7E8290", // Grey #2
|
|
1444
|
+
"#FFBC2C", // Yellow #2
|
|
1445
|
+
"#056BD9", // Blue #3
|
|
1446
|
+
"#6D2387", // Violet #3
|
|
1447
|
+
"#982738", // Red #3
|
|
1448
|
+
"#0E8270", // Teal #3
|
|
1449
|
+
"#BE5D10", // Orange #3
|
|
1450
|
+
"#3A3580", // Purple #3
|
|
1451
|
+
"#545B70", // Grey #3
|
|
1452
|
+
"#C08A16", // Yellow #3
|
|
1453
|
+
"#155193", // Blue #4
|
|
1454
|
+
"#4F1565", // Violet #4
|
|
1455
|
+
"#791B29", // Red #4
|
|
1456
|
+
"#105F53", // Teal #4
|
|
1457
|
+
"#7D380D", // Orange #4
|
|
1458
|
+
"#26235F", // Purple #4
|
|
1459
|
+
"#3F4250", // Grey #4
|
|
1460
|
+
"#936A12", // Yellow #4
|
|
1461
|
+
];
|
|
1383
1462
|
function getNthColor(index, palette) {
|
|
1384
1463
|
return palette[index % palette.length];
|
|
1385
1464
|
}
|
|
@@ -1397,6 +1476,20 @@ function getColorsPalette(quantity) {
|
|
|
1397
1476
|
return COLORS_XL;
|
|
1398
1477
|
}
|
|
1399
1478
|
}
|
|
1479
|
+
function getAlternatingColorsPalette(quantity) {
|
|
1480
|
+
if (quantity <= 6) {
|
|
1481
|
+
return COLORS_SM;
|
|
1482
|
+
}
|
|
1483
|
+
else if (quantity <= 12) {
|
|
1484
|
+
return ALTERNATING_COLORS_MD;
|
|
1485
|
+
}
|
|
1486
|
+
else if (quantity <= 24) {
|
|
1487
|
+
return ALTERNATING_COLORS_LG;
|
|
1488
|
+
}
|
|
1489
|
+
else {
|
|
1490
|
+
return ALTERNATING_COLORS_XL;
|
|
1491
|
+
}
|
|
1492
|
+
}
|
|
1400
1493
|
class ColorGenerator {
|
|
1401
1494
|
preferredColors;
|
|
1402
1495
|
currentColorIndex = 0;
|
|
@@ -1411,6 +1504,62 @@ class ColorGenerator {
|
|
|
1411
1504
|
: getNthColor(this.currentColorIndex++, this.palette);
|
|
1412
1505
|
}
|
|
1413
1506
|
}
|
|
1507
|
+
class AlternatingColorGenerator extends ColorGenerator {
|
|
1508
|
+
constructor(paletteSize, preferredColors = []) {
|
|
1509
|
+
super(paletteSize, preferredColors);
|
|
1510
|
+
this.palette = getAlternatingColorsPalette(paletteSize).filter((c) => !preferredColors.includes(c));
|
|
1511
|
+
}
|
|
1512
|
+
}
|
|
1513
|
+
/**
|
|
1514
|
+
* Returns a function that maps a value to a color using a color scale defined by the given
|
|
1515
|
+
* color/threshold values pairs.
|
|
1516
|
+
*/
|
|
1517
|
+
function getColorScale(colorScalePoints) {
|
|
1518
|
+
if (colorScalePoints.length < 2) {
|
|
1519
|
+
throw new Error("Color scale must have at least 2 points");
|
|
1520
|
+
}
|
|
1521
|
+
const sortedColorScalePoints = [...colorScalePoints.sort((a, b) => a.value - b.value)];
|
|
1522
|
+
const thresholds = [];
|
|
1523
|
+
for (let i = 1; i < sortedColorScalePoints.length; i++) {
|
|
1524
|
+
const minColor = colorToNumber(sortedColorScalePoints[i - 1].color);
|
|
1525
|
+
const maxColor = colorToNumber(sortedColorScalePoints[i].color);
|
|
1526
|
+
thresholds.push({
|
|
1527
|
+
min: sortedColorScalePoints[i - 1].value,
|
|
1528
|
+
max: sortedColorScalePoints[i].value,
|
|
1529
|
+
minColor,
|
|
1530
|
+
maxColor,
|
|
1531
|
+
colorDiff: computeColorDiffUnits(sortedColorScalePoints[i - 1].value, sortedColorScalePoints[i].value, minColor, maxColor),
|
|
1532
|
+
});
|
|
1533
|
+
}
|
|
1534
|
+
return (value) => {
|
|
1535
|
+
if (value < thresholds[0].min) {
|
|
1536
|
+
return colorNumberString(thresholds[0].minColor);
|
|
1537
|
+
}
|
|
1538
|
+
for (const threshold of thresholds) {
|
|
1539
|
+
if (value >= threshold.min && value <= threshold.max) {
|
|
1540
|
+
return colorNumberString(colorCell(value, threshold.min, threshold.minColor, threshold.colorDiff));
|
|
1541
|
+
}
|
|
1542
|
+
}
|
|
1543
|
+
return colorNumberString(thresholds[thresholds.length - 1].maxColor);
|
|
1544
|
+
};
|
|
1545
|
+
}
|
|
1546
|
+
function computeColorDiffUnits(minValue, maxValue, minColor, maxColor) {
|
|
1547
|
+
const deltaValue = maxValue - minValue;
|
|
1548
|
+
const deltaColorR = ((minColor >> 16) % 256) - ((maxColor >> 16) % 256);
|
|
1549
|
+
const deltaColorG = ((minColor >> 8) % 256) - ((maxColor >> 8) % 256);
|
|
1550
|
+
const deltaColorB = (minColor % 256) - (maxColor % 256);
|
|
1551
|
+
const colorDiffUnitR = deltaColorR / deltaValue;
|
|
1552
|
+
const colorDiffUnitG = deltaColorG / deltaValue;
|
|
1553
|
+
const colorDiffUnitB = deltaColorB / deltaValue;
|
|
1554
|
+
return [colorDiffUnitR, colorDiffUnitG, colorDiffUnitB];
|
|
1555
|
+
}
|
|
1556
|
+
function colorCell(value, minValue, minColor, colorDiffUnit) {
|
|
1557
|
+
const [colorDiffUnitR, colorDiffUnitG, colorDiffUnitB] = colorDiffUnit;
|
|
1558
|
+
const r = Math.round(((minColor >> 16) % 256) - colorDiffUnitR * (value - minValue));
|
|
1559
|
+
const g = Math.round(((minColor >> 8) % 256) - colorDiffUnitG * (value - minValue));
|
|
1560
|
+
const b = Math.round((minColor % 256) - colorDiffUnitB * (value - minValue));
|
|
1561
|
+
return (r << 16) | (g << 8) | b;
|
|
1562
|
+
}
|
|
1414
1563
|
|
|
1415
1564
|
//------------------------------------------------------------------------------
|
|
1416
1565
|
// Coordinate
|
|
@@ -3282,6 +3431,7 @@ const invalidateEvaluationCommands = new Set([
|
|
|
3282
3431
|
]);
|
|
3283
3432
|
const invalidateChartEvaluationCommands = new Set([
|
|
3284
3433
|
"EVALUATE_CELLS",
|
|
3434
|
+
"EVALUATE_CHARTS",
|
|
3285
3435
|
"UPDATE_CELL",
|
|
3286
3436
|
"UNHIDE_COLUMNS_ROWS",
|
|
3287
3437
|
"HIDE_COLUMNS_ROWS",
|
|
@@ -3318,6 +3468,7 @@ const readonlyAllowedCommands = new Set([
|
|
|
3318
3468
|
"RESIZE_SHEETVIEW",
|
|
3319
3469
|
"SET_VIEWPORT_OFFSET",
|
|
3320
3470
|
"EVALUATE_CELLS",
|
|
3471
|
+
"EVALUATE_CHARTS",
|
|
3321
3472
|
"SET_FORMULA_VISIBILITY",
|
|
3322
3473
|
"UPDATE_FILTER",
|
|
3323
3474
|
]);
|
|
@@ -6522,7 +6673,7 @@ class BorderClipboardHandler extends AbstractCellClipboardHandler {
|
|
|
6522
6673
|
const POSTFIX_UNARY_OPERATORS = ["%"];
|
|
6523
6674
|
const OPERATORS = "+,-,*,/,:,=,<>,>=,>,<=,<,^,&".split(",").concat(POSTFIX_UNARY_OPERATORS);
|
|
6524
6675
|
function tokenize(str, locale = DEFAULT_LOCALE) {
|
|
6525
|
-
str =
|
|
6676
|
+
str = replaceNewLines(str);
|
|
6526
6677
|
const chars = new TokenizingChars(str);
|
|
6527
6678
|
const result = [];
|
|
6528
6679
|
while (!chars.isOver()) {
|
|
@@ -6669,12 +6820,12 @@ function tokenizeSpace(chars) {
|
|
|
6669
6820
|
if (length) {
|
|
6670
6821
|
return { type: "SPACE", value: NEWLINE.repeat(length) };
|
|
6671
6822
|
}
|
|
6672
|
-
|
|
6673
|
-
|
|
6674
|
-
chars.shift();
|
|
6823
|
+
let spaces = "";
|
|
6824
|
+
while (chars.current && chars.current.match(whiteSpaceRegexp)) {
|
|
6825
|
+
spaces += chars.shift();
|
|
6675
6826
|
}
|
|
6676
|
-
if (
|
|
6677
|
-
return { type: "SPACE", value:
|
|
6827
|
+
if (spaces) {
|
|
6828
|
+
return { type: "SPACE", value: spaces };
|
|
6678
6829
|
}
|
|
6679
6830
|
return null;
|
|
6680
6831
|
}
|
|
@@ -7533,6 +7684,16 @@ function getRunningTotalDomainKey(domain, fieldNameWithGranularity) {
|
|
|
7533
7684
|
}
|
|
7534
7685
|
return domainToString([...domain.slice(0, index), ...domain.slice(index + 1)]);
|
|
7535
7686
|
}
|
|
7687
|
+
function sortPivotTree(tree, baseDomain, sortFn) {
|
|
7688
|
+
const sortedTree = [...tree];
|
|
7689
|
+
const domain = [...baseDomain];
|
|
7690
|
+
sortedTree.sort((node1, node2) => sortFn([...domain, node1], [...domain, node2]));
|
|
7691
|
+
for (const node of tree) {
|
|
7692
|
+
const children = sortPivotTree(node.children, [...domain, node], sortFn);
|
|
7693
|
+
node.children = children;
|
|
7694
|
+
}
|
|
7695
|
+
return sortedTree;
|
|
7696
|
+
}
|
|
7536
7697
|
|
|
7537
7698
|
const pivotTimeAdapterRegistry = new Registry();
|
|
7538
7699
|
function pivotTimeAdapter(granularity) {
|
|
@@ -8043,6 +8204,29 @@ function addIndentAndAlignToPivotHeader(pivot, domain, functionResult) {
|
|
|
8043
8204
|
format: `${" ".repeat(indent)}${format}* `,
|
|
8044
8205
|
};
|
|
8045
8206
|
}
|
|
8207
|
+
function isSortedColumnValid(sortedColumn, pivot) {
|
|
8208
|
+
try {
|
|
8209
|
+
if (!pivot.getMeasure(sortedColumn.measure)) {
|
|
8210
|
+
return false;
|
|
8211
|
+
}
|
|
8212
|
+
const columns = pivot.definition.columns;
|
|
8213
|
+
for (let i = 0; i < sortedColumn.domain.length; i++) {
|
|
8214
|
+
if (columns[i].nameWithGranularity !== sortedColumn.domain[i].field) {
|
|
8215
|
+
return false;
|
|
8216
|
+
}
|
|
8217
|
+
const possibleValues = pivot
|
|
8218
|
+
.getPossibleFieldValues(columns[i])
|
|
8219
|
+
.map((v) => v.value);
|
|
8220
|
+
if (!possibleValues.includes(sortedColumn.domain[i].value)) {
|
|
8221
|
+
return false;
|
|
8222
|
+
}
|
|
8223
|
+
}
|
|
8224
|
+
return true;
|
|
8225
|
+
}
|
|
8226
|
+
catch (e) {
|
|
8227
|
+
return false;
|
|
8228
|
+
}
|
|
8229
|
+
}
|
|
8046
8230
|
|
|
8047
8231
|
class CellClipboardHandler extends AbstractCellClipboardHandler {
|
|
8048
8232
|
isCutAllowed(data) {
|
|
@@ -14651,7 +14835,7 @@ const SORT_TYPES = [
|
|
|
14651
14835
|
CellValueType.boolean,
|
|
14652
14836
|
];
|
|
14653
14837
|
function cellsSortingCriterion(sortingOrder) {
|
|
14654
|
-
const inverse = sortingOrder === "
|
|
14838
|
+
const inverse = sortingOrder === "asc" ? 1 : -1;
|
|
14655
14839
|
return (left, right) => {
|
|
14656
14840
|
if (left.type === CellValueType.empty) {
|
|
14657
14841
|
return right.type === CellValueType.empty ? 0 : 1;
|
|
@@ -14743,7 +14927,7 @@ function sortMatrix(matrix, locale, ...criteria) {
|
|
|
14743
14927
|
const sortColumns = [];
|
|
14744
14928
|
const nRows = matrix.length;
|
|
14745
14929
|
for (let i = 0; i < criteria.length; i += 2) {
|
|
14746
|
-
sortingOrders.push(toBoolean(toScalar(criteria[i + 1])?.value) ? "
|
|
14930
|
+
sortingOrders.push(toBoolean(toScalar(criteria[i + 1])?.value) ? "asc" : "desc");
|
|
14747
14931
|
const sortColumn = criteria[i];
|
|
14748
14932
|
if (isMatrix(sortColumn) && (sortColumn.length > 1 || sortColumn[0].length > 1)) {
|
|
14749
14933
|
assert(() => sortColumn.length === 1 && sortColumn[0].length === nRows, _t("Wrong size for %s. Expected a range of size 1x%s. Got %sx%s.", `sort_column${i + 1}`, nRows, sortColumn.length, sortColumn[0].length));
|
|
@@ -14760,12 +14944,12 @@ function sortMatrix(matrix, locale, ...criteria) {
|
|
|
14760
14944
|
if (sortColumns.length === 0) {
|
|
14761
14945
|
for (let i = 0; i < matrix[0].length; i++) {
|
|
14762
14946
|
sortColumns.push(matrix.map((row) => row[i].value));
|
|
14763
|
-
sortingOrders.push("
|
|
14947
|
+
sortingOrders.push("asc");
|
|
14764
14948
|
}
|
|
14765
14949
|
}
|
|
14766
14950
|
const sortingCriteria = {
|
|
14767
|
-
|
|
14768
|
-
|
|
14951
|
+
desc: cellsSortingCriterion("desc"),
|
|
14952
|
+
asc: cellsSortingCriterion("asc"),
|
|
14769
14953
|
};
|
|
14770
14954
|
const indexes = range(0, matrix.length);
|
|
14771
14955
|
indexes.sort((a, b) => {
|
|
@@ -20489,20 +20673,8 @@ class AbstractComposerStore extends SpreadsheetStore {
|
|
|
20489
20673
|
replaceSelectedRange(zone) {
|
|
20490
20674
|
const ref = this.getZoneReference(zone);
|
|
20491
20675
|
const currentToken = this.tokenAtCursor;
|
|
20492
|
-
|
|
20493
|
-
|
|
20494
|
-
replaceStart = currentToken.start;
|
|
20495
|
-
}
|
|
20496
|
-
else if (currentToken?.type === "RIGHT_PAREN") {
|
|
20497
|
-
// match left parenthesis
|
|
20498
|
-
const leftParenthesisIndex = this.currentTokens.findIndex((token) => token.type === "LEFT_PAREN" && token.parenthesesCode === currentToken.parenthesesCode);
|
|
20499
|
-
const functionToken = this.currentTokens[leftParenthesisIndex - 1];
|
|
20500
|
-
if (functionToken === undefined) {
|
|
20501
|
-
return;
|
|
20502
|
-
}
|
|
20503
|
-
replaceStart = functionToken.start;
|
|
20504
|
-
}
|
|
20505
|
-
this.replaceText(ref, replaceStart, this.selectionEnd);
|
|
20676
|
+
const start = currentToken?.type === "REFERENCE" ? currentToken.start : this.selectionStart;
|
|
20677
|
+
this.replaceText(ref, start, this.selectionEnd);
|
|
20506
20678
|
}
|
|
20507
20679
|
/**
|
|
20508
20680
|
* Replace the reference of the old zone by the new one.
|
|
@@ -20535,17 +20707,6 @@ class AbstractComposerStore extends SpreadsheetStore {
|
|
|
20535
20707
|
getZoneReference(zone) {
|
|
20536
20708
|
const inputSheetId = this.sheetId;
|
|
20537
20709
|
const sheetId = this.getters.getActiveSheetId();
|
|
20538
|
-
if (zone.top === zone.bottom && zone.left === zone.right) {
|
|
20539
|
-
const position = { sheetId, col: zone.left, row: zone.top };
|
|
20540
|
-
const pivotId = this.getters.getPivotIdFromPosition(position);
|
|
20541
|
-
const pivotCell = this.getters.getPivotCellFromPosition(position);
|
|
20542
|
-
const cell = this.getters.getCell(position);
|
|
20543
|
-
if (pivotId && pivotCell.type !== "EMPTY" && !cell?.isFormula) {
|
|
20544
|
-
const formulaPivotId = this.getters.getPivotFormulaId(pivotId);
|
|
20545
|
-
const formula = createPivotFormula(formulaPivotId, pivotCell);
|
|
20546
|
-
return localizeFormula(formula, this.getters.getLocale()).slice(1); // strip leading =
|
|
20547
|
-
}
|
|
20548
|
-
}
|
|
20549
20710
|
const range = this.getters.getRangeFromZone(sheetId, zone);
|
|
20550
20711
|
return this.getters.getSelectionRangeString(range, inputSheetId);
|
|
20551
20712
|
}
|
|
@@ -20716,37 +20877,21 @@ class AbstractComposerStore extends SpreadsheetStore {
|
|
|
20716
20877
|
const editionSheetId = this.sheetId;
|
|
20717
20878
|
const rangeColor = (rangeString) => {
|
|
20718
20879
|
const colorIndex = this.colorIndexByRange[rangeString];
|
|
20719
|
-
return colors
|
|
20880
|
+
return colors[colorIndex % colors.length];
|
|
20720
20881
|
};
|
|
20721
|
-
|
|
20722
|
-
for (const range of this.getReferencedRanges()) {
|
|
20882
|
+
return this.getReferencedRanges().map((range) => {
|
|
20723
20883
|
const rangeString = this.getters.getRangeString(range, editionSheetId);
|
|
20724
20884
|
const { numberOfRows, numberOfCols } = zoneToDimension(range.zone);
|
|
20725
20885
|
const zone = numberOfRows * numberOfCols === 1
|
|
20726
20886
|
? this.getters.expandZone(range.sheetId, range.zone)
|
|
20727
20887
|
: range.zone;
|
|
20728
|
-
|
|
20888
|
+
return {
|
|
20729
20889
|
zone,
|
|
20730
20890
|
color: rangeColor(rangeString),
|
|
20731
20891
|
sheetId: range.sheetId,
|
|
20732
20892
|
interactive: true,
|
|
20733
|
-
}
|
|
20734
|
-
}
|
|
20735
|
-
const activeSheetId = this.getters.getActiveSheetId();
|
|
20736
|
-
const selectionZone = this.model.selection.getAnchor().zone;
|
|
20737
|
-
const isSelectionHightlighted = highlights.find((highlight) => highlight.sheetId === activeSheetId && isEqual(highlight.zone, selectionZone));
|
|
20738
|
-
if (this.editionMode === "selecting" && !isSelectionHightlighted) {
|
|
20739
|
-
highlights.push({
|
|
20740
|
-
zone: selectionZone,
|
|
20741
|
-
color: "#445566",
|
|
20742
|
-
sheetId: activeSheetId,
|
|
20743
|
-
dashed: true,
|
|
20744
|
-
interactive: false,
|
|
20745
|
-
noFill: true,
|
|
20746
|
-
thinLine: true,
|
|
20747
|
-
});
|
|
20748
|
-
}
|
|
20749
|
-
return highlights;
|
|
20893
|
+
};
|
|
20894
|
+
});
|
|
20750
20895
|
}
|
|
20751
20896
|
/**
|
|
20752
20897
|
* Return ranges currently referenced in the composer
|
|
@@ -20974,6 +21119,7 @@ function compileTokens(tokens) {
|
|
|
20974
21119
|
return error;
|
|
20975
21120
|
},
|
|
20976
21121
|
isBadExpression: true,
|
|
21122
|
+
normalizedFormula: tokens.map((t) => t.value).join(""),
|
|
20977
21123
|
};
|
|
20978
21124
|
}
|
|
20979
21125
|
}
|
|
@@ -21101,6 +21247,7 @@ function compileTokensOrThrow(tokens) {
|
|
|
21101
21247
|
symbols,
|
|
21102
21248
|
tokens,
|
|
21103
21249
|
isBadExpression: false,
|
|
21250
|
+
normalizedFormula: cacheKey,
|
|
21104
21251
|
};
|
|
21105
21252
|
return compiledFormula;
|
|
21106
21253
|
}
|
|
@@ -21365,6 +21512,7 @@ autoCompleteProviders.add("pivot_ids", {
|
|
|
21365
21512
|
description: definition.name,
|
|
21366
21513
|
htmlContent: [{ value: str, color: tokenColors.NUMBER }],
|
|
21367
21514
|
fuzzySearchKey: str + definition.name,
|
|
21515
|
+
alwaysExpanded: true,
|
|
21368
21516
|
};
|
|
21369
21517
|
})
|
|
21370
21518
|
.filter(isDefined);
|
|
@@ -27758,6 +27906,19 @@ const ChartTerms = {
|
|
|
27758
27906
|
["GaugeLowerInflectionPointNaN" /* CommandResult.GaugeLowerInflectionPointNaN */]: _t("The lower inflection point value must be a number"),
|
|
27759
27907
|
["GaugeUpperInflectionPointNaN" /* CommandResult.GaugeUpperInflectionPointNaN */]: _t("The upper inflection point value must be a number"),
|
|
27760
27908
|
},
|
|
27909
|
+
GeoChart: {
|
|
27910
|
+
ColorScales: {
|
|
27911
|
+
blues: _t("Blues"),
|
|
27912
|
+
cividis: _t("Cividis"),
|
|
27913
|
+
greens: _t("Greens"),
|
|
27914
|
+
greys: _t("Greys"),
|
|
27915
|
+
oranges: _t("Oranges"),
|
|
27916
|
+
purples: _t("Purples"),
|
|
27917
|
+
rainbow: _t("Rainbow"),
|
|
27918
|
+
reds: _t("Reds"),
|
|
27919
|
+
viridis: _t("Viridis"),
|
|
27920
|
+
},
|
|
27921
|
+
},
|
|
27761
27922
|
};
|
|
27762
27923
|
const CustomCurrencyTerms = {
|
|
27763
27924
|
Custom: _t("Custom"),
|
|
@@ -28161,6 +28322,26 @@ function getRadarChartData(definition, dataSets, labelRange, getters) {
|
|
|
28161
28322
|
locale: getters.getLocale(),
|
|
28162
28323
|
};
|
|
28163
28324
|
}
|
|
28325
|
+
function getGeoChartData(definition, dataSets, labelRange, getters) {
|
|
28326
|
+
const labelValues = getChartLabelValues(getters, dataSets, labelRange);
|
|
28327
|
+
let labels = labelValues.formattedValues;
|
|
28328
|
+
if (definition.dataSetsHaveTitle) {
|
|
28329
|
+
labels.shift();
|
|
28330
|
+
}
|
|
28331
|
+
let dataSetsValues = getChartDatasetValues(getters, dataSets);
|
|
28332
|
+
({ labels, dataSetsValues } = aggregateDataForLabels(labels, dataSetsValues));
|
|
28333
|
+
const format = getChartDatasetFormat(getters, dataSets, "left") ||
|
|
28334
|
+
getChartDatasetFormat(getters, dataSets, "right");
|
|
28335
|
+
return {
|
|
28336
|
+
dataSetsValues,
|
|
28337
|
+
axisFormats: { y: format },
|
|
28338
|
+
labels,
|
|
28339
|
+
locale: getters.getLocale(),
|
|
28340
|
+
availableRegions: getters.getGeoChartAvailableRegions(),
|
|
28341
|
+
geoFeatureNameToId: getters.geoFeatureNameToId,
|
|
28342
|
+
getGeoJsonFeatures: getters.getGeoJsonFeatures,
|
|
28343
|
+
};
|
|
28344
|
+
}
|
|
28164
28345
|
function getTrendDatasetForBarChart(config, data) {
|
|
28165
28346
|
const filteredValues = [];
|
|
28166
28347
|
const filteredLabels = [];
|
|
@@ -28713,6 +28894,43 @@ function getRadarChartDatasets(definition, args) {
|
|
|
28713
28894
|
}
|
|
28714
28895
|
return datasets;
|
|
28715
28896
|
}
|
|
28897
|
+
function getGeoChartDatasets(definition, args) {
|
|
28898
|
+
const { availableRegions, dataSetsValues, labels } = args;
|
|
28899
|
+
const regionName = definition.region || availableRegions[0]?.id;
|
|
28900
|
+
const features = regionName ? args.getGeoJsonFeatures(regionName) : undefined;
|
|
28901
|
+
const dataset = {
|
|
28902
|
+
outline: features,
|
|
28903
|
+
showOutline: !!features,
|
|
28904
|
+
data: [],
|
|
28905
|
+
};
|
|
28906
|
+
if (features && regionName) {
|
|
28907
|
+
const labelsAndValues = {};
|
|
28908
|
+
if (dataSetsValues[0]) {
|
|
28909
|
+
for (let i = 0; i < dataSetsValues[0].data.length; i++) {
|
|
28910
|
+
if (!labels[i] || dataSetsValues[0].data[i] === undefined) {
|
|
28911
|
+
continue;
|
|
28912
|
+
}
|
|
28913
|
+
const featureId = args.geoFeatureNameToId(regionName, labels[i]);
|
|
28914
|
+
if (featureId) {
|
|
28915
|
+
labelsAndValues[featureId] = { value: dataSetsValues[0].data[i], label: labels[i] };
|
|
28916
|
+
}
|
|
28917
|
+
}
|
|
28918
|
+
}
|
|
28919
|
+
for (const feature of features) {
|
|
28920
|
+
if (!feature.id) {
|
|
28921
|
+
continue;
|
|
28922
|
+
}
|
|
28923
|
+
dataset.data.push({
|
|
28924
|
+
feature: {
|
|
28925
|
+
...feature,
|
|
28926
|
+
properties: { name: labelsAndValues[feature.id]?.label },
|
|
28927
|
+
},
|
|
28928
|
+
value: labelsAndValues[feature.id]?.value,
|
|
28929
|
+
});
|
|
28930
|
+
}
|
|
28931
|
+
}
|
|
28932
|
+
return [dataset];
|
|
28933
|
+
}
|
|
28716
28934
|
function getTrendingLineDataSet(dataset, config, data) {
|
|
28717
28935
|
const defaultBorderColor = colorToRGBA(dataset.backgroundColor);
|
|
28718
28936
|
defaultBorderColor.a = 1;
|
|
@@ -29090,6 +29308,44 @@ function getRadarChartScales(definition, args) {
|
|
|
29090
29308
|
},
|
|
29091
29309
|
};
|
|
29092
29310
|
}
|
|
29311
|
+
function getGeoChartScales(definition, args) {
|
|
29312
|
+
const { locale, axisFormats, availableRegions } = args;
|
|
29313
|
+
const geoLegendPosition = legendPositionToGeoLegendPosition(definition.legendPosition);
|
|
29314
|
+
const region = definition.region
|
|
29315
|
+
? availableRegions.find((r) => r.id === definition.region)
|
|
29316
|
+
: availableRegions[0];
|
|
29317
|
+
const format = axisFormats?.y || axisFormats?.y1;
|
|
29318
|
+
return {
|
|
29319
|
+
projection: {
|
|
29320
|
+
// projection: region?.defaultProjection,
|
|
29321
|
+
projection: getGeoChartProjection(region?.defaultProjection || "mercator"),
|
|
29322
|
+
axis: "x",
|
|
29323
|
+
},
|
|
29324
|
+
color: {
|
|
29325
|
+
axis: "x",
|
|
29326
|
+
display: definition.legendPosition !== "none",
|
|
29327
|
+
border: { color: GRAY_300 },
|
|
29328
|
+
grid: { color: GRAY_300 },
|
|
29329
|
+
ticks: {
|
|
29330
|
+
color: chartFontColor(definition.background),
|
|
29331
|
+
callback: formatTickValue({ locale, format }),
|
|
29332
|
+
},
|
|
29333
|
+
legend: {
|
|
29334
|
+
position: geoLegendPosition,
|
|
29335
|
+
align: geoLegendPosition.includes("right") ? "left" : "right",
|
|
29336
|
+
margin: getLegendMargin(definition),
|
|
29337
|
+
},
|
|
29338
|
+
interpolate: getRuntimeColorScale(definition),
|
|
29339
|
+
missing: definition.missingValueColor || "#ffffff",
|
|
29340
|
+
},
|
|
29341
|
+
};
|
|
29342
|
+
}
|
|
29343
|
+
function getGeoChartProjection(projection) {
|
|
29344
|
+
if (projection === "conicConformal") {
|
|
29345
|
+
return window.ChartGeo.geoConicConformal().rotate([100, 0]); // Centered on the US
|
|
29346
|
+
}
|
|
29347
|
+
return projection;
|
|
29348
|
+
}
|
|
29093
29349
|
function getChartAxisTitleRuntime(design) {
|
|
29094
29350
|
if (design?.title?.text) {
|
|
29095
29351
|
const { text, color, align, italic, bold } = design.title;
|
|
@@ -29153,6 +29409,44 @@ function getChartAxis(definition, position, type, options) {
|
|
|
29153
29409
|
};
|
|
29154
29410
|
}
|
|
29155
29411
|
}
|
|
29412
|
+
function getRuntimeColorScale(definition) {
|
|
29413
|
+
if (!definition.colorScale || typeof definition.colorScale === "string") {
|
|
29414
|
+
return definition.colorScale || "oranges";
|
|
29415
|
+
}
|
|
29416
|
+
const scaleColors = [{ value: 0, color: definition.colorScale.minColor }];
|
|
29417
|
+
if (definition.colorScale.midColor) {
|
|
29418
|
+
scaleColors.push({ value: 0.5, color: definition.colorScale.midColor });
|
|
29419
|
+
}
|
|
29420
|
+
scaleColors.push({ value: 1, color: definition.colorScale.maxColor });
|
|
29421
|
+
return getColorScale(scaleColors);
|
|
29422
|
+
}
|
|
29423
|
+
function getLegendMargin(definition) {
|
|
29424
|
+
switch (definition.legendPosition) {
|
|
29425
|
+
case "top":
|
|
29426
|
+
case "right":
|
|
29427
|
+
const hasTitle = !!definition.title.text;
|
|
29428
|
+
const topMargin = hasTitle ? CHART_PADDING_TOP + 30 : CHART_PADDING_TOP;
|
|
29429
|
+
return { top: topMargin, left: CHART_PADDING$1, right: CHART_PADDING$1 };
|
|
29430
|
+
case "bottom":
|
|
29431
|
+
case "left":
|
|
29432
|
+
case "none":
|
|
29433
|
+
return { left: CHART_PADDING$1, right: CHART_PADDING$1, bottom: CHART_PADDING_BOTTOM };
|
|
29434
|
+
}
|
|
29435
|
+
}
|
|
29436
|
+
function legendPositionToGeoLegendPosition(position) {
|
|
29437
|
+
switch (position) {
|
|
29438
|
+
case "top":
|
|
29439
|
+
return "top-left";
|
|
29440
|
+
case "right":
|
|
29441
|
+
return "top-right";
|
|
29442
|
+
case "bottom":
|
|
29443
|
+
return "bottom-right";
|
|
29444
|
+
case "left":
|
|
29445
|
+
return "bottom-left";
|
|
29446
|
+
case "none":
|
|
29447
|
+
return "bottom-left";
|
|
29448
|
+
}
|
|
29449
|
+
}
|
|
29156
29450
|
|
|
29157
29451
|
function getChartShowValues(definition, args) {
|
|
29158
29452
|
const { axisFormats, locale } = args;
|
|
@@ -29312,6 +29606,25 @@ function getRadarChartTooltip(definition, args) {
|
|
|
29312
29606
|
},
|
|
29313
29607
|
};
|
|
29314
29608
|
}
|
|
29609
|
+
function getGeoChartTooltip(definition, args) {
|
|
29610
|
+
const { locale, axisFormats } = args;
|
|
29611
|
+
const format = axisFormats?.y || axisFormats?.y1;
|
|
29612
|
+
return {
|
|
29613
|
+
filter: function (tooltipItem) {
|
|
29614
|
+
return tooltipItem.raw.value !== undefined;
|
|
29615
|
+
},
|
|
29616
|
+
callbacks: {
|
|
29617
|
+
label: function (tooltipItem) {
|
|
29618
|
+
const rawItem = tooltipItem.raw;
|
|
29619
|
+
const xLabel = rawItem.feature.properties.name;
|
|
29620
|
+
const yLabel = rawItem.value;
|
|
29621
|
+
const toolTipFormat = !format && Math.abs(yLabel) >= 1000 ? "#,##" : format;
|
|
29622
|
+
const yLabelStr = formatValue(yLabel, { format: toolTipFormat, locale });
|
|
29623
|
+
return xLabel ? `${xLabel}: ${yLabelStr}` : yLabelStr;
|
|
29624
|
+
},
|
|
29625
|
+
},
|
|
29626
|
+
};
|
|
29627
|
+
}
|
|
29315
29628
|
function calculatePercentage(dataset, dataIndex) {
|
|
29316
29629
|
const numericData = dataset.filter((value) => typeof value === "number");
|
|
29317
29630
|
const total = numericData.reduce((sum, value) => sum + value, 0);
|
|
@@ -29338,6 +29651,10 @@ var CHART_RUNTIME_HELPERS = /*#__PURE__*/Object.freeze({
|
|
|
29338
29651
|
getComboChartDatasets: getComboChartDatasets,
|
|
29339
29652
|
getComboChartLegend: getComboChartLegend,
|
|
29340
29653
|
getData: getData,
|
|
29654
|
+
getGeoChartData: getGeoChartData,
|
|
29655
|
+
getGeoChartDatasets: getGeoChartDatasets,
|
|
29656
|
+
getGeoChartScales: getGeoChartScales,
|
|
29657
|
+
getGeoChartTooltip: getGeoChartTooltip,
|
|
29341
29658
|
getLineChartData: getLineChartData,
|
|
29342
29659
|
getLineChartDatasets: getLineChartDatasets,
|
|
29343
29660
|
getLineChartLegend: getLineChartLegend,
|
|
@@ -29911,6 +30228,133 @@ function getSectionThresholdValue(threshold, minValue, maxValue) {
|
|
|
29911
30228
|
return clip(value, minValue, maxValue);
|
|
29912
30229
|
}
|
|
29913
30230
|
|
|
30231
|
+
class GeoChart extends AbstractChart {
|
|
30232
|
+
dataSets;
|
|
30233
|
+
labelRange;
|
|
30234
|
+
background;
|
|
30235
|
+
legendPosition;
|
|
30236
|
+
type = "geo";
|
|
30237
|
+
dataSetsHaveTitle;
|
|
30238
|
+
dataSetDesign;
|
|
30239
|
+
colorScale;
|
|
30240
|
+
missingValueColor;
|
|
30241
|
+
region;
|
|
30242
|
+
constructor(definition, sheetId, getters) {
|
|
30243
|
+
super(definition, sheetId, getters);
|
|
30244
|
+
this.dataSets = createDataSets(getters, definition.dataSets, sheetId, definition.dataSetsHaveTitle);
|
|
30245
|
+
this.labelRange = createValidRange(getters, sheetId, definition.labelRange);
|
|
30246
|
+
this.background = definition.background;
|
|
30247
|
+
this.legendPosition = definition.legendPosition;
|
|
30248
|
+
this.dataSetsHaveTitle = definition.dataSetsHaveTitle;
|
|
30249
|
+
this.dataSetDesign = definition.dataSets;
|
|
30250
|
+
this.colorScale = definition.colorScale;
|
|
30251
|
+
this.missingValueColor = definition.missingValueColor;
|
|
30252
|
+
this.region = definition.region;
|
|
30253
|
+
}
|
|
30254
|
+
static transformDefinition(definition, executed) {
|
|
30255
|
+
return transformChartDefinitionWithDataSetsWithZone(definition, executed);
|
|
30256
|
+
}
|
|
30257
|
+
static validateChartDefinition(validator, definition) {
|
|
30258
|
+
return validator.checkValidations(definition, checkDataset, checkLabelRange);
|
|
30259
|
+
}
|
|
30260
|
+
static getDefinitionFromContextCreation(context) {
|
|
30261
|
+
return {
|
|
30262
|
+
background: context.background,
|
|
30263
|
+
dataSets: context.range ?? [],
|
|
30264
|
+
dataSetsHaveTitle: context.dataSetsHaveTitle ?? false,
|
|
30265
|
+
legendPosition: context.legendPosition ?? "top",
|
|
30266
|
+
title: context.title || { text: "" },
|
|
30267
|
+
type: "geo",
|
|
30268
|
+
labelRange: context.auxiliaryRange || undefined,
|
|
30269
|
+
aggregated: context.aggregated,
|
|
30270
|
+
};
|
|
30271
|
+
}
|
|
30272
|
+
getContextCreation() {
|
|
30273
|
+
const range = [];
|
|
30274
|
+
for (const [i, dataSet] of this.dataSets.entries()) {
|
|
30275
|
+
range.push({
|
|
30276
|
+
...this.dataSetDesign?.[i],
|
|
30277
|
+
dataRange: this.getters.getRangeString(dataSet.dataRange, this.sheetId),
|
|
30278
|
+
});
|
|
30279
|
+
}
|
|
30280
|
+
return {
|
|
30281
|
+
...this,
|
|
30282
|
+
range,
|
|
30283
|
+
auxiliaryRange: this.labelRange
|
|
30284
|
+
? this.getters.getRangeString(this.labelRange, this.sheetId)
|
|
30285
|
+
: undefined,
|
|
30286
|
+
};
|
|
30287
|
+
}
|
|
30288
|
+
copyForSheetId(sheetId) {
|
|
30289
|
+
const dataSets = copyDataSetsWithNewSheetId(this.sheetId, sheetId, this.dataSets);
|
|
30290
|
+
const labelRange = copyLabelRangeWithNewSheetId(this.sheetId, sheetId, this.labelRange);
|
|
30291
|
+
const definition = this.getDefinitionWithSpecificDataSets(dataSets, labelRange, sheetId);
|
|
30292
|
+
return new GeoChart(definition, sheetId, this.getters);
|
|
30293
|
+
}
|
|
30294
|
+
copyInSheetId(sheetId) {
|
|
30295
|
+
const definition = this.getDefinitionWithSpecificDataSets(this.dataSets, this.labelRange, sheetId);
|
|
30296
|
+
return new GeoChart(definition, sheetId, this.getters);
|
|
30297
|
+
}
|
|
30298
|
+
getDefinition() {
|
|
30299
|
+
return this.getDefinitionWithSpecificDataSets(this.dataSets, this.labelRange);
|
|
30300
|
+
}
|
|
30301
|
+
getDefinitionWithSpecificDataSets(dataSets, labelRange, targetSheetId) {
|
|
30302
|
+
const ranges = [];
|
|
30303
|
+
for (const [i, dataSet] of dataSets.entries()) {
|
|
30304
|
+
ranges.push({
|
|
30305
|
+
...this.dataSetDesign?.[i],
|
|
30306
|
+
dataRange: this.getters.getRangeString(dataSet.dataRange, targetSheetId || this.sheetId),
|
|
30307
|
+
});
|
|
30308
|
+
}
|
|
30309
|
+
return {
|
|
30310
|
+
type: "geo",
|
|
30311
|
+
dataSetsHaveTitle: dataSets.length ? Boolean(dataSets[0].labelCell) : false,
|
|
30312
|
+
background: this.background,
|
|
30313
|
+
dataSets: ranges,
|
|
30314
|
+
legendPosition: this.legendPosition,
|
|
30315
|
+
labelRange: labelRange
|
|
30316
|
+
? this.getters.getRangeString(labelRange, targetSheetId || this.sheetId)
|
|
30317
|
+
: undefined,
|
|
30318
|
+
title: this.title,
|
|
30319
|
+
colorScale: this.colorScale,
|
|
30320
|
+
missingValueColor: this.missingValueColor,
|
|
30321
|
+
region: this.region,
|
|
30322
|
+
};
|
|
30323
|
+
}
|
|
30324
|
+
getDefinitionForExcel() {
|
|
30325
|
+
return undefined;
|
|
30326
|
+
}
|
|
30327
|
+
updateRanges(applyChange) {
|
|
30328
|
+
const { dataSets, labelRange, isStale } = updateChartRangesWithDataSets(this.getters, applyChange, this.dataSets, this.labelRange);
|
|
30329
|
+
if (!isStale) {
|
|
30330
|
+
return this;
|
|
30331
|
+
}
|
|
30332
|
+
const definition = this.getDefinitionWithSpecificDataSets(dataSets, labelRange);
|
|
30333
|
+
return new GeoChart(definition, this.sheetId, this.getters);
|
|
30334
|
+
}
|
|
30335
|
+
}
|
|
30336
|
+
function createGeoChartRuntime(chart, getters) {
|
|
30337
|
+
const definition = chart.getDefinition();
|
|
30338
|
+
const chartData = getGeoChartData(definition, chart.dataSets, chart.labelRange, getters);
|
|
30339
|
+
const config = {
|
|
30340
|
+
type: "choropleth",
|
|
30341
|
+
data: {
|
|
30342
|
+
datasets: getGeoChartDatasets(definition, chartData),
|
|
30343
|
+
},
|
|
30344
|
+
options: {
|
|
30345
|
+
...CHART_COMMON_OPTIONS,
|
|
30346
|
+
layout: getChartLayout(),
|
|
30347
|
+
scales: getGeoChartScales(definition, chartData),
|
|
30348
|
+
plugins: {
|
|
30349
|
+
title: getChartTitle(definition),
|
|
30350
|
+
tooltip: getGeoChartTooltip(definition, chartData),
|
|
30351
|
+
legend: { display: false },
|
|
30352
|
+
},
|
|
30353
|
+
},
|
|
30354
|
+
};
|
|
30355
|
+
return { chartJsConfig: config, background: chart.background || BACKGROUND_CHART_COLOR };
|
|
30356
|
+
}
|
|
30357
|
+
|
|
29914
30358
|
class LineChart extends AbstractChart {
|
|
29915
30359
|
dataSets;
|
|
29916
30360
|
labelRange;
|
|
@@ -30904,6 +31348,15 @@ chartRegistry.add("radar", {
|
|
|
30904
31348
|
getChartDefinitionFromContextCreation: RadarChart.getDefinitionFromContextCreation,
|
|
30905
31349
|
sequence: 80,
|
|
30906
31350
|
});
|
|
31351
|
+
chartRegistry.add("geo", {
|
|
31352
|
+
match: (type) => type === "geo",
|
|
31353
|
+
createChart: (definition, sheetId, getters) => new GeoChart(definition, sheetId, getters),
|
|
31354
|
+
getChartRuntime: createGeoChartRuntime,
|
|
31355
|
+
validateChartDefinition: GeoChart.validateChartDefinition,
|
|
31356
|
+
transformDefinition: GeoChart.transformDefinition,
|
|
31357
|
+
getChartDefinitionFromContextCreation: GeoChart.getDefinitionFromContextCreation,
|
|
31358
|
+
sequence: 90,
|
|
31359
|
+
});
|
|
30907
31360
|
const chartComponentRegistry = new Registry();
|
|
30908
31361
|
chartComponentRegistry.add("line", ChartJsComponent);
|
|
30909
31362
|
chartComponentRegistry.add("bar", ChartJsComponent);
|
|
@@ -30915,6 +31368,7 @@ chartComponentRegistry.add("scorecard", ScorecardChart);
|
|
|
30915
31368
|
chartComponentRegistry.add("waterfall", ChartJsComponent);
|
|
30916
31369
|
chartComponentRegistry.add("pyramid", ChartJsComponent);
|
|
30917
31370
|
chartComponentRegistry.add("radar", ChartJsComponent);
|
|
31371
|
+
chartComponentRegistry.add("geo", ChartJsComponent);
|
|
30918
31372
|
const chartCategories = {
|
|
30919
31373
|
line: _t("Line"),
|
|
30920
31374
|
column: _t("Column"),
|
|
@@ -31074,6 +31528,13 @@ chartSubtypeRegistry
|
|
|
31074
31528
|
subtypeDefinition: { fillArea: true },
|
|
31075
31529
|
category: "misc",
|
|
31076
31530
|
preview: "o-spreadsheet-ChartPreview.FILLED_RADAR_CHART",
|
|
31531
|
+
})
|
|
31532
|
+
.add("geo", {
|
|
31533
|
+
displayName: _t("Geo Chart"),
|
|
31534
|
+
chartSubtype: "geo",
|
|
31535
|
+
chartType: "geo",
|
|
31536
|
+
category: "misc",
|
|
31537
|
+
preview: "o-spreadsheet-ChartPreview.GEO_CHART",
|
|
31077
31538
|
});
|
|
31078
31539
|
|
|
31079
31540
|
/**
|
|
@@ -31924,18 +32385,66 @@ const ErrorToolTipPopoverBuilder = {
|
|
|
31924
32385
|
},
|
|
31925
32386
|
};
|
|
31926
32387
|
|
|
31927
|
-
|
|
31928
|
-
|
|
31929
|
-
|
|
31930
|
-
|
|
31931
|
-
|
|
31932
|
-
|
|
31933
|
-
|
|
32388
|
+
const CHECK_SVG = /*xml*/ `
|
|
32389
|
+
<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'>
|
|
32390
|
+
<path fill='none' stroke='#FFF' stroke-linecap='round' stroke-linejoin='round' stroke-width='3' d='m6 10 3 3 6-6'/>
|
|
32391
|
+
</svg>
|
|
32392
|
+
`;
|
|
32393
|
+
const CHECKBOX_WIDTH = 14;
|
|
32394
|
+
css /* scss */ `
|
|
32395
|
+
label.o-checkbox {
|
|
32396
|
+
input {
|
|
32397
|
+
appearance: none;
|
|
32398
|
+
-webkit-appearance: none;
|
|
32399
|
+
-moz-appearance: none;
|
|
32400
|
+
border-radius: 0;
|
|
32401
|
+
width: ${CHECKBOX_WIDTH}px;
|
|
32402
|
+
height: ${CHECKBOX_WIDTH}px;
|
|
32403
|
+
vertical-align: top;
|
|
32404
|
+
box-sizing: border-box;
|
|
32405
|
+
outline: none;
|
|
32406
|
+
border: 1px solid ${GRAY_300};
|
|
32407
|
+
cursor: pointer;
|
|
32408
|
+
|
|
32409
|
+
&:hover {
|
|
32410
|
+
border-color: ${ACTION_COLOR};
|
|
32411
|
+
}
|
|
32412
|
+
|
|
32413
|
+
&:checked {
|
|
32414
|
+
background: url("data:image/svg+xml,${encodeURIComponent(CHECK_SVG)}");
|
|
32415
|
+
background-color: ${ACTION_COLOR};
|
|
32416
|
+
border-color: ${ACTION_COLOR};
|
|
32417
|
+
}
|
|
32418
|
+
|
|
32419
|
+
&:focus {
|
|
32420
|
+
outline: none;
|
|
32421
|
+
box-shadow: 0 0 0 0.25rem rgba(113, 75, 103, 0.25);
|
|
32422
|
+
border-color: ${ACTION_COLOR};
|
|
32423
|
+
}
|
|
31934
32424
|
}
|
|
31935
32425
|
}
|
|
31936
32426
|
`;
|
|
32427
|
+
class Checkbox extends Component {
|
|
32428
|
+
static template = "o-spreadsheet.Checkbox";
|
|
32429
|
+
static props = {
|
|
32430
|
+
label: { type: String, optional: true },
|
|
32431
|
+
value: { type: Boolean, optional: true },
|
|
32432
|
+
className: { type: String, optional: true },
|
|
32433
|
+
name: { type: String, optional: true },
|
|
32434
|
+
title: { type: String, optional: true },
|
|
32435
|
+
disabled: { type: Boolean, optional: true },
|
|
32436
|
+
onChange: Function,
|
|
32437
|
+
};
|
|
32438
|
+
static defaultProps = { value: false };
|
|
32439
|
+
onChange(ev) {
|
|
32440
|
+
const value = ev.target.checked;
|
|
32441
|
+
this.props.onChange(value);
|
|
32442
|
+
}
|
|
32443
|
+
}
|
|
32444
|
+
|
|
31937
32445
|
class FilterMenuValueItem extends Component {
|
|
31938
32446
|
static template = "o-spreadsheet-FilterMenuValueItem";
|
|
32447
|
+
static components = { Checkbox };
|
|
31939
32448
|
static props = {
|
|
31940
32449
|
value: String,
|
|
31941
32450
|
isChecked: Boolean,
|
|
@@ -31973,8 +32482,6 @@ const CSS = css /* scss */ `
|
|
|
31973
32482
|
.o-filter-menu-item {
|
|
31974
32483
|
display: flex;
|
|
31975
32484
|
box-sizing: border-box;
|
|
31976
|
-
height: ${MENU_ITEM_HEIGHT}px;
|
|
31977
|
-
padding: 4px 4px 4px 0px;
|
|
31978
32485
|
cursor: pointer;
|
|
31979
32486
|
user-select: none;
|
|
31980
32487
|
|
|
@@ -31983,14 +32490,6 @@ const CSS = css /* scss */ `
|
|
|
31983
32490
|
}
|
|
31984
32491
|
}
|
|
31985
32492
|
|
|
31986
|
-
input {
|
|
31987
|
-
box-sizing: border-box;
|
|
31988
|
-
margin-bottom: 5px;
|
|
31989
|
-
border: 1px solid #949494;
|
|
31990
|
-
height: 24px;
|
|
31991
|
-
padding-right: 28px;
|
|
31992
|
-
}
|
|
31993
|
-
|
|
31994
32493
|
.o-search-icon {
|
|
31995
32494
|
right: 5px;
|
|
31996
32495
|
top: 3px;
|
|
@@ -32007,19 +32506,12 @@ const CSS = css /* scss */ `
|
|
|
32007
32506
|
display: flex;
|
|
32008
32507
|
flex-direction: row;
|
|
32009
32508
|
margin-bottom: 4px;
|
|
32010
|
-
|
|
32011
|
-
.o-filter-menu-action-text {
|
|
32012
|
-
cursor: pointer;
|
|
32013
|
-
margin-right: 10px;
|
|
32014
|
-
color: blue;
|
|
32015
|
-
text-decoration: underline;
|
|
32016
|
-
}
|
|
32017
32509
|
}
|
|
32018
32510
|
|
|
32019
32511
|
.o-filter-menu-list {
|
|
32020
32512
|
flex: auto;
|
|
32021
32513
|
overflow-y: auto;
|
|
32022
|
-
border: 1px solid
|
|
32514
|
+
border: 1px solid ${GRAY_300};
|
|
32023
32515
|
|
|
32024
32516
|
.o-filter-menu-no-values {
|
|
32025
32517
|
color: #949494;
|
|
@@ -34211,6 +34703,21 @@ const pivotProperties = {
|
|
|
34211
34703
|
isReadonlyAllowed: true,
|
|
34212
34704
|
icon: "o-spreadsheet-Icon.PIVOT",
|
|
34213
34705
|
};
|
|
34706
|
+
const pivotSortingAsc = {
|
|
34707
|
+
name: _t("Ascending"),
|
|
34708
|
+
execute: (env) => sortPivot(env, "asc"),
|
|
34709
|
+
isActive: (env) => isPivotSortMenuItemActive(env, "asc"),
|
|
34710
|
+
};
|
|
34711
|
+
const pivotSortingDesc = {
|
|
34712
|
+
name: _t("Descending"),
|
|
34713
|
+
execute: (env) => sortPivot(env, "desc"),
|
|
34714
|
+
isActive: (env) => isPivotSortMenuItemActive(env, "desc"),
|
|
34715
|
+
};
|
|
34716
|
+
const noPivotSorting = {
|
|
34717
|
+
name: _t("No sorting"),
|
|
34718
|
+
execute: (env) => sortPivot(env, "none"),
|
|
34719
|
+
isActive: (env) => isPivotSortMenuItemActive(env, "none"),
|
|
34720
|
+
};
|
|
34214
34721
|
const FIX_FORMULAS = {
|
|
34215
34722
|
name: _t("Convert to individual formulas"),
|
|
34216
34723
|
execute(env) {
|
|
@@ -34247,6 +34754,66 @@ const FIX_FORMULAS = {
|
|
|
34247
34754
|
},
|
|
34248
34755
|
icon: "o-spreadsheet-Icon.PIVOT",
|
|
34249
34756
|
};
|
|
34757
|
+
function canSortPivot(env) {
|
|
34758
|
+
const position = env.model.getters.getActivePosition();
|
|
34759
|
+
const pivotId = env.model.getters.getPivotIdFromPosition(position);
|
|
34760
|
+
if (!pivotId ||
|
|
34761
|
+
!env.model.getters.isExistingPivot(pivotId) ||
|
|
34762
|
+
!env.model.getters.isSpillPivotFormula(position)) {
|
|
34763
|
+
return false;
|
|
34764
|
+
}
|
|
34765
|
+
const pivot = env.model.getters.getPivot(pivotId);
|
|
34766
|
+
if (!pivot.isValid()) {
|
|
34767
|
+
return false;
|
|
34768
|
+
}
|
|
34769
|
+
const pivotCell = env.model.getters.getPivotCellFromPosition(position);
|
|
34770
|
+
return pivotCell.type === "VALUE" || pivotCell.type === "MEASURE_HEADER";
|
|
34771
|
+
}
|
|
34772
|
+
function sortPivot(env, order) {
|
|
34773
|
+
const position = env.model.getters.getActivePosition();
|
|
34774
|
+
const pivotId = env.model.getters.getPivotIdFromPosition(position);
|
|
34775
|
+
const pivotCell = env.model.getters.getPivotCellFromPosition(position);
|
|
34776
|
+
if (pivotCell.type === "EMPTY" || pivotCell.type === "HEADER" || !pivotId) {
|
|
34777
|
+
return;
|
|
34778
|
+
}
|
|
34779
|
+
if (order === "none") {
|
|
34780
|
+
env.model.dispatch("UPDATE_PIVOT", {
|
|
34781
|
+
pivotId: pivotId,
|
|
34782
|
+
pivot: {
|
|
34783
|
+
...env.model.getters.getPivotCoreDefinition(pivotId),
|
|
34784
|
+
sortedColumn: undefined,
|
|
34785
|
+
},
|
|
34786
|
+
});
|
|
34787
|
+
return;
|
|
34788
|
+
}
|
|
34789
|
+
const pivot = env.model.getters.getPivot(pivotId);
|
|
34790
|
+
const colDomain = domainToColRowDomain(pivot, pivotCell.domain).colDomain;
|
|
34791
|
+
env.model.dispatch("UPDATE_PIVOT", {
|
|
34792
|
+
pivotId: pivotId,
|
|
34793
|
+
pivot: {
|
|
34794
|
+
...env.model.getters.getPivotCoreDefinition(pivotId),
|
|
34795
|
+
sortedColumn: { domain: colDomain, order, measure: pivotCell.measure },
|
|
34796
|
+
},
|
|
34797
|
+
});
|
|
34798
|
+
}
|
|
34799
|
+
function isPivotSortMenuItemActive(env, order) {
|
|
34800
|
+
const position = env.model.getters.getActivePosition();
|
|
34801
|
+
const pivotId = env.model.getters.getPivotIdFromPosition(position);
|
|
34802
|
+
const pivotCell = env.model.getters.getPivotCellFromPosition(position);
|
|
34803
|
+
if (pivotCell.type === "EMPTY" || pivotCell.type === "HEADER" || !pivotId) {
|
|
34804
|
+
return false;
|
|
34805
|
+
}
|
|
34806
|
+
const pivot = env.model.getters.getPivot(pivotId);
|
|
34807
|
+
const colDomain = domainToColRowDomain(pivot, pivotCell.domain).colDomain;
|
|
34808
|
+
const sortedColumn = pivot.definition.sortedColumn;
|
|
34809
|
+
if (order === "none") {
|
|
34810
|
+
return !sortedColumn;
|
|
34811
|
+
}
|
|
34812
|
+
if (!sortedColumn || sortedColumn.order !== order) {
|
|
34813
|
+
return false;
|
|
34814
|
+
}
|
|
34815
|
+
return sortedColumn.measure === pivotCell.measure && deepEquals(sortedColumn.domain, colDomain);
|
|
34816
|
+
}
|
|
34250
34817
|
|
|
34251
34818
|
//------------------------------------------------------------------------------
|
|
34252
34819
|
// Context Menu Registry
|
|
@@ -34345,14 +34912,33 @@ cellMenuRegistry
|
|
|
34345
34912
|
name: INSERT_LINK_NAME,
|
|
34346
34913
|
sequence: 150,
|
|
34347
34914
|
separator: true,
|
|
34915
|
+
})
|
|
34916
|
+
.add("pivot_sorting", {
|
|
34917
|
+
name: _t("Sort pivot"),
|
|
34918
|
+
sequence: 155,
|
|
34919
|
+
icon: "o-spreadsheet-Icon.SORT_RANGE",
|
|
34920
|
+
isVisible: canSortPivot,
|
|
34348
34921
|
})
|
|
34349
34922
|
.add("pivot_fix_formulas", {
|
|
34350
34923
|
...FIX_FORMULAS,
|
|
34351
|
-
sequence:
|
|
34924
|
+
sequence: 160,
|
|
34352
34925
|
})
|
|
34353
34926
|
.add("pivot_properties", {
|
|
34354
34927
|
...pivotProperties,
|
|
34355
34928
|
sequence: 170,
|
|
34929
|
+
separator: true,
|
|
34930
|
+
})
|
|
34931
|
+
.addChild("pivot_sorting_asc", ["pivot_sorting"], {
|
|
34932
|
+
...pivotSortingAsc,
|
|
34933
|
+
sequence: 10,
|
|
34934
|
+
})
|
|
34935
|
+
.addChild("pivot_sorting_desc", ["pivot_sorting"], {
|
|
34936
|
+
...pivotSortingDesc,
|
|
34937
|
+
sequence: 20,
|
|
34938
|
+
})
|
|
34939
|
+
.addChild("pivot_sorting_none", ["pivot_sorting"], {
|
|
34940
|
+
...noPivotSorting,
|
|
34941
|
+
sequence: 30,
|
|
34356
34942
|
});
|
|
34357
34943
|
|
|
34358
34944
|
const sortRange = {
|
|
@@ -34365,7 +34951,7 @@ const sortAscending = {
|
|
|
34365
34951
|
execute: (env) => {
|
|
34366
34952
|
const { anchor, zones } = env.model.getters.getSelection();
|
|
34367
34953
|
const sheetId = env.model.getters.getActiveSheetId();
|
|
34368
|
-
interactiveSortSelection(env, sheetId, anchor.cell, zones[0], "
|
|
34954
|
+
interactiveSortSelection(env, sheetId, anchor.cell, zones[0], "asc");
|
|
34369
34955
|
},
|
|
34370
34956
|
icon: "o-spreadsheet-Icon.SORT_ASCENDING",
|
|
34371
34957
|
};
|
|
@@ -34393,7 +34979,7 @@ const sortDescending = {
|
|
|
34393
34979
|
execute: (env) => {
|
|
34394
34980
|
const { anchor, zones } = env.model.getters.getSelection();
|
|
34395
34981
|
const sheetId = env.model.getters.getActiveSheetId();
|
|
34396
|
-
interactiveSortSelection(env, sheetId, anchor.cell, zones[0], "
|
|
34982
|
+
interactiveSortSelection(env, sheetId, anchor.cell, zones[0], "desc");
|
|
34397
34983
|
},
|
|
34398
34984
|
icon: "o-spreadsheet-Icon.SORT_DESCENDING",
|
|
34399
34985
|
};
|
|
@@ -34876,6 +35462,227 @@ function interactiveFreezeColumnsRows(env, dimension, base) {
|
|
|
34876
35462
|
}
|
|
34877
35463
|
}
|
|
34878
35464
|
|
|
35465
|
+
class PositionMap {
|
|
35466
|
+
map = {};
|
|
35467
|
+
constructor(entries = []) {
|
|
35468
|
+
for (const [position, value] of entries) {
|
|
35469
|
+
this.set(position, value);
|
|
35470
|
+
}
|
|
35471
|
+
}
|
|
35472
|
+
set({ sheetId, col, row }, value) {
|
|
35473
|
+
const map = this.map;
|
|
35474
|
+
if (!map[sheetId]) {
|
|
35475
|
+
map[sheetId] = {};
|
|
35476
|
+
}
|
|
35477
|
+
if (!map[sheetId][col]) {
|
|
35478
|
+
map[sheetId][col] = {};
|
|
35479
|
+
}
|
|
35480
|
+
map[sheetId][col][row] = value;
|
|
35481
|
+
}
|
|
35482
|
+
get({ sheetId, col, row }) {
|
|
35483
|
+
return this.map[sheetId]?.[col]?.[row];
|
|
35484
|
+
}
|
|
35485
|
+
getSheet(sheetId) {
|
|
35486
|
+
return this.map[sheetId];
|
|
35487
|
+
}
|
|
35488
|
+
has({ sheetId, col, row }) {
|
|
35489
|
+
return this.map[sheetId]?.[col]?.[row] !== undefined;
|
|
35490
|
+
}
|
|
35491
|
+
delete({ sheetId, col, row }) {
|
|
35492
|
+
delete this.map[sheetId]?.[col]?.[row];
|
|
35493
|
+
}
|
|
35494
|
+
keys() {
|
|
35495
|
+
const map = this.map;
|
|
35496
|
+
const keys = [];
|
|
35497
|
+
for (const sheetId in map) {
|
|
35498
|
+
for (const col in map[sheetId]) {
|
|
35499
|
+
for (const row in map[sheetId][col]) {
|
|
35500
|
+
keys.push({ sheetId, col: parseInt(col), row: parseInt(row) });
|
|
35501
|
+
}
|
|
35502
|
+
}
|
|
35503
|
+
}
|
|
35504
|
+
return keys;
|
|
35505
|
+
}
|
|
35506
|
+
keysForSheet(sheetId) {
|
|
35507
|
+
const map = this.map[sheetId];
|
|
35508
|
+
if (!map) {
|
|
35509
|
+
return [];
|
|
35510
|
+
}
|
|
35511
|
+
const keys = [];
|
|
35512
|
+
for (const col in map) {
|
|
35513
|
+
for (const row in map[col]) {
|
|
35514
|
+
keys.push({ sheetId, col: parseInt(col), row: parseInt(row) });
|
|
35515
|
+
}
|
|
35516
|
+
}
|
|
35517
|
+
return keys;
|
|
35518
|
+
}
|
|
35519
|
+
*entries() {
|
|
35520
|
+
const map = this.map;
|
|
35521
|
+
for (const position of this.keys()) {
|
|
35522
|
+
const { sheetId, col, row } = position;
|
|
35523
|
+
yield [position, map[sheetId][col][row]];
|
|
35524
|
+
}
|
|
35525
|
+
}
|
|
35526
|
+
}
|
|
35527
|
+
|
|
35528
|
+
class FormulaFingerprintStore extends SpreadsheetStore {
|
|
35529
|
+
mutators = ["enable", "disable"];
|
|
35530
|
+
isInvalidated = false;
|
|
35531
|
+
fingerprintColors = {
|
|
35532
|
+
[DATA_FINGERPRINT]: "#D9D9D9",
|
|
35533
|
+
};
|
|
35534
|
+
isEnabled = false;
|
|
35535
|
+
colors = new PositionMap();
|
|
35536
|
+
handle(cmd) {
|
|
35537
|
+
if (isCoreCommand(cmd) && this.isEnabled) {
|
|
35538
|
+
this.isInvalidated = true;
|
|
35539
|
+
}
|
|
35540
|
+
switch (cmd.type) {
|
|
35541
|
+
case "UNDO":
|
|
35542
|
+
case "REDO":
|
|
35543
|
+
case "ACTIVATE_SHEET":
|
|
35544
|
+
if (this.isEnabled) {
|
|
35545
|
+
this.isInvalidated = true;
|
|
35546
|
+
}
|
|
35547
|
+
break;
|
|
35548
|
+
}
|
|
35549
|
+
}
|
|
35550
|
+
finalize() {
|
|
35551
|
+
if (this.isInvalidated) {
|
|
35552
|
+
this.isInvalidated = false;
|
|
35553
|
+
this.computeFingerprints();
|
|
35554
|
+
}
|
|
35555
|
+
}
|
|
35556
|
+
enable() {
|
|
35557
|
+
this.isEnabled = true;
|
|
35558
|
+
this.computeFingerprints();
|
|
35559
|
+
}
|
|
35560
|
+
disable() {
|
|
35561
|
+
this.isEnabled = false;
|
|
35562
|
+
this.colors = new PositionMap();
|
|
35563
|
+
}
|
|
35564
|
+
computeFingerprints() {
|
|
35565
|
+
this.colors = new PositionMap();
|
|
35566
|
+
const fingerprints = new PositionMap();
|
|
35567
|
+
const allFingerprints = new Set();
|
|
35568
|
+
const activeSheetId = this.getters.getActiveSheetId();
|
|
35569
|
+
const cells = this.getters.getCells(activeSheetId);
|
|
35570
|
+
for (const cellId in cells) {
|
|
35571
|
+
const fingerprint = this.computeFingerprint(cells[cellId]);
|
|
35572
|
+
if (!fingerprint) {
|
|
35573
|
+
continue;
|
|
35574
|
+
}
|
|
35575
|
+
allFingerprints.add(fingerprint);
|
|
35576
|
+
const position = this.getters.getCellPosition(cellId);
|
|
35577
|
+
fingerprints.set(position, fingerprint);
|
|
35578
|
+
}
|
|
35579
|
+
this.assignColors(allFingerprints);
|
|
35580
|
+
for (const [position, fingerprint] of fingerprints.entries()) {
|
|
35581
|
+
const color = this.fingerprintColors[fingerprint];
|
|
35582
|
+
this.colors.set(position, color);
|
|
35583
|
+
this.colorSpreadZone(position, color);
|
|
35584
|
+
}
|
|
35585
|
+
}
|
|
35586
|
+
colorSpreadZone(position, fingerprintColor) {
|
|
35587
|
+
const spreadZone = this.getters.getSpreadZone(position);
|
|
35588
|
+
if (!spreadZone) {
|
|
35589
|
+
return;
|
|
35590
|
+
}
|
|
35591
|
+
const sheetId = position.sheetId;
|
|
35592
|
+
for (let row = spreadZone.top; row <= spreadZone.bottom; row++) {
|
|
35593
|
+
for (let col = spreadZone.left; col <= spreadZone.right; col++) {
|
|
35594
|
+
const spreadPosition = { sheetId, col, row };
|
|
35595
|
+
this.colors.set(spreadPosition, fingerprintColor);
|
|
35596
|
+
}
|
|
35597
|
+
}
|
|
35598
|
+
}
|
|
35599
|
+
assignColors(fingerprints) {
|
|
35600
|
+
const colors = new AlternatingColorGenerator(fingerprints.size);
|
|
35601
|
+
Object.keys(this.fingerprintColors).forEach(() => colors.next());
|
|
35602
|
+
for (const fingerprint of fingerprints) {
|
|
35603
|
+
if (!this.fingerprintColors[fingerprint]) {
|
|
35604
|
+
this.fingerprintColors[fingerprint] = setColorAlpha(colors.next(), 0.8);
|
|
35605
|
+
}
|
|
35606
|
+
}
|
|
35607
|
+
}
|
|
35608
|
+
computeFingerprint(cell) {
|
|
35609
|
+
const position = this.getters.getCellPosition(cell.id);
|
|
35610
|
+
if (cell.isFormula) {
|
|
35611
|
+
return this.computeFormulaFingerprint(position, cell);
|
|
35612
|
+
}
|
|
35613
|
+
else {
|
|
35614
|
+
return this.getLiteralFingerprint(position);
|
|
35615
|
+
}
|
|
35616
|
+
}
|
|
35617
|
+
computeFormulaFingerprint(position, cell) {
|
|
35618
|
+
const dependencies = cell.compiledFormula.dependencies;
|
|
35619
|
+
const colCellOffset = position.col;
|
|
35620
|
+
const rowCellOffset = position.row;
|
|
35621
|
+
const positionSheetIndex = this.getters.getSheetIds().indexOf(position.sheetId);
|
|
35622
|
+
// As an optimization, we do not build each reference vector individually
|
|
35623
|
+
// to sum them up, but instead we directly add each component to the resulting
|
|
35624
|
+
// vector. This is equivalent to summing up all reference vectors.
|
|
35625
|
+
const fingerprintVector = {
|
|
35626
|
+
dx: 0,
|
|
35627
|
+
dy: 0,
|
|
35628
|
+
dSheet: 0,
|
|
35629
|
+
};
|
|
35630
|
+
for (const range of dependencies) {
|
|
35631
|
+
const zone = range.zone;
|
|
35632
|
+
const [left, right] = range.parts;
|
|
35633
|
+
const rangeSheetIndex = this.getters.getSheetIds().indexOf(range.sheetId);
|
|
35634
|
+
fingerprintVector.dSheet = rangeSheetIndex - positionSheetIndex;
|
|
35635
|
+
// in relative mode, we offset the col and row by the cell's position
|
|
35636
|
+
// in absolute mode, we offset the col and row relative to the sheet
|
|
35637
|
+
const isLeftUnbounded = range.isFullRow && !range.unboundedZone.hasHeader;
|
|
35638
|
+
const isTopUnbounded = range.isFullCol && !range.unboundedZone.hasHeader;
|
|
35639
|
+
const leftOffset = isLeftUnbounded || left?.colFixed ? 0 : colCellOffset;
|
|
35640
|
+
const topOffset = isTopUnbounded || left?.rowFixed ? 0 : rowCellOffset;
|
|
35641
|
+
const isRightFixed = (!right && left?.colFixed) || right?.colFixed;
|
|
35642
|
+
const isBottomFixed = (!right && left.rowFixed) || right?.rowFixed;
|
|
35643
|
+
const isRightUnbounded = range.unboundedZone.right === undefined;
|
|
35644
|
+
const isBottomUnbounded = range.unboundedZone.bottom === undefined;
|
|
35645
|
+
const rightOffset = isRightUnbounded || isRightFixed ? 0 : colCellOffset;
|
|
35646
|
+
const bottomOffset = isBottomUnbounded || isBottomFixed ? 0 : rowCellOffset;
|
|
35647
|
+
const referenceZone = reorderZone({
|
|
35648
|
+
top: zone.top - topOffset,
|
|
35649
|
+
left: zone.left - leftOffset,
|
|
35650
|
+
right: zone.right - rightOffset,
|
|
35651
|
+
bottom: zone.bottom - bottomOffset,
|
|
35652
|
+
});
|
|
35653
|
+
for (let dy = referenceZone.top; dy <= referenceZone.bottom; dy++) {
|
|
35654
|
+
for (let dx = referenceZone.left; dx <= referenceZone.right; dx++) {
|
|
35655
|
+
fingerprintVector.dx += dx;
|
|
35656
|
+
fingerprintVector.dy += dy;
|
|
35657
|
+
}
|
|
35658
|
+
}
|
|
35659
|
+
}
|
|
35660
|
+
// removes the index placeholders from the normalized formula
|
|
35661
|
+
// =|N0|+|N1|+|N0| -> =|N|+|N|+|N|
|
|
35662
|
+
const normalizedFormula = cell.compiledFormula.normalizedFormula.replace(/(|\w)(\d)(|)/g, "$1$3");
|
|
35663
|
+
return hash(fingerprintVector) + normalizedFormula;
|
|
35664
|
+
}
|
|
35665
|
+
getLiteralFingerprint(position) {
|
|
35666
|
+
const evaluatedCell = this.getters.getEvaluatedCell(position);
|
|
35667
|
+
switch (evaluatedCell.type) {
|
|
35668
|
+
case CellValueType.number:
|
|
35669
|
+
case CellValueType.boolean:
|
|
35670
|
+
return DATA_FINGERPRINT;
|
|
35671
|
+
case CellValueType.text:
|
|
35672
|
+
case CellValueType.empty:
|
|
35673
|
+
case CellValueType.error:
|
|
35674
|
+
return undefined;
|
|
35675
|
+
}
|
|
35676
|
+
}
|
|
35677
|
+
}
|
|
35678
|
+
function hash(vector) {
|
|
35679
|
+
return Object.entries(vector)
|
|
35680
|
+
.sort(([a], [b]) => a.localeCompare(b))
|
|
35681
|
+
.map(([_, value]) => value)
|
|
35682
|
+
.join(",");
|
|
35683
|
+
}
|
|
35684
|
+
const DATA_FINGERPRINT = "DATA_FINGERPRINT";
|
|
35685
|
+
|
|
34879
35686
|
const hideCols = {
|
|
34880
35687
|
name: HIDE_COLUMNS_NAME,
|
|
34881
35688
|
execute: (env) => {
|
|
@@ -35047,6 +35854,19 @@ const viewGridlines = {
|
|
|
35047
35854
|
return env.model.getters.getGridLinesVisibility(sheetId);
|
|
35048
35855
|
},
|
|
35049
35856
|
};
|
|
35857
|
+
const irregularityMap = {
|
|
35858
|
+
name: _t("Irregularity map"),
|
|
35859
|
+
execute: (env) => {
|
|
35860
|
+
const fingerprintStore = env.getStore(FormulaFingerprintStore);
|
|
35861
|
+
if (fingerprintStore.isEnabled) {
|
|
35862
|
+
fingerprintStore.disable();
|
|
35863
|
+
}
|
|
35864
|
+
else {
|
|
35865
|
+
fingerprintStore.enable();
|
|
35866
|
+
}
|
|
35867
|
+
},
|
|
35868
|
+
icon: "o-spreadsheet-Icon.IRREGULARITY_MAP",
|
|
35869
|
+
};
|
|
35050
35870
|
const viewFormulas = {
|
|
35051
35871
|
name: _t("Formulas"),
|
|
35052
35872
|
isActive: (env) => env.model.getters.shouldShowFormulas(),
|
|
@@ -35664,6 +36484,11 @@ topbarMenuRegistry
|
|
|
35664
36484
|
.addChild("view_formulas", ["view", "show"], {
|
|
35665
36485
|
...viewFormulas,
|
|
35666
36486
|
sequence: 10,
|
|
36487
|
+
})
|
|
36488
|
+
.addChild("view_irregularity_map", ["view"], {
|
|
36489
|
+
...irregularityMap,
|
|
36490
|
+
sequence: 40,
|
|
36491
|
+
separator: true,
|
|
35667
36492
|
})
|
|
35668
36493
|
// ---------------------------------------------------------------------
|
|
35669
36494
|
// INSERT MENU ITEMS
|
|
@@ -35966,62 +36791,6 @@ class OTRegistry extends Registry {
|
|
|
35966
36791
|
}
|
|
35967
36792
|
const otRegistry = new OTRegistry();
|
|
35968
36793
|
|
|
35969
|
-
const CHECK_SVG = /*xml*/ `
|
|
35970
|
-
<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'>
|
|
35971
|
-
<path fill='none' stroke='#FFF' stroke-linecap='round' stroke-linejoin='round' stroke-width='3' d='m6 10 3 3 6-6'/>
|
|
35972
|
-
</svg>
|
|
35973
|
-
`;
|
|
35974
|
-
const CHECKBOX_WIDTH = 14;
|
|
35975
|
-
css /* scss */ `
|
|
35976
|
-
label.o-checkbox {
|
|
35977
|
-
input {
|
|
35978
|
-
appearance: none;
|
|
35979
|
-
-webkit-appearance: none;
|
|
35980
|
-
-moz-appearance: none;
|
|
35981
|
-
border-radius: 0;
|
|
35982
|
-
width: ${CHECKBOX_WIDTH}px;
|
|
35983
|
-
height: ${CHECKBOX_WIDTH}px;
|
|
35984
|
-
vertical-align: top;
|
|
35985
|
-
box-sizing: border-box;
|
|
35986
|
-
outline: none;
|
|
35987
|
-
border: 1px solid ${GRAY_300};
|
|
35988
|
-
|
|
35989
|
-
&:hover {
|
|
35990
|
-
border-color: ${ACTION_COLOR};
|
|
35991
|
-
}
|
|
35992
|
-
|
|
35993
|
-
&:checked {
|
|
35994
|
-
background: url("data:image/svg+xml,${encodeURIComponent(CHECK_SVG)}");
|
|
35995
|
-
background-color: ${ACTION_COLOR};
|
|
35996
|
-
border-color: ${ACTION_COLOR};
|
|
35997
|
-
}
|
|
35998
|
-
|
|
35999
|
-
&:focus {
|
|
36000
|
-
outline: none;
|
|
36001
|
-
box-shadow: 0 0 0 0.25rem rgba(113, 75, 103, 0.25);
|
|
36002
|
-
border-color: ${ACTION_COLOR};
|
|
36003
|
-
}
|
|
36004
|
-
}
|
|
36005
|
-
}
|
|
36006
|
-
`;
|
|
36007
|
-
class Checkbox extends Component {
|
|
36008
|
-
static template = "o-spreadsheet.Checkbox";
|
|
36009
|
-
static props = {
|
|
36010
|
-
label: { type: String, optional: true },
|
|
36011
|
-
value: { type: Boolean, optional: true },
|
|
36012
|
-
className: { type: String, optional: true },
|
|
36013
|
-
name: { type: String, optional: true },
|
|
36014
|
-
title: { type: String, optional: true },
|
|
36015
|
-
disabled: { type: Boolean, optional: true },
|
|
36016
|
-
onChange: Function,
|
|
36017
|
-
};
|
|
36018
|
-
static defaultProps = { value: false };
|
|
36019
|
-
onChange(ev) {
|
|
36020
|
-
const value = ev.target.checked;
|
|
36021
|
-
this.props.onChange(value);
|
|
36022
|
-
}
|
|
36023
|
-
}
|
|
36024
|
-
|
|
36025
36794
|
class Section extends Component {
|
|
36026
36795
|
static template = "o_spreadsheet.Section";
|
|
36027
36796
|
static props = {
|
|
@@ -38261,6 +39030,104 @@ class GaugeChartDesignPanel extends Component {
|
|
|
38261
39030
|
}
|
|
38262
39031
|
}
|
|
38263
39032
|
|
|
39033
|
+
class GeoChartRegionSelectSection extends Component {
|
|
39034
|
+
static template = "o-spreadsheet-GeoChartRegionSelectSection";
|
|
39035
|
+
static components = { Section };
|
|
39036
|
+
static props = {
|
|
39037
|
+
figureId: String,
|
|
39038
|
+
definition: Object,
|
|
39039
|
+
updateChart: Function,
|
|
39040
|
+
};
|
|
39041
|
+
updateSelectedRegion(ev) {
|
|
39042
|
+
const value = ev.target.value;
|
|
39043
|
+
this.props.updateChart(this.props.figureId, { region: value });
|
|
39044
|
+
}
|
|
39045
|
+
get availableRegions() {
|
|
39046
|
+
return this.env.model.getters.getGeoChartAvailableRegions();
|
|
39047
|
+
}
|
|
39048
|
+
get selectedRegion() {
|
|
39049
|
+
return this.props.definition.region || this.availableRegions[0]?.id;
|
|
39050
|
+
}
|
|
39051
|
+
}
|
|
39052
|
+
|
|
39053
|
+
class GeoChartConfigPanel extends GenericChartConfigPanel {
|
|
39054
|
+
static template = "o-spreadsheet-GeoChartConfigPanel";
|
|
39055
|
+
static components = { ...GenericChartConfigPanel.components, GeoChartRegionSelectSection };
|
|
39056
|
+
get dataRanges() {
|
|
39057
|
+
return this.getDataSeriesRanges().slice(0, 1);
|
|
39058
|
+
}
|
|
39059
|
+
onDataSeriesConfirmed() {
|
|
39060
|
+
this.dataSeriesRanges = spreadRange(this.env.model.getters, this.dataSeriesRanges).slice(0, 1);
|
|
39061
|
+
this.state.datasetDispatchResult = this.props.updateChart(this.props.figureId, {
|
|
39062
|
+
dataSets: this.dataSeriesRanges,
|
|
39063
|
+
});
|
|
39064
|
+
}
|
|
39065
|
+
getLabelRangeOptions() {
|
|
39066
|
+
return [
|
|
39067
|
+
{
|
|
39068
|
+
name: "dataSetsHaveTitle",
|
|
39069
|
+
label: this.dataSetsHaveTitleLabel,
|
|
39070
|
+
value: this.props.definition.dataSetsHaveTitle,
|
|
39071
|
+
onChange: this.onUpdateDataSetsHaveTitle.bind(this),
|
|
39072
|
+
},
|
|
39073
|
+
];
|
|
39074
|
+
}
|
|
39075
|
+
}
|
|
39076
|
+
|
|
39077
|
+
const DEFAULT_CUSTOM_COLOR_SCALE = {
|
|
39078
|
+
minColor: "#FFF5EB",
|
|
39079
|
+
midColor: "#FD8D3C",
|
|
39080
|
+
maxColor: "#7F2704",
|
|
39081
|
+
};
|
|
39082
|
+
class GeoChartDesignPanel extends ChartWithAxisDesignPanel {
|
|
39083
|
+
static template = "o-spreadsheet-GeoChartDesignPanel";
|
|
39084
|
+
static components = { ...ChartWithAxisDesignPanel.components, RoundColorPicker };
|
|
39085
|
+
colorScalesChoices = ChartTerms.GeoChart.ColorScales;
|
|
39086
|
+
updateColorScaleType(ev) {
|
|
39087
|
+
const value = ev.target.value;
|
|
39088
|
+
value === "custom"
|
|
39089
|
+
? this.updateColorScale(DEFAULT_CUSTOM_COLOR_SCALE)
|
|
39090
|
+
: this.updateColorScale(value);
|
|
39091
|
+
}
|
|
39092
|
+
updateColorScale(colorScale) {
|
|
39093
|
+
this.props.updateChart(this.props.figureId, { colorScale });
|
|
39094
|
+
}
|
|
39095
|
+
updateMissingValueColor(color) {
|
|
39096
|
+
this.props.updateChart(this.props.figureId, { missingValueColor: color });
|
|
39097
|
+
}
|
|
39098
|
+
updateLegendPosition(ev) {
|
|
39099
|
+
const value = ev.target.value;
|
|
39100
|
+
this.props.updateChart(this.props.figureId, { legendPosition: value });
|
|
39101
|
+
}
|
|
39102
|
+
get selectedColorScale() {
|
|
39103
|
+
return typeof this.props.definition.colorScale === "object"
|
|
39104
|
+
? "custom"
|
|
39105
|
+
: this.props.definition.colorScale || "oranges";
|
|
39106
|
+
}
|
|
39107
|
+
get selectedMissingValueColor() {
|
|
39108
|
+
return this.props.definition.missingValueColor || "#ffffff";
|
|
39109
|
+
}
|
|
39110
|
+
get customColorScale() {
|
|
39111
|
+
if (typeof this.props.definition.colorScale === "object") {
|
|
39112
|
+
return this.props.definition.colorScale;
|
|
39113
|
+
}
|
|
39114
|
+
return undefined;
|
|
39115
|
+
}
|
|
39116
|
+
getCustomColorScaleColor(color) {
|
|
39117
|
+
return this.customColorScale?.[color] ?? "";
|
|
39118
|
+
}
|
|
39119
|
+
setCustomColorScaleColor(colorType, color) {
|
|
39120
|
+
if (!color && colorType !== "midColor") {
|
|
39121
|
+
color = "#fff";
|
|
39122
|
+
}
|
|
39123
|
+
const customColorScale = this.customColorScale;
|
|
39124
|
+
if (!customColorScale) {
|
|
39125
|
+
return;
|
|
39126
|
+
}
|
|
39127
|
+
this.updateColorScale({ ...customColorScale, [colorType]: color });
|
|
39128
|
+
}
|
|
39129
|
+
}
|
|
39130
|
+
|
|
38264
39131
|
class LineConfigPanel extends GenericChartConfigPanel {
|
|
38265
39132
|
static template = "o-spreadsheet-LineConfigPanel";
|
|
38266
39133
|
get canTreatLabelsAsText() {
|
|
@@ -38574,6 +39441,10 @@ chartSidePanelComponentRegistry
|
|
|
38574
39441
|
.add("radar", {
|
|
38575
39442
|
configuration: GenericChartConfigPanel,
|
|
38576
39443
|
design: RadarChartDesignPanel,
|
|
39444
|
+
})
|
|
39445
|
+
.add("geo", {
|
|
39446
|
+
configuration: GeoChartConfigPanel,
|
|
39447
|
+
design: GeoChartDesignPanel,
|
|
38577
39448
|
});
|
|
38578
39449
|
|
|
38579
39450
|
css /* scss */ `
|
|
@@ -39937,7 +40808,7 @@ css /* scss */ `
|
|
|
39937
40808
|
border-color: ${GRAY_300};
|
|
39938
40809
|
|
|
39939
40810
|
&.active {
|
|
39940
|
-
border-color: ${
|
|
40811
|
+
border-color: ${ACTION_COLOR};
|
|
39941
40812
|
}
|
|
39942
40813
|
|
|
39943
40814
|
&.o-invalid {
|
|
@@ -40895,7 +41766,7 @@ class ConditionalFormattingEditor extends Component {
|
|
|
40895
41766
|
}
|
|
40896
41767
|
const point = this.state.rules.colorScale[target];
|
|
40897
41768
|
if (point) {
|
|
40898
|
-
point.color =
|
|
41769
|
+
point.color = colorToNumber(color);
|
|
40899
41770
|
}
|
|
40900
41771
|
this.updateConditionalFormat({ rule: this.state.rules.colorScale });
|
|
40901
41772
|
this.closeMenus();
|
|
@@ -41040,6 +41911,9 @@ class ConditionalFormattingEditor extends Component {
|
|
|
41040
41911
|
return [this.state.rules.dataBar.rangeValues || ""];
|
|
41041
41912
|
}
|
|
41042
41913
|
updateDataBarColor(color) {
|
|
41914
|
+
if (!isColorValid(color)) {
|
|
41915
|
+
return;
|
|
41916
|
+
}
|
|
41043
41917
|
this.state.rules.dataBar.color = Number.parseInt(color.slice(1), 16);
|
|
41044
41918
|
this.updateConditionalFormat({ rule: this.state.rules.dataBar });
|
|
41045
41919
|
}
|
|
@@ -43643,6 +44517,64 @@ class PivotMeasureEditor extends Component {
|
|
|
43643
44517
|
}
|
|
43644
44518
|
}
|
|
43645
44519
|
|
|
44520
|
+
css /* scss */ `
|
|
44521
|
+
.o-pivot-sort {
|
|
44522
|
+
.o-sort-card {
|
|
44523
|
+
width: fit-content;
|
|
44524
|
+
background-color: ${GRAY_100};
|
|
44525
|
+
border: 1px solid ${GRAY_300};
|
|
44526
|
+
|
|
44527
|
+
.o-sort-value {
|
|
44528
|
+
color: ${PRIMARY_BUTTON_BG};
|
|
44529
|
+
}
|
|
44530
|
+
}
|
|
44531
|
+
}
|
|
44532
|
+
`;
|
|
44533
|
+
class PivotSortSection extends Component {
|
|
44534
|
+
static template = "o-spreadsheet-PivotSortSection";
|
|
44535
|
+
static components = {
|
|
44536
|
+
Section,
|
|
44537
|
+
};
|
|
44538
|
+
static props = {
|
|
44539
|
+
definition: Object,
|
|
44540
|
+
pivotId: String,
|
|
44541
|
+
};
|
|
44542
|
+
get hasValidSort() {
|
|
44543
|
+
const pivot = this.env.model.getters.getPivot(this.props.pivotId);
|
|
44544
|
+
return (!!this.props.definition.sortedColumn &&
|
|
44545
|
+
isSortedColumnValid(this.props.definition.sortedColumn, pivot));
|
|
44546
|
+
}
|
|
44547
|
+
get sortDescription() {
|
|
44548
|
+
const sortOrder = this.props.definition.sortedColumn?.order === "asc" ? _t("ascending") : _t("descending");
|
|
44549
|
+
return _t("Sorted on column (%(ascOrDesc)s):", {
|
|
44550
|
+
ascOrDesc: sortOrder,
|
|
44551
|
+
});
|
|
44552
|
+
}
|
|
44553
|
+
get sortValuesAndFields() {
|
|
44554
|
+
const sortedColumn = this.props.definition.sortedColumn;
|
|
44555
|
+
if (!sortedColumn) {
|
|
44556
|
+
return [];
|
|
44557
|
+
}
|
|
44558
|
+
const pivot = this.env.model.getters.getPivot(this.props.pivotId);
|
|
44559
|
+
const locale = this.env.model.getters.getLocale();
|
|
44560
|
+
const currentDomain = [];
|
|
44561
|
+
const sortValues = [];
|
|
44562
|
+
for (const domainItem of sortedColumn.domain) {
|
|
44563
|
+
currentDomain.push(domainItem);
|
|
44564
|
+
const { value, format } = pivot.getPivotHeaderValueAndFormat(currentDomain);
|
|
44565
|
+
const label = formatValue(value, { format, locale });
|
|
44566
|
+
const field = pivot.definition.getDimension(domainItem.field);
|
|
44567
|
+
sortValues.push({ field: getFieldDisplayName(field), value: label });
|
|
44568
|
+
}
|
|
44569
|
+
if (sortedColumn.domain.length === 0) {
|
|
44570
|
+
sortValues.push({ value: _t("Total") });
|
|
44571
|
+
}
|
|
44572
|
+
const measureLabel = pivot.getMeasure(sortedColumn.measure).displayName;
|
|
44573
|
+
sortValues.push({ value: measureLabel, field: _t("Measure") });
|
|
44574
|
+
return sortValues;
|
|
44575
|
+
}
|
|
44576
|
+
}
|
|
44577
|
+
|
|
43646
44578
|
css /* scss */ `
|
|
43647
44579
|
.add-calculated-measure {
|
|
43648
44580
|
cursor: pointer;
|
|
@@ -43656,6 +44588,7 @@ class PivotLayoutConfigurator extends Component {
|
|
|
43656
44588
|
PivotDimensionOrder,
|
|
43657
44589
|
PivotDimensionGranularity,
|
|
43658
44590
|
PivotMeasureEditor,
|
|
44591
|
+
PivotSortSection,
|
|
43659
44592
|
};
|
|
43660
44593
|
static props = {
|
|
43661
44594
|
definition: Object,
|
|
@@ -43812,9 +44745,13 @@ class PivotLayoutConfigurator extends Component {
|
|
|
43812
44745
|
}
|
|
43813
44746
|
updateMeasure(measure, newMeasure) {
|
|
43814
44747
|
const { measures } = this.props.definition;
|
|
43815
|
-
|
|
44748
|
+
const update = {
|
|
43816
44749
|
measures: measures.map((m) => (m.id === measure.id ? newMeasure : m)),
|
|
43817
|
-
}
|
|
44750
|
+
};
|
|
44751
|
+
if (this.props.definition.sortedColumn?.measure === measure.id) {
|
|
44752
|
+
update.sortedColumn = { ...this.props.definition.sortedColumn, measure: newMeasure.id };
|
|
44753
|
+
}
|
|
44754
|
+
this.props.onDimensionsUpdated(update);
|
|
43818
44755
|
}
|
|
43819
44756
|
getMeasureId(fieldName, aggregator) {
|
|
43820
44757
|
const baseId = fieldName + (aggregator ? `:${aggregator}` : "");
|
|
@@ -43970,10 +44907,12 @@ class PivotRuntimeDefinition {
|
|
|
43970
44907
|
measures;
|
|
43971
44908
|
columns;
|
|
43972
44909
|
rows;
|
|
44910
|
+
sortedColumn;
|
|
43973
44911
|
constructor(definition, fields) {
|
|
43974
44912
|
this.measures = definition.measures.map((measure) => createMeasure(fields, measure));
|
|
43975
44913
|
this.columns = definition.columns.map((dimension) => createPivotDimension(fields, dimension));
|
|
43976
44914
|
this.rows = definition.rows.map((dimension) => createPivotDimension(fields, dimension));
|
|
44915
|
+
this.sortedColumn = definition.sortedColumn;
|
|
43977
44916
|
}
|
|
43978
44917
|
getDimension(nameWithGranularity) {
|
|
43979
44918
|
const dimension = this.columns.find((d) => d.nameWithGranularity === nameWithGranularity) ||
|
|
@@ -44127,6 +45066,7 @@ class SpreadsheetPivotTable {
|
|
|
44127
45066
|
pivotCells = {};
|
|
44128
45067
|
rowTree;
|
|
44129
45068
|
colTree;
|
|
45069
|
+
isSorted = false;
|
|
44130
45070
|
constructor(columns, rows, measures, fieldsType) {
|
|
44131
45071
|
this.columns = columns.map((row) => {
|
|
44132
45072
|
// offset in the pivot table
|
|
@@ -44300,6 +45240,7 @@ class SpreadsheetPivotTable {
|
|
|
44300
45240
|
value,
|
|
44301
45241
|
field: row.fields[rowDepth],
|
|
44302
45242
|
children: [],
|
|
45243
|
+
type: this.fieldsType[fieldName] || "char",
|
|
44303
45244
|
width: 0, // not used
|
|
44304
45245
|
};
|
|
44305
45246
|
treesAtDepth[depth].push(node);
|
|
@@ -44322,6 +45263,7 @@ class SpreadsheetPivotTable {
|
|
|
44322
45263
|
field: leaf.fields[depth],
|
|
44323
45264
|
children: [],
|
|
44324
45265
|
width: leaf.width,
|
|
45266
|
+
type: this.fieldsType[fieldName] || "char",
|
|
44325
45267
|
};
|
|
44326
45268
|
if (treesAtDepth[depth]?.at(-1)?.value !== value) {
|
|
44327
45269
|
treesAtDepth[depth + 1] = [];
|
|
@@ -44340,6 +45282,35 @@ class SpreadsheetPivotTable {
|
|
|
44340
45282
|
fieldsType: this.fieldsType,
|
|
44341
45283
|
};
|
|
44342
45284
|
}
|
|
45285
|
+
sort(measure, sortedColumn, getValue) {
|
|
45286
|
+
if (this.isSorted) {
|
|
45287
|
+
return;
|
|
45288
|
+
}
|
|
45289
|
+
const getSortValue = (measure, domain) => {
|
|
45290
|
+
const rawValue = getValue(measure, domain).value;
|
|
45291
|
+
return typeof rawValue === "number" ? rawValue : -Infinity;
|
|
45292
|
+
};
|
|
45293
|
+
const sortColDomain = sortedColumn.domain;
|
|
45294
|
+
const sortFn = (rowDomain1, rowDomain2) => {
|
|
45295
|
+
const value1 = getSortValue(measure, [...rowDomain1, ...sortColDomain]);
|
|
45296
|
+
const value2 = getSortValue(measure, [...rowDomain2, ...sortColDomain]);
|
|
45297
|
+
return sortedColumn.order === "asc" ? value1 - value2 : value2 - value1;
|
|
45298
|
+
};
|
|
45299
|
+
const sortedRowTree = sortPivotTree(this.rowTree(), [], sortFn);
|
|
45300
|
+
this.rowTree = lazy(sortedRowTree);
|
|
45301
|
+
this.rows = [...this.rowTreeToRows(sortedRowTree), this.rows[this.rows.length - 1]];
|
|
45302
|
+
this.isSorted = true;
|
|
45303
|
+
}
|
|
45304
|
+
rowTreeToRows(tree, parentRow) {
|
|
45305
|
+
return tree.flatMap((node) => {
|
|
45306
|
+
const row = {
|
|
45307
|
+
indent: parentRow ? parentRow.indent + 1 : 0,
|
|
45308
|
+
fields: [...(parentRow?.fields || []), node.field],
|
|
45309
|
+
values: [...(parentRow?.values || []), node.value],
|
|
45310
|
+
};
|
|
45311
|
+
return [row, ...this.rowTreeToRows(node.children, row)];
|
|
45312
|
+
});
|
|
45313
|
+
}
|
|
44343
45314
|
}
|
|
44344
45315
|
const EMPTY_PIVOT_CELL = { type: "EMPTY" };
|
|
44345
45316
|
|
|
@@ -44417,6 +45388,7 @@ function dataEntriesToColumnsTree(dataEntries, columns, index) {
|
|
|
44417
45388
|
value: groups[key]?.[0]?.[column.nameWithGranularity]?.value ?? null,
|
|
44418
45389
|
field: colName,
|
|
44419
45390
|
children: dataEntriesToColumnsTree(groups[key] || [], columns, index + 1),
|
|
45391
|
+
type: column.type,
|
|
44420
45392
|
width: 0,
|
|
44421
45393
|
};
|
|
44422
45394
|
});
|
|
@@ -45282,6 +46254,7 @@ class PivotSidePanelStore extends SpreadsheetStore {
|
|
|
45282
46254
|
format: measure.format,
|
|
45283
46255
|
display: measure.display,
|
|
45284
46256
|
})),
|
|
46257
|
+
sortedColumn: this.shouldKeepSortedColumn(definition) ? definition.sortedColumn : undefined,
|
|
45285
46258
|
};
|
|
45286
46259
|
if (!this.draft && deepEquals(coreDefinition, cleanedDefinition)) {
|
|
45287
46260
|
return;
|
|
@@ -45340,6 +46313,19 @@ class PivotSidePanelStore extends SpreadsheetStore {
|
|
|
45340
46313
|
}
|
|
45341
46314
|
return granularitiesPerFields;
|
|
45342
46315
|
}
|
|
46316
|
+
/**
|
|
46317
|
+
* Check if we want to keep the sorted column when updating the pivot definition. We should remove it if either
|
|
46318
|
+
* the measure is not in the new definition or the columns have changed.
|
|
46319
|
+
*/
|
|
46320
|
+
shouldKeepSortedColumn(newDefinition) {
|
|
46321
|
+
const { sortedColumn } = newDefinition;
|
|
46322
|
+
if (!sortedColumn) {
|
|
46323
|
+
return true;
|
|
46324
|
+
}
|
|
46325
|
+
const oldDefinition = this.getters.getPivotCoreDefinition(this.pivotId);
|
|
46326
|
+
return (newDefinition.measures.find((measure) => measure.id === sortedColumn.measure) &&
|
|
46327
|
+
deepEquals(oldDefinition.columns, newDefinition.columns));
|
|
46328
|
+
}
|
|
45343
46329
|
}
|
|
45344
46330
|
|
|
45345
46331
|
class PivotSpreadsheetSidePanel extends Component {
|
|
@@ -46441,6 +47427,7 @@ css /* scss */ `
|
|
|
46441
47427
|
}
|
|
46442
47428
|
}
|
|
46443
47429
|
`;
|
|
47430
|
+
const DEFAULT_TABLE_STYLE_COLOR = "#3C78D8";
|
|
46444
47431
|
class TableStyleEditorPanel extends Component {
|
|
46445
47432
|
static template = "o-spreadsheet-TableStyleEditorPanel";
|
|
46446
47433
|
static components = { Section, RoundColorPicker, TableStylePreview };
|
|
@@ -46459,7 +47446,7 @@ class TableStyleEditorPanel extends Component {
|
|
|
46459
47446
|
: null;
|
|
46460
47447
|
return {
|
|
46461
47448
|
pickerOpened: false,
|
|
46462
|
-
primaryColor: editedStyle?.primaryColor ||
|
|
47449
|
+
primaryColor: editedStyle?.primaryColor || DEFAULT_TABLE_STYLE_COLOR,
|
|
46463
47450
|
selectedTemplateName: editedStyle?.templateName || "lightColoredText",
|
|
46464
47451
|
styleName: editedStyle?.displayName || this.env.model.getters.getNewCustomTableStyleName(),
|
|
46465
47452
|
};
|
|
@@ -46468,7 +47455,7 @@ class TableStyleEditorPanel extends Component {
|
|
|
46468
47455
|
this.state.pickerOpened = !this.state.pickerOpened;
|
|
46469
47456
|
}
|
|
46470
47457
|
onColorPicked(color) {
|
|
46471
|
-
this.state.primaryColor = color;
|
|
47458
|
+
this.state.primaryColor = isColorValid(color) ? color : DEFAULT_TABLE_STYLE_COLOR;
|
|
46472
47459
|
this.state.pickerOpened = false;
|
|
46473
47460
|
}
|
|
46474
47461
|
onTemplatePicked(templateName) {
|
|
@@ -48084,6 +49071,7 @@ class FiguresContainer extends Component {
|
|
|
48084
49071
|
draggedFigure: undefined,
|
|
48085
49072
|
horizontalSnap: undefined,
|
|
48086
49073
|
verticalSnap: undefined,
|
|
49074
|
+
cancelDnd: undefined,
|
|
48087
49075
|
});
|
|
48088
49076
|
setup() {
|
|
48089
49077
|
onMounted(() => {
|
|
@@ -48096,12 +49084,28 @@ class FiguresContainer extends Component {
|
|
|
48096
49084
|
// new rendering
|
|
48097
49085
|
this.render();
|
|
48098
49086
|
});
|
|
49087
|
+
onWillUpdateProps(() => {
|
|
49088
|
+
const sheetId = this.env.model.getters.getActiveSheetId();
|
|
49089
|
+
const draggedFigureId = this.dnd.draggedFigure?.id;
|
|
49090
|
+
if (draggedFigureId && !this.env.model.getters.getFigure(sheetId, draggedFigureId)) {
|
|
49091
|
+
if (this.dnd.cancelDnd) {
|
|
49092
|
+
this.dnd.cancelDnd();
|
|
49093
|
+
}
|
|
49094
|
+
this.dnd.draggedFigure = undefined;
|
|
49095
|
+
this.dnd.horizontalSnap = undefined;
|
|
49096
|
+
this.dnd.verticalSnap = undefined;
|
|
49097
|
+
this.dnd.cancelDnd = undefined;
|
|
49098
|
+
}
|
|
49099
|
+
});
|
|
48099
49100
|
}
|
|
48100
49101
|
getVisibleFigures() {
|
|
48101
49102
|
const visibleFigures = this.env.model.getters.getVisibleFigures();
|
|
48102
49103
|
if (this.dnd.draggedFigure &&
|
|
48103
49104
|
!visibleFigures.some((figure) => figure.id === this.dnd.draggedFigure?.id)) {
|
|
48104
|
-
|
|
49105
|
+
const draggedFigure = this.env.model.getters.getFigure(this.env.model.getters.getActiveSheetId(), this.dnd.draggedFigure?.id);
|
|
49106
|
+
if (draggedFigure) {
|
|
49107
|
+
visibleFigures.push(draggedFigure);
|
|
49108
|
+
}
|
|
48105
49109
|
}
|
|
48106
49110
|
return visibleFigures;
|
|
48107
49111
|
}
|
|
@@ -48220,7 +49224,7 @@ class FiguresContainer extends Component {
|
|
|
48220
49224
|
this.dnd.verticalSnap = undefined;
|
|
48221
49225
|
this.env.model.dispatch("UPDATE_FIGURE", { sheetId, id: figure.id, x, y });
|
|
48222
49226
|
};
|
|
48223
|
-
startDnd(onMouseMove, onMouseUp);
|
|
49227
|
+
this.dnd.cancelDnd = startDnd(onMouseMove, onMouseUp);
|
|
48224
49228
|
}
|
|
48225
49229
|
/**
|
|
48226
49230
|
* Initialize the resize of a figure with mouse movements
|
|
@@ -48268,7 +49272,7 @@ class FiguresContainer extends Component {
|
|
|
48268
49272
|
this.dnd.horizontalSnap = undefined;
|
|
48269
49273
|
this.dnd.verticalSnap = undefined;
|
|
48270
49274
|
};
|
|
48271
|
-
startDnd(onMouseMove, onMouseUp);
|
|
49275
|
+
this.dnd.cancelDnd = startDnd(onMouseMove, onMouseUp);
|
|
48272
49276
|
}
|
|
48273
49277
|
getOtherFigures(figId) {
|
|
48274
49278
|
return this.getVisibleFigures().filter((f) => f.id !== figId);
|
|
@@ -49375,9 +50379,11 @@ class HeadersOverlay extends Component {
|
|
|
49375
50379
|
class GridRenderer {
|
|
49376
50380
|
getters;
|
|
49377
50381
|
renderer;
|
|
50382
|
+
fingerprints;
|
|
49378
50383
|
constructor(get) {
|
|
49379
50384
|
this.getters = get(ModelStore).getters;
|
|
49380
50385
|
this.renderer = get(RendererStore);
|
|
50386
|
+
this.fingerprints = get(FormulaFingerprintStore);
|
|
49381
50387
|
this.renderer.register(this);
|
|
49382
50388
|
}
|
|
49383
50389
|
get renderingLayers() {
|
|
@@ -49855,14 +50861,22 @@ class GridRenderer {
|
|
|
49855
50861
|
const showFormula = this.getters.shouldShowFormulas();
|
|
49856
50862
|
const { x, y, width, height } = this.getters.getVisibleRect(zone);
|
|
49857
50863
|
const { verticalAlign } = this.getters.getCellStyle(position);
|
|
50864
|
+
let style = this.getters.getCellComputedStyle(position);
|
|
50865
|
+
if (this.fingerprints.isEnabled) {
|
|
50866
|
+
const fingerprintColor = this.fingerprints.colors.get(position);
|
|
50867
|
+
style = { ...style, fillColor: fingerprintColor };
|
|
50868
|
+
}
|
|
50869
|
+
const dataBarFill = this.fingerprints.isEnabled
|
|
50870
|
+
? undefined
|
|
50871
|
+
: this.getters.getConditionalDataBar(position);
|
|
49858
50872
|
const box = {
|
|
49859
50873
|
x,
|
|
49860
50874
|
y,
|
|
49861
50875
|
width,
|
|
49862
50876
|
height,
|
|
49863
50877
|
border: this.getters.getCellComputedBorder(position) || undefined,
|
|
49864
|
-
style
|
|
49865
|
-
dataBarFill
|
|
50878
|
+
style,
|
|
50879
|
+
dataBarFill,
|
|
49866
50880
|
verticalAlign,
|
|
49867
50881
|
isError: (cell.type === CellValueType.error && !!cell.message) ||
|
|
49868
50882
|
this.getters.isDataValidationInvalid(position),
|
|
@@ -49887,7 +50901,6 @@ class GridRenderer {
|
|
|
49887
50901
|
box.hasIcon = this.getters.doesCellHaveGridIcon(position);
|
|
49888
50902
|
const headerIconWidth = box.hasIcon ? GRID_ICON_EDGE_LENGTH + GRID_ICON_MARGIN : 0;
|
|
49889
50903
|
/** Content */
|
|
49890
|
-
const style = this.getters.getCellComputedStyle(position);
|
|
49891
50904
|
const wrapping = style.wrapping || "overflow";
|
|
49892
50905
|
const wrapText = wrapping === "wrap" && !showFormula;
|
|
49893
50906
|
const maxWidth = width - 2 * MIN_CELL_TEXT_MARGIN;
|
|
@@ -51865,62 +52878,6 @@ class BordersPlugin extends CorePlugin {
|
|
|
51865
52878
|
}
|
|
51866
52879
|
}
|
|
51867
52880
|
|
|
51868
|
-
class PositionMap {
|
|
51869
|
-
map = {};
|
|
51870
|
-
constructor(entries = []) {
|
|
51871
|
-
for (const [position, value] of entries) {
|
|
51872
|
-
this.set(position, value);
|
|
51873
|
-
}
|
|
51874
|
-
}
|
|
51875
|
-
set({ sheetId, col, row }, value) {
|
|
51876
|
-
const map = this.map;
|
|
51877
|
-
if (!map[sheetId]) {
|
|
51878
|
-
map[sheetId] = {};
|
|
51879
|
-
}
|
|
51880
|
-
if (!map[sheetId][col]) {
|
|
51881
|
-
map[sheetId][col] = {};
|
|
51882
|
-
}
|
|
51883
|
-
map[sheetId][col][row] = value;
|
|
51884
|
-
}
|
|
51885
|
-
get({ sheetId, col, row }) {
|
|
51886
|
-
return this.map[sheetId]?.[col]?.[row];
|
|
51887
|
-
}
|
|
51888
|
-
getSheet(sheetId) {
|
|
51889
|
-
return this.map[sheetId];
|
|
51890
|
-
}
|
|
51891
|
-
has({ sheetId, col, row }) {
|
|
51892
|
-
return this.map[sheetId]?.[col]?.[row] !== undefined;
|
|
51893
|
-
}
|
|
51894
|
-
delete({ sheetId, col, row }) {
|
|
51895
|
-
delete this.map[sheetId]?.[col]?.[row];
|
|
51896
|
-
}
|
|
51897
|
-
keys() {
|
|
51898
|
-
const map = this.map;
|
|
51899
|
-
const keys = [];
|
|
51900
|
-
for (const sheetId in map) {
|
|
51901
|
-
for (const col in map[sheetId]) {
|
|
51902
|
-
for (const row in map[sheetId][col]) {
|
|
51903
|
-
keys.push({ sheetId, col: parseInt(col), row: parseInt(row) });
|
|
51904
|
-
}
|
|
51905
|
-
}
|
|
51906
|
-
}
|
|
51907
|
-
return keys;
|
|
51908
|
-
}
|
|
51909
|
-
keysForSheet(sheetId) {
|
|
51910
|
-
const map = this.map[sheetId];
|
|
51911
|
-
if (!map) {
|
|
51912
|
-
return [];
|
|
51913
|
-
}
|
|
51914
|
-
const keys = [];
|
|
51915
|
-
for (const col in map) {
|
|
51916
|
-
for (const row in map[col]) {
|
|
51917
|
-
keys.push({ sheetId, col: parseInt(col), row: parseInt(row) });
|
|
51918
|
-
}
|
|
51919
|
-
}
|
|
51920
|
-
return keys;
|
|
51921
|
-
}
|
|
51922
|
-
}
|
|
51923
|
-
|
|
51924
52881
|
/**
|
|
51925
52882
|
* Core Plugin
|
|
51926
52883
|
*
|
|
@@ -52359,7 +53316,7 @@ class CellPlugin extends CorePlugin {
|
|
|
52359
53316
|
const before = this.getters.getCell({ sheetId, col, row });
|
|
52360
53317
|
const hasContent = "content" in after || "formula" in after;
|
|
52361
53318
|
// Compute the new cell properties
|
|
52362
|
-
const afterContent = hasContent ?
|
|
53319
|
+
const afterContent = hasContent ? replaceNewLines(after?.content) : before?.content || "";
|
|
52363
53320
|
let style;
|
|
52364
53321
|
if (after.style !== undefined) {
|
|
52365
53322
|
style = after.style || undefined;
|
|
@@ -55304,6 +56261,9 @@ class SheetPlugin extends CorePlugin {
|
|
|
55304
56261
|
};
|
|
55305
56262
|
}
|
|
55306
56263
|
getUnboundedZone(sheetId, zone) {
|
|
56264
|
+
if (zone.bottom === undefined || zone.right === undefined) {
|
|
56265
|
+
return zone;
|
|
56266
|
+
}
|
|
55307
56267
|
const isFullRow = zone.left === 0 && zone.right === this.getNumberCols(sheetId) - 1;
|
|
55308
56268
|
const isFullCol = zone.top === 0 && zone.bottom === this.getNumberRows(sheetId) - 1;
|
|
55309
56269
|
return {
|
|
@@ -59587,66 +60547,25 @@ class EvaluationConditionalFormatPlugin extends UIPlugin {
|
|
|
59587
60547
|
return;
|
|
59588
60548
|
}
|
|
59589
60549
|
const zone = this.getters.getRangeFromSheetXC(sheetId, range).zone;
|
|
59590
|
-
const
|
|
60550
|
+
const colorThresholds = [{ value: minValue, color: rule.minimum.color }];
|
|
59591
60551
|
if (rule.midpoint && midValue) {
|
|
59592
|
-
|
|
59593
|
-
minValue,
|
|
59594
|
-
minColor: rule.minimum.color,
|
|
59595
|
-
colorDiffUnit: this.computeColorDiffUnits(minValue, midValue, rule.minimum.color, rule.midpoint.color),
|
|
59596
|
-
});
|
|
59597
|
-
colorCellArgs.push({
|
|
59598
|
-
minValue: midValue,
|
|
59599
|
-
minColor: rule.midpoint.color,
|
|
59600
|
-
colorDiffUnit: this.computeColorDiffUnits(midValue, maxValue, rule.midpoint.color, rule.maximum.color),
|
|
59601
|
-
});
|
|
59602
|
-
}
|
|
59603
|
-
else {
|
|
59604
|
-
colorCellArgs.push({
|
|
59605
|
-
minValue,
|
|
59606
|
-
minColor: rule.minimum.color,
|
|
59607
|
-
colorDiffUnit: this.computeColorDiffUnits(minValue, maxValue, rule.minimum.color, rule.maximum.color),
|
|
59608
|
-
});
|
|
60552
|
+
colorThresholds.push({ value: midValue, color: rule.midpoint.color });
|
|
59609
60553
|
}
|
|
60554
|
+
colorThresholds.push({ value: maxValue, color: rule.maximum.color });
|
|
60555
|
+
const colorScale = getColorScale(colorThresholds);
|
|
59610
60556
|
for (let row = zone.top; row <= zone.bottom; row++) {
|
|
59611
60557
|
for (let col = zone.left; col <= zone.right; col++) {
|
|
59612
60558
|
const cell = this.getters.getEvaluatedCell({ sheetId, col, row });
|
|
59613
60559
|
if (cell.type === CellValueType.number) {
|
|
59614
60560
|
const value = clip(cell.value, minValue, maxValue);
|
|
59615
|
-
let color;
|
|
59616
|
-
if (colorCellArgs.length === 2 && midValue) {
|
|
59617
|
-
color =
|
|
59618
|
-
value <= midValue
|
|
59619
|
-
? this.colorCell(value, colorCellArgs[0].minValue, colorCellArgs[0].minColor, colorCellArgs[0].colorDiffUnit)
|
|
59620
|
-
: this.colorCell(value, colorCellArgs[1].minValue, colorCellArgs[1].minColor, colorCellArgs[1].colorDiffUnit);
|
|
59621
|
-
}
|
|
59622
|
-
else {
|
|
59623
|
-
color = this.colorCell(value, colorCellArgs[0].minValue, colorCellArgs[0].minColor, colorCellArgs[0].colorDiffUnit);
|
|
59624
|
-
}
|
|
59625
60561
|
if (!computedStyle[col])
|
|
59626
60562
|
computedStyle[col] = [];
|
|
59627
60563
|
computedStyle[col][row] = computedStyle[col]?.[row] || {};
|
|
59628
|
-
computedStyle[col][row].fillColor =
|
|
60564
|
+
computedStyle[col][row].fillColor = colorScale(value);
|
|
59629
60565
|
}
|
|
59630
60566
|
}
|
|
59631
60567
|
}
|
|
59632
60568
|
}
|
|
59633
|
-
computeColorDiffUnits(minValue, maxValue, minColor, maxColor) {
|
|
59634
|
-
const deltaValue = maxValue - minValue;
|
|
59635
|
-
const deltaColorR = ((minColor >> 16) % 256) - ((maxColor >> 16) % 256);
|
|
59636
|
-
const deltaColorG = ((minColor >> 8) % 256) - ((maxColor >> 8) % 256);
|
|
59637
|
-
const deltaColorB = (minColor % 256) - (maxColor % 256);
|
|
59638
|
-
const colorDiffUnitR = deltaColorR / deltaValue;
|
|
59639
|
-
const colorDiffUnitG = deltaColorG / deltaValue;
|
|
59640
|
-
const colorDiffUnitB = deltaColorB / deltaValue;
|
|
59641
|
-
return [colorDiffUnitR, colorDiffUnitG, colorDiffUnitB];
|
|
59642
|
-
}
|
|
59643
|
-
colorCell(value, minValue, minColor, colorDiffUnit) {
|
|
59644
|
-
const [colorDiffUnitR, colorDiffUnitG, colorDiffUnitB] = colorDiffUnit;
|
|
59645
|
-
const r = Math.round(((minColor >> 16) % 256) - colorDiffUnitR * (value - minValue));
|
|
59646
|
-
const g = Math.round(((minColor >> 8) % 256) - colorDiffUnitG * (value - minValue));
|
|
59647
|
-
const b = Math.round((minColor % 256) - colorDiffUnitB * (value - minValue));
|
|
59648
|
-
return (r << 16) | (g << 8) | b;
|
|
59649
|
-
}
|
|
59650
60569
|
/**
|
|
59651
60570
|
* Execute the predicate to know if a conditional formatting rule should be applied to a cell
|
|
59652
60571
|
*/
|
|
@@ -60272,7 +61191,7 @@ function withPivotPresentationLayer (PivotClass) {
|
|
|
60272
61191
|
}
|
|
60273
61192
|
getValuesToAggregate(measure, domain) {
|
|
60274
61193
|
const { rowDomain, colDomain } = domainToColRowDomain(this, domain);
|
|
60275
|
-
const table =
|
|
61194
|
+
const table = super.getTableStructure();
|
|
60276
61195
|
const values = [];
|
|
60277
61196
|
if (colDomain.length === 0 &&
|
|
60278
61197
|
rowDomain.length < this.definition.rows.length &&
|
|
@@ -60706,6 +61625,21 @@ function withPivotPresentationLayer (PivotClass) {
|
|
|
60706
61625
|
}
|
|
60707
61626
|
throw new Error(`Value ${result.value} is not a number`);
|
|
60708
61627
|
}
|
|
61628
|
+
getTableStructure() {
|
|
61629
|
+
const table = super.getTableStructure();
|
|
61630
|
+
this.sortTableStructure(table);
|
|
61631
|
+
return table;
|
|
61632
|
+
}
|
|
61633
|
+
sortTableStructure(table) {
|
|
61634
|
+
if (!this.definition.sortedColumn || table.isSorted) {
|
|
61635
|
+
return;
|
|
61636
|
+
}
|
|
61637
|
+
const measure = this.definition.sortedColumn.measure;
|
|
61638
|
+
const isSortValid = isSortedColumnValid(this.definition.sortedColumn, this);
|
|
61639
|
+
if (isSortValid) {
|
|
61640
|
+
table.sort(measure, this.definition.sortedColumn, (measure, domain) => this._getPivotCellValueAndFormat(measure, domain));
|
|
61641
|
+
}
|
|
61642
|
+
}
|
|
60709
61643
|
}
|
|
60710
61644
|
return PivotPresentationLayer;
|
|
60711
61645
|
}
|
|
@@ -62481,23 +63415,6 @@ class Session extends EventBus {
|
|
|
62481
63415
|
}
|
|
62482
63416
|
}
|
|
62483
63417
|
|
|
62484
|
-
function randomChoice(arr) {
|
|
62485
|
-
return arr[Math.floor(Math.random() * arr.length)];
|
|
62486
|
-
}
|
|
62487
|
-
const colors = [
|
|
62488
|
-
"#ff851b",
|
|
62489
|
-
"#0074d9",
|
|
62490
|
-
"#7fdbff",
|
|
62491
|
-
"#b10dc9",
|
|
62492
|
-
"#39cccc",
|
|
62493
|
-
"#f012be",
|
|
62494
|
-
"#3d9970",
|
|
62495
|
-
"#111111",
|
|
62496
|
-
"#ff4136",
|
|
62497
|
-
"#aaaaaa",
|
|
62498
|
-
"#85144b",
|
|
62499
|
-
"#001f3f",
|
|
62500
|
-
];
|
|
62501
63418
|
class CollaborativePlugin extends UIPlugin {
|
|
62502
63419
|
static getters = [
|
|
62503
63420
|
"getClientsToDisplay",
|
|
@@ -62506,7 +63423,7 @@ class CollaborativePlugin extends UIPlugin {
|
|
|
62506
63423
|
"isFullySynchronized",
|
|
62507
63424
|
];
|
|
62508
63425
|
static layers = ["Selection"];
|
|
62509
|
-
availableColors = new
|
|
63426
|
+
availableColors = new AlternatingColorGenerator(12);
|
|
62510
63427
|
colors = {};
|
|
62511
63428
|
session;
|
|
62512
63429
|
constructor(config) {
|
|
@@ -62517,14 +63434,6 @@ class CollaborativePlugin extends UIPlugin {
|
|
|
62517
63434
|
return (position.row < this.getters.getNumberRows(position.sheetId) &&
|
|
62518
63435
|
position.col < this.getters.getNumberCols(position.sheetId));
|
|
62519
63436
|
}
|
|
62520
|
-
chooseNewColor() {
|
|
62521
|
-
if (this.availableColors.size === 0) {
|
|
62522
|
-
this.availableColors = new Set(colors);
|
|
62523
|
-
}
|
|
62524
|
-
const color = randomChoice([...this.availableColors.values()]);
|
|
62525
|
-
this.availableColors.delete(color);
|
|
62526
|
-
return color;
|
|
62527
|
-
}
|
|
62528
63437
|
getClient() {
|
|
62529
63438
|
return this.session.getClient();
|
|
62530
63439
|
}
|
|
@@ -62559,7 +63468,7 @@ class CollaborativePlugin extends UIPlugin {
|
|
|
62559
63468
|
this.isPositionValid(client.position)) {
|
|
62560
63469
|
const position = client.position;
|
|
62561
63470
|
if (!this.colors[client.id]) {
|
|
62562
|
-
this.colors[client.id] = this.
|
|
63471
|
+
this.colors[client.id] = this.availableColors.next();
|
|
62563
63472
|
}
|
|
62564
63473
|
const color = this.colors[client.id];
|
|
62565
63474
|
clients.push({ ...client, position, color });
|
|
@@ -62796,7 +63705,7 @@ class FormatPlugin extends UIPlugin {
|
|
|
62796
63705
|
for (let row = zone.top; row <= zone.bottom; row++) {
|
|
62797
63706
|
const position = { sheetId, col, row };
|
|
62798
63707
|
const pivotCell = this.getters.getPivotCellFromPosition(position);
|
|
62799
|
-
if (
|
|
63708
|
+
if (this.isSpilledPivotValueFormula(position, pivotCell)) {
|
|
62800
63709
|
measurePositions.push(position);
|
|
62801
63710
|
const pivotId = this.getters.getPivotIdFromPosition(position) || "";
|
|
62802
63711
|
measuresByPivotId[pivotId] ??= new Set();
|
|
@@ -62833,6 +63742,10 @@ class FormatPlugin extends UIPlugin {
|
|
|
62833
63742
|
format,
|
|
62834
63743
|
});
|
|
62835
63744
|
}
|
|
63745
|
+
isSpilledPivotValueFormula(position, pivotCell) {
|
|
63746
|
+
const cell = this.getters.getCell(position);
|
|
63747
|
+
return pivotCell.type === "VALUE" && !cell?.isFormula;
|
|
63748
|
+
}
|
|
62836
63749
|
/**
|
|
62837
63750
|
* This function allows to adjust the quantity of decimal places after a decimal
|
|
62838
63751
|
* point on cells containing number value. It does this by changing the cells
|
|
@@ -62883,6 +63796,69 @@ class FormatPlugin extends UIPlugin {
|
|
|
62883
63796
|
}
|
|
62884
63797
|
}
|
|
62885
63798
|
|
|
63799
|
+
class GeoFeaturePlugin extends UIPlugin {
|
|
63800
|
+
static getters = [
|
|
63801
|
+
"getGeoJsonFeatures",
|
|
63802
|
+
"geoFeatureNameToId",
|
|
63803
|
+
"getGeoChartAvailableRegions",
|
|
63804
|
+
];
|
|
63805
|
+
geoJsonService;
|
|
63806
|
+
geoJsonCache = {};
|
|
63807
|
+
constructor(config) {
|
|
63808
|
+
super(config);
|
|
63809
|
+
this.geoJsonService = config.external.geoJsonService;
|
|
63810
|
+
}
|
|
63811
|
+
getGeoChartAvailableRegions() {
|
|
63812
|
+
if (!this.geoJsonService) {
|
|
63813
|
+
console.error("No geoJsonService provided to the model");
|
|
63814
|
+
return [];
|
|
63815
|
+
}
|
|
63816
|
+
return this.geoJsonService.getAvailableRegions() || [];
|
|
63817
|
+
}
|
|
63818
|
+
getGeoJsonFeatures(region) {
|
|
63819
|
+
if (!this.geoJsonService) {
|
|
63820
|
+
console.error("No geoJsonService provided to the model");
|
|
63821
|
+
return;
|
|
63822
|
+
}
|
|
63823
|
+
const cachedGeoJson = this.geoJsonCache[region];
|
|
63824
|
+
if (cachedGeoJson instanceof Promise) {
|
|
63825
|
+
return undefined;
|
|
63826
|
+
}
|
|
63827
|
+
if (cachedGeoJson !== undefined) {
|
|
63828
|
+
return cachedGeoJson ?? undefined;
|
|
63829
|
+
}
|
|
63830
|
+
this.geoJsonCache[region] = new Promise(async (resolve) => {
|
|
63831
|
+
const json = await this.geoJsonService?.getTopoJson(region);
|
|
63832
|
+
this.geoJsonCache[region] = this.convertToGeoJson(json);
|
|
63833
|
+
this.dispatch("EVALUATE_CHARTS");
|
|
63834
|
+
resolve();
|
|
63835
|
+
});
|
|
63836
|
+
return undefined;
|
|
63837
|
+
}
|
|
63838
|
+
geoFeatureNameToId(region, featureName) {
|
|
63839
|
+
if (!this.geoJsonService) {
|
|
63840
|
+
console.error("No geoJsonService provided to the model");
|
|
63841
|
+
return;
|
|
63842
|
+
}
|
|
63843
|
+
return this.geoJsonService.geoFeatureNameToId(region, featureName);
|
|
63844
|
+
}
|
|
63845
|
+
convertToGeoJson(json) {
|
|
63846
|
+
if (!json) {
|
|
63847
|
+
return null;
|
|
63848
|
+
}
|
|
63849
|
+
// TopoJSON
|
|
63850
|
+
if (json.type === "Topology") {
|
|
63851
|
+
const features = window.ChartGeo.topojson.feature(json, Object.values(json.objects)[0]);
|
|
63852
|
+
return features.type === "FeatureCollection" ? features.features : [features];
|
|
63853
|
+
}
|
|
63854
|
+
// GeoJSON
|
|
63855
|
+
else if (json.type === "FeatureCollection") {
|
|
63856
|
+
return json.features;
|
|
63857
|
+
}
|
|
63858
|
+
throw new Error("Invalid TopoJSON");
|
|
63859
|
+
}
|
|
63860
|
+
}
|
|
63861
|
+
|
|
62886
63862
|
class HeaderVisibilityUIPlugin extends UIPlugin {
|
|
62887
63863
|
static getters = [
|
|
62888
63864
|
"getNextVisibleCellPosition",
|
|
@@ -64420,10 +65396,13 @@ class TableResizeUI extends UIPlugin {
|
|
|
64420
65396
|
case "RESIZE_TABLE": {
|
|
64421
65397
|
const table = this.getters.getCoreTableMatchingTopLeft(cmd.sheetId, cmd.zone);
|
|
64422
65398
|
this.dispatch("UPDATE_TABLE", { ...cmd });
|
|
64423
|
-
if (!table
|
|
65399
|
+
if (!table)
|
|
64424
65400
|
return;
|
|
64425
|
-
const oldTableZone = table.range.zone;
|
|
64426
65401
|
const newTableZone = this.getters.getRangeFromRangeData(cmd.newTableRange).zone;
|
|
65402
|
+
this.selection.selectCell(newTableZone.right, newTableZone.bottom);
|
|
65403
|
+
if (!table.config.automaticAutofill)
|
|
65404
|
+
return;
|
|
65405
|
+
const oldTableZone = table.range.zone;
|
|
64427
65406
|
if (newTableZone.bottom >= oldTableZone.bottom) {
|
|
64428
65407
|
for (let col = newTableZone.left; col <= newTableZone.right; col++) {
|
|
64429
65408
|
const autofillSource = { col, row: oldTableZone.bottom, sheetId: cmd.sheetId };
|
|
@@ -65629,15 +66608,23 @@ class GridSelectionPlugin extends UIPlugin {
|
|
|
65629
66608
|
handler.paste({ zones: pasteTarget, sheetId }, data, { isCutOperation: true });
|
|
65630
66609
|
const toRemove = isBasedBefore ? cmd.elements.map((el) => el + thickness) : cmd.elements;
|
|
65631
66610
|
let currentIndex = cmd.base;
|
|
66611
|
+
const resizingGroups = {};
|
|
65632
66612
|
for (const element of toRemove) {
|
|
65633
66613
|
const size = this.getters.getHeaderSize(cmd.sheetId, cmd.dimension, element);
|
|
66614
|
+
const currentSize = this.getters.getHeaderSize(cmd.sheetId, cmd.dimension, currentIndex);
|
|
66615
|
+
if (size != currentSize) {
|
|
66616
|
+
resizingGroups[size] ??= [];
|
|
66617
|
+
resizingGroups[size].push(currentIndex);
|
|
66618
|
+
currentIndex += 1;
|
|
66619
|
+
}
|
|
66620
|
+
}
|
|
66621
|
+
for (const size in resizingGroups) {
|
|
65634
66622
|
this.dispatch("RESIZE_COLUMNS_ROWS", {
|
|
65635
66623
|
dimension: cmd.dimension,
|
|
65636
66624
|
sheetId: cmd.sheetId,
|
|
65637
|
-
size,
|
|
65638
|
-
elements: [
|
|
66625
|
+
size: parseInt(size, 10),
|
|
66626
|
+
elements: resizingGroups[size],
|
|
65639
66627
|
});
|
|
65640
|
-
currentIndex += 1;
|
|
65641
66628
|
}
|
|
65642
66629
|
this.dispatch("REMOVE_COLUMNS_ROWS", {
|
|
65643
66630
|
dimension: cmd.dimension,
|
|
@@ -66219,6 +67206,7 @@ class SheetViewPlugin extends UIPlugin {
|
|
|
66219
67206
|
break;
|
|
66220
67207
|
case "DELETE_SHEET":
|
|
66221
67208
|
this.cleanViewports();
|
|
67209
|
+
this.sheetsWithDirtyViewports.delete(cmd.sheetId);
|
|
66222
67210
|
break;
|
|
66223
67211
|
case "ACTIVATE_SHEET":
|
|
66224
67212
|
this.sheetsWithDirtyViewports.add(cmd.sheetIdTo);
|
|
@@ -66869,7 +67857,8 @@ const featurePluginRegistry = new Registry()
|
|
|
66869
67857
|
.add("data_cleanup", DataCleanupPlugin)
|
|
66870
67858
|
.add("table_autofill", TableAutofillPlugin)
|
|
66871
67859
|
.add("table_ui_resize", TableResizeUI)
|
|
66872
|
-
.add("datavalidation_insert", DataValidationInsertionPlugin)
|
|
67860
|
+
.add("datavalidation_insert", DataValidationInsertionPlugin)
|
|
67861
|
+
.add("geo_features", GeoFeaturePlugin);
|
|
66873
67862
|
// Plugins which have a state, but which should not be shared in collaborative
|
|
66874
67863
|
const statefulUIPluginRegistry = new Registry()
|
|
66875
67864
|
.add("selection", GridSelectionPlugin)
|
|
@@ -68613,8 +69602,8 @@ css /* scss */ `
|
|
|
68613
69602
|
.o-topbar-composer {
|
|
68614
69603
|
height: fit-content;
|
|
68615
69604
|
margin-top: -1px;
|
|
69605
|
+
margin-bottom: -1px;
|
|
68616
69606
|
border: 1px solid;
|
|
68617
|
-
z-index: ${ComponentsImportance.TopBarComposer};
|
|
68618
69607
|
font-family: ${DEFAULT_FONT};
|
|
68619
69608
|
|
|
68620
69609
|
.o-composer:empty:not(:focus):not(.active)::before {
|
|
@@ -68672,6 +69661,7 @@ class TopBarComposer extends Component {
|
|
|
68672
69661
|
}
|
|
68673
69662
|
return cssPropertiesToCss({
|
|
68674
69663
|
"border-color": SELECTION_BORDER_COLOR,
|
|
69664
|
+
"z-index": String(ComponentsImportance.TopBarComposer),
|
|
68675
69665
|
});
|
|
68676
69666
|
}
|
|
68677
69667
|
onFocus(selection) {
|
|
@@ -68780,6 +69770,15 @@ css /* scss */ `
|
|
|
68780
69770
|
}
|
|
68781
69771
|
}
|
|
68782
69772
|
|
|
69773
|
+
.irregularity-map {
|
|
69774
|
+
border-top: 1px solid ${SEPARATOR_COLOR};
|
|
69775
|
+
height: ${TOPBAR_TOOLBAR_HEIGHT}px;
|
|
69776
|
+
|
|
69777
|
+
.alert-info {
|
|
69778
|
+
border-left: 3px solid ${ALERT_INFO_BORDER};
|
|
69779
|
+
}
|
|
69780
|
+
}
|
|
69781
|
+
|
|
68783
69782
|
.o-topbar-composer {
|
|
68784
69783
|
flex-grow: 1;
|
|
68785
69784
|
}
|
|
@@ -68873,8 +69872,10 @@ class TopBar extends Component {
|
|
|
68873
69872
|
formatNumberMenuItemSpec = formatNumberMenuItemSpec;
|
|
68874
69873
|
isntToolbarMenu = false;
|
|
68875
69874
|
composerFocusStore;
|
|
69875
|
+
fingerprints;
|
|
68876
69876
|
setup() {
|
|
68877
69877
|
this.composerFocusStore = useStore(ComposerFocusStore);
|
|
69878
|
+
this.fingerprints = useStore(FormulaFingerprintStore);
|
|
68878
69879
|
useExternalListener(window, "click", this.onExternalClick);
|
|
68879
69880
|
onWillStart(() => this.updateCellState());
|
|
68880
69881
|
onWillUpdateProps(() => this.updateCellState());
|
|
@@ -69345,7 +70346,7 @@ class Spreadsheet extends Component {
|
|
|
69345
70346
|
properties["grid-template-rows"] = `auto`;
|
|
69346
70347
|
}
|
|
69347
70348
|
else {
|
|
69348
|
-
properties["grid-template-rows"] =
|
|
70349
|
+
properties["grid-template-rows"] = `max-content auto ${BOTTOMBAR_HEIGHT + 1}px`;
|
|
69349
70350
|
}
|
|
69350
70351
|
properties["grid-template-columns"] = `auto ${this.sidePanel.panelSize}px`;
|
|
69351
70352
|
return cssPropertiesToCss(properties);
|
|
@@ -70423,9 +71424,6 @@ class SelectionStreamProcessorImpl {
|
|
|
70423
71424
|
getBackToDefault() {
|
|
70424
71425
|
this.stream.getBackToDefault();
|
|
70425
71426
|
}
|
|
70426
|
-
getAnchor() {
|
|
70427
|
-
return this.anchor;
|
|
70428
|
-
}
|
|
70429
71427
|
modifyAnchor(anchor, mode, options) {
|
|
70430
71428
|
const sheetId = this.getters.getActiveSheetId();
|
|
70431
71429
|
anchor = {
|
|
@@ -73357,6 +74355,7 @@ class Model extends EventBus {
|
|
|
73357
74355
|
session: this.session,
|
|
73358
74356
|
defaultCurrency: this.config.defaultCurrency,
|
|
73359
74357
|
customColors: this.config.customColors || [],
|
|
74358
|
+
external: this.config.external,
|
|
73360
74359
|
};
|
|
73361
74360
|
}
|
|
73362
74361
|
// ---------------------------------------------------------------------------
|
|
@@ -73588,7 +74587,6 @@ const SPREADSHEET_DIMENSIONS = {
|
|
|
73588
74587
|
MIN_COL_WIDTH,
|
|
73589
74588
|
HEADER_HEIGHT,
|
|
73590
74589
|
HEADER_WIDTH,
|
|
73591
|
-
TOPBAR_HEIGHT,
|
|
73592
74590
|
BOTTOMBAR_HEIGHT,
|
|
73593
74591
|
DEFAULT_CELL_WIDTH,
|
|
73594
74592
|
DEFAULT_CELL_HEIGHT,
|
|
@@ -73785,6 +74783,6 @@ const chartHelpers = { ...CHART_HELPERS, ...CHART_RUNTIME_HELPERS };
|
|
|
73785
74783
|
export { AbstractCellClipboardHandler, AbstractChart, AbstractFigureClipboardHandler, CellErrorType, CommandResult, CorePlugin, DispatchResult, EvaluationError, Model, PivotRuntimeDefinition, Registry, Revision, SPREADSHEET_DIMENSIONS, Spreadsheet, SpreadsheetPivotTable, UIPlugin, __info__, addFunction, addRenderingLayer, astToFormula, chartHelpers, 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 };
|
|
73786
74784
|
|
|
73787
74785
|
|
|
73788
|
-
__info__.version = "18.1.0-alpha.
|
|
73789
|
-
__info__.date = "2024-12-
|
|
73790
|
-
__info__.hash = "
|
|
74786
|
+
__info__.version = "18.1.0-alpha.8";
|
|
74787
|
+
__info__.date = "2024-12-19T07:49:54.421Z";
|
|
74788
|
+
__info__.hash = "87a1567";
|