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