@odoo/o-spreadsheet 18.1.0-alpha.7 → 18.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/o-spreadsheet.cjs.js +1317 -315
- package/dist/o-spreadsheet.d.ts +398 -292
- package/dist/o-spreadsheet.esm.js +1317 -315
- package/dist/o-spreadsheet.iife.js +1317 -315
- package/dist/o-spreadsheet.iife.min.js +515 -515
- package/dist/o_spreadsheet.xml +217 -23
- package/package.json +4 -3
|
@@ -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
|
|
6
|
-
* @date 2024-12-
|
|
7
|
-
* @hash
|
|
5
|
+
* @version 18.1.0
|
|
6
|
+
* @date 2024-12-26T06:37:07.879Z
|
|
7
|
+
* @hash c520e89
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
'use strict';
|
|
@@ -267,7 +267,6 @@ const MIN_ROW_HEIGHT = 10;
|
|
|
267
267
|
const MIN_COL_WIDTH = 5;
|
|
268
268
|
const HEADER_HEIGHT = 26;
|
|
269
269
|
const HEADER_WIDTH = 48;
|
|
270
|
-
const TOPBAR_HEIGHT = 63;
|
|
271
270
|
const TOPBAR_TOOLBAR_HEIGHT = 34;
|
|
272
271
|
const BOTTOMBAR_HEIGHT = 36;
|
|
273
272
|
const DEFAULT_CELL_WIDTH = 96;
|
|
@@ -791,6 +790,7 @@ function removeFalsyAttributes(obj) {
|
|
|
791
790
|
* https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions/Character_Classes
|
|
792
791
|
*/
|
|
793
792
|
const whiteSpaceSpecialCharacters = [
|
|
793
|
+
" ",
|
|
794
794
|
"\t",
|
|
795
795
|
"\f",
|
|
796
796
|
"\v",
|
|
@@ -805,17 +805,15 @@ const whiteSpaceSpecialCharacters = [
|
|
|
805
805
|
String.fromCharCode(parseInt("3000", 16)),
|
|
806
806
|
String.fromCharCode(parseInt("feff", 16)),
|
|
807
807
|
];
|
|
808
|
-
const whiteSpaceRegexp = new RegExp(whiteSpaceSpecialCharacters.join("|")
|
|
808
|
+
const whiteSpaceRegexp = new RegExp(whiteSpaceSpecialCharacters.join("|"), "g");
|
|
809
|
+
const newLineRegexp = /(\r\n|\r)/g;
|
|
809
810
|
/**
|
|
810
|
-
* Replace all
|
|
811
|
-
* different newlines types by \n.
|
|
811
|
+
* Replace all different newlines characters by \n
|
|
812
812
|
*/
|
|
813
|
-
function
|
|
813
|
+
function replaceNewLines(text) {
|
|
814
814
|
if (!text)
|
|
815
815
|
return "";
|
|
816
|
-
|
|
817
|
-
return text;
|
|
818
|
-
return text.replace(whiteSpaceRegexp, (match, newLine) => (newLine ? NEWLINE : " "));
|
|
816
|
+
return text.replace(newLineRegexp, NEWLINE);
|
|
819
817
|
}
|
|
820
818
|
/**
|
|
821
819
|
* Determine if the numbers are consecutive.
|
|
@@ -981,7 +979,7 @@ function transpose2dPOJO(pojo) {
|
|
|
981
979
|
|
|
982
980
|
const RBA_REGEX = /rgba?\(|\s+|\)/gi;
|
|
983
981
|
const HEX_MATCH = /^#([A-F\d]{2}){3,4}$/;
|
|
984
|
-
const colors
|
|
982
|
+
const colors = [
|
|
985
983
|
"#eb6d00",
|
|
986
984
|
"#0074d9",
|
|
987
985
|
"#ad8e00",
|
|
@@ -1004,6 +1002,12 @@ const colors$1 = [
|
|
|
1004
1002
|
function colorNumberString(color) {
|
|
1005
1003
|
return toHex(color.toString(16).padStart(6, "0"));
|
|
1006
1004
|
}
|
|
1005
|
+
function colorToNumber(color) {
|
|
1006
|
+
if (typeof color === "number") {
|
|
1007
|
+
return color;
|
|
1008
|
+
}
|
|
1009
|
+
return Number.parseInt(toHex(color).slice(1), 16);
|
|
1010
|
+
}
|
|
1007
1011
|
/**
|
|
1008
1012
|
* Converts any CSS color value to a standardized hex6 value.
|
|
1009
1013
|
* Accepts: hex3, hex6, hex8, rgb[1] and rgba[1].
|
|
@@ -1354,9 +1358,9 @@ const COLORS_XL = [
|
|
|
1354
1358
|
"#056BD9", // Blue #3
|
|
1355
1359
|
"#155193", // Blue #4
|
|
1356
1360
|
"#A76DBC", // Violet #1
|
|
1357
|
-
"#7F4295", // Violet #
|
|
1358
|
-
"#6D2387", // Violet #
|
|
1359
|
-
"#4F1565", // Violet #
|
|
1361
|
+
"#7F4295", // Violet #2
|
|
1362
|
+
"#6D2387", // Violet #3
|
|
1363
|
+
"#4F1565", // Violet #4
|
|
1360
1364
|
"#EA6175", // Red #1
|
|
1361
1365
|
"#CE4257", // Red #2
|
|
1362
1366
|
"#982738", // Red #3
|
|
@@ -1382,6 +1386,81 @@ const COLORS_XL = [
|
|
|
1382
1386
|
"#C08A16", // Yellow #3
|
|
1383
1387
|
"#936A12", // Yellow #4
|
|
1384
1388
|
];
|
|
1389
|
+
// Same as above but with alternating colors
|
|
1390
|
+
const ALTERNATING_COLORS_MD = [
|
|
1391
|
+
"#4EA7F2", // Blue #1
|
|
1392
|
+
"#43C5B1", // Teal #1
|
|
1393
|
+
"#EA6175", // Red #1
|
|
1394
|
+
"#F4A261", // Orange #1
|
|
1395
|
+
"#8481DD", // Purple #1
|
|
1396
|
+
"#FFD86D", // Yellow #1
|
|
1397
|
+
"#3188E6", // Blue #2
|
|
1398
|
+
"#00A78D", // Teal #2
|
|
1399
|
+
"#CE4257", // Red #2
|
|
1400
|
+
"#F48935", // Orange #2
|
|
1401
|
+
"#5752D1", // Purple #2
|
|
1402
|
+
"#FFBC2C", // Yellow #2
|
|
1403
|
+
];
|
|
1404
|
+
const ALTERNATING_COLORS_LG = [
|
|
1405
|
+
"#4EA7F2", // Blue #1
|
|
1406
|
+
"#A76DBC", // Violet #1
|
|
1407
|
+
"#EA6175", // Red #1
|
|
1408
|
+
"#43C5B1", // Teal #1
|
|
1409
|
+
"#F4A261", // Orange #1
|
|
1410
|
+
"#8481DD", // Purple #1
|
|
1411
|
+
"#A4A8B6", // Gray #1
|
|
1412
|
+
"#FFD86D", // Yellow #1
|
|
1413
|
+
"#3188E6", // Blue #2
|
|
1414
|
+
"#7F4295", // Violet #2
|
|
1415
|
+
"#CE4257", // Red #2
|
|
1416
|
+
"#00A78D", // Teal #2
|
|
1417
|
+
"#F48935", // Orange #2
|
|
1418
|
+
"#5752D1", // Purple #2
|
|
1419
|
+
"#7E8290", // Gray #2
|
|
1420
|
+
"#FFBC2C", // Yellow #2
|
|
1421
|
+
"#056BD9", // Blue #3
|
|
1422
|
+
"#6D2387", // Violet #3
|
|
1423
|
+
"#982738", // Red #3
|
|
1424
|
+
"#0E8270", // Teal #3
|
|
1425
|
+
"#BE5D10", // Orange #3
|
|
1426
|
+
"#3A3580", // Purple #3
|
|
1427
|
+
"#545B70", // Gray #3
|
|
1428
|
+
"#C08A16", // Yellow #3
|
|
1429
|
+
];
|
|
1430
|
+
const ALTERNATING_COLORS_XL = [
|
|
1431
|
+
"#4EA7F2", // Blue #1
|
|
1432
|
+
"#A76DBC", // Violet #1
|
|
1433
|
+
"#EA6175", // Red #1
|
|
1434
|
+
"#43C5B1", // Teal #1
|
|
1435
|
+
"#F4A261", // Orange #1
|
|
1436
|
+
"#8481DD", // Purple #1
|
|
1437
|
+
"#A4A8B6", // Grey #1
|
|
1438
|
+
"#FFD86D", // Yellow #1
|
|
1439
|
+
"#3188E6", // Blue #2
|
|
1440
|
+
"#7F4295", // Violet #2
|
|
1441
|
+
"#CE4257", // Red #2
|
|
1442
|
+
"#00A78D", // Teal #2
|
|
1443
|
+
"#F48935", // Orange #2
|
|
1444
|
+
"#5752D1", // Purple #2
|
|
1445
|
+
"#7E8290", // Grey #2
|
|
1446
|
+
"#FFBC2C", // Yellow #2
|
|
1447
|
+
"#056BD9", // Blue #3
|
|
1448
|
+
"#6D2387", // Violet #3
|
|
1449
|
+
"#982738", // Red #3
|
|
1450
|
+
"#0E8270", // Teal #3
|
|
1451
|
+
"#BE5D10", // Orange #3
|
|
1452
|
+
"#3A3580", // Purple #3
|
|
1453
|
+
"#545B70", // Grey #3
|
|
1454
|
+
"#C08A16", // Yellow #3
|
|
1455
|
+
"#155193", // Blue #4
|
|
1456
|
+
"#4F1565", // Violet #4
|
|
1457
|
+
"#791B29", // Red #4
|
|
1458
|
+
"#105F53", // Teal #4
|
|
1459
|
+
"#7D380D", // Orange #4
|
|
1460
|
+
"#26235F", // Purple #4
|
|
1461
|
+
"#3F4250", // Grey #4
|
|
1462
|
+
"#936A12", // Yellow #4
|
|
1463
|
+
];
|
|
1385
1464
|
function getNthColor(index, palette) {
|
|
1386
1465
|
return palette[index % palette.length];
|
|
1387
1466
|
}
|
|
@@ -1399,6 +1478,20 @@ function getColorsPalette(quantity) {
|
|
|
1399
1478
|
return COLORS_XL;
|
|
1400
1479
|
}
|
|
1401
1480
|
}
|
|
1481
|
+
function getAlternatingColorsPalette(quantity) {
|
|
1482
|
+
if (quantity <= 6) {
|
|
1483
|
+
return COLORS_SM;
|
|
1484
|
+
}
|
|
1485
|
+
else if (quantity <= 12) {
|
|
1486
|
+
return ALTERNATING_COLORS_MD;
|
|
1487
|
+
}
|
|
1488
|
+
else if (quantity <= 24) {
|
|
1489
|
+
return ALTERNATING_COLORS_LG;
|
|
1490
|
+
}
|
|
1491
|
+
else {
|
|
1492
|
+
return ALTERNATING_COLORS_XL;
|
|
1493
|
+
}
|
|
1494
|
+
}
|
|
1402
1495
|
class ColorGenerator {
|
|
1403
1496
|
preferredColors;
|
|
1404
1497
|
currentColorIndex = 0;
|
|
@@ -1413,6 +1506,62 @@ class ColorGenerator {
|
|
|
1413
1506
|
: getNthColor(this.currentColorIndex++, this.palette);
|
|
1414
1507
|
}
|
|
1415
1508
|
}
|
|
1509
|
+
class AlternatingColorGenerator extends ColorGenerator {
|
|
1510
|
+
constructor(paletteSize, preferredColors = []) {
|
|
1511
|
+
super(paletteSize, preferredColors);
|
|
1512
|
+
this.palette = getAlternatingColorsPalette(paletteSize).filter((c) => !preferredColors.includes(c));
|
|
1513
|
+
}
|
|
1514
|
+
}
|
|
1515
|
+
/**
|
|
1516
|
+
* Returns a function that maps a value to a color using a color scale defined by the given
|
|
1517
|
+
* color/threshold values pairs.
|
|
1518
|
+
*/
|
|
1519
|
+
function getColorScale(colorScalePoints) {
|
|
1520
|
+
if (colorScalePoints.length < 2) {
|
|
1521
|
+
throw new Error("Color scale must have at least 2 points");
|
|
1522
|
+
}
|
|
1523
|
+
const sortedColorScalePoints = [...colorScalePoints.sort((a, b) => a.value - b.value)];
|
|
1524
|
+
const thresholds = [];
|
|
1525
|
+
for (let i = 1; i < sortedColorScalePoints.length; i++) {
|
|
1526
|
+
const minColor = colorToNumber(sortedColorScalePoints[i - 1].color);
|
|
1527
|
+
const maxColor = colorToNumber(sortedColorScalePoints[i].color);
|
|
1528
|
+
thresholds.push({
|
|
1529
|
+
min: sortedColorScalePoints[i - 1].value,
|
|
1530
|
+
max: sortedColorScalePoints[i].value,
|
|
1531
|
+
minColor,
|
|
1532
|
+
maxColor,
|
|
1533
|
+
colorDiff: computeColorDiffUnits(sortedColorScalePoints[i - 1].value, sortedColorScalePoints[i].value, minColor, maxColor),
|
|
1534
|
+
});
|
|
1535
|
+
}
|
|
1536
|
+
return (value) => {
|
|
1537
|
+
if (value < thresholds[0].min) {
|
|
1538
|
+
return colorNumberString(thresholds[0].minColor);
|
|
1539
|
+
}
|
|
1540
|
+
for (const threshold of thresholds) {
|
|
1541
|
+
if (value >= threshold.min && value <= threshold.max) {
|
|
1542
|
+
return colorNumberString(colorCell(value, threshold.min, threshold.minColor, threshold.colorDiff));
|
|
1543
|
+
}
|
|
1544
|
+
}
|
|
1545
|
+
return colorNumberString(thresholds[thresholds.length - 1].maxColor);
|
|
1546
|
+
};
|
|
1547
|
+
}
|
|
1548
|
+
function computeColorDiffUnits(minValue, maxValue, minColor, maxColor) {
|
|
1549
|
+
const deltaValue = maxValue - minValue;
|
|
1550
|
+
const deltaColorR = ((minColor >> 16) % 256) - ((maxColor >> 16) % 256);
|
|
1551
|
+
const deltaColorG = ((minColor >> 8) % 256) - ((maxColor >> 8) % 256);
|
|
1552
|
+
const deltaColorB = (minColor % 256) - (maxColor % 256);
|
|
1553
|
+
const colorDiffUnitR = deltaColorR / deltaValue;
|
|
1554
|
+
const colorDiffUnitG = deltaColorG / deltaValue;
|
|
1555
|
+
const colorDiffUnitB = deltaColorB / deltaValue;
|
|
1556
|
+
return [colorDiffUnitR, colorDiffUnitG, colorDiffUnitB];
|
|
1557
|
+
}
|
|
1558
|
+
function colorCell(value, minValue, minColor, colorDiffUnit) {
|
|
1559
|
+
const [colorDiffUnitR, colorDiffUnitG, colorDiffUnitB] = colorDiffUnit;
|
|
1560
|
+
const r = Math.round(((minColor >> 16) % 256) - colorDiffUnitR * (value - minValue));
|
|
1561
|
+
const g = Math.round(((minColor >> 8) % 256) - colorDiffUnitG * (value - minValue));
|
|
1562
|
+
const b = Math.round((minColor % 256) - colorDiffUnitB * (value - minValue));
|
|
1563
|
+
return (r << 16) | (g << 8) | b;
|
|
1564
|
+
}
|
|
1416
1565
|
|
|
1417
1566
|
//------------------------------------------------------------------------------
|
|
1418
1567
|
// Coordinate
|
|
@@ -3284,6 +3433,7 @@ const invalidateEvaluationCommands = new Set([
|
|
|
3284
3433
|
]);
|
|
3285
3434
|
const invalidateChartEvaluationCommands = new Set([
|
|
3286
3435
|
"EVALUATE_CELLS",
|
|
3436
|
+
"EVALUATE_CHARTS",
|
|
3287
3437
|
"UPDATE_CELL",
|
|
3288
3438
|
"UNHIDE_COLUMNS_ROWS",
|
|
3289
3439
|
"HIDE_COLUMNS_ROWS",
|
|
@@ -3320,6 +3470,7 @@ const readonlyAllowedCommands = new Set([
|
|
|
3320
3470
|
"RESIZE_SHEETVIEW",
|
|
3321
3471
|
"SET_VIEWPORT_OFFSET",
|
|
3322
3472
|
"EVALUATE_CELLS",
|
|
3473
|
+
"EVALUATE_CHARTS",
|
|
3323
3474
|
"SET_FORMULA_VISIBILITY",
|
|
3324
3475
|
"UPDATE_FILTER",
|
|
3325
3476
|
]);
|
|
@@ -6524,7 +6675,7 @@ class BorderClipboardHandler extends AbstractCellClipboardHandler {
|
|
|
6524
6675
|
const POSTFIX_UNARY_OPERATORS = ["%"];
|
|
6525
6676
|
const OPERATORS = "+,-,*,/,:,=,<>,>=,>,<=,<,^,&".split(",").concat(POSTFIX_UNARY_OPERATORS);
|
|
6526
6677
|
function tokenize(str, locale = DEFAULT_LOCALE) {
|
|
6527
|
-
str =
|
|
6678
|
+
str = replaceNewLines(str);
|
|
6528
6679
|
const chars = new TokenizingChars(str);
|
|
6529
6680
|
const result = [];
|
|
6530
6681
|
while (!chars.isOver()) {
|
|
@@ -6671,12 +6822,12 @@ function tokenizeSpace(chars) {
|
|
|
6671
6822
|
if (length) {
|
|
6672
6823
|
return { type: "SPACE", value: NEWLINE.repeat(length) };
|
|
6673
6824
|
}
|
|
6674
|
-
|
|
6675
|
-
|
|
6676
|
-
chars.shift();
|
|
6825
|
+
let spaces = "";
|
|
6826
|
+
while (chars.current && chars.current.match(whiteSpaceRegexp)) {
|
|
6827
|
+
spaces += chars.shift();
|
|
6677
6828
|
}
|
|
6678
|
-
if (
|
|
6679
|
-
return { type: "SPACE", value:
|
|
6829
|
+
if (spaces) {
|
|
6830
|
+
return { type: "SPACE", value: spaces };
|
|
6680
6831
|
}
|
|
6681
6832
|
return null;
|
|
6682
6833
|
}
|
|
@@ -7535,6 +7686,16 @@ function getRunningTotalDomainKey(domain, fieldNameWithGranularity) {
|
|
|
7535
7686
|
}
|
|
7536
7687
|
return domainToString([...domain.slice(0, index), ...domain.slice(index + 1)]);
|
|
7537
7688
|
}
|
|
7689
|
+
function sortPivotTree(tree, baseDomain, sortFn) {
|
|
7690
|
+
const sortedTree = [...tree];
|
|
7691
|
+
const domain = [...baseDomain];
|
|
7692
|
+
sortedTree.sort((node1, node2) => sortFn([...domain, node1], [...domain, node2]));
|
|
7693
|
+
for (const node of tree) {
|
|
7694
|
+
const children = sortPivotTree(node.children, [...domain, node], sortFn);
|
|
7695
|
+
node.children = children;
|
|
7696
|
+
}
|
|
7697
|
+
return sortedTree;
|
|
7698
|
+
}
|
|
7538
7699
|
|
|
7539
7700
|
const pivotTimeAdapterRegistry = new Registry();
|
|
7540
7701
|
function pivotTimeAdapter(granularity) {
|
|
@@ -8045,6 +8206,29 @@ function addIndentAndAlignToPivotHeader(pivot, domain, functionResult) {
|
|
|
8045
8206
|
format: `${" ".repeat(indent)}${format}* `,
|
|
8046
8207
|
};
|
|
8047
8208
|
}
|
|
8209
|
+
function isSortedColumnValid(sortedColumn, pivot) {
|
|
8210
|
+
try {
|
|
8211
|
+
if (!pivot.getMeasure(sortedColumn.measure)) {
|
|
8212
|
+
return false;
|
|
8213
|
+
}
|
|
8214
|
+
const columns = pivot.definition.columns;
|
|
8215
|
+
for (let i = 0; i < sortedColumn.domain.length; i++) {
|
|
8216
|
+
if (columns[i].nameWithGranularity !== sortedColumn.domain[i].field) {
|
|
8217
|
+
return false;
|
|
8218
|
+
}
|
|
8219
|
+
const possibleValues = pivot
|
|
8220
|
+
.getPossibleFieldValues(columns[i])
|
|
8221
|
+
.map((v) => v.value);
|
|
8222
|
+
if (!possibleValues.includes(sortedColumn.domain[i].value)) {
|
|
8223
|
+
return false;
|
|
8224
|
+
}
|
|
8225
|
+
}
|
|
8226
|
+
return true;
|
|
8227
|
+
}
|
|
8228
|
+
catch (e) {
|
|
8229
|
+
return false;
|
|
8230
|
+
}
|
|
8231
|
+
}
|
|
8048
8232
|
|
|
8049
8233
|
class CellClipboardHandler extends AbstractCellClipboardHandler {
|
|
8050
8234
|
isCutAllowed(data) {
|
|
@@ -14653,7 +14837,7 @@ const SORT_TYPES = [
|
|
|
14653
14837
|
CellValueType.boolean,
|
|
14654
14838
|
];
|
|
14655
14839
|
function cellsSortingCriterion(sortingOrder) {
|
|
14656
|
-
const inverse = sortingOrder === "
|
|
14840
|
+
const inverse = sortingOrder === "asc" ? 1 : -1;
|
|
14657
14841
|
return (left, right) => {
|
|
14658
14842
|
if (left.type === CellValueType.empty) {
|
|
14659
14843
|
return right.type === CellValueType.empty ? 0 : 1;
|
|
@@ -14745,7 +14929,7 @@ function sortMatrix(matrix, locale, ...criteria) {
|
|
|
14745
14929
|
const sortColumns = [];
|
|
14746
14930
|
const nRows = matrix.length;
|
|
14747
14931
|
for (let i = 0; i < criteria.length; i += 2) {
|
|
14748
|
-
sortingOrders.push(toBoolean(toScalar(criteria[i + 1])?.value) ? "
|
|
14932
|
+
sortingOrders.push(toBoolean(toScalar(criteria[i + 1])?.value) ? "asc" : "desc");
|
|
14749
14933
|
const sortColumn = criteria[i];
|
|
14750
14934
|
if (isMatrix(sortColumn) && (sortColumn.length > 1 || sortColumn[0].length > 1)) {
|
|
14751
14935
|
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));
|
|
@@ -14762,12 +14946,12 @@ function sortMatrix(matrix, locale, ...criteria) {
|
|
|
14762
14946
|
if (sortColumns.length === 0) {
|
|
14763
14947
|
for (let i = 0; i < matrix[0].length; i++) {
|
|
14764
14948
|
sortColumns.push(matrix.map((row) => row[i].value));
|
|
14765
|
-
sortingOrders.push("
|
|
14949
|
+
sortingOrders.push("asc");
|
|
14766
14950
|
}
|
|
14767
14951
|
}
|
|
14768
14952
|
const sortingCriteria = {
|
|
14769
|
-
|
|
14770
|
-
|
|
14953
|
+
desc: cellsSortingCriterion("desc"),
|
|
14954
|
+
asc: cellsSortingCriterion("asc"),
|
|
14771
14955
|
};
|
|
14772
14956
|
const indexes = range(0, matrix.length);
|
|
14773
14957
|
indexes.sort((a, b) => {
|
|
@@ -20491,20 +20675,8 @@ class AbstractComposerStore extends SpreadsheetStore {
|
|
|
20491
20675
|
replaceSelectedRange(zone) {
|
|
20492
20676
|
const ref = this.getZoneReference(zone);
|
|
20493
20677
|
const currentToken = this.tokenAtCursor;
|
|
20494
|
-
|
|
20495
|
-
|
|
20496
|
-
replaceStart = currentToken.start;
|
|
20497
|
-
}
|
|
20498
|
-
else if (currentToken?.type === "RIGHT_PAREN") {
|
|
20499
|
-
// match left parenthesis
|
|
20500
|
-
const leftParenthesisIndex = this.currentTokens.findIndex((token) => token.type === "LEFT_PAREN" && token.parenthesesCode === currentToken.parenthesesCode);
|
|
20501
|
-
const functionToken = this.currentTokens[leftParenthesisIndex - 1];
|
|
20502
|
-
if (functionToken === undefined) {
|
|
20503
|
-
return;
|
|
20504
|
-
}
|
|
20505
|
-
replaceStart = functionToken.start;
|
|
20506
|
-
}
|
|
20507
|
-
this.replaceText(ref, replaceStart, this.selectionEnd);
|
|
20678
|
+
const start = currentToken?.type === "REFERENCE" ? currentToken.start : this.selectionStart;
|
|
20679
|
+
this.replaceText(ref, start, this.selectionEnd);
|
|
20508
20680
|
}
|
|
20509
20681
|
/**
|
|
20510
20682
|
* Replace the reference of the old zone by the new one.
|
|
@@ -20537,17 +20709,6 @@ class AbstractComposerStore extends SpreadsheetStore {
|
|
|
20537
20709
|
getZoneReference(zone) {
|
|
20538
20710
|
const inputSheetId = this.sheetId;
|
|
20539
20711
|
const sheetId = this.getters.getActiveSheetId();
|
|
20540
|
-
if (zone.top === zone.bottom && zone.left === zone.right) {
|
|
20541
|
-
const position = { sheetId, col: zone.left, row: zone.top };
|
|
20542
|
-
const pivotId = this.getters.getPivotIdFromPosition(position);
|
|
20543
|
-
const pivotCell = this.getters.getPivotCellFromPosition(position);
|
|
20544
|
-
const cell = this.getters.getCell(position);
|
|
20545
|
-
if (pivotId && pivotCell.type !== "EMPTY" && !cell?.isFormula) {
|
|
20546
|
-
const formulaPivotId = this.getters.getPivotFormulaId(pivotId);
|
|
20547
|
-
const formula = createPivotFormula(formulaPivotId, pivotCell);
|
|
20548
|
-
return localizeFormula(formula, this.getters.getLocale()).slice(1); // strip leading =
|
|
20549
|
-
}
|
|
20550
|
-
}
|
|
20551
20712
|
const range = this.getters.getRangeFromZone(sheetId, zone);
|
|
20552
20713
|
return this.getters.getSelectionRangeString(range, inputSheetId);
|
|
20553
20714
|
}
|
|
@@ -20718,37 +20879,21 @@ class AbstractComposerStore extends SpreadsheetStore {
|
|
|
20718
20879
|
const editionSheetId = this.sheetId;
|
|
20719
20880
|
const rangeColor = (rangeString) => {
|
|
20720
20881
|
const colorIndex = this.colorIndexByRange[rangeString];
|
|
20721
|
-
return colors
|
|
20882
|
+
return colors[colorIndex % colors.length];
|
|
20722
20883
|
};
|
|
20723
|
-
|
|
20724
|
-
for (const range of this.getReferencedRanges()) {
|
|
20884
|
+
return this.getReferencedRanges().map((range) => {
|
|
20725
20885
|
const rangeString = this.getters.getRangeString(range, editionSheetId);
|
|
20726
20886
|
const { numberOfRows, numberOfCols } = zoneToDimension(range.zone);
|
|
20727
20887
|
const zone = numberOfRows * numberOfCols === 1
|
|
20728
20888
|
? this.getters.expandZone(range.sheetId, range.zone)
|
|
20729
20889
|
: range.zone;
|
|
20730
|
-
|
|
20890
|
+
return {
|
|
20731
20891
|
zone,
|
|
20732
20892
|
color: rangeColor(rangeString),
|
|
20733
20893
|
sheetId: range.sheetId,
|
|
20734
20894
|
interactive: true,
|
|
20735
|
-
}
|
|
20736
|
-
}
|
|
20737
|
-
const activeSheetId = this.getters.getActiveSheetId();
|
|
20738
|
-
const selectionZone = this.model.selection.getAnchor().zone;
|
|
20739
|
-
const isSelectionHightlighted = highlights.find((highlight) => highlight.sheetId === activeSheetId && isEqual(highlight.zone, selectionZone));
|
|
20740
|
-
if (this.editionMode === "selecting" && !isSelectionHightlighted) {
|
|
20741
|
-
highlights.push({
|
|
20742
|
-
zone: selectionZone,
|
|
20743
|
-
color: "#445566",
|
|
20744
|
-
sheetId: activeSheetId,
|
|
20745
|
-
dashed: true,
|
|
20746
|
-
interactive: false,
|
|
20747
|
-
noFill: true,
|
|
20748
|
-
thinLine: true,
|
|
20749
|
-
});
|
|
20750
|
-
}
|
|
20751
|
-
return highlights;
|
|
20895
|
+
};
|
|
20896
|
+
});
|
|
20752
20897
|
}
|
|
20753
20898
|
/**
|
|
20754
20899
|
* Return ranges currently referenced in the composer
|
|
@@ -20976,6 +21121,7 @@ function compileTokens(tokens) {
|
|
|
20976
21121
|
return error;
|
|
20977
21122
|
},
|
|
20978
21123
|
isBadExpression: true,
|
|
21124
|
+
normalizedFormula: tokens.map((t) => t.value).join(""),
|
|
20979
21125
|
};
|
|
20980
21126
|
}
|
|
20981
21127
|
}
|
|
@@ -21103,6 +21249,7 @@ function compileTokensOrThrow(tokens) {
|
|
|
21103
21249
|
symbols,
|
|
21104
21250
|
tokens,
|
|
21105
21251
|
isBadExpression: false,
|
|
21252
|
+
normalizedFormula: cacheKey,
|
|
21106
21253
|
};
|
|
21107
21254
|
return compiledFormula;
|
|
21108
21255
|
}
|
|
@@ -21367,6 +21514,7 @@ autoCompleteProviders.add("pivot_ids", {
|
|
|
21367
21514
|
description: definition.name,
|
|
21368
21515
|
htmlContent: [{ value: str, color: tokenColors.NUMBER }],
|
|
21369
21516
|
fuzzySearchKey: str + definition.name,
|
|
21517
|
+
alwaysExpanded: true,
|
|
21370
21518
|
};
|
|
21371
21519
|
})
|
|
21372
21520
|
.filter(isDefined);
|
|
@@ -27760,6 +27908,19 @@ const ChartTerms = {
|
|
|
27760
27908
|
["GaugeLowerInflectionPointNaN" /* CommandResult.GaugeLowerInflectionPointNaN */]: _t("The lower inflection point value must be a number"),
|
|
27761
27909
|
["GaugeUpperInflectionPointNaN" /* CommandResult.GaugeUpperInflectionPointNaN */]: _t("The upper inflection point value must be a number"),
|
|
27762
27910
|
},
|
|
27911
|
+
GeoChart: {
|
|
27912
|
+
ColorScales: {
|
|
27913
|
+
blues: _t("Blues"),
|
|
27914
|
+
cividis: _t("Cividis"),
|
|
27915
|
+
greens: _t("Greens"),
|
|
27916
|
+
greys: _t("Greys"),
|
|
27917
|
+
oranges: _t("Oranges"),
|
|
27918
|
+
purples: _t("Purples"),
|
|
27919
|
+
rainbow: _t("Rainbow"),
|
|
27920
|
+
reds: _t("Reds"),
|
|
27921
|
+
viridis: _t("Viridis"),
|
|
27922
|
+
},
|
|
27923
|
+
},
|
|
27763
27924
|
};
|
|
27764
27925
|
const CustomCurrencyTerms = {
|
|
27765
27926
|
Custom: _t("Custom"),
|
|
@@ -28163,6 +28324,26 @@ function getRadarChartData(definition, dataSets, labelRange, getters) {
|
|
|
28163
28324
|
locale: getters.getLocale(),
|
|
28164
28325
|
};
|
|
28165
28326
|
}
|
|
28327
|
+
function getGeoChartData(definition, dataSets, labelRange, getters) {
|
|
28328
|
+
const labelValues = getChartLabelValues(getters, dataSets, labelRange);
|
|
28329
|
+
let labels = labelValues.formattedValues;
|
|
28330
|
+
if (definition.dataSetsHaveTitle) {
|
|
28331
|
+
labels.shift();
|
|
28332
|
+
}
|
|
28333
|
+
let dataSetsValues = getChartDatasetValues(getters, dataSets);
|
|
28334
|
+
({ labels, dataSetsValues } = aggregateDataForLabels(labels, dataSetsValues));
|
|
28335
|
+
const format = getChartDatasetFormat(getters, dataSets, "left") ||
|
|
28336
|
+
getChartDatasetFormat(getters, dataSets, "right");
|
|
28337
|
+
return {
|
|
28338
|
+
dataSetsValues,
|
|
28339
|
+
axisFormats: { y: format },
|
|
28340
|
+
labels,
|
|
28341
|
+
locale: getters.getLocale(),
|
|
28342
|
+
availableRegions: getters.getGeoChartAvailableRegions(),
|
|
28343
|
+
geoFeatureNameToId: getters.geoFeatureNameToId,
|
|
28344
|
+
getGeoJsonFeatures: getters.getGeoJsonFeatures,
|
|
28345
|
+
};
|
|
28346
|
+
}
|
|
28166
28347
|
function getTrendDatasetForBarChart(config, data) {
|
|
28167
28348
|
const filteredValues = [];
|
|
28168
28349
|
const filteredLabels = [];
|
|
@@ -28715,6 +28896,43 @@ function getRadarChartDatasets(definition, args) {
|
|
|
28715
28896
|
}
|
|
28716
28897
|
return datasets;
|
|
28717
28898
|
}
|
|
28899
|
+
function getGeoChartDatasets(definition, args) {
|
|
28900
|
+
const { availableRegions, dataSetsValues, labels } = args;
|
|
28901
|
+
const regionName = definition.region || availableRegions[0]?.id;
|
|
28902
|
+
const features = regionName ? args.getGeoJsonFeatures(regionName) : undefined;
|
|
28903
|
+
const dataset = {
|
|
28904
|
+
outline: features,
|
|
28905
|
+
showOutline: !!features,
|
|
28906
|
+
data: [],
|
|
28907
|
+
};
|
|
28908
|
+
if (features && regionName) {
|
|
28909
|
+
const labelsAndValues = {};
|
|
28910
|
+
if (dataSetsValues[0]) {
|
|
28911
|
+
for (let i = 0; i < dataSetsValues[0].data.length; i++) {
|
|
28912
|
+
if (!labels[i] || dataSetsValues[0].data[i] === undefined) {
|
|
28913
|
+
continue;
|
|
28914
|
+
}
|
|
28915
|
+
const featureId = args.geoFeatureNameToId(regionName, labels[i]);
|
|
28916
|
+
if (featureId) {
|
|
28917
|
+
labelsAndValues[featureId] = { value: dataSetsValues[0].data[i], label: labels[i] };
|
|
28918
|
+
}
|
|
28919
|
+
}
|
|
28920
|
+
}
|
|
28921
|
+
for (const feature of features) {
|
|
28922
|
+
if (!feature.id) {
|
|
28923
|
+
continue;
|
|
28924
|
+
}
|
|
28925
|
+
dataset.data.push({
|
|
28926
|
+
feature: {
|
|
28927
|
+
...feature,
|
|
28928
|
+
properties: { name: labelsAndValues[feature.id]?.label },
|
|
28929
|
+
},
|
|
28930
|
+
value: labelsAndValues[feature.id]?.value,
|
|
28931
|
+
});
|
|
28932
|
+
}
|
|
28933
|
+
}
|
|
28934
|
+
return [dataset];
|
|
28935
|
+
}
|
|
28718
28936
|
function getTrendingLineDataSet(dataset, config, data) {
|
|
28719
28937
|
const defaultBorderColor = colorToRGBA(dataset.backgroundColor);
|
|
28720
28938
|
defaultBorderColor.a = 1;
|
|
@@ -29092,6 +29310,44 @@ function getRadarChartScales(definition, args) {
|
|
|
29092
29310
|
},
|
|
29093
29311
|
};
|
|
29094
29312
|
}
|
|
29313
|
+
function getGeoChartScales(definition, args) {
|
|
29314
|
+
const { locale, axisFormats, availableRegions } = args;
|
|
29315
|
+
const geoLegendPosition = legendPositionToGeoLegendPosition(definition.legendPosition);
|
|
29316
|
+
const region = definition.region
|
|
29317
|
+
? availableRegions.find((r) => r.id === definition.region)
|
|
29318
|
+
: availableRegions[0];
|
|
29319
|
+
const format = axisFormats?.y || axisFormats?.y1;
|
|
29320
|
+
return {
|
|
29321
|
+
projection: {
|
|
29322
|
+
// projection: region?.defaultProjection,
|
|
29323
|
+
projection: getGeoChartProjection(region?.defaultProjection || "mercator"),
|
|
29324
|
+
axis: "x",
|
|
29325
|
+
},
|
|
29326
|
+
color: {
|
|
29327
|
+
axis: "x",
|
|
29328
|
+
display: definition.legendPosition !== "none",
|
|
29329
|
+
border: { color: GRAY_300 },
|
|
29330
|
+
grid: { color: GRAY_300 },
|
|
29331
|
+
ticks: {
|
|
29332
|
+
color: chartFontColor(definition.background),
|
|
29333
|
+
callback: formatTickValue({ locale, format }),
|
|
29334
|
+
},
|
|
29335
|
+
legend: {
|
|
29336
|
+
position: geoLegendPosition,
|
|
29337
|
+
align: geoLegendPosition.includes("right") ? "left" : "right",
|
|
29338
|
+
margin: getLegendMargin(definition),
|
|
29339
|
+
},
|
|
29340
|
+
interpolate: getRuntimeColorScale(definition),
|
|
29341
|
+
missing: definition.missingValueColor || "#ffffff",
|
|
29342
|
+
},
|
|
29343
|
+
};
|
|
29344
|
+
}
|
|
29345
|
+
function getGeoChartProjection(projection) {
|
|
29346
|
+
if (projection === "conicConformal") {
|
|
29347
|
+
return window.ChartGeo.geoConicConformal().rotate([100, 0]); // Centered on the US
|
|
29348
|
+
}
|
|
29349
|
+
return projection;
|
|
29350
|
+
}
|
|
29095
29351
|
function getChartAxisTitleRuntime(design) {
|
|
29096
29352
|
if (design?.title?.text) {
|
|
29097
29353
|
const { text, color, align, italic, bold } = design.title;
|
|
@@ -29155,6 +29411,44 @@ function getChartAxis(definition, position, type, options) {
|
|
|
29155
29411
|
};
|
|
29156
29412
|
}
|
|
29157
29413
|
}
|
|
29414
|
+
function getRuntimeColorScale(definition) {
|
|
29415
|
+
if (!definition.colorScale || typeof definition.colorScale === "string") {
|
|
29416
|
+
return definition.colorScale || "oranges";
|
|
29417
|
+
}
|
|
29418
|
+
const scaleColors = [{ value: 0, color: definition.colorScale.minColor }];
|
|
29419
|
+
if (definition.colorScale.midColor) {
|
|
29420
|
+
scaleColors.push({ value: 0.5, color: definition.colorScale.midColor });
|
|
29421
|
+
}
|
|
29422
|
+
scaleColors.push({ value: 1, color: definition.colorScale.maxColor });
|
|
29423
|
+
return getColorScale(scaleColors);
|
|
29424
|
+
}
|
|
29425
|
+
function getLegendMargin(definition) {
|
|
29426
|
+
switch (definition.legendPosition) {
|
|
29427
|
+
case "top":
|
|
29428
|
+
case "right":
|
|
29429
|
+
const hasTitle = !!definition.title.text;
|
|
29430
|
+
const topMargin = hasTitle ? CHART_PADDING_TOP + 30 : CHART_PADDING_TOP;
|
|
29431
|
+
return { top: topMargin, left: CHART_PADDING$1, right: CHART_PADDING$1 };
|
|
29432
|
+
case "bottom":
|
|
29433
|
+
case "left":
|
|
29434
|
+
case "none":
|
|
29435
|
+
return { left: CHART_PADDING$1, right: CHART_PADDING$1, bottom: CHART_PADDING_BOTTOM };
|
|
29436
|
+
}
|
|
29437
|
+
}
|
|
29438
|
+
function legendPositionToGeoLegendPosition(position) {
|
|
29439
|
+
switch (position) {
|
|
29440
|
+
case "top":
|
|
29441
|
+
return "top-left";
|
|
29442
|
+
case "right":
|
|
29443
|
+
return "top-right";
|
|
29444
|
+
case "bottom":
|
|
29445
|
+
return "bottom-right";
|
|
29446
|
+
case "left":
|
|
29447
|
+
return "bottom-left";
|
|
29448
|
+
case "none":
|
|
29449
|
+
return "bottom-left";
|
|
29450
|
+
}
|
|
29451
|
+
}
|
|
29158
29452
|
|
|
29159
29453
|
function getChartShowValues(definition, args) {
|
|
29160
29454
|
const { axisFormats, locale } = args;
|
|
@@ -29314,6 +29608,25 @@ function getRadarChartTooltip(definition, args) {
|
|
|
29314
29608
|
},
|
|
29315
29609
|
};
|
|
29316
29610
|
}
|
|
29611
|
+
function getGeoChartTooltip(definition, args) {
|
|
29612
|
+
const { locale, axisFormats } = args;
|
|
29613
|
+
const format = axisFormats?.y || axisFormats?.y1;
|
|
29614
|
+
return {
|
|
29615
|
+
filter: function (tooltipItem) {
|
|
29616
|
+
return tooltipItem.raw.value !== undefined;
|
|
29617
|
+
},
|
|
29618
|
+
callbacks: {
|
|
29619
|
+
label: function (tooltipItem) {
|
|
29620
|
+
const rawItem = tooltipItem.raw;
|
|
29621
|
+
const xLabel = rawItem.feature.properties.name;
|
|
29622
|
+
const yLabel = rawItem.value;
|
|
29623
|
+
const toolTipFormat = !format && Math.abs(yLabel) >= 1000 ? "#,##" : format;
|
|
29624
|
+
const yLabelStr = formatValue(yLabel, { format: toolTipFormat, locale });
|
|
29625
|
+
return xLabel ? `${xLabel}: ${yLabelStr}` : yLabelStr;
|
|
29626
|
+
},
|
|
29627
|
+
},
|
|
29628
|
+
};
|
|
29629
|
+
}
|
|
29317
29630
|
function calculatePercentage(dataset, dataIndex) {
|
|
29318
29631
|
const numericData = dataset.filter((value) => typeof value === "number");
|
|
29319
29632
|
const total = numericData.reduce((sum, value) => sum + value, 0);
|
|
@@ -29340,6 +29653,10 @@ var CHART_RUNTIME_HELPERS = /*#__PURE__*/Object.freeze({
|
|
|
29340
29653
|
getComboChartDatasets: getComboChartDatasets,
|
|
29341
29654
|
getComboChartLegend: getComboChartLegend,
|
|
29342
29655
|
getData: getData,
|
|
29656
|
+
getGeoChartData: getGeoChartData,
|
|
29657
|
+
getGeoChartDatasets: getGeoChartDatasets,
|
|
29658
|
+
getGeoChartScales: getGeoChartScales,
|
|
29659
|
+
getGeoChartTooltip: getGeoChartTooltip,
|
|
29343
29660
|
getLineChartData: getLineChartData,
|
|
29344
29661
|
getLineChartDatasets: getLineChartDatasets,
|
|
29345
29662
|
getLineChartLegend: getLineChartLegend,
|
|
@@ -29913,6 +30230,133 @@ function getSectionThresholdValue(threshold, minValue, maxValue) {
|
|
|
29913
30230
|
return clip(value, minValue, maxValue);
|
|
29914
30231
|
}
|
|
29915
30232
|
|
|
30233
|
+
class GeoChart extends AbstractChart {
|
|
30234
|
+
dataSets;
|
|
30235
|
+
labelRange;
|
|
30236
|
+
background;
|
|
30237
|
+
legendPosition;
|
|
30238
|
+
type = "geo";
|
|
30239
|
+
dataSetsHaveTitle;
|
|
30240
|
+
dataSetDesign;
|
|
30241
|
+
colorScale;
|
|
30242
|
+
missingValueColor;
|
|
30243
|
+
region;
|
|
30244
|
+
constructor(definition, sheetId, getters) {
|
|
30245
|
+
super(definition, sheetId, getters);
|
|
30246
|
+
this.dataSets = createDataSets(getters, definition.dataSets, sheetId, definition.dataSetsHaveTitle);
|
|
30247
|
+
this.labelRange = createValidRange(getters, sheetId, definition.labelRange);
|
|
30248
|
+
this.background = definition.background;
|
|
30249
|
+
this.legendPosition = definition.legendPosition;
|
|
30250
|
+
this.dataSetsHaveTitle = definition.dataSetsHaveTitle;
|
|
30251
|
+
this.dataSetDesign = definition.dataSets;
|
|
30252
|
+
this.colorScale = definition.colorScale;
|
|
30253
|
+
this.missingValueColor = definition.missingValueColor;
|
|
30254
|
+
this.region = definition.region;
|
|
30255
|
+
}
|
|
30256
|
+
static transformDefinition(definition, executed) {
|
|
30257
|
+
return transformChartDefinitionWithDataSetsWithZone(definition, executed);
|
|
30258
|
+
}
|
|
30259
|
+
static validateChartDefinition(validator, definition) {
|
|
30260
|
+
return validator.checkValidations(definition, checkDataset, checkLabelRange);
|
|
30261
|
+
}
|
|
30262
|
+
static getDefinitionFromContextCreation(context) {
|
|
30263
|
+
return {
|
|
30264
|
+
background: context.background,
|
|
30265
|
+
dataSets: context.range ?? [],
|
|
30266
|
+
dataSetsHaveTitle: context.dataSetsHaveTitle ?? false,
|
|
30267
|
+
legendPosition: context.legendPosition ?? "top",
|
|
30268
|
+
title: context.title || { text: "" },
|
|
30269
|
+
type: "geo",
|
|
30270
|
+
labelRange: context.auxiliaryRange || undefined,
|
|
30271
|
+
aggregated: context.aggregated,
|
|
30272
|
+
};
|
|
30273
|
+
}
|
|
30274
|
+
getContextCreation() {
|
|
30275
|
+
const range = [];
|
|
30276
|
+
for (const [i, dataSet] of this.dataSets.entries()) {
|
|
30277
|
+
range.push({
|
|
30278
|
+
...this.dataSetDesign?.[i],
|
|
30279
|
+
dataRange: this.getters.getRangeString(dataSet.dataRange, this.sheetId),
|
|
30280
|
+
});
|
|
30281
|
+
}
|
|
30282
|
+
return {
|
|
30283
|
+
...this,
|
|
30284
|
+
range,
|
|
30285
|
+
auxiliaryRange: this.labelRange
|
|
30286
|
+
? this.getters.getRangeString(this.labelRange, this.sheetId)
|
|
30287
|
+
: undefined,
|
|
30288
|
+
};
|
|
30289
|
+
}
|
|
30290
|
+
copyForSheetId(sheetId) {
|
|
30291
|
+
const dataSets = copyDataSetsWithNewSheetId(this.sheetId, sheetId, this.dataSets);
|
|
30292
|
+
const labelRange = copyLabelRangeWithNewSheetId(this.sheetId, sheetId, this.labelRange);
|
|
30293
|
+
const definition = this.getDefinitionWithSpecificDataSets(dataSets, labelRange, sheetId);
|
|
30294
|
+
return new GeoChart(definition, sheetId, this.getters);
|
|
30295
|
+
}
|
|
30296
|
+
copyInSheetId(sheetId) {
|
|
30297
|
+
const definition = this.getDefinitionWithSpecificDataSets(this.dataSets, this.labelRange, sheetId);
|
|
30298
|
+
return new GeoChart(definition, sheetId, this.getters);
|
|
30299
|
+
}
|
|
30300
|
+
getDefinition() {
|
|
30301
|
+
return this.getDefinitionWithSpecificDataSets(this.dataSets, this.labelRange);
|
|
30302
|
+
}
|
|
30303
|
+
getDefinitionWithSpecificDataSets(dataSets, labelRange, targetSheetId) {
|
|
30304
|
+
const ranges = [];
|
|
30305
|
+
for (const [i, dataSet] of dataSets.entries()) {
|
|
30306
|
+
ranges.push({
|
|
30307
|
+
...this.dataSetDesign?.[i],
|
|
30308
|
+
dataRange: this.getters.getRangeString(dataSet.dataRange, targetSheetId || this.sheetId),
|
|
30309
|
+
});
|
|
30310
|
+
}
|
|
30311
|
+
return {
|
|
30312
|
+
type: "geo",
|
|
30313
|
+
dataSetsHaveTitle: dataSets.length ? Boolean(dataSets[0].labelCell) : false,
|
|
30314
|
+
background: this.background,
|
|
30315
|
+
dataSets: ranges,
|
|
30316
|
+
legendPosition: this.legendPosition,
|
|
30317
|
+
labelRange: labelRange
|
|
30318
|
+
? this.getters.getRangeString(labelRange, targetSheetId || this.sheetId)
|
|
30319
|
+
: undefined,
|
|
30320
|
+
title: this.title,
|
|
30321
|
+
colorScale: this.colorScale,
|
|
30322
|
+
missingValueColor: this.missingValueColor,
|
|
30323
|
+
region: this.region,
|
|
30324
|
+
};
|
|
30325
|
+
}
|
|
30326
|
+
getDefinitionForExcel() {
|
|
30327
|
+
return undefined;
|
|
30328
|
+
}
|
|
30329
|
+
updateRanges(applyChange) {
|
|
30330
|
+
const { dataSets, labelRange, isStale } = updateChartRangesWithDataSets(this.getters, applyChange, this.dataSets, this.labelRange);
|
|
30331
|
+
if (!isStale) {
|
|
30332
|
+
return this;
|
|
30333
|
+
}
|
|
30334
|
+
const definition = this.getDefinitionWithSpecificDataSets(dataSets, labelRange);
|
|
30335
|
+
return new GeoChart(definition, this.sheetId, this.getters);
|
|
30336
|
+
}
|
|
30337
|
+
}
|
|
30338
|
+
function createGeoChartRuntime(chart, getters) {
|
|
30339
|
+
const definition = chart.getDefinition();
|
|
30340
|
+
const chartData = getGeoChartData(definition, chart.dataSets, chart.labelRange, getters);
|
|
30341
|
+
const config = {
|
|
30342
|
+
type: "choropleth",
|
|
30343
|
+
data: {
|
|
30344
|
+
datasets: getGeoChartDatasets(definition, chartData),
|
|
30345
|
+
},
|
|
30346
|
+
options: {
|
|
30347
|
+
...CHART_COMMON_OPTIONS,
|
|
30348
|
+
layout: getChartLayout(),
|
|
30349
|
+
scales: getGeoChartScales(definition, chartData),
|
|
30350
|
+
plugins: {
|
|
30351
|
+
title: getChartTitle(definition),
|
|
30352
|
+
tooltip: getGeoChartTooltip(definition, chartData),
|
|
30353
|
+
legend: { display: false },
|
|
30354
|
+
},
|
|
30355
|
+
},
|
|
30356
|
+
};
|
|
30357
|
+
return { chartJsConfig: config, background: chart.background || BACKGROUND_CHART_COLOR };
|
|
30358
|
+
}
|
|
30359
|
+
|
|
29916
30360
|
class LineChart extends AbstractChart {
|
|
29917
30361
|
dataSets;
|
|
29918
30362
|
labelRange;
|
|
@@ -30906,6 +31350,15 @@ chartRegistry.add("radar", {
|
|
|
30906
31350
|
getChartDefinitionFromContextCreation: RadarChart.getDefinitionFromContextCreation,
|
|
30907
31351
|
sequence: 80,
|
|
30908
31352
|
});
|
|
31353
|
+
chartRegistry.add("geo", {
|
|
31354
|
+
match: (type) => type === "geo",
|
|
31355
|
+
createChart: (definition, sheetId, getters) => new GeoChart(definition, sheetId, getters),
|
|
31356
|
+
getChartRuntime: createGeoChartRuntime,
|
|
31357
|
+
validateChartDefinition: GeoChart.validateChartDefinition,
|
|
31358
|
+
transformDefinition: GeoChart.transformDefinition,
|
|
31359
|
+
getChartDefinitionFromContextCreation: GeoChart.getDefinitionFromContextCreation,
|
|
31360
|
+
sequence: 90,
|
|
31361
|
+
});
|
|
30909
31362
|
const chartComponentRegistry = new Registry();
|
|
30910
31363
|
chartComponentRegistry.add("line", ChartJsComponent);
|
|
30911
31364
|
chartComponentRegistry.add("bar", ChartJsComponent);
|
|
@@ -30917,6 +31370,7 @@ chartComponentRegistry.add("scorecard", ScorecardChart);
|
|
|
30917
31370
|
chartComponentRegistry.add("waterfall", ChartJsComponent);
|
|
30918
31371
|
chartComponentRegistry.add("pyramid", ChartJsComponent);
|
|
30919
31372
|
chartComponentRegistry.add("radar", ChartJsComponent);
|
|
31373
|
+
chartComponentRegistry.add("geo", ChartJsComponent);
|
|
30920
31374
|
const chartCategories = {
|
|
30921
31375
|
line: _t("Line"),
|
|
30922
31376
|
column: _t("Column"),
|
|
@@ -31076,6 +31530,13 @@ chartSubtypeRegistry
|
|
|
31076
31530
|
subtypeDefinition: { fillArea: true },
|
|
31077
31531
|
category: "misc",
|
|
31078
31532
|
preview: "o-spreadsheet-ChartPreview.FILLED_RADAR_CHART",
|
|
31533
|
+
})
|
|
31534
|
+
.add("geo", {
|
|
31535
|
+
displayName: _t("Geo Chart"),
|
|
31536
|
+
chartSubtype: "geo",
|
|
31537
|
+
chartType: "geo",
|
|
31538
|
+
category: "misc",
|
|
31539
|
+
preview: "o-spreadsheet-ChartPreview.GEO_CHART",
|
|
31079
31540
|
});
|
|
31080
31541
|
|
|
31081
31542
|
/**
|
|
@@ -31926,18 +32387,66 @@ const ErrorToolTipPopoverBuilder = {
|
|
|
31926
32387
|
},
|
|
31927
32388
|
};
|
|
31928
32389
|
|
|
31929
|
-
|
|
31930
|
-
|
|
31931
|
-
|
|
31932
|
-
|
|
31933
|
-
|
|
31934
|
-
|
|
31935
|
-
|
|
32390
|
+
const CHECK_SVG = /*xml*/ `
|
|
32391
|
+
<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'>
|
|
32392
|
+
<path fill='none' stroke='#FFF' stroke-linecap='round' stroke-linejoin='round' stroke-width='3' d='m6 10 3 3 6-6'/>
|
|
32393
|
+
</svg>
|
|
32394
|
+
`;
|
|
32395
|
+
const CHECKBOX_WIDTH = 14;
|
|
32396
|
+
css /* scss */ `
|
|
32397
|
+
label.o-checkbox {
|
|
32398
|
+
input {
|
|
32399
|
+
appearance: none;
|
|
32400
|
+
-webkit-appearance: none;
|
|
32401
|
+
-moz-appearance: none;
|
|
32402
|
+
border-radius: 0;
|
|
32403
|
+
width: ${CHECKBOX_WIDTH}px;
|
|
32404
|
+
height: ${CHECKBOX_WIDTH}px;
|
|
32405
|
+
vertical-align: top;
|
|
32406
|
+
box-sizing: border-box;
|
|
32407
|
+
outline: none;
|
|
32408
|
+
border: 1px solid ${GRAY_300};
|
|
32409
|
+
cursor: pointer;
|
|
32410
|
+
|
|
32411
|
+
&:hover {
|
|
32412
|
+
border-color: ${ACTION_COLOR};
|
|
32413
|
+
}
|
|
32414
|
+
|
|
32415
|
+
&:checked {
|
|
32416
|
+
background: url("data:image/svg+xml,${encodeURIComponent(CHECK_SVG)}");
|
|
32417
|
+
background-color: ${ACTION_COLOR};
|
|
32418
|
+
border-color: ${ACTION_COLOR};
|
|
32419
|
+
}
|
|
32420
|
+
|
|
32421
|
+
&:focus {
|
|
32422
|
+
outline: none;
|
|
32423
|
+
box-shadow: 0 0 0 0.25rem rgba(113, 75, 103, 0.25);
|
|
32424
|
+
border-color: ${ACTION_COLOR};
|
|
32425
|
+
}
|
|
31936
32426
|
}
|
|
31937
32427
|
}
|
|
31938
32428
|
`;
|
|
32429
|
+
class Checkbox extends owl.Component {
|
|
32430
|
+
static template = "o-spreadsheet.Checkbox";
|
|
32431
|
+
static props = {
|
|
32432
|
+
label: { type: String, optional: true },
|
|
32433
|
+
value: { type: Boolean, optional: true },
|
|
32434
|
+
className: { type: String, optional: true },
|
|
32435
|
+
name: { type: String, optional: true },
|
|
32436
|
+
title: { type: String, optional: true },
|
|
32437
|
+
disabled: { type: Boolean, optional: true },
|
|
32438
|
+
onChange: Function,
|
|
32439
|
+
};
|
|
32440
|
+
static defaultProps = { value: false };
|
|
32441
|
+
onChange(ev) {
|
|
32442
|
+
const value = ev.target.checked;
|
|
32443
|
+
this.props.onChange(value);
|
|
32444
|
+
}
|
|
32445
|
+
}
|
|
32446
|
+
|
|
31939
32447
|
class FilterMenuValueItem extends owl.Component {
|
|
31940
32448
|
static template = "o-spreadsheet-FilterMenuValueItem";
|
|
32449
|
+
static components = { Checkbox };
|
|
31941
32450
|
static props = {
|
|
31942
32451
|
value: String,
|
|
31943
32452
|
isChecked: Boolean,
|
|
@@ -31975,8 +32484,6 @@ const CSS = css /* scss */ `
|
|
|
31975
32484
|
.o-filter-menu-item {
|
|
31976
32485
|
display: flex;
|
|
31977
32486
|
box-sizing: border-box;
|
|
31978
|
-
height: ${MENU_ITEM_HEIGHT}px;
|
|
31979
|
-
padding: 4px 4px 4px 0px;
|
|
31980
32487
|
cursor: pointer;
|
|
31981
32488
|
user-select: none;
|
|
31982
32489
|
|
|
@@ -31985,14 +32492,6 @@ const CSS = css /* scss */ `
|
|
|
31985
32492
|
}
|
|
31986
32493
|
}
|
|
31987
32494
|
|
|
31988
|
-
input {
|
|
31989
|
-
box-sizing: border-box;
|
|
31990
|
-
margin-bottom: 5px;
|
|
31991
|
-
border: 1px solid #949494;
|
|
31992
|
-
height: 24px;
|
|
31993
|
-
padding-right: 28px;
|
|
31994
|
-
}
|
|
31995
|
-
|
|
31996
32495
|
.o-search-icon {
|
|
31997
32496
|
right: 5px;
|
|
31998
32497
|
top: 3px;
|
|
@@ -32009,19 +32508,12 @@ const CSS = css /* scss */ `
|
|
|
32009
32508
|
display: flex;
|
|
32010
32509
|
flex-direction: row;
|
|
32011
32510
|
margin-bottom: 4px;
|
|
32012
|
-
|
|
32013
|
-
.o-filter-menu-action-text {
|
|
32014
|
-
cursor: pointer;
|
|
32015
|
-
margin-right: 10px;
|
|
32016
|
-
color: blue;
|
|
32017
|
-
text-decoration: underline;
|
|
32018
|
-
}
|
|
32019
32511
|
}
|
|
32020
32512
|
|
|
32021
32513
|
.o-filter-menu-list {
|
|
32022
32514
|
flex: auto;
|
|
32023
32515
|
overflow-y: auto;
|
|
32024
|
-
border: 1px solid
|
|
32516
|
+
border: 1px solid ${GRAY_300};
|
|
32025
32517
|
|
|
32026
32518
|
.o-filter-menu-no-values {
|
|
32027
32519
|
color: #949494;
|
|
@@ -34213,6 +34705,21 @@ const pivotProperties = {
|
|
|
34213
34705
|
isReadonlyAllowed: true,
|
|
34214
34706
|
icon: "o-spreadsheet-Icon.PIVOT",
|
|
34215
34707
|
};
|
|
34708
|
+
const pivotSortingAsc = {
|
|
34709
|
+
name: _t("Ascending"),
|
|
34710
|
+
execute: (env) => sortPivot(env, "asc"),
|
|
34711
|
+
isActive: (env) => isPivotSortMenuItemActive(env, "asc"),
|
|
34712
|
+
};
|
|
34713
|
+
const pivotSortingDesc = {
|
|
34714
|
+
name: _t("Descending"),
|
|
34715
|
+
execute: (env) => sortPivot(env, "desc"),
|
|
34716
|
+
isActive: (env) => isPivotSortMenuItemActive(env, "desc"),
|
|
34717
|
+
};
|
|
34718
|
+
const noPivotSorting = {
|
|
34719
|
+
name: _t("No sorting"),
|
|
34720
|
+
execute: (env) => sortPivot(env, "none"),
|
|
34721
|
+
isActive: (env) => isPivotSortMenuItemActive(env, "none"),
|
|
34722
|
+
};
|
|
34216
34723
|
const FIX_FORMULAS = {
|
|
34217
34724
|
name: _t("Convert to individual formulas"),
|
|
34218
34725
|
execute(env) {
|
|
@@ -34249,6 +34756,66 @@ const FIX_FORMULAS = {
|
|
|
34249
34756
|
},
|
|
34250
34757
|
icon: "o-spreadsheet-Icon.PIVOT",
|
|
34251
34758
|
};
|
|
34759
|
+
function canSortPivot(env) {
|
|
34760
|
+
const position = env.model.getters.getActivePosition();
|
|
34761
|
+
const pivotId = env.model.getters.getPivotIdFromPosition(position);
|
|
34762
|
+
if (!pivotId ||
|
|
34763
|
+
!env.model.getters.isExistingPivot(pivotId) ||
|
|
34764
|
+
!env.model.getters.isSpillPivotFormula(position)) {
|
|
34765
|
+
return false;
|
|
34766
|
+
}
|
|
34767
|
+
const pivot = env.model.getters.getPivot(pivotId);
|
|
34768
|
+
if (!pivot.isValid()) {
|
|
34769
|
+
return false;
|
|
34770
|
+
}
|
|
34771
|
+
const pivotCell = env.model.getters.getPivotCellFromPosition(position);
|
|
34772
|
+
return pivotCell.type === "VALUE" || pivotCell.type === "MEASURE_HEADER";
|
|
34773
|
+
}
|
|
34774
|
+
function sortPivot(env, order) {
|
|
34775
|
+
const position = env.model.getters.getActivePosition();
|
|
34776
|
+
const pivotId = env.model.getters.getPivotIdFromPosition(position);
|
|
34777
|
+
const pivotCell = env.model.getters.getPivotCellFromPosition(position);
|
|
34778
|
+
if (pivotCell.type === "EMPTY" || pivotCell.type === "HEADER" || !pivotId) {
|
|
34779
|
+
return;
|
|
34780
|
+
}
|
|
34781
|
+
if (order === "none") {
|
|
34782
|
+
env.model.dispatch("UPDATE_PIVOT", {
|
|
34783
|
+
pivotId: pivotId,
|
|
34784
|
+
pivot: {
|
|
34785
|
+
...env.model.getters.getPivotCoreDefinition(pivotId),
|
|
34786
|
+
sortedColumn: undefined,
|
|
34787
|
+
},
|
|
34788
|
+
});
|
|
34789
|
+
return;
|
|
34790
|
+
}
|
|
34791
|
+
const pivot = env.model.getters.getPivot(pivotId);
|
|
34792
|
+
const colDomain = domainToColRowDomain(pivot, pivotCell.domain).colDomain;
|
|
34793
|
+
env.model.dispatch("UPDATE_PIVOT", {
|
|
34794
|
+
pivotId: pivotId,
|
|
34795
|
+
pivot: {
|
|
34796
|
+
...env.model.getters.getPivotCoreDefinition(pivotId),
|
|
34797
|
+
sortedColumn: { domain: colDomain, order, measure: pivotCell.measure },
|
|
34798
|
+
},
|
|
34799
|
+
});
|
|
34800
|
+
}
|
|
34801
|
+
function isPivotSortMenuItemActive(env, order) {
|
|
34802
|
+
const position = env.model.getters.getActivePosition();
|
|
34803
|
+
const pivotId = env.model.getters.getPivotIdFromPosition(position);
|
|
34804
|
+
const pivotCell = env.model.getters.getPivotCellFromPosition(position);
|
|
34805
|
+
if (pivotCell.type === "EMPTY" || pivotCell.type === "HEADER" || !pivotId) {
|
|
34806
|
+
return false;
|
|
34807
|
+
}
|
|
34808
|
+
const pivot = env.model.getters.getPivot(pivotId);
|
|
34809
|
+
const colDomain = domainToColRowDomain(pivot, pivotCell.domain).colDomain;
|
|
34810
|
+
const sortedColumn = pivot.definition.sortedColumn;
|
|
34811
|
+
if (order === "none") {
|
|
34812
|
+
return !sortedColumn;
|
|
34813
|
+
}
|
|
34814
|
+
if (!sortedColumn || sortedColumn.order !== order) {
|
|
34815
|
+
return false;
|
|
34816
|
+
}
|
|
34817
|
+
return sortedColumn.measure === pivotCell.measure && deepEquals(sortedColumn.domain, colDomain);
|
|
34818
|
+
}
|
|
34252
34819
|
|
|
34253
34820
|
//------------------------------------------------------------------------------
|
|
34254
34821
|
// Context Menu Registry
|
|
@@ -34347,14 +34914,33 @@ cellMenuRegistry
|
|
|
34347
34914
|
name: INSERT_LINK_NAME,
|
|
34348
34915
|
sequence: 150,
|
|
34349
34916
|
separator: true,
|
|
34917
|
+
})
|
|
34918
|
+
.add("pivot_sorting", {
|
|
34919
|
+
name: _t("Sort pivot"),
|
|
34920
|
+
sequence: 155,
|
|
34921
|
+
icon: "o-spreadsheet-Icon.SORT_RANGE",
|
|
34922
|
+
isVisible: canSortPivot,
|
|
34350
34923
|
})
|
|
34351
34924
|
.add("pivot_fix_formulas", {
|
|
34352
34925
|
...FIX_FORMULAS,
|
|
34353
|
-
sequence:
|
|
34926
|
+
sequence: 160,
|
|
34354
34927
|
})
|
|
34355
34928
|
.add("pivot_properties", {
|
|
34356
34929
|
...pivotProperties,
|
|
34357
34930
|
sequence: 170,
|
|
34931
|
+
separator: true,
|
|
34932
|
+
})
|
|
34933
|
+
.addChild("pivot_sorting_asc", ["pivot_sorting"], {
|
|
34934
|
+
...pivotSortingAsc,
|
|
34935
|
+
sequence: 10,
|
|
34936
|
+
})
|
|
34937
|
+
.addChild("pivot_sorting_desc", ["pivot_sorting"], {
|
|
34938
|
+
...pivotSortingDesc,
|
|
34939
|
+
sequence: 20,
|
|
34940
|
+
})
|
|
34941
|
+
.addChild("pivot_sorting_none", ["pivot_sorting"], {
|
|
34942
|
+
...noPivotSorting,
|
|
34943
|
+
sequence: 30,
|
|
34358
34944
|
});
|
|
34359
34945
|
|
|
34360
34946
|
const sortRange = {
|
|
@@ -34367,7 +34953,7 @@ const sortAscending = {
|
|
|
34367
34953
|
execute: (env) => {
|
|
34368
34954
|
const { anchor, zones } = env.model.getters.getSelection();
|
|
34369
34955
|
const sheetId = env.model.getters.getActiveSheetId();
|
|
34370
|
-
interactiveSortSelection(env, sheetId, anchor.cell, zones[0], "
|
|
34956
|
+
interactiveSortSelection(env, sheetId, anchor.cell, zones[0], "asc");
|
|
34371
34957
|
},
|
|
34372
34958
|
icon: "o-spreadsheet-Icon.SORT_ASCENDING",
|
|
34373
34959
|
};
|
|
@@ -34395,7 +34981,7 @@ const sortDescending = {
|
|
|
34395
34981
|
execute: (env) => {
|
|
34396
34982
|
const { anchor, zones } = env.model.getters.getSelection();
|
|
34397
34983
|
const sheetId = env.model.getters.getActiveSheetId();
|
|
34398
|
-
interactiveSortSelection(env, sheetId, anchor.cell, zones[0], "
|
|
34984
|
+
interactiveSortSelection(env, sheetId, anchor.cell, zones[0], "desc");
|
|
34399
34985
|
},
|
|
34400
34986
|
icon: "o-spreadsheet-Icon.SORT_DESCENDING",
|
|
34401
34987
|
};
|
|
@@ -34878,6 +35464,227 @@ function interactiveFreezeColumnsRows(env, dimension, base) {
|
|
|
34878
35464
|
}
|
|
34879
35465
|
}
|
|
34880
35466
|
|
|
35467
|
+
class PositionMap {
|
|
35468
|
+
map = {};
|
|
35469
|
+
constructor(entries = []) {
|
|
35470
|
+
for (const [position, value] of entries) {
|
|
35471
|
+
this.set(position, value);
|
|
35472
|
+
}
|
|
35473
|
+
}
|
|
35474
|
+
set({ sheetId, col, row }, value) {
|
|
35475
|
+
const map = this.map;
|
|
35476
|
+
if (!map[sheetId]) {
|
|
35477
|
+
map[sheetId] = {};
|
|
35478
|
+
}
|
|
35479
|
+
if (!map[sheetId][col]) {
|
|
35480
|
+
map[sheetId][col] = {};
|
|
35481
|
+
}
|
|
35482
|
+
map[sheetId][col][row] = value;
|
|
35483
|
+
}
|
|
35484
|
+
get({ sheetId, col, row }) {
|
|
35485
|
+
return this.map[sheetId]?.[col]?.[row];
|
|
35486
|
+
}
|
|
35487
|
+
getSheet(sheetId) {
|
|
35488
|
+
return this.map[sheetId];
|
|
35489
|
+
}
|
|
35490
|
+
has({ sheetId, col, row }) {
|
|
35491
|
+
return this.map[sheetId]?.[col]?.[row] !== undefined;
|
|
35492
|
+
}
|
|
35493
|
+
delete({ sheetId, col, row }) {
|
|
35494
|
+
delete this.map[sheetId]?.[col]?.[row];
|
|
35495
|
+
}
|
|
35496
|
+
keys() {
|
|
35497
|
+
const map = this.map;
|
|
35498
|
+
const keys = [];
|
|
35499
|
+
for (const sheetId in map) {
|
|
35500
|
+
for (const col in map[sheetId]) {
|
|
35501
|
+
for (const row in map[sheetId][col]) {
|
|
35502
|
+
keys.push({ sheetId, col: parseInt(col), row: parseInt(row) });
|
|
35503
|
+
}
|
|
35504
|
+
}
|
|
35505
|
+
}
|
|
35506
|
+
return keys;
|
|
35507
|
+
}
|
|
35508
|
+
keysForSheet(sheetId) {
|
|
35509
|
+
const map = this.map[sheetId];
|
|
35510
|
+
if (!map) {
|
|
35511
|
+
return [];
|
|
35512
|
+
}
|
|
35513
|
+
const keys = [];
|
|
35514
|
+
for (const col in map) {
|
|
35515
|
+
for (const row in map[col]) {
|
|
35516
|
+
keys.push({ sheetId, col: parseInt(col), row: parseInt(row) });
|
|
35517
|
+
}
|
|
35518
|
+
}
|
|
35519
|
+
return keys;
|
|
35520
|
+
}
|
|
35521
|
+
*entries() {
|
|
35522
|
+
const map = this.map;
|
|
35523
|
+
for (const position of this.keys()) {
|
|
35524
|
+
const { sheetId, col, row } = position;
|
|
35525
|
+
yield [position, map[sheetId][col][row]];
|
|
35526
|
+
}
|
|
35527
|
+
}
|
|
35528
|
+
}
|
|
35529
|
+
|
|
35530
|
+
class FormulaFingerprintStore extends SpreadsheetStore {
|
|
35531
|
+
mutators = ["enable", "disable"];
|
|
35532
|
+
isInvalidated = false;
|
|
35533
|
+
fingerprintColors = {
|
|
35534
|
+
[DATA_FINGERPRINT]: "#D9D9D9",
|
|
35535
|
+
};
|
|
35536
|
+
isEnabled = false;
|
|
35537
|
+
colors = new PositionMap();
|
|
35538
|
+
handle(cmd) {
|
|
35539
|
+
if (isCoreCommand(cmd) && this.isEnabled) {
|
|
35540
|
+
this.isInvalidated = true;
|
|
35541
|
+
}
|
|
35542
|
+
switch (cmd.type) {
|
|
35543
|
+
case "UNDO":
|
|
35544
|
+
case "REDO":
|
|
35545
|
+
case "ACTIVATE_SHEET":
|
|
35546
|
+
if (this.isEnabled) {
|
|
35547
|
+
this.isInvalidated = true;
|
|
35548
|
+
}
|
|
35549
|
+
break;
|
|
35550
|
+
}
|
|
35551
|
+
}
|
|
35552
|
+
finalize() {
|
|
35553
|
+
if (this.isInvalidated) {
|
|
35554
|
+
this.isInvalidated = false;
|
|
35555
|
+
this.computeFingerprints();
|
|
35556
|
+
}
|
|
35557
|
+
}
|
|
35558
|
+
enable() {
|
|
35559
|
+
this.isEnabled = true;
|
|
35560
|
+
this.computeFingerprints();
|
|
35561
|
+
}
|
|
35562
|
+
disable() {
|
|
35563
|
+
this.isEnabled = false;
|
|
35564
|
+
this.colors = new PositionMap();
|
|
35565
|
+
}
|
|
35566
|
+
computeFingerprints() {
|
|
35567
|
+
this.colors = new PositionMap();
|
|
35568
|
+
const fingerprints = new PositionMap();
|
|
35569
|
+
const allFingerprints = new Set();
|
|
35570
|
+
const activeSheetId = this.getters.getActiveSheetId();
|
|
35571
|
+
const cells = this.getters.getCells(activeSheetId);
|
|
35572
|
+
for (const cellId in cells) {
|
|
35573
|
+
const fingerprint = this.computeFingerprint(cells[cellId]);
|
|
35574
|
+
if (!fingerprint) {
|
|
35575
|
+
continue;
|
|
35576
|
+
}
|
|
35577
|
+
allFingerprints.add(fingerprint);
|
|
35578
|
+
const position = this.getters.getCellPosition(cellId);
|
|
35579
|
+
fingerprints.set(position, fingerprint);
|
|
35580
|
+
}
|
|
35581
|
+
this.assignColors(allFingerprints);
|
|
35582
|
+
for (const [position, fingerprint] of fingerprints.entries()) {
|
|
35583
|
+
const color = this.fingerprintColors[fingerprint];
|
|
35584
|
+
this.colors.set(position, color);
|
|
35585
|
+
this.colorSpreadZone(position, color);
|
|
35586
|
+
}
|
|
35587
|
+
}
|
|
35588
|
+
colorSpreadZone(position, fingerprintColor) {
|
|
35589
|
+
const spreadZone = this.getters.getSpreadZone(position);
|
|
35590
|
+
if (!spreadZone) {
|
|
35591
|
+
return;
|
|
35592
|
+
}
|
|
35593
|
+
const sheetId = position.sheetId;
|
|
35594
|
+
for (let row = spreadZone.top; row <= spreadZone.bottom; row++) {
|
|
35595
|
+
for (let col = spreadZone.left; col <= spreadZone.right; col++) {
|
|
35596
|
+
const spreadPosition = { sheetId, col, row };
|
|
35597
|
+
this.colors.set(spreadPosition, fingerprintColor);
|
|
35598
|
+
}
|
|
35599
|
+
}
|
|
35600
|
+
}
|
|
35601
|
+
assignColors(fingerprints) {
|
|
35602
|
+
const colors = new AlternatingColorGenerator(fingerprints.size);
|
|
35603
|
+
Object.keys(this.fingerprintColors).forEach(() => colors.next());
|
|
35604
|
+
for (const fingerprint of fingerprints) {
|
|
35605
|
+
if (!this.fingerprintColors[fingerprint]) {
|
|
35606
|
+
this.fingerprintColors[fingerprint] = setColorAlpha(colors.next(), 0.8);
|
|
35607
|
+
}
|
|
35608
|
+
}
|
|
35609
|
+
}
|
|
35610
|
+
computeFingerprint(cell) {
|
|
35611
|
+
const position = this.getters.getCellPosition(cell.id);
|
|
35612
|
+
if (cell.isFormula) {
|
|
35613
|
+
return this.computeFormulaFingerprint(position, cell);
|
|
35614
|
+
}
|
|
35615
|
+
else {
|
|
35616
|
+
return this.getLiteralFingerprint(position);
|
|
35617
|
+
}
|
|
35618
|
+
}
|
|
35619
|
+
computeFormulaFingerprint(position, cell) {
|
|
35620
|
+
const dependencies = cell.compiledFormula.dependencies;
|
|
35621
|
+
const colCellOffset = position.col;
|
|
35622
|
+
const rowCellOffset = position.row;
|
|
35623
|
+
const positionSheetIndex = this.getters.getSheetIds().indexOf(position.sheetId);
|
|
35624
|
+
// As an optimization, we do not build each reference vector individually
|
|
35625
|
+
// to sum them up, but instead we directly add each component to the resulting
|
|
35626
|
+
// vector. This is equivalent to summing up all reference vectors.
|
|
35627
|
+
const fingerprintVector = {
|
|
35628
|
+
dx: 0,
|
|
35629
|
+
dy: 0,
|
|
35630
|
+
dSheet: 0,
|
|
35631
|
+
};
|
|
35632
|
+
for (const range of dependencies) {
|
|
35633
|
+
const zone = range.zone;
|
|
35634
|
+
const [left, right] = range.parts;
|
|
35635
|
+
const rangeSheetIndex = this.getters.getSheetIds().indexOf(range.sheetId);
|
|
35636
|
+
fingerprintVector.dSheet = rangeSheetIndex - positionSheetIndex;
|
|
35637
|
+
// in relative mode, we offset the col and row by the cell's position
|
|
35638
|
+
// in absolute mode, we offset the col and row relative to the sheet
|
|
35639
|
+
const isLeftUnbounded = range.isFullRow && !range.unboundedZone.hasHeader;
|
|
35640
|
+
const isTopUnbounded = range.isFullCol && !range.unboundedZone.hasHeader;
|
|
35641
|
+
const leftOffset = isLeftUnbounded || left?.colFixed ? 0 : colCellOffset;
|
|
35642
|
+
const topOffset = isTopUnbounded || left?.rowFixed ? 0 : rowCellOffset;
|
|
35643
|
+
const isRightFixed = (!right && left?.colFixed) || right?.colFixed;
|
|
35644
|
+
const isBottomFixed = (!right && left.rowFixed) || right?.rowFixed;
|
|
35645
|
+
const isRightUnbounded = range.unboundedZone.right === undefined;
|
|
35646
|
+
const isBottomUnbounded = range.unboundedZone.bottom === undefined;
|
|
35647
|
+
const rightOffset = isRightUnbounded || isRightFixed ? 0 : colCellOffset;
|
|
35648
|
+
const bottomOffset = isBottomUnbounded || isBottomFixed ? 0 : rowCellOffset;
|
|
35649
|
+
const referenceZone = reorderZone({
|
|
35650
|
+
top: zone.top - topOffset,
|
|
35651
|
+
left: zone.left - leftOffset,
|
|
35652
|
+
right: zone.right - rightOffset,
|
|
35653
|
+
bottom: zone.bottom - bottomOffset,
|
|
35654
|
+
});
|
|
35655
|
+
for (let dy = referenceZone.top; dy <= referenceZone.bottom; dy++) {
|
|
35656
|
+
for (let dx = referenceZone.left; dx <= referenceZone.right; dx++) {
|
|
35657
|
+
fingerprintVector.dx += dx;
|
|
35658
|
+
fingerprintVector.dy += dy;
|
|
35659
|
+
}
|
|
35660
|
+
}
|
|
35661
|
+
}
|
|
35662
|
+
// removes the index placeholders from the normalized formula
|
|
35663
|
+
// =|N0|+|N1|+|N0| -> =|N|+|N|+|N|
|
|
35664
|
+
const normalizedFormula = cell.compiledFormula.normalizedFormula.replace(/(|\w)(\d)(|)/g, "$1$3");
|
|
35665
|
+
return hash(fingerprintVector) + normalizedFormula;
|
|
35666
|
+
}
|
|
35667
|
+
getLiteralFingerprint(position) {
|
|
35668
|
+
const evaluatedCell = this.getters.getEvaluatedCell(position);
|
|
35669
|
+
switch (evaluatedCell.type) {
|
|
35670
|
+
case CellValueType.number:
|
|
35671
|
+
case CellValueType.boolean:
|
|
35672
|
+
return DATA_FINGERPRINT;
|
|
35673
|
+
case CellValueType.text:
|
|
35674
|
+
case CellValueType.empty:
|
|
35675
|
+
case CellValueType.error:
|
|
35676
|
+
return undefined;
|
|
35677
|
+
}
|
|
35678
|
+
}
|
|
35679
|
+
}
|
|
35680
|
+
function hash(vector) {
|
|
35681
|
+
return Object.entries(vector)
|
|
35682
|
+
.sort(([a], [b]) => a.localeCompare(b))
|
|
35683
|
+
.map(([_, value]) => value)
|
|
35684
|
+
.join(",");
|
|
35685
|
+
}
|
|
35686
|
+
const DATA_FINGERPRINT = "DATA_FINGERPRINT";
|
|
35687
|
+
|
|
34881
35688
|
const hideCols = {
|
|
34882
35689
|
name: HIDE_COLUMNS_NAME,
|
|
34883
35690
|
execute: (env) => {
|
|
@@ -35049,6 +35856,19 @@ const viewGridlines = {
|
|
|
35049
35856
|
return env.model.getters.getGridLinesVisibility(sheetId);
|
|
35050
35857
|
},
|
|
35051
35858
|
};
|
|
35859
|
+
const irregularityMap = {
|
|
35860
|
+
name: _t("Irregularity map"),
|
|
35861
|
+
execute: (env) => {
|
|
35862
|
+
const fingerprintStore = env.getStore(FormulaFingerprintStore);
|
|
35863
|
+
if (fingerprintStore.isEnabled) {
|
|
35864
|
+
fingerprintStore.disable();
|
|
35865
|
+
}
|
|
35866
|
+
else {
|
|
35867
|
+
fingerprintStore.enable();
|
|
35868
|
+
}
|
|
35869
|
+
},
|
|
35870
|
+
icon: "o-spreadsheet-Icon.IRREGULARITY_MAP",
|
|
35871
|
+
};
|
|
35052
35872
|
const viewFormulas = {
|
|
35053
35873
|
name: _t("Formulas"),
|
|
35054
35874
|
isActive: (env) => env.model.getters.shouldShowFormulas(),
|
|
@@ -35666,6 +36486,11 @@ topbarMenuRegistry
|
|
|
35666
36486
|
.addChild("view_formulas", ["view", "show"], {
|
|
35667
36487
|
...viewFormulas,
|
|
35668
36488
|
sequence: 10,
|
|
36489
|
+
})
|
|
36490
|
+
.addChild("view_irregularity_map", ["view"], {
|
|
36491
|
+
...irregularityMap,
|
|
36492
|
+
sequence: 40,
|
|
36493
|
+
separator: true,
|
|
35669
36494
|
})
|
|
35670
36495
|
// ---------------------------------------------------------------------
|
|
35671
36496
|
// INSERT MENU ITEMS
|
|
@@ -35968,62 +36793,6 @@ class OTRegistry extends Registry {
|
|
|
35968
36793
|
}
|
|
35969
36794
|
const otRegistry = new OTRegistry();
|
|
35970
36795
|
|
|
35971
|
-
const CHECK_SVG = /*xml*/ `
|
|
35972
|
-
<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'>
|
|
35973
|
-
<path fill='none' stroke='#FFF' stroke-linecap='round' stroke-linejoin='round' stroke-width='3' d='m6 10 3 3 6-6'/>
|
|
35974
|
-
</svg>
|
|
35975
|
-
`;
|
|
35976
|
-
const CHECKBOX_WIDTH = 14;
|
|
35977
|
-
css /* scss */ `
|
|
35978
|
-
label.o-checkbox {
|
|
35979
|
-
input {
|
|
35980
|
-
appearance: none;
|
|
35981
|
-
-webkit-appearance: none;
|
|
35982
|
-
-moz-appearance: none;
|
|
35983
|
-
border-radius: 0;
|
|
35984
|
-
width: ${CHECKBOX_WIDTH}px;
|
|
35985
|
-
height: ${CHECKBOX_WIDTH}px;
|
|
35986
|
-
vertical-align: top;
|
|
35987
|
-
box-sizing: border-box;
|
|
35988
|
-
outline: none;
|
|
35989
|
-
border: 1px solid ${GRAY_300};
|
|
35990
|
-
|
|
35991
|
-
&:hover {
|
|
35992
|
-
border-color: ${ACTION_COLOR};
|
|
35993
|
-
}
|
|
35994
|
-
|
|
35995
|
-
&:checked {
|
|
35996
|
-
background: url("data:image/svg+xml,${encodeURIComponent(CHECK_SVG)}");
|
|
35997
|
-
background-color: ${ACTION_COLOR};
|
|
35998
|
-
border-color: ${ACTION_COLOR};
|
|
35999
|
-
}
|
|
36000
|
-
|
|
36001
|
-
&:focus {
|
|
36002
|
-
outline: none;
|
|
36003
|
-
box-shadow: 0 0 0 0.25rem rgba(113, 75, 103, 0.25);
|
|
36004
|
-
border-color: ${ACTION_COLOR};
|
|
36005
|
-
}
|
|
36006
|
-
}
|
|
36007
|
-
}
|
|
36008
|
-
`;
|
|
36009
|
-
class Checkbox extends owl.Component {
|
|
36010
|
-
static template = "o-spreadsheet.Checkbox";
|
|
36011
|
-
static props = {
|
|
36012
|
-
label: { type: String, optional: true },
|
|
36013
|
-
value: { type: Boolean, optional: true },
|
|
36014
|
-
className: { type: String, optional: true },
|
|
36015
|
-
name: { type: String, optional: true },
|
|
36016
|
-
title: { type: String, optional: true },
|
|
36017
|
-
disabled: { type: Boolean, optional: true },
|
|
36018
|
-
onChange: Function,
|
|
36019
|
-
};
|
|
36020
|
-
static defaultProps = { value: false };
|
|
36021
|
-
onChange(ev) {
|
|
36022
|
-
const value = ev.target.checked;
|
|
36023
|
-
this.props.onChange(value);
|
|
36024
|
-
}
|
|
36025
|
-
}
|
|
36026
|
-
|
|
36027
36796
|
class Section extends owl.Component {
|
|
36028
36797
|
static template = "o_spreadsheet.Section";
|
|
36029
36798
|
static props = {
|
|
@@ -38263,6 +39032,104 @@ class GaugeChartDesignPanel extends owl.Component {
|
|
|
38263
39032
|
}
|
|
38264
39033
|
}
|
|
38265
39034
|
|
|
39035
|
+
class GeoChartRegionSelectSection extends owl.Component {
|
|
39036
|
+
static template = "o-spreadsheet-GeoChartRegionSelectSection";
|
|
39037
|
+
static components = { Section };
|
|
39038
|
+
static props = {
|
|
39039
|
+
figureId: String,
|
|
39040
|
+
definition: Object,
|
|
39041
|
+
updateChart: Function,
|
|
39042
|
+
};
|
|
39043
|
+
updateSelectedRegion(ev) {
|
|
39044
|
+
const value = ev.target.value;
|
|
39045
|
+
this.props.updateChart(this.props.figureId, { region: value });
|
|
39046
|
+
}
|
|
39047
|
+
get availableRegions() {
|
|
39048
|
+
return this.env.model.getters.getGeoChartAvailableRegions();
|
|
39049
|
+
}
|
|
39050
|
+
get selectedRegion() {
|
|
39051
|
+
return this.props.definition.region || this.availableRegions[0]?.id;
|
|
39052
|
+
}
|
|
39053
|
+
}
|
|
39054
|
+
|
|
39055
|
+
class GeoChartConfigPanel extends GenericChartConfigPanel {
|
|
39056
|
+
static template = "o-spreadsheet-GeoChartConfigPanel";
|
|
39057
|
+
static components = { ...GenericChartConfigPanel.components, GeoChartRegionSelectSection };
|
|
39058
|
+
get dataRanges() {
|
|
39059
|
+
return this.getDataSeriesRanges().slice(0, 1);
|
|
39060
|
+
}
|
|
39061
|
+
onDataSeriesConfirmed() {
|
|
39062
|
+
this.dataSeriesRanges = spreadRange(this.env.model.getters, this.dataSeriesRanges).slice(0, 1);
|
|
39063
|
+
this.state.datasetDispatchResult = this.props.updateChart(this.props.figureId, {
|
|
39064
|
+
dataSets: this.dataSeriesRanges,
|
|
39065
|
+
});
|
|
39066
|
+
}
|
|
39067
|
+
getLabelRangeOptions() {
|
|
39068
|
+
return [
|
|
39069
|
+
{
|
|
39070
|
+
name: "dataSetsHaveTitle",
|
|
39071
|
+
label: this.dataSetsHaveTitleLabel,
|
|
39072
|
+
value: this.props.definition.dataSetsHaveTitle,
|
|
39073
|
+
onChange: this.onUpdateDataSetsHaveTitle.bind(this),
|
|
39074
|
+
},
|
|
39075
|
+
];
|
|
39076
|
+
}
|
|
39077
|
+
}
|
|
39078
|
+
|
|
39079
|
+
const DEFAULT_CUSTOM_COLOR_SCALE = {
|
|
39080
|
+
minColor: "#FFF5EB",
|
|
39081
|
+
midColor: "#FD8D3C",
|
|
39082
|
+
maxColor: "#7F2704",
|
|
39083
|
+
};
|
|
39084
|
+
class GeoChartDesignPanel extends ChartWithAxisDesignPanel {
|
|
39085
|
+
static template = "o-spreadsheet-GeoChartDesignPanel";
|
|
39086
|
+
static components = { ...ChartWithAxisDesignPanel.components, RoundColorPicker };
|
|
39087
|
+
colorScalesChoices = ChartTerms.GeoChart.ColorScales;
|
|
39088
|
+
updateColorScaleType(ev) {
|
|
39089
|
+
const value = ev.target.value;
|
|
39090
|
+
value === "custom"
|
|
39091
|
+
? this.updateColorScale(DEFAULT_CUSTOM_COLOR_SCALE)
|
|
39092
|
+
: this.updateColorScale(value);
|
|
39093
|
+
}
|
|
39094
|
+
updateColorScale(colorScale) {
|
|
39095
|
+
this.props.updateChart(this.props.figureId, { colorScale });
|
|
39096
|
+
}
|
|
39097
|
+
updateMissingValueColor(color) {
|
|
39098
|
+
this.props.updateChart(this.props.figureId, { missingValueColor: color });
|
|
39099
|
+
}
|
|
39100
|
+
updateLegendPosition(ev) {
|
|
39101
|
+
const value = ev.target.value;
|
|
39102
|
+
this.props.updateChart(this.props.figureId, { legendPosition: value });
|
|
39103
|
+
}
|
|
39104
|
+
get selectedColorScale() {
|
|
39105
|
+
return typeof this.props.definition.colorScale === "object"
|
|
39106
|
+
? "custom"
|
|
39107
|
+
: this.props.definition.colorScale || "oranges";
|
|
39108
|
+
}
|
|
39109
|
+
get selectedMissingValueColor() {
|
|
39110
|
+
return this.props.definition.missingValueColor || "#ffffff";
|
|
39111
|
+
}
|
|
39112
|
+
get customColorScale() {
|
|
39113
|
+
if (typeof this.props.definition.colorScale === "object") {
|
|
39114
|
+
return this.props.definition.colorScale;
|
|
39115
|
+
}
|
|
39116
|
+
return undefined;
|
|
39117
|
+
}
|
|
39118
|
+
getCustomColorScaleColor(color) {
|
|
39119
|
+
return this.customColorScale?.[color] ?? "";
|
|
39120
|
+
}
|
|
39121
|
+
setCustomColorScaleColor(colorType, color) {
|
|
39122
|
+
if (!color && colorType !== "midColor") {
|
|
39123
|
+
color = "#fff";
|
|
39124
|
+
}
|
|
39125
|
+
const customColorScale = this.customColorScale;
|
|
39126
|
+
if (!customColorScale) {
|
|
39127
|
+
return;
|
|
39128
|
+
}
|
|
39129
|
+
this.updateColorScale({ ...customColorScale, [colorType]: color });
|
|
39130
|
+
}
|
|
39131
|
+
}
|
|
39132
|
+
|
|
38266
39133
|
class LineConfigPanel extends GenericChartConfigPanel {
|
|
38267
39134
|
static template = "o-spreadsheet-LineConfigPanel";
|
|
38268
39135
|
get canTreatLabelsAsText() {
|
|
@@ -38576,6 +39443,10 @@ chartSidePanelComponentRegistry
|
|
|
38576
39443
|
.add("radar", {
|
|
38577
39444
|
configuration: GenericChartConfigPanel,
|
|
38578
39445
|
design: RadarChartDesignPanel,
|
|
39446
|
+
})
|
|
39447
|
+
.add("geo", {
|
|
39448
|
+
configuration: GeoChartConfigPanel,
|
|
39449
|
+
design: GeoChartDesignPanel,
|
|
38579
39450
|
});
|
|
38580
39451
|
|
|
38581
39452
|
css /* scss */ `
|
|
@@ -39939,7 +40810,7 @@ css /* scss */ `
|
|
|
39939
40810
|
border-color: ${GRAY_300};
|
|
39940
40811
|
|
|
39941
40812
|
&.active {
|
|
39942
|
-
border-color: ${
|
|
40813
|
+
border-color: ${ACTION_COLOR};
|
|
39943
40814
|
}
|
|
39944
40815
|
|
|
39945
40816
|
&.o-invalid {
|
|
@@ -40897,7 +41768,7 @@ class ConditionalFormattingEditor extends owl.Component {
|
|
|
40897
41768
|
}
|
|
40898
41769
|
const point = this.state.rules.colorScale[target];
|
|
40899
41770
|
if (point) {
|
|
40900
|
-
point.color =
|
|
41771
|
+
point.color = colorToNumber(color);
|
|
40901
41772
|
}
|
|
40902
41773
|
this.updateConditionalFormat({ rule: this.state.rules.colorScale });
|
|
40903
41774
|
this.closeMenus();
|
|
@@ -41042,6 +41913,9 @@ class ConditionalFormattingEditor extends owl.Component {
|
|
|
41042
41913
|
return [this.state.rules.dataBar.rangeValues || ""];
|
|
41043
41914
|
}
|
|
41044
41915
|
updateDataBarColor(color) {
|
|
41916
|
+
if (!isColorValid(color)) {
|
|
41917
|
+
return;
|
|
41918
|
+
}
|
|
41045
41919
|
this.state.rules.dataBar.color = Number.parseInt(color.slice(1), 16);
|
|
41046
41920
|
this.updateConditionalFormat({ rule: this.state.rules.dataBar });
|
|
41047
41921
|
}
|
|
@@ -41172,6 +42046,10 @@ css /* scss */ `
|
|
|
41172
42046
|
border: 1px solid #d8dadd;
|
|
41173
42047
|
color: #374151;
|
|
41174
42048
|
}
|
|
42049
|
+
|
|
42050
|
+
table {
|
|
42051
|
+
table-layout: fixed;
|
|
42052
|
+
}
|
|
41175
42053
|
}
|
|
41176
42054
|
`;
|
|
41177
42055
|
class CustomCurrencyPanel extends owl.Component {
|
|
@@ -43645,6 +44523,64 @@ class PivotMeasureEditor extends owl.Component {
|
|
|
43645
44523
|
}
|
|
43646
44524
|
}
|
|
43647
44525
|
|
|
44526
|
+
css /* scss */ `
|
|
44527
|
+
.o-pivot-sort {
|
|
44528
|
+
.o-sort-card {
|
|
44529
|
+
width: fit-content;
|
|
44530
|
+
background-color: ${GRAY_100};
|
|
44531
|
+
border: 1px solid ${GRAY_300};
|
|
44532
|
+
|
|
44533
|
+
.o-sort-value {
|
|
44534
|
+
color: ${PRIMARY_BUTTON_BG};
|
|
44535
|
+
}
|
|
44536
|
+
}
|
|
44537
|
+
}
|
|
44538
|
+
`;
|
|
44539
|
+
class PivotSortSection extends owl.Component {
|
|
44540
|
+
static template = "o-spreadsheet-PivotSortSection";
|
|
44541
|
+
static components = {
|
|
44542
|
+
Section,
|
|
44543
|
+
};
|
|
44544
|
+
static props = {
|
|
44545
|
+
definition: Object,
|
|
44546
|
+
pivotId: String,
|
|
44547
|
+
};
|
|
44548
|
+
get hasValidSort() {
|
|
44549
|
+
const pivot = this.env.model.getters.getPivot(this.props.pivotId);
|
|
44550
|
+
return (!!this.props.definition.sortedColumn &&
|
|
44551
|
+
isSortedColumnValid(this.props.definition.sortedColumn, pivot));
|
|
44552
|
+
}
|
|
44553
|
+
get sortDescription() {
|
|
44554
|
+
const sortOrder = this.props.definition.sortedColumn?.order === "asc" ? _t("ascending") : _t("descending");
|
|
44555
|
+
return _t("Sorted on column (%(ascOrDesc)s):", {
|
|
44556
|
+
ascOrDesc: sortOrder,
|
|
44557
|
+
});
|
|
44558
|
+
}
|
|
44559
|
+
get sortValuesAndFields() {
|
|
44560
|
+
const sortedColumn = this.props.definition.sortedColumn;
|
|
44561
|
+
if (!sortedColumn) {
|
|
44562
|
+
return [];
|
|
44563
|
+
}
|
|
44564
|
+
const pivot = this.env.model.getters.getPivot(this.props.pivotId);
|
|
44565
|
+
const locale = this.env.model.getters.getLocale();
|
|
44566
|
+
const currentDomain = [];
|
|
44567
|
+
const sortValues = [];
|
|
44568
|
+
for (const domainItem of sortedColumn.domain) {
|
|
44569
|
+
currentDomain.push(domainItem);
|
|
44570
|
+
const { value, format } = pivot.getPivotHeaderValueAndFormat(currentDomain);
|
|
44571
|
+
const label = formatValue(value, { format, locale });
|
|
44572
|
+
const field = pivot.definition.getDimension(domainItem.field);
|
|
44573
|
+
sortValues.push({ field: getFieldDisplayName(field), value: label });
|
|
44574
|
+
}
|
|
44575
|
+
if (sortedColumn.domain.length === 0) {
|
|
44576
|
+
sortValues.push({ value: _t("Total") });
|
|
44577
|
+
}
|
|
44578
|
+
const measureLabel = pivot.getMeasure(sortedColumn.measure).displayName;
|
|
44579
|
+
sortValues.push({ value: measureLabel, field: _t("Measure") });
|
|
44580
|
+
return sortValues;
|
|
44581
|
+
}
|
|
44582
|
+
}
|
|
44583
|
+
|
|
43648
44584
|
css /* scss */ `
|
|
43649
44585
|
.add-calculated-measure {
|
|
43650
44586
|
cursor: pointer;
|
|
@@ -43658,6 +44594,7 @@ class PivotLayoutConfigurator extends owl.Component {
|
|
|
43658
44594
|
PivotDimensionOrder,
|
|
43659
44595
|
PivotDimensionGranularity,
|
|
43660
44596
|
PivotMeasureEditor,
|
|
44597
|
+
PivotSortSection,
|
|
43661
44598
|
};
|
|
43662
44599
|
static props = {
|
|
43663
44600
|
definition: Object,
|
|
@@ -43814,9 +44751,13 @@ class PivotLayoutConfigurator extends owl.Component {
|
|
|
43814
44751
|
}
|
|
43815
44752
|
updateMeasure(measure, newMeasure) {
|
|
43816
44753
|
const { measures } = this.props.definition;
|
|
43817
|
-
|
|
44754
|
+
const update = {
|
|
43818
44755
|
measures: measures.map((m) => (m.id === measure.id ? newMeasure : m)),
|
|
43819
|
-
}
|
|
44756
|
+
};
|
|
44757
|
+
if (this.props.definition.sortedColumn?.measure === measure.id) {
|
|
44758
|
+
update.sortedColumn = { ...this.props.definition.sortedColumn, measure: newMeasure.id };
|
|
44759
|
+
}
|
|
44760
|
+
this.props.onDimensionsUpdated(update);
|
|
43820
44761
|
}
|
|
43821
44762
|
getMeasureId(fieldName, aggregator) {
|
|
43822
44763
|
const baseId = fieldName + (aggregator ? `:${aggregator}` : "");
|
|
@@ -43972,10 +44913,12 @@ class PivotRuntimeDefinition {
|
|
|
43972
44913
|
measures;
|
|
43973
44914
|
columns;
|
|
43974
44915
|
rows;
|
|
44916
|
+
sortedColumn;
|
|
43975
44917
|
constructor(definition, fields) {
|
|
43976
44918
|
this.measures = definition.measures.map((measure) => createMeasure(fields, measure));
|
|
43977
44919
|
this.columns = definition.columns.map((dimension) => createPivotDimension(fields, dimension));
|
|
43978
44920
|
this.rows = definition.rows.map((dimension) => createPivotDimension(fields, dimension));
|
|
44921
|
+
this.sortedColumn = definition.sortedColumn;
|
|
43979
44922
|
}
|
|
43980
44923
|
getDimension(nameWithGranularity) {
|
|
43981
44924
|
const dimension = this.columns.find((d) => d.nameWithGranularity === nameWithGranularity) ||
|
|
@@ -44129,6 +45072,7 @@ class SpreadsheetPivotTable {
|
|
|
44129
45072
|
pivotCells = {};
|
|
44130
45073
|
rowTree;
|
|
44131
45074
|
colTree;
|
|
45075
|
+
isSorted = false;
|
|
44132
45076
|
constructor(columns, rows, measures, fieldsType) {
|
|
44133
45077
|
this.columns = columns.map((row) => {
|
|
44134
45078
|
// offset in the pivot table
|
|
@@ -44302,6 +45246,7 @@ class SpreadsheetPivotTable {
|
|
|
44302
45246
|
value,
|
|
44303
45247
|
field: row.fields[rowDepth],
|
|
44304
45248
|
children: [],
|
|
45249
|
+
type: this.fieldsType[fieldName] || "char",
|
|
44305
45250
|
width: 0, // not used
|
|
44306
45251
|
};
|
|
44307
45252
|
treesAtDepth[depth].push(node);
|
|
@@ -44324,6 +45269,7 @@ class SpreadsheetPivotTable {
|
|
|
44324
45269
|
field: leaf.fields[depth],
|
|
44325
45270
|
children: [],
|
|
44326
45271
|
width: leaf.width,
|
|
45272
|
+
type: this.fieldsType[fieldName] || "char",
|
|
44327
45273
|
};
|
|
44328
45274
|
if (treesAtDepth[depth]?.at(-1)?.value !== value) {
|
|
44329
45275
|
treesAtDepth[depth + 1] = [];
|
|
@@ -44342,6 +45288,35 @@ class SpreadsheetPivotTable {
|
|
|
44342
45288
|
fieldsType: this.fieldsType,
|
|
44343
45289
|
};
|
|
44344
45290
|
}
|
|
45291
|
+
sort(measure, sortedColumn, getValue) {
|
|
45292
|
+
if (this.isSorted) {
|
|
45293
|
+
return;
|
|
45294
|
+
}
|
|
45295
|
+
const getSortValue = (measure, domain) => {
|
|
45296
|
+
const rawValue = getValue(measure, domain).value;
|
|
45297
|
+
return typeof rawValue === "number" ? rawValue : -Infinity;
|
|
45298
|
+
};
|
|
45299
|
+
const sortColDomain = sortedColumn.domain;
|
|
45300
|
+
const sortFn = (rowDomain1, rowDomain2) => {
|
|
45301
|
+
const value1 = getSortValue(measure, [...rowDomain1, ...sortColDomain]);
|
|
45302
|
+
const value2 = getSortValue(measure, [...rowDomain2, ...sortColDomain]);
|
|
45303
|
+
return sortedColumn.order === "asc" ? value1 - value2 : value2 - value1;
|
|
45304
|
+
};
|
|
45305
|
+
const sortedRowTree = sortPivotTree(this.rowTree(), [], sortFn);
|
|
45306
|
+
this.rowTree = lazy(sortedRowTree);
|
|
45307
|
+
this.rows = [...this.rowTreeToRows(sortedRowTree), this.rows[this.rows.length - 1]];
|
|
45308
|
+
this.isSorted = true;
|
|
45309
|
+
}
|
|
45310
|
+
rowTreeToRows(tree, parentRow) {
|
|
45311
|
+
return tree.flatMap((node) => {
|
|
45312
|
+
const row = {
|
|
45313
|
+
indent: parentRow ? parentRow.indent + 1 : 0,
|
|
45314
|
+
fields: [...(parentRow?.fields || []), node.field],
|
|
45315
|
+
values: [...(parentRow?.values || []), node.value],
|
|
45316
|
+
};
|
|
45317
|
+
return [row, ...this.rowTreeToRows(node.children, row)];
|
|
45318
|
+
});
|
|
45319
|
+
}
|
|
44345
45320
|
}
|
|
44346
45321
|
const EMPTY_PIVOT_CELL = { type: "EMPTY" };
|
|
44347
45322
|
|
|
@@ -44419,6 +45394,7 @@ function dataEntriesToColumnsTree(dataEntries, columns, index) {
|
|
|
44419
45394
|
value: groups[key]?.[0]?.[column.nameWithGranularity]?.value ?? null,
|
|
44420
45395
|
field: colName,
|
|
44421
45396
|
children: dataEntriesToColumnsTree(groups[key] || [], columns, index + 1),
|
|
45397
|
+
type: column.type,
|
|
44422
45398
|
width: 0,
|
|
44423
45399
|
};
|
|
44424
45400
|
});
|
|
@@ -45284,6 +46260,7 @@ class PivotSidePanelStore extends SpreadsheetStore {
|
|
|
45284
46260
|
format: measure.format,
|
|
45285
46261
|
display: measure.display,
|
|
45286
46262
|
})),
|
|
46263
|
+
sortedColumn: this.shouldKeepSortedColumn(definition) ? definition.sortedColumn : undefined,
|
|
45287
46264
|
};
|
|
45288
46265
|
if (!this.draft && deepEquals(coreDefinition, cleanedDefinition)) {
|
|
45289
46266
|
return;
|
|
@@ -45342,6 +46319,19 @@ class PivotSidePanelStore extends SpreadsheetStore {
|
|
|
45342
46319
|
}
|
|
45343
46320
|
return granularitiesPerFields;
|
|
45344
46321
|
}
|
|
46322
|
+
/**
|
|
46323
|
+
* Check if we want to keep the sorted column when updating the pivot definition. We should remove it if either
|
|
46324
|
+
* the measure is not in the new definition or the columns have changed.
|
|
46325
|
+
*/
|
|
46326
|
+
shouldKeepSortedColumn(newDefinition) {
|
|
46327
|
+
const { sortedColumn } = newDefinition;
|
|
46328
|
+
if (!sortedColumn) {
|
|
46329
|
+
return true;
|
|
46330
|
+
}
|
|
46331
|
+
const oldDefinition = this.getters.getPivotCoreDefinition(this.pivotId);
|
|
46332
|
+
return (newDefinition.measures.find((measure) => measure.id === sortedColumn.measure) &&
|
|
46333
|
+
deepEquals(oldDefinition.columns, newDefinition.columns));
|
|
46334
|
+
}
|
|
45345
46335
|
}
|
|
45346
46336
|
|
|
45347
46337
|
class PivotSpreadsheetSidePanel extends owl.Component {
|
|
@@ -46443,6 +47433,7 @@ css /* scss */ `
|
|
|
46443
47433
|
}
|
|
46444
47434
|
}
|
|
46445
47435
|
`;
|
|
47436
|
+
const DEFAULT_TABLE_STYLE_COLOR = "#3C78D8";
|
|
46446
47437
|
class TableStyleEditorPanel extends owl.Component {
|
|
46447
47438
|
static template = "o-spreadsheet-TableStyleEditorPanel";
|
|
46448
47439
|
static components = { Section, RoundColorPicker, TableStylePreview };
|
|
@@ -46461,7 +47452,7 @@ class TableStyleEditorPanel extends owl.Component {
|
|
|
46461
47452
|
: null;
|
|
46462
47453
|
return {
|
|
46463
47454
|
pickerOpened: false,
|
|
46464
|
-
primaryColor: editedStyle?.primaryColor ||
|
|
47455
|
+
primaryColor: editedStyle?.primaryColor || DEFAULT_TABLE_STYLE_COLOR,
|
|
46465
47456
|
selectedTemplateName: editedStyle?.templateName || "lightColoredText",
|
|
46466
47457
|
styleName: editedStyle?.displayName || this.env.model.getters.getNewCustomTableStyleName(),
|
|
46467
47458
|
};
|
|
@@ -46470,7 +47461,7 @@ class TableStyleEditorPanel extends owl.Component {
|
|
|
46470
47461
|
this.state.pickerOpened = !this.state.pickerOpened;
|
|
46471
47462
|
}
|
|
46472
47463
|
onColorPicked(color) {
|
|
46473
|
-
this.state.primaryColor = color;
|
|
47464
|
+
this.state.primaryColor = isColorValid(color) ? color : DEFAULT_TABLE_STYLE_COLOR;
|
|
46474
47465
|
this.state.pickerOpened = false;
|
|
46475
47466
|
}
|
|
46476
47467
|
onTemplatePicked(templateName) {
|
|
@@ -48086,6 +49077,7 @@ class FiguresContainer extends owl.Component {
|
|
|
48086
49077
|
draggedFigure: undefined,
|
|
48087
49078
|
horizontalSnap: undefined,
|
|
48088
49079
|
verticalSnap: undefined,
|
|
49080
|
+
cancelDnd: undefined,
|
|
48089
49081
|
});
|
|
48090
49082
|
setup() {
|
|
48091
49083
|
owl.onMounted(() => {
|
|
@@ -48098,12 +49090,28 @@ class FiguresContainer extends owl.Component {
|
|
|
48098
49090
|
// new rendering
|
|
48099
49091
|
this.render();
|
|
48100
49092
|
});
|
|
49093
|
+
owl.onWillUpdateProps(() => {
|
|
49094
|
+
const sheetId = this.env.model.getters.getActiveSheetId();
|
|
49095
|
+
const draggedFigureId = this.dnd.draggedFigure?.id;
|
|
49096
|
+
if (draggedFigureId && !this.env.model.getters.getFigure(sheetId, draggedFigureId)) {
|
|
49097
|
+
if (this.dnd.cancelDnd) {
|
|
49098
|
+
this.dnd.cancelDnd();
|
|
49099
|
+
}
|
|
49100
|
+
this.dnd.draggedFigure = undefined;
|
|
49101
|
+
this.dnd.horizontalSnap = undefined;
|
|
49102
|
+
this.dnd.verticalSnap = undefined;
|
|
49103
|
+
this.dnd.cancelDnd = undefined;
|
|
49104
|
+
}
|
|
49105
|
+
});
|
|
48101
49106
|
}
|
|
48102
49107
|
getVisibleFigures() {
|
|
48103
49108
|
const visibleFigures = this.env.model.getters.getVisibleFigures();
|
|
48104
49109
|
if (this.dnd.draggedFigure &&
|
|
48105
49110
|
!visibleFigures.some((figure) => figure.id === this.dnd.draggedFigure?.id)) {
|
|
48106
|
-
|
|
49111
|
+
const draggedFigure = this.env.model.getters.getFigure(this.env.model.getters.getActiveSheetId(), this.dnd.draggedFigure?.id);
|
|
49112
|
+
if (draggedFigure) {
|
|
49113
|
+
visibleFigures.push(draggedFigure);
|
|
49114
|
+
}
|
|
48107
49115
|
}
|
|
48108
49116
|
return visibleFigures;
|
|
48109
49117
|
}
|
|
@@ -48222,7 +49230,7 @@ class FiguresContainer extends owl.Component {
|
|
|
48222
49230
|
this.dnd.verticalSnap = undefined;
|
|
48223
49231
|
this.env.model.dispatch("UPDATE_FIGURE", { sheetId, id: figure.id, x, y });
|
|
48224
49232
|
};
|
|
48225
|
-
startDnd(onMouseMove, onMouseUp);
|
|
49233
|
+
this.dnd.cancelDnd = startDnd(onMouseMove, onMouseUp);
|
|
48226
49234
|
}
|
|
48227
49235
|
/**
|
|
48228
49236
|
* Initialize the resize of a figure with mouse movements
|
|
@@ -48270,7 +49278,7 @@ class FiguresContainer extends owl.Component {
|
|
|
48270
49278
|
this.dnd.horizontalSnap = undefined;
|
|
48271
49279
|
this.dnd.verticalSnap = undefined;
|
|
48272
49280
|
};
|
|
48273
|
-
startDnd(onMouseMove, onMouseUp);
|
|
49281
|
+
this.dnd.cancelDnd = startDnd(onMouseMove, onMouseUp);
|
|
48274
49282
|
}
|
|
48275
49283
|
getOtherFigures(figId) {
|
|
48276
49284
|
return this.getVisibleFigures().filter((f) => f.id !== figId);
|
|
@@ -49377,9 +50385,11 @@ class HeadersOverlay extends owl.Component {
|
|
|
49377
50385
|
class GridRenderer {
|
|
49378
50386
|
getters;
|
|
49379
50387
|
renderer;
|
|
50388
|
+
fingerprints;
|
|
49380
50389
|
constructor(get) {
|
|
49381
50390
|
this.getters = get(ModelStore).getters;
|
|
49382
50391
|
this.renderer = get(RendererStore);
|
|
50392
|
+
this.fingerprints = get(FormulaFingerprintStore);
|
|
49383
50393
|
this.renderer.register(this);
|
|
49384
50394
|
}
|
|
49385
50395
|
get renderingLayers() {
|
|
@@ -49857,14 +50867,22 @@ class GridRenderer {
|
|
|
49857
50867
|
const showFormula = this.getters.shouldShowFormulas();
|
|
49858
50868
|
const { x, y, width, height } = this.getters.getVisibleRect(zone);
|
|
49859
50869
|
const { verticalAlign } = this.getters.getCellStyle(position);
|
|
50870
|
+
let style = this.getters.getCellComputedStyle(position);
|
|
50871
|
+
if (this.fingerprints.isEnabled) {
|
|
50872
|
+
const fingerprintColor = this.fingerprints.colors.get(position);
|
|
50873
|
+
style = { ...style, fillColor: fingerprintColor };
|
|
50874
|
+
}
|
|
50875
|
+
const dataBarFill = this.fingerprints.isEnabled
|
|
50876
|
+
? undefined
|
|
50877
|
+
: this.getters.getConditionalDataBar(position);
|
|
49860
50878
|
const box = {
|
|
49861
50879
|
x,
|
|
49862
50880
|
y,
|
|
49863
50881
|
width,
|
|
49864
50882
|
height,
|
|
49865
50883
|
border: this.getters.getCellComputedBorder(position) || undefined,
|
|
49866
|
-
style
|
|
49867
|
-
dataBarFill
|
|
50884
|
+
style,
|
|
50885
|
+
dataBarFill,
|
|
49868
50886
|
verticalAlign,
|
|
49869
50887
|
isError: (cell.type === CellValueType.error && !!cell.message) ||
|
|
49870
50888
|
this.getters.isDataValidationInvalid(position),
|
|
@@ -49889,7 +50907,6 @@ class GridRenderer {
|
|
|
49889
50907
|
box.hasIcon = this.getters.doesCellHaveGridIcon(position);
|
|
49890
50908
|
const headerIconWidth = box.hasIcon ? GRID_ICON_EDGE_LENGTH + GRID_ICON_MARGIN : 0;
|
|
49891
50909
|
/** Content */
|
|
49892
|
-
const style = this.getters.getCellComputedStyle(position);
|
|
49893
50910
|
const wrapping = style.wrapping || "overflow";
|
|
49894
50911
|
const wrapText = wrapping === "wrap" && !showFormula;
|
|
49895
50912
|
const maxWidth = width - 2 * MIN_CELL_TEXT_MARGIN;
|
|
@@ -51867,62 +52884,6 @@ class BordersPlugin extends CorePlugin {
|
|
|
51867
52884
|
}
|
|
51868
52885
|
}
|
|
51869
52886
|
|
|
51870
|
-
class PositionMap {
|
|
51871
|
-
map = {};
|
|
51872
|
-
constructor(entries = []) {
|
|
51873
|
-
for (const [position, value] of entries) {
|
|
51874
|
-
this.set(position, value);
|
|
51875
|
-
}
|
|
51876
|
-
}
|
|
51877
|
-
set({ sheetId, col, row }, value) {
|
|
51878
|
-
const map = this.map;
|
|
51879
|
-
if (!map[sheetId]) {
|
|
51880
|
-
map[sheetId] = {};
|
|
51881
|
-
}
|
|
51882
|
-
if (!map[sheetId][col]) {
|
|
51883
|
-
map[sheetId][col] = {};
|
|
51884
|
-
}
|
|
51885
|
-
map[sheetId][col][row] = value;
|
|
51886
|
-
}
|
|
51887
|
-
get({ sheetId, col, row }) {
|
|
51888
|
-
return this.map[sheetId]?.[col]?.[row];
|
|
51889
|
-
}
|
|
51890
|
-
getSheet(sheetId) {
|
|
51891
|
-
return this.map[sheetId];
|
|
51892
|
-
}
|
|
51893
|
-
has({ sheetId, col, row }) {
|
|
51894
|
-
return this.map[sheetId]?.[col]?.[row] !== undefined;
|
|
51895
|
-
}
|
|
51896
|
-
delete({ sheetId, col, row }) {
|
|
51897
|
-
delete this.map[sheetId]?.[col]?.[row];
|
|
51898
|
-
}
|
|
51899
|
-
keys() {
|
|
51900
|
-
const map = this.map;
|
|
51901
|
-
const keys = [];
|
|
51902
|
-
for (const sheetId in map) {
|
|
51903
|
-
for (const col in map[sheetId]) {
|
|
51904
|
-
for (const row in map[sheetId][col]) {
|
|
51905
|
-
keys.push({ sheetId, col: parseInt(col), row: parseInt(row) });
|
|
51906
|
-
}
|
|
51907
|
-
}
|
|
51908
|
-
}
|
|
51909
|
-
return keys;
|
|
51910
|
-
}
|
|
51911
|
-
keysForSheet(sheetId) {
|
|
51912
|
-
const map = this.map[sheetId];
|
|
51913
|
-
if (!map) {
|
|
51914
|
-
return [];
|
|
51915
|
-
}
|
|
51916
|
-
const keys = [];
|
|
51917
|
-
for (const col in map) {
|
|
51918
|
-
for (const row in map[col]) {
|
|
51919
|
-
keys.push({ sheetId, col: parseInt(col), row: parseInt(row) });
|
|
51920
|
-
}
|
|
51921
|
-
}
|
|
51922
|
-
return keys;
|
|
51923
|
-
}
|
|
51924
|
-
}
|
|
51925
|
-
|
|
51926
52887
|
/**
|
|
51927
52888
|
* Core Plugin
|
|
51928
52889
|
*
|
|
@@ -52361,7 +53322,7 @@ class CellPlugin extends CorePlugin {
|
|
|
52361
53322
|
const before = this.getters.getCell({ sheetId, col, row });
|
|
52362
53323
|
const hasContent = "content" in after || "formula" in after;
|
|
52363
53324
|
// Compute the new cell properties
|
|
52364
|
-
const afterContent = hasContent ?
|
|
53325
|
+
const afterContent = hasContent ? replaceNewLines(after?.content) : before?.content || "";
|
|
52365
53326
|
let style;
|
|
52366
53327
|
if (after.style !== undefined) {
|
|
52367
53328
|
style = after.style || undefined;
|
|
@@ -55306,6 +56267,9 @@ class SheetPlugin extends CorePlugin {
|
|
|
55306
56267
|
};
|
|
55307
56268
|
}
|
|
55308
56269
|
getUnboundedZone(sheetId, zone) {
|
|
56270
|
+
if (zone.bottom === undefined || zone.right === undefined) {
|
|
56271
|
+
return zone;
|
|
56272
|
+
}
|
|
55309
56273
|
const isFullRow = zone.left === 0 && zone.right === this.getNumberCols(sheetId) - 1;
|
|
55310
56274
|
const isFullCol = zone.top === 0 && zone.bottom === this.getNumberRows(sheetId) - 1;
|
|
55311
56275
|
return {
|
|
@@ -59589,66 +60553,25 @@ class EvaluationConditionalFormatPlugin extends UIPlugin {
|
|
|
59589
60553
|
return;
|
|
59590
60554
|
}
|
|
59591
60555
|
const zone = this.getters.getRangeFromSheetXC(sheetId, range).zone;
|
|
59592
|
-
const
|
|
60556
|
+
const colorThresholds = [{ value: minValue, color: rule.minimum.color }];
|
|
59593
60557
|
if (rule.midpoint && midValue) {
|
|
59594
|
-
|
|
59595
|
-
minValue,
|
|
59596
|
-
minColor: rule.minimum.color,
|
|
59597
|
-
colorDiffUnit: this.computeColorDiffUnits(minValue, midValue, rule.minimum.color, rule.midpoint.color),
|
|
59598
|
-
});
|
|
59599
|
-
colorCellArgs.push({
|
|
59600
|
-
minValue: midValue,
|
|
59601
|
-
minColor: rule.midpoint.color,
|
|
59602
|
-
colorDiffUnit: this.computeColorDiffUnits(midValue, maxValue, rule.midpoint.color, rule.maximum.color),
|
|
59603
|
-
});
|
|
59604
|
-
}
|
|
59605
|
-
else {
|
|
59606
|
-
colorCellArgs.push({
|
|
59607
|
-
minValue,
|
|
59608
|
-
minColor: rule.minimum.color,
|
|
59609
|
-
colorDiffUnit: this.computeColorDiffUnits(minValue, maxValue, rule.minimum.color, rule.maximum.color),
|
|
59610
|
-
});
|
|
60558
|
+
colorThresholds.push({ value: midValue, color: rule.midpoint.color });
|
|
59611
60559
|
}
|
|
60560
|
+
colorThresholds.push({ value: maxValue, color: rule.maximum.color });
|
|
60561
|
+
const colorScale = getColorScale(colorThresholds);
|
|
59612
60562
|
for (let row = zone.top; row <= zone.bottom; row++) {
|
|
59613
60563
|
for (let col = zone.left; col <= zone.right; col++) {
|
|
59614
60564
|
const cell = this.getters.getEvaluatedCell({ sheetId, col, row });
|
|
59615
60565
|
if (cell.type === CellValueType.number) {
|
|
59616
60566
|
const value = clip(cell.value, minValue, maxValue);
|
|
59617
|
-
let color;
|
|
59618
|
-
if (colorCellArgs.length === 2 && midValue) {
|
|
59619
|
-
color =
|
|
59620
|
-
value <= midValue
|
|
59621
|
-
? this.colorCell(value, colorCellArgs[0].minValue, colorCellArgs[0].minColor, colorCellArgs[0].colorDiffUnit)
|
|
59622
|
-
: this.colorCell(value, colorCellArgs[1].minValue, colorCellArgs[1].minColor, colorCellArgs[1].colorDiffUnit);
|
|
59623
|
-
}
|
|
59624
|
-
else {
|
|
59625
|
-
color = this.colorCell(value, colorCellArgs[0].minValue, colorCellArgs[0].minColor, colorCellArgs[0].colorDiffUnit);
|
|
59626
|
-
}
|
|
59627
60567
|
if (!computedStyle[col])
|
|
59628
60568
|
computedStyle[col] = [];
|
|
59629
60569
|
computedStyle[col][row] = computedStyle[col]?.[row] || {};
|
|
59630
|
-
computedStyle[col][row].fillColor =
|
|
60570
|
+
computedStyle[col][row].fillColor = colorScale(value);
|
|
59631
60571
|
}
|
|
59632
60572
|
}
|
|
59633
60573
|
}
|
|
59634
60574
|
}
|
|
59635
|
-
computeColorDiffUnits(minValue, maxValue, minColor, maxColor) {
|
|
59636
|
-
const deltaValue = maxValue - minValue;
|
|
59637
|
-
const deltaColorR = ((minColor >> 16) % 256) - ((maxColor >> 16) % 256);
|
|
59638
|
-
const deltaColorG = ((minColor >> 8) % 256) - ((maxColor >> 8) % 256);
|
|
59639
|
-
const deltaColorB = (minColor % 256) - (maxColor % 256);
|
|
59640
|
-
const colorDiffUnitR = deltaColorR / deltaValue;
|
|
59641
|
-
const colorDiffUnitG = deltaColorG / deltaValue;
|
|
59642
|
-
const colorDiffUnitB = deltaColorB / deltaValue;
|
|
59643
|
-
return [colorDiffUnitR, colorDiffUnitG, colorDiffUnitB];
|
|
59644
|
-
}
|
|
59645
|
-
colorCell(value, minValue, minColor, colorDiffUnit) {
|
|
59646
|
-
const [colorDiffUnitR, colorDiffUnitG, colorDiffUnitB] = colorDiffUnit;
|
|
59647
|
-
const r = Math.round(((minColor >> 16) % 256) - colorDiffUnitR * (value - minValue));
|
|
59648
|
-
const g = Math.round(((minColor >> 8) % 256) - colorDiffUnitG * (value - minValue));
|
|
59649
|
-
const b = Math.round((minColor % 256) - colorDiffUnitB * (value - minValue));
|
|
59650
|
-
return (r << 16) | (g << 8) | b;
|
|
59651
|
-
}
|
|
59652
60575
|
/**
|
|
59653
60576
|
* Execute the predicate to know if a conditional formatting rule should be applied to a cell
|
|
59654
60577
|
*/
|
|
@@ -60274,7 +61197,7 @@ function withPivotPresentationLayer (PivotClass) {
|
|
|
60274
61197
|
}
|
|
60275
61198
|
getValuesToAggregate(measure, domain) {
|
|
60276
61199
|
const { rowDomain, colDomain } = domainToColRowDomain(this, domain);
|
|
60277
|
-
const table =
|
|
61200
|
+
const table = super.getTableStructure();
|
|
60278
61201
|
const values = [];
|
|
60279
61202
|
if (colDomain.length === 0 &&
|
|
60280
61203
|
rowDomain.length < this.definition.rows.length &&
|
|
@@ -60708,6 +61631,21 @@ function withPivotPresentationLayer (PivotClass) {
|
|
|
60708
61631
|
}
|
|
60709
61632
|
throw new Error(`Value ${result.value} is not a number`);
|
|
60710
61633
|
}
|
|
61634
|
+
getTableStructure() {
|
|
61635
|
+
const table = super.getTableStructure();
|
|
61636
|
+
this.sortTableStructure(table);
|
|
61637
|
+
return table;
|
|
61638
|
+
}
|
|
61639
|
+
sortTableStructure(table) {
|
|
61640
|
+
if (!this.definition.sortedColumn || table.isSorted) {
|
|
61641
|
+
return;
|
|
61642
|
+
}
|
|
61643
|
+
const measure = this.definition.sortedColumn.measure;
|
|
61644
|
+
const isSortValid = isSortedColumnValid(this.definition.sortedColumn, this);
|
|
61645
|
+
if (isSortValid) {
|
|
61646
|
+
table.sort(measure, this.definition.sortedColumn, (measure, domain) => this._getPivotCellValueAndFormat(measure, domain));
|
|
61647
|
+
}
|
|
61648
|
+
}
|
|
60711
61649
|
}
|
|
60712
61650
|
return PivotPresentationLayer;
|
|
60713
61651
|
}
|
|
@@ -62483,23 +63421,6 @@ class Session extends EventBus {
|
|
|
62483
63421
|
}
|
|
62484
63422
|
}
|
|
62485
63423
|
|
|
62486
|
-
function randomChoice(arr) {
|
|
62487
|
-
return arr[Math.floor(Math.random() * arr.length)];
|
|
62488
|
-
}
|
|
62489
|
-
const colors = [
|
|
62490
|
-
"#ff851b",
|
|
62491
|
-
"#0074d9",
|
|
62492
|
-
"#7fdbff",
|
|
62493
|
-
"#b10dc9",
|
|
62494
|
-
"#39cccc",
|
|
62495
|
-
"#f012be",
|
|
62496
|
-
"#3d9970",
|
|
62497
|
-
"#111111",
|
|
62498
|
-
"#ff4136",
|
|
62499
|
-
"#aaaaaa",
|
|
62500
|
-
"#85144b",
|
|
62501
|
-
"#001f3f",
|
|
62502
|
-
];
|
|
62503
63424
|
class CollaborativePlugin extends UIPlugin {
|
|
62504
63425
|
static getters = [
|
|
62505
63426
|
"getClientsToDisplay",
|
|
@@ -62508,7 +63429,7 @@ class CollaborativePlugin extends UIPlugin {
|
|
|
62508
63429
|
"isFullySynchronized",
|
|
62509
63430
|
];
|
|
62510
63431
|
static layers = ["Selection"];
|
|
62511
|
-
availableColors = new
|
|
63432
|
+
availableColors = new AlternatingColorGenerator(12);
|
|
62512
63433
|
colors = {};
|
|
62513
63434
|
session;
|
|
62514
63435
|
constructor(config) {
|
|
@@ -62519,14 +63440,6 @@ class CollaborativePlugin extends UIPlugin {
|
|
|
62519
63440
|
return (position.row < this.getters.getNumberRows(position.sheetId) &&
|
|
62520
63441
|
position.col < this.getters.getNumberCols(position.sheetId));
|
|
62521
63442
|
}
|
|
62522
|
-
chooseNewColor() {
|
|
62523
|
-
if (this.availableColors.size === 0) {
|
|
62524
|
-
this.availableColors = new Set(colors);
|
|
62525
|
-
}
|
|
62526
|
-
const color = randomChoice([...this.availableColors.values()]);
|
|
62527
|
-
this.availableColors.delete(color);
|
|
62528
|
-
return color;
|
|
62529
|
-
}
|
|
62530
63443
|
getClient() {
|
|
62531
63444
|
return this.session.getClient();
|
|
62532
63445
|
}
|
|
@@ -62561,7 +63474,7 @@ class CollaborativePlugin extends UIPlugin {
|
|
|
62561
63474
|
this.isPositionValid(client.position)) {
|
|
62562
63475
|
const position = client.position;
|
|
62563
63476
|
if (!this.colors[client.id]) {
|
|
62564
|
-
this.colors[client.id] = this.
|
|
63477
|
+
this.colors[client.id] = this.availableColors.next();
|
|
62565
63478
|
}
|
|
62566
63479
|
const color = this.colors[client.id];
|
|
62567
63480
|
clients.push({ ...client, position, color });
|
|
@@ -62798,7 +63711,7 @@ class FormatPlugin extends UIPlugin {
|
|
|
62798
63711
|
for (let row = zone.top; row <= zone.bottom; row++) {
|
|
62799
63712
|
const position = { sheetId, col, row };
|
|
62800
63713
|
const pivotCell = this.getters.getPivotCellFromPosition(position);
|
|
62801
|
-
if (
|
|
63714
|
+
if (this.isSpilledPivotValueFormula(position, pivotCell)) {
|
|
62802
63715
|
measurePositions.push(position);
|
|
62803
63716
|
const pivotId = this.getters.getPivotIdFromPosition(position) || "";
|
|
62804
63717
|
measuresByPivotId[pivotId] ??= new Set();
|
|
@@ -62835,6 +63748,10 @@ class FormatPlugin extends UIPlugin {
|
|
|
62835
63748
|
format,
|
|
62836
63749
|
});
|
|
62837
63750
|
}
|
|
63751
|
+
isSpilledPivotValueFormula(position, pivotCell) {
|
|
63752
|
+
const cell = this.getters.getCell(position);
|
|
63753
|
+
return pivotCell.type === "VALUE" && !cell?.isFormula;
|
|
63754
|
+
}
|
|
62838
63755
|
/**
|
|
62839
63756
|
* This function allows to adjust the quantity of decimal places after a decimal
|
|
62840
63757
|
* point on cells containing number value. It does this by changing the cells
|
|
@@ -62885,6 +63802,69 @@ class FormatPlugin extends UIPlugin {
|
|
|
62885
63802
|
}
|
|
62886
63803
|
}
|
|
62887
63804
|
|
|
63805
|
+
class GeoFeaturePlugin extends UIPlugin {
|
|
63806
|
+
static getters = [
|
|
63807
|
+
"getGeoJsonFeatures",
|
|
63808
|
+
"geoFeatureNameToId",
|
|
63809
|
+
"getGeoChartAvailableRegions",
|
|
63810
|
+
];
|
|
63811
|
+
geoJsonService;
|
|
63812
|
+
geoJsonCache = {};
|
|
63813
|
+
constructor(config) {
|
|
63814
|
+
super(config);
|
|
63815
|
+
this.geoJsonService = config.external.geoJsonService;
|
|
63816
|
+
}
|
|
63817
|
+
getGeoChartAvailableRegions() {
|
|
63818
|
+
if (!this.geoJsonService) {
|
|
63819
|
+
console.error("No geoJsonService provided to the model");
|
|
63820
|
+
return [];
|
|
63821
|
+
}
|
|
63822
|
+
return this.geoJsonService.getAvailableRegions() || [];
|
|
63823
|
+
}
|
|
63824
|
+
getGeoJsonFeatures(region) {
|
|
63825
|
+
if (!this.geoJsonService) {
|
|
63826
|
+
console.error("No geoJsonService provided to the model");
|
|
63827
|
+
return;
|
|
63828
|
+
}
|
|
63829
|
+
const cachedGeoJson = this.geoJsonCache[region];
|
|
63830
|
+
if (cachedGeoJson instanceof Promise) {
|
|
63831
|
+
return undefined;
|
|
63832
|
+
}
|
|
63833
|
+
if (cachedGeoJson !== undefined) {
|
|
63834
|
+
return cachedGeoJson ?? undefined;
|
|
63835
|
+
}
|
|
63836
|
+
this.geoJsonCache[region] = new Promise(async (resolve) => {
|
|
63837
|
+
const json = await this.geoJsonService?.getTopoJson(region);
|
|
63838
|
+
this.geoJsonCache[region] = this.convertToGeoJson(json);
|
|
63839
|
+
this.dispatch("EVALUATE_CHARTS");
|
|
63840
|
+
resolve();
|
|
63841
|
+
});
|
|
63842
|
+
return undefined;
|
|
63843
|
+
}
|
|
63844
|
+
geoFeatureNameToId(region, featureName) {
|
|
63845
|
+
if (!this.geoJsonService) {
|
|
63846
|
+
console.error("No geoJsonService provided to the model");
|
|
63847
|
+
return;
|
|
63848
|
+
}
|
|
63849
|
+
return this.geoJsonService.geoFeatureNameToId(region, featureName);
|
|
63850
|
+
}
|
|
63851
|
+
convertToGeoJson(json) {
|
|
63852
|
+
if (!json) {
|
|
63853
|
+
return null;
|
|
63854
|
+
}
|
|
63855
|
+
// TopoJSON
|
|
63856
|
+
if (json.type === "Topology") {
|
|
63857
|
+
const features = window.ChartGeo.topojson.feature(json, Object.values(json.objects)[0]);
|
|
63858
|
+
return features.type === "FeatureCollection" ? features.features : [features];
|
|
63859
|
+
}
|
|
63860
|
+
// GeoJSON
|
|
63861
|
+
else if (json.type === "FeatureCollection") {
|
|
63862
|
+
return json.features;
|
|
63863
|
+
}
|
|
63864
|
+
throw new Error("Invalid TopoJSON");
|
|
63865
|
+
}
|
|
63866
|
+
}
|
|
63867
|
+
|
|
62888
63868
|
class HeaderVisibilityUIPlugin extends UIPlugin {
|
|
62889
63869
|
static getters = [
|
|
62890
63870
|
"getNextVisibleCellPosition",
|
|
@@ -64422,10 +65402,13 @@ class TableResizeUI extends UIPlugin {
|
|
|
64422
65402
|
case "RESIZE_TABLE": {
|
|
64423
65403
|
const table = this.getters.getCoreTableMatchingTopLeft(cmd.sheetId, cmd.zone);
|
|
64424
65404
|
this.dispatch("UPDATE_TABLE", { ...cmd });
|
|
64425
|
-
if (!table
|
|
65405
|
+
if (!table)
|
|
64426
65406
|
return;
|
|
64427
|
-
const oldTableZone = table.range.zone;
|
|
64428
65407
|
const newTableZone = this.getters.getRangeFromRangeData(cmd.newTableRange).zone;
|
|
65408
|
+
this.selection.selectCell(newTableZone.right, newTableZone.bottom);
|
|
65409
|
+
if (!table.config.automaticAutofill)
|
|
65410
|
+
return;
|
|
65411
|
+
const oldTableZone = table.range.zone;
|
|
64429
65412
|
if (newTableZone.bottom >= oldTableZone.bottom) {
|
|
64430
65413
|
for (let col = newTableZone.left; col <= newTableZone.right; col++) {
|
|
64431
65414
|
const autofillSource = { col, row: oldTableZone.bottom, sheetId: cmd.sheetId };
|
|
@@ -65631,15 +66614,23 @@ class GridSelectionPlugin extends UIPlugin {
|
|
|
65631
66614
|
handler.paste({ zones: pasteTarget, sheetId }, data, { isCutOperation: true });
|
|
65632
66615
|
const toRemove = isBasedBefore ? cmd.elements.map((el) => el + thickness) : cmd.elements;
|
|
65633
66616
|
let currentIndex = cmd.base;
|
|
66617
|
+
const resizingGroups = {};
|
|
65634
66618
|
for (const element of toRemove) {
|
|
65635
66619
|
const size = this.getters.getHeaderSize(cmd.sheetId, cmd.dimension, element);
|
|
66620
|
+
const currentSize = this.getters.getHeaderSize(cmd.sheetId, cmd.dimension, currentIndex);
|
|
66621
|
+
if (size != currentSize) {
|
|
66622
|
+
resizingGroups[size] ??= [];
|
|
66623
|
+
resizingGroups[size].push(currentIndex);
|
|
66624
|
+
currentIndex += 1;
|
|
66625
|
+
}
|
|
66626
|
+
}
|
|
66627
|
+
for (const size in resizingGroups) {
|
|
65636
66628
|
this.dispatch("RESIZE_COLUMNS_ROWS", {
|
|
65637
66629
|
dimension: cmd.dimension,
|
|
65638
66630
|
sheetId: cmd.sheetId,
|
|
65639
|
-
size,
|
|
65640
|
-
elements: [
|
|
66631
|
+
size: parseInt(size, 10),
|
|
66632
|
+
elements: resizingGroups[size],
|
|
65641
66633
|
});
|
|
65642
|
-
currentIndex += 1;
|
|
65643
66634
|
}
|
|
65644
66635
|
this.dispatch("REMOVE_COLUMNS_ROWS", {
|
|
65645
66636
|
dimension: cmd.dimension,
|
|
@@ -66221,6 +67212,7 @@ class SheetViewPlugin extends UIPlugin {
|
|
|
66221
67212
|
break;
|
|
66222
67213
|
case "DELETE_SHEET":
|
|
66223
67214
|
this.cleanViewports();
|
|
67215
|
+
this.sheetsWithDirtyViewports.delete(cmd.sheetId);
|
|
66224
67216
|
break;
|
|
66225
67217
|
case "ACTIVATE_SHEET":
|
|
66226
67218
|
this.sheetsWithDirtyViewports.add(cmd.sheetIdTo);
|
|
@@ -66871,7 +67863,8 @@ const featurePluginRegistry = new Registry()
|
|
|
66871
67863
|
.add("data_cleanup", DataCleanupPlugin)
|
|
66872
67864
|
.add("table_autofill", TableAutofillPlugin)
|
|
66873
67865
|
.add("table_ui_resize", TableResizeUI)
|
|
66874
|
-
.add("datavalidation_insert", DataValidationInsertionPlugin)
|
|
67866
|
+
.add("datavalidation_insert", DataValidationInsertionPlugin)
|
|
67867
|
+
.add("geo_features", GeoFeaturePlugin);
|
|
66875
67868
|
// Plugins which have a state, but which should not be shared in collaborative
|
|
66876
67869
|
const statefulUIPluginRegistry = new Registry()
|
|
66877
67870
|
.add("selection", GridSelectionPlugin)
|
|
@@ -68615,8 +69608,8 @@ css /* scss */ `
|
|
|
68615
69608
|
.o-topbar-composer {
|
|
68616
69609
|
height: fit-content;
|
|
68617
69610
|
margin-top: -1px;
|
|
69611
|
+
margin-bottom: -1px;
|
|
68618
69612
|
border: 1px solid;
|
|
68619
|
-
z-index: ${ComponentsImportance.TopBarComposer};
|
|
68620
69613
|
font-family: ${DEFAULT_FONT};
|
|
68621
69614
|
|
|
68622
69615
|
.o-composer:empty:not(:focus):not(.active)::before {
|
|
@@ -68674,6 +69667,7 @@ class TopBarComposer extends owl.Component {
|
|
|
68674
69667
|
}
|
|
68675
69668
|
return cssPropertiesToCss({
|
|
68676
69669
|
"border-color": SELECTION_BORDER_COLOR,
|
|
69670
|
+
"z-index": String(ComponentsImportance.TopBarComposer),
|
|
68677
69671
|
});
|
|
68678
69672
|
}
|
|
68679
69673
|
onFocus(selection) {
|
|
@@ -68782,6 +69776,15 @@ css /* scss */ `
|
|
|
68782
69776
|
}
|
|
68783
69777
|
}
|
|
68784
69778
|
|
|
69779
|
+
.irregularity-map {
|
|
69780
|
+
border-top: 1px solid ${SEPARATOR_COLOR};
|
|
69781
|
+
height: ${TOPBAR_TOOLBAR_HEIGHT}px;
|
|
69782
|
+
|
|
69783
|
+
.alert-info {
|
|
69784
|
+
border-left: 3px solid ${ALERT_INFO_BORDER};
|
|
69785
|
+
}
|
|
69786
|
+
}
|
|
69787
|
+
|
|
68785
69788
|
.o-topbar-composer {
|
|
68786
69789
|
flex-grow: 1;
|
|
68787
69790
|
}
|
|
@@ -68875,8 +69878,10 @@ class TopBar extends owl.Component {
|
|
|
68875
69878
|
formatNumberMenuItemSpec = formatNumberMenuItemSpec;
|
|
68876
69879
|
isntToolbarMenu = false;
|
|
68877
69880
|
composerFocusStore;
|
|
69881
|
+
fingerprints;
|
|
68878
69882
|
setup() {
|
|
68879
69883
|
this.composerFocusStore = useStore(ComposerFocusStore);
|
|
69884
|
+
this.fingerprints = useStore(FormulaFingerprintStore);
|
|
68880
69885
|
owl.useExternalListener(window, "click", this.onExternalClick);
|
|
68881
69886
|
owl.onWillStart(() => this.updateCellState());
|
|
68882
69887
|
owl.onWillUpdateProps(() => this.updateCellState());
|
|
@@ -69347,7 +70352,7 @@ class Spreadsheet extends owl.Component {
|
|
|
69347
70352
|
properties["grid-template-rows"] = `auto`;
|
|
69348
70353
|
}
|
|
69349
70354
|
else {
|
|
69350
|
-
properties["grid-template-rows"] =
|
|
70355
|
+
properties["grid-template-rows"] = `max-content auto ${BOTTOMBAR_HEIGHT + 1}px`;
|
|
69351
70356
|
}
|
|
69352
70357
|
properties["grid-template-columns"] = `auto ${this.sidePanel.panelSize}px`;
|
|
69353
70358
|
return cssPropertiesToCss(properties);
|
|
@@ -70425,9 +71430,6 @@ class SelectionStreamProcessorImpl {
|
|
|
70425
71430
|
getBackToDefault() {
|
|
70426
71431
|
this.stream.getBackToDefault();
|
|
70427
71432
|
}
|
|
70428
|
-
getAnchor() {
|
|
70429
|
-
return this.anchor;
|
|
70430
|
-
}
|
|
70431
71433
|
modifyAnchor(anchor, mode, options) {
|
|
70432
71434
|
const sheetId = this.getters.getActiveSheetId();
|
|
70433
71435
|
anchor = {
|
|
@@ -73359,6 +74361,7 @@ class Model extends EventBus {
|
|
|
73359
74361
|
session: this.session,
|
|
73360
74362
|
defaultCurrency: this.config.defaultCurrency,
|
|
73361
74363
|
customColors: this.config.customColors || [],
|
|
74364
|
+
external: this.config.external,
|
|
73362
74365
|
};
|
|
73363
74366
|
}
|
|
73364
74367
|
// ---------------------------------------------------------------------------
|
|
@@ -73590,7 +74593,6 @@ const SPREADSHEET_DIMENSIONS = {
|
|
|
73590
74593
|
MIN_COL_WIDTH,
|
|
73591
74594
|
HEADER_HEIGHT,
|
|
73592
74595
|
HEADER_WIDTH,
|
|
73593
|
-
TOPBAR_HEIGHT,
|
|
73594
74596
|
BOTTOMBAR_HEIGHT,
|
|
73595
74597
|
DEFAULT_CELL_WIDTH,
|
|
73596
74598
|
DEFAULT_CELL_HEIGHT,
|
|
@@ -73831,6 +74833,6 @@ exports.tokenColors = tokenColors;
|
|
|
73831
74833
|
exports.tokenize = tokenize;
|
|
73832
74834
|
|
|
73833
74835
|
|
|
73834
|
-
__info__.version = "18.1.0
|
|
73835
|
-
__info__.date = "2024-12-
|
|
73836
|
-
__info__.hash = "
|
|
74836
|
+
__info__.version = "18.1.0";
|
|
74837
|
+
__info__.date = "2024-12-26T06:37:07.879Z";
|
|
74838
|
+
__info__.hash = "c520e89";
|