@odoo/o-spreadsheet 18.4.0-alpha.3 → 18.4.0-alpha.4
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 +1385 -819
- package/dist/o-spreadsheet.d.ts +359 -307
- package/dist/o-spreadsheet.esm.js +1385 -820
- package/dist/o-spreadsheet.iife.js +1385 -819
- package/dist/o-spreadsheet.iife.min.js +454 -443
- package/dist/o_spreadsheet.xml +82 -55
- package/package.json +1 -1
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* This file is generated by o-spreadsheet build tools. Do not edit it.
|
|
4
4
|
* @see https://github.com/odoo/o-spreadsheet
|
|
5
|
-
* @version 18.4.0-alpha.
|
|
6
|
-
* @date 2025-05-
|
|
7
|
-
* @hash
|
|
5
|
+
* @version 18.4.0-alpha.4
|
|
6
|
+
* @date 2025-05-20T05:57:45.452Z
|
|
7
|
+
* @hash 5c28bca
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
(function (exports, owl) {
|
|
@@ -185,7 +185,7 @@
|
|
|
185
185
|
const ALERT_INFO_BORDER = "#98DBE2";
|
|
186
186
|
const ALERT_INFO_TEXT_COLOR = "#09414A";
|
|
187
187
|
const BADGE_SELECTED_COLOR = "#E6F2F3";
|
|
188
|
-
const CHART_PADDING
|
|
188
|
+
const CHART_PADDING = 20;
|
|
189
189
|
const CHART_PADDING_BOTTOM = 10;
|
|
190
190
|
const CHART_PADDING_TOP = 15;
|
|
191
191
|
const CHART_TITLE_FONT_SIZE = 16;
|
|
@@ -347,6 +347,8 @@
|
|
|
347
347
|
const DEFAULT_SCORECARD_BASELINE_MODE = "difference";
|
|
348
348
|
const DEFAULT_SCORECARD_BASELINE_COLOR_UP = "#43C5B1";
|
|
349
349
|
const DEFAULT_SCORECARD_BASELINE_COLOR_DOWN = "#EA6175";
|
|
350
|
+
const DEFAULT_SCORECARD_KEY_VALUE_FONT_SIZE = 32;
|
|
351
|
+
const DEFAULT_SCORECARD_BASELINE_FONT_SIZE = 16;
|
|
350
352
|
const LINE_FILL_TRANSPARENCY = 0.4;
|
|
351
353
|
const DEFAULT_WINDOW_SIZE = 2;
|
|
352
354
|
// session
|
|
@@ -396,6 +398,7 @@
|
|
|
396
398
|
styleId: "TableStyleMedium5",
|
|
397
399
|
automaticAutofill: false,
|
|
398
400
|
};
|
|
401
|
+
const PIVOT_INDENT = 15;
|
|
399
402
|
const DEFAULT_CURRENCY = {
|
|
400
403
|
symbol: "$",
|
|
401
404
|
position: "before",
|
|
@@ -1637,18 +1640,53 @@
|
|
|
1637
1640
|
let result = 0;
|
|
1638
1641
|
const l = letters.length;
|
|
1639
1642
|
for (let i = 0; i < l; i++) {
|
|
1640
|
-
const
|
|
1641
|
-
const colIndex = charCode >= 65 && charCode <= 90 ? charCode - 64 : charCode - 96;
|
|
1643
|
+
const colIndex = charToNumber(letters[i]);
|
|
1642
1644
|
result = result * 26 + colIndex;
|
|
1643
1645
|
}
|
|
1644
1646
|
return result - 1;
|
|
1645
1647
|
}
|
|
1648
|
+
function charToNumber(char) {
|
|
1649
|
+
const charCode = char.charCodeAt(0);
|
|
1650
|
+
return charCode >= 65 && charCode <= 90 ? charCode - 64 : charCode - 96;
|
|
1651
|
+
}
|
|
1646
1652
|
function isCharALetter(char) {
|
|
1647
1653
|
return (char >= "A" && char <= "Z") || (char >= "a" && char <= "z");
|
|
1648
1654
|
}
|
|
1649
1655
|
function isCharADigit(char) {
|
|
1650
1656
|
return char >= "0" && char <= "9";
|
|
1651
1657
|
}
|
|
1658
|
+
// we limit the max column to 3 letters and max row to 7 digits for performance reasons
|
|
1659
|
+
const MAX_COL = lettersToNumber("ZZZ");
|
|
1660
|
+
const MAX_ROW = 9999998;
|
|
1661
|
+
function consumeSpaces(chars) {
|
|
1662
|
+
while (chars.current === " ") {
|
|
1663
|
+
chars.advanceBy(1);
|
|
1664
|
+
}
|
|
1665
|
+
}
|
|
1666
|
+
function consumeLetters(chars) {
|
|
1667
|
+
if (chars.current === "$")
|
|
1668
|
+
chars.advanceBy(1);
|
|
1669
|
+
if (!chars.current || !isCharALetter(chars.current)) {
|
|
1670
|
+
return -1;
|
|
1671
|
+
}
|
|
1672
|
+
let colCoordinate = 0;
|
|
1673
|
+
while (chars.current && isCharALetter(chars.current)) {
|
|
1674
|
+
colCoordinate = colCoordinate * 26 + charToNumber(chars.shift());
|
|
1675
|
+
}
|
|
1676
|
+
return colCoordinate;
|
|
1677
|
+
}
|
|
1678
|
+
function consumeDigits(chars) {
|
|
1679
|
+
if (chars.current === "$")
|
|
1680
|
+
chars.advanceBy(1);
|
|
1681
|
+
if (!chars.current || !isCharADigit(chars.current)) {
|
|
1682
|
+
return -1;
|
|
1683
|
+
}
|
|
1684
|
+
let num = 0;
|
|
1685
|
+
while (chars.current && isCharADigit(chars.current)) {
|
|
1686
|
+
num = num * 10 + Number(chars.shift());
|
|
1687
|
+
}
|
|
1688
|
+
return num;
|
|
1689
|
+
}
|
|
1652
1690
|
/**
|
|
1653
1691
|
* Convert a "XC" coordinate to cartesian coordinates.
|
|
1654
1692
|
*
|
|
@@ -1659,33 +1697,17 @@
|
|
|
1659
1697
|
* Note: it also accepts lowercase coordinates, but not fixed references
|
|
1660
1698
|
*/
|
|
1661
1699
|
function toCartesian(xc) {
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
// Process letter part
|
|
1667
|
-
if (xc[i] === "$")
|
|
1668
|
-
i++;
|
|
1669
|
-
while (i < xc.length && isCharALetter(xc[i])) {
|
|
1670
|
-
letterPart += xc[i++];
|
|
1671
|
-
}
|
|
1672
|
-
if (letterPart.length === 0 || letterPart.length > 3) {
|
|
1673
|
-
// limit to max 3 letters for performance reasons
|
|
1700
|
+
const chars = new TokenizingChars(xc);
|
|
1701
|
+
consumeSpaces(chars);
|
|
1702
|
+
const letterPart = consumeLetters(chars);
|
|
1703
|
+
if (letterPart === -1 || !chars.current) {
|
|
1674
1704
|
throw new Error(`Invalid cell description: ${xc}`);
|
|
1675
1705
|
}
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
}
|
|
1682
|
-
if (i !== xc.length || numberPart.length === 0 || numberPart.length > 7) {
|
|
1683
|
-
// limit to max 7 numbers for performance reasons
|
|
1684
|
-
throw new Error(`Invalid cell description: ${xc}`);
|
|
1685
|
-
}
|
|
1686
|
-
const col = lettersToNumber(letterPart);
|
|
1687
|
-
const row = Number(numberPart) - 1;
|
|
1688
|
-
if (isNaN(row)) {
|
|
1706
|
+
const num = consumeDigits(chars);
|
|
1707
|
+
consumeSpaces(chars);
|
|
1708
|
+
const col = letterPart - 1;
|
|
1709
|
+
const row = num - 1;
|
|
1710
|
+
if (!chars.isOver() || col > MAX_COL || row > MAX_ROW) {
|
|
1689
1711
|
throw new Error(`Invalid cell description: ${xc}`);
|
|
1690
1712
|
}
|
|
1691
1713
|
return { col, row };
|
|
@@ -5374,67 +5396,6 @@
|
|
|
5374
5396
|
return result;
|
|
5375
5397
|
}
|
|
5376
5398
|
|
|
5377
|
-
/** Reference of a cell (eg. A1, $B$5) */
|
|
5378
|
-
const cellReference = new RegExp(/\$?([A-Z]{1,3})\$?([0-9]{1,7})/, "i");
|
|
5379
|
-
// Same as above, but matches the exact string (nothing before or after)
|
|
5380
|
-
const singleCellReference = new RegExp(/^\$?([A-Z]{1,3})\$?([0-9]{1,7})$/, "i");
|
|
5381
|
-
/** Reference of a column header (eg. A, AB, $A) */
|
|
5382
|
-
const colHeader = new RegExp(/^\$?([A-Z]{1,3})+$/, "i");
|
|
5383
|
-
/** Reference of a row header (eg. 1, $1) */
|
|
5384
|
-
const rowHeader = new RegExp(/^\$?([0-9]{1,7})+$/, "i");
|
|
5385
|
-
/** Reference of a column (eg. A, $CA, Sheet1!B) */
|
|
5386
|
-
const colReference = new RegExp(/^\s*('.+'!|[^']+!)?\$?([A-Z]{1,3})$/, "i");
|
|
5387
|
-
/** Reference of a row (eg. 1, 59, Sheet1!9) */
|
|
5388
|
-
const rowReference = new RegExp(/^\s*('.+'!|[^']+!)?\$?([0-9]{1,7})$/, "i");
|
|
5389
|
-
/** Reference of a normal range or a full row range (eg. A1:B1, 1:$5, $A2:5) */
|
|
5390
|
-
const fullRowXc = /(\$?[A-Z]{1,3})?\$?[0-9]{1,7}\s*:\s*(\$?[A-Z]{1,3})?\$?[0-9]{1,7}\s*/i;
|
|
5391
|
-
/** Reference of a normal range or a column row range (eg. A1:B1, A:$B, $A1:C) */
|
|
5392
|
-
const fullColXc = /\$?[A-Z]{1,3}(\$?[0-9]{1,7})?\s*:\s*\$?[A-Z]{1,3}(\$?[0-9]{1,7})?\s*/i;
|
|
5393
|
-
/** Reference of a cell or a range, it can be a bounded range, a full row or a full column */
|
|
5394
|
-
const rangeReference = new RegExp(/^\s*('.+'!|[^']+!)?/.source +
|
|
5395
|
-
"(" +
|
|
5396
|
-
[cellReference.source, fullRowXc.source, fullColXc.source].join("|") +
|
|
5397
|
-
")" +
|
|
5398
|
-
/$/.source, "i");
|
|
5399
|
-
/**
|
|
5400
|
-
* Return true if the given xc is the reference of a column (e.g. A or AC or Sheet1!A)
|
|
5401
|
-
*/
|
|
5402
|
-
function isColReference(xc) {
|
|
5403
|
-
return colReference.test(xc);
|
|
5404
|
-
}
|
|
5405
|
-
/**
|
|
5406
|
-
* Return true if the given xc is the reference of a column (e.g. 1 or Sheet1!1)
|
|
5407
|
-
*/
|
|
5408
|
-
function isRowReference(xc) {
|
|
5409
|
-
return rowReference.test(xc);
|
|
5410
|
-
}
|
|
5411
|
-
function isColHeader(str) {
|
|
5412
|
-
return colHeader.test(str);
|
|
5413
|
-
}
|
|
5414
|
-
function isRowHeader(str) {
|
|
5415
|
-
return rowHeader.test(str);
|
|
5416
|
-
}
|
|
5417
|
-
/**
|
|
5418
|
-
* Return true if the given xc is the reference of a single cell,
|
|
5419
|
-
* without any specified sheet (e.g. A1)
|
|
5420
|
-
*/
|
|
5421
|
-
function isSingleCellReference(xc) {
|
|
5422
|
-
return singleCellReference.test(xc);
|
|
5423
|
-
}
|
|
5424
|
-
function splitReference(ref) {
|
|
5425
|
-
if (!ref.includes("!")) {
|
|
5426
|
-
return { xc: ref };
|
|
5427
|
-
}
|
|
5428
|
-
const parts = ref.split("!");
|
|
5429
|
-
const xc = parts.pop();
|
|
5430
|
-
const sheetName = getUnquotedSheetName(parts.join("!")) || undefined;
|
|
5431
|
-
return { sheetName, xc };
|
|
5432
|
-
}
|
|
5433
|
-
/** Return a reference SheetName!xc from the given arguments */
|
|
5434
|
-
function getFullReference(sheetName, xc) {
|
|
5435
|
-
return sheetName !== undefined ? `${getCanonicalSymbolName(sheetName)}!${xc}` : xc;
|
|
5436
|
-
}
|
|
5437
|
-
|
|
5438
5399
|
/**
|
|
5439
5400
|
* Convert from a cartesian reference to a Zone
|
|
5440
5401
|
* The range boundaries will be kept in the same order as the
|
|
@@ -5452,63 +5413,55 @@
|
|
|
5452
5413
|
*
|
|
5453
5414
|
*/
|
|
5454
5415
|
function toZoneWithoutBoundaryChanges(xc) {
|
|
5455
|
-
|
|
5456
|
-
|
|
5457
|
-
|
|
5458
|
-
if (
|
|
5459
|
-
|
|
5460
|
-
}
|
|
5461
|
-
|
|
5462
|
-
|
|
5463
|
-
if (xc.includes(":")) {
|
|
5464
|
-
[firstRangePart, secondRangePart] = xc.split(":");
|
|
5465
|
-
firstRangePart = firstRangePart.trim();
|
|
5466
|
-
secondRangePart = secondRangePart.trim();
|
|
5467
|
-
}
|
|
5468
|
-
else {
|
|
5469
|
-
firstRangePart = xc.trim();
|
|
5470
|
-
}
|
|
5416
|
+
const chars = new TokenizingChars(xc);
|
|
5417
|
+
consumeSpaces(chars);
|
|
5418
|
+
const sheetSeparatorIndex = xc.indexOf("!");
|
|
5419
|
+
if (sheetSeparatorIndex !== -1) {
|
|
5420
|
+
chars.advanceBy(sheetSeparatorIndex + 1);
|
|
5421
|
+
}
|
|
5422
|
+
const leftLetters = consumeLetters(chars);
|
|
5423
|
+
const leftNumbers = consumeDigits(chars);
|
|
5471
5424
|
let top, bottom, left, right;
|
|
5472
5425
|
let fullCol = false;
|
|
5473
5426
|
let fullRow = false;
|
|
5474
5427
|
let hasHeader = false;
|
|
5475
|
-
if (
|
|
5476
|
-
left = right =
|
|
5428
|
+
if (leftNumbers === -1) {
|
|
5429
|
+
left = right = leftLetters - 1;
|
|
5477
5430
|
top = bottom = 0;
|
|
5478
5431
|
fullCol = true;
|
|
5479
5432
|
}
|
|
5480
|
-
else if (
|
|
5481
|
-
top = bottom =
|
|
5433
|
+
else if (leftLetters === -1) {
|
|
5434
|
+
top = bottom = leftNumbers - 1;
|
|
5482
5435
|
left = right = 0;
|
|
5483
5436
|
fullRow = true;
|
|
5484
5437
|
}
|
|
5485
5438
|
else {
|
|
5486
|
-
|
|
5487
|
-
|
|
5488
|
-
top = bottom = c.row;
|
|
5439
|
+
left = right = leftLetters - 1;
|
|
5440
|
+
top = bottom = leftNumbers - 1;
|
|
5489
5441
|
hasHeader = true;
|
|
5490
5442
|
}
|
|
5491
|
-
|
|
5492
|
-
|
|
5493
|
-
|
|
5443
|
+
consumeSpaces(chars);
|
|
5444
|
+
if (chars.current === ":") {
|
|
5445
|
+
chars.advanceBy(1);
|
|
5446
|
+
consumeSpaces(chars);
|
|
5447
|
+
const rightLetters = consumeLetters(chars);
|
|
5448
|
+
const rightNumbers = consumeDigits(chars);
|
|
5449
|
+
if (rightNumbers === -1) {
|
|
5450
|
+
right = rightLetters - 1;
|
|
5494
5451
|
fullCol = true;
|
|
5495
5452
|
}
|
|
5496
|
-
else if (
|
|
5497
|
-
bottom =
|
|
5453
|
+
else if (rightLetters === -1) {
|
|
5454
|
+
bottom = rightNumbers - 1;
|
|
5498
5455
|
fullRow = true;
|
|
5499
5456
|
}
|
|
5500
5457
|
else {
|
|
5501
|
-
|
|
5502
|
-
|
|
5503
|
-
bottom = c.row;
|
|
5458
|
+
right = rightLetters - 1;
|
|
5459
|
+
bottom = rightNumbers - 1;
|
|
5504
5460
|
top = fullCol ? bottom : top;
|
|
5505
5461
|
left = fullRow ? right : left;
|
|
5506
5462
|
hasHeader = true;
|
|
5507
5463
|
}
|
|
5508
5464
|
}
|
|
5509
|
-
if (fullCol && fullRow) {
|
|
5510
|
-
throw new Error("Wrong zone xc. The zone cannot be at the same time a full column and a full row");
|
|
5511
|
-
}
|
|
5512
5465
|
const zone = {
|
|
5513
5466
|
top,
|
|
5514
5467
|
left,
|
|
@@ -5537,7 +5490,16 @@
|
|
|
5537
5490
|
*/
|
|
5538
5491
|
function toUnboundedZone(xc) {
|
|
5539
5492
|
const zone = toZoneWithoutBoundaryChanges(xc);
|
|
5540
|
-
|
|
5493
|
+
const orderedZone = reorderZone(zone);
|
|
5494
|
+
const bottom = orderedZone.bottom;
|
|
5495
|
+
const right = orderedZone.right;
|
|
5496
|
+
if ((bottom !== undefined && bottom > MAX_ROW) || (right !== undefined && right > MAX_COL)) {
|
|
5497
|
+
throw new Error(`Range string out of bounds: ${xc}`); // limit the size of the zone for performance
|
|
5498
|
+
}
|
|
5499
|
+
if (bottom === undefined && right === undefined) {
|
|
5500
|
+
throw new Error("Wrong zone xc. The zone cannot be at the same time a full column and a full row");
|
|
5501
|
+
}
|
|
5502
|
+
return orderedZone;
|
|
5541
5503
|
}
|
|
5542
5504
|
/**
|
|
5543
5505
|
* Convert from a cartesian reference to a Zone.
|
|
@@ -6151,6 +6113,67 @@
|
|
|
6151
6113
|
return MIN_DELAY + (MAX_DELAY - MIN_DELAY) * Math.exp(-ACCELERATION * (value - 1));
|
|
6152
6114
|
}
|
|
6153
6115
|
|
|
6116
|
+
/** Reference of a cell (eg. A1, $B$5) */
|
|
6117
|
+
const cellReference = new RegExp(/\$?([A-Z]{1,3})\$?([0-9]{1,7})/, "i");
|
|
6118
|
+
// Same as above, but matches the exact string (nothing before or after)
|
|
6119
|
+
const singleCellReference = new RegExp(/^\$?([A-Z]{1,3})\$?([0-9]{1,7})$/, "i");
|
|
6120
|
+
/** Reference of a column header (eg. A, AB, $A) */
|
|
6121
|
+
const colHeader = new RegExp(/^\$?([A-Z]{1,3})+$/, "i");
|
|
6122
|
+
/** Reference of a row header (eg. 1, $1) */
|
|
6123
|
+
const rowHeader = new RegExp(/^\$?([0-9]{1,7})+$/, "i");
|
|
6124
|
+
/** Reference of a column (eg. A, $CA, Sheet1!B) */
|
|
6125
|
+
const colReference = new RegExp(/^\s*('.+'!|[^']+!)?\$?([A-Z]{1,3})$/, "i");
|
|
6126
|
+
/** Reference of a row (eg. 1, 59, Sheet1!9) */
|
|
6127
|
+
const rowReference = new RegExp(/^\s*('.+'!|[^']+!)?\$?([0-9]{1,7})$/, "i");
|
|
6128
|
+
/** Reference of a normal range or a full row range (eg. A1:B1, 1:$5, $A2:5) */
|
|
6129
|
+
const fullRowXc = /(\$?[A-Z]{1,3})?\$?[0-9]{1,7}\s*:\s*(\$?[A-Z]{1,3})?\$?[0-9]{1,7}\s*/i;
|
|
6130
|
+
/** Reference of a normal range or a column row range (eg. A1:B1, A:$B, $A1:C) */
|
|
6131
|
+
const fullColXc = /\$?[A-Z]{1,3}(\$?[0-9]{1,7})?\s*:\s*\$?[A-Z]{1,3}(\$?[0-9]{1,7})?\s*/i;
|
|
6132
|
+
/** Reference of a cell or a range, it can be a bounded range, a full row or a full column */
|
|
6133
|
+
const rangeReference = new RegExp(/^\s*('.+'!|[^']+!)?/.source +
|
|
6134
|
+
"(" +
|
|
6135
|
+
[cellReference.source, fullRowXc.source, fullColXc.source].join("|") +
|
|
6136
|
+
")" +
|
|
6137
|
+
/$/.source, "i");
|
|
6138
|
+
/**
|
|
6139
|
+
* Return true if the given xc is the reference of a column (e.g. A or AC or Sheet1!A)
|
|
6140
|
+
*/
|
|
6141
|
+
function isColReference(xc) {
|
|
6142
|
+
return colReference.test(xc);
|
|
6143
|
+
}
|
|
6144
|
+
/**
|
|
6145
|
+
* Return true if the given xc is the reference of a column (e.g. 1 or Sheet1!1)
|
|
6146
|
+
*/
|
|
6147
|
+
function isRowReference(xc) {
|
|
6148
|
+
return rowReference.test(xc);
|
|
6149
|
+
}
|
|
6150
|
+
function isColHeader(str) {
|
|
6151
|
+
return colHeader.test(str);
|
|
6152
|
+
}
|
|
6153
|
+
function isRowHeader(str) {
|
|
6154
|
+
return rowHeader.test(str);
|
|
6155
|
+
}
|
|
6156
|
+
/**
|
|
6157
|
+
* Return true if the given xc is the reference of a single cell,
|
|
6158
|
+
* without any specified sheet (e.g. A1)
|
|
6159
|
+
*/
|
|
6160
|
+
function isSingleCellReference(xc) {
|
|
6161
|
+
return singleCellReference.test(xc);
|
|
6162
|
+
}
|
|
6163
|
+
function splitReference(ref) {
|
|
6164
|
+
if (!ref.includes("!")) {
|
|
6165
|
+
return { xc: ref };
|
|
6166
|
+
}
|
|
6167
|
+
const parts = ref.split("!");
|
|
6168
|
+
const xc = parts.pop();
|
|
6169
|
+
const sheetName = getUnquotedSheetName(parts.join("!")) || undefined;
|
|
6170
|
+
return { sheetName, xc };
|
|
6171
|
+
}
|
|
6172
|
+
/** Return a reference SheetName!xc from the given arguments */
|
|
6173
|
+
function getFullReference(sheetName, xc) {
|
|
6174
|
+
return sheetName !== undefined ? `${getCanonicalSymbolName(sheetName)}!${xc}` : xc;
|
|
6175
|
+
}
|
|
6176
|
+
|
|
6154
6177
|
function createDefaultRows(rowNumber) {
|
|
6155
6178
|
const rows = [];
|
|
6156
6179
|
for (let i = 0; i < rowNumber; i++) {
|
|
@@ -6847,9 +6870,6 @@
|
|
|
6847
6870
|
}
|
|
6848
6871
|
return fontSize;
|
|
6849
6872
|
}
|
|
6850
|
-
function computeIconWidth(style) {
|
|
6851
|
-
return computeTextFontSizeInPixels(style) + 2 * MIN_CF_ICON_MARGIN;
|
|
6852
|
-
}
|
|
6853
6873
|
/** Transform a string to lower case. If the string is undefined, return an empty string */
|
|
6854
6874
|
function toLowerCase(str) {
|
|
6855
6875
|
return str ? str.toLowerCase() : "";
|
|
@@ -8083,208 +8103,6 @@
|
|
|
8083
8103
|
return values;
|
|
8084
8104
|
}
|
|
8085
8105
|
|
|
8086
|
-
const PREVIOUS_VALUE = "(previous)";
|
|
8087
|
-
const NEXT_VALUE = "(next)";
|
|
8088
|
-
function getDomainOfParentRow(pivot, domain) {
|
|
8089
|
-
const { colDomain, rowDomain } = domainToColRowDomain(pivot, domain);
|
|
8090
|
-
return [...colDomain, ...rowDomain.slice(0, rowDomain.length - 1)];
|
|
8091
|
-
}
|
|
8092
|
-
function getDomainOfParentCol(pivot, domain) {
|
|
8093
|
-
const { colDomain, rowDomain } = domainToColRowDomain(pivot, domain);
|
|
8094
|
-
return [...colDomain.slice(0, colDomain.length - 1), ...rowDomain];
|
|
8095
|
-
}
|
|
8096
|
-
/**
|
|
8097
|
-
* Split a pivot domain into the part related to the rows of the pivot, and the part related to the columns.
|
|
8098
|
-
*/
|
|
8099
|
-
function domainToColRowDomain(pivot, domain) {
|
|
8100
|
-
const rowFields = pivot.definition.rows.map((c) => c.nameWithGranularity);
|
|
8101
|
-
const rowDomain = domain.filter((node) => rowFields.includes(node.field));
|
|
8102
|
-
const columnFields = pivot.definition.columns.map((c) => c.nameWithGranularity);
|
|
8103
|
-
const colDomain = domain.filter((node) => columnFields.includes(node.field));
|
|
8104
|
-
return { colDomain, rowDomain };
|
|
8105
|
-
}
|
|
8106
|
-
function getDimensionDomain(pivot, dimension, domain) {
|
|
8107
|
-
return dimension === "column"
|
|
8108
|
-
? domainToColRowDomain(pivot, domain).colDomain
|
|
8109
|
-
: domainToColRowDomain(pivot, domain).rowDomain;
|
|
8110
|
-
}
|
|
8111
|
-
function getFieldValueInDomain(fieldNameWithGranularity, domain) {
|
|
8112
|
-
const node = domain.find((n) => n.field === fieldNameWithGranularity);
|
|
8113
|
-
return node?.value;
|
|
8114
|
-
}
|
|
8115
|
-
function isDomainIsInPivot(pivot, domain) {
|
|
8116
|
-
const { rowDomain, colDomain } = domainToColRowDomain(pivot, domain);
|
|
8117
|
-
return (checkIfDomainInInTree(rowDomain, pivot.getTableStructure().getRowTree()) &&
|
|
8118
|
-
checkIfDomainInInTree(colDomain, pivot.getTableStructure().getColTree()));
|
|
8119
|
-
}
|
|
8120
|
-
function checkIfDomainInInTree(domain, tree) {
|
|
8121
|
-
return walkDomainTree(domain, tree) !== undefined;
|
|
8122
|
-
}
|
|
8123
|
-
/**
|
|
8124
|
-
* Given a tree of the col/rows of a pivot, and a domain related to those col/rows, return the node of the tree
|
|
8125
|
-
* corresponding to the domain.
|
|
8126
|
-
*
|
|
8127
|
-
* @param domain The domain to find in the tree
|
|
8128
|
-
* @param tree The tree to search in7
|
|
8129
|
-
* @param stopAtField If provided, the search will stop at the field with this name
|
|
8130
|
-
*/
|
|
8131
|
-
function walkDomainTree(domain, tree, stopAtField) {
|
|
8132
|
-
let currentTreeNode = tree;
|
|
8133
|
-
for (const node of domain) {
|
|
8134
|
-
const child = currentTreeNode.find((n) => n.value === node.value);
|
|
8135
|
-
if (!child) {
|
|
8136
|
-
return undefined;
|
|
8137
|
-
}
|
|
8138
|
-
if (child.field === stopAtField) {
|
|
8139
|
-
return currentTreeNode;
|
|
8140
|
-
}
|
|
8141
|
-
currentTreeNode = child.children;
|
|
8142
|
-
}
|
|
8143
|
-
return currentTreeNode;
|
|
8144
|
-
}
|
|
8145
|
-
/**
|
|
8146
|
-
* Get the domain parent of the given domain with the field `parentFieldName` as leaf of the domain.
|
|
8147
|
-
*
|
|
8148
|
-
* In practice, if the `parentFieldName` is a row in the pivot, the helper will return a domain with the same column
|
|
8149
|
-
* domain, and with a row domain all groupBys children to `parentFieldName` removed.
|
|
8150
|
-
*/
|
|
8151
|
-
function getFieldParentDomain(pivot, parentFieldName, domain) {
|
|
8152
|
-
let { rowDomain, colDomain } = domainToColRowDomain(pivot, domain);
|
|
8153
|
-
const dimension = getFieldDimensionType(pivot, parentFieldName);
|
|
8154
|
-
if (dimension === "row") {
|
|
8155
|
-
const index = rowDomain.findIndex((node) => node.field === parentFieldName);
|
|
8156
|
-
if (index === -1) {
|
|
8157
|
-
return domain;
|
|
8158
|
-
}
|
|
8159
|
-
rowDomain = rowDomain.slice(0, index + 1);
|
|
8160
|
-
}
|
|
8161
|
-
else {
|
|
8162
|
-
const index = colDomain.findIndex((node) => node.field === parentFieldName);
|
|
8163
|
-
if (index === -1) {
|
|
8164
|
-
return domain;
|
|
8165
|
-
}
|
|
8166
|
-
colDomain = colDomain.slice(0, index + 1);
|
|
8167
|
-
}
|
|
8168
|
-
return [...rowDomain, ...colDomain];
|
|
8169
|
-
}
|
|
8170
|
-
/**
|
|
8171
|
-
* Replace in the domain the value of the field `fieldNameWithGranularity` with the given `value`
|
|
8172
|
-
*/
|
|
8173
|
-
function replaceFieldValueInDomain(domain, fieldNameWithGranularity, value) {
|
|
8174
|
-
domain = deepCopy(domain);
|
|
8175
|
-
const node = domain.find((n) => n.field === fieldNameWithGranularity);
|
|
8176
|
-
if (!node) {
|
|
8177
|
-
return domain;
|
|
8178
|
-
}
|
|
8179
|
-
node.value = value;
|
|
8180
|
-
return domain;
|
|
8181
|
-
}
|
|
8182
|
-
function isFieldInDomain(nameWithGranularity, domain) {
|
|
8183
|
-
return domain.some((node) => node.field === nameWithGranularity);
|
|
8184
|
-
}
|
|
8185
|
-
/**
|
|
8186
|
-
* Check if the field is in the rows or columns of the pivot
|
|
8187
|
-
*/
|
|
8188
|
-
function getFieldDimensionType(pivot, nameWithGranularity) {
|
|
8189
|
-
const rowFields = pivot.definition.rows.map((c) => c.nameWithGranularity);
|
|
8190
|
-
if (rowFields.includes(nameWithGranularity)) {
|
|
8191
|
-
return "row";
|
|
8192
|
-
}
|
|
8193
|
-
const columnFields = pivot.definition.columns.map((c) => c.nameWithGranularity);
|
|
8194
|
-
if (columnFields.includes(nameWithGranularity)) {
|
|
8195
|
-
return "column";
|
|
8196
|
-
}
|
|
8197
|
-
throw new Error(`Field ${nameWithGranularity} not found in pivot`);
|
|
8198
|
-
}
|
|
8199
|
-
/**
|
|
8200
|
-
* Replace in the given domain the value of the field `fieldNameWithGranularity` with the previous or next value.
|
|
8201
|
-
*/
|
|
8202
|
-
function getPreviousOrNextValueDomain(pivot, domain, fieldNameWithGranularity, direction) {
|
|
8203
|
-
const dimension = getFieldDimensionType(pivot, fieldNameWithGranularity);
|
|
8204
|
-
const tree = dimension === "row"
|
|
8205
|
-
? pivot.getTableStructure().getRowTree()
|
|
8206
|
-
: pivot.getTableStructure().getColTree();
|
|
8207
|
-
const dimDomain = getDimensionDomain(pivot, dimension, domain);
|
|
8208
|
-
const currentTreeNode = walkDomainTree(dimDomain, tree, fieldNameWithGranularity);
|
|
8209
|
-
const values = currentTreeNode?.map((n) => n.value) ?? [];
|
|
8210
|
-
const value = getFieldValueInDomain(fieldNameWithGranularity, domain);
|
|
8211
|
-
if (value === undefined) {
|
|
8212
|
-
return undefined;
|
|
8213
|
-
}
|
|
8214
|
-
const valueIndex = values.indexOf(value);
|
|
8215
|
-
if (value === undefined || valueIndex === -1) {
|
|
8216
|
-
return undefined;
|
|
8217
|
-
}
|
|
8218
|
-
const offset = direction === PREVIOUS_VALUE ? -1 : 1;
|
|
8219
|
-
const newIndex = clip(valueIndex + offset, 0, values.length - 1);
|
|
8220
|
-
return replaceFieldValueInDomain(domain, fieldNameWithGranularity, values[newIndex]);
|
|
8221
|
-
}
|
|
8222
|
-
function domainToString(domain) {
|
|
8223
|
-
return domain ? domain.map(domainNodeToString).join(", ") : "";
|
|
8224
|
-
}
|
|
8225
|
-
function domainNodeToString(domainNode) {
|
|
8226
|
-
return domainNode ? `${domainNode.field}=${domainNode.value}` : "";
|
|
8227
|
-
}
|
|
8228
|
-
/**
|
|
8229
|
-
*
|
|
8230
|
-
* For the ranking, the pivot cell values of a column (or row) at the same depth are grouped together before being sorted
|
|
8231
|
-
* and ranked.
|
|
8232
|
-
*
|
|
8233
|
-
* The grouping of a pivot cell is done with both the value of the domain nodes that are parent of the field
|
|
8234
|
-
* `fieldNameWithGranularity` and the value of the last node of the domain of the pivot cell, if it's not the field
|
|
8235
|
-
* `fieldNameWithGranularity`.
|
|
8236
|
-
*
|
|
8237
|
-
* For example, let's take a pivot grouped by (Date:year, Stage, User, Product), where we want to rank by "Stage" field.
|
|
8238
|
-
* The domain nodes parents of the "Stage" are [Date:year]. The pivot cell with domain:
|
|
8239
|
-
* - [Date:year=2021] is not ranked because it does not contain the "Stage" field
|
|
8240
|
-
* - [Date:year=2021, Stage=Lead] is grouped with the cells [Date:year=2021, Stage=*, User=None, Product=None],
|
|
8241
|
-
* and then ranked within the group
|
|
8242
|
-
* - [Date:year=2021, Stage=Lead, User=Bob] is grouped with the cells [Date:year=2021, Stage=*, User=Bob, Product=None],
|
|
8243
|
-
* and then ranked within the group
|
|
8244
|
-
* - [Date:year=2021, Stage=Lead, User=Bob, Product=Table] is grouped with the cells [Date:year=2021, Stage=*, User=*, Product=Table],
|
|
8245
|
-
* and then ranked within the group
|
|
8246
|
-
*
|
|
8247
|
-
* If we rank the pivot on "User" instead, the parent domain becomes [Date:year, Sage] .The cell with domain:
|
|
8248
|
-
* - [Date:year=2021] is not ranked because it does not contain the "Stage" field
|
|
8249
|
-
* - [Date:year=2021, Stage=Lead] is not ranked because it does not contain the "User" field
|
|
8250
|
-
* - [Date:year=2021, Stage=Lead, User=Bob] is grouped with the cells [Date:year=2021, Stage=Lead, User=Bob, Product=None],
|
|
8251
|
-
* and then ranked within the group
|
|
8252
|
-
* - [Date:year=2021, Stage=Lead, User=Bob, Product=Table] is grouped with the cells with [Date:year=2021, Stage=Lead, User=*, Product=Table],
|
|
8253
|
-
* and then ranked within the group
|
|
8254
|
-
*
|
|
8255
|
-
*/
|
|
8256
|
-
function getRankingDomainKey(domain, fieldNameWithGranularity) {
|
|
8257
|
-
const index = domain.findIndex((node) => node.field === fieldNameWithGranularity);
|
|
8258
|
-
if (index === -1) {
|
|
8259
|
-
return "";
|
|
8260
|
-
}
|
|
8261
|
-
const parent = domain.slice(0, index);
|
|
8262
|
-
const lastNode = domain.at(-1);
|
|
8263
|
-
return domainToString(lastNode.field === fieldNameWithGranularity ? parent : [...parent, lastNode]);
|
|
8264
|
-
}
|
|
8265
|
-
/**
|
|
8266
|
-
* The running total domain is the domain without the field `fieldNameWithGranularity`, ie. we do the running total of
|
|
8267
|
-
* all the pivot cells of the column that have any value for the field `fieldNameWithGranularity` and the same value for
|
|
8268
|
-
* the other fields.
|
|
8269
|
-
*/
|
|
8270
|
-
function getRunningTotalDomainKey(domain, fieldNameWithGranularity) {
|
|
8271
|
-
const index = domain.findIndex((node) => node.field === fieldNameWithGranularity);
|
|
8272
|
-
if (index === -1) {
|
|
8273
|
-
return "";
|
|
8274
|
-
}
|
|
8275
|
-
return domainToString([...domain.slice(0, index), ...domain.slice(index + 1)]);
|
|
8276
|
-
}
|
|
8277
|
-
function sortPivotTree(tree, baseDomain, sortFn) {
|
|
8278
|
-
const sortedTree = [...tree];
|
|
8279
|
-
const domain = [...baseDomain];
|
|
8280
|
-
sortedTree.sort((node1, node2) => sortFn([...domain, node1], [...domain, node2]));
|
|
8281
|
-
for (const node of tree) {
|
|
8282
|
-
const children = sortPivotTree(node.children, [...domain, node], sortFn);
|
|
8283
|
-
node.children = children;
|
|
8284
|
-
}
|
|
8285
|
-
return sortedTree;
|
|
8286
|
-
}
|
|
8287
|
-
|
|
8288
8106
|
const pivotTimeAdapterRegistry = new Registry();
|
|
8289
8107
|
function pivotTimeAdapter(granularity) {
|
|
8290
8108
|
return pivotTimeAdapterRegistry.get(granularity);
|
|
@@ -8795,23 +8613,11 @@
|
|
|
8795
8613
|
function getFieldDisplayName(field) {
|
|
8796
8614
|
return field.displayName + (field.granularity ? ` (${ALL_PERIODS[field.granularity]})` : "");
|
|
8797
8615
|
}
|
|
8798
|
-
function
|
|
8799
|
-
|
|
8800
|
-
if (rowDomain.length === 0 && colDomain.length === 0) {
|
|
8616
|
+
function addAlignFormatToPivotHeader(domain, functionResult) {
|
|
8617
|
+
if (domain.length === 0) {
|
|
8801
8618
|
return functionResult;
|
|
8802
8619
|
}
|
|
8803
|
-
|
|
8804
|
-
return {
|
|
8805
|
-
...functionResult,
|
|
8806
|
-
format: (functionResult.format || "@") + "* ",
|
|
8807
|
-
};
|
|
8808
|
-
}
|
|
8809
|
-
const indent = rowDomain.length - 1;
|
|
8810
|
-
const format = functionResult.format || "@";
|
|
8811
|
-
return {
|
|
8812
|
-
...functionResult,
|
|
8813
|
-
format: `${" ".repeat(indent)}${format}* `,
|
|
8814
|
-
};
|
|
8620
|
+
return { ...functionResult, format: (functionResult.format || "@") + "* " };
|
|
8815
8621
|
}
|
|
8816
8622
|
function isSortedColumnValid(sortedColumn, pivot) {
|
|
8817
8623
|
try {
|
|
@@ -9842,6 +9648,13 @@
|
|
|
9842
9648
|
resetStores() {
|
|
9843
9649
|
this.dependencies.clear();
|
|
9844
9650
|
}
|
|
9651
|
+
dispose() {
|
|
9652
|
+
for (const instance of this.dependencies.values()) {
|
|
9653
|
+
if ("dispose" in instance && typeof instance.dispose === "function") {
|
|
9654
|
+
instance.dispose();
|
|
9655
|
+
}
|
|
9656
|
+
}
|
|
9657
|
+
}
|
|
9845
9658
|
}
|
|
9846
9659
|
class StoreFactory {
|
|
9847
9660
|
get;
|
|
@@ -9920,6 +9733,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
9920
9733
|
return proxifyStoreMutation(store, () => container.trigger("store-updated"));
|
|
9921
9734
|
},
|
|
9922
9735
|
});
|
|
9736
|
+
owl.onWillUnmount(() => container.dispose());
|
|
9923
9737
|
return container;
|
|
9924
9738
|
}
|
|
9925
9739
|
/**
|
|
@@ -10273,6 +10087,19 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
10273
10087
|
}
|
|
10274
10088
|
}
|
|
10275
10089
|
|
|
10090
|
+
class ChartAnimationStore extends SpreadsheetStore {
|
|
10091
|
+
mutators = ["disableAnimationForChart", "enableAnimationForChart"];
|
|
10092
|
+
animationPlayed = {};
|
|
10093
|
+
disableAnimationForChart(chartId, chartType) {
|
|
10094
|
+
this.animationPlayed[chartId] = chartType;
|
|
10095
|
+
return "noStateChange";
|
|
10096
|
+
}
|
|
10097
|
+
enableAnimationForChart(chartId) {
|
|
10098
|
+
this.animationPlayed[chartId] = undefined;
|
|
10099
|
+
return "noStateChange";
|
|
10100
|
+
}
|
|
10101
|
+
}
|
|
10102
|
+
|
|
10276
10103
|
function getFunnelChartController() {
|
|
10277
10104
|
return class FunnelChartController extends window.Chart.BarController {
|
|
10278
10105
|
static id = "funnel";
|
|
@@ -11926,10 +11753,12 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
11926
11753
|
static template = "o-spreadsheet-ChartJsComponent";
|
|
11927
11754
|
static props = {
|
|
11928
11755
|
figureUI: Object,
|
|
11756
|
+
isFullScreen: { type: Boolean, optional: true },
|
|
11929
11757
|
};
|
|
11930
11758
|
canvas = owl.useRef("graphContainer");
|
|
11931
11759
|
chart;
|
|
11932
11760
|
currentRuntime;
|
|
11761
|
+
animationStore;
|
|
11933
11762
|
currentDevicePixelRatio = window.devicePixelRatio;
|
|
11934
11763
|
get background() {
|
|
11935
11764
|
return this.chartRuntime.background;
|
|
@@ -11945,6 +11774,9 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
11945
11774
|
return runtime;
|
|
11946
11775
|
}
|
|
11947
11776
|
setup() {
|
|
11777
|
+
if (this.env.model.getters.isDashboard()) {
|
|
11778
|
+
this.animationStore = useStore(ChartAnimationStore);
|
|
11779
|
+
}
|
|
11948
11780
|
owl.onMounted(() => {
|
|
11949
11781
|
const runtime = this.chartRuntime;
|
|
11950
11782
|
this.currentRuntime = runtime;
|
|
@@ -11971,11 +11803,25 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
11971
11803
|
});
|
|
11972
11804
|
}
|
|
11973
11805
|
createChart(chartData) {
|
|
11806
|
+
if (this.env.model.getters.isDashboard() && this.animationStore) {
|
|
11807
|
+
const chartType = this.env.model.getters.getChart(this.props.figureUI.id)?.type;
|
|
11808
|
+
if (chartType && this.animationStore.animationPlayed[this.animationFigureId] !== chartType) {
|
|
11809
|
+
chartData = this.enableAnimationInChartData(chartData);
|
|
11810
|
+
this.animationStore.disableAnimationForChart(this.animationFigureId, chartType);
|
|
11811
|
+
}
|
|
11812
|
+
}
|
|
11974
11813
|
const canvas = this.canvas.el;
|
|
11975
11814
|
const ctx = canvas.getContext("2d");
|
|
11976
11815
|
this.chart = new window.Chart(ctx, chartData);
|
|
11977
11816
|
}
|
|
11978
11817
|
updateChartJs(chartData) {
|
|
11818
|
+
if (this.env.model.getters.isDashboard()) {
|
|
11819
|
+
const chartType = this.env.model.getters.getChart(this.props.figureUI.id)?.type;
|
|
11820
|
+
if (chartType && this.hasChartDataChanged() && this.animationStore) {
|
|
11821
|
+
chartData = this.enableAnimationInChartData(chartData);
|
|
11822
|
+
this.animationStore.disableAnimationForChart(this.animationFigureId, chartType);
|
|
11823
|
+
}
|
|
11824
|
+
}
|
|
11979
11825
|
if (chartData.data && chartData.data.datasets) {
|
|
11980
11826
|
this.chart.data = chartData.data;
|
|
11981
11827
|
if (chartData.options?.plugins?.title) {
|
|
@@ -11988,6 +11834,20 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
11988
11834
|
this.chart.config.options = chartData.options;
|
|
11989
11835
|
this.chart.update();
|
|
11990
11836
|
}
|
|
11837
|
+
hasChartDataChanged() {
|
|
11838
|
+
return !deepEquals(this.currentRuntime.chartJsConfig.data, this.chartRuntime.chartJsConfig.data);
|
|
11839
|
+
}
|
|
11840
|
+
enableAnimationInChartData(chartData) {
|
|
11841
|
+
return {
|
|
11842
|
+
...chartData,
|
|
11843
|
+
options: { ...chartData.options, animation: { animateRotate: true } },
|
|
11844
|
+
};
|
|
11845
|
+
}
|
|
11846
|
+
get animationFigureId() {
|
|
11847
|
+
return this.props.isFullScreen
|
|
11848
|
+
? this.props.figureUI.id + "-fullscreen"
|
|
11849
|
+
: this.props.figureUI.id;
|
|
11850
|
+
}
|
|
11991
11851
|
}
|
|
11992
11852
|
|
|
11993
11853
|
/**
|
|
@@ -12113,6 +11973,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
12113
11973
|
const arrowUpPath = new window.Path2D("M8.7 5.5a.5.5 0 0 0 0-.75l-3.8-4a.5.5 0 0 0-.75 0l-3.8 4a.5.5 0 0 0 0 .75l.4.4a.5.5 0 0 0 .75 0l2.3-2.4v5.8c0 .25.25.5.5.5h.6c.25 0 .5-.25.5-.5v-5.8l2.2 2.4a.5.5 0 0 0 .75 0z");
|
|
12114
11974
|
let ScorecardChart$1 = class ScorecardChart extends AbstractChart {
|
|
12115
11975
|
keyValue;
|
|
11976
|
+
keyDescr;
|
|
12116
11977
|
baseline;
|
|
12117
11978
|
baselineMode;
|
|
12118
11979
|
baselineDescr;
|
|
@@ -12126,6 +11987,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
12126
11987
|
constructor(definition, sheetId, getters) {
|
|
12127
11988
|
super(definition, sheetId, getters);
|
|
12128
11989
|
this.keyValue = createValidRange(getters, sheetId, definition.keyValue);
|
|
11990
|
+
this.keyDescr = definition.keyDescr;
|
|
12129
11991
|
this.baseline = createValidRange(getters, sheetId, definition.baseline);
|
|
12130
11992
|
this.baselineMode = definition.baselineMode;
|
|
12131
11993
|
this.baselineDescr = definition.baselineDescr;
|
|
@@ -12203,6 +12065,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
12203
12065
|
keyValue: keyValue
|
|
12204
12066
|
? this.getters.getRangeString(keyValue, targetSheetId || this.sheetId)
|
|
12205
12067
|
: undefined,
|
|
12068
|
+
keyDescr: this.keyDescr,
|
|
12206
12069
|
humanize: this.humanize,
|
|
12207
12070
|
};
|
|
12208
12071
|
}
|
|
@@ -12226,7 +12089,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
12226
12089
|
canvas.width = dpr * structure.canvas.width;
|
|
12227
12090
|
canvas.height = dpr * structure.canvas.height;
|
|
12228
12091
|
ctx.scale(dpr, dpr);
|
|
12229
|
-
const availableWidth = structure.canvas.width - CHART_PADDING
|
|
12092
|
+
const availableWidth = structure.canvas.width - CHART_PADDING;
|
|
12230
12093
|
ctx.fillStyle = structure.canvas.backgroundColor;
|
|
12231
12094
|
ctx.fillRect(0, 0, structure.canvas.width, structure.canvas.height);
|
|
12232
12095
|
if (structure.title) {
|
|
@@ -12262,18 +12125,22 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
12262
12125
|
ctx.restore();
|
|
12263
12126
|
}
|
|
12264
12127
|
if (structure.baselineDescr) {
|
|
12265
|
-
const descr = structure.baselineDescr
|
|
12128
|
+
const descr = structure.baselineDescr;
|
|
12266
12129
|
ctx.font = descr.style.font;
|
|
12267
12130
|
ctx.fillStyle = descr.style.color;
|
|
12268
|
-
|
|
12269
|
-
ctx.fillText(clipTextWithEllipsis(ctx, description.text, availableWidth - description.position.x), description.position.x, description.position.y);
|
|
12270
|
-
}
|
|
12131
|
+
ctx.fillText(clipTextWithEllipsis(ctx, descr.text, availableWidth - descr.position.x), descr.position.x, descr.position.y);
|
|
12271
12132
|
}
|
|
12272
12133
|
if (structure.key) {
|
|
12273
12134
|
ctx.font = structure.key.style.font;
|
|
12274
12135
|
ctx.fillStyle = structure.key.style.color;
|
|
12275
12136
|
drawDecoratedText(ctx, clipTextWithEllipsis(ctx, structure.key.text, availableWidth - structure.key.position.x), structure.key.position, structure.key.style.underline, structure.key.style.strikethrough);
|
|
12276
12137
|
}
|
|
12138
|
+
if (structure.keyDescr) {
|
|
12139
|
+
const descr = structure.keyDescr;
|
|
12140
|
+
ctx.font = structure.keyDescr?.style.font ?? descr.style.font;
|
|
12141
|
+
ctx.fillStyle = descr.style.color;
|
|
12142
|
+
ctx.fillText(clipTextWithEllipsis(ctx, descr.text, availableWidth - descr.position.x), descr.position.x, descr.position.y);
|
|
12143
|
+
}
|
|
12277
12144
|
if (structure.progressBar) {
|
|
12278
12145
|
ctx.fillStyle = structure.progressBar.style.backgroundColor;
|
|
12279
12146
|
ctx.beginPath();
|
|
@@ -12328,28 +12195,41 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
12328
12195
|
text: chart.title.text ? _t(chart.title.text) : "",
|
|
12329
12196
|
},
|
|
12330
12197
|
keyValue: formattedKeyValue,
|
|
12198
|
+
keyDescr: chart.keyDescr?.text
|
|
12199
|
+
? _t(chart.keyDescr.text) // descriptions are extracted from .json files and they are translated at runtime here
|
|
12200
|
+
: "",
|
|
12331
12201
|
baselineDisplay,
|
|
12332
12202
|
baselineArrow: getBaselineArrowDirection(baselineCell, keyValueCell, chart.baselineMode),
|
|
12333
12203
|
baselineColor: getBaselineColor(baselineCell, chart.baselineMode, keyValueCell, chart.baselineColorUp, chart.baselineColorDown),
|
|
12334
|
-
baselineDescr: chart.baselineMode !== "progress" && chart.baselineDescr
|
|
12335
|
-
? _t(chart.baselineDescr) // descriptions are extracted from .json files and they are translated at runtime here
|
|
12204
|
+
baselineDescr: chart.baselineMode !== "progress" && chart.baselineDescr?.text
|
|
12205
|
+
? _t(chart.baselineDescr.text) // descriptions are extracted from .json files and they are translated at runtime here
|
|
12336
12206
|
: "",
|
|
12337
12207
|
fontColor,
|
|
12338
12208
|
background,
|
|
12339
|
-
baselineStyle:
|
|
12340
|
-
|
|
12341
|
-
|
|
12342
|
-
|
|
12343
|
-
|
|
12344
|
-
|
|
12345
|
-
|
|
12346
|
-
|
|
12347
|
-
|
|
12348
|
-
|
|
12349
|
-
|
|
12350
|
-
|
|
12351
|
-
|
|
12352
|
-
|
|
12209
|
+
baselineStyle: {
|
|
12210
|
+
...(chart.baselineMode !== "percentage" && chart.baselineMode !== "progress" && baseline
|
|
12211
|
+
? getters.getCellComputedStyle({
|
|
12212
|
+
sheetId: baseline.sheetId,
|
|
12213
|
+
col: baseline.zone.left,
|
|
12214
|
+
row: baseline.zone.top,
|
|
12215
|
+
})
|
|
12216
|
+
: undefined),
|
|
12217
|
+
fontSize: chart.baselineDescr?.fontSize,
|
|
12218
|
+
align: chart.baselineDescr?.align,
|
|
12219
|
+
},
|
|
12220
|
+
baselineDescrStyle: { textColor: chart.baselineDescr?.color, ...chart.baselineDescr },
|
|
12221
|
+
keyValueStyle: {
|
|
12222
|
+
...(chart.keyValue
|
|
12223
|
+
? getters.getCellComputedStyle({
|
|
12224
|
+
sheetId: chart.keyValue.sheetId,
|
|
12225
|
+
col: chart.keyValue.zone.left,
|
|
12226
|
+
row: chart.keyValue.zone.top,
|
|
12227
|
+
})
|
|
12228
|
+
: undefined),
|
|
12229
|
+
fontSize: chart.keyDescr?.fontSize,
|
|
12230
|
+
align: chart.keyDescr?.align,
|
|
12231
|
+
},
|
|
12232
|
+
keyValueDescrStyle: { textColor: chart.keyDescr?.color, ...chart.keyDescr },
|
|
12353
12233
|
progressBar: chart.baselineMode === "progress"
|
|
12354
12234
|
? {
|
|
12355
12235
|
value: baselineValue,
|
|
@@ -12360,11 +12240,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
12360
12240
|
}
|
|
12361
12241
|
|
|
12362
12242
|
/* Padding at the border of the chart */
|
|
12363
|
-
const CHART_PADDING = 10;
|
|
12364
12243
|
const BOTTOM_PADDING_RATIO = 0.05;
|
|
12365
|
-
/* Maximum font sizes of each element */
|
|
12366
|
-
const KEY_VALUE_FONT_SIZE = 32;
|
|
12367
|
-
const BASELINE_MAX_FONT_SIZE = 16;
|
|
12368
12244
|
function formatBaselineDescr(baselineDescr, baseline) {
|
|
12369
12245
|
const _baselineDescr = baselineDescr || "";
|
|
12370
12246
|
return baseline && _baselineDescr ? " " + _baselineDescr : _baselineDescr;
|
|
@@ -12414,7 +12290,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
12414
12290
|
style: style.title,
|
|
12415
12291
|
position: {
|
|
12416
12292
|
x,
|
|
12417
|
-
y:
|
|
12293
|
+
y: CHART_PADDING_BOTTOM + titleHeight / 2,
|
|
12418
12294
|
},
|
|
12419
12295
|
};
|
|
12420
12296
|
}
|
|
@@ -12424,42 +12300,49 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
12424
12300
|
baselineHeight = this.getTextDimensions(this.baselineDescr, style.baselineDescr.font).height;
|
|
12425
12301
|
}
|
|
12426
12302
|
const baselineDescrWidth = this.getTextDimensions(this.baselineDescr, style.baselineDescr.font).width;
|
|
12427
|
-
|
|
12428
|
-
|
|
12429
|
-
|
|
12430
|
-
|
|
12431
|
-
|
|
12432
|
-
|
|
12433
|
-
|
|
12434
|
-
|
|
12435
|
-
|
|
12436
|
-
|
|
12437
|
-
|
|
12438
|
-
if (
|
|
12439
|
-
structure.baseline
|
|
12440
|
-
|
|
12441
|
-
|
|
12442
|
-
structure.baselineArrow = {
|
|
12443
|
-
direction: this.baselineArrow,
|
|
12444
|
-
style: style.baselineArrow,
|
|
12303
|
+
let baselineX;
|
|
12304
|
+
switch (this.runtime.baselineStyle?.align) {
|
|
12305
|
+
case "right":
|
|
12306
|
+
baselineX = this.width - CHART_PADDING - baselineDescrWidth - baselineWidth;
|
|
12307
|
+
break;
|
|
12308
|
+
case "left":
|
|
12309
|
+
baselineX = CHART_PADDING + baselineArrowSize;
|
|
12310
|
+
break;
|
|
12311
|
+
default:
|
|
12312
|
+
baselineX = (this.width - baselineWidth - baselineDescrWidth + baselineArrowSize) / 2;
|
|
12313
|
+
}
|
|
12314
|
+
if (this.baseline) {
|
|
12315
|
+
structure.baseline = {
|
|
12316
|
+
text: this.baseline,
|
|
12317
|
+
style: style.baselineValue,
|
|
12445
12318
|
position: {
|
|
12446
|
-
x:
|
|
12447
|
-
y:
|
|
12319
|
+
x: baselineX,
|
|
12320
|
+
y: this.keyValue
|
|
12321
|
+
? this.height * (1 - BOTTOM_PADDING_RATIO * (this.runtime.progressBar ? 1 : 2))
|
|
12322
|
+
: this.height - (this.height - titleHeight - baselineHeight) / 2 - CHART_PADDING_BOTTOM,
|
|
12448
12323
|
},
|
|
12449
12324
|
};
|
|
12325
|
+
if (style.baselineArrow && !this.runtime.progressBar) {
|
|
12326
|
+
structure.baselineArrow = {
|
|
12327
|
+
direction: this.baselineArrow,
|
|
12328
|
+
style: style.baselineArrow,
|
|
12329
|
+
position: {
|
|
12330
|
+
x: structure.baseline.position.x - baselineArrowSize,
|
|
12331
|
+
y: structure.baseline.position.y - (baselineHeight + baselineArrowSize) / 2,
|
|
12332
|
+
},
|
|
12333
|
+
};
|
|
12334
|
+
}
|
|
12450
12335
|
}
|
|
12451
|
-
if (this.baselineDescr) {
|
|
12336
|
+
if (structure.baseline && this.baselineDescr) {
|
|
12452
12337
|
const position = {
|
|
12453
12338
|
x: structure.baseline.position.x + baselineWidth,
|
|
12454
12339
|
y: structure.baseline.position.y,
|
|
12455
12340
|
};
|
|
12456
|
-
structure.baselineDescr =
|
|
12457
|
-
|
|
12458
|
-
|
|
12459
|
-
|
|
12460
|
-
|
|
12461
|
-
},
|
|
12462
|
-
];
|
|
12341
|
+
structure.baselineDescr = {
|
|
12342
|
+
text: this.baselineDescr,
|
|
12343
|
+
style: style.baselineDescr,
|
|
12344
|
+
position,
|
|
12345
|
+
};
|
|
12463
12346
|
}
|
|
12464
12347
|
let progressBarHeight = 0;
|
|
12465
12348
|
if (this.runtime.progressBar) {
|
|
@@ -12481,18 +12364,41 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
12481
12364
|
};
|
|
12482
12365
|
}
|
|
12483
12366
|
const { width: keyWidth, height: keyHeight } = this.getFullTextDimensions(this.keyValue, style.keyValue.font);
|
|
12367
|
+
const keyDescrWidth = this.getTextDimensions(this.keyDescr, style.keyDescr.font).width;
|
|
12368
|
+
let keyX;
|
|
12369
|
+
switch (this.runtime.keyValueStyle?.align) {
|
|
12370
|
+
case "right":
|
|
12371
|
+
keyX = this.width - CHART_PADDING - keyDescrWidth - keyWidth;
|
|
12372
|
+
break;
|
|
12373
|
+
case "left":
|
|
12374
|
+
keyX = CHART_PADDING;
|
|
12375
|
+
break;
|
|
12376
|
+
default:
|
|
12377
|
+
keyX = (this.width - keyWidth - keyDescrWidth) / 2;
|
|
12378
|
+
}
|
|
12484
12379
|
if (this.keyValue) {
|
|
12485
12380
|
structure.key = {
|
|
12486
12381
|
text: this.keyValue,
|
|
12487
12382
|
style: style.keyValue,
|
|
12488
12383
|
position: {
|
|
12489
|
-
x: Math.max(CHART_PADDING,
|
|
12384
|
+
x: Math.max(CHART_PADDING, keyX),
|
|
12490
12385
|
y: this.height * (0.5 - BOTTOM_PADDING_RATIO * 2) +
|
|
12491
|
-
|
|
12386
|
+
CHART_PADDING_BOTTOM / 2 +
|
|
12492
12387
|
(titleHeight + keyHeight / 2) / 2,
|
|
12493
12388
|
},
|
|
12494
12389
|
};
|
|
12495
12390
|
}
|
|
12391
|
+
if (structure.key && this.keyDescr) {
|
|
12392
|
+
const position = {
|
|
12393
|
+
x: structure.key.position.x + keyWidth,
|
|
12394
|
+
y: structure.key.position.y,
|
|
12395
|
+
};
|
|
12396
|
+
structure.keyDescr = {
|
|
12397
|
+
text: this.keyDescr,
|
|
12398
|
+
style: style.keyDescr,
|
|
12399
|
+
position,
|
|
12400
|
+
};
|
|
12401
|
+
}
|
|
12496
12402
|
return structure;
|
|
12497
12403
|
}
|
|
12498
12404
|
get title() {
|
|
@@ -12501,6 +12407,9 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
12501
12407
|
get keyValue() {
|
|
12502
12408
|
return this.runtime.keyValue;
|
|
12503
12409
|
}
|
|
12410
|
+
get keyDescr() {
|
|
12411
|
+
return formatBaselineDescr(this.runtime.keyDescr, this.keyValue);
|
|
12412
|
+
}
|
|
12504
12413
|
get baseline() {
|
|
12505
12414
|
return this.runtime.baselineDisplay;
|
|
12506
12415
|
}
|
|
@@ -12533,7 +12442,9 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
12533
12442
|
};
|
|
12534
12443
|
}
|
|
12535
12444
|
getTextStyles() {
|
|
12536
|
-
|
|
12445
|
+
const keyValueFontSize = this.runtime.keyValueStyle?.fontSize ?? DEFAULT_SCORECARD_KEY_VALUE_FONT_SIZE;
|
|
12446
|
+
const keyValueDescrFontSize = Math.floor(0.9 * keyValueFontSize);
|
|
12447
|
+
let baselineValueFontSize = this.runtime.baselineStyle?.fontSize ?? DEFAULT_SCORECARD_BASELINE_FONT_SIZE;
|
|
12537
12448
|
const baselineDescrFontSize = Math.floor(0.9 * baselineValueFontSize);
|
|
12538
12449
|
if (this.runtime.progressBar) {
|
|
12539
12450
|
baselineValueFontSize /= 1.5;
|
|
@@ -12545,27 +12456,37 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
12545
12456
|
},
|
|
12546
12457
|
keyValue: {
|
|
12547
12458
|
color: this.runtime.keyValueStyle?.textColor || this.runtime.fontColor,
|
|
12548
|
-
font: getDefaultContextFont(
|
|
12459
|
+
font: getDefaultContextFont(keyValueFontSize, this.runtime.keyValueStyle?.bold, this.runtime.keyValueStyle?.italic),
|
|
12549
12460
|
strikethrough: this.runtime.keyValueStyle?.strikethrough,
|
|
12550
12461
|
underline: this.runtime.keyValueStyle?.underline,
|
|
12551
12462
|
},
|
|
12463
|
+
keyDescr: {
|
|
12464
|
+
color: this.runtime.keyValueDescrStyle?.textColor || this.runtime.fontColor,
|
|
12465
|
+
font: getDefaultContextFont(keyValueDescrFontSize, this.runtime.keyValueDescrStyle?.bold, this.runtime.keyValueDescrStyle?.italic),
|
|
12466
|
+
strikethrough: this.runtime.keyValueDescrStyle?.strikethrough,
|
|
12467
|
+
underline: this.runtime.keyValueDescrStyle?.underline,
|
|
12468
|
+
},
|
|
12552
12469
|
baselineValue: {
|
|
12553
12470
|
font: getDefaultContextFont(baselineValueFontSize, this.runtime.baselineStyle?.bold, this.runtime.baselineStyle?.italic),
|
|
12554
12471
|
strikethrough: this.runtime.baselineStyle?.strikethrough,
|
|
12555
12472
|
underline: this.runtime.baselineStyle?.underline,
|
|
12556
|
-
color: this.runtime.
|
|
12557
|
-
this.runtime.
|
|
12473
|
+
color: this.runtime.baselineColor ||
|
|
12474
|
+
this.runtime.baselineStyle?.textColor ||
|
|
12558
12475
|
this.secondaryFontColor,
|
|
12559
12476
|
},
|
|
12560
12477
|
baselineDescr: {
|
|
12561
|
-
font: getDefaultContextFont(baselineDescrFontSize),
|
|
12562
|
-
|
|
12478
|
+
font: getDefaultContextFont(baselineDescrFontSize, this.runtime.baselineDescrStyle?.bold, this.runtime.baselineDescrStyle?.italic),
|
|
12479
|
+
strikethrough: this.runtime.baselineDescrStyle?.strikethrough,
|
|
12480
|
+
underline: this.runtime.baselineDescrStyle?.underline,
|
|
12481
|
+
color: this.runtime.baselineDescrStyle?.textColor ?? this.secondaryFontColor,
|
|
12563
12482
|
},
|
|
12564
12483
|
baselineArrow: this.baselineArrow === "neutral" || this.runtime.progressBar
|
|
12565
12484
|
? undefined
|
|
12566
12485
|
: {
|
|
12567
12486
|
size: this.keyValue ? 0.8 * baselineValueFontSize : 0,
|
|
12568
|
-
color: this.runtime.baselineColor ||
|
|
12487
|
+
color: this.runtime.baselineColor ||
|
|
12488
|
+
this.runtime.baselineStyle?.textColor ||
|
|
12489
|
+
this.secondaryFontColor,
|
|
12569
12490
|
},
|
|
12570
12491
|
};
|
|
12571
12492
|
}
|
|
@@ -12959,14 +12880,14 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
12959
12880
|
}
|
|
12960
12881
|
switch (runtime.title.align) {
|
|
12961
12882
|
case "right":
|
|
12962
|
-
x = boundingRect.width - titleWidth - CHART_PADDING
|
|
12883
|
+
x = boundingRect.width - titleWidth - CHART_PADDING;
|
|
12963
12884
|
break;
|
|
12964
12885
|
case "center":
|
|
12965
12886
|
x = (boundingRect.width - titleWidth) / 2;
|
|
12966
12887
|
break;
|
|
12967
12888
|
case "left":
|
|
12968
12889
|
default:
|
|
12969
|
-
x = CHART_PADDING
|
|
12890
|
+
x = CHART_PADDING;
|
|
12970
12891
|
break;
|
|
12971
12892
|
}
|
|
12972
12893
|
return {
|
|
@@ -14288,8 +14209,8 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
14288
14209
|
function getChartLayout(definition, args) {
|
|
14289
14210
|
return {
|
|
14290
14211
|
padding: {
|
|
14291
|
-
left: CHART_PADDING
|
|
14292
|
-
right: CHART_PADDING
|
|
14212
|
+
left: CHART_PADDING,
|
|
14213
|
+
right: CHART_PADDING,
|
|
14293
14214
|
top: Math.max(CHART_PADDING_TOP, args.topPadding || 0),
|
|
14294
14215
|
bottom: CHART_PADDING_BOTTOM,
|
|
14295
14216
|
},
|
|
@@ -14840,11 +14761,11 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
14840
14761
|
case "right":
|
|
14841
14762
|
const hasTitle = !!definition.title.text;
|
|
14842
14763
|
const topMargin = hasTitle ? CHART_PADDING_TOP + 30 : CHART_PADDING_TOP;
|
|
14843
|
-
return { top: topMargin, left: CHART_PADDING
|
|
14764
|
+
return { top: topMargin, left: CHART_PADDING, right: CHART_PADDING };
|
|
14844
14765
|
case "bottom":
|
|
14845
14766
|
case "left":
|
|
14846
14767
|
case "none":
|
|
14847
|
-
return { left: CHART_PADDING
|
|
14768
|
+
return { left: CHART_PADDING, right: CHART_PADDING, bottom: CHART_PADDING_BOTTOM };
|
|
14848
14769
|
}
|
|
14849
14770
|
}
|
|
14850
14771
|
function legendPositionToGeoLegendPosition(position) {
|
|
@@ -14912,7 +14833,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
14912
14833
|
padding: {
|
|
14913
14834
|
// Disable title top/left/right padding to use the chart padding instead.
|
|
14914
14835
|
// The legend already has a top padding, so bottom padding is useless for the title there.
|
|
14915
|
-
bottom: definition.legendPosition === "top" ? 0 : CHART_PADDING
|
|
14836
|
+
bottom: definition.legendPosition === "top" ? 0 : CHART_PADDING,
|
|
14916
14837
|
},
|
|
14917
14838
|
};
|
|
14918
14839
|
}
|
|
@@ -15475,6 +15396,10 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
15475
15396
|
|
|
15476
15397
|
class GaugeChartComponent extends owl.Component {
|
|
15477
15398
|
static template = "o-spreadsheet-GaugeChartComponent";
|
|
15399
|
+
static props = {
|
|
15400
|
+
figureUI: Object,
|
|
15401
|
+
isFullScreen: { type: Boolean, optional: true },
|
|
15402
|
+
};
|
|
15478
15403
|
canvas = owl.useRef("chartContainer");
|
|
15479
15404
|
get runtime() {
|
|
15480
15405
|
return this.env.model.getters.getChartRuntime(this.props.figureUI.id);
|
|
@@ -15487,9 +15412,6 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
15487
15412
|
});
|
|
15488
15413
|
}
|
|
15489
15414
|
}
|
|
15490
|
-
GaugeChartComponent.props = {
|
|
15491
|
-
figureUI: Object,
|
|
15492
|
-
};
|
|
15493
15415
|
|
|
15494
15416
|
class ComboChart extends AbstractChart {
|
|
15495
15417
|
dataSets;
|
|
@@ -18293,6 +18215,26 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
18293
18215
|
};
|
|
18294
18216
|
}
|
|
18295
18217
|
|
|
18218
|
+
class FullScreenChartStore extends SpreadsheetStore {
|
|
18219
|
+
mutators = ["toggleFullScreenChart"];
|
|
18220
|
+
fullScreenFigure = undefined;
|
|
18221
|
+
toggleFullScreenChart(figureId) {
|
|
18222
|
+
if (this.fullScreenFigure?.id === figureId) {
|
|
18223
|
+
this.fullScreenFigure = undefined;
|
|
18224
|
+
}
|
|
18225
|
+
else {
|
|
18226
|
+
this.makeFullScreen(figureId);
|
|
18227
|
+
}
|
|
18228
|
+
}
|
|
18229
|
+
makeFullScreen(figureId) {
|
|
18230
|
+
const sheetId = this.getters.getActiveSheetId();
|
|
18231
|
+
const figure = this.getters.getFigure(sheetId, figureId);
|
|
18232
|
+
if (figure) {
|
|
18233
|
+
this.fullScreenFigure = { ...figure, x: 0, y: 0, width: 0, height: 0 };
|
|
18234
|
+
}
|
|
18235
|
+
}
|
|
18236
|
+
}
|
|
18237
|
+
|
|
18296
18238
|
/**
|
|
18297
18239
|
* Repeatedly calls a callback function with a time delay between calls.
|
|
18298
18240
|
*/
|
|
@@ -18412,7 +18354,9 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
18412
18354
|
const spreadsheetRect = useSpreadsheetRect();
|
|
18413
18355
|
function updateRect() {
|
|
18414
18356
|
const env = component.env;
|
|
18415
|
-
const newRect = "getPopoverContainerRect" in env
|
|
18357
|
+
const newRect = "getPopoverContainerRect" in env && env.getPopoverContainerRect
|
|
18358
|
+
? env.getPopoverContainerRect()
|
|
18359
|
+
: spreadsheetRect;
|
|
18416
18360
|
container.x = newRect.x;
|
|
18417
18361
|
container.y = newRect.y;
|
|
18418
18362
|
container.width = newRect.width;
|
|
@@ -18937,9 +18881,11 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
18937
18881
|
static components = { Menu };
|
|
18938
18882
|
static props = { figureUI: Object };
|
|
18939
18883
|
originalChartDefinition;
|
|
18884
|
+
fullScreenFigureStore;
|
|
18940
18885
|
menuState = owl.useState({ isOpen: false, anchorRect: null, menuItems: [] });
|
|
18941
18886
|
setup() {
|
|
18942
18887
|
super.setup();
|
|
18888
|
+
this.fullScreenFigureStore = useStore(FullScreenChartStore);
|
|
18943
18889
|
this.originalChartDefinition = this.env.model.getters.getChartDefinition(this.props.figureUI.id);
|
|
18944
18890
|
owl.onWillUpdateProps(({ figureUI }) => {
|
|
18945
18891
|
if (figureUI.id !== this.props.figureUI.id) {
|
|
@@ -18947,7 +18893,10 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
18947
18893
|
}
|
|
18948
18894
|
});
|
|
18949
18895
|
}
|
|
18950
|
-
|
|
18896
|
+
getMenuItems() {
|
|
18897
|
+
return [this.fullScreenMenuItem, ...this.changeChartTypeMenuItems].filter(isDefined);
|
|
18898
|
+
}
|
|
18899
|
+
get changeChartTypeMenuItems() {
|
|
18951
18900
|
const definition = this.env.model.getters.getChartDefinition(this.props.figureUI.id);
|
|
18952
18901
|
if (!["line", "bar", "pie"].includes(definition.type)) {
|
|
18953
18902
|
return [];
|
|
@@ -18955,8 +18904,11 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
18955
18904
|
return ["column", "line", "pie"].map((type) => {
|
|
18956
18905
|
const item = chartSubtypeRegistry.get(type);
|
|
18957
18906
|
return {
|
|
18958
|
-
|
|
18959
|
-
|
|
18907
|
+
id: item.chartType,
|
|
18908
|
+
label: item.displayName,
|
|
18909
|
+
onClick: () => this.onTypeChange(item.chartType),
|
|
18910
|
+
isSelected: item.chartType === this.selectedChartType,
|
|
18911
|
+
iconClass: this.getIconClasses(item.chartType),
|
|
18960
18912
|
};
|
|
18961
18913
|
});
|
|
18962
18914
|
}
|
|
@@ -19011,6 +18963,30 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
19011
18963
|
this.menuState.anchorRect = { x: ev.clientX, y: ev.clientY, width: 0, height: 0 };
|
|
19012
18964
|
this.menuState.menuItems = getChartMenuActions(this.props.figureUI.id, () => { }, this.env);
|
|
19013
18965
|
}
|
|
18966
|
+
get fullScreenMenuItem() {
|
|
18967
|
+
const definition = this.env.model.getters.getChartDefinition(this.props.figureUI.id);
|
|
18968
|
+
if (definition.type === "scorecard") {
|
|
18969
|
+
return undefined;
|
|
18970
|
+
}
|
|
18971
|
+
if (this.props.figureUI.id === this.fullScreenFigureStore.fullScreenFigure?.id) {
|
|
18972
|
+
return {
|
|
18973
|
+
id: "fullScreenChart",
|
|
18974
|
+
label: _t("Exit Full Screen"),
|
|
18975
|
+
iconClass: "fa fa-compress",
|
|
18976
|
+
onClick: () => {
|
|
18977
|
+
this.fullScreenFigureStore.toggleFullScreenChart(this.props.figureUI.id);
|
|
18978
|
+
},
|
|
18979
|
+
};
|
|
18980
|
+
}
|
|
18981
|
+
return {
|
|
18982
|
+
id: "fullScreenChart",
|
|
18983
|
+
label: _t("Full Screen"),
|
|
18984
|
+
iconClass: "fa fa-expand",
|
|
18985
|
+
onClick: () => {
|
|
18986
|
+
this.fullScreenFigureStore.toggleFullScreenChart(this.props.figureUI.id);
|
|
18987
|
+
},
|
|
18988
|
+
};
|
|
18989
|
+
}
|
|
19014
18990
|
}
|
|
19015
18991
|
|
|
19016
18992
|
// -----------------------------------------------------------------------------
|
|
@@ -29029,7 +29005,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
29029
29005
|
if (error) {
|
|
29030
29006
|
return error;
|
|
29031
29007
|
}
|
|
29032
|
-
const table = pivot.
|
|
29008
|
+
const table = pivot.getCollapsedTableStructure();
|
|
29033
29009
|
const cells = table.getPivotCells(_includedTotal, _includeColumnHeaders);
|
|
29034
29010
|
const headerRows = _includeColumnHeaders ? table.columns.length : 0;
|
|
29035
29011
|
const pivotTitle = this.getters.getPivotDisplayName(pivotId);
|
|
@@ -29049,7 +29025,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
29049
29025
|
break;
|
|
29050
29026
|
case "HEADER":
|
|
29051
29027
|
const valueAndFormat = pivot.getPivotHeaderValueAndFormat(pivotCell.domain);
|
|
29052
|
-
result[col].push(
|
|
29028
|
+
result[col].push(addAlignFormatToPivotHeader(pivotCell.domain, valueAndFormat));
|
|
29053
29029
|
break;
|
|
29054
29030
|
case "MEASURE_HEADER":
|
|
29055
29031
|
result[col].push(pivot.getPivotMeasureValue(pivotCell.measure, pivotCell.domain));
|
|
@@ -32976,12 +32952,13 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
32976
32952
|
return res;
|
|
32977
32953
|
}
|
|
32978
32954
|
getComposerContent() {
|
|
32955
|
+
let content = this._currentContent;
|
|
32979
32956
|
if (this.editionMode === "inactive") {
|
|
32980
32957
|
// References in the content might not be linked to the current active sheet
|
|
32981
32958
|
// We here force the sheet name prefix for all references that are not in
|
|
32982
32959
|
// the current active sheet
|
|
32983
32960
|
const defaultRangeSheetId = this.args().defaultRangeSheetId;
|
|
32984
|
-
|
|
32961
|
+
content = rangeTokenize(this.args().content)
|
|
32985
32962
|
.map((token) => {
|
|
32986
32963
|
if (token.type === "REFERENCE") {
|
|
32987
32964
|
const range = this.getters.getRangeFromSheetXC(defaultRangeSheetId, token.value);
|
|
@@ -32991,7 +32968,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
32991
32968
|
})
|
|
32992
32969
|
.join("");
|
|
32993
32970
|
}
|
|
32994
|
-
return this.
|
|
32971
|
+
return localizeContent(content, this.getters.getLocale());
|
|
32995
32972
|
}
|
|
32996
32973
|
stopEdition() {
|
|
32997
32974
|
this._stopEdition();
|
|
@@ -35670,11 +35647,6 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
35670
35647
|
};
|
|
35671
35648
|
}
|
|
35672
35649
|
|
|
35673
|
-
/**
|
|
35674
|
-
* Registry to draw icons on cells
|
|
35675
|
-
*/
|
|
35676
|
-
const iconsOnCellRegistry = new Registry();
|
|
35677
|
-
|
|
35678
35650
|
css /* scss */ `
|
|
35679
35651
|
.o-spreadsheet {
|
|
35680
35652
|
.o-icon {
|
|
@@ -35811,13 +35783,6 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
35811
35783
|
bad: "dotBad",
|
|
35812
35784
|
},
|
|
35813
35785
|
};
|
|
35814
|
-
iconsOnCellRegistry.add("conditional_formatting", (getters, position) => {
|
|
35815
|
-
const icon = getters.getConditionalIcon(position);
|
|
35816
|
-
if (icon) {
|
|
35817
|
-
return ICONS[icon].svg;
|
|
35818
|
-
}
|
|
35819
|
-
return undefined;
|
|
35820
|
-
});
|
|
35821
35786
|
|
|
35822
35787
|
/**
|
|
35823
35788
|
* Map of the different types of conversions warnings and their name in error messages
|
|
@@ -39925,6 +39890,22 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
39925
39890
|
}
|
|
39926
39891
|
return data;
|
|
39927
39892
|
},
|
|
39893
|
+
})
|
|
39894
|
+
.add("18.4.2", {
|
|
39895
|
+
migrate(data) {
|
|
39896
|
+
for (const sheet of data.sheets || []) {
|
|
39897
|
+
for (const figure of sheet.figures || []) {
|
|
39898
|
+
if (figure.tag !== "chart" || figure.data.type !== "scorecard") {
|
|
39899
|
+
continue;
|
|
39900
|
+
}
|
|
39901
|
+
const scData = figure.data;
|
|
39902
|
+
if (scData.baselineDescr) {
|
|
39903
|
+
scData.baselineDescr = { text: scData.baselineDescr };
|
|
39904
|
+
}
|
|
39905
|
+
}
|
|
39906
|
+
}
|
|
39907
|
+
return data;
|
|
39908
|
+
},
|
|
39928
39909
|
});
|
|
39929
39910
|
function fixOverlappingFilters(data) {
|
|
39930
39911
|
for (const sheet of data.sheets || []) {
|
|
@@ -40799,7 +40780,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
40799
40780
|
sequence: index,
|
|
40800
40781
|
execute: (env) => {
|
|
40801
40782
|
const zone = env.model.getters.getSelectedZone();
|
|
40802
|
-
const table = env.model.getters.getPivot(pivotId).
|
|
40783
|
+
const table = env.model.getters.getPivot(pivotId).getCollapsedTableStructure().export();
|
|
40803
40784
|
env.model.dispatch("INSERT_PIVOT_WITH_TABLE", {
|
|
40804
40785
|
pivotId,
|
|
40805
40786
|
table,
|
|
@@ -40818,7 +40799,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
40818
40799
|
sequence: index,
|
|
40819
40800
|
execute: (env) => {
|
|
40820
40801
|
const zone = env.model.getters.getSelectedZone();
|
|
40821
|
-
const table = env.model.getters.getPivot(pivotId).
|
|
40802
|
+
const table = env.model.getters.getPivot(pivotId).getExpandedTableStructure().export();
|
|
40822
40803
|
env.model.dispatch("INSERT_PIVOT_WITH_TABLE", {
|
|
40823
40804
|
pivotId,
|
|
40824
40805
|
table,
|
|
@@ -42199,6 +42180,218 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
42199
42180
|
}
|
|
42200
42181
|
}
|
|
42201
42182
|
|
|
42183
|
+
const PREVIOUS_VALUE = "(previous)";
|
|
42184
|
+
const NEXT_VALUE = "(next)";
|
|
42185
|
+
function getDomainOfParentRow(pivot, domain) {
|
|
42186
|
+
const { colDomain, rowDomain } = domainToColRowDomain(pivot, domain);
|
|
42187
|
+
return [...colDomain, ...rowDomain.slice(0, rowDomain.length - 1)];
|
|
42188
|
+
}
|
|
42189
|
+
function getDomainOfParentCol(pivot, domain) {
|
|
42190
|
+
const { colDomain, rowDomain } = domainToColRowDomain(pivot, domain);
|
|
42191
|
+
return [...colDomain.slice(0, colDomain.length - 1), ...rowDomain];
|
|
42192
|
+
}
|
|
42193
|
+
/**
|
|
42194
|
+
* Split a pivot domain into the part related to the rows of the pivot, and the part related to the columns.
|
|
42195
|
+
*/
|
|
42196
|
+
function domainToColRowDomain(pivot, domain) {
|
|
42197
|
+
const rowFields = pivot.definition.rows.map((c) => c.nameWithGranularity);
|
|
42198
|
+
const rowDomain = domain.filter((node) => rowFields.includes(node.field));
|
|
42199
|
+
const columnFields = pivot.definition.columns.map((c) => c.nameWithGranularity);
|
|
42200
|
+
const colDomain = domain.filter((node) => columnFields.includes(node.field));
|
|
42201
|
+
return { colDomain, rowDomain };
|
|
42202
|
+
}
|
|
42203
|
+
function getDimensionDomain(pivot, dimension, domain) {
|
|
42204
|
+
return dimension === "column"
|
|
42205
|
+
? domainToColRowDomain(pivot, domain).colDomain
|
|
42206
|
+
: domainToColRowDomain(pivot, domain).rowDomain;
|
|
42207
|
+
}
|
|
42208
|
+
function getFieldValueInDomain(fieldNameWithGranularity, domain) {
|
|
42209
|
+
const node = domain.find((n) => n.field === fieldNameWithGranularity);
|
|
42210
|
+
return node?.value;
|
|
42211
|
+
}
|
|
42212
|
+
function isDomainIsInPivot(pivot, domain) {
|
|
42213
|
+
for (const node of domain) {
|
|
42214
|
+
if (pivot.definition.rows.find((row) => row.nameWithGranularity === node.field) === undefined &&
|
|
42215
|
+
pivot.definition.columns.find((col) => col.nameWithGranularity === node.field) === undefined) {
|
|
42216
|
+
return false;
|
|
42217
|
+
}
|
|
42218
|
+
}
|
|
42219
|
+
const { rowDomain, colDomain } = domainToColRowDomain(pivot, domain);
|
|
42220
|
+
return (checkIfDomainInInTree(rowDomain, pivot.getExpandedTableStructure().getRowTree()) &&
|
|
42221
|
+
checkIfDomainInInTree(colDomain, pivot.getExpandedTableStructure().getColTree()));
|
|
42222
|
+
}
|
|
42223
|
+
function checkIfDomainInInTree(domain, tree) {
|
|
42224
|
+
return walkDomainTree(domain, tree) !== undefined;
|
|
42225
|
+
}
|
|
42226
|
+
/**
|
|
42227
|
+
* Given a tree of the col/rows of a pivot, and a domain related to those col/rows, return the node of the tree
|
|
42228
|
+
* corresponding to the domain.
|
|
42229
|
+
*
|
|
42230
|
+
* @param domain The domain to find in the tree
|
|
42231
|
+
* @param tree The tree to search in7
|
|
42232
|
+
* @param stopAtField If provided, the search will stop at the field with this name
|
|
42233
|
+
*/
|
|
42234
|
+
function walkDomainTree(domain, tree, stopAtField) {
|
|
42235
|
+
let currentTreeNode = tree;
|
|
42236
|
+
for (const node of domain) {
|
|
42237
|
+
const child = currentTreeNode.find((n) => n.value === node.value);
|
|
42238
|
+
if (!child) {
|
|
42239
|
+
return undefined;
|
|
42240
|
+
}
|
|
42241
|
+
if (child.field === stopAtField) {
|
|
42242
|
+
return currentTreeNode;
|
|
42243
|
+
}
|
|
42244
|
+
currentTreeNode = child.children;
|
|
42245
|
+
}
|
|
42246
|
+
return currentTreeNode;
|
|
42247
|
+
}
|
|
42248
|
+
/**
|
|
42249
|
+
* Get the domain parent of the given domain with the field `parentFieldName` as leaf of the domain.
|
|
42250
|
+
*
|
|
42251
|
+
* In practice, if the `parentFieldName` is a row in the pivot, the helper will return a domain with the same column
|
|
42252
|
+
* domain, and with a row domain all groupBys children to `parentFieldName` removed.
|
|
42253
|
+
*/
|
|
42254
|
+
function getFieldParentDomain(pivot, parentFieldName, domain) {
|
|
42255
|
+
let { rowDomain, colDomain } = domainToColRowDomain(pivot, domain);
|
|
42256
|
+
const dimension = getFieldDimensionType(pivot, parentFieldName);
|
|
42257
|
+
if (dimension === "row") {
|
|
42258
|
+
const index = rowDomain.findIndex((node) => node.field === parentFieldName);
|
|
42259
|
+
if (index === -1) {
|
|
42260
|
+
return domain;
|
|
42261
|
+
}
|
|
42262
|
+
rowDomain = rowDomain.slice(0, index + 1);
|
|
42263
|
+
}
|
|
42264
|
+
else {
|
|
42265
|
+
const index = colDomain.findIndex((node) => node.field === parentFieldName);
|
|
42266
|
+
if (index === -1) {
|
|
42267
|
+
return domain;
|
|
42268
|
+
}
|
|
42269
|
+
colDomain = colDomain.slice(0, index + 1);
|
|
42270
|
+
}
|
|
42271
|
+
return [...rowDomain, ...colDomain];
|
|
42272
|
+
}
|
|
42273
|
+
/**
|
|
42274
|
+
* Replace in the domain the value of the field `fieldNameWithGranularity` with the given `value`
|
|
42275
|
+
*/
|
|
42276
|
+
function replaceFieldValueInDomain(domain, fieldNameWithGranularity, value) {
|
|
42277
|
+
domain = deepCopy(domain);
|
|
42278
|
+
const node = domain.find((n) => n.field === fieldNameWithGranularity);
|
|
42279
|
+
if (!node) {
|
|
42280
|
+
return domain;
|
|
42281
|
+
}
|
|
42282
|
+
node.value = value;
|
|
42283
|
+
return domain;
|
|
42284
|
+
}
|
|
42285
|
+
function isFieldInDomain(nameWithGranularity, domain) {
|
|
42286
|
+
return domain.some((node) => node.field === nameWithGranularity);
|
|
42287
|
+
}
|
|
42288
|
+
/**
|
|
42289
|
+
* Check if the field is in the rows or columns of the pivot
|
|
42290
|
+
*/
|
|
42291
|
+
function getFieldDimensionType(pivot, nameWithGranularity) {
|
|
42292
|
+
const rowFields = pivot.definition.rows.map((c) => c.nameWithGranularity);
|
|
42293
|
+
if (rowFields.includes(nameWithGranularity)) {
|
|
42294
|
+
return "row";
|
|
42295
|
+
}
|
|
42296
|
+
const columnFields = pivot.definition.columns.map((c) => c.nameWithGranularity);
|
|
42297
|
+
if (columnFields.includes(nameWithGranularity)) {
|
|
42298
|
+
return "column";
|
|
42299
|
+
}
|
|
42300
|
+
throw new Error(`Field ${nameWithGranularity} not found in pivot`);
|
|
42301
|
+
}
|
|
42302
|
+
/**
|
|
42303
|
+
* Replace in the given domain the value of the field `fieldNameWithGranularity` with the previous or next value.
|
|
42304
|
+
*/
|
|
42305
|
+
function getPreviousOrNextValueDomain(pivot, domain, fieldNameWithGranularity, direction) {
|
|
42306
|
+
const dimension = getFieldDimensionType(pivot, fieldNameWithGranularity);
|
|
42307
|
+
const tree = dimension === "row"
|
|
42308
|
+
? pivot.getExpandedTableStructure().getRowTree()
|
|
42309
|
+
: pivot.getExpandedTableStructure().getColTree();
|
|
42310
|
+
const dimDomain = getDimensionDomain(pivot, dimension, domain);
|
|
42311
|
+
const currentTreeNode = walkDomainTree(dimDomain, tree, fieldNameWithGranularity);
|
|
42312
|
+
const values = currentTreeNode?.map((n) => n.value) ?? [];
|
|
42313
|
+
const value = getFieldValueInDomain(fieldNameWithGranularity, domain);
|
|
42314
|
+
if (value === undefined) {
|
|
42315
|
+
return undefined;
|
|
42316
|
+
}
|
|
42317
|
+
const valueIndex = values.indexOf(value);
|
|
42318
|
+
if (value === undefined || valueIndex === -1) {
|
|
42319
|
+
return undefined;
|
|
42320
|
+
}
|
|
42321
|
+
const offset = direction === PREVIOUS_VALUE ? -1 : 1;
|
|
42322
|
+
const newIndex = clip(valueIndex + offset, 0, values.length - 1);
|
|
42323
|
+
return replaceFieldValueInDomain(domain, fieldNameWithGranularity, values[newIndex]);
|
|
42324
|
+
}
|
|
42325
|
+
function domainToString(domain) {
|
|
42326
|
+
return domain ? domain.map(domainNodeToString).join(", ") : "";
|
|
42327
|
+
}
|
|
42328
|
+
function domainNodeToString(domainNode) {
|
|
42329
|
+
return domainNode ? `${domainNode.field}=${domainNode.value}` : "";
|
|
42330
|
+
}
|
|
42331
|
+
/**
|
|
42332
|
+
*
|
|
42333
|
+
* For the ranking, the pivot cell values of a column (or row) at the same depth are grouped together before being sorted
|
|
42334
|
+
* and ranked.
|
|
42335
|
+
*
|
|
42336
|
+
* The grouping of a pivot cell is done with both the value of the domain nodes that are parent of the field
|
|
42337
|
+
* `fieldNameWithGranularity` and the value of the last node of the domain of the pivot cell, if it's not the field
|
|
42338
|
+
* `fieldNameWithGranularity`.
|
|
42339
|
+
*
|
|
42340
|
+
* For example, let's take a pivot grouped by (Date:year, Stage, User, Product), where we want to rank by "Stage" field.
|
|
42341
|
+
* The domain nodes parents of the "Stage" are [Date:year]. The pivot cell with domain:
|
|
42342
|
+
* - [Date:year=2021] is not ranked because it does not contain the "Stage" field
|
|
42343
|
+
* - [Date:year=2021, Stage=Lead] is grouped with the cells [Date:year=2021, Stage=*, User=None, Product=None],
|
|
42344
|
+
* and then ranked within the group
|
|
42345
|
+
* - [Date:year=2021, Stage=Lead, User=Bob] is grouped with the cells [Date:year=2021, Stage=*, User=Bob, Product=None],
|
|
42346
|
+
* and then ranked within the group
|
|
42347
|
+
* - [Date:year=2021, Stage=Lead, User=Bob, Product=Table] is grouped with the cells [Date:year=2021, Stage=*, User=*, Product=Table],
|
|
42348
|
+
* and then ranked within the group
|
|
42349
|
+
*
|
|
42350
|
+
* If we rank the pivot on "User" instead, the parent domain becomes [Date:year, Sage] .The cell with domain:
|
|
42351
|
+
* - [Date:year=2021] is not ranked because it does not contain the "Stage" field
|
|
42352
|
+
* - [Date:year=2021, Stage=Lead] is not ranked because it does not contain the "User" field
|
|
42353
|
+
* - [Date:year=2021, Stage=Lead, User=Bob] is grouped with the cells [Date:year=2021, Stage=Lead, User=Bob, Product=None],
|
|
42354
|
+
* and then ranked within the group
|
|
42355
|
+
* - [Date:year=2021, Stage=Lead, User=Bob, Product=Table] is grouped with the cells with [Date:year=2021, Stage=Lead, User=*, Product=Table],
|
|
42356
|
+
* and then ranked within the group
|
|
42357
|
+
*
|
|
42358
|
+
*/
|
|
42359
|
+
function getRankingDomainKey(domain, fieldNameWithGranularity) {
|
|
42360
|
+
const index = domain.findIndex((node) => node.field === fieldNameWithGranularity);
|
|
42361
|
+
if (index === -1) {
|
|
42362
|
+
return "";
|
|
42363
|
+
}
|
|
42364
|
+
const parent = domain.slice(0, index);
|
|
42365
|
+
const lastNode = domain.at(-1);
|
|
42366
|
+
return domainToString(lastNode.field === fieldNameWithGranularity ? parent : [...parent, lastNode]);
|
|
42367
|
+
}
|
|
42368
|
+
/**
|
|
42369
|
+
* The running total domain is the domain without the field `fieldNameWithGranularity`, ie. we do the running total of
|
|
42370
|
+
* all the pivot cells of the column that have any value for the field `fieldNameWithGranularity` and the same value for
|
|
42371
|
+
* the other fields.
|
|
42372
|
+
*/
|
|
42373
|
+
function getRunningTotalDomainKey(domain, fieldNameWithGranularity) {
|
|
42374
|
+
const index = domain.findIndex((node) => node.field === fieldNameWithGranularity);
|
|
42375
|
+
if (index === -1) {
|
|
42376
|
+
return "";
|
|
42377
|
+
}
|
|
42378
|
+
return domainToString([...domain.slice(0, index), ...domain.slice(index + 1)]);
|
|
42379
|
+
}
|
|
42380
|
+
function sortPivotTree(tree, baseDomain, sortFn) {
|
|
42381
|
+
const sortedTree = [...tree];
|
|
42382
|
+
const domain = [...baseDomain];
|
|
42383
|
+
sortedTree.sort((node1, node2) => sortFn([...domain, node1], [...domain, node2]));
|
|
42384
|
+
for (const node of tree) {
|
|
42385
|
+
const children = sortPivotTree(node.children, [...domain, node], sortFn);
|
|
42386
|
+
node.children = children;
|
|
42387
|
+
}
|
|
42388
|
+
return sortedTree;
|
|
42389
|
+
}
|
|
42390
|
+
function isParentDomain(domain, parentDomain) {
|
|
42391
|
+
return (domain.length > parentDomain.length &&
|
|
42392
|
+
parentDomain.every((node, i) => deepEquals(node, domain[i])));
|
|
42393
|
+
}
|
|
42394
|
+
|
|
42202
42395
|
const pivotProperties = {
|
|
42203
42396
|
name: _t("See pivot properties"),
|
|
42204
42397
|
execute(env) {
|
|
@@ -43199,6 +43392,66 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
43199
43392
|
}
|
|
43200
43393
|
}
|
|
43201
43394
|
|
|
43395
|
+
class ClientFocusStore extends SpreadsheetStore {
|
|
43396
|
+
mutators = [
|
|
43397
|
+
"focusClient",
|
|
43398
|
+
"unfocusClient",
|
|
43399
|
+
"showClientTag",
|
|
43400
|
+
"hideClientTag",
|
|
43401
|
+
"jumpToClient",
|
|
43402
|
+
];
|
|
43403
|
+
_showClientTag = false;
|
|
43404
|
+
clientFocusTimeout = {};
|
|
43405
|
+
constructor(get) {
|
|
43406
|
+
super(get);
|
|
43407
|
+
this.onDispose(() => {
|
|
43408
|
+
for (const clientId in this.clientFocusTimeout) {
|
|
43409
|
+
this.unfocusClient(clientId);
|
|
43410
|
+
}
|
|
43411
|
+
});
|
|
43412
|
+
}
|
|
43413
|
+
get focusedClients() {
|
|
43414
|
+
const focused = new Set();
|
|
43415
|
+
this.model.getters.getConnectedClients().forEach((client) => {
|
|
43416
|
+
if (this._showClientTag || this.clientFocusTimeout[client.id] !== undefined) {
|
|
43417
|
+
focused.add(client.id);
|
|
43418
|
+
}
|
|
43419
|
+
});
|
|
43420
|
+
return focused;
|
|
43421
|
+
}
|
|
43422
|
+
jumpToClient(clientId) {
|
|
43423
|
+
const client = this.model.getters.getClient(clientId);
|
|
43424
|
+
this.focusClient(clientId);
|
|
43425
|
+
if (client.position) {
|
|
43426
|
+
this.model.dispatch("ACTIVATE_SHEET", {
|
|
43427
|
+
sheetIdTo: client.position.sheetId,
|
|
43428
|
+
sheetIdFrom: this.getters.getActiveSheetId(),
|
|
43429
|
+
});
|
|
43430
|
+
this.model.dispatch("SCROLL_TO_CELL", { col: client.position.col, row: client.position.row });
|
|
43431
|
+
}
|
|
43432
|
+
}
|
|
43433
|
+
showClientTag() {
|
|
43434
|
+
this._showClientTag = true;
|
|
43435
|
+
}
|
|
43436
|
+
hideClientTag() {
|
|
43437
|
+
this._showClientTag = false;
|
|
43438
|
+
}
|
|
43439
|
+
focusClient(clientId) {
|
|
43440
|
+
if (this.clientFocusTimeout[clientId]) {
|
|
43441
|
+
clearTimeout(this.clientFocusTimeout[clientId]);
|
|
43442
|
+
}
|
|
43443
|
+
// This call to unfocus client isn't proxyfied and doesn't trigger a render.
|
|
43444
|
+
// The focus will visually disappear when the next render is triggered
|
|
43445
|
+
this.clientFocusTimeout[clientId] = setTimeout(() => this.unfocusClient(clientId), 3000);
|
|
43446
|
+
}
|
|
43447
|
+
unfocusClient(clientId) {
|
|
43448
|
+
if (this.clientFocusTimeout[clientId]) {
|
|
43449
|
+
clearTimeout(this.clientFocusTimeout[clientId]);
|
|
43450
|
+
}
|
|
43451
|
+
this.clientFocusTimeout[clientId] = undefined;
|
|
43452
|
+
}
|
|
43453
|
+
}
|
|
43454
|
+
|
|
43202
43455
|
/**
|
|
43203
43456
|
* Function to be used during a pointerdown event, this function allows to
|
|
43204
43457
|
* perform actions related to the pointermove and pointerup events and adjusts the viewport
|
|
@@ -43472,6 +43725,11 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
43472
43725
|
get tagStyle() {
|
|
43473
43726
|
const { col, row, color } = this.props;
|
|
43474
43727
|
const { height } = this.env.model.getters.getSheetViewDimensionWithHeaders();
|
|
43728
|
+
const visible = this.env.model.getters.isVisibleInViewport({
|
|
43729
|
+
sheetId: this.env.model.getters.getActiveSheetId(),
|
|
43730
|
+
col,
|
|
43731
|
+
row,
|
|
43732
|
+
});
|
|
43475
43733
|
const { x, y } = this.env.model.getters.getVisibleRect({
|
|
43476
43734
|
left: col,
|
|
43477
43735
|
top: row,
|
|
@@ -43483,6 +43741,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
43483
43741
|
left: `${x - 1}px`,
|
|
43484
43742
|
border: `1px solid ${color}`,
|
|
43485
43743
|
"background-color": color,
|
|
43744
|
+
visibility: visible ? "visible" : "hidden",
|
|
43486
43745
|
});
|
|
43487
43746
|
}
|
|
43488
43747
|
}
|
|
@@ -43908,151 +44167,6 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
43908
44167
|
}
|
|
43909
44168
|
}
|
|
43910
44169
|
|
|
43911
|
-
css /* scss */ `
|
|
43912
|
-
.o-grid-cell-icon {
|
|
43913
|
-
width: ${GRID_ICON_EDGE_LENGTH}px;
|
|
43914
|
-
height: ${GRID_ICON_EDGE_LENGTH}px;
|
|
43915
|
-
}
|
|
43916
|
-
`;
|
|
43917
|
-
class GridCellIcon extends owl.Component {
|
|
43918
|
-
static template = "o-spreadsheet-GridCellIcon";
|
|
43919
|
-
static props = {
|
|
43920
|
-
cellPosition: Object,
|
|
43921
|
-
horizontalAlign: { type: String, optional: true },
|
|
43922
|
-
verticalAlign: { type: String, optional: true },
|
|
43923
|
-
slots: Object,
|
|
43924
|
-
};
|
|
43925
|
-
get iconStyle() {
|
|
43926
|
-
const cellPosition = this.props.cellPosition;
|
|
43927
|
-
const merge = this.env.model.getters.getMerge(cellPosition);
|
|
43928
|
-
const zone = merge || positionToZone(cellPosition);
|
|
43929
|
-
const rect = this.env.model.getters.getVisibleRectWithoutHeaders(zone);
|
|
43930
|
-
const x = this.getIconHorizontalPosition(rect, cellPosition);
|
|
43931
|
-
const y = this.getIconVerticalPosition(rect, cellPosition);
|
|
43932
|
-
return cssPropertiesToCss({
|
|
43933
|
-
top: `${y}px`,
|
|
43934
|
-
left: `${x}px`,
|
|
43935
|
-
});
|
|
43936
|
-
}
|
|
43937
|
-
getIconVerticalPosition(rect, cellPosition) {
|
|
43938
|
-
const start = rect.y;
|
|
43939
|
-
const end = rect.y + rect.height;
|
|
43940
|
-
const cell = this.env.model.getters.getCell(cellPosition);
|
|
43941
|
-
const align = this.props.verticalAlign || cell?.style?.verticalAlign || DEFAULT_VERTICAL_ALIGN;
|
|
43942
|
-
switch (align) {
|
|
43943
|
-
case "bottom":
|
|
43944
|
-
return end - GRID_ICON_MARGIN - GRID_ICON_EDGE_LENGTH;
|
|
43945
|
-
case "top":
|
|
43946
|
-
return start + GRID_ICON_MARGIN;
|
|
43947
|
-
default:
|
|
43948
|
-
const centeringOffset = Math.floor((end - start - GRID_ICON_EDGE_LENGTH) / 2);
|
|
43949
|
-
return end - GRID_ICON_EDGE_LENGTH - centeringOffset;
|
|
43950
|
-
}
|
|
43951
|
-
}
|
|
43952
|
-
getIconHorizontalPosition(rect, cellPosition) {
|
|
43953
|
-
const start = rect.x;
|
|
43954
|
-
const end = rect.x + rect.width;
|
|
43955
|
-
const cell = this.env.model.getters.getCell(cellPosition);
|
|
43956
|
-
const evaluatedCell = this.env.model.getters.getEvaluatedCell(cellPosition);
|
|
43957
|
-
const align = this.props.horizontalAlign || cell?.style?.align || evaluatedCell.defaultAlign;
|
|
43958
|
-
switch (align) {
|
|
43959
|
-
case "right":
|
|
43960
|
-
return end - GRID_ICON_MARGIN - GRID_ICON_EDGE_LENGTH;
|
|
43961
|
-
case "left":
|
|
43962
|
-
return start + GRID_ICON_MARGIN;
|
|
43963
|
-
default:
|
|
43964
|
-
const centeringOffset = Math.floor((end - start - GRID_ICON_EDGE_LENGTH) / 2);
|
|
43965
|
-
return end - GRID_ICON_EDGE_LENGTH - centeringOffset;
|
|
43966
|
-
}
|
|
43967
|
-
}
|
|
43968
|
-
isPositionVisible(position) {
|
|
43969
|
-
const rect = this.env.model.getters.getVisibleRect(positionToZone(position));
|
|
43970
|
-
return !(rect.width === 0 || rect.height === 0);
|
|
43971
|
-
}
|
|
43972
|
-
}
|
|
43973
|
-
|
|
43974
|
-
const MARGIN = (GRID_ICON_EDGE_LENGTH - CHECKBOX_WIDTH) / 2;
|
|
43975
|
-
css /* scss */ `
|
|
43976
|
-
.o-dv-checkbox {
|
|
43977
|
-
margin: ${MARGIN}px;
|
|
43978
|
-
/* required to prevent the checkbox position to be sensible to the font-size (affects Firefox) */
|
|
43979
|
-
position: absolute;
|
|
43980
|
-
}
|
|
43981
|
-
`;
|
|
43982
|
-
class DataValidationCheckbox extends owl.Component {
|
|
43983
|
-
static template = "o-spreadsheet-DataValidationCheckbox";
|
|
43984
|
-
static components = {
|
|
43985
|
-
Checkbox,
|
|
43986
|
-
};
|
|
43987
|
-
static props = {
|
|
43988
|
-
cellPosition: Object,
|
|
43989
|
-
};
|
|
43990
|
-
onCheckboxChange(value) {
|
|
43991
|
-
const { sheetId, col, row } = this.props.cellPosition;
|
|
43992
|
-
const cellContent = value ? "TRUE" : "FALSE";
|
|
43993
|
-
this.env.model.dispatch("UPDATE_CELL", { sheetId, col, row, content: cellContent });
|
|
43994
|
-
}
|
|
43995
|
-
get checkBoxValue() {
|
|
43996
|
-
return !!this.env.model.getters.getEvaluatedCell(this.props.cellPosition).value;
|
|
43997
|
-
}
|
|
43998
|
-
get isDisabled() {
|
|
43999
|
-
const cell = this.env.model.getters.getCell(this.props.cellPosition);
|
|
44000
|
-
return this.env.model.getters.isReadonly() || !!cell?.isFormula;
|
|
44001
|
-
}
|
|
44002
|
-
}
|
|
44003
|
-
|
|
44004
|
-
const ICON_WIDTH = 13;
|
|
44005
|
-
css /* scss */ `
|
|
44006
|
-
.o-dv-list-icon {
|
|
44007
|
-
color: ${TEXT_BODY_MUTED};
|
|
44008
|
-
border-radius: 1px;
|
|
44009
|
-
height: ${GRID_ICON_EDGE_LENGTH}px;
|
|
44010
|
-
width: ${GRID_ICON_EDGE_LENGTH}px;
|
|
44011
|
-
|
|
44012
|
-
&:hover {
|
|
44013
|
-
color: #ffffff;
|
|
44014
|
-
background-color: ${TEXT_BODY_MUTED};
|
|
44015
|
-
}
|
|
44016
|
-
|
|
44017
|
-
svg {
|
|
44018
|
-
width: ${ICON_WIDTH}px;
|
|
44019
|
-
height: ${ICON_WIDTH}px;
|
|
44020
|
-
}
|
|
44021
|
-
}
|
|
44022
|
-
`;
|
|
44023
|
-
class DataValidationListIcon extends owl.Component {
|
|
44024
|
-
static template = "o-spreadsheet-DataValidationListIcon";
|
|
44025
|
-
static props = {
|
|
44026
|
-
cellPosition: Object,
|
|
44027
|
-
};
|
|
44028
|
-
onClick() {
|
|
44029
|
-
const { col, row } = this.props.cellPosition;
|
|
44030
|
-
this.env.model.selection.selectCell(col, row);
|
|
44031
|
-
this.env.startCellEdition();
|
|
44032
|
-
}
|
|
44033
|
-
}
|
|
44034
|
-
|
|
44035
|
-
class DataValidationOverlay extends owl.Component {
|
|
44036
|
-
static template = "o-spreadsheet-DataValidationOverlay";
|
|
44037
|
-
static props = {};
|
|
44038
|
-
static components = { GridCellIcon, DataValidationCheckbox, DataValidationListIcon };
|
|
44039
|
-
get checkBoxCellPositions() {
|
|
44040
|
-
return this.env.model.getters
|
|
44041
|
-
.getVisibleCellPositions()
|
|
44042
|
-
.filter((position) => this.env.model.getters.isCellValidCheckbox(position) &&
|
|
44043
|
-
!this.env.model.getters.isFilterHeader(position));
|
|
44044
|
-
}
|
|
44045
|
-
get listIconsCellPositions() {
|
|
44046
|
-
if (this.env.model.getters.isReadonly()) {
|
|
44047
|
-
return [];
|
|
44048
|
-
}
|
|
44049
|
-
return this.env.model.getters
|
|
44050
|
-
.getVisibleCellPositions()
|
|
44051
|
-
.filter((position) => this.env.model.getters.cellHasListDataValidationIcon(position) &&
|
|
44052
|
-
!this.env.model.getters.isFilterHeader(position));
|
|
44053
|
-
}
|
|
44054
|
-
}
|
|
44055
|
-
|
|
44056
44170
|
function dragFigureForMove({ x: mouseX, y: mouseY }, { x: mouseInitialX, y: mouseInitialY }, initialFigure, { maxX, maxY }, { scrollX: initialScrollX, scrollY: initialScrollY }, { scrollX, scrollY }) {
|
|
44057
44171
|
const deltaX = mouseX - mouseInitialX + scrollX - initialScrollX;
|
|
44058
44172
|
const newX = clip(initialFigure.x + deltaX, 0, maxX - initialFigure.width);
|
|
@@ -44066,14 +44180,14 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
44066
44180
|
const deltaX = Math.min(dirX * (mouseInitialX - mouseX + scrollX - initialScrollX), width - minFigSize);
|
|
44067
44181
|
const deltaY = Math.min(dirY * (mouseInitialY - mouseY + scrollY - initialScrollY), height - minFigSize);
|
|
44068
44182
|
const fraction = Math.min(deltaX / width, deltaY / height);
|
|
44069
|
-
width = width * (1 - fraction);
|
|
44070
|
-
height = height * (1 - fraction);
|
|
44071
44183
|
if (dirX < 0) {
|
|
44072
44184
|
x = x + width * fraction;
|
|
44073
44185
|
}
|
|
44074
44186
|
if (dirY < 0) {
|
|
44075
44187
|
y = y + height * fraction;
|
|
44076
44188
|
}
|
|
44189
|
+
width = width * (1 - fraction);
|
|
44190
|
+
height = height * (1 - fraction);
|
|
44077
44191
|
}
|
|
44078
44192
|
else {
|
|
44079
44193
|
const deltaX = Math.max(dirX * (mouseX - mouseInitialX + scrollX - initialScrollX), minFigSize - width);
|
|
@@ -44654,78 +44768,6 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
44654
44768
|
}
|
|
44655
44769
|
}
|
|
44656
44770
|
|
|
44657
|
-
css /* scss */ `
|
|
44658
|
-
.o-filter-icon {
|
|
44659
|
-
color: ${FILTERS_COLOR};
|
|
44660
|
-
display: flex;
|
|
44661
|
-
align-items: center;
|
|
44662
|
-
justify-content: center;
|
|
44663
|
-
width: ${GRID_ICON_EDGE_LENGTH}px;
|
|
44664
|
-
height: ${GRID_ICON_EDGE_LENGTH}px;
|
|
44665
|
-
|
|
44666
|
-
&:hover {
|
|
44667
|
-
background: ${FILTERS_COLOR};
|
|
44668
|
-
color: #fff;
|
|
44669
|
-
}
|
|
44670
|
-
|
|
44671
|
-
&.o-high-contrast {
|
|
44672
|
-
color: #defade;
|
|
44673
|
-
}
|
|
44674
|
-
&.o-high-contrast:hover {
|
|
44675
|
-
color: ${FILTERS_COLOR};
|
|
44676
|
-
background: #fff;
|
|
44677
|
-
}
|
|
44678
|
-
}
|
|
44679
|
-
.o-filter-icon:hover {
|
|
44680
|
-
background: ${FILTERS_COLOR};
|
|
44681
|
-
color: #fff;
|
|
44682
|
-
}
|
|
44683
|
-
`;
|
|
44684
|
-
class FilterIcon extends owl.Component {
|
|
44685
|
-
static template = "o-spreadsheet-FilterIcon";
|
|
44686
|
-
static props = {
|
|
44687
|
-
cellPosition: Object,
|
|
44688
|
-
};
|
|
44689
|
-
cellPopovers;
|
|
44690
|
-
setup() {
|
|
44691
|
-
this.cellPopovers = useStore(CellPopoverStore);
|
|
44692
|
-
}
|
|
44693
|
-
onClick() {
|
|
44694
|
-
const position = this.props.cellPosition;
|
|
44695
|
-
const activePopover = this.cellPopovers.persistentCellPopover;
|
|
44696
|
-
const { col, row } = position;
|
|
44697
|
-
if (activePopover.isOpen &&
|
|
44698
|
-
activePopover.col === col &&
|
|
44699
|
-
activePopover.row === row &&
|
|
44700
|
-
activePopover.type === "FilterMenu") {
|
|
44701
|
-
this.cellPopovers.close();
|
|
44702
|
-
return;
|
|
44703
|
-
}
|
|
44704
|
-
this.cellPopovers.open({ col, row }, "FilterMenu");
|
|
44705
|
-
}
|
|
44706
|
-
get isFilterActive() {
|
|
44707
|
-
return this.env.model.getters.isFilterActive(this.props.cellPosition);
|
|
44708
|
-
}
|
|
44709
|
-
get iconClass() {
|
|
44710
|
-
const cellStyle = this.env.model.getters.getCellComputedStyle(this.props.cellPosition);
|
|
44711
|
-
const luminance = relativeLuminance(cellStyle.fillColor || "#fff");
|
|
44712
|
-
return luminance < 0.45 ? "o-high-contrast" : "";
|
|
44713
|
-
}
|
|
44714
|
-
}
|
|
44715
|
-
|
|
44716
|
-
class FilterIconsOverlay extends owl.Component {
|
|
44717
|
-
static template = "o-spreadsheet-FilterIconsOverlay";
|
|
44718
|
-
static props = {};
|
|
44719
|
-
static components = {
|
|
44720
|
-
GridCellIcon,
|
|
44721
|
-
FilterIcon,
|
|
44722
|
-
};
|
|
44723
|
-
getFilterHeadersPositions() {
|
|
44724
|
-
const sheetId = this.env.model.getters.getActiveSheetId();
|
|
44725
|
-
return this.env.model.getters.getFilterHeaders(sheetId);
|
|
44726
|
-
}
|
|
44727
|
-
}
|
|
44728
|
-
|
|
44729
44771
|
css /* scss */ `
|
|
44730
44772
|
.o-validation {
|
|
44731
44773
|
border-radius: 4px;
|
|
@@ -44866,6 +44908,78 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
44866
44908
|
}
|
|
44867
44909
|
}
|
|
44868
44910
|
|
|
44911
|
+
class GridCellIcon extends owl.Component {
|
|
44912
|
+
static template = "o-spreadsheet-GridCellIcon";
|
|
44913
|
+
static props = {
|
|
44914
|
+
icon: Object,
|
|
44915
|
+
verticalAlign: { type: String, optional: true },
|
|
44916
|
+
slots: Object,
|
|
44917
|
+
};
|
|
44918
|
+
get iconStyle() {
|
|
44919
|
+
const cellPosition = this.props.icon.position;
|
|
44920
|
+
const merge = this.env.model.getters.getMerge(cellPosition);
|
|
44921
|
+
const zone = merge || positionToZone(cellPosition);
|
|
44922
|
+
const rect = this.env.model.getters.getVisibleRectWithoutHeaders(zone);
|
|
44923
|
+
const x = this.getIconHorizontalPosition(rect, cellPosition);
|
|
44924
|
+
const y = this.getIconVerticalPosition(rect, cellPosition);
|
|
44925
|
+
return cssPropertiesToCss({
|
|
44926
|
+
top: `${y}px`,
|
|
44927
|
+
left: `${x}px`,
|
|
44928
|
+
width: `${this.props.icon.size}px`,
|
|
44929
|
+
height: `${this.props.icon.size}px`,
|
|
44930
|
+
});
|
|
44931
|
+
}
|
|
44932
|
+
getIconVerticalPosition(rect, cellPosition) {
|
|
44933
|
+
const start = rect.y;
|
|
44934
|
+
const end = rect.y + rect.height;
|
|
44935
|
+
const cell = this.env.model.getters.getCell(cellPosition);
|
|
44936
|
+
const align = this.props.verticalAlign || cell?.style?.verticalAlign || DEFAULT_VERTICAL_ALIGN;
|
|
44937
|
+
switch (align) {
|
|
44938
|
+
case "bottom":
|
|
44939
|
+
return end - GRID_ICON_MARGIN - GRID_ICON_EDGE_LENGTH;
|
|
44940
|
+
case "top":
|
|
44941
|
+
return start + GRID_ICON_MARGIN;
|
|
44942
|
+
default:
|
|
44943
|
+
const centeringOffset = Math.floor((end - start - GRID_ICON_EDGE_LENGTH) / 2);
|
|
44944
|
+
return end - GRID_ICON_EDGE_LENGTH - centeringOffset;
|
|
44945
|
+
}
|
|
44946
|
+
}
|
|
44947
|
+
getIconHorizontalPosition(rect, cellPosition) {
|
|
44948
|
+
const start = rect.x;
|
|
44949
|
+
const end = rect.x + rect.width;
|
|
44950
|
+
const cell = this.env.model.getters.getCell(cellPosition);
|
|
44951
|
+
const evaluatedCell = this.env.model.getters.getEvaluatedCell(cellPosition);
|
|
44952
|
+
const align = this.props.icon.horizontalAlign || cell?.style?.align || evaluatedCell.defaultAlign;
|
|
44953
|
+
switch (align) {
|
|
44954
|
+
case "right":
|
|
44955
|
+
return end - this.props.icon.size - this.props.icon.margin;
|
|
44956
|
+
case "left":
|
|
44957
|
+
return start + this.props.icon.margin;
|
|
44958
|
+
default:
|
|
44959
|
+
const centeringOffset = Math.floor((end - start - this.props.icon.size) / 2);
|
|
44960
|
+
return end - this.props.icon.size - centeringOffset;
|
|
44961
|
+
}
|
|
44962
|
+
}
|
|
44963
|
+
isPositionVisible(position) {
|
|
44964
|
+
const rect = this.env.model.getters.getVisibleRect(positionToZone(position));
|
|
44965
|
+
return !(rect.width === 0 || rect.height === 0);
|
|
44966
|
+
}
|
|
44967
|
+
}
|
|
44968
|
+
|
|
44969
|
+
class GridCellIconOverlay extends owl.Component {
|
|
44970
|
+
static template = "o-spreadsheet-GridCellIconOverlay";
|
|
44971
|
+
static props = {};
|
|
44972
|
+
static components = { GridCellIcon };
|
|
44973
|
+
get icons() {
|
|
44974
|
+
const icons = [];
|
|
44975
|
+
for (const position of this.env.model.getters.getVisibleCellPositions()) {
|
|
44976
|
+
const cellIcons = this.env.model.getters.getCellIcons(position);
|
|
44977
|
+
icons.push(...cellIcons.filter((icon) => icon.component));
|
|
44978
|
+
}
|
|
44979
|
+
return icons;
|
|
44980
|
+
}
|
|
44981
|
+
}
|
|
44982
|
+
|
|
44869
44983
|
/**
|
|
44870
44984
|
* Manages an event listener on a ref. Useful for hooks that want to manage
|
|
44871
44985
|
* event listeners, especially more than one. Prefer using t-on directly in
|
|
@@ -45117,9 +45231,8 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
45117
45231
|
};
|
|
45118
45232
|
static components = {
|
|
45119
45233
|
FiguresContainer,
|
|
45120
|
-
DataValidationOverlay,
|
|
45121
45234
|
GridAddRowsFooter,
|
|
45122
|
-
|
|
45235
|
+
GridCellIconOverlay,
|
|
45123
45236
|
};
|
|
45124
45237
|
static defaultProps = {
|
|
45125
45238
|
onCellDoubleClicked: () => { },
|
|
@@ -46129,13 +46242,12 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
46129
46242
|
// compute horizontal align start point parameter
|
|
46130
46243
|
let x = box.x;
|
|
46131
46244
|
if (align === "left") {
|
|
46132
|
-
|
|
46245
|
+
const leftIconSize = box.icons.left ? box.icons.left.size + box.icons.left.margin : 0;
|
|
46246
|
+
x += MIN_CELL_TEXT_MARGIN + leftIconSize;
|
|
46133
46247
|
}
|
|
46134
46248
|
else if (align === "right") {
|
|
46135
|
-
|
|
46136
|
-
|
|
46137
|
-
MIN_CELL_TEXT_MARGIN -
|
|
46138
|
-
(box.hasIcon ? GRID_ICON_EDGE_LENGTH + GRID_ICON_MARGIN : 0);
|
|
46249
|
+
const rightIconSize = box.icons.right ? box.icons.right.size + box.icons.right.margin : 0;
|
|
46250
|
+
x += box.width - MIN_CELL_TEXT_MARGIN - rightIconSize;
|
|
46139
46251
|
}
|
|
46140
46252
|
else {
|
|
46141
46253
|
x += box.width / 2;
|
|
@@ -46169,18 +46281,28 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
46169
46281
|
drawIcon(renderingContext, boxes) {
|
|
46170
46282
|
const { ctx } = renderingContext;
|
|
46171
46283
|
for (const box of boxes) {
|
|
46172
|
-
|
|
46284
|
+
for (const icon of Object.values(box.icons)) {
|
|
46285
|
+
if (!icon || !icon.svg) {
|
|
46286
|
+
continue;
|
|
46287
|
+
}
|
|
46173
46288
|
ctx.save();
|
|
46174
|
-
|
|
46175
|
-
|
|
46176
|
-
|
|
46177
|
-
|
|
46178
|
-
|
|
46289
|
+
ctx.beginPath();
|
|
46290
|
+
ctx.rect(box.x, box.y, box.width, box.height);
|
|
46291
|
+
ctx.clip();
|
|
46292
|
+
const iconSize = icon.size;
|
|
46293
|
+
const iconY = this.computeTextYCoordinate(box, iconSize);
|
|
46294
|
+
const svg = icon.svg;
|
|
46295
|
+
let x;
|
|
46296
|
+
if (icon.horizontalAlign === "left") {
|
|
46297
|
+
x = box.x + icon.margin;
|
|
46298
|
+
}
|
|
46299
|
+
else if (icon.horizontalAlign === "right") {
|
|
46300
|
+
x = box.x + box.width - iconSize - icon.margin;
|
|
46179
46301
|
}
|
|
46180
|
-
|
|
46181
|
-
|
|
46182
|
-
|
|
46183
|
-
ctx.translate(
|
|
46302
|
+
else {
|
|
46303
|
+
x = box.x + (box.width - iconSize) / 2;
|
|
46304
|
+
}
|
|
46305
|
+
ctx.translate(x, iconY);
|
|
46184
46306
|
ctx.scale(iconSize / svg.width, iconSize / svg.height);
|
|
46185
46307
|
ctx.fillStyle = svg.fillColor;
|
|
46186
46308
|
ctx.fill(new Path2D(svg.path));
|
|
@@ -46357,13 +46479,11 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
46357
46479
|
const position = { sheetId, col: col + 1, row };
|
|
46358
46480
|
const nextCell = this.getters.getEvaluatedCell(position);
|
|
46359
46481
|
const nextCellBorder = this.getters.getCellComputedBorder(position);
|
|
46360
|
-
const
|
|
46361
|
-
const cellHasCheckbox = this.getters.isCellValidCheckbox(position);
|
|
46482
|
+
const doesCellHaveGridIcon = this.getters.doesCellHaveGridIcon(position);
|
|
46362
46483
|
if (nextCell.type !== CellValueType.empty ||
|
|
46363
46484
|
this.getters.isInMerge(position) ||
|
|
46364
46485
|
nextCellBorder?.left ||
|
|
46365
|
-
|
|
46366
|
-
cellHasCheckbox) {
|
|
46486
|
+
doesCellHaveGridIcon) {
|
|
46367
46487
|
return col;
|
|
46368
46488
|
}
|
|
46369
46489
|
col++;
|
|
@@ -46377,13 +46497,11 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
46377
46497
|
const position = { sheetId, col: col - 1, row };
|
|
46378
46498
|
const previousCell = this.getters.getEvaluatedCell(position);
|
|
46379
46499
|
const previousCellBorder = this.getters.getCellComputedBorder(position);
|
|
46380
|
-
const
|
|
46381
|
-
const cellHasCheckbox = this.getters.isCellValidCheckbox(position);
|
|
46500
|
+
const doesCellHaveGridIcon = this.getters.doesCellHaveGridIcon(position);
|
|
46382
46501
|
if (previousCell.type !== CellValueType.empty ||
|
|
46383
46502
|
this.getters.isInMerge(position) ||
|
|
46384
46503
|
previousCellBorder?.right ||
|
|
46385
|
-
|
|
46386
|
-
cellHasCheckbox) {
|
|
46504
|
+
doesCellHaveGridIcon) {
|
|
46387
46505
|
return col;
|
|
46388
46506
|
}
|
|
46389
46507
|
col--;
|
|
@@ -46419,6 +46537,12 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
46419
46537
|
const dataBarFill = this.fingerprints.isEnabled
|
|
46420
46538
|
? undefined
|
|
46421
46539
|
: this.getters.getConditionalDataBar(position);
|
|
46540
|
+
const iconsList = this.getters.getCellIcons(position);
|
|
46541
|
+
const cellIcons = {
|
|
46542
|
+
left: iconsList.find((icon) => icon?.horizontalAlign === "left"),
|
|
46543
|
+
right: iconsList.find((icon) => icon?.horizontalAlign === "right"),
|
|
46544
|
+
center: iconsList.find((icon) => icon?.horizontalAlign === "center"),
|
|
46545
|
+
};
|
|
46422
46546
|
const box = {
|
|
46423
46547
|
x,
|
|
46424
46548
|
y,
|
|
@@ -46431,32 +46555,21 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
46431
46555
|
overlayColor: this.hoveredTables.overlayColors.get(position),
|
|
46432
46556
|
isError: (cell.type === CellValueType.error && !!cell.message) ||
|
|
46433
46557
|
this.getters.isDataValidationInvalid(position),
|
|
46558
|
+
icons: cellIcons,
|
|
46434
46559
|
};
|
|
46435
|
-
/** Icon */
|
|
46436
|
-
const iconSvg = this.getters.getCellIconSvg(position);
|
|
46437
46560
|
const fontSizePX = computeTextFontSizeInPixels(box.style);
|
|
46438
|
-
|
|
46439
|
-
if (iconSvg) {
|
|
46440
|
-
box.image = {
|
|
46441
|
-
type: "icon",
|
|
46442
|
-
size: fontSizePX,
|
|
46443
|
-
clipIcon: { x: box.x, y: box.y, width: Math.min(iconBoxWidth, width), height },
|
|
46444
|
-
svg: iconSvg,
|
|
46445
|
-
};
|
|
46446
|
-
}
|
|
46447
|
-
if (cell.type === CellValueType.empty || this.getters.isCellValidCheckbox(position)) {
|
|
46561
|
+
if (cell.type === CellValueType.empty || box.icons.center) {
|
|
46448
46562
|
return box;
|
|
46449
46563
|
}
|
|
46450
|
-
/** Filter Header or data validation icon */
|
|
46451
|
-
box.hasIcon = this.getters.doesCellHaveGridIcon(position);
|
|
46452
|
-
const headerIconWidth = box.hasIcon ? GRID_ICON_EDGE_LENGTH + GRID_ICON_MARGIN : 0;
|
|
46453
46564
|
/** Content */
|
|
46454
46565
|
const wrapping = style.wrapping || "overflow";
|
|
46455
46566
|
const wrapText = wrapping === "wrap" && !showFormula;
|
|
46456
46567
|
const maxWidth = width - 2 * MIN_CELL_TEXT_MARGIN;
|
|
46457
46568
|
const multiLineText = this.getters.getCellMultiLineText(position, { maxWidth, wrapText });
|
|
46458
46569
|
const textWidth = Math.max(...multiLineText.map((line) => this.getters.getTextWidth(line, style) + MIN_CELL_TEXT_MARGIN));
|
|
46459
|
-
const
|
|
46570
|
+
const leftIconWidth = box.icons.left ? box.icons.left.size + box.icons.left.margin : 0;
|
|
46571
|
+
const rightIconWidth = box.icons.right ? box.icons.right.size + box.icons.right.margin : 0;
|
|
46572
|
+
const contentWidth = leftIconWidth + textWidth + rightIconWidth;
|
|
46460
46573
|
const align = this.computeCellAlignment(position, contentWidth > width);
|
|
46461
46574
|
box.content = {
|
|
46462
46575
|
textLines: multiLineText,
|
|
@@ -46465,11 +46578,11 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
46465
46578
|
};
|
|
46466
46579
|
/** ClipRect */
|
|
46467
46580
|
const isOverflowing = contentWidth > width || fontSizePX > height;
|
|
46468
|
-
if (
|
|
46581
|
+
if (box.icons.left || box.icons.right) {
|
|
46469
46582
|
box.clipRect = {
|
|
46470
|
-
x: box.x +
|
|
46583
|
+
x: box.x + leftIconWidth,
|
|
46471
46584
|
y: box.y,
|
|
46472
|
-
width: Math.max(0, width -
|
|
46585
|
+
width: Math.max(0, width - leftIconWidth - rightIconWidth),
|
|
46473
46586
|
height,
|
|
46474
46587
|
};
|
|
46475
46588
|
}
|
|
@@ -48331,14 +48444,16 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
48331
48444
|
static components = { Section, TextStyler };
|
|
48332
48445
|
static props = {
|
|
48333
48446
|
title: { type: String, optional: true },
|
|
48447
|
+
placeholder: { type: String, optional: true },
|
|
48334
48448
|
updateTitle: Function,
|
|
48335
|
-
name: { type: String
|
|
48449
|
+
name: { type: String },
|
|
48336
48450
|
style: Object,
|
|
48337
48451
|
defaultStyle: { type: Object, optional: true },
|
|
48338
48452
|
updateStyle: Function,
|
|
48339
48453
|
};
|
|
48340
48454
|
static defaultProps = {
|
|
48341
48455
|
title: "",
|
|
48456
|
+
placeholder: "",
|
|
48342
48457
|
};
|
|
48343
48458
|
updateTitle(ev) {
|
|
48344
48459
|
this.props.updateTitle(ev.target.value);
|
|
@@ -49368,6 +49483,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
49368
49483
|
SidePanelCollapsible,
|
|
49369
49484
|
Section,
|
|
49370
49485
|
Checkbox,
|
|
49486
|
+
ChartTitle,
|
|
49371
49487
|
};
|
|
49372
49488
|
static props = {
|
|
49373
49489
|
figureId: String,
|
|
@@ -49392,9 +49508,6 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
49392
49508
|
translate(term) {
|
|
49393
49509
|
return _t(term);
|
|
49394
49510
|
}
|
|
49395
|
-
updateBaselineDescr(ev) {
|
|
49396
|
-
this.props.updateChart(this.props.figureId, { baselineDescr: ev.target.value });
|
|
49397
|
-
}
|
|
49398
49511
|
setColor(color, colorPickerId) {
|
|
49399
49512
|
switch (colorPickerId) {
|
|
49400
49513
|
case "backgroundColor":
|
|
@@ -49408,6 +49521,38 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
49408
49521
|
break;
|
|
49409
49522
|
}
|
|
49410
49523
|
}
|
|
49524
|
+
get keyStyle() {
|
|
49525
|
+
return {
|
|
49526
|
+
align: "center",
|
|
49527
|
+
fontSize: DEFAULT_SCORECARD_KEY_VALUE_FONT_SIZE,
|
|
49528
|
+
...this.props.definition.keyDescr,
|
|
49529
|
+
};
|
|
49530
|
+
}
|
|
49531
|
+
get baselineStyle() {
|
|
49532
|
+
return {
|
|
49533
|
+
align: "center",
|
|
49534
|
+
fontSize: DEFAULT_SCORECARD_BASELINE_FONT_SIZE,
|
|
49535
|
+
...this.props.definition.baselineDescr,
|
|
49536
|
+
};
|
|
49537
|
+
}
|
|
49538
|
+
setKeyText(text) {
|
|
49539
|
+
this.props.updateChart(this.props.figureId, {
|
|
49540
|
+
keyDescr: { ...this.props.definition.keyDescr, text },
|
|
49541
|
+
});
|
|
49542
|
+
}
|
|
49543
|
+
updateKeyStyle(style) {
|
|
49544
|
+
const keyDescr = { ...this.keyStyle, ...style };
|
|
49545
|
+
this.props.updateChart(this.props.figureId, { keyDescr });
|
|
49546
|
+
}
|
|
49547
|
+
setBaselineText(text) {
|
|
49548
|
+
this.props.updateChart(this.props.figureId, {
|
|
49549
|
+
baselineDescr: { ...this.props.definition.baselineDescr, text },
|
|
49550
|
+
});
|
|
49551
|
+
}
|
|
49552
|
+
updateBaselineStyle(style) {
|
|
49553
|
+
const baselineDescr = { ...this.baselineStyle, ...style };
|
|
49554
|
+
this.props.updateChart(this.props.figureId, { baselineDescr });
|
|
49555
|
+
}
|
|
49411
49556
|
}
|
|
49412
49557
|
|
|
49413
49558
|
class SunburstChartDesignPanel extends owl.Component {
|
|
@@ -52318,6 +52463,9 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
52318
52463
|
}
|
|
52319
52464
|
return undefined;
|
|
52320
52465
|
}
|
|
52466
|
+
get isCalculatedMeasureInvalid() {
|
|
52467
|
+
return this.env.model.getters.getMeasureCompiledFormula(this.props.measure).isBadExpression;
|
|
52468
|
+
}
|
|
52321
52469
|
}
|
|
52322
52470
|
|
|
52323
52471
|
css /* scss */ `
|
|
@@ -52808,11 +52956,13 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
52808
52956
|
columns;
|
|
52809
52957
|
rows;
|
|
52810
52958
|
sortedColumn;
|
|
52959
|
+
collapsedDomains;
|
|
52811
52960
|
constructor(definition, fields) {
|
|
52812
52961
|
this.measures = definition.measures.map((measure) => createMeasure(fields, measure));
|
|
52813
52962
|
this.columns = definition.columns.map((dimension) => createPivotDimension(fields, dimension));
|
|
52814
52963
|
this.rows = definition.rows.map((dimension) => createPivotDimension(fields, dimension));
|
|
52815
52964
|
this.sortedColumn = definition.sortedColumn;
|
|
52965
|
+
this.collapsedDomains = definition.collapsedDomains;
|
|
52816
52966
|
}
|
|
52817
52967
|
getDimension(nameWithGranularity) {
|
|
52818
52968
|
const dimension = this.columns.find((d) => d.nameWithGranularity === nameWithGranularity) ||
|
|
@@ -52967,24 +53117,62 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
52967
53117
|
rowTree;
|
|
52968
53118
|
colTree;
|
|
52969
53119
|
isSorted = false;
|
|
52970
|
-
constructor(columns, rows, measures, fieldsType) {
|
|
52971
|
-
this.
|
|
53120
|
+
constructor(columns, rows, measures, fieldsType, collapsedDomains = { COL: [], ROW: [] }) {
|
|
53121
|
+
this.measures = measures;
|
|
53122
|
+
this.fieldsType = fieldsType;
|
|
53123
|
+
if (collapsedDomains.COL.length) {
|
|
53124
|
+
columns = this.removeCollapsedColumns(columns, measures, collapsedDomains.COL);
|
|
53125
|
+
}
|
|
53126
|
+
this.columns = columns.map((cols) => {
|
|
52972
53127
|
// offset in the pivot table
|
|
52973
53128
|
// starts at 1 because the first column is the row title
|
|
52974
53129
|
let offset = 1;
|
|
52975
|
-
return
|
|
53130
|
+
return cols.map((col) => {
|
|
52976
53131
|
col = { ...col, offset };
|
|
52977
53132
|
offset += col.width;
|
|
52978
53133
|
return col;
|
|
52979
53134
|
});
|
|
52980
53135
|
});
|
|
52981
|
-
this.rows = rows;
|
|
52982
|
-
this.measures = measures;
|
|
52983
|
-
this.fieldsType = fieldsType;
|
|
53136
|
+
this.rows = rows.filter((row) => !this.isParentCollapsed(collapsedDomains.ROW, row));
|
|
52984
53137
|
this.maxIndent = Math.max(...this.rows.map((row) => row.indent));
|
|
52985
53138
|
this.rowTree = lazy(() => this.buildRowsTree());
|
|
52986
53139
|
this.colTree = lazy(() => this.buildColumnsTree());
|
|
52987
53140
|
}
|
|
53141
|
+
removeCollapsedColumns(columns, measures, collapsedDomains) {
|
|
53142
|
+
const replaceCollapsedChildrenWithSubTotalColumns = (parentCol, depth) => {
|
|
53143
|
+
const parentDomain = this.getDomain(parentCol);
|
|
53144
|
+
const cols = columns[depth];
|
|
53145
|
+
const startIndex = cols.findIndex((col) => isParentDomain(this.getDomain(col), parentDomain));
|
|
53146
|
+
const endIndex = cols.findLastIndex((col) => isParentDomain(this.getDomain(col), parentDomain));
|
|
53147
|
+
const isLeaf = depth === columns.length - 1;
|
|
53148
|
+
const newColumns = measures.map((measure) => {
|
|
53149
|
+
const fields = isLeaf ? [...parentCol.fields, "measure"] : [];
|
|
53150
|
+
const values = isLeaf ? [...parentCol.values, measure] : [];
|
|
53151
|
+
return { fields, values, width: 1, offset: 0, collapsedHeader: !isLeaf };
|
|
53152
|
+
});
|
|
53153
|
+
cols.splice(startIndex, endIndex - startIndex + 1, ...newColumns);
|
|
53154
|
+
};
|
|
53155
|
+
return columns.map((cols, i) => {
|
|
53156
|
+
for (const col of cols) {
|
|
53157
|
+
if (i >= columns.length - 2) {
|
|
53158
|
+
return cols;
|
|
53159
|
+
}
|
|
53160
|
+
const domain = this.getDomain(col);
|
|
53161
|
+
if (!collapsedDomains.some((collapsedDomain) => deepEquals(domain, collapsedDomain))) {
|
|
53162
|
+
continue;
|
|
53163
|
+
}
|
|
53164
|
+
col.width = measures.length;
|
|
53165
|
+
for (let depth = i + 1; depth < columns.length; depth++) {
|
|
53166
|
+
replaceCollapsedChildrenWithSubTotalColumns(col, depth);
|
|
53167
|
+
}
|
|
53168
|
+
}
|
|
53169
|
+
return cols;
|
|
53170
|
+
});
|
|
53171
|
+
}
|
|
53172
|
+
isParentCollapsed(collapsedDomains, dim) {
|
|
53173
|
+
const domain = this.getDomain(dim);
|
|
53174
|
+
return collapsedDomains.some((collapsedDomain) => isParentDomain(domain, collapsedDomain));
|
|
53175
|
+
}
|
|
52988
53176
|
/**
|
|
52989
53177
|
* Get the number of columns leafs (i.e. the number of the last row of columns)
|
|
52990
53178
|
*/
|
|
@@ -53040,19 +53228,19 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
53040
53228
|
}
|
|
53041
53229
|
else if (row <= colHeadersHeight - 1) {
|
|
53042
53230
|
const domain = this.getColHeaderDomain(col, row);
|
|
53043
|
-
return domain ? { type: "HEADER", domain } : EMPTY_PIVOT_CELL;
|
|
53231
|
+
return domain ? { type: "HEADER", domain, dimension: "COL" } : EMPTY_PIVOT_CELL;
|
|
53044
53232
|
}
|
|
53045
53233
|
else if (col === 0) {
|
|
53046
53234
|
const rowIndex = row - colHeadersHeight;
|
|
53047
|
-
const domain = this.
|
|
53048
|
-
return { type: "HEADER", domain };
|
|
53235
|
+
const domain = this.getDomain(this.rows[rowIndex]);
|
|
53236
|
+
return { type: "HEADER", domain, dimension: "ROW" };
|
|
53049
53237
|
}
|
|
53050
53238
|
else {
|
|
53051
53239
|
const rowIndex = row - colHeadersHeight;
|
|
53052
53240
|
if (!includeTotal && this.isTotalRow(rowIndex)) {
|
|
53053
53241
|
return EMPTY_PIVOT_CELL;
|
|
53054
53242
|
}
|
|
53055
|
-
const domain = [...this.
|
|
53243
|
+
const domain = [...this.getDomain(this.rows[rowIndex]), ...this.getColDomain(col)];
|
|
53056
53244
|
const measure = this.getColMeasure(col);
|
|
53057
53245
|
return { type: "VALUE", domain, measure };
|
|
53058
53246
|
}
|
|
@@ -53061,31 +53249,31 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
53061
53249
|
if (col === 0) {
|
|
53062
53250
|
return undefined;
|
|
53063
53251
|
}
|
|
53064
|
-
const domain = [];
|
|
53065
53252
|
const pivotCol = this.columns[row].find((pivotCol) => pivotCol.offset === col);
|
|
53066
|
-
if (!pivotCol) {
|
|
53253
|
+
if (!pivotCol || pivotCol.collapsedHeader) {
|
|
53067
53254
|
return undefined;
|
|
53068
53255
|
}
|
|
53069
|
-
|
|
53070
|
-
|
|
53256
|
+
return this.getDomain(pivotCol);
|
|
53257
|
+
}
|
|
53258
|
+
getDomain(dim) {
|
|
53259
|
+
return dim.fields.map((fieldWithGranularity, i) => {
|
|
53071
53260
|
if (fieldWithGranularity === "measure") {
|
|
53072
|
-
|
|
53261
|
+
return {
|
|
53073
53262
|
type: "char",
|
|
53074
53263
|
field: fieldWithGranularity,
|
|
53075
|
-
value: toNormalizedPivotValue({ displayName: "measure", type: "char" },
|
|
53076
|
-
}
|
|
53264
|
+
value: toNormalizedPivotValue({ displayName: "measure", type: "char" }, dim.values[i]),
|
|
53265
|
+
};
|
|
53077
53266
|
}
|
|
53078
53267
|
else {
|
|
53079
53268
|
const { fieldName, granularity } = parseDimension(fieldWithGranularity);
|
|
53080
53269
|
const type = this.fieldsType[fieldName] || "char";
|
|
53081
|
-
|
|
53270
|
+
return {
|
|
53082
53271
|
type,
|
|
53083
53272
|
field: fieldWithGranularity,
|
|
53084
|
-
value: toNormalizedPivotValue({ displayName: fieldName, type, granularity },
|
|
53085
|
-
}
|
|
53273
|
+
value: toNormalizedPivotValue({ displayName: fieldName, type, granularity }, dim.values[i]),
|
|
53274
|
+
};
|
|
53086
53275
|
}
|
|
53087
|
-
}
|
|
53088
|
-
return domain;
|
|
53276
|
+
});
|
|
53089
53277
|
}
|
|
53090
53278
|
getColDomain(col) {
|
|
53091
53279
|
const domain = this.getColHeaderDomain(col, this.columns.length - 1);
|
|
@@ -53099,20 +53287,6 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
53099
53287
|
}
|
|
53100
53288
|
return measure.toString();
|
|
53101
53289
|
}
|
|
53102
|
-
getRowDomain(row) {
|
|
53103
|
-
const domain = [];
|
|
53104
|
-
for (let i = 0; i < this.rows[row].fields.length; i++) {
|
|
53105
|
-
const fieldWithGranularity = this.rows[row].fields[i];
|
|
53106
|
-
const { fieldName, granularity } = parseDimension(fieldWithGranularity);
|
|
53107
|
-
const type = this.fieldsType[fieldName] || "char";
|
|
53108
|
-
domain.push({
|
|
53109
|
-
type,
|
|
53110
|
-
field: fieldWithGranularity,
|
|
53111
|
-
value: toNormalizedPivotValue({ displayName: fieldName, type, granularity }, this.rows[row].values[i]),
|
|
53112
|
-
});
|
|
53113
|
-
}
|
|
53114
|
-
return domain;
|
|
53115
|
-
}
|
|
53116
53290
|
buildRowsTree() {
|
|
53117
53291
|
const tree = [];
|
|
53118
53292
|
let depth = 0;
|
|
@@ -53217,7 +53391,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
53217
53391
|
/**
|
|
53218
53392
|
* This function converts a list of data entry into a spreadsheet pivot table.
|
|
53219
53393
|
*/
|
|
53220
|
-
function dataEntriesToSpreadsheetPivotTable(dataEntries, definition) {
|
|
53394
|
+
function dataEntriesToSpreadsheetPivotTable(dataEntries, definition, mode) {
|
|
53221
53395
|
const measureIds = definition.measures.filter((measure) => !measure.isHidden).map((m) => m.id);
|
|
53222
53396
|
const columnsTree = dataEntriesToColumnsTree(dataEntries, definition.columns, 0);
|
|
53223
53397
|
computeWidthOfColumnsNodes(columnsTree, measureIds.length);
|
|
@@ -53236,7 +53410,8 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
53236
53410
|
for (const row of definition.rows) {
|
|
53237
53411
|
fieldsType[row.fieldName] = row.type;
|
|
53238
53412
|
}
|
|
53239
|
-
|
|
53413
|
+
const collapsedDomains = mode === "collapsed" ? definition.collapsedDomains : undefined;
|
|
53414
|
+
return new SpreadsheetPivotTable(cols, rows, measureIds, fieldsType, collapsedDomains);
|
|
53240
53415
|
}
|
|
53241
53416
|
// -----------------------------------------------------------------------------
|
|
53242
53417
|
// ROWS
|
|
@@ -53609,7 +53784,8 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
53609
53784
|
* This object contains the pivot table structure. It is created from the
|
|
53610
53785
|
* data entries and the pivot definition.
|
|
53611
53786
|
*/
|
|
53612
|
-
|
|
53787
|
+
collapsedTable;
|
|
53788
|
+
expandedTable;
|
|
53613
53789
|
/**
|
|
53614
53790
|
* This error is set when the range is invalid. It is used to show an error
|
|
53615
53791
|
* message to the user.
|
|
@@ -53642,7 +53818,8 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
53642
53818
|
this.dataEntries = this.loadData();
|
|
53643
53819
|
}
|
|
53644
53820
|
if (type >= ReloadType.TABLE) {
|
|
53645
|
-
this.
|
|
53821
|
+
this.collapsedTable = undefined;
|
|
53822
|
+
this.expandedTable = undefined;
|
|
53646
53823
|
}
|
|
53647
53824
|
}
|
|
53648
53825
|
onDefinitionChange(nextDefinition) {
|
|
@@ -53802,14 +53979,23 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
53802
53979
|
}
|
|
53803
53980
|
return values;
|
|
53804
53981
|
}
|
|
53805
|
-
|
|
53982
|
+
getCollapsedTableStructure() {
|
|
53806
53983
|
if (!this.isValid()) {
|
|
53807
53984
|
throw new Error("Pivot is not valid !");
|
|
53808
53985
|
}
|
|
53809
|
-
if (!this.
|
|
53810
|
-
this.
|
|
53986
|
+
if (!this.collapsedTable) {
|
|
53987
|
+
this.collapsedTable = dataEntriesToSpreadsheetPivotTable(this.dataEntries, this.definition, "collapsed");
|
|
53988
|
+
}
|
|
53989
|
+
return this.collapsedTable;
|
|
53990
|
+
}
|
|
53991
|
+
getExpandedTableStructure() {
|
|
53992
|
+
if (!this.isValid()) {
|
|
53993
|
+
throw new Error("Pivot is not valid !");
|
|
53994
|
+
}
|
|
53995
|
+
if (!this.expandedTable) {
|
|
53996
|
+
this.expandedTable = dataEntriesToSpreadsheetPivotTable(this.dataEntries, this.definition, "expanded");
|
|
53811
53997
|
}
|
|
53812
|
-
return this.
|
|
53998
|
+
return this.expandedTable;
|
|
53813
53999
|
}
|
|
53814
54000
|
getFields() {
|
|
53815
54001
|
return this.metaData.fields;
|
|
@@ -54167,6 +54353,13 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
54167
54353
|
})),
|
|
54168
54354
|
sortedColumn: this.shouldKeepSortedColumn(definition) ? definition.sortedColumn : undefined,
|
|
54169
54355
|
};
|
|
54356
|
+
if (cleanedDefinition.collapsedDomains) {
|
|
54357
|
+
const { COL, ROW } = cleanedDefinition.collapsedDomains;
|
|
54358
|
+
cleanedDefinition.collapsedDomains = {
|
|
54359
|
+
COL: COL.filter((domain) => this.areDomainFieldsValid(domain, cleanedDefinition.columns)),
|
|
54360
|
+
ROW: ROW.filter((domain) => this.areDomainFieldsValid(domain, cleanedDefinition.rows)),
|
|
54361
|
+
};
|
|
54362
|
+
}
|
|
54170
54363
|
if (!this.draft && deepEquals(coreDefinition, cleanedDefinition)) {
|
|
54171
54364
|
return;
|
|
54172
54365
|
}
|
|
@@ -54256,6 +54449,15 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
54256
54449
|
return (newDefinition.measures.find((measure) => measure.id === sortedColumn.measure) &&
|
|
54257
54450
|
deepEquals(oldDefinition.columns, newDefinition.columns));
|
|
54258
54451
|
}
|
|
54452
|
+
areDomainFieldsValid(domain, dims) {
|
|
54453
|
+
const fieldsNameWithGranularity = dims.map(({ fieldName, granularity }) => fieldName + (granularity ? `:${granularity}` : ""));
|
|
54454
|
+
for (let i = 0; i < domain.length; i++) {
|
|
54455
|
+
if (domain[i].field !== fieldsNameWithGranularity[i]) {
|
|
54456
|
+
return false;
|
|
54457
|
+
}
|
|
54458
|
+
}
|
|
54459
|
+
return true;
|
|
54460
|
+
}
|
|
54259
54461
|
}
|
|
54260
54462
|
|
|
54261
54463
|
class PivotSpreadsheetSidePanel extends owl.Component {
|
|
@@ -55523,6 +55725,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
55523
55725
|
composerFocusStore;
|
|
55524
55726
|
DOMFocusableElementStore;
|
|
55525
55727
|
paintFormatStore;
|
|
55728
|
+
clientFocusStore;
|
|
55526
55729
|
dragNDropGrid = useDragAndDropBeyondTheViewport(this.env);
|
|
55527
55730
|
onMouseWheel;
|
|
55528
55731
|
hoveredCell;
|
|
@@ -55540,6 +55743,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
55540
55743
|
this.DOMFocusableElementStore = useStore(DOMFocusableElementStore);
|
|
55541
55744
|
this.sidePanel = useStore(SidePanelStore);
|
|
55542
55745
|
this.paintFormatStore = useStore(PaintFormatStore);
|
|
55746
|
+
this.clientFocusStore = useStore(ClientFocusStore);
|
|
55543
55747
|
useStore(ArrayFormulaHighlight);
|
|
55544
55748
|
owl.useChildSubEnv({ getPopoverContainerRect: () => this.getGridRect() });
|
|
55545
55749
|
owl.useExternalListener(document.body, "cut", this.copy.bind(this, true));
|
|
@@ -55822,6 +56026,9 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
55822
56026
|
isCellHovered(col, row) {
|
|
55823
56027
|
return this.hoveredCell.col === col && this.hoveredCell.row === row;
|
|
55824
56028
|
}
|
|
56029
|
+
get focusedClients() {
|
|
56030
|
+
return this.clientFocusStore.focusedClients;
|
|
56031
|
+
}
|
|
55825
56032
|
getGridRect() {
|
|
55826
56033
|
return {
|
|
55827
56034
|
...getRefBoundingRect(this.gridRef),
|
|
@@ -56140,6 +56347,51 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
56140
56347
|
const supportedPivotPositionalFormulaRegistry = new Registry();
|
|
56141
56348
|
supportedPivotPositionalFormulaRegistry.add("SPREADSHEET", false);
|
|
56142
56349
|
|
|
56350
|
+
class FullScreenChart extends owl.Component {
|
|
56351
|
+
static template = "o-spreadsheet-FullScreenChart";
|
|
56352
|
+
static props = {};
|
|
56353
|
+
static components = { ChartDashboardMenu };
|
|
56354
|
+
fullScreenChartStore;
|
|
56355
|
+
ref = owl.useRef("fullScreenChart");
|
|
56356
|
+
spreadsheetRect = useSpreadsheetRect();
|
|
56357
|
+
figureRegistry = figureRegistry;
|
|
56358
|
+
setup() {
|
|
56359
|
+
this.fullScreenChartStore = useStore(FullScreenChartStore);
|
|
56360
|
+
const animationStore = useStore(ChartAnimationStore);
|
|
56361
|
+
let lastFigureId = undefined;
|
|
56362
|
+
owl.onWillUpdateProps(() => {
|
|
56363
|
+
if (lastFigureId !== this.figureUI?.id) {
|
|
56364
|
+
animationStore.enableAnimationForChart(this.figureUI?.id + "-fullscreen");
|
|
56365
|
+
}
|
|
56366
|
+
lastFigureId = this.figureUI?.id;
|
|
56367
|
+
});
|
|
56368
|
+
owl.useEffect((el) => el?.focus(), () => [this.ref.el]);
|
|
56369
|
+
}
|
|
56370
|
+
get figureUI() {
|
|
56371
|
+
return this.fullScreenChartStore.fullScreenFigure;
|
|
56372
|
+
}
|
|
56373
|
+
exitFullScreen() {
|
|
56374
|
+
if (this.figureUI) {
|
|
56375
|
+
this.fullScreenChartStore.toggleFullScreenChart(this.figureUI.id);
|
|
56376
|
+
}
|
|
56377
|
+
}
|
|
56378
|
+
onKeyDown(ev) {
|
|
56379
|
+
if (ev.key === "Escape") {
|
|
56380
|
+
this.exitFullScreen();
|
|
56381
|
+
}
|
|
56382
|
+
}
|
|
56383
|
+
get chartComponent() {
|
|
56384
|
+
if (!this.figureUI)
|
|
56385
|
+
return undefined;
|
|
56386
|
+
const type = this.env.model.getters.getChartType(this.figureUI.id);
|
|
56387
|
+
const component = chartComponentRegistry.get(type);
|
|
56388
|
+
if (!component) {
|
|
56389
|
+
throw new Error(`Component is not defined for type ${type}`);
|
|
56390
|
+
}
|
|
56391
|
+
return component;
|
|
56392
|
+
}
|
|
56393
|
+
}
|
|
56394
|
+
|
|
56143
56395
|
css /* scss */ `
|
|
56144
56396
|
.o_pivot_html_renderer {
|
|
56145
56397
|
width: 100%;
|
|
@@ -56194,7 +56446,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
56194
56446
|
showMissingValuesOnly: false,
|
|
56195
56447
|
});
|
|
56196
56448
|
setup() {
|
|
56197
|
-
const table = this.pivot.
|
|
56449
|
+
const table = this.pivot.getExpandedTableStructure();
|
|
56198
56450
|
const formulaId = this.env.model.getters.getPivotFormulaId(this.props.pivotId);
|
|
56199
56451
|
this.data = {
|
|
56200
56452
|
columns: this._buildColHeaders(formulaId, table),
|
|
@@ -56238,7 +56490,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
56238
56490
|
* The parent of "January" is "Australia"
|
|
56239
56491
|
*/
|
|
56240
56492
|
addRecursiveRow(index) {
|
|
56241
|
-
const rows = this.pivot.
|
|
56493
|
+
const rows = this.pivot.getExpandedTableStructure().rows;
|
|
56242
56494
|
const row = [...rows[index].values];
|
|
56243
56495
|
if (row.length <= 1) {
|
|
56244
56496
|
return [index];
|
|
@@ -63647,10 +63899,9 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
63647
63899
|
return this.evaluatedCells.keysForSheet(sheetId);
|
|
63648
63900
|
}
|
|
63649
63901
|
getArrayFormulaSpreadingOn(position) {
|
|
63650
|
-
const
|
|
63651
|
-
|
|
63652
|
-
|
|
63653
|
-
return this.spreadingRelations.isArrayFormula(position) ? position : undefined;
|
|
63902
|
+
const isEmpty = this.getEvaluatedCell(position).type === CellValueType.empty;
|
|
63903
|
+
if (isEmpty) {
|
|
63904
|
+
return undefined;
|
|
63654
63905
|
}
|
|
63655
63906
|
const arrayFormulas = this.spreadingRelations.searchFormulaPositionsSpreadingOn(position.sheetId, positionToZone(position));
|
|
63656
63907
|
return Array.from(arrayFormulas).find((position) => !this.blockedArrayFormulas.has(position));
|
|
@@ -65052,6 +65303,323 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
65052
65303
|
}
|
|
65053
65304
|
}
|
|
65054
65305
|
|
|
65306
|
+
const MARGIN = (GRID_ICON_EDGE_LENGTH - CHECKBOX_WIDTH) / 2;
|
|
65307
|
+
css /* scss */ `
|
|
65308
|
+
.o-dv-checkbox {
|
|
65309
|
+
margin: ${MARGIN}px;
|
|
65310
|
+
/* required to prevent the checkbox position to be sensible to the font-size (affects Firefox) */
|
|
65311
|
+
position: absolute;
|
|
65312
|
+
}
|
|
65313
|
+
`;
|
|
65314
|
+
class DataValidationCheckbox extends owl.Component {
|
|
65315
|
+
static template = "o-spreadsheet-DataValidationCheckbox";
|
|
65316
|
+
static components = {
|
|
65317
|
+
Checkbox,
|
|
65318
|
+
};
|
|
65319
|
+
static props = {
|
|
65320
|
+
cellPosition: Object,
|
|
65321
|
+
};
|
|
65322
|
+
onCheckboxChange(value) {
|
|
65323
|
+
const { sheetId, col, row } = this.props.cellPosition;
|
|
65324
|
+
const cellContent = value ? "TRUE" : "FALSE";
|
|
65325
|
+
this.env.model.dispatch("UPDATE_CELL", { sheetId, col, row, content: cellContent });
|
|
65326
|
+
}
|
|
65327
|
+
get checkBoxValue() {
|
|
65328
|
+
return !!this.env.model.getters.getEvaluatedCell(this.props.cellPosition).value;
|
|
65329
|
+
}
|
|
65330
|
+
get isDisabled() {
|
|
65331
|
+
const cell = this.env.model.getters.getCell(this.props.cellPosition);
|
|
65332
|
+
return this.env.model.getters.isReadonly() || !!cell?.isFormula;
|
|
65333
|
+
}
|
|
65334
|
+
}
|
|
65335
|
+
|
|
65336
|
+
const ICON_WIDTH = 13;
|
|
65337
|
+
css /* scss */ `
|
|
65338
|
+
.o-dv-list-icon {
|
|
65339
|
+
color: ${TEXT_BODY_MUTED};
|
|
65340
|
+
border-radius: 1px;
|
|
65341
|
+
height: ${GRID_ICON_EDGE_LENGTH}px;
|
|
65342
|
+
width: ${GRID_ICON_EDGE_LENGTH}px;
|
|
65343
|
+
|
|
65344
|
+
&:hover {
|
|
65345
|
+
color: #ffffff;
|
|
65346
|
+
background-color: ${TEXT_BODY_MUTED};
|
|
65347
|
+
}
|
|
65348
|
+
|
|
65349
|
+
svg {
|
|
65350
|
+
width: ${ICON_WIDTH}px;
|
|
65351
|
+
height: ${ICON_WIDTH}px;
|
|
65352
|
+
}
|
|
65353
|
+
}
|
|
65354
|
+
`;
|
|
65355
|
+
class DataValidationListIcon extends owl.Component {
|
|
65356
|
+
static template = "o-spreadsheet-DataValidationListIcon";
|
|
65357
|
+
static props = {
|
|
65358
|
+
cellPosition: Object,
|
|
65359
|
+
};
|
|
65360
|
+
onClick() {
|
|
65361
|
+
const { col, row } = this.props.cellPosition;
|
|
65362
|
+
this.env.model.selection.selectCell(col, row);
|
|
65363
|
+
this.env.startCellEdition();
|
|
65364
|
+
}
|
|
65365
|
+
}
|
|
65366
|
+
|
|
65367
|
+
css /* scss */ `
|
|
65368
|
+
.o-filter-icon {
|
|
65369
|
+
color: ${FILTERS_COLOR};
|
|
65370
|
+
display: flex;
|
|
65371
|
+
align-items: center;
|
|
65372
|
+
justify-content: center;
|
|
65373
|
+
width: ${GRID_ICON_EDGE_LENGTH}px;
|
|
65374
|
+
height: ${GRID_ICON_EDGE_LENGTH}px;
|
|
65375
|
+
|
|
65376
|
+
&:hover {
|
|
65377
|
+
background: ${FILTERS_COLOR};
|
|
65378
|
+
color: #fff;
|
|
65379
|
+
}
|
|
65380
|
+
|
|
65381
|
+
&.o-high-contrast {
|
|
65382
|
+
color: #defade;
|
|
65383
|
+
}
|
|
65384
|
+
&.o-high-contrast:hover {
|
|
65385
|
+
color: ${FILTERS_COLOR};
|
|
65386
|
+
background: #fff;
|
|
65387
|
+
}
|
|
65388
|
+
}
|
|
65389
|
+
.o-filter-icon:hover {
|
|
65390
|
+
background: ${FILTERS_COLOR};
|
|
65391
|
+
color: #fff;
|
|
65392
|
+
}
|
|
65393
|
+
`;
|
|
65394
|
+
class FilterIcon extends owl.Component {
|
|
65395
|
+
static template = "o-spreadsheet-FilterIcon";
|
|
65396
|
+
static props = {
|
|
65397
|
+
cellPosition: Object,
|
|
65398
|
+
};
|
|
65399
|
+
cellPopovers;
|
|
65400
|
+
setup() {
|
|
65401
|
+
this.cellPopovers = useStore(CellPopoverStore);
|
|
65402
|
+
}
|
|
65403
|
+
onClick() {
|
|
65404
|
+
const position = this.props.cellPosition;
|
|
65405
|
+
const activePopover = this.cellPopovers.persistentCellPopover;
|
|
65406
|
+
const { col, row } = position;
|
|
65407
|
+
if (activePopover.isOpen &&
|
|
65408
|
+
activePopover.col === col &&
|
|
65409
|
+
activePopover.row === row &&
|
|
65410
|
+
activePopover.type === "FilterMenu") {
|
|
65411
|
+
this.cellPopovers.close();
|
|
65412
|
+
return;
|
|
65413
|
+
}
|
|
65414
|
+
this.cellPopovers.open({ col, row }, "FilterMenu");
|
|
65415
|
+
}
|
|
65416
|
+
get isFilterActive() {
|
|
65417
|
+
return this.env.model.getters.isFilterActive(this.props.cellPosition);
|
|
65418
|
+
}
|
|
65419
|
+
get iconClass() {
|
|
65420
|
+
const cellStyle = this.env.model.getters.getCellComputedStyle(this.props.cellPosition);
|
|
65421
|
+
const luminance = relativeLuminance(cellStyle.fillColor || "#fff");
|
|
65422
|
+
return luminance < 0.45 ? "o-high-contrast" : "";
|
|
65423
|
+
}
|
|
65424
|
+
}
|
|
65425
|
+
|
|
65426
|
+
css /* scss */ `
|
|
65427
|
+
.o-spreadsheet {
|
|
65428
|
+
.o-pivot-collapse-icon {
|
|
65429
|
+
cursor: pointer;
|
|
65430
|
+
width: 11px;
|
|
65431
|
+
height: 11px;
|
|
65432
|
+
border: 1px solid #777;
|
|
65433
|
+
background-color: #eee;
|
|
65434
|
+
margin: 3px 0 3px 6px;
|
|
65435
|
+
|
|
65436
|
+
.o-icon {
|
|
65437
|
+
width: 5px;
|
|
65438
|
+
height: 5px;
|
|
65439
|
+
}
|
|
65440
|
+
}
|
|
65441
|
+
}
|
|
65442
|
+
`;
|
|
65443
|
+
class PivotCollapseIcon extends owl.Component {
|
|
65444
|
+
static template = "o-spreadsheet-PivotCollapseIcon";
|
|
65445
|
+
static props = {
|
|
65446
|
+
cellPosition: Object,
|
|
65447
|
+
};
|
|
65448
|
+
onClick() {
|
|
65449
|
+
const pivotCell = this.env.model.getters.getPivotCellFromPosition(this.props.cellPosition);
|
|
65450
|
+
const pivotId = this.env.model.getters.getPivotIdFromPosition(this.props.cellPosition);
|
|
65451
|
+
if (!pivotId || pivotCell.type !== "HEADER") {
|
|
65452
|
+
return;
|
|
65453
|
+
}
|
|
65454
|
+
const definition = this.env.model.getters.getPivotCoreDefinition(pivotId);
|
|
65455
|
+
const collapsedDomains = definition.collapsedDomains?.[pivotCell.dimension]
|
|
65456
|
+
? [...definition.collapsedDomains[pivotCell.dimension]]
|
|
65457
|
+
: [];
|
|
65458
|
+
const index = collapsedDomains.findIndex((domain) => deepEquals(domain, pivotCell.domain));
|
|
65459
|
+
if (index !== -1) {
|
|
65460
|
+
collapsedDomains.splice(index, 1);
|
|
65461
|
+
}
|
|
65462
|
+
else {
|
|
65463
|
+
collapsedDomains.push(pivotCell.domain);
|
|
65464
|
+
}
|
|
65465
|
+
const newDomains = definition.collapsedDomains
|
|
65466
|
+
? { ...definition.collapsedDomains }
|
|
65467
|
+
: { COL: [], ROW: [] };
|
|
65468
|
+
newDomains[pivotCell.dimension] = collapsedDomains;
|
|
65469
|
+
this.env.model.dispatch("UPDATE_PIVOT", {
|
|
65470
|
+
pivotId,
|
|
65471
|
+
pivot: { ...definition, collapsedDomains: newDomains },
|
|
65472
|
+
});
|
|
65473
|
+
}
|
|
65474
|
+
get isCollapsed() {
|
|
65475
|
+
const pivotCell = this.env.model.getters.getPivotCellFromPosition(this.props.cellPosition);
|
|
65476
|
+
const pivotId = this.env.model.getters.getPivotIdFromPosition(this.props.cellPosition);
|
|
65477
|
+
if (!pivotId || pivotCell.type !== "HEADER") {
|
|
65478
|
+
return false;
|
|
65479
|
+
}
|
|
65480
|
+
const definition = this.env.model.getters.getPivotCoreDefinition(pivotId);
|
|
65481
|
+
const domains = definition.collapsedDomains?.[pivotCell.dimension] ?? [];
|
|
65482
|
+
return domains?.some((domain) => deepEquals(domain, pivotCell.domain));
|
|
65483
|
+
}
|
|
65484
|
+
}
|
|
65485
|
+
|
|
65486
|
+
/**
|
|
65487
|
+
* Registry to draw icons on cells
|
|
65488
|
+
*/
|
|
65489
|
+
const iconsOnCellRegistry = new Registry();
|
|
65490
|
+
iconsOnCellRegistry.add("data_validation_checkbox", (getters, position) => {
|
|
65491
|
+
const hasIcon = getters.isCellValidCheckbox(position);
|
|
65492
|
+
if (hasIcon) {
|
|
65493
|
+
return {
|
|
65494
|
+
svg: undefined,
|
|
65495
|
+
priority: 2,
|
|
65496
|
+
horizontalAlign: "center",
|
|
65497
|
+
size: GRID_ICON_EDGE_LENGTH,
|
|
65498
|
+
margin: GRID_ICON_MARGIN,
|
|
65499
|
+
component: DataValidationCheckbox,
|
|
65500
|
+
position,
|
|
65501
|
+
};
|
|
65502
|
+
}
|
|
65503
|
+
return undefined;
|
|
65504
|
+
});
|
|
65505
|
+
iconsOnCellRegistry.add("data_validation_list_icon", (getters, position) => {
|
|
65506
|
+
const hasIcon = !getters.isReadonly() && getters.cellHasListDataValidationIcon(position);
|
|
65507
|
+
if (hasIcon) {
|
|
65508
|
+
return {
|
|
65509
|
+
svg: undefined,
|
|
65510
|
+
priority: 2,
|
|
65511
|
+
horizontalAlign: "right",
|
|
65512
|
+
size: GRID_ICON_EDGE_LENGTH,
|
|
65513
|
+
margin: GRID_ICON_MARGIN,
|
|
65514
|
+
component: DataValidationListIcon,
|
|
65515
|
+
position,
|
|
65516
|
+
};
|
|
65517
|
+
}
|
|
65518
|
+
return undefined;
|
|
65519
|
+
});
|
|
65520
|
+
iconsOnCellRegistry.add("filter_icon", (getters, position) => {
|
|
65521
|
+
const hasIcon = getters.isFilterHeader(position);
|
|
65522
|
+
if (hasIcon) {
|
|
65523
|
+
return {
|
|
65524
|
+
svg: undefined,
|
|
65525
|
+
priority: 3,
|
|
65526
|
+
horizontalAlign: "right",
|
|
65527
|
+
size: GRID_ICON_EDGE_LENGTH,
|
|
65528
|
+
margin: GRID_ICON_MARGIN,
|
|
65529
|
+
component: FilterIcon,
|
|
65530
|
+
position,
|
|
65531
|
+
};
|
|
65532
|
+
}
|
|
65533
|
+
return undefined;
|
|
65534
|
+
});
|
|
65535
|
+
iconsOnCellRegistry.add("conditional_formatting", (getters, position) => {
|
|
65536
|
+
const icon = getters.getConditionalIcon(position);
|
|
65537
|
+
if (icon) {
|
|
65538
|
+
const style = getters.getCellStyle(position);
|
|
65539
|
+
return {
|
|
65540
|
+
svg: ICONS[icon].svg,
|
|
65541
|
+
priority: 1,
|
|
65542
|
+
horizontalAlign: "left",
|
|
65543
|
+
size: computeTextFontSizeInPixels(style),
|
|
65544
|
+
margin: MIN_CF_ICON_MARGIN,
|
|
65545
|
+
position,
|
|
65546
|
+
};
|
|
65547
|
+
}
|
|
65548
|
+
return undefined;
|
|
65549
|
+
});
|
|
65550
|
+
iconsOnCellRegistry.add("pivot_collapse", (getters, position) => {
|
|
65551
|
+
if (!getters.isSpillPivotFormula(position)) {
|
|
65552
|
+
return undefined;
|
|
65553
|
+
}
|
|
65554
|
+
const pivotCell = getters.getPivotCellFromPosition(position);
|
|
65555
|
+
const pivotId = getters.getPivotIdFromPosition(position);
|
|
65556
|
+
if (pivotCell.type === "HEADER" && pivotId && pivotCell.domain.length) {
|
|
65557
|
+
const definition = getters.getPivotCoreDefinition(pivotId);
|
|
65558
|
+
const isDashboard = getters.isDashboard();
|
|
65559
|
+
const fields = pivotCell.dimension === "COL" ? definition.columns : definition.rows;
|
|
65560
|
+
const component = !isDashboard && pivotCell.domain.length !== fields.length ? PivotCollapseIcon : undefined;
|
|
65561
|
+
return {
|
|
65562
|
+
priority: 4,
|
|
65563
|
+
horizontalAlign: "left",
|
|
65564
|
+
size: !!component || (!isDashboard && pivotCell.dimension === "ROW" && definition.rows.length > 1)
|
|
65565
|
+
? GRID_ICON_EDGE_LENGTH
|
|
65566
|
+
: 0,
|
|
65567
|
+
margin: pivotCell.dimension === "ROW" ? (pivotCell.domain.length - 1) * PIVOT_INDENT : 0,
|
|
65568
|
+
component,
|
|
65569
|
+
position,
|
|
65570
|
+
};
|
|
65571
|
+
}
|
|
65572
|
+
return undefined;
|
|
65573
|
+
});
|
|
65574
|
+
|
|
65575
|
+
class CellIconPlugin extends CoreViewPlugin {
|
|
65576
|
+
static getters = ["doesCellHaveGridIcon", "getCellIcons"];
|
|
65577
|
+
cellIconsCache = {};
|
|
65578
|
+
handle(cmd) {
|
|
65579
|
+
if (cmd.type !== "SET_VIEWPORT_OFFSET") {
|
|
65580
|
+
this.cellIconsCache = {};
|
|
65581
|
+
}
|
|
65582
|
+
}
|
|
65583
|
+
getCellIcons(position) {
|
|
65584
|
+
if (!this.cellIconsCache[position.sheetId]) {
|
|
65585
|
+
this.cellIconsCache[position.sheetId] = {};
|
|
65586
|
+
}
|
|
65587
|
+
if (!this.cellIconsCache[position.sheetId][position.col]) {
|
|
65588
|
+
this.cellIconsCache[position.sheetId][position.col] = {};
|
|
65589
|
+
}
|
|
65590
|
+
if (!this.cellIconsCache[position.sheetId][position.col][position.row]) {
|
|
65591
|
+
this.cellIconsCache[position.sheetId][position.col][position.row] =
|
|
65592
|
+
this.computeCellIcons(position);
|
|
65593
|
+
}
|
|
65594
|
+
return this.cellIconsCache[position.sheetId][position.col][position.row];
|
|
65595
|
+
}
|
|
65596
|
+
computeCellIcons(position) {
|
|
65597
|
+
const icons = { left: undefined, right: undefined, center: undefined };
|
|
65598
|
+
const callbacks = iconsOnCellRegistry.getAll();
|
|
65599
|
+
for (const callback of callbacks) {
|
|
65600
|
+
const icon = callback(this.getters, position);
|
|
65601
|
+
if (icon &&
|
|
65602
|
+
(!icons[icon.horizontalAlign] || icon.priority > icons[icon.horizontalAlign].priority)) {
|
|
65603
|
+
icons[icon.horizontalAlign] = icon;
|
|
65604
|
+
}
|
|
65605
|
+
}
|
|
65606
|
+
if (icons.center && (icons.left || icons.right)) {
|
|
65607
|
+
const sideIconsPriority = Math.max(icons.left?.priority || 0, icons.right?.priority || 0);
|
|
65608
|
+
if (icons.center.priority < sideIconsPriority) {
|
|
65609
|
+
icons.center = undefined;
|
|
65610
|
+
}
|
|
65611
|
+
else {
|
|
65612
|
+
icons.left = undefined;
|
|
65613
|
+
icons.right = undefined;
|
|
65614
|
+
}
|
|
65615
|
+
}
|
|
65616
|
+
return Object.values(icons).filter(isDefined);
|
|
65617
|
+
}
|
|
65618
|
+
doesCellHaveGridIcon(position) {
|
|
65619
|
+
return Boolean(this.getCellIcons(position).length);
|
|
65620
|
+
}
|
|
65621
|
+
}
|
|
65622
|
+
|
|
65055
65623
|
class DynamicTablesPlugin extends CoreViewPlugin {
|
|
65056
65624
|
static getters = [
|
|
65057
65625
|
"canCreateDynamicTableOnZones",
|
|
@@ -65491,7 +66059,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
65491
66059
|
}
|
|
65492
66060
|
getValuesToAggregate(measure, domain) {
|
|
65493
66061
|
const { rowDomain, colDomain } = domainToColRowDomain(this, domain);
|
|
65494
|
-
const table = super.
|
|
66062
|
+
const table = super.getExpandedTableStructure();
|
|
65495
66063
|
const values = [];
|
|
65496
66064
|
if (colDomain.length === 0 &&
|
|
65497
66065
|
rowDomain.length < this.definition.rows.length &&
|
|
@@ -65911,7 +66479,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
65911
66479
|
return this.strictMeasureValueToNumber(comparedValue);
|
|
65912
66480
|
}
|
|
65913
66481
|
getPivotValueCells(measureId) {
|
|
65914
|
-
return this.
|
|
66482
|
+
return this.getCollapsedTableStructure()
|
|
65915
66483
|
.getPivotCells()
|
|
65916
66484
|
.map((col) => col.filter((cell) => cell.type === "VALUE" && cell.measure === measureId))
|
|
65917
66485
|
.filter((col) => col.length > 0);
|
|
@@ -65935,8 +66503,13 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
65935
66503
|
}
|
|
65936
66504
|
throw new Error(`Value ${result.value} is not a number`);
|
|
65937
66505
|
}
|
|
65938
|
-
|
|
65939
|
-
const table = super.
|
|
66506
|
+
getCollapsedTableStructure() {
|
|
66507
|
+
const table = super.getCollapsedTableStructure();
|
|
66508
|
+
this.sortTableStructure(table);
|
|
66509
|
+
return table;
|
|
66510
|
+
}
|
|
66511
|
+
getExpandedTableStructure() {
|
|
66512
|
+
const table = super.getExpandedTableStructure();
|
|
65940
66513
|
this.sortTableStructure(table);
|
|
65941
66514
|
return table;
|
|
65942
66515
|
}
|
|
@@ -66126,7 +66699,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
66126
66699
|
const includeColumnHeaders = toScalar(args[3]);
|
|
66127
66700
|
const shouldIncludeColumnHeaders = includeColumnHeaders === undefined ? true : toBoolean(includeColumnHeaders);
|
|
66128
66701
|
const pivotCells = pivot
|
|
66129
|
-
.
|
|
66702
|
+
.getCollapsedTableStructure()
|
|
66130
66703
|
.getPivotCells(shouldIncludeTotal, shouldIncludeColumnHeaders);
|
|
66131
66704
|
const pivotCol = position.col - mainPosition.col;
|
|
66132
66705
|
const pivotRow = position.row - mainPosition.row;
|
|
@@ -66143,9 +66716,11 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
66143
66716
|
}
|
|
66144
66717
|
else if (functionName === "PIVOT.HEADER") {
|
|
66145
66718
|
const domain = pivot.parseArgsToPivotDomain(args.slice(1).map((value) => ({ value })));
|
|
66719
|
+
const colRowDomain = domainToColRowDomain(pivot, domain);
|
|
66146
66720
|
return {
|
|
66147
66721
|
type: "HEADER",
|
|
66148
66722
|
domain,
|
|
66723
|
+
dimension: colRowDomain.colDomain.length ? "COL" : "ROW",
|
|
66149
66724
|
};
|
|
66150
66725
|
}
|
|
66151
66726
|
const [measure, ...domainArgs] = args.slice(1);
|
|
@@ -68145,8 +68720,11 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
68145
68720
|
version: MESSAGE_VERSION,
|
|
68146
68721
|
});
|
|
68147
68722
|
}
|
|
68148
|
-
|
|
68149
|
-
|
|
68723
|
+
getCurrentClient() {
|
|
68724
|
+
return this.getClient(this.clientId);
|
|
68725
|
+
}
|
|
68726
|
+
getClient(clientId) {
|
|
68727
|
+
const client = this.clients[clientId];
|
|
68150
68728
|
if (!client) {
|
|
68151
68729
|
throw new ClientDisconnectedError("The client left the session");
|
|
68152
68730
|
}
|
|
@@ -68180,7 +68758,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
68180
68758
|
return;
|
|
68181
68759
|
}
|
|
68182
68760
|
const type = currentPosition ? "CLIENT_MOVED" : "CLIENT_JOINED";
|
|
68183
|
-
const client = this.
|
|
68761
|
+
const client = this.getCurrentClient();
|
|
68184
68762
|
this.clients[this.clientId] = { ...client, position };
|
|
68185
68763
|
this.transportService.sendMessage({
|
|
68186
68764
|
type,
|
|
@@ -68391,6 +68969,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
68391
68969
|
static getters = [
|
|
68392
68970
|
"getClientsToDisplay",
|
|
68393
68971
|
"getClient",
|
|
68972
|
+
"getCurrentClient",
|
|
68394
68973
|
"getConnectedClients",
|
|
68395
68974
|
"isFullySynchronized",
|
|
68396
68975
|
];
|
|
@@ -68406,11 +68985,16 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
68406
68985
|
return (position.row < this.getters.getNumberRows(position.sheetId) &&
|
|
68407
68986
|
position.col < this.getters.getNumberCols(position.sheetId));
|
|
68408
68987
|
}
|
|
68409
|
-
getClient() {
|
|
68410
|
-
return this.session.getClient();
|
|
68988
|
+
getClient(clientId) {
|
|
68989
|
+
return this.session.getClient(clientId);
|
|
68990
|
+
}
|
|
68991
|
+
getCurrentClient() {
|
|
68992
|
+
return this.session.getCurrentClient();
|
|
68411
68993
|
}
|
|
68412
68994
|
getConnectedClients() {
|
|
68413
|
-
return this.session.getConnectedClients()
|
|
68995
|
+
return [...this.session.getConnectedClients()].map((client) => {
|
|
68996
|
+
return { ...client, color: this.colors[client.id] };
|
|
68997
|
+
});
|
|
68414
68998
|
}
|
|
68415
68999
|
isFullySynchronized() {
|
|
68416
69000
|
return this.session.isFullySynchronized();
|
|
@@ -68421,7 +69005,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
68421
69005
|
*/
|
|
68422
69006
|
getClientsToDisplay() {
|
|
68423
69007
|
try {
|
|
68424
|
-
this.getters.
|
|
69008
|
+
this.getters.getCurrentClient();
|
|
68425
69009
|
}
|
|
68426
69010
|
catch (e) {
|
|
68427
69011
|
if (e instanceof ClientDisconnectedError) {
|
|
@@ -68434,16 +69018,14 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
68434
69018
|
const sheetId = this.getters.getActiveSheetId();
|
|
68435
69019
|
const clients = [];
|
|
68436
69020
|
for (const client of this.getters.getConnectedClients()) {
|
|
68437
|
-
if (client.id !== this.getters.
|
|
69021
|
+
if (client.id !== this.getters.getCurrentClient().id &&
|
|
68438
69022
|
client.position &&
|
|
68439
69023
|
client.position.sheetId === sheetId &&
|
|
68440
69024
|
this.isPositionValid(client.position)) {
|
|
68441
|
-
const position = client.position;
|
|
68442
69025
|
if (!this.colors[client.id]) {
|
|
68443
69026
|
this.colors[client.id] = this.availableColors.next();
|
|
68444
69027
|
}
|
|
68445
|
-
|
|
68446
|
-
clients.push({ ...client, position, color });
|
|
69028
|
+
clients.push({ ...client, color: this.colors[client.id], position: client.position });
|
|
68447
69029
|
}
|
|
68448
69030
|
}
|
|
68449
69031
|
return clients;
|
|
@@ -68969,7 +69551,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
68969
69551
|
sheetIdTo: sheetId,
|
|
68970
69552
|
});
|
|
68971
69553
|
const pivot = this.getters.getPivot(pivotId);
|
|
68972
|
-
this.insertPivotWithTable(sheetId, 0, 0, pivotId, pivot.
|
|
69554
|
+
this.insertPivotWithTable(sheetId, 0, 0, pivotId, pivot.getCollapsedTableStructure().export(), "dynamic");
|
|
68973
69555
|
}
|
|
68974
69556
|
duplicatePivotInNewSheet(pivotId, newPivotId, newSheetId) {
|
|
68975
69557
|
this.dispatch("DUPLICATE_PIVOT", {
|
|
@@ -68992,7 +69574,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
68992
69574
|
if (result.isSuccessful) {
|
|
68993
69575
|
this.dispatch("ACTIVATE_SHEET", { sheetIdFrom: activeSheetId, sheetIdTo: newSheetId });
|
|
68994
69576
|
const pivot = this.getters.getPivot(pivotId);
|
|
68995
|
-
this.insertPivotWithTable(newSheetId, 0, 0, newPivotId, pivot.
|
|
69577
|
+
this.insertPivotWithTable(newSheetId, 0, 0, newPivotId, pivot.getCollapsedTableStructure().export(), "dynamic");
|
|
68996
69578
|
}
|
|
68997
69579
|
}
|
|
68998
69580
|
getPivotDuplicateSheetName(pivotName) {
|
|
@@ -69284,9 +69866,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
69284
69866
|
|
|
69285
69867
|
class SheetUIPlugin extends UIPlugin {
|
|
69286
69868
|
static getters = [
|
|
69287
|
-
"doesCellHaveGridIcon",
|
|
69288
69869
|
"getCellWidth",
|
|
69289
|
-
"getCellIconSvg",
|
|
69290
69870
|
"getTextWidth",
|
|
69291
69871
|
"getCellText",
|
|
69292
69872
|
"getCellMultiLineText",
|
|
@@ -69341,12 +69921,8 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
69341
69921
|
const multiLineText = splitTextToWidth(this.ctx, content, style, undefined);
|
|
69342
69922
|
contentWidth += Math.max(...multiLineText.map((line) => computeTextWidth(this.ctx, line, style)));
|
|
69343
69923
|
}
|
|
69344
|
-
const icon
|
|
69345
|
-
|
|
69346
|
-
contentWidth += computeIconWidth(style);
|
|
69347
|
-
}
|
|
69348
|
-
if (this.getters.doesCellHaveGridIcon(position)) {
|
|
69349
|
-
contentWidth += ICON_EDGE_LENGTH + GRID_ICON_MARGIN;
|
|
69924
|
+
for (const icon of this.getters.getCellIcons(position)) {
|
|
69925
|
+
contentWidth += icon.margin + icon.size;
|
|
69350
69926
|
}
|
|
69351
69927
|
if (contentWidth === 0) {
|
|
69352
69928
|
return 0;
|
|
@@ -69358,16 +69934,6 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
69358
69934
|
}
|
|
69359
69935
|
return contentWidth;
|
|
69360
69936
|
}
|
|
69361
|
-
getCellIconSvg(position) {
|
|
69362
|
-
const callbacks = iconsOnCellRegistry.getAll();
|
|
69363
|
-
for (const callback of callbacks) {
|
|
69364
|
-
const imageSrc = callback(this.getters, position);
|
|
69365
|
-
if (imageSrc) {
|
|
69366
|
-
return imageSrc;
|
|
69367
|
-
}
|
|
69368
|
-
}
|
|
69369
|
-
return undefined;
|
|
69370
|
-
}
|
|
69371
69937
|
getTextWidth(text, style) {
|
|
69372
69938
|
return computeTextWidth(this.ctx, text, style);
|
|
69373
69939
|
}
|
|
@@ -69407,11 +69973,6 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
69407
69973
|
});
|
|
69408
69974
|
return splitTextToWidth(this.ctx, text, style, args.wrapText ? args.maxWidth : undefined);
|
|
69409
69975
|
}
|
|
69410
|
-
doesCellHaveGridIcon(position) {
|
|
69411
|
-
const isFilterHeader = this.getters.isFilterHeader(position);
|
|
69412
|
-
const hasListIcon = !this.getters.isReadonly() && this.getters.cellHasListDataValidationIcon(position);
|
|
69413
|
-
return isFilterHeader || hasListIcon;
|
|
69414
|
-
}
|
|
69415
69976
|
/**
|
|
69416
69977
|
* Expands the given zone until bordered by empty cells or reached the sheet boundaries.
|
|
69417
69978
|
*/
|
|
@@ -73303,7 +73864,8 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
73303
73864
|
.add("data_validation_ui", EvaluationDataValidationPlugin)
|
|
73304
73865
|
.add("dynamic_tables", DynamicTablesPlugin)
|
|
73305
73866
|
.add("custom_colors", CustomColorsPlugin)
|
|
73306
|
-
.add("pivot_ui", PivotUIPlugin)
|
|
73867
|
+
.add("pivot_ui", PivotUIPlugin)
|
|
73868
|
+
.add("cell_icon", CellIconPlugin);
|
|
73307
73869
|
|
|
73308
73870
|
autoCompleteProviders.add("dataValidation", {
|
|
73309
73871
|
displayAllOnInitialContent: true,
|
|
@@ -74453,6 +75015,21 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
74453
75015
|
}
|
|
74454
75016
|
const topbarComponentRegistry = new TopBarComponentRegistry();
|
|
74455
75017
|
|
|
75018
|
+
class LocalTransportService {
|
|
75019
|
+
listeners = [];
|
|
75020
|
+
async sendMessage(message) {
|
|
75021
|
+
for (const { callback } of this.listeners) {
|
|
75022
|
+
callback(message);
|
|
75023
|
+
}
|
|
75024
|
+
}
|
|
75025
|
+
onNewMessage(id, callback) {
|
|
75026
|
+
this.listeners.push({ id, callback });
|
|
75027
|
+
}
|
|
75028
|
+
leave(id) {
|
|
75029
|
+
this.listeners = this.listeners.filter((listener) => listener.id !== id);
|
|
75030
|
+
}
|
|
75031
|
+
}
|
|
75032
|
+
|
|
74456
75033
|
class ImageProvider {
|
|
74457
75034
|
fileStore;
|
|
74458
75035
|
constructor(fileStore) {
|
|
@@ -77290,6 +77867,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
77290
77867
|
SidePanel,
|
|
77291
77868
|
SpreadsheetDashboard,
|
|
77292
77869
|
HeaderGroupContainer,
|
|
77870
|
+
FullScreenChart,
|
|
77293
77871
|
};
|
|
77294
77872
|
sidePanel;
|
|
77295
77873
|
spreadsheetRef = owl.useRef("spreadsheet");
|
|
@@ -77448,21 +78026,6 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
77448
78026
|
}
|
|
77449
78027
|
}
|
|
77450
78028
|
|
|
77451
|
-
class LocalTransportService {
|
|
77452
|
-
listeners = [];
|
|
77453
|
-
async sendMessage(message) {
|
|
77454
|
-
for (const { callback } of this.listeners) {
|
|
77455
|
-
callback(message);
|
|
77456
|
-
}
|
|
77457
|
-
}
|
|
77458
|
-
onNewMessage(id, callback) {
|
|
77459
|
-
this.listeners.push({ id, callback });
|
|
77460
|
-
}
|
|
77461
|
-
leave(id) {
|
|
77462
|
-
this.listeners = this.listeners.filter((listener) => listener.id !== id);
|
|
77463
|
-
}
|
|
77464
|
-
}
|
|
77465
|
-
|
|
77466
78029
|
function inverseCommand(cmd) {
|
|
77467
78030
|
return inverseCommandRegistry.get(cmd.type)(cmd);
|
|
77468
78031
|
}
|
|
@@ -81886,6 +82449,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
81886
82449
|
RadioSelection,
|
|
81887
82450
|
GeoChartRegionSelectSection,
|
|
81888
82451
|
ChartDashboardMenu,
|
|
82452
|
+
FullScreenChart,
|
|
81889
82453
|
};
|
|
81890
82454
|
const hooks = {
|
|
81891
82455
|
useDragAndDropListItems,
|
|
@@ -81912,6 +82476,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
81912
82476
|
SidePanelStore,
|
|
81913
82477
|
PivotSidePanelStore,
|
|
81914
82478
|
PivotMeasureDisplayPanelStore,
|
|
82479
|
+
ClientFocusStore,
|
|
81915
82480
|
};
|
|
81916
82481
|
function addFunction(functionName, functionDescription) {
|
|
81917
82482
|
functionRegistry.add(functionName, functionDescription);
|
|
@@ -81935,6 +82500,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
81935
82500
|
exports.CoreViewPlugin = CoreViewPlugin;
|
|
81936
82501
|
exports.DispatchResult = DispatchResult;
|
|
81937
82502
|
exports.EvaluationError = EvaluationError;
|
|
82503
|
+
exports.LocalTransportService = LocalTransportService;
|
|
81938
82504
|
exports.Model = Model;
|
|
81939
82505
|
exports.PivotRuntimeDefinition = PivotRuntimeDefinition;
|
|
81940
82506
|
exports.Registry = Registry;
|
|
@@ -81976,9 +82542,9 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
81976
82542
|
exports.tokenize = tokenize;
|
|
81977
82543
|
|
|
81978
82544
|
|
|
81979
|
-
__info__.version = "18.4.0-alpha.
|
|
81980
|
-
__info__.date = "2025-05-
|
|
81981
|
-
__info__.hash = "
|
|
82545
|
+
__info__.version = "18.4.0-alpha.4";
|
|
82546
|
+
__info__.date = "2025-05-20T05:57:45.452Z";
|
|
82547
|
+
__info__.hash = "5c28bca";
|
|
81982
82548
|
|
|
81983
82549
|
|
|
81984
82550
|
})(this.o_spreadsheet = this.o_spreadsheet || {}, owl);
|