@odoo/o-spreadsheet 17.4.0-alpha.5 → 17.4.0-alpha.6
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 +1212 -705
- package/dist/o-spreadsheet.d.ts +230 -104
- package/dist/o-spreadsheet.esm.js +1212 -705
- package/dist/o-spreadsheet.iife.js +1212 -705
- package/dist/o-spreadsheet.iife.min.js +377 -344
- package/dist/o_spreadsheet.xml +250 -23
- package/package.json +1 -1
|
@@ -3,9 +3,9 @@
|
|
|
3
3
|
/**
|
|
4
4
|
* This file is generated by o-spreadsheet build tools. Do not edit it.
|
|
5
5
|
* @see https://github.com/odoo/o-spreadsheet
|
|
6
|
-
* @version 17.4.0-alpha.
|
|
7
|
-
* @date 2024-06-
|
|
8
|
-
* @hash
|
|
6
|
+
* @version 17.4.0-alpha.6
|
|
7
|
+
* @date 2024-06-19T13:46:27.157Z
|
|
8
|
+
* @hash a4f22e4
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
11
|
import { useEnv, useSubEnv, onWillUnmount, useComponent, status, Component, useRef, onMounted, useEffect, useState, onPatched, onWillPatch, onWillUpdateProps, useExternalListener, onWillStart, xml, useChildSubEnv, markRaw, toRaw } from '@odoo/owl';
|
|
@@ -6200,7 +6200,7 @@ class CellClipboardHandler extends AbstractCellClipboardHandler {
|
|
|
6200
6200
|
};
|
|
6201
6201
|
}
|
|
6202
6202
|
isPasteAllowed(sheetId, target, content, clipboardOptions) {
|
|
6203
|
-
if (!
|
|
6203
|
+
if (!content.cells) {
|
|
6204
6204
|
return "Success" /* CommandResult.Success */;
|
|
6205
6205
|
}
|
|
6206
6206
|
if (clipboardOptions?.isCutOperation && clipboardOptions?.pasteOption !== undefined) {
|
|
@@ -6214,6 +6214,17 @@ class CellClipboardHandler extends AbstractCellClipboardHandler {
|
|
|
6214
6214
|
return "WrongPasteSelection" /* CommandResult.WrongPasteSelection */;
|
|
6215
6215
|
}
|
|
6216
6216
|
}
|
|
6217
|
+
const clipboardHeight = content.cells.length;
|
|
6218
|
+
const clipboardWidth = content.cells[0].length;
|
|
6219
|
+
for (const zone of getPasteZones(target, content.cells)) {
|
|
6220
|
+
if (this.getters.doesIntersectMerge(sheetId, zone)) {
|
|
6221
|
+
if (target.length > 1 ||
|
|
6222
|
+
!this.getters.isSingleCellOrMerge(sheetId, target[0]) ||
|
|
6223
|
+
clipboardHeight * clipboardWidth !== 1) {
|
|
6224
|
+
return "WillRemoveExistingMerge" /* CommandResult.WillRemoveExistingMerge */;
|
|
6225
|
+
}
|
|
6226
|
+
}
|
|
6227
|
+
}
|
|
6217
6228
|
return "Success" /* CommandResult.Success */;
|
|
6218
6229
|
}
|
|
6219
6230
|
/**
|
|
@@ -6453,13 +6464,22 @@ class ConditionalFormatClipboardHandler extends AbstractCellClipboardHandler {
|
|
|
6453
6464
|
}
|
|
6454
6465
|
const { rowsIndexes, columnsIndexes } = data;
|
|
6455
6466
|
const sheetId = data.sheetId;
|
|
6456
|
-
const
|
|
6457
|
-
|
|
6458
|
-
|
|
6459
|
-
|
|
6467
|
+
const cfRules = [];
|
|
6468
|
+
for (const row of rowsIndexes) {
|
|
6469
|
+
const cfRuleInRow = [];
|
|
6470
|
+
for (const col of columnsIndexes) {
|
|
6471
|
+
const cfRules = Array.from(this.getters.getRulesByCell(sheetId, col, row));
|
|
6472
|
+
cfRuleInRow.push({
|
|
6473
|
+
position: { col, row, sheetId },
|
|
6474
|
+
rules: cfRules,
|
|
6475
|
+
});
|
|
6476
|
+
}
|
|
6477
|
+
cfRules.push(cfRuleInRow);
|
|
6478
|
+
}
|
|
6479
|
+
return { cfRules };
|
|
6460
6480
|
}
|
|
6461
6481
|
paste(target, clippedContent, options) {
|
|
6462
|
-
if (!clippedContent?.
|
|
6482
|
+
if (!clippedContent?.cfRules ||
|
|
6463
6483
|
options?.pasteOption === "asValue" ||
|
|
6464
6484
|
!("zones" in target) ||
|
|
6465
6485
|
!target.zones.length) {
|
|
@@ -6468,7 +6488,7 @@ class ConditionalFormatClipboardHandler extends AbstractCellClipboardHandler {
|
|
|
6468
6488
|
const zones = target.zones;
|
|
6469
6489
|
const sheetId = target.sheetId;
|
|
6470
6490
|
if (!options?.isCutOperation) {
|
|
6471
|
-
this.pasteFromCopy(sheetId, zones, clippedContent.
|
|
6491
|
+
this.pasteFromCopy(sheetId, zones, clippedContent.cfRules, options);
|
|
6472
6492
|
}
|
|
6473
6493
|
else {
|
|
6474
6494
|
this.pasteFromCut(sheetId, zones, clippedContent);
|
|
@@ -6476,12 +6496,12 @@ class ConditionalFormatClipboardHandler extends AbstractCellClipboardHandler {
|
|
|
6476
6496
|
}
|
|
6477
6497
|
pasteFromCut(sheetId, target, content) {
|
|
6478
6498
|
const selection = target[0];
|
|
6479
|
-
this.pasteZone(sheetId, selection.left, selection.top, content.
|
|
6499
|
+
this.pasteZone(sheetId, selection.left, selection.top, content.cfRules, {
|
|
6480
6500
|
isCutOperation: true,
|
|
6481
6501
|
});
|
|
6482
6502
|
}
|
|
6483
|
-
pasteZone(sheetId, col, row,
|
|
6484
|
-
for (const [r, rowCells] of
|
|
6503
|
+
pasteZone(sheetId, col, row, cfRules, clipboardOptions) {
|
|
6504
|
+
for (const [r, rowCells] of cfRules.entries()) {
|
|
6485
6505
|
for (const [c, origin] of rowCells.entries()) {
|
|
6486
6506
|
const position = { col: col + c, row: row + r, sheetId };
|
|
6487
6507
|
this.pasteCf(origin, position, clipboardOptions?.isCutOperation);
|
|
@@ -6489,23 +6509,21 @@ class ConditionalFormatClipboardHandler extends AbstractCellClipboardHandler {
|
|
|
6489
6509
|
}
|
|
6490
6510
|
}
|
|
6491
6511
|
pasteCf(origin, target, isCutOperation) {
|
|
6492
|
-
|
|
6493
|
-
|
|
6494
|
-
for (const
|
|
6495
|
-
|
|
6496
|
-
|
|
6497
|
-
|
|
6498
|
-
|
|
6499
|
-
|
|
6500
|
-
|
|
6501
|
-
|
|
6502
|
-
|
|
6503
|
-
|
|
6504
|
-
|
|
6505
|
-
|
|
6506
|
-
|
|
6507
|
-
this.adaptCFRules(target.sheetId, cfToCopyTo, [zone], []);
|
|
6508
|
-
}
|
|
6512
|
+
if (origin?.rules && origin.rules.length > 0) {
|
|
6513
|
+
const zone = positionToZone(target);
|
|
6514
|
+
for (const rule of origin.rules) {
|
|
6515
|
+
const toRemoveZones = [];
|
|
6516
|
+
if (isCutOperation) {
|
|
6517
|
+
//remove from current rule
|
|
6518
|
+
toRemoveZones.push(positionToZone(origin.position));
|
|
6519
|
+
}
|
|
6520
|
+
if (origin.position.sheetId === target.sheetId) {
|
|
6521
|
+
this.adaptCFRules(origin.position.sheetId, rule, [zone], toRemoveZones);
|
|
6522
|
+
}
|
|
6523
|
+
else {
|
|
6524
|
+
this.adaptCFRules(origin.position.sheetId, rule, [], toRemoveZones);
|
|
6525
|
+
const cfToCopyTo = this.getCFToCopyTo(target.sheetId, rule);
|
|
6526
|
+
this.adaptCFRules(target.sheetId, cfToCopyTo, [zone], []);
|
|
6509
6527
|
}
|
|
6510
6528
|
}
|
|
6511
6529
|
}
|
|
@@ -6548,13 +6566,20 @@ class DataValidationClipboardHandler extends AbstractCellClipboardHandler {
|
|
|
6548
6566
|
}
|
|
6549
6567
|
const { rowsIndexes, columnsIndexes } = data;
|
|
6550
6568
|
const sheetId = data.sheetId;
|
|
6551
|
-
const
|
|
6552
|
-
|
|
6553
|
-
|
|
6554
|
-
|
|
6569
|
+
const dvRules = [];
|
|
6570
|
+
for (const row of rowsIndexes) {
|
|
6571
|
+
const dvRuleInRow = [];
|
|
6572
|
+
for (const col of columnsIndexes) {
|
|
6573
|
+
const position = { sheetId, col, row };
|
|
6574
|
+
const rule = this.getters.getValidationRuleForCell(position);
|
|
6575
|
+
dvRuleInRow.push({ position, rule });
|
|
6576
|
+
}
|
|
6577
|
+
dvRules.push(dvRuleInRow);
|
|
6578
|
+
}
|
|
6579
|
+
return { dvRules };
|
|
6555
6580
|
}
|
|
6556
6581
|
paste(target, clippedContent, options) {
|
|
6557
|
-
if (!clippedContent?.
|
|
6582
|
+
if (!clippedContent?.dvRules) {
|
|
6558
6583
|
return;
|
|
6559
6584
|
}
|
|
6560
6585
|
if (options?.pasteOption) {
|
|
@@ -6566,7 +6591,7 @@ class DataValidationClipboardHandler extends AbstractCellClipboardHandler {
|
|
|
6566
6591
|
const zones = target.zones;
|
|
6567
6592
|
const sheetId = target.sheetId;
|
|
6568
6593
|
if (!options?.isCutOperation) {
|
|
6569
|
-
this.pasteFromCopy(sheetId, zones, clippedContent.
|
|
6594
|
+
this.pasteFromCopy(sheetId, zones, clippedContent.dvRules);
|
|
6570
6595
|
}
|
|
6571
6596
|
else {
|
|
6572
6597
|
this.pasteFromCut(sheetId, zones, clippedContent);
|
|
@@ -6574,12 +6599,12 @@ class DataValidationClipboardHandler extends AbstractCellClipboardHandler {
|
|
|
6574
6599
|
}
|
|
6575
6600
|
pasteFromCut(sheetId, target, content) {
|
|
6576
6601
|
const selection = target[0];
|
|
6577
|
-
this.pasteZone(sheetId, selection.left, selection.top, content.
|
|
6602
|
+
this.pasteZone(sheetId, selection.left, selection.top, content.dvRules, {
|
|
6578
6603
|
isCutOperation: true,
|
|
6579
6604
|
});
|
|
6580
6605
|
}
|
|
6581
|
-
pasteZone(sheetId, col, row,
|
|
6582
|
-
for (const [r, rowCells] of
|
|
6606
|
+
pasteZone(sheetId, col, row, dvRules, clipboardOptions) {
|
|
6607
|
+
for (const [r, rowCells] of dvRules.entries()) {
|
|
6583
6608
|
for (const [c, origin] of rowCells.entries()) {
|
|
6584
6609
|
const position = { col: col + c, row: row + r, sheetId };
|
|
6585
6610
|
this.pasteDataValidation(origin, position, clipboardOptions?.isCutOperation);
|
|
@@ -6587,41 +6612,43 @@ class DataValidationClipboardHandler extends AbstractCellClipboardHandler {
|
|
|
6587
6612
|
}
|
|
6588
6613
|
}
|
|
6589
6614
|
pasteDataValidation(origin, target, isCutOperation) {
|
|
6590
|
-
|
|
6591
|
-
|
|
6592
|
-
const
|
|
6593
|
-
if (
|
|
6594
|
-
|
|
6595
|
-
|
|
6596
|
-
|
|
6597
|
-
|
|
6598
|
-
}
|
|
6599
|
-
const zone = positionToZone(target);
|
|
6600
|
-
for (const range of rule.ranges) {
|
|
6601
|
-
if (isInside(origin.col, origin.row, range.zone)) {
|
|
6602
|
-
const toRemoveZone = [];
|
|
6603
|
-
if (isCutOperation) {
|
|
6604
|
-
toRemoveZone.push(positionToZone(origin));
|
|
6605
|
-
}
|
|
6606
|
-
if (origin.sheetId === target.sheetId) {
|
|
6607
|
-
this.adaptDataValidationRule(origin.sheetId, rule, [zone], toRemoveZone);
|
|
6615
|
+
if (origin) {
|
|
6616
|
+
const zone = positionToZone(target);
|
|
6617
|
+
const rule = origin.rule;
|
|
6618
|
+
if (!rule) {
|
|
6619
|
+
const targetRule = this.getters.getValidationRuleForCell(target);
|
|
6620
|
+
if (targetRule) {
|
|
6621
|
+
// Remove the data validation rule on the target cell
|
|
6622
|
+
this.adaptDataValidationRule(target.sheetId, targetRule, [], [zone]);
|
|
6608
6623
|
}
|
|
6609
|
-
|
|
6610
|
-
|
|
6611
|
-
|
|
6612
|
-
|
|
6624
|
+
return;
|
|
6625
|
+
}
|
|
6626
|
+
const toRemoveZone = [];
|
|
6627
|
+
if (isCutOperation) {
|
|
6628
|
+
toRemoveZone.push(positionToZone(origin.position));
|
|
6629
|
+
}
|
|
6630
|
+
if (origin.position.sheetId === target.sheetId) {
|
|
6631
|
+
const copyToRule = this.getDataValidationRuleToCopyTo(target.sheetId, rule, false);
|
|
6632
|
+
this.adaptDataValidationRule(origin.position.sheetId, copyToRule, [zone], toRemoveZone);
|
|
6633
|
+
}
|
|
6634
|
+
else {
|
|
6635
|
+
const originRule = this.getters.getValidationRuleForCell(origin.position);
|
|
6636
|
+
if (originRule) {
|
|
6637
|
+
this.adaptDataValidationRule(origin.position.sheetId, originRule, [], toRemoveZone);
|
|
6613
6638
|
}
|
|
6639
|
+
const copyToRule = this.getDataValidationRuleToCopyTo(target.sheetId, rule);
|
|
6640
|
+
this.adaptDataValidationRule(target.sheetId, copyToRule, [zone], []);
|
|
6614
6641
|
}
|
|
6615
6642
|
}
|
|
6616
6643
|
}
|
|
6617
|
-
getDataValidationRuleToCopyTo(targetSheetId, originRule) {
|
|
6644
|
+
getDataValidationRuleToCopyTo(targetSheetId, originRule, newId = true) {
|
|
6618
6645
|
const ruleInTargetSheet = this.getters
|
|
6619
6646
|
.getDataValidationRules(targetSheetId)
|
|
6620
6647
|
.find((rule) => deepEquals(originRule.criterion, rule.criterion) &&
|
|
6621
6648
|
originRule.isBlocking === rule.isBlocking);
|
|
6622
6649
|
return ruleInTargetSheet
|
|
6623
6650
|
? ruleInTargetSheet
|
|
6624
|
-
: { ...originRule, id: this.uuidGenerator.uuidv4(), ranges: [] };
|
|
6651
|
+
: { ...originRule, id: newId ? this.uuidGenerator.uuidv4() : originRule.id, ranges: [] };
|
|
6625
6652
|
}
|
|
6626
6653
|
/**
|
|
6627
6654
|
* Add or remove XCs to a given data validation rule.
|
|
@@ -6713,69 +6740,63 @@ class ImageClipboardHandler extends AbstractFigureClipboardHandler {
|
|
|
6713
6740
|
}
|
|
6714
6741
|
|
|
6715
6742
|
class MergeClipboardHandler extends AbstractCellClipboardHandler {
|
|
6716
|
-
|
|
6717
|
-
if (!
|
|
6718
|
-
return
|
|
6743
|
+
copy(data) {
|
|
6744
|
+
if (!data.zones.length) {
|
|
6745
|
+
return;
|
|
6719
6746
|
}
|
|
6720
|
-
const
|
|
6721
|
-
const
|
|
6722
|
-
|
|
6723
|
-
|
|
6724
|
-
|
|
6725
|
-
|
|
6726
|
-
|
|
6727
|
-
|
|
6728
|
-
}
|
|
6747
|
+
const sheetId = this.getters.getActiveSheetId();
|
|
6748
|
+
const { rowsIndexes, columnsIndexes } = data;
|
|
6749
|
+
const merges = [];
|
|
6750
|
+
for (const row of rowsIndexes) {
|
|
6751
|
+
const mergesInRow = [];
|
|
6752
|
+
for (const col of columnsIndexes) {
|
|
6753
|
+
const position = { col, row, sheetId };
|
|
6754
|
+
mergesInRow.push(this.getters.getMerge(position));
|
|
6729
6755
|
}
|
|
6756
|
+
merges.push(mergesInRow);
|
|
6730
6757
|
}
|
|
6731
|
-
return
|
|
6758
|
+
return { merges };
|
|
6732
6759
|
}
|
|
6733
6760
|
/**
|
|
6734
6761
|
* Paste the clipboard content in the given target
|
|
6735
6762
|
*/
|
|
6736
6763
|
paste(target, content, options) {
|
|
6737
|
-
if (
|
|
6764
|
+
if (!content.merges ||
|
|
6765
|
+
options?.isCutOperation ||
|
|
6766
|
+
!("zones" in target) ||
|
|
6767
|
+
!target.zones.length) {
|
|
6738
6768
|
return;
|
|
6739
6769
|
}
|
|
6740
|
-
this.pasteFromCopy(target.sheetId, target.zones, content.
|
|
6770
|
+
this.pasteFromCopy(target.sheetId, target.zones, content.merges, options);
|
|
6741
6771
|
}
|
|
6742
|
-
pasteZone(sheetId, col, row,
|
|
6743
|
-
for (const [r,
|
|
6744
|
-
for (const [c,
|
|
6745
|
-
if (!origin.position) {
|
|
6746
|
-
continue;
|
|
6747
|
-
}
|
|
6772
|
+
pasteZone(sheetId, col, row, merges) {
|
|
6773
|
+
for (const [r, rowMerges] of merges.entries()) {
|
|
6774
|
+
for (const [c, originMerge] of rowMerges.entries()) {
|
|
6748
6775
|
const position = { col: col + c, row: row + r, sheetId };
|
|
6749
|
-
this.
|
|
6776
|
+
this.pasteMerge(originMerge, position);
|
|
6750
6777
|
}
|
|
6751
6778
|
}
|
|
6752
6779
|
}
|
|
6753
|
-
|
|
6754
|
-
|
|
6755
|
-
|
|
6756
|
-
|
|
6757
|
-
|
|
6758
|
-
|
|
6759
|
-
const { col: mainCellColOrigin, row: mainCellRowOrigin } = this.getters.getMainCellPosition(origin);
|
|
6760
|
-
if (mainCellColOrigin === col && mainCellRowOrigin === row) {
|
|
6761
|
-
const merge = this.getters.getMerge(origin);
|
|
6762
|
-
if (!merge) {
|
|
6763
|
-
return;
|
|
6764
|
-
}
|
|
6765
|
-
({ sheetId, col, row } = target);
|
|
6766
|
-
this.dispatch("ADD_MERGE", {
|
|
6767
|
-
sheetId,
|
|
6768
|
-
force: true,
|
|
6769
|
-
target: [
|
|
6770
|
-
{
|
|
6771
|
-
left: col,
|
|
6772
|
-
top: row,
|
|
6773
|
-
right: col + merge.right - merge.left,
|
|
6774
|
-
bottom: row + merge.bottom - merge.top,
|
|
6775
|
-
},
|
|
6776
|
-
],
|
|
6777
|
-
});
|
|
6780
|
+
pasteMerge(originMerge, target) {
|
|
6781
|
+
if (!originMerge) {
|
|
6782
|
+
return;
|
|
6783
|
+
}
|
|
6784
|
+
if (this.getters.isInMerge(target)) {
|
|
6785
|
+
return;
|
|
6778
6786
|
}
|
|
6787
|
+
const { sheetId, col, row } = target;
|
|
6788
|
+
this.dispatch("ADD_MERGE", {
|
|
6789
|
+
sheetId,
|
|
6790
|
+
force: true,
|
|
6791
|
+
target: [
|
|
6792
|
+
{
|
|
6793
|
+
left: col,
|
|
6794
|
+
top: row,
|
|
6795
|
+
right: col + originMerge.right - originMerge.left,
|
|
6796
|
+
bottom: row + originMerge.bottom - originMerge.top,
|
|
6797
|
+
},
|
|
6798
|
+
],
|
|
6799
|
+
});
|
|
6779
6800
|
}
|
|
6780
6801
|
}
|
|
6781
6802
|
|
|
@@ -7755,6 +7776,486 @@ function errorCell(value, message) {
|
|
|
7755
7776
|
};
|
|
7756
7777
|
}
|
|
7757
7778
|
|
|
7779
|
+
function boolAnd(args) {
|
|
7780
|
+
let foundBoolean = false;
|
|
7781
|
+
let acc = true;
|
|
7782
|
+
conditionalVisitBoolean(args, (arg) => {
|
|
7783
|
+
foundBoolean = true;
|
|
7784
|
+
acc = acc && arg;
|
|
7785
|
+
return acc;
|
|
7786
|
+
});
|
|
7787
|
+
return {
|
|
7788
|
+
foundBoolean,
|
|
7789
|
+
result: acc,
|
|
7790
|
+
};
|
|
7791
|
+
}
|
|
7792
|
+
function boolOr(args) {
|
|
7793
|
+
let foundBoolean = false;
|
|
7794
|
+
let acc = false;
|
|
7795
|
+
conditionalVisitBoolean(args, (arg) => {
|
|
7796
|
+
foundBoolean = true;
|
|
7797
|
+
acc = acc || arg;
|
|
7798
|
+
return !acc;
|
|
7799
|
+
});
|
|
7800
|
+
return {
|
|
7801
|
+
foundBoolean,
|
|
7802
|
+
result: acc,
|
|
7803
|
+
};
|
|
7804
|
+
}
|
|
7805
|
+
|
|
7806
|
+
function sum(values, locale) {
|
|
7807
|
+
return reduceNumbers(values, (acc, a) => acc + a, 0, locale);
|
|
7808
|
+
}
|
|
7809
|
+
function countUnique(args) {
|
|
7810
|
+
return reduceAny(args, (acc, a) => (isDataNonEmpty(a) ? acc.add(a?.value) : acc), new Set()).size;
|
|
7811
|
+
}
|
|
7812
|
+
|
|
7813
|
+
function assertSameNumberOfElements(...args) {
|
|
7814
|
+
const dims = args[0].length;
|
|
7815
|
+
args.forEach((arg, i) => assert(() => arg.length === dims, _t("[[FUNCTION_NAME]] has mismatched dimensions for argument %s (%s vs %s).", i.toString(), dims.toString(), arg.length.toString())));
|
|
7816
|
+
}
|
|
7817
|
+
function average(values, locale) {
|
|
7818
|
+
let count = 0;
|
|
7819
|
+
const sum = reduceNumbers(values, (acc, a) => {
|
|
7820
|
+
count += 1;
|
|
7821
|
+
return acc + a;
|
|
7822
|
+
}, 0, locale);
|
|
7823
|
+
assertNotZero(count);
|
|
7824
|
+
return sum / count;
|
|
7825
|
+
}
|
|
7826
|
+
function countNumbers(values, locale) {
|
|
7827
|
+
let count = 0;
|
|
7828
|
+
for (let n of values) {
|
|
7829
|
+
if (isMatrix(n)) {
|
|
7830
|
+
for (let i of n) {
|
|
7831
|
+
for (let j of i) {
|
|
7832
|
+
if (typeof j.value === "number") {
|
|
7833
|
+
count += 1;
|
|
7834
|
+
}
|
|
7835
|
+
}
|
|
7836
|
+
}
|
|
7837
|
+
}
|
|
7838
|
+
else {
|
|
7839
|
+
const value = n?.value;
|
|
7840
|
+
if (!isEvaluationError(value) &&
|
|
7841
|
+
(typeof value !== "string" || isNumber(value, locale) || parseDateTime(value, locale))) {
|
|
7842
|
+
count += 1;
|
|
7843
|
+
}
|
|
7844
|
+
}
|
|
7845
|
+
}
|
|
7846
|
+
return count;
|
|
7847
|
+
}
|
|
7848
|
+
function countAny(values) {
|
|
7849
|
+
return reduceAny(values, (acc, a) => (a !== undefined && a.value !== null ? acc + 1 : acc), 0);
|
|
7850
|
+
}
|
|
7851
|
+
function max(values, locale) {
|
|
7852
|
+
const result = reduceNumbers(values, (acc, a) => (acc < a ? a : acc), -Infinity, locale);
|
|
7853
|
+
return result === -Infinity ? 0 : result;
|
|
7854
|
+
}
|
|
7855
|
+
function min(values, locale) {
|
|
7856
|
+
const result = reduceNumbers(values, (acc, a) => (a < acc ? a : acc), Infinity, locale);
|
|
7857
|
+
return result === Infinity ? 0 : result;
|
|
7858
|
+
}
|
|
7859
|
+
|
|
7860
|
+
const pivotTimeAdapterRegistry = new Registry();
|
|
7861
|
+
function pivotTimeAdapter(granularity) {
|
|
7862
|
+
return pivotTimeAdapterRegistry.get(granularity);
|
|
7863
|
+
}
|
|
7864
|
+
/**
|
|
7865
|
+
* The Time Adapter: Managing Time Periods for Pivot Functions
|
|
7866
|
+
*
|
|
7867
|
+
* Overview:
|
|
7868
|
+
* A time adapter is responsible for managing time periods associated with pivot functions.
|
|
7869
|
+
* Each type of period (day, week, month, quarter, etc.) has its own dedicated adapter.
|
|
7870
|
+
* The adapter's primary role is to normalize period values between spreadsheet functions,
|
|
7871
|
+
* and the pivot.
|
|
7872
|
+
* By normalizing the period value, it can be stored consistently in the pivot.
|
|
7873
|
+
*
|
|
7874
|
+
* Normalization Process:
|
|
7875
|
+
* When working with functions in the spreadsheet, the time adapter normalizes
|
|
7876
|
+
* the provided period to facilitate accurate lookup of values in the pivot.
|
|
7877
|
+
* For instance, if the spreadsheet function represents a day period as a number generated
|
|
7878
|
+
* by the DATE function (DATE(2023, 12, 25)), the time adapter will normalize it accordingly.
|
|
7879
|
+
*
|
|
7880
|
+
*/
|
|
7881
|
+
/**
|
|
7882
|
+
* Normalized value: "12/25/2023"
|
|
7883
|
+
*
|
|
7884
|
+
* Note: Those two format are equivalent:
|
|
7885
|
+
* - "MM/dd/yyyy" (luxon format)
|
|
7886
|
+
* - "mm/dd/yyyy" (spreadsheet format)
|
|
7887
|
+
**/
|
|
7888
|
+
const dayAdapter = {
|
|
7889
|
+
normalizeFunctionValue(value) {
|
|
7890
|
+
const date = toNumber(value, DEFAULT_LOCALE);
|
|
7891
|
+
return formatValue(date, { locale: DEFAULT_LOCALE, format: "mm/dd/yyyy" });
|
|
7892
|
+
},
|
|
7893
|
+
getFormat(locale) {
|
|
7894
|
+
return (locale ?? DEFAULT_LOCALE).dateFormat;
|
|
7895
|
+
},
|
|
7896
|
+
formatValue(normalizedValue, locale) {
|
|
7897
|
+
locale = locale ?? DEFAULT_LOCALE;
|
|
7898
|
+
const value = toNumber(normalizedValue, DEFAULT_LOCALE);
|
|
7899
|
+
return formatValue(value, { locale, format: this.getFormat(locale) });
|
|
7900
|
+
},
|
|
7901
|
+
toCellValue(normalizedValue) {
|
|
7902
|
+
return toNumber(normalizedValue, DEFAULT_LOCALE);
|
|
7903
|
+
},
|
|
7904
|
+
};
|
|
7905
|
+
/**
|
|
7906
|
+
* normalizes day of month number
|
|
7907
|
+
*/
|
|
7908
|
+
const dayOfMonthAdapter = {
|
|
7909
|
+
normalizeFunctionValue(value) {
|
|
7910
|
+
const day = toNumber(value, DEFAULT_LOCALE);
|
|
7911
|
+
if (day < 1 || day > 31) {
|
|
7912
|
+
throw new EvaluationError(_t("%s is not a valid day of month (it should be a number between 1 and 31)", day));
|
|
7913
|
+
}
|
|
7914
|
+
return day;
|
|
7915
|
+
},
|
|
7916
|
+
getFormat() {
|
|
7917
|
+
return "0";
|
|
7918
|
+
},
|
|
7919
|
+
formatValue(normalizedValue, locale) {
|
|
7920
|
+
locale = locale ?? DEFAULT_LOCALE;
|
|
7921
|
+
const value = toNumber(normalizedValue, DEFAULT_LOCALE);
|
|
7922
|
+
return formatValue(value, { locale, format: this.getFormat(locale) });
|
|
7923
|
+
},
|
|
7924
|
+
toCellValue(normalizedValue) {
|
|
7925
|
+
return toNumber(normalizedValue, DEFAULT_LOCALE);
|
|
7926
|
+
},
|
|
7927
|
+
};
|
|
7928
|
+
/**
|
|
7929
|
+
* Normalized value: "2/2023" for week 2 of 2023
|
|
7930
|
+
*/
|
|
7931
|
+
const weekAdapter = {
|
|
7932
|
+
normalizeFunctionValue(value) {
|
|
7933
|
+
const [week, year] = value.split("/");
|
|
7934
|
+
return `${Number(week)}/${Number(year)}`;
|
|
7935
|
+
},
|
|
7936
|
+
getFormat() {
|
|
7937
|
+
return undefined;
|
|
7938
|
+
},
|
|
7939
|
+
formatValue(normalizedValue) {
|
|
7940
|
+
const [week, year] = normalizedValue.split("/");
|
|
7941
|
+
return _t("W%(week)s %(year)s", { week, year });
|
|
7942
|
+
},
|
|
7943
|
+
toCellValue(normalizedValue) {
|
|
7944
|
+
return this.formatValue(normalizedValue);
|
|
7945
|
+
},
|
|
7946
|
+
};
|
|
7947
|
+
/**
|
|
7948
|
+
* normalizes iso week number
|
|
7949
|
+
*/
|
|
7950
|
+
const isoWeekNumberAdapter = {
|
|
7951
|
+
normalizeFunctionValue(value) {
|
|
7952
|
+
const isoWeek = toNumber(value, DEFAULT_LOCALE);
|
|
7953
|
+
if (isoWeek < 0 || isoWeek > 53) {
|
|
7954
|
+
throw new EvaluationError(_t("%s is not a valid week (it should be a number between 0 and 53)", isoWeek));
|
|
7955
|
+
}
|
|
7956
|
+
return isoWeek;
|
|
7957
|
+
},
|
|
7958
|
+
getFormat() {
|
|
7959
|
+
return "0";
|
|
7960
|
+
},
|
|
7961
|
+
formatValue(normalizedValue, locale) {
|
|
7962
|
+
locale = locale ?? DEFAULT_LOCALE;
|
|
7963
|
+
const value = toNumber(normalizedValue, DEFAULT_LOCALE);
|
|
7964
|
+
return formatValue(value, { locale, format: this.getFormat(locale) });
|
|
7965
|
+
},
|
|
7966
|
+
toCellValue(normalizedValue) {
|
|
7967
|
+
return toNumber(normalizedValue, DEFAULT_LOCALE);
|
|
7968
|
+
},
|
|
7969
|
+
};
|
|
7970
|
+
/**
|
|
7971
|
+
* normalized month value is a string formatted as "MM/yyyy" (luxon format)
|
|
7972
|
+
* e.g. "01/2020" for January 2020
|
|
7973
|
+
*/
|
|
7974
|
+
const monthAdapter = {
|
|
7975
|
+
normalizeFunctionValue(value) {
|
|
7976
|
+
const date = toNumber(value, DEFAULT_LOCALE);
|
|
7977
|
+
return formatValue(date, { locale: DEFAULT_LOCALE, format: "mm/yyyy" });
|
|
7978
|
+
},
|
|
7979
|
+
getFormat() {
|
|
7980
|
+
return "mmmm yyyy";
|
|
7981
|
+
},
|
|
7982
|
+
formatValue(normalizedValue, locale) {
|
|
7983
|
+
locale = locale ?? DEFAULT_LOCALE;
|
|
7984
|
+
const value = toNumber(normalizedValue, DEFAULT_LOCALE);
|
|
7985
|
+
return formatValue(value, { locale, format: this.getFormat(locale) });
|
|
7986
|
+
},
|
|
7987
|
+
toCellValue(normalizedValue) {
|
|
7988
|
+
return toNumber(normalizedValue, DEFAULT_LOCALE);
|
|
7989
|
+
},
|
|
7990
|
+
};
|
|
7991
|
+
/**
|
|
7992
|
+
* normalizes month number
|
|
7993
|
+
*/
|
|
7994
|
+
const monthNumberAdapter = {
|
|
7995
|
+
normalizeFunctionValue(value) {
|
|
7996
|
+
const month = toNumber(value, DEFAULT_LOCALE);
|
|
7997
|
+
if (month < 1 || month > 12) {
|
|
7998
|
+
throw new EvaluationError(_t("%s is not a valid month (it should be a number between 1 and 12)", month));
|
|
7999
|
+
}
|
|
8000
|
+
return month;
|
|
8001
|
+
},
|
|
8002
|
+
getFormat() {
|
|
8003
|
+
return "0";
|
|
8004
|
+
},
|
|
8005
|
+
formatValue(normalizedValue, locale) {
|
|
8006
|
+
locale = locale ?? DEFAULT_LOCALE;
|
|
8007
|
+
const value = toNumber(normalizedValue, DEFAULT_LOCALE);
|
|
8008
|
+
return formatValue(value, { locale, format: this.getFormat(locale) });
|
|
8009
|
+
},
|
|
8010
|
+
toCellValue(normalizedValue) {
|
|
8011
|
+
return MONTHS[toNumber(normalizedValue, DEFAULT_LOCALE) - 1].toString();
|
|
8012
|
+
},
|
|
8013
|
+
};
|
|
8014
|
+
/**
|
|
8015
|
+
* normalized quarter value is "quarter/year"
|
|
8016
|
+
* e.g. "1/2020" for Q1 2020
|
|
8017
|
+
*/
|
|
8018
|
+
const quarterAdapter = {
|
|
8019
|
+
normalizeFunctionValue(value) {
|
|
8020
|
+
const [quarter, year] = value.split("/");
|
|
8021
|
+
return `${quarter}/${year}`;
|
|
8022
|
+
},
|
|
8023
|
+
getFormat() {
|
|
8024
|
+
return undefined;
|
|
8025
|
+
},
|
|
8026
|
+
formatValue(normalizedValue) {
|
|
8027
|
+
const [quarter, year] = normalizedValue.split("/");
|
|
8028
|
+
return _t("Q%(quarter)s %(year)s", { quarter, year });
|
|
8029
|
+
},
|
|
8030
|
+
toCellValue(normalizedValue) {
|
|
8031
|
+
return this.formatValue(normalizedValue);
|
|
8032
|
+
},
|
|
8033
|
+
};
|
|
8034
|
+
/**
|
|
8035
|
+
* normalizes quarter number
|
|
8036
|
+
*/
|
|
8037
|
+
const quarterNumberAdapter = {
|
|
8038
|
+
normalizeFunctionValue(value) {
|
|
8039
|
+
const quarter = toNumber(value, DEFAULT_LOCALE);
|
|
8040
|
+
if (quarter < 1 || quarter > 4) {
|
|
8041
|
+
throw new EvaluationError(_t("%s is not a valid quarter (it should be a number between 1 and 4)", quarter));
|
|
8042
|
+
}
|
|
8043
|
+
return quarter;
|
|
8044
|
+
},
|
|
8045
|
+
getFormat() {
|
|
8046
|
+
return "0";
|
|
8047
|
+
},
|
|
8048
|
+
formatValue(normalizedValue, locale) {
|
|
8049
|
+
locale = locale ?? DEFAULT_LOCALE;
|
|
8050
|
+
const value = toNumber(normalizedValue, DEFAULT_LOCALE);
|
|
8051
|
+
return formatValue(value, { locale, format: this.getFormat(locale) });
|
|
8052
|
+
},
|
|
8053
|
+
toCellValue(normalizedValue) {
|
|
8054
|
+
return toNumber(normalizedValue, DEFAULT_LOCALE);
|
|
8055
|
+
},
|
|
8056
|
+
};
|
|
8057
|
+
const yearAdapter = {
|
|
8058
|
+
normalizeFunctionValue(value) {
|
|
8059
|
+
return toNumber(value, DEFAULT_LOCALE);
|
|
8060
|
+
},
|
|
8061
|
+
getFormat() {
|
|
8062
|
+
return "0";
|
|
8063
|
+
},
|
|
8064
|
+
formatValue(normalizedValue, locale) {
|
|
8065
|
+
locale = locale ?? DEFAULT_LOCALE;
|
|
8066
|
+
return formatValue(normalizedValue, { locale, format: "0" });
|
|
8067
|
+
},
|
|
8068
|
+
toCellValue(normalizedValue) {
|
|
8069
|
+
return toNumber(normalizedValue, DEFAULT_LOCALE);
|
|
8070
|
+
},
|
|
8071
|
+
};
|
|
8072
|
+
pivotTimeAdapterRegistry
|
|
8073
|
+
.add("day", dayAdapter)
|
|
8074
|
+
.add("week", weekAdapter)
|
|
8075
|
+
.add("month", monthAdapter)
|
|
8076
|
+
.add("quarter", quarterAdapter)
|
|
8077
|
+
.add("year", yearAdapter)
|
|
8078
|
+
.add("day_of_month", dayOfMonthAdapter)
|
|
8079
|
+
.add("iso_week_number", isoWeekNumberAdapter)
|
|
8080
|
+
.add("month_number", monthNumberAdapter)
|
|
8081
|
+
.add("quarter_number", quarterNumberAdapter)
|
|
8082
|
+
.add("year_number", yearAdapter);
|
|
8083
|
+
|
|
8084
|
+
const AGGREGATOR_NAMES = {
|
|
8085
|
+
count: _t("Count"),
|
|
8086
|
+
count_distinct: _t("Count Distinct"),
|
|
8087
|
+
bool_and: _t("Boolean And"),
|
|
8088
|
+
bool_or: _t("Boolean Or"),
|
|
8089
|
+
max: _t("Maximum"),
|
|
8090
|
+
min: _t("Minimum"),
|
|
8091
|
+
avg: _t("Average"),
|
|
8092
|
+
sum: _t("Sum"),
|
|
8093
|
+
};
|
|
8094
|
+
const NUMBER_CHAR_AGGREGATORS = ["max", "min", "avg", "sum", "count_distinct", "count"];
|
|
8095
|
+
const AGGREGATORS_BY_FIELD_TYPE = {
|
|
8096
|
+
integer: NUMBER_CHAR_AGGREGATORS,
|
|
8097
|
+
char: NUMBER_CHAR_AGGREGATORS,
|
|
8098
|
+
boolean: ["count_distinct", "count", "bool_and", "bool_or"],
|
|
8099
|
+
};
|
|
8100
|
+
const AGGREGATORS = {};
|
|
8101
|
+
for (const type in AGGREGATORS_BY_FIELD_TYPE) {
|
|
8102
|
+
AGGREGATORS[type] = {};
|
|
8103
|
+
for (const aggregator of AGGREGATORS_BY_FIELD_TYPE[type]) {
|
|
8104
|
+
AGGREGATORS[type][aggregator] = AGGREGATOR_NAMES[aggregator];
|
|
8105
|
+
}
|
|
8106
|
+
}
|
|
8107
|
+
const AGGREGATORS_FN = {
|
|
8108
|
+
count: {
|
|
8109
|
+
fn: (args) => countAny([args]),
|
|
8110
|
+
format: () => "0",
|
|
8111
|
+
},
|
|
8112
|
+
count_distinct: {
|
|
8113
|
+
fn: (args) => countUnique([args]),
|
|
8114
|
+
format: () => "0",
|
|
8115
|
+
},
|
|
8116
|
+
bool_and: {
|
|
8117
|
+
fn: (args) => boolAnd([args]).result,
|
|
8118
|
+
format: () => undefined,
|
|
8119
|
+
},
|
|
8120
|
+
bool_or: {
|
|
8121
|
+
fn: (args) => boolOr([args]).result,
|
|
8122
|
+
format: () => undefined,
|
|
8123
|
+
},
|
|
8124
|
+
max: {
|
|
8125
|
+
fn: (args, locale) => max([args], locale),
|
|
8126
|
+
format: inferFormat,
|
|
8127
|
+
},
|
|
8128
|
+
min: {
|
|
8129
|
+
fn: (args, locale) => min([args], locale),
|
|
8130
|
+
format: inferFormat,
|
|
8131
|
+
},
|
|
8132
|
+
avg: {
|
|
8133
|
+
fn: (args, locale) => average([args], locale),
|
|
8134
|
+
format: inferFormat,
|
|
8135
|
+
},
|
|
8136
|
+
sum: {
|
|
8137
|
+
fn: (args, locale) => sum([args], locale),
|
|
8138
|
+
format: inferFormat,
|
|
8139
|
+
},
|
|
8140
|
+
};
|
|
8141
|
+
function makePivotFormulaFromPivotCell(pivotFormulaId, pivotCell) {
|
|
8142
|
+
switch (pivotCell.type) {
|
|
8143
|
+
case "HEADER":
|
|
8144
|
+
return makePivotFormula("PIVOT.HEADER", [pivotFormulaId, ...flatPivotDomain(pivotCell.domain)].filter(isDefined));
|
|
8145
|
+
case "MEASURE_HEADER":
|
|
8146
|
+
return makePivotFormula("PIVOT.HEADER", [pivotFormulaId, ...flatPivotDomain(pivotCell.domain), "measure", pivotCell.measure].filter(isDefined));
|
|
8147
|
+
case "VALUE":
|
|
8148
|
+
return makePivotFormula("PIVOT.VALUE", [pivotFormulaId, pivotCell.measure, ...flatPivotDomain(pivotCell.domain)].filter(isDefined));
|
|
8149
|
+
case "EMPTY":
|
|
8150
|
+
return "";
|
|
8151
|
+
}
|
|
8152
|
+
}
|
|
8153
|
+
/**
|
|
8154
|
+
* Build a pivot formula expression
|
|
8155
|
+
*/
|
|
8156
|
+
function makePivotFormula(formula, args) {
|
|
8157
|
+
return `=${formula}(${args
|
|
8158
|
+
.map((arg) => {
|
|
8159
|
+
const stringIsNumber = typeof arg == "string" && !isNaN(Number(arg)) && Number(arg).toString() === arg;
|
|
8160
|
+
const convertToNumber = typeof arg == "number" || stringIsNumber;
|
|
8161
|
+
return convertToNumber ? `${arg}` : `"${arg.toString().replace(/"/g, '\\"')}"`;
|
|
8162
|
+
})
|
|
8163
|
+
.join(",")})`;
|
|
8164
|
+
}
|
|
8165
|
+
/**
|
|
8166
|
+
* Given an object of form {"1": {...}, "2": {...}, ...} get the maximum ID used
|
|
8167
|
+
* in this object
|
|
8168
|
+
* If the object has no keys, return 0
|
|
8169
|
+
*
|
|
8170
|
+
*/
|
|
8171
|
+
function getMaxObjectId(o) {
|
|
8172
|
+
const keys = Object.keys(o);
|
|
8173
|
+
if (!keys.length) {
|
|
8174
|
+
return 0;
|
|
8175
|
+
}
|
|
8176
|
+
const nums = keys.map((id) => parseInt(id, 10));
|
|
8177
|
+
const max = Math.max(...nums);
|
|
8178
|
+
return max;
|
|
8179
|
+
}
|
|
8180
|
+
const ALL_PERIODS = {
|
|
8181
|
+
year: _t("Year"),
|
|
8182
|
+
quarter: _t("Quarter"),
|
|
8183
|
+
month: _t("Month"),
|
|
8184
|
+
week: _t("Week"),
|
|
8185
|
+
day: _t("Day"),
|
|
8186
|
+
year_number: _t("Year"),
|
|
8187
|
+
quarter_number: _t("Quarter"),
|
|
8188
|
+
month_number: _t("Month"),
|
|
8189
|
+
iso_week_number: _t("Week"),
|
|
8190
|
+
day_of_month: _t("Day of Month"),
|
|
8191
|
+
};
|
|
8192
|
+
const DATE_FIELDS = ["date", "datetime"];
|
|
8193
|
+
/**
|
|
8194
|
+
* Parse a dimension string into a pivot dimension definition.
|
|
8195
|
+
* e.g "create_date:month" => { name: "create_date", granularity: "month" }
|
|
8196
|
+
*/
|
|
8197
|
+
function parseDimension(dimension) {
|
|
8198
|
+
const [name, granularity] = dimension.split(":");
|
|
8199
|
+
if (granularity) {
|
|
8200
|
+
return { name, granularity };
|
|
8201
|
+
}
|
|
8202
|
+
return { name };
|
|
8203
|
+
}
|
|
8204
|
+
function isDateField(field) {
|
|
8205
|
+
return DATE_FIELDS.includes(field.type);
|
|
8206
|
+
}
|
|
8207
|
+
function toPivotDomain(domainStr) {
|
|
8208
|
+
if (domainStr.length % 2 !== 0) {
|
|
8209
|
+
throw new Error("Invalid domain: odd number of elements");
|
|
8210
|
+
}
|
|
8211
|
+
const domain = [];
|
|
8212
|
+
for (let i = 0; i < domainStr.length - 1; i += 2) {
|
|
8213
|
+
domain.push({ field: domainStr[i], value: domainStr[i + 1] });
|
|
8214
|
+
}
|
|
8215
|
+
return domain;
|
|
8216
|
+
}
|
|
8217
|
+
function flatPivotDomain(domain) {
|
|
8218
|
+
return domain.flatMap((arg) => [arg.field, arg.value]);
|
|
8219
|
+
}
|
|
8220
|
+
/**
|
|
8221
|
+
* Parses the value defining a pivot group in a PIVOT formula
|
|
8222
|
+
* e.g. given the following formula PIVOT.VALUE("1", "stage_id", "42", "status", "won"),
|
|
8223
|
+
* the two group values are "42" and "won".
|
|
8224
|
+
*/
|
|
8225
|
+
function toNormalizedPivotValue(dimension, groupValue) {
|
|
8226
|
+
if (groupValue === null || groupValue === "null") {
|
|
8227
|
+
return null;
|
|
8228
|
+
}
|
|
8229
|
+
const groupValueString = typeof groupValue === "boolean"
|
|
8230
|
+
? toString(groupValue).toLocaleLowerCase()
|
|
8231
|
+
: toString(groupValue);
|
|
8232
|
+
if (!pivotNormalizationValueRegistry.contains(dimension.type)) {
|
|
8233
|
+
throw new EvaluationError(_t("Field %(field)s is not supported because of its type (%(type)s)", {
|
|
8234
|
+
field: dimension.displayName,
|
|
8235
|
+
type: dimension.type,
|
|
8236
|
+
}));
|
|
8237
|
+
}
|
|
8238
|
+
// represents a field which is not set (=False server side)
|
|
8239
|
+
if (groupValueString === "false") {
|
|
8240
|
+
return false;
|
|
8241
|
+
}
|
|
8242
|
+
const normalizer = pivotNormalizationValueRegistry.get(dimension.type);
|
|
8243
|
+
return normalizer(groupValueString, dimension.granularity);
|
|
8244
|
+
}
|
|
8245
|
+
function normalizeDateTime(value, granularity) {
|
|
8246
|
+
if (!granularity) {
|
|
8247
|
+
throw "";
|
|
8248
|
+
}
|
|
8249
|
+
return pivotTimeAdapter(granularity).normalizeFunctionValue(value);
|
|
8250
|
+
}
|
|
8251
|
+
const pivotNormalizationValueRegistry = new Registry();
|
|
8252
|
+
pivotNormalizationValueRegistry
|
|
8253
|
+
.add("date", normalizeDateTime)
|
|
8254
|
+
.add("datetime", normalizeDateTime)
|
|
8255
|
+
.add("integer", (value) => toNumber(value, DEFAULT_LOCALE))
|
|
8256
|
+
.add("boolean", (value) => toBoolean(value))
|
|
8257
|
+
.add("char", (value) => toString(value));
|
|
8258
|
+
|
|
7758
8259
|
/**
|
|
7759
8260
|
* Change the reference types inside the given token, if the token represent a range or a cell
|
|
7760
8261
|
*
|
|
@@ -7953,6 +8454,11 @@ const CellIsOperators = {
|
|
|
7953
8454
|
const ChartTerms = {
|
|
7954
8455
|
Series: _t("Series"),
|
|
7955
8456
|
BackgroundColor: _t("Background color"),
|
|
8457
|
+
StackedBarChart: _t("Stacked bar chart"),
|
|
8458
|
+
StackedLineChart: _t("Stacked line chart"),
|
|
8459
|
+
CumulativeData: _t("Cumulative data"),
|
|
8460
|
+
TreatLabelsAsText: _t("Treat labels as text"),
|
|
8461
|
+
AggregatedChart: _t("Aggregate"),
|
|
7956
8462
|
Errors: {
|
|
7957
8463
|
Unexpected: _t("The chart definition is invalid for an unknown reason"),
|
|
7958
8464
|
// BASIC CHART ERRORS (LINE | BAR | PIE)
|
|
@@ -8885,6 +9391,7 @@ function drawHighlight(renderingContext, highlight, rect) {
|
|
|
8885
9391
|
}
|
|
8886
9392
|
const color = highlight.color || HIGHLIGHT_COLOR;
|
|
8887
9393
|
const { ctx } = renderingContext;
|
|
9394
|
+
ctx.save();
|
|
8888
9395
|
if (!highlight.noBorder) {
|
|
8889
9396
|
if (highlight.dashed) {
|
|
8890
9397
|
ctx.setLineDash([5, 3]);
|
|
@@ -8904,6 +9411,7 @@ function drawHighlight(renderingContext, highlight, rect) {
|
|
|
8904
9411
|
ctx.fillStyle = setColorAlpha(toHex(color), highlight.fillAlpha ?? 0.12);
|
|
8905
9412
|
ctx.fillRect(x, y, width, height);
|
|
8906
9413
|
}
|
|
9414
|
+
ctx.restore();
|
|
8907
9415
|
}
|
|
8908
9416
|
|
|
8909
9417
|
class HighlightStore extends SpreadsheetStore {
|
|
@@ -9434,8 +9942,20 @@ class ComposerStore extends SpreadsheetStore {
|
|
|
9434
9942
|
replaceSelectedRange(zone) {
|
|
9435
9943
|
const ref = this.getZoneReference(zone);
|
|
9436
9944
|
const currentToken = this.tokenAtCursor;
|
|
9437
|
-
|
|
9438
|
-
|
|
9945
|
+
let replaceStart = this.selectionStart;
|
|
9946
|
+
if (currentToken?.type === "REFERENCE") {
|
|
9947
|
+
replaceStart = currentToken.start;
|
|
9948
|
+
}
|
|
9949
|
+
else if (currentToken?.type === "RIGHT_PAREN") {
|
|
9950
|
+
// match left parenthesis
|
|
9951
|
+
const leftParenthesisIndex = this.currentTokens.findIndex((token) => token.type === "LEFT_PAREN" && token.parenIndex === currentToken.parenIndex);
|
|
9952
|
+
const functionToken = this.currentTokens[leftParenthesisIndex - 1];
|
|
9953
|
+
if (functionToken === undefined) {
|
|
9954
|
+
return;
|
|
9955
|
+
}
|
|
9956
|
+
replaceStart = functionToken.start;
|
|
9957
|
+
}
|
|
9958
|
+
this.replaceText(ref, replaceStart, this.selectionEnd);
|
|
9439
9959
|
}
|
|
9440
9960
|
/**
|
|
9441
9961
|
* Replace the reference of the old zone by the new one.
|
|
@@ -9455,10 +9975,8 @@ class ComposerStore extends SpreadsheetStore {
|
|
|
9455
9975
|
const refRange = this.getters.getRangeFromSheetXC(activeSheetId, xc);
|
|
9456
9976
|
return isEqual(this.getters.expandZone(activeSheetId, refRange.zone), oldZone);
|
|
9457
9977
|
});
|
|
9458
|
-
// this function assumes that the previous range is always found because
|
|
9459
|
-
// it's called when changing a highlight, which exists by definition
|
|
9460
9978
|
if (!previousRefToken) {
|
|
9461
|
-
|
|
9979
|
+
return;
|
|
9462
9980
|
}
|
|
9463
9981
|
const previousRange = this.getters.getRangeFromSheetXC(activeSheetId, previousRefToken.value);
|
|
9464
9982
|
this.selectionStart = previousRefToken.start;
|
|
@@ -9470,6 +9988,17 @@ class ComposerStore extends SpreadsheetStore {
|
|
|
9470
9988
|
getZoneReference(zone) {
|
|
9471
9989
|
const inputSheetId = this.currentEditedCell.sheetId;
|
|
9472
9990
|
const sheetId = this.getters.getActiveSheetId();
|
|
9991
|
+
if (zone.top === zone.bottom && zone.left === zone.right) {
|
|
9992
|
+
const position = { sheetId, col: zone.left, row: zone.top };
|
|
9993
|
+
const pivotId = this.getters.getPivotIdFromPosition(position);
|
|
9994
|
+
const pivotCell = this.getters.getPivotCellFromPosition(position);
|
|
9995
|
+
const cell = this.getters.getCell(position);
|
|
9996
|
+
if (pivotId && pivotCell.type !== "EMPTY" && !cell?.isFormula) {
|
|
9997
|
+
const formulaPivotId = this.getters.getPivotFormulaId(pivotId);
|
|
9998
|
+
const formula = makePivotFormulaFromPivotCell(formulaPivotId, pivotCell);
|
|
9999
|
+
return formula.slice(1); // strip leading =
|
|
10000
|
+
}
|
|
10001
|
+
}
|
|
9473
10002
|
const range = this.getters.getRangeFromZone(sheetId, zone);
|
|
9474
10003
|
return this.getters.getSelectionRangeString(range, inputSheetId);
|
|
9475
10004
|
}
|
|
@@ -9572,19 +10101,35 @@ class ComposerStore extends SpreadsheetStore {
|
|
|
9572
10101
|
const colorIndex = this.colorIndexByRange[rangeString];
|
|
9573
10102
|
return colors$1[colorIndex % colors$1.length];
|
|
9574
10103
|
};
|
|
9575
|
-
|
|
10104
|
+
const highlights = [];
|
|
10105
|
+
for (const range of this.getReferencedRanges()) {
|
|
9576
10106
|
const rangeString = this.getters.getRangeString(range, editionSheetId);
|
|
9577
10107
|
const { numberOfRows, numberOfCols } = zoneToDimension(range.zone);
|
|
9578
10108
|
const zone = numberOfRows * numberOfCols === 1
|
|
9579
10109
|
? this.getters.expandZone(range.sheetId, range.zone)
|
|
9580
10110
|
: range.zone;
|
|
9581
|
-
|
|
10111
|
+
highlights.push({
|
|
9582
10112
|
zone,
|
|
9583
10113
|
color: rangeColor(rangeString),
|
|
9584
10114
|
sheetId: range.sheetId,
|
|
9585
10115
|
interactive: true,
|
|
9586
|
-
};
|
|
9587
|
-
}
|
|
10116
|
+
});
|
|
10117
|
+
}
|
|
10118
|
+
const activeSheetId = this.getters.getActiveSheetId();
|
|
10119
|
+
const selectionZone = this.model.selection.getAnchor().zone;
|
|
10120
|
+
const isSelectionHightlighted = highlights.find((highlight) => highlight.sheetId === activeSheetId && isEqual(highlight.zone, selectionZone));
|
|
10121
|
+
if (this.editionMode === "selecting" && !isSelectionHightlighted) {
|
|
10122
|
+
highlights.push({
|
|
10123
|
+
zone: selectionZone,
|
|
10124
|
+
color: "#445566",
|
|
10125
|
+
sheetId: activeSheetId,
|
|
10126
|
+
dashed: true,
|
|
10127
|
+
interactive: false,
|
|
10128
|
+
noFill: true,
|
|
10129
|
+
thinLine: true,
|
|
10130
|
+
});
|
|
10131
|
+
}
|
|
10132
|
+
return highlights;
|
|
9588
10133
|
}
|
|
9589
10134
|
/**
|
|
9590
10135
|
* Return ranges currently referenced in the composer
|
|
@@ -9843,8 +10388,14 @@ class ChartJsComponent extends Component {
|
|
|
9843
10388
|
useEffect(() => {
|
|
9844
10389
|
const runtime = this.chartRuntime;
|
|
9845
10390
|
if (!deepEquals(runtime, this.currentRuntime, "ignoreFunctions")) {
|
|
10391
|
+
if (runtime.chartJsConfig.type !== this.currentRuntime.chartJsConfig.type) {
|
|
10392
|
+
this.chart?.destroy();
|
|
10393
|
+
this.createChart(deepCopy(runtime.chartJsConfig));
|
|
10394
|
+
}
|
|
10395
|
+
else {
|
|
10396
|
+
this.updateChartJs(deepCopy(runtime));
|
|
10397
|
+
}
|
|
9846
10398
|
this.currentRuntime = runtime;
|
|
9847
|
-
this.updateChartJs(deepCopy(runtime));
|
|
9848
10399
|
}
|
|
9849
10400
|
});
|
|
9850
10401
|
}
|
|
@@ -10212,6 +10763,9 @@ function getChartAxisTitleRuntime(design) {
|
|
|
10212
10763
|
}
|
|
10213
10764
|
function getDefinedAxis(definition) {
|
|
10214
10765
|
let useLeftAxis = false, useRightAxis = false;
|
|
10766
|
+
if ("horizontal" in definition && definition.horizontal) {
|
|
10767
|
+
return { useLeftAxis: true, useRightAxis: false };
|
|
10768
|
+
}
|
|
10215
10769
|
for (const design of definition.dataSets || []) {
|
|
10216
10770
|
if (design.yAxisId === "y1") {
|
|
10217
10771
|
useRightAxis = true;
|
|
@@ -11779,13 +12333,6 @@ var misc = /*#__PURE__*/Object.freeze({
|
|
|
11779
12333
|
FORMAT_LARGE_NUMBER: FORMAT_LARGE_NUMBER
|
|
11780
12334
|
});
|
|
11781
12335
|
|
|
11782
|
-
function sum(values, locale) {
|
|
11783
|
-
return reduceNumbers(values, (acc, a) => acc + a, 0, locale);
|
|
11784
|
-
}
|
|
11785
|
-
function countUnique(args) {
|
|
11786
|
-
return reduceAny(args, (acc, a) => (isDataNonEmpty(a) ? acc.add(a?.value) : acc), new Set()).size;
|
|
11787
|
-
}
|
|
11788
|
-
|
|
11789
12336
|
const DEFAULT_FACTOR = 1;
|
|
11790
12337
|
const DEFAULT_MODE = 0;
|
|
11791
12338
|
const DEFAULT_PLACES = 0;
|
|
@@ -12899,53 +13446,6 @@ var math = /*#__PURE__*/Object.freeze({
|
|
|
12899
13446
|
TRUNC: TRUNC
|
|
12900
13447
|
});
|
|
12901
13448
|
|
|
12902
|
-
function assertSameNumberOfElements(...args) {
|
|
12903
|
-
const dims = args[0].length;
|
|
12904
|
-
args.forEach((arg, i) => assert(() => arg.length === dims, _t("[[FUNCTION_NAME]] has mismatched dimensions for argument %s (%s vs %s).", i.toString(), dims.toString(), arg.length.toString())));
|
|
12905
|
-
}
|
|
12906
|
-
function average(values, locale) {
|
|
12907
|
-
let count = 0;
|
|
12908
|
-
const sum = reduceNumbers(values, (acc, a) => {
|
|
12909
|
-
count += 1;
|
|
12910
|
-
return acc + a;
|
|
12911
|
-
}, 0, locale);
|
|
12912
|
-
assertNotZero(count);
|
|
12913
|
-
return sum / count;
|
|
12914
|
-
}
|
|
12915
|
-
function countNumbers(values, locale) {
|
|
12916
|
-
let count = 0;
|
|
12917
|
-
for (let n of values) {
|
|
12918
|
-
if (isMatrix(n)) {
|
|
12919
|
-
for (let i of n) {
|
|
12920
|
-
for (let j of i) {
|
|
12921
|
-
if (typeof j.value === "number") {
|
|
12922
|
-
count += 1;
|
|
12923
|
-
}
|
|
12924
|
-
}
|
|
12925
|
-
}
|
|
12926
|
-
}
|
|
12927
|
-
else {
|
|
12928
|
-
const value = n?.value;
|
|
12929
|
-
if (!isEvaluationError(value) &&
|
|
12930
|
-
(typeof value !== "string" || isNumber(value, locale) || parseDateTime(value, locale))) {
|
|
12931
|
-
count += 1;
|
|
12932
|
-
}
|
|
12933
|
-
}
|
|
12934
|
-
}
|
|
12935
|
-
return count;
|
|
12936
|
-
}
|
|
12937
|
-
function countAny(values) {
|
|
12938
|
-
return reduceAny(values, (acc, a) => (a !== undefined && a.value !== null ? acc + 1 : acc), 0);
|
|
12939
|
-
}
|
|
12940
|
-
function max(values, locale) {
|
|
12941
|
-
const result = reduceNumbers(values, (acc, a) => (acc < a ? a : acc), -Infinity, locale);
|
|
12942
|
-
return result === -Infinity ? 0 : result;
|
|
12943
|
-
}
|
|
12944
|
-
function min(values, locale) {
|
|
12945
|
-
const result = reduceNumbers(values, (acc, a) => (a < acc ? a : acc), Infinity, locale);
|
|
12946
|
-
return result === Infinity ? 0 : result;
|
|
12947
|
-
}
|
|
12948
|
-
|
|
12949
13449
|
function filterAndFlatData(dataY, dataX) {
|
|
12950
13450
|
const _flatDataY = [];
|
|
12951
13451
|
const _flatDataX = [];
|
|
@@ -13501,7 +14001,7 @@ const LARGE = {
|
|
|
13501
14001
|
// LINEST
|
|
13502
14002
|
// -----------------------------------------------------------------------------
|
|
13503
14003
|
const LINEST = {
|
|
13504
|
-
description: _t("
|
|
14004
|
+
description: _t("Given partial data about a linear trend, calculates various parameters about the ideal linear trend using the least-squares method."),
|
|
13505
14005
|
args: [
|
|
13506
14006
|
arg("data_y (range<number>)", _t("The range representing the array or matrix of dependent data.")),
|
|
13507
14007
|
arg("data_x (range<number>, default={1;2;3;...})", _t("The range representing the array or matrix of independent data.")),
|
|
@@ -13517,7 +14017,7 @@ const LINEST = {
|
|
|
13517
14017
|
// LOGEST
|
|
13518
14018
|
// -----------------------------------------------------------------------------
|
|
13519
14019
|
const LOGEST = {
|
|
13520
|
-
description: _t("
|
|
14020
|
+
description: _t("Given partial data about an exponential growth curve, calculates various parameters about the best fit ideal exponential growth curve."),
|
|
13521
14021
|
args: [
|
|
13522
14022
|
arg("data_y (range<number>)", _t("The range representing the array or matrix of dependent data.")),
|
|
13523
14023
|
arg("data_x (range<number>, optional, default={1;2;3;...})", _t("The range representing the array or matrix of independent data.")),
|
|
@@ -17920,33 +18420,6 @@ var info = /*#__PURE__*/Object.freeze({
|
|
|
17920
18420
|
NA: NA
|
|
17921
18421
|
});
|
|
17922
18422
|
|
|
17923
|
-
function boolAnd(args) {
|
|
17924
|
-
let foundBoolean = false;
|
|
17925
|
-
let acc = true;
|
|
17926
|
-
conditionalVisitBoolean(args, (arg) => {
|
|
17927
|
-
foundBoolean = true;
|
|
17928
|
-
acc = acc && arg;
|
|
17929
|
-
return acc;
|
|
17930
|
-
});
|
|
17931
|
-
return {
|
|
17932
|
-
foundBoolean,
|
|
17933
|
-
result: acc,
|
|
17934
|
-
};
|
|
17935
|
-
}
|
|
17936
|
-
function boolOr(args) {
|
|
17937
|
-
let foundBoolean = false;
|
|
17938
|
-
let acc = false;
|
|
17939
|
-
conditionalVisitBoolean(args, (arg) => {
|
|
17940
|
-
foundBoolean = true;
|
|
17941
|
-
acc = acc || arg;
|
|
17942
|
-
return !acc;
|
|
17943
|
-
});
|
|
17944
|
-
return {
|
|
17945
|
-
foundBoolean,
|
|
17946
|
-
result: acc,
|
|
17947
|
-
};
|
|
17948
|
-
}
|
|
17949
|
-
|
|
17950
18423
|
// -----------------------------------------------------------------------------
|
|
17951
18424
|
// AND
|
|
17952
18425
|
// -----------------------------------------------------------------------------
|
|
@@ -18144,393 +18617,6 @@ var logical = /*#__PURE__*/Object.freeze({
|
|
|
18144
18617
|
XOR: XOR
|
|
18145
18618
|
});
|
|
18146
18619
|
|
|
18147
|
-
const pivotTimeAdapterRegistry = new Registry();
|
|
18148
|
-
function pivotTimeAdapter(granularity) {
|
|
18149
|
-
return pivotTimeAdapterRegistry.get(granularity);
|
|
18150
|
-
}
|
|
18151
|
-
/**
|
|
18152
|
-
* The Time Adapter: Managing Time Periods for Pivot Functions
|
|
18153
|
-
*
|
|
18154
|
-
* Overview:
|
|
18155
|
-
* A time adapter is responsible for managing time periods associated with pivot functions.
|
|
18156
|
-
* Each type of period (day, week, month, quarter, etc.) has its own dedicated adapter.
|
|
18157
|
-
* The adapter's primary role is to normalize period values between spreadsheet functions,
|
|
18158
|
-
* and the pivot.
|
|
18159
|
-
* By normalizing the period value, it can be stored consistently in the pivot.
|
|
18160
|
-
*
|
|
18161
|
-
* Normalization Process:
|
|
18162
|
-
* When working with functions in the spreadsheet, the time adapter normalizes
|
|
18163
|
-
* the provided period to facilitate accurate lookup of values in the pivot.
|
|
18164
|
-
* For instance, if the spreadsheet function represents a day period as a number generated
|
|
18165
|
-
* by the DATE function (DATE(2023, 12, 25)), the time adapter will normalize it accordingly.
|
|
18166
|
-
*
|
|
18167
|
-
*/
|
|
18168
|
-
/**
|
|
18169
|
-
* Normalized value: "12/25/2023"
|
|
18170
|
-
*
|
|
18171
|
-
* Note: Those two format are equivalent:
|
|
18172
|
-
* - "MM/dd/yyyy" (luxon format)
|
|
18173
|
-
* - "mm/dd/yyyy" (spreadsheet format)
|
|
18174
|
-
**/
|
|
18175
|
-
const dayAdapter = {
|
|
18176
|
-
normalizeFunctionValue(value) {
|
|
18177
|
-
const date = toNumber(value, DEFAULT_LOCALE);
|
|
18178
|
-
return formatValue(date, { locale: DEFAULT_LOCALE, format: "mm/dd/yyyy" });
|
|
18179
|
-
},
|
|
18180
|
-
getFormat(locale) {
|
|
18181
|
-
return (locale ?? DEFAULT_LOCALE).dateFormat;
|
|
18182
|
-
},
|
|
18183
|
-
formatValue(normalizedValue, locale) {
|
|
18184
|
-
locale = locale ?? DEFAULT_LOCALE;
|
|
18185
|
-
const value = toNumber(normalizedValue, DEFAULT_LOCALE);
|
|
18186
|
-
return formatValue(value, { locale, format: this.getFormat(locale) });
|
|
18187
|
-
},
|
|
18188
|
-
toCellValue(normalizedValue) {
|
|
18189
|
-
return toNumber(normalizedValue, DEFAULT_LOCALE);
|
|
18190
|
-
},
|
|
18191
|
-
};
|
|
18192
|
-
/**
|
|
18193
|
-
* normalizes day of month number
|
|
18194
|
-
*/
|
|
18195
|
-
const dayOfMonthAdapter = {
|
|
18196
|
-
normalizeFunctionValue(value) {
|
|
18197
|
-
const day = toNumber(value, DEFAULT_LOCALE);
|
|
18198
|
-
if (day < 1 || day > 31) {
|
|
18199
|
-
throw new EvaluationError(_t("%s is not a valid day of month (it should be a number between 1 and 31)", day));
|
|
18200
|
-
}
|
|
18201
|
-
return day;
|
|
18202
|
-
},
|
|
18203
|
-
getFormat() {
|
|
18204
|
-
return "0";
|
|
18205
|
-
},
|
|
18206
|
-
formatValue(normalizedValue, locale) {
|
|
18207
|
-
locale = locale ?? DEFAULT_LOCALE;
|
|
18208
|
-
const value = toNumber(normalizedValue, DEFAULT_LOCALE);
|
|
18209
|
-
return formatValue(value, { locale, format: this.getFormat(locale) });
|
|
18210
|
-
},
|
|
18211
|
-
toCellValue(normalizedValue) {
|
|
18212
|
-
return toNumber(normalizedValue, DEFAULT_LOCALE);
|
|
18213
|
-
},
|
|
18214
|
-
};
|
|
18215
|
-
/**
|
|
18216
|
-
* Normalized value: "2/2023" for week 2 of 2023
|
|
18217
|
-
*/
|
|
18218
|
-
const weekAdapter = {
|
|
18219
|
-
normalizeFunctionValue(value) {
|
|
18220
|
-
const [week, year] = value.split("/");
|
|
18221
|
-
return `${Number(week)}/${Number(year)}`;
|
|
18222
|
-
},
|
|
18223
|
-
getFormat() {
|
|
18224
|
-
return undefined;
|
|
18225
|
-
},
|
|
18226
|
-
formatValue(normalizedValue) {
|
|
18227
|
-
const [week, year] = normalizedValue.split("/");
|
|
18228
|
-
return _t("W%(week)s %(year)s", { week, year });
|
|
18229
|
-
},
|
|
18230
|
-
toCellValue(normalizedValue) {
|
|
18231
|
-
return this.formatValue(normalizedValue);
|
|
18232
|
-
},
|
|
18233
|
-
};
|
|
18234
|
-
/**
|
|
18235
|
-
* normalizes iso week number
|
|
18236
|
-
*/
|
|
18237
|
-
const isoWeekNumberAdapter = {
|
|
18238
|
-
normalizeFunctionValue(value) {
|
|
18239
|
-
const isoWeek = toNumber(value, DEFAULT_LOCALE);
|
|
18240
|
-
if (isoWeek < 0 || isoWeek > 53) {
|
|
18241
|
-
throw new EvaluationError(_t("%s is not a valid week (it should be a number between 0 and 53)", isoWeek));
|
|
18242
|
-
}
|
|
18243
|
-
return isoWeek;
|
|
18244
|
-
},
|
|
18245
|
-
getFormat() {
|
|
18246
|
-
return "0";
|
|
18247
|
-
},
|
|
18248
|
-
formatValue(normalizedValue, locale) {
|
|
18249
|
-
locale = locale ?? DEFAULT_LOCALE;
|
|
18250
|
-
const value = toNumber(normalizedValue, DEFAULT_LOCALE);
|
|
18251
|
-
return formatValue(value, { locale, format: this.getFormat(locale) });
|
|
18252
|
-
},
|
|
18253
|
-
toCellValue(normalizedValue) {
|
|
18254
|
-
return toNumber(normalizedValue, DEFAULT_LOCALE);
|
|
18255
|
-
},
|
|
18256
|
-
};
|
|
18257
|
-
/**
|
|
18258
|
-
* normalized month value is a string formatted as "MM/yyyy" (luxon format)
|
|
18259
|
-
* e.g. "01/2020" for January 2020
|
|
18260
|
-
*/
|
|
18261
|
-
const monthAdapter = {
|
|
18262
|
-
normalizeFunctionValue(value) {
|
|
18263
|
-
const date = toNumber(value, DEFAULT_LOCALE);
|
|
18264
|
-
return formatValue(date, { locale: DEFAULT_LOCALE, format: "mm/yyyy" });
|
|
18265
|
-
},
|
|
18266
|
-
getFormat() {
|
|
18267
|
-
return "mmmm yyyy";
|
|
18268
|
-
},
|
|
18269
|
-
formatValue(normalizedValue, locale) {
|
|
18270
|
-
locale = locale ?? DEFAULT_LOCALE;
|
|
18271
|
-
const value = toNumber(normalizedValue, DEFAULT_LOCALE);
|
|
18272
|
-
return formatValue(value, { locale, format: this.getFormat(locale) });
|
|
18273
|
-
},
|
|
18274
|
-
toCellValue(normalizedValue) {
|
|
18275
|
-
return toNumber(normalizedValue, DEFAULT_LOCALE);
|
|
18276
|
-
},
|
|
18277
|
-
};
|
|
18278
|
-
/**
|
|
18279
|
-
* normalizes month number
|
|
18280
|
-
*/
|
|
18281
|
-
const monthNumberAdapter = {
|
|
18282
|
-
normalizeFunctionValue(value) {
|
|
18283
|
-
const month = toNumber(value, DEFAULT_LOCALE);
|
|
18284
|
-
if (month < 1 || month > 12) {
|
|
18285
|
-
throw new EvaluationError(_t("%s is not a valid month (it should be a number between 1 and 12)", month));
|
|
18286
|
-
}
|
|
18287
|
-
return month;
|
|
18288
|
-
},
|
|
18289
|
-
getFormat() {
|
|
18290
|
-
return "0";
|
|
18291
|
-
},
|
|
18292
|
-
formatValue(normalizedValue, locale) {
|
|
18293
|
-
locale = locale ?? DEFAULT_LOCALE;
|
|
18294
|
-
const value = toNumber(normalizedValue, DEFAULT_LOCALE);
|
|
18295
|
-
return formatValue(value, { locale, format: this.getFormat(locale) });
|
|
18296
|
-
},
|
|
18297
|
-
toCellValue(normalizedValue) {
|
|
18298
|
-
return MONTHS[toNumber(normalizedValue, DEFAULT_LOCALE) - 1].toString();
|
|
18299
|
-
},
|
|
18300
|
-
};
|
|
18301
|
-
/**
|
|
18302
|
-
* normalized quarter value is "quarter/year"
|
|
18303
|
-
* e.g. "1/2020" for Q1 2020
|
|
18304
|
-
*/
|
|
18305
|
-
const quarterAdapter = {
|
|
18306
|
-
normalizeFunctionValue(value) {
|
|
18307
|
-
const [quarter, year] = value.split("/");
|
|
18308
|
-
return `${quarter}/${year}`;
|
|
18309
|
-
},
|
|
18310
|
-
getFormat() {
|
|
18311
|
-
return undefined;
|
|
18312
|
-
},
|
|
18313
|
-
formatValue(normalizedValue) {
|
|
18314
|
-
const [quarter, year] = normalizedValue.split("/");
|
|
18315
|
-
return _t("Q%(quarter)s %(year)s", { quarter, year });
|
|
18316
|
-
},
|
|
18317
|
-
toCellValue(normalizedValue) {
|
|
18318
|
-
return this.formatValue(normalizedValue);
|
|
18319
|
-
},
|
|
18320
|
-
};
|
|
18321
|
-
/**
|
|
18322
|
-
* normalizes quarter number
|
|
18323
|
-
*/
|
|
18324
|
-
const quarterNumberAdapter = {
|
|
18325
|
-
normalizeFunctionValue(value) {
|
|
18326
|
-
const quarter = toNumber(value, DEFAULT_LOCALE);
|
|
18327
|
-
if (quarter < 1 || quarter > 4) {
|
|
18328
|
-
throw new EvaluationError(_t("%s is not a valid quarter (it should be a number between 1 and 4)", quarter));
|
|
18329
|
-
}
|
|
18330
|
-
return quarter;
|
|
18331
|
-
},
|
|
18332
|
-
getFormat() {
|
|
18333
|
-
return "0";
|
|
18334
|
-
},
|
|
18335
|
-
formatValue(normalizedValue, locale) {
|
|
18336
|
-
locale = locale ?? DEFAULT_LOCALE;
|
|
18337
|
-
const value = toNumber(normalizedValue, DEFAULT_LOCALE);
|
|
18338
|
-
return formatValue(value, { locale, format: this.getFormat(locale) });
|
|
18339
|
-
},
|
|
18340
|
-
toCellValue(normalizedValue) {
|
|
18341
|
-
return toNumber(normalizedValue, DEFAULT_LOCALE);
|
|
18342
|
-
},
|
|
18343
|
-
};
|
|
18344
|
-
const yearAdapter = {
|
|
18345
|
-
normalizeFunctionValue(value) {
|
|
18346
|
-
return toNumber(value, DEFAULT_LOCALE);
|
|
18347
|
-
},
|
|
18348
|
-
getFormat() {
|
|
18349
|
-
return "0";
|
|
18350
|
-
},
|
|
18351
|
-
formatValue(normalizedValue, locale) {
|
|
18352
|
-
locale = locale ?? DEFAULT_LOCALE;
|
|
18353
|
-
return formatValue(normalizedValue, { locale, format: "0" });
|
|
18354
|
-
},
|
|
18355
|
-
toCellValue(normalizedValue) {
|
|
18356
|
-
return toNumber(normalizedValue, DEFAULT_LOCALE);
|
|
18357
|
-
},
|
|
18358
|
-
};
|
|
18359
|
-
pivotTimeAdapterRegistry
|
|
18360
|
-
.add("day", dayAdapter)
|
|
18361
|
-
.add("week", weekAdapter)
|
|
18362
|
-
.add("month", monthAdapter)
|
|
18363
|
-
.add("quarter", quarterAdapter)
|
|
18364
|
-
.add("year", yearAdapter)
|
|
18365
|
-
.add("day_of_month", dayOfMonthAdapter)
|
|
18366
|
-
.add("iso_week_number", isoWeekNumberAdapter)
|
|
18367
|
-
.add("month_number", monthNumberAdapter)
|
|
18368
|
-
.add("quarter_number", quarterNumberAdapter)
|
|
18369
|
-
.add("year_number", yearAdapter);
|
|
18370
|
-
|
|
18371
|
-
const AGGREGATOR_NAMES = {
|
|
18372
|
-
count: _t("Count"),
|
|
18373
|
-
count_distinct: _t("Count Distinct"),
|
|
18374
|
-
bool_and: _t("Boolean And"),
|
|
18375
|
-
bool_or: _t("Boolean Or"),
|
|
18376
|
-
max: _t("Maximum"),
|
|
18377
|
-
min: _t("Minimum"),
|
|
18378
|
-
avg: _t("Average"),
|
|
18379
|
-
sum: _t("Sum"),
|
|
18380
|
-
};
|
|
18381
|
-
const NUMBER_CHAR_AGGREGATORS = ["max", "min", "avg", "sum", "count_distinct", "count"];
|
|
18382
|
-
const AGGREGATORS_BY_FIELD_TYPE = {
|
|
18383
|
-
integer: NUMBER_CHAR_AGGREGATORS,
|
|
18384
|
-
char: NUMBER_CHAR_AGGREGATORS,
|
|
18385
|
-
boolean: ["count_distinct", "count", "bool_and", "bool_or"],
|
|
18386
|
-
};
|
|
18387
|
-
const AGGREGATORS = {};
|
|
18388
|
-
for (const type in AGGREGATORS_BY_FIELD_TYPE) {
|
|
18389
|
-
AGGREGATORS[type] = {};
|
|
18390
|
-
for (const aggregator of AGGREGATORS_BY_FIELD_TYPE[type]) {
|
|
18391
|
-
AGGREGATORS[type][aggregator] = AGGREGATOR_NAMES[aggregator];
|
|
18392
|
-
}
|
|
18393
|
-
}
|
|
18394
|
-
const AGGREGATORS_FN = {
|
|
18395
|
-
count: {
|
|
18396
|
-
fn: (args) => countAny([args]),
|
|
18397
|
-
format: () => "0",
|
|
18398
|
-
},
|
|
18399
|
-
count_distinct: {
|
|
18400
|
-
fn: (args) => countUnique([args]),
|
|
18401
|
-
format: () => "0",
|
|
18402
|
-
},
|
|
18403
|
-
bool_and: {
|
|
18404
|
-
fn: (args) => boolAnd([args]).result,
|
|
18405
|
-
format: () => undefined,
|
|
18406
|
-
},
|
|
18407
|
-
bool_or: {
|
|
18408
|
-
fn: (args) => boolOr([args]).result,
|
|
18409
|
-
format: () => undefined,
|
|
18410
|
-
},
|
|
18411
|
-
max: {
|
|
18412
|
-
fn: (args, locale) => max([args], locale),
|
|
18413
|
-
format: inferFormat,
|
|
18414
|
-
},
|
|
18415
|
-
min: {
|
|
18416
|
-
fn: (args, locale) => min([args], locale),
|
|
18417
|
-
format: inferFormat,
|
|
18418
|
-
},
|
|
18419
|
-
avg: {
|
|
18420
|
-
fn: (args, locale) => average([args], locale),
|
|
18421
|
-
format: inferFormat,
|
|
18422
|
-
},
|
|
18423
|
-
sum: {
|
|
18424
|
-
fn: (args, locale) => sum([args], locale),
|
|
18425
|
-
format: inferFormat,
|
|
18426
|
-
},
|
|
18427
|
-
};
|
|
18428
|
-
/**
|
|
18429
|
-
* Build a pivot formula expression
|
|
18430
|
-
*/
|
|
18431
|
-
function makePivotFormula(formula, args) {
|
|
18432
|
-
return `=${formula}(${args
|
|
18433
|
-
.map((arg) => {
|
|
18434
|
-
const stringIsNumber = typeof arg == "string" && !isNaN(Number(arg)) && Number(arg).toString() === arg;
|
|
18435
|
-
const convertToNumber = typeof arg == "number" || stringIsNumber;
|
|
18436
|
-
return convertToNumber ? `${arg}` : `"${arg.toString().replace(/"/g, '\\"')}"`;
|
|
18437
|
-
})
|
|
18438
|
-
.join(",")})`;
|
|
18439
|
-
}
|
|
18440
|
-
/**
|
|
18441
|
-
* Given an object of form {"1": {...}, "2": {...}, ...} get the maximum ID used
|
|
18442
|
-
* in this object
|
|
18443
|
-
* If the object has no keys, return 0
|
|
18444
|
-
*
|
|
18445
|
-
*/
|
|
18446
|
-
function getMaxObjectId(o) {
|
|
18447
|
-
const keys = Object.keys(o);
|
|
18448
|
-
if (!keys.length) {
|
|
18449
|
-
return 0;
|
|
18450
|
-
}
|
|
18451
|
-
const nums = keys.map((id) => parseInt(id, 10));
|
|
18452
|
-
const max = Math.max(...nums);
|
|
18453
|
-
return max;
|
|
18454
|
-
}
|
|
18455
|
-
const ALL_PERIODS = {
|
|
18456
|
-
year: _t("Year"),
|
|
18457
|
-
quarter: _t("Quarter"),
|
|
18458
|
-
month: _t("Month"),
|
|
18459
|
-
week: _t("Week"),
|
|
18460
|
-
day: _t("Day"),
|
|
18461
|
-
year_number: _t("Year"),
|
|
18462
|
-
quarter_number: _t("Quarter"),
|
|
18463
|
-
month_number: _t("Month"),
|
|
18464
|
-
iso_week_number: _t("Week"),
|
|
18465
|
-
day_of_month: _t("Day of Month"),
|
|
18466
|
-
};
|
|
18467
|
-
const DATE_FIELDS = ["date", "datetime"];
|
|
18468
|
-
/**
|
|
18469
|
-
* Parse a dimension string into a pivot dimension definition.
|
|
18470
|
-
* e.g "create_date:month" => { name: "create_date", granularity: "month" }
|
|
18471
|
-
*/
|
|
18472
|
-
function parseDimension(dimension) {
|
|
18473
|
-
const [name, granularity] = dimension.split(":");
|
|
18474
|
-
if (granularity) {
|
|
18475
|
-
return { name, granularity };
|
|
18476
|
-
}
|
|
18477
|
-
return { name };
|
|
18478
|
-
}
|
|
18479
|
-
function isDateField(field) {
|
|
18480
|
-
return DATE_FIELDS.includes(field.type);
|
|
18481
|
-
}
|
|
18482
|
-
function toPivotDomain(domainStr) {
|
|
18483
|
-
if (domainStr.length % 2 !== 0) {
|
|
18484
|
-
throw new Error("Invalid domain: odd number of elements");
|
|
18485
|
-
}
|
|
18486
|
-
const domain = [];
|
|
18487
|
-
for (let i = 0; i < domainStr.length - 1; i += 2) {
|
|
18488
|
-
domain.push({ field: domainStr[i], value: domainStr[i + 1] });
|
|
18489
|
-
}
|
|
18490
|
-
return domain;
|
|
18491
|
-
}
|
|
18492
|
-
function flatPivotDomain(domain) {
|
|
18493
|
-
return domain.flatMap((arg) => [arg.field, arg.value]);
|
|
18494
|
-
}
|
|
18495
|
-
/**
|
|
18496
|
-
* Parses the value defining a pivot group in a PIVOT formula
|
|
18497
|
-
* e.g. given the following formula PIVOT.VALUE("1", "stage_id", "42", "status", "won"),
|
|
18498
|
-
* the two group values are "42" and "won".
|
|
18499
|
-
*/
|
|
18500
|
-
function toNormalizedPivotValue(dimension, groupValue) {
|
|
18501
|
-
if (groupValue === null || groupValue === "null") {
|
|
18502
|
-
return null;
|
|
18503
|
-
}
|
|
18504
|
-
const groupValueString = typeof groupValue === "boolean"
|
|
18505
|
-
? toString(groupValue).toLocaleLowerCase()
|
|
18506
|
-
: toString(groupValue);
|
|
18507
|
-
if (!pivotNormalizationValueRegistry.contains(dimension.type)) {
|
|
18508
|
-
throw new EvaluationError(_t("Field %(field)s is not supported because of its type (%(type)s)", {
|
|
18509
|
-
field: dimension.displayName,
|
|
18510
|
-
type: dimension.type,
|
|
18511
|
-
}));
|
|
18512
|
-
}
|
|
18513
|
-
// represents a field which is not set (=False server side)
|
|
18514
|
-
if (groupValueString === "false") {
|
|
18515
|
-
return false;
|
|
18516
|
-
}
|
|
18517
|
-
const normalizer = pivotNormalizationValueRegistry.get(dimension.type);
|
|
18518
|
-
return normalizer(groupValueString, dimension.granularity);
|
|
18519
|
-
}
|
|
18520
|
-
function normalizeDateTime(value, granularity) {
|
|
18521
|
-
if (!granularity) {
|
|
18522
|
-
throw "";
|
|
18523
|
-
}
|
|
18524
|
-
return pivotTimeAdapter(granularity).normalizeFunctionValue(value);
|
|
18525
|
-
}
|
|
18526
|
-
const pivotNormalizationValueRegistry = new Registry();
|
|
18527
|
-
pivotNormalizationValueRegistry
|
|
18528
|
-
.add("date", normalizeDateTime)
|
|
18529
|
-
.add("datetime", normalizeDateTime)
|
|
18530
|
-
.add("integer", (value) => toNumber(value, DEFAULT_LOCALE))
|
|
18531
|
-
.add("boolean", (value) => toBoolean(value))
|
|
18532
|
-
.add("char", (value) => toString(value));
|
|
18533
|
-
|
|
18534
18620
|
/**
|
|
18535
18621
|
* Get the pivot ID from the formula pivot ID.
|
|
18536
18622
|
*/
|
|
@@ -19107,14 +19193,10 @@ const PIVOT = {
|
|
|
19107
19193
|
result[col].push({ value: "" });
|
|
19108
19194
|
break;
|
|
19109
19195
|
case "HEADER":
|
|
19110
|
-
|
|
19111
|
-
|
|
19112
|
-
|
|
19113
|
-
|
|
19114
|
-
}
|
|
19115
|
-
else {
|
|
19116
|
-
result[col].push(pivot.getPivotHeaderValueAndFormat(domain));
|
|
19117
|
-
}
|
|
19196
|
+
result[col].push(pivot.getPivotHeaderValueAndFormat(pivotCell.domain));
|
|
19197
|
+
break;
|
|
19198
|
+
case "MEASURE_HEADER":
|
|
19199
|
+
result[col].push(pivot.getPivotMeasureValue(pivotCell.measure, pivotCell.domain));
|
|
19118
19200
|
break;
|
|
19119
19201
|
case "VALUE":
|
|
19120
19202
|
result[col].push(pivot.getPivotCellValueAndFormat(pivotCell.measure, pivotCell.domain));
|
|
@@ -20153,6 +20235,8 @@ const MODIFIER_KEYS = ["Shift", "Control", "Alt", "Meta"];
|
|
|
20153
20235
|
* a child element.
|
|
20154
20236
|
*/
|
|
20155
20237
|
function isChildEvent(parent, ev) {
|
|
20238
|
+
if (!parent)
|
|
20239
|
+
return false;
|
|
20156
20240
|
return !!ev.target && parent.contains(ev.target);
|
|
20157
20241
|
}
|
|
20158
20242
|
function gridOverlayPosition() {
|
|
@@ -22692,7 +22776,8 @@ function truncateLabel(label) {
|
|
|
22692
22776
|
/**
|
|
22693
22777
|
* Get a default chart js configuration
|
|
22694
22778
|
*/
|
|
22695
|
-
function getDefaultChartJsRuntime(chart, labels, fontColor,
|
|
22779
|
+
function getDefaultChartJsRuntime(chart, labels, fontColor, args) {
|
|
22780
|
+
const { format, locale, truncateLabels, horizontalChart } = args;
|
|
22696
22781
|
const chartTitle = chart.title.text ? chart.title : { ...chart.title, content: "" };
|
|
22697
22782
|
const options = {
|
|
22698
22783
|
// https://www.chartjs.org/docs/latest/general/responsive.html
|
|
@@ -22736,8 +22821,11 @@ function getDefaultChartJsRuntime(chart, labels, fontColor, { format, locale, tr
|
|
|
22736
22821
|
callbacks: {
|
|
22737
22822
|
label: function (tooltipItem) {
|
|
22738
22823
|
const xLabel = tooltipItem.dataset?.label || tooltipItem.label;
|
|
22739
|
-
// tooltipItem.parsed
|
|
22740
|
-
|
|
22824
|
+
// tooltipItem.parsed can be an object or a number for pie charts
|
|
22825
|
+
let yLabel = horizontalChart ? tooltipItem.parsed.x : tooltipItem.parsed.y;
|
|
22826
|
+
if (!yLabel) {
|
|
22827
|
+
yLabel = tooltipItem.parsed;
|
|
22828
|
+
}
|
|
22741
22829
|
const toolTipFormat = !format && Math.abs(yLabel) >= 1000 ? "#,##" : format;
|
|
22742
22830
|
const yLabelStr = formatValue(yLabel, { format: toolTipFormat, locale });
|
|
22743
22831
|
return xLabel ? `${xLabel}: ${yLabelStr}` : yLabelStr;
|
|
@@ -22911,6 +22999,7 @@ class BarChart extends AbstractChart {
|
|
|
22911
22999
|
dataSetsHaveTitle;
|
|
22912
23000
|
dataSetDesign;
|
|
22913
23001
|
axesDesign;
|
|
23002
|
+
horizontal;
|
|
22914
23003
|
constructor(definition, sheetId, getters) {
|
|
22915
23004
|
super(definition, sheetId, getters);
|
|
22916
23005
|
this.dataSets = createDataSets(getters, definition.dataSets, sheetId, definition.dataSetsHaveTitle);
|
|
@@ -22922,6 +23011,7 @@ class BarChart extends AbstractChart {
|
|
|
22922
23011
|
this.dataSetsHaveTitle = definition.dataSetsHaveTitle;
|
|
22923
23012
|
this.dataSetDesign = definition.dataSets;
|
|
22924
23013
|
this.axesDesign = definition.axesDesign;
|
|
23014
|
+
this.horizontal = definition.horizontal;
|
|
22925
23015
|
}
|
|
22926
23016
|
static transformDefinition(definition, executed) {
|
|
22927
23017
|
return transformChartDefinitionWithDataSetsWithZone(definition, executed);
|
|
@@ -22993,6 +23083,7 @@ class BarChart extends AbstractChart {
|
|
|
22993
23083
|
stacked: this.stacked,
|
|
22994
23084
|
aggregated: this.aggregated,
|
|
22995
23085
|
axesDesign: this.axesDesign,
|
|
23086
|
+
horizontal: this.horizontal,
|
|
22996
23087
|
};
|
|
22997
23088
|
}
|
|
22998
23089
|
getDefinitionForExcel() {
|
|
@@ -23024,7 +23115,10 @@ class BarChart extends AbstractChart {
|
|
|
23024
23115
|
}
|
|
23025
23116
|
function getBarConfiguration(chart, labels, localeFormat) {
|
|
23026
23117
|
const fontColor = chartFontColor(chart.background);
|
|
23027
|
-
const config = getDefaultChartJsRuntime(chart, labels, fontColor,
|
|
23118
|
+
const config = getDefaultChartJsRuntime(chart, labels, fontColor, {
|
|
23119
|
+
...localeFormat,
|
|
23120
|
+
horizontalChart: chart.horizontal,
|
|
23121
|
+
});
|
|
23028
23122
|
const legend = {
|
|
23029
23123
|
labels: { color: fontColor },
|
|
23030
23124
|
};
|
|
@@ -23038,16 +23132,10 @@ function getBarConfiguration(chart, labels, localeFormat) {
|
|
|
23038
23132
|
config.options.layout = {
|
|
23039
23133
|
padding: { left: 20, right: 20, top: chart.title ? 10 : 25, bottom: 10 },
|
|
23040
23134
|
};
|
|
23041
|
-
config.options.
|
|
23042
|
-
|
|
23043
|
-
|
|
23044
|
-
|
|
23045
|
-
color: fontColor,
|
|
23046
|
-
},
|
|
23047
|
-
title: getChartAxisTitleRuntime(chart.axesDesign?.x),
|
|
23048
|
-
},
|
|
23049
|
-
};
|
|
23050
|
-
const yAxis = {
|
|
23135
|
+
config.options.indexAxis = chart.horizontal ? "y" : "x";
|
|
23136
|
+
config.options.scales = {};
|
|
23137
|
+
const labelsAxis = { ticks: { padding: 5, color: fontColor } };
|
|
23138
|
+
const valuesAxis = {
|
|
23051
23139
|
beginAtZero: true, // the origin of the y axis is always zero
|
|
23052
23140
|
ticks: {
|
|
23053
23141
|
color: fontColor,
|
|
@@ -23063,7 +23151,10 @@ function getBarConfiguration(chart, labels, localeFormat) {
|
|
|
23063
23151
|
},
|
|
23064
23152
|
},
|
|
23065
23153
|
};
|
|
23154
|
+
const xAxis = chart.horizontal ? valuesAxis : labelsAxis;
|
|
23155
|
+
const yAxis = chart.horizontal ? labelsAxis : valuesAxis;
|
|
23066
23156
|
const { useLeftAxis, useRightAxis } = getDefinedAxis(chart.getDefinition());
|
|
23157
|
+
config.options.scales.x = { ...xAxis, title: getChartAxisTitleRuntime(chart.axesDesign?.x) };
|
|
23067
23158
|
if (useLeftAxis) {
|
|
23068
23159
|
config.options.scales.y = {
|
|
23069
23160
|
...yAxis,
|
|
@@ -23130,7 +23221,7 @@ function createBarChartRuntime(chart, getters) {
|
|
|
23130
23221
|
const label = definition.dataSets[index].label;
|
|
23131
23222
|
dataset.label = label;
|
|
23132
23223
|
}
|
|
23133
|
-
if (definition.dataSets?.[index]?.yAxisId) {
|
|
23224
|
+
if (definition.dataSets?.[index]?.yAxisId && !chart.horizontal) {
|
|
23134
23225
|
dataset["yAxisID"] = definition.dataSets[index].yAxisId;
|
|
23135
23226
|
}
|
|
23136
23227
|
}
|
|
@@ -24121,6 +24212,7 @@ class PieChart extends AbstractChart {
|
|
|
24121
24212
|
type = "pie";
|
|
24122
24213
|
aggregated;
|
|
24123
24214
|
dataSetsHaveTitle;
|
|
24215
|
+
isDoughnut;
|
|
24124
24216
|
constructor(definition, sheetId, getters) {
|
|
24125
24217
|
super(definition, sheetId, getters);
|
|
24126
24218
|
this.dataSets = createDataSets(getters, definition.dataSets, sheetId, definition.dataSetsHaveTitle);
|
|
@@ -24129,6 +24221,7 @@ class PieChart extends AbstractChart {
|
|
|
24129
24221
|
this.legendPosition = definition.legendPosition;
|
|
24130
24222
|
this.aggregated = definition.aggregated;
|
|
24131
24223
|
this.dataSetsHaveTitle = definition.dataSetsHaveTitle;
|
|
24224
|
+
this.isDoughnut = definition.isDoughnut;
|
|
24132
24225
|
}
|
|
24133
24226
|
static transformDefinition(definition, executed) {
|
|
24134
24227
|
return transformChartDefinitionWithDataSetsWithZone(definition, executed);
|
|
@@ -24146,6 +24239,7 @@ class PieChart extends AbstractChart {
|
|
|
24146
24239
|
type: "pie",
|
|
24147
24240
|
labelRange: context.auxiliaryRange || undefined,
|
|
24148
24241
|
aggregated: context.aggregated ?? false,
|
|
24242
|
+
isDoughnut: false,
|
|
24149
24243
|
};
|
|
24150
24244
|
}
|
|
24151
24245
|
getDefinition() {
|
|
@@ -24176,6 +24270,7 @@ class PieChart extends AbstractChart {
|
|
|
24176
24270
|
: undefined,
|
|
24177
24271
|
title: this.title,
|
|
24178
24272
|
aggregated: this.aggregated,
|
|
24273
|
+
isDoughnut: this.isDoughnut,
|
|
24179
24274
|
};
|
|
24180
24275
|
}
|
|
24181
24276
|
copyForSheetId(sheetId) {
|
|
@@ -24310,6 +24405,141 @@ function createPieChartRuntime(chart, getters) {
|
|
|
24310
24405
|
};
|
|
24311
24406
|
config.data.datasets.push(dataset);
|
|
24312
24407
|
}
|
|
24408
|
+
if (chart.isDoughnut) {
|
|
24409
|
+
config.type = "doughnut";
|
|
24410
|
+
}
|
|
24411
|
+
return { chartJsConfig: config, background: chart.background || BACKGROUND_CHART_COLOR };
|
|
24412
|
+
}
|
|
24413
|
+
|
|
24414
|
+
class PyramidChart extends AbstractChart {
|
|
24415
|
+
dataSets;
|
|
24416
|
+
labelRange;
|
|
24417
|
+
background;
|
|
24418
|
+
legendPosition;
|
|
24419
|
+
aggregated;
|
|
24420
|
+
type = "pyramid";
|
|
24421
|
+
dataSetsHaveTitle;
|
|
24422
|
+
dataSetDesign;
|
|
24423
|
+
axesDesign;
|
|
24424
|
+
horizontal = true;
|
|
24425
|
+
stacked = true;
|
|
24426
|
+
constructor(definition, sheetId, getters) {
|
|
24427
|
+
super(definition, sheetId, getters);
|
|
24428
|
+
this.dataSets = createDataSets(getters, definition.dataSets, sheetId, definition.dataSetsHaveTitle).slice(0, 2);
|
|
24429
|
+
this.labelRange = createValidRange(getters, sheetId, definition.labelRange);
|
|
24430
|
+
this.background = definition.background;
|
|
24431
|
+
this.legendPosition = definition.legendPosition;
|
|
24432
|
+
this.aggregated = definition.aggregated;
|
|
24433
|
+
this.dataSetsHaveTitle = definition.dataSetsHaveTitle;
|
|
24434
|
+
this.dataSetDesign = definition.dataSets;
|
|
24435
|
+
this.axesDesign = definition.axesDesign;
|
|
24436
|
+
}
|
|
24437
|
+
static transformDefinition(definition, executed) {
|
|
24438
|
+
return transformChartDefinitionWithDataSetsWithZone(definition, executed);
|
|
24439
|
+
}
|
|
24440
|
+
static validateChartDefinition(validator, definition) {
|
|
24441
|
+
return validator.checkValidations(definition, checkDataset, checkLabelRange);
|
|
24442
|
+
}
|
|
24443
|
+
static getDefinitionFromContextCreation(context) {
|
|
24444
|
+
return {
|
|
24445
|
+
background: context.background,
|
|
24446
|
+
dataSets: context.range ?? [],
|
|
24447
|
+
dataSetsHaveTitle: context.dataSetsHaveTitle ?? false,
|
|
24448
|
+
aggregated: context.aggregated ?? false,
|
|
24449
|
+
legendPosition: context.legendPosition ?? "top",
|
|
24450
|
+
title: context.title || { text: "" },
|
|
24451
|
+
type: "pyramid",
|
|
24452
|
+
labelRange: context.auxiliaryRange || undefined,
|
|
24453
|
+
axesDesign: context.axesDesign,
|
|
24454
|
+
horizontal: true,
|
|
24455
|
+
stacked: true,
|
|
24456
|
+
};
|
|
24457
|
+
}
|
|
24458
|
+
getContextCreation() {
|
|
24459
|
+
const range = [];
|
|
24460
|
+
for (const [i, dataSet] of this.dataSets.entries()) {
|
|
24461
|
+
range.push({
|
|
24462
|
+
...this.dataSetDesign?.[i],
|
|
24463
|
+
dataRange: this.getters.getRangeString(dataSet.dataRange, this.sheetId),
|
|
24464
|
+
});
|
|
24465
|
+
}
|
|
24466
|
+
return {
|
|
24467
|
+
...this,
|
|
24468
|
+
range,
|
|
24469
|
+
auxiliaryRange: this.labelRange
|
|
24470
|
+
? this.getters.getRangeString(this.labelRange, this.sheetId)
|
|
24471
|
+
: undefined,
|
|
24472
|
+
};
|
|
24473
|
+
}
|
|
24474
|
+
copyForSheetId(sheetId) {
|
|
24475
|
+
const dataSets = copyDataSetsWithNewSheetId(this.sheetId, sheetId, this.dataSets);
|
|
24476
|
+
const labelRange = copyLabelRangeWithNewSheetId(this.sheetId, sheetId, this.labelRange);
|
|
24477
|
+
const definition = this.getDefinitionWithSpecificDataSets(dataSets, labelRange, sheetId);
|
|
24478
|
+
return new PyramidChart(definition, sheetId, this.getters);
|
|
24479
|
+
}
|
|
24480
|
+
copyInSheetId(sheetId) {
|
|
24481
|
+
const definition = this.getDefinitionWithSpecificDataSets(this.dataSets, this.labelRange, sheetId);
|
|
24482
|
+
return new PyramidChart(definition, sheetId, this.getters);
|
|
24483
|
+
}
|
|
24484
|
+
getDefinition() {
|
|
24485
|
+
return this.getDefinitionWithSpecificDataSets(this.dataSets, this.labelRange);
|
|
24486
|
+
}
|
|
24487
|
+
getDefinitionWithSpecificDataSets(dataSets, labelRange, targetSheetId) {
|
|
24488
|
+
const ranges = [];
|
|
24489
|
+
for (const [i, dataSet] of dataSets.entries()) {
|
|
24490
|
+
ranges.push({
|
|
24491
|
+
...this.dataSetDesign?.[i],
|
|
24492
|
+
dataRange: this.getters.getRangeString(dataSet.dataRange, targetSheetId || this.sheetId),
|
|
24493
|
+
});
|
|
24494
|
+
}
|
|
24495
|
+
return {
|
|
24496
|
+
type: "pyramid",
|
|
24497
|
+
dataSetsHaveTitle: dataSets.length ? Boolean(dataSets[0].labelCell) : false,
|
|
24498
|
+
background: this.background,
|
|
24499
|
+
dataSets: ranges,
|
|
24500
|
+
legendPosition: this.legendPosition,
|
|
24501
|
+
labelRange: labelRange
|
|
24502
|
+
? this.getters.getRangeString(labelRange, targetSheetId || this.sheetId)
|
|
24503
|
+
: undefined,
|
|
24504
|
+
title: this.title,
|
|
24505
|
+
aggregated: this.aggregated,
|
|
24506
|
+
axesDesign: this.axesDesign,
|
|
24507
|
+
horizontal: true,
|
|
24508
|
+
stacked: true,
|
|
24509
|
+
};
|
|
24510
|
+
}
|
|
24511
|
+
getDefinitionForExcel() {
|
|
24512
|
+
return undefined;
|
|
24513
|
+
}
|
|
24514
|
+
updateRanges(applyChange) {
|
|
24515
|
+
const { dataSets, labelRange, isStale } = updateChartRangesWithDataSets(this.getters, applyChange, this.dataSets, this.labelRange);
|
|
24516
|
+
if (!isStale) {
|
|
24517
|
+
return this;
|
|
24518
|
+
}
|
|
24519
|
+
const definition = this.getDefinitionWithSpecificDataSets(dataSets, labelRange);
|
|
24520
|
+
return new PyramidChart(definition, this.sheetId, this.getters);
|
|
24521
|
+
}
|
|
24522
|
+
}
|
|
24523
|
+
function createPyramidChartRuntime(chart, getters) {
|
|
24524
|
+
const barDef = { ...chart.getDefinition(), type: "bar" };
|
|
24525
|
+
const barChart = new BarChart(barDef, chart.sheetId, getters);
|
|
24526
|
+
const barRuntime = createBarChartRuntime(barChart, getters);
|
|
24527
|
+
const config = barRuntime.chartJsConfig;
|
|
24528
|
+
let datasets = config.data?.datasets;
|
|
24529
|
+
if (datasets && datasets[0]) {
|
|
24530
|
+
datasets[0].data = datasets[0].data.map((value) => (value > 0 ? value : 0));
|
|
24531
|
+
}
|
|
24532
|
+
if (datasets && datasets[1]) {
|
|
24533
|
+
datasets[1].data = datasets[1].data.map((value) => (value > 0 ? -value : 0));
|
|
24534
|
+
}
|
|
24535
|
+
const scales = config.options.scales;
|
|
24536
|
+
const scalesXCallback = scales.x.ticks.callback;
|
|
24537
|
+
scales.x.ticks.callback = (value) => scalesXCallback(Math.abs(value));
|
|
24538
|
+
const tooltipLabelCallback = config.options.plugins.tooltip.callbacks.label;
|
|
24539
|
+
config.options.plugins.tooltip.callbacks.label = (item) => {
|
|
24540
|
+
const tooltipItem = { ...item, parsed: { y: item.parsed.y, x: Math.abs(item.parsed.x) } };
|
|
24541
|
+
return tooltipLabelCallback(tooltipItem);
|
|
24542
|
+
};
|
|
24313
24543
|
return { chartJsConfig: config, background: chart.background || BACKGROUND_CHART_COLOR };
|
|
24314
24544
|
}
|
|
24315
24545
|
|
|
@@ -24752,7 +24982,6 @@ chartRegistry.add("bar", {
|
|
|
24752
24982
|
validateChartDefinition: BarChart.validateChartDefinition,
|
|
24753
24983
|
transformDefinition: BarChart.transformDefinition,
|
|
24754
24984
|
getChartDefinitionFromContextCreation: BarChart.getDefinitionFromContextCreation,
|
|
24755
|
-
name: _t("Bar"),
|
|
24756
24985
|
sequence: 10,
|
|
24757
24986
|
});
|
|
24758
24987
|
chartRegistry.add("combo", {
|
|
@@ -24762,7 +24991,6 @@ chartRegistry.add("combo", {
|
|
|
24762
24991
|
validateChartDefinition: ComboChart.validateChartDefinition,
|
|
24763
24992
|
transformDefinition: ComboChart.transformDefinition,
|
|
24764
24993
|
getChartDefinitionFromContextCreation: ComboChart.getDefinitionFromContextCreation,
|
|
24765
|
-
name: _t("Combo"),
|
|
24766
24994
|
sequence: 15,
|
|
24767
24995
|
});
|
|
24768
24996
|
chartRegistry.add("line", {
|
|
@@ -24772,7 +25000,6 @@ chartRegistry.add("line", {
|
|
|
24772
25000
|
validateChartDefinition: LineChart.validateChartDefinition,
|
|
24773
25001
|
transformDefinition: LineChart.transformDefinition,
|
|
24774
25002
|
getChartDefinitionFromContextCreation: LineChart.getDefinitionFromContextCreation,
|
|
24775
|
-
name: _t("Line"),
|
|
24776
25003
|
sequence: 20,
|
|
24777
25004
|
});
|
|
24778
25005
|
chartRegistry.add("pie", {
|
|
@@ -24782,7 +25009,6 @@ chartRegistry.add("pie", {
|
|
|
24782
25009
|
validateChartDefinition: PieChart.validateChartDefinition,
|
|
24783
25010
|
transformDefinition: PieChart.transformDefinition,
|
|
24784
25011
|
getChartDefinitionFromContextCreation: PieChart.getDefinitionFromContextCreation,
|
|
24785
|
-
name: _t("Pie"),
|
|
24786
25012
|
sequence: 30,
|
|
24787
25013
|
});
|
|
24788
25014
|
chartRegistry.add("scorecard", {
|
|
@@ -24792,7 +25018,6 @@ chartRegistry.add("scorecard", {
|
|
|
24792
25018
|
validateChartDefinition: ScorecardChart$1.validateChartDefinition,
|
|
24793
25019
|
transformDefinition: ScorecardChart$1.transformDefinition,
|
|
24794
25020
|
getChartDefinitionFromContextCreation: ScorecardChart$1.getDefinitionFromContextCreation,
|
|
24795
|
-
name: _t("Scorecard"),
|
|
24796
25021
|
sequence: 40,
|
|
24797
25022
|
});
|
|
24798
25023
|
chartRegistry.add("gauge", {
|
|
@@ -24802,7 +25027,6 @@ chartRegistry.add("gauge", {
|
|
|
24802
25027
|
validateChartDefinition: GaugeChart.validateChartDefinition,
|
|
24803
25028
|
transformDefinition: GaugeChart.transformDefinition,
|
|
24804
25029
|
getChartDefinitionFromContextCreation: GaugeChart.getDefinitionFromContextCreation,
|
|
24805
|
-
name: _t("Gauge"),
|
|
24806
25030
|
sequence: 50,
|
|
24807
25031
|
});
|
|
24808
25032
|
chartRegistry.add("scatter", {
|
|
@@ -24812,7 +25036,6 @@ chartRegistry.add("scatter", {
|
|
|
24812
25036
|
validateChartDefinition: ScatterChart.validateChartDefinition,
|
|
24813
25037
|
transformDefinition: ScatterChart.transformDefinition,
|
|
24814
25038
|
getChartDefinitionFromContextCreation: ScatterChart.getDefinitionFromContextCreation,
|
|
24815
|
-
name: _t("Scatter"),
|
|
24816
25039
|
sequence: 60,
|
|
24817
25040
|
});
|
|
24818
25041
|
chartRegistry.add("waterfall", {
|
|
@@ -24822,9 +25045,17 @@ chartRegistry.add("waterfall", {
|
|
|
24822
25045
|
validateChartDefinition: WaterfallChart.validateChartDefinition,
|
|
24823
25046
|
transformDefinition: WaterfallChart.transformDefinition,
|
|
24824
25047
|
getChartDefinitionFromContextCreation: WaterfallChart.getDefinitionFromContextCreation,
|
|
24825
|
-
name: _t("Waterfall"),
|
|
24826
25048
|
sequence: 70,
|
|
24827
25049
|
});
|
|
25050
|
+
chartRegistry.add("pyramid", {
|
|
25051
|
+
match: (type) => type === "pyramid",
|
|
25052
|
+
createChart: (definition, sheetId, getters) => new PyramidChart(definition, sheetId, getters),
|
|
25053
|
+
getChartRuntime: createPyramidChartRuntime,
|
|
25054
|
+
validateChartDefinition: PyramidChart.validateChartDefinition,
|
|
25055
|
+
transformDefinition: PyramidChart.transformDefinition,
|
|
25056
|
+
getChartDefinitionFromContextCreation: PyramidChart.getDefinitionFromContextCreation,
|
|
25057
|
+
sequence: 80,
|
|
25058
|
+
});
|
|
24828
25059
|
const chartComponentRegistry = new Registry();
|
|
24829
25060
|
chartComponentRegistry.add("line", ChartJsComponent);
|
|
24830
25061
|
chartComponentRegistry.add("bar", ChartJsComponent);
|
|
@@ -24834,6 +25065,130 @@ chartComponentRegistry.add("gauge", GaugeChartComponent);
|
|
|
24834
25065
|
chartComponentRegistry.add("scatter", ChartJsComponent);
|
|
24835
25066
|
chartComponentRegistry.add("scorecard", ScorecardChart);
|
|
24836
25067
|
chartComponentRegistry.add("waterfall", ChartJsComponent);
|
|
25068
|
+
chartComponentRegistry.add("pyramid", ChartJsComponent);
|
|
25069
|
+
const chartCategories = {
|
|
25070
|
+
line: _t("Line"),
|
|
25071
|
+
column: _t("Column"),
|
|
25072
|
+
bar: _t("Bar"),
|
|
25073
|
+
pie: _t("Pie"),
|
|
25074
|
+
misc: _t("Miscellaneous"),
|
|
25075
|
+
};
|
|
25076
|
+
const chartSubtypeRegistry = new Registry();
|
|
25077
|
+
chartSubtypeRegistry
|
|
25078
|
+
.add("line", {
|
|
25079
|
+
matcher: (definition) => definition.type === "line" && !definition.stacked,
|
|
25080
|
+
displayName: _t("Line"),
|
|
25081
|
+
chartType: "line",
|
|
25082
|
+
chartSubtype: "line",
|
|
25083
|
+
subtypeDefinition: { stacked: false },
|
|
25084
|
+
category: "line",
|
|
25085
|
+
preview: "o-spreadsheet-ChartPreview.LINE_CHART",
|
|
25086
|
+
})
|
|
25087
|
+
.add("stacked_line", {
|
|
25088
|
+
matcher: (definition) => definition.type === "line" && definition.stacked,
|
|
25089
|
+
displayName: _t("Stacked Line"),
|
|
25090
|
+
chartType: "line",
|
|
25091
|
+
chartSubtype: "stacked_line",
|
|
25092
|
+
subtypeDefinition: { stacked: true },
|
|
25093
|
+
category: "line",
|
|
25094
|
+
preview: "o-spreadsheet-ChartPreview.STACKED_AREA_CHART",
|
|
25095
|
+
})
|
|
25096
|
+
.add("scatter", {
|
|
25097
|
+
displayName: _t("Scatter"),
|
|
25098
|
+
chartType: "scatter",
|
|
25099
|
+
chartSubtype: "scatter",
|
|
25100
|
+
category: "misc",
|
|
25101
|
+
preview: "o-spreadsheet-ChartPreview.SCATTER_CHART",
|
|
25102
|
+
})
|
|
25103
|
+
.add("column", {
|
|
25104
|
+
matcher: (definition) => definition.type === "bar" && !definition.stacked && !definition.horizontal,
|
|
25105
|
+
displayName: _t("Column"),
|
|
25106
|
+
chartType: "bar",
|
|
25107
|
+
chartSubtype: "column",
|
|
25108
|
+
subtypeDefinition: { stacked: false, horizontal: false },
|
|
25109
|
+
category: "column",
|
|
25110
|
+
preview: "o-spreadsheet-ChartPreview.COLUMN_CHART",
|
|
25111
|
+
})
|
|
25112
|
+
.add("stacked_column", {
|
|
25113
|
+
matcher: (definition) => definition.type === "bar" && definition.stacked && !definition.horizontal,
|
|
25114
|
+
displayName: _t("Stacked Column"),
|
|
25115
|
+
chartType: "bar",
|
|
25116
|
+
chartSubtype: "stacked_column",
|
|
25117
|
+
subtypeDefinition: { stacked: true, horizontal: false },
|
|
25118
|
+
category: "column",
|
|
25119
|
+
preview: "o-spreadsheet-ChartPreview.STACKED_COLUMN_CHART",
|
|
25120
|
+
})
|
|
25121
|
+
.add("bar", {
|
|
25122
|
+
matcher: (definition) => definition.type === "bar" && !definition.stacked && !!definition.horizontal,
|
|
25123
|
+
displayName: _t("Bar"),
|
|
25124
|
+
chartType: "bar",
|
|
25125
|
+
chartSubtype: "bar",
|
|
25126
|
+
subtypeDefinition: { horizontal: true, stacked: false },
|
|
25127
|
+
category: "bar",
|
|
25128
|
+
preview: "o-spreadsheet-ChartPreview.BAR_CHART",
|
|
25129
|
+
})
|
|
25130
|
+
.add("stacked_bar", {
|
|
25131
|
+
matcher: (definition) => definition.type === "bar" && definition.stacked && !!definition.horizontal,
|
|
25132
|
+
displayName: _t("Stacked Bar"),
|
|
25133
|
+
chartType: "bar",
|
|
25134
|
+
chartSubtype: "stacked_bar",
|
|
25135
|
+
subtypeDefinition: { horizontal: true, stacked: true },
|
|
25136
|
+
category: "bar",
|
|
25137
|
+
preview: "o-spreadsheet-ChartPreview.STACKED_BAR_CHART",
|
|
25138
|
+
})
|
|
25139
|
+
.add("combo", {
|
|
25140
|
+
displayName: _t("Combo"),
|
|
25141
|
+
chartSubtype: "combo",
|
|
25142
|
+
chartType: "combo",
|
|
25143
|
+
category: "line",
|
|
25144
|
+
preview: "o-spreadsheet-ChartPreview.COMBO_CHART",
|
|
25145
|
+
})
|
|
25146
|
+
.add("pie", {
|
|
25147
|
+
matcher: (definition) => definition.type === "pie" && !definition.isDoughnut,
|
|
25148
|
+
displayName: _t("Pie"),
|
|
25149
|
+
chartSubtype: "pie",
|
|
25150
|
+
chartType: "pie",
|
|
25151
|
+
subtypeDefinition: { isDoughnut: false },
|
|
25152
|
+
category: "pie",
|
|
25153
|
+
preview: "o-spreadsheet-ChartPreview.PIE_CHART",
|
|
25154
|
+
})
|
|
25155
|
+
.add("doughnut", {
|
|
25156
|
+
matcher: (definition) => definition.type === "pie" && !!definition.isDoughnut,
|
|
25157
|
+
displayName: _t("Doughnut"),
|
|
25158
|
+
chartSubtype: "doughnut",
|
|
25159
|
+
chartType: "pie",
|
|
25160
|
+
subtypeDefinition: { isDoughnut: true },
|
|
25161
|
+
category: "pie",
|
|
25162
|
+
preview: "o-spreadsheet-ChartPreview.DOUGHNUT_CHART",
|
|
25163
|
+
})
|
|
25164
|
+
.add("gauge", {
|
|
25165
|
+
displayName: _t("Gauge"),
|
|
25166
|
+
chartSubtype: "gauge",
|
|
25167
|
+
chartType: "gauge",
|
|
25168
|
+
category: "misc",
|
|
25169
|
+
preview: "o-spreadsheet-ChartPreview.GAUGE_CHART",
|
|
25170
|
+
})
|
|
25171
|
+
.add("scorecard", {
|
|
25172
|
+
displayName: _t("Scorecard"),
|
|
25173
|
+
chartSubtype: "scorecard",
|
|
25174
|
+
chartType: "scorecard",
|
|
25175
|
+
category: "misc",
|
|
25176
|
+
preview: "o-spreadsheet-ChartPreview.SCORECARD_CHART",
|
|
25177
|
+
})
|
|
25178
|
+
.add("waterfall", {
|
|
25179
|
+
displayName: _t("Waterfall"),
|
|
25180
|
+
chartSubtype: "waterfall",
|
|
25181
|
+
chartType: "waterfall",
|
|
25182
|
+
category: "misc",
|
|
25183
|
+
preview: "o-spreadsheet-ChartPreview.WATERFALL_CHART",
|
|
25184
|
+
})
|
|
25185
|
+
.add("pyramid", {
|
|
25186
|
+
displayName: _t("Population Pyramid"),
|
|
25187
|
+
chartSubtype: "pyramid",
|
|
25188
|
+
chartType: "pyramid",
|
|
25189
|
+
category: "misc",
|
|
25190
|
+
preview: "o-spreadsheet-ChartPreview.POPULATION_PYRAMID_CHART",
|
|
25191
|
+
});
|
|
24837
25192
|
|
|
24838
25193
|
/**
|
|
24839
25194
|
* Registry intended to support usual currencies. It is mainly used to create
|
|
@@ -26690,20 +27045,6 @@ function transformDefinition(definition, executed) {
|
|
|
26690
27045
|
}
|
|
26691
27046
|
return transformation.transformDefinition(definition, executed);
|
|
26692
27047
|
}
|
|
26693
|
-
/**
|
|
26694
|
-
* Get an empty definition based on the given context and the given type
|
|
26695
|
-
*/
|
|
26696
|
-
function getChartDefinitionFromContextCreation(context, type) {
|
|
26697
|
-
const chartClass = chartRegistry.get(type);
|
|
26698
|
-
return chartClass.getChartDefinitionFromContextCreation(context);
|
|
26699
|
-
}
|
|
26700
|
-
function getChartTypes() {
|
|
26701
|
-
const result = {};
|
|
26702
|
-
for (const key of chartRegistry.getKeys()) {
|
|
26703
|
-
result[key] = chartRegistry.get(key).name;
|
|
26704
|
-
}
|
|
26705
|
-
return result;
|
|
26706
|
-
}
|
|
26707
27048
|
/**
|
|
26708
27049
|
* Return a "smart" chart definition in the given zone. The definition is "smart" because it will
|
|
26709
27050
|
* use the best type of chart to display the data of the zone.
|
|
@@ -28163,6 +28504,40 @@ const pivotProperties = {
|
|
|
28163
28504
|
},
|
|
28164
28505
|
icon: "o-spreadsheet-Icon.PIVOT",
|
|
28165
28506
|
};
|
|
28507
|
+
const FIX_FORMULAS = {
|
|
28508
|
+
name: _t("Convert to individual formulas"),
|
|
28509
|
+
execute(env) {
|
|
28510
|
+
const position = env.model.getters.getActivePosition();
|
|
28511
|
+
const cell = env.model.getters.getCorrespondingFormulaCell(position);
|
|
28512
|
+
const pivotId = env.model.getters.getPivotIdFromPosition(position);
|
|
28513
|
+
if (!cell || !pivotId) {
|
|
28514
|
+
return;
|
|
28515
|
+
}
|
|
28516
|
+
const { sheetId, col, row } = env.model.getters.getCellPosition(cell.id);
|
|
28517
|
+
const pivot = env.model.getters.getPivot(pivotId);
|
|
28518
|
+
pivot.init();
|
|
28519
|
+
if (!pivot.isValid()) {
|
|
28520
|
+
return;
|
|
28521
|
+
}
|
|
28522
|
+
env.model.dispatch("INSERT_PIVOT", {
|
|
28523
|
+
sheetId,
|
|
28524
|
+
col,
|
|
28525
|
+
row,
|
|
28526
|
+
pivotId,
|
|
28527
|
+
table: pivot.getTableStructure().export(),
|
|
28528
|
+
});
|
|
28529
|
+
},
|
|
28530
|
+
isVisible: (env) => {
|
|
28531
|
+
const position = env.model.getters.getActivePosition();
|
|
28532
|
+
const pivotId = env.model.getters.getPivotIdFromPosition(position);
|
|
28533
|
+
if (!pivotId) {
|
|
28534
|
+
return false;
|
|
28535
|
+
}
|
|
28536
|
+
const pivot = env.model.getters.getPivot(pivotId);
|
|
28537
|
+
return pivot.isValid() && env.model.getters.isSpillPivotFormula(position);
|
|
28538
|
+
},
|
|
28539
|
+
icon: "o-spreadsheet-Icon.PIVOT",
|
|
28540
|
+
};
|
|
28166
28541
|
|
|
28167
28542
|
//------------------------------------------------------------------------------
|
|
28168
28543
|
// Context Menu Registry
|
|
@@ -28261,6 +28636,10 @@ cellMenuRegistry
|
|
|
28261
28636
|
name: INSERT_LINK_NAME,
|
|
28262
28637
|
sequence: 150,
|
|
28263
28638
|
separator: true,
|
|
28639
|
+
})
|
|
28640
|
+
.add("pivot_fix_formulas", {
|
|
28641
|
+
...FIX_FORMULAS,
|
|
28642
|
+
sequence: 155,
|
|
28264
28643
|
})
|
|
28265
28644
|
.add("pivot_properties", {
|
|
28266
28645
|
...pivotProperties,
|
|
@@ -30483,6 +30862,7 @@ class GenericChartConfigPanel extends Component {
|
|
|
30483
30862
|
});
|
|
30484
30863
|
dataSeriesRanges = [];
|
|
30485
30864
|
labelRange;
|
|
30865
|
+
chartTerms = ChartTerms;
|
|
30486
30866
|
setup() {
|
|
30487
30867
|
this.dataSeriesRanges = this.props.definition.dataSets;
|
|
30488
30868
|
this.labelRange = this.props.definition.labelRange;
|
|
@@ -30507,7 +30887,7 @@ class GenericChartConfigPanel extends Component {
|
|
|
30507
30887
|
return [
|
|
30508
30888
|
{
|
|
30509
30889
|
name: "aggregated",
|
|
30510
|
-
label:
|
|
30890
|
+
label: this.chartTerms.AggregatedChart,
|
|
30511
30891
|
value: this.props.definition.aggregated ?? false,
|
|
30512
30892
|
onChange: this.onUpdateAggregated.bind(this),
|
|
30513
30893
|
},
|
|
@@ -30583,9 +30963,6 @@ class GenericChartConfigPanel extends Component {
|
|
|
30583
30963
|
|
|
30584
30964
|
class BarConfigPanel extends GenericChartConfigPanel {
|
|
30585
30965
|
static template = "o-spreadsheet-BarConfigPanel";
|
|
30586
|
-
get stackedLabel() {
|
|
30587
|
-
return _t("Stacked barchart");
|
|
30588
|
-
}
|
|
30589
30966
|
onUpdateStacked(stacked) {
|
|
30590
30967
|
this.props.updateChart(this.props.figureId, {
|
|
30591
30968
|
stacked,
|
|
@@ -31575,6 +31952,9 @@ class ChartWithAxisDesignPanel extends Component {
|
|
|
31575
31952
|
return "left";
|
|
31576
31953
|
return dataSets[this.state.index].yAxisId === "y1" ? "right" : "left";
|
|
31577
31954
|
}
|
|
31955
|
+
get canHaveTwoVerticalAxis() {
|
|
31956
|
+
return "horizontal" in this.props.definition ? !this.props.definition.horizontal : true;
|
|
31957
|
+
}
|
|
31578
31958
|
updateDataSeriesLabel(ev) {
|
|
31579
31959
|
const label = ev.target.value;
|
|
31580
31960
|
const dataSets = this.props.definition.dataSets;
|
|
@@ -31739,19 +32119,13 @@ class LineConfigPanel extends GenericChartConfigPanel {
|
|
|
31739
32119
|
}
|
|
31740
32120
|
return false;
|
|
31741
32121
|
}
|
|
31742
|
-
get stackedLabel() {
|
|
31743
|
-
return _t("Stacked linechart");
|
|
31744
|
-
}
|
|
31745
|
-
get cumulativeLabel() {
|
|
31746
|
-
return _t("Cumulative data");
|
|
31747
|
-
}
|
|
31748
32122
|
getLabelRangeOptions() {
|
|
31749
32123
|
const options = super.getLabelRangeOptions();
|
|
31750
32124
|
if (this.canTreatLabelsAsText) {
|
|
31751
32125
|
options.push({
|
|
31752
32126
|
name: "labelsAsText",
|
|
31753
32127
|
value: this.props.definition.labelsAsText,
|
|
31754
|
-
label:
|
|
32128
|
+
label: this.chartTerms.TreatLabelsAsText,
|
|
31755
32129
|
onChange: this.onUpdateLabelsAsText.bind(this),
|
|
31756
32130
|
});
|
|
31757
32131
|
}
|
|
@@ -31818,7 +32192,7 @@ class ScatterConfigPanel extends GenericChartConfigPanel {
|
|
|
31818
32192
|
options.push({
|
|
31819
32193
|
name: "labelsAsText",
|
|
31820
32194
|
value: this.props.definition.labelsAsText,
|
|
31821
|
-
label:
|
|
32195
|
+
label: this.chartTerms.TreatLabelsAsText,
|
|
31822
32196
|
onChange: this.onUpdateLabelsAsText.bind(this),
|
|
31823
32197
|
});
|
|
31824
32198
|
}
|
|
@@ -32025,8 +32399,106 @@ chartSidePanelComponentRegistry
|
|
|
32025
32399
|
.add("waterfall", {
|
|
32026
32400
|
configuration: GenericChartConfigPanel,
|
|
32027
32401
|
design: WaterfallChartDesignPanel,
|
|
32402
|
+
})
|
|
32403
|
+
.add("pyramid", {
|
|
32404
|
+
configuration: GenericChartConfigPanel,
|
|
32405
|
+
design: ChartWithAxisDesignPanel,
|
|
32028
32406
|
});
|
|
32029
32407
|
|
|
32408
|
+
css /* scss */ `
|
|
32409
|
+
.o-section .o-type-selector {
|
|
32410
|
+
height: 30px;
|
|
32411
|
+
padding-left: 30px;
|
|
32412
|
+
}
|
|
32413
|
+
.o-type-selector-preview {
|
|
32414
|
+
left: 5px;
|
|
32415
|
+
top: 3px;
|
|
32416
|
+
.o-chart-preview {
|
|
32417
|
+
width: 24px;
|
|
32418
|
+
height: 24px;
|
|
32419
|
+
}
|
|
32420
|
+
}
|
|
32421
|
+
|
|
32422
|
+
.o-popover .o-chart-select-popover {
|
|
32423
|
+
box-sizing: border-box;
|
|
32424
|
+
background: #fff;
|
|
32425
|
+
.o-chart-type-item {
|
|
32426
|
+
cursor: pointer;
|
|
32427
|
+
padding: 3px 6px;
|
|
32428
|
+
margin: 1px 2px;
|
|
32429
|
+
&.selected,
|
|
32430
|
+
&:hover {
|
|
32431
|
+
background: #f5f5f5;
|
|
32432
|
+
border: 1px solid #ccc;
|
|
32433
|
+
padding: 2px 5px;
|
|
32434
|
+
}
|
|
32435
|
+
.o-chart-preview {
|
|
32436
|
+
width: 48px;
|
|
32437
|
+
height: 48px;
|
|
32438
|
+
}
|
|
32439
|
+
}
|
|
32440
|
+
}
|
|
32441
|
+
`;
|
|
32442
|
+
class ChartTypePicker extends Component {
|
|
32443
|
+
static template = "o-spreadsheet-ChartTypePicker";
|
|
32444
|
+
static components = { Section, Popover };
|
|
32445
|
+
static props = { figureId: String, chartPanelStore: Object };
|
|
32446
|
+
categories = chartCategories;
|
|
32447
|
+
chartTypeByCategories = {};
|
|
32448
|
+
popoverRef = useRef("popoverRef");
|
|
32449
|
+
selectRef = useRef("selectRef");
|
|
32450
|
+
state = useState({ popoverProps: undefined, popoverStyle: "" });
|
|
32451
|
+
setup() {
|
|
32452
|
+
useExternalListener(window, "pointerdown", this.onExternalClick, { capture: true });
|
|
32453
|
+
for (const subtypeProperties of chartSubtypeRegistry.getAll()) {
|
|
32454
|
+
if (this.chartTypeByCategories[subtypeProperties.category]) {
|
|
32455
|
+
this.chartTypeByCategories[subtypeProperties.category].push(subtypeProperties);
|
|
32456
|
+
}
|
|
32457
|
+
else {
|
|
32458
|
+
this.chartTypeByCategories[subtypeProperties.category] = [subtypeProperties];
|
|
32459
|
+
}
|
|
32460
|
+
}
|
|
32461
|
+
}
|
|
32462
|
+
onExternalClick(ev) {
|
|
32463
|
+
if (isChildEvent(this.popoverRef.el?.parentElement, ev) ||
|
|
32464
|
+
isChildEvent(this.selectRef.el, ev)) {
|
|
32465
|
+
return;
|
|
32466
|
+
}
|
|
32467
|
+
this.closePopover();
|
|
32468
|
+
}
|
|
32469
|
+
onTypeChange(type) {
|
|
32470
|
+
this.props.chartPanelStore.changeChartType(this.props.figureId, type);
|
|
32471
|
+
this.closePopover();
|
|
32472
|
+
}
|
|
32473
|
+
getChartDefinition(figureId) {
|
|
32474
|
+
return this.env.model.getters.getChartDefinition(figureId);
|
|
32475
|
+
}
|
|
32476
|
+
getSelectedChartSubtypeProperties() {
|
|
32477
|
+
const definition = this.getChartDefinition(this.props.figureId);
|
|
32478
|
+
const matchedChart = chartSubtypeRegistry
|
|
32479
|
+
.getAll()
|
|
32480
|
+
.find((c) => c.matcher?.(definition) || false);
|
|
32481
|
+
return matchedChart || chartSubtypeRegistry.get(definition.type);
|
|
32482
|
+
}
|
|
32483
|
+
onPointerDown(ev) {
|
|
32484
|
+
if (this.state.popoverProps) {
|
|
32485
|
+
this.closePopover();
|
|
32486
|
+
return;
|
|
32487
|
+
}
|
|
32488
|
+
const target = ev.currentTarget;
|
|
32489
|
+
const { bottom, right, width } = target.getBoundingClientRect();
|
|
32490
|
+
this.state.popoverProps = {
|
|
32491
|
+
anchorRect: { x: right, y: bottom, width: 0, height: 0 },
|
|
32492
|
+
positioning: "TopRight",
|
|
32493
|
+
verticalOffset: 0,
|
|
32494
|
+
};
|
|
32495
|
+
this.state.popoverStyle = cssPropertiesToCss({ width: `${width}px` });
|
|
32496
|
+
}
|
|
32497
|
+
closePopover() {
|
|
32498
|
+
this.state.popoverProps = undefined;
|
|
32499
|
+
}
|
|
32500
|
+
}
|
|
32501
|
+
|
|
32030
32502
|
class MainChartPanelStore extends SpreadsheetStore {
|
|
32031
32503
|
mutators = ["activatePanel", "changeChartType"];
|
|
32032
32504
|
panel = "configuration";
|
|
@@ -32034,7 +32506,7 @@ class MainChartPanelStore extends SpreadsheetStore {
|
|
|
32034
32506
|
activatePanel(panel) {
|
|
32035
32507
|
this.panel = panel;
|
|
32036
32508
|
}
|
|
32037
|
-
changeChartType(figureId,
|
|
32509
|
+
changeChartType(figureId, newDisplayType) {
|
|
32038
32510
|
this.creationContext = {
|
|
32039
32511
|
...this.creationContext,
|
|
32040
32512
|
...this.getters.getContextCreationChart(figureId),
|
|
@@ -32043,13 +32515,25 @@ class MainChartPanelStore extends SpreadsheetStore {
|
|
|
32043
32515
|
if (!sheetId) {
|
|
32044
32516
|
return;
|
|
32045
32517
|
}
|
|
32046
|
-
const definition = getChartDefinitionFromContextCreation(
|
|
32518
|
+
const definition = this.getChartDefinitionFromContextCreation(figureId, newDisplayType);
|
|
32047
32519
|
this.model.dispatch("UPDATE_CHART", {
|
|
32048
32520
|
definition,
|
|
32049
32521
|
id: figureId,
|
|
32050
32522
|
sheetId,
|
|
32051
32523
|
});
|
|
32052
32524
|
}
|
|
32525
|
+
getChartDefinitionFromContextCreation(figureId, newDisplayType) {
|
|
32526
|
+
const newChartInfo = chartSubtypeRegistry.get(newDisplayType);
|
|
32527
|
+
const ChartClass = chartRegistry.get(newChartInfo.chartType);
|
|
32528
|
+
const contextCreation = {
|
|
32529
|
+
...this.creationContext,
|
|
32530
|
+
...this.getters.getContextCreationChart(figureId),
|
|
32531
|
+
};
|
|
32532
|
+
return {
|
|
32533
|
+
...ChartClass.getChartDefinitionFromContextCreation(contextCreation),
|
|
32534
|
+
...newChartInfo.subtypeDefinition,
|
|
32535
|
+
};
|
|
32536
|
+
}
|
|
32053
32537
|
}
|
|
32054
32538
|
|
|
32055
32539
|
css /* scss */ `
|
|
@@ -32078,7 +32562,7 @@ css /* scss */ `
|
|
|
32078
32562
|
`;
|
|
32079
32563
|
class ChartPanel extends Component {
|
|
32080
32564
|
static template = "o-spreadsheet-ChartPanel";
|
|
32081
|
-
static components = { Section };
|
|
32565
|
+
static components = { Section, ChartTypePicker };
|
|
32082
32566
|
static props = { onCloseSidePanel: Function, figureId: String };
|
|
32083
32567
|
store;
|
|
32084
32568
|
get figureId() {
|
|
@@ -32138,9 +32622,6 @@ class ChartPanel extends Component {
|
|
|
32138
32622
|
getChartDefinition(figureId) {
|
|
32139
32623
|
return this.env.model.getters.getChartDefinition(figureId);
|
|
32140
32624
|
}
|
|
32141
|
-
get chartTypes() {
|
|
32142
|
-
return getChartTypes();
|
|
32143
|
-
}
|
|
32144
32625
|
}
|
|
32145
32626
|
|
|
32146
32627
|
css /* scss */ `
|
|
@@ -35214,9 +35695,17 @@ class SpreadsheetPivotTable {
|
|
|
35214
35695
|
}
|
|
35215
35696
|
getPivotCell(col, row, includeTotal = true) {
|
|
35216
35697
|
const colHeadersHeight = this.columns.length;
|
|
35217
|
-
if (row
|
|
35698
|
+
if (col > 0 && row === colHeadersHeight - 1) {
|
|
35218
35699
|
const domain = this.getColHeaderDomain(col, row);
|
|
35219
|
-
|
|
35700
|
+
if (!domain) {
|
|
35701
|
+
return EMPTY_PIVOT_CELL;
|
|
35702
|
+
}
|
|
35703
|
+
const measure = domain.at(-1)?.value.toString() || "";
|
|
35704
|
+
return { type: "MEASURE_HEADER", domain: domain.slice(0, -1), measure };
|
|
35705
|
+
}
|
|
35706
|
+
else if (row <= colHeadersHeight - 1) {
|
|
35707
|
+
const domain = this.getColHeaderDomain(col, row);
|
|
35708
|
+
return domain ? { type: "HEADER", domain } : EMPTY_PIVOT_CELL;
|
|
35220
35709
|
}
|
|
35221
35710
|
else if (col === 0) {
|
|
35222
35711
|
const rowIndex = row - colHeadersHeight;
|
|
@@ -35226,7 +35715,7 @@ class SpreadsheetPivotTable {
|
|
|
35226
35715
|
else {
|
|
35227
35716
|
const rowIndex = row - colHeadersHeight;
|
|
35228
35717
|
if (!includeTotal && this.isTotalRow(rowIndex)) {
|
|
35229
|
-
return
|
|
35718
|
+
return EMPTY_PIVOT_CELL;
|
|
35230
35719
|
}
|
|
35231
35720
|
const domain = [...this.getRowDomain(rowIndex), ...this.getColDomain(col)];
|
|
35232
35721
|
const measure = this.getColMeasure(col);
|
|
@@ -35280,6 +35769,7 @@ class SpreadsheetPivotTable {
|
|
|
35280
35769
|
};
|
|
35281
35770
|
}
|
|
35282
35771
|
}
|
|
35772
|
+
const EMPTY_PIVOT_CELL = { type: "EMPTY" };
|
|
35283
35773
|
|
|
35284
35774
|
/**
|
|
35285
35775
|
* This function converts a list of data entry into a spreadsheet pivot table.
|
|
@@ -35477,6 +35967,9 @@ function createDate(dimension, value, locale) {
|
|
|
35477
35967
|
if (!(granularity in MAP_VALUE_DIMENSION_DATE)) {
|
|
35478
35968
|
throw new Error(`Unknown date granularity: ${granularity}`);
|
|
35479
35969
|
}
|
|
35970
|
+
if (value === null) {
|
|
35971
|
+
return null;
|
|
35972
|
+
}
|
|
35480
35973
|
if (!MAP_VALUE_DIMENSION_DATE[granularity].set.has(value)) {
|
|
35481
35974
|
MAP_VALUE_DIMENSION_DATE[granularity].set.add(value);
|
|
35482
35975
|
const date = toJsDate(value, locale);
|
|
@@ -35707,14 +36200,17 @@ class SpreadsheetPivot {
|
|
|
35707
36200
|
if (dimension.type === "date") {
|
|
35708
36201
|
const adapter = pivotTimeAdapter(dimension.granularity);
|
|
35709
36202
|
return {
|
|
35710
|
-
value:
|
|
36203
|
+
value: lastNode.value !== "null"
|
|
36204
|
+
? adapter.toCellValue(toNormalizedPivotValue(dimension, lastNode.value))
|
|
36205
|
+
: _t("(Undefined)"),
|
|
35711
36206
|
format: adapter.getFormat(this.getters.getLocale()),
|
|
35712
36207
|
};
|
|
35713
36208
|
}
|
|
35714
36209
|
if (!finalCell) {
|
|
35715
36210
|
return { value: "" };
|
|
35716
36211
|
}
|
|
35717
|
-
|
|
36212
|
+
// Value can be null but stringified (e.g. an empty date, as for now every date is stringified)
|
|
36213
|
+
if (finalCell.value === null || finalCell.value === `${null}`) {
|
|
35718
36214
|
return { value: _t("(Undefined)") };
|
|
35719
36215
|
}
|
|
35720
36216
|
return {
|
|
@@ -35736,10 +36232,15 @@ class SpreadsheetPivot {
|
|
|
35736
36232
|
if (!operator) {
|
|
35737
36233
|
throw new Error(`Aggregator ${aggregator} does not exist`);
|
|
35738
36234
|
}
|
|
35739
|
-
|
|
35740
|
-
|
|
35741
|
-
|
|
35742
|
-
|
|
36235
|
+
try {
|
|
36236
|
+
return {
|
|
36237
|
+
value: values.length ? operator.fn([values], this.getters.getLocale()) : "",
|
|
36238
|
+
format: operator.format(values[0]),
|
|
36239
|
+
};
|
|
36240
|
+
}
|
|
36241
|
+
catch (e) {
|
|
36242
|
+
return handleError(e, aggregator.toUpperCase());
|
|
36243
|
+
}
|
|
35743
36244
|
}
|
|
35744
36245
|
getPossibleFieldValues(dimension) {
|
|
35745
36246
|
const values = [];
|
|
@@ -51106,12 +51607,12 @@ class PivotCorePlugin extends CorePlugin {
|
|
|
51106
51607
|
for (let col = 0; col < pivotCells.length; col++) {
|
|
51107
51608
|
for (let row = 0; row < pivotCells[col].length; row++) {
|
|
51108
51609
|
const pivotCell = pivotCells[col][row];
|
|
51109
|
-
|
|
51610
|
+
this.dispatch("UPDATE_CELL", {
|
|
51110
51611
|
sheetId: position.sheetId,
|
|
51111
51612
|
col: position.col + col,
|
|
51112
51613
|
row: position.row + row,
|
|
51113
|
-
|
|
51114
|
-
|
|
51614
|
+
content: makePivotFormulaFromPivotCell(formulaId, pivotCell),
|
|
51615
|
+
});
|
|
51115
51616
|
}
|
|
51116
51617
|
}
|
|
51117
51618
|
}
|
|
@@ -51141,21 +51642,6 @@ class PivotCorePlugin extends CorePlugin {
|
|
|
51141
51642
|
});
|
|
51142
51643
|
}
|
|
51143
51644
|
}
|
|
51144
|
-
addPivotFormula(position, formulaId, pivotCell) {
|
|
51145
|
-
let content = undefined;
|
|
51146
|
-
switch (pivotCell.type) {
|
|
51147
|
-
case "HEADER":
|
|
51148
|
-
content = makePivotFormula("PIVOT.HEADER", [formulaId, ...flatPivotDomain(pivotCell.domain)].filter(isDefined));
|
|
51149
|
-
break;
|
|
51150
|
-
case "VALUE":
|
|
51151
|
-
content = makePivotFormula("PIVOT.VALUE", [formulaId, pivotCell.measure, ...flatPivotDomain(pivotCell.domain)].filter(isDefined));
|
|
51152
|
-
break;
|
|
51153
|
-
}
|
|
51154
|
-
this.dispatch("UPDATE_CELL", {
|
|
51155
|
-
...position,
|
|
51156
|
-
content,
|
|
51157
|
-
});
|
|
51158
|
-
}
|
|
51159
51645
|
getPivotCore(pivotId) {
|
|
51160
51646
|
const pivot = this.pivots[pivotId];
|
|
51161
51647
|
if (!pivot) {
|
|
@@ -52820,7 +53306,6 @@ class Evaluator {
|
|
|
52820
53306
|
if (!this.blockedArrayFormulas.has(position)) {
|
|
52821
53307
|
this.invalidateSpreading(position);
|
|
52822
53308
|
}
|
|
52823
|
-
this.spreadingRelations.removeNode(position);
|
|
52824
53309
|
const cell = this.getters.getCell(position);
|
|
52825
53310
|
if (cell === undefined) {
|
|
52826
53311
|
return EMPTY_CELL;
|
|
@@ -52862,6 +53347,7 @@ class Evaluator {
|
|
|
52862
53347
|
this.assertSheetHasEnoughSpaceToSpreadFormulaResult(formulaPosition, formulaReturn);
|
|
52863
53348
|
const nbColumns = formulaReturn.length;
|
|
52864
53349
|
const nbRows = formulaReturn[0].length;
|
|
53350
|
+
this.spreadingRelations.removeNode(formulaPosition);
|
|
52865
53351
|
forEachSpreadPositionInMatrix(nbColumns, nbRows, this.updateSpreadRelation(formulaPosition));
|
|
52866
53352
|
this.assertNoMergedCellsInSpreadZone(formulaPosition, formulaReturn);
|
|
52867
53353
|
forEachSpreadPositionInMatrix(nbColumns, nbRows, this.checkCollision(formulaPosition));
|
|
@@ -54389,9 +54875,10 @@ class PivotUIPlugin extends UIPlugin {
|
|
|
54389
54875
|
"getPivot",
|
|
54390
54876
|
"getFirstPivotFunction",
|
|
54391
54877
|
"getPivotIdFromPosition",
|
|
54392
|
-
"
|
|
54878
|
+
"getPivotCellFromPosition",
|
|
54393
54879
|
"isPivotUnused",
|
|
54394
54880
|
"areDomainArgsFieldsValid",
|
|
54881
|
+
"isSpillPivotFormula",
|
|
54395
54882
|
];
|
|
54396
54883
|
pivots = {};
|
|
54397
54884
|
unusedPivots;
|
|
@@ -54470,6 +54957,14 @@ class PivotUIPlugin extends UIPlugin {
|
|
|
54470
54957
|
}
|
|
54471
54958
|
return undefined;
|
|
54472
54959
|
}
|
|
54960
|
+
isSpillPivotFormula(position) {
|
|
54961
|
+
const cell = this.getters.getCorrespondingFormulaCell(position);
|
|
54962
|
+
if (cell && cell.isFormula) {
|
|
54963
|
+
const pivotFunction = this.getFirstPivotFunction(cell.compiledFormula.tokens);
|
|
54964
|
+
return pivotFunction?.functionName === "PIVOT";
|
|
54965
|
+
}
|
|
54966
|
+
return false;
|
|
54967
|
+
}
|
|
54473
54968
|
getFirstPivotFunction(tokens) {
|
|
54474
54969
|
const pivotFunction = getFirstPivotFunction(tokens);
|
|
54475
54970
|
if (!pivotFunction) {
|
|
@@ -54503,29 +54998,29 @@ class PivotUIPlugin extends UIPlugin {
|
|
|
54503
54998
|
* If the cell is the result of PIVOT, the result is the domain of the cell
|
|
54504
54999
|
* as if it was the individual pivot formula
|
|
54505
55000
|
*/
|
|
54506
|
-
|
|
55001
|
+
getPivotCellFromPosition(position) {
|
|
54507
55002
|
const cell = this.getters.getCorrespondingFormulaCell(position);
|
|
54508
55003
|
if (!cell || !cell.isFormula || getNumberOfPivotFunctions(cell.compiledFormula.tokens) === 0) {
|
|
54509
|
-
return
|
|
55004
|
+
return EMPTY_PIVOT_CELL;
|
|
54510
55005
|
}
|
|
54511
55006
|
const mainPosition = this.getters.getCellPosition(cell.id);
|
|
54512
55007
|
const result = this.getters.getFirstPivotFunction(cell.compiledFormula.tokens);
|
|
54513
55008
|
if (!result) {
|
|
54514
|
-
return
|
|
55009
|
+
return EMPTY_PIVOT_CELL;
|
|
54515
55010
|
}
|
|
54516
55011
|
const { functionName, args } = result;
|
|
54517
55012
|
if (functionName === "PIVOT") {
|
|
54518
55013
|
const formulaId = args[0];
|
|
54519
55014
|
if (!formulaId) {
|
|
54520
|
-
return
|
|
55015
|
+
return EMPTY_PIVOT_CELL;
|
|
54521
55016
|
}
|
|
54522
55017
|
const pivotId = this.getters.getPivotId(formulaId.toString());
|
|
54523
55018
|
if (!pivotId) {
|
|
54524
|
-
return
|
|
55019
|
+
return EMPTY_PIVOT_CELL;
|
|
54525
55020
|
}
|
|
54526
55021
|
const pivot = this.getPivot(pivotId);
|
|
54527
55022
|
if (!pivot.isValid()) {
|
|
54528
|
-
return
|
|
55023
|
+
return EMPTY_PIVOT_CELL;
|
|
54529
55024
|
}
|
|
54530
55025
|
const includeTotal = args[2] === false ? false : undefined;
|
|
54531
55026
|
const includeColumnHeaders = args[3] === false ? false : undefined;
|
|
@@ -54534,22 +55029,28 @@ class PivotUIPlugin extends UIPlugin {
|
|
|
54534
55029
|
.getPivotCells(includeTotal, includeColumnHeaders);
|
|
54535
55030
|
const pivotCol = position.col - mainPosition.col;
|
|
54536
55031
|
const pivotRow = position.row - mainPosition.row;
|
|
54537
|
-
|
|
54538
|
-
if (pivotCell.type === "EMPTY") {
|
|
54539
|
-
return undefined;
|
|
54540
|
-
}
|
|
54541
|
-
let domain = pivotCell.domain;
|
|
54542
|
-
if (domain.at(-1)?.field === "measure") {
|
|
54543
|
-
domain = domain.slice(0, -1);
|
|
54544
|
-
}
|
|
54545
|
-
return { domainArgs: domain, isHeader: pivotCell.type === "HEADER" };
|
|
55032
|
+
return pivotCells[pivotCol][pivotRow];
|
|
54546
55033
|
}
|
|
54547
|
-
|
|
54548
|
-
|
|
54549
|
-
|
|
55034
|
+
if (functionName === "PIVOT.HEADER" && args.at(-2) === "measure") {
|
|
55035
|
+
const domain = toPivotDomain(args.slice(1, -2).map((x) => `${x}`));
|
|
55036
|
+
return {
|
|
55037
|
+
type: "MEASURE_HEADER",
|
|
55038
|
+
domain,
|
|
55039
|
+
measure: args.at(-1)?.toString() || "",
|
|
55040
|
+
};
|
|
55041
|
+
}
|
|
55042
|
+
else if (functionName === "PIVOT.HEADER") {
|
|
55043
|
+
return {
|
|
55044
|
+
type: "HEADER",
|
|
55045
|
+
domain: toPivotDomain(args.slice(1).map((x) => `${x}`)),
|
|
55046
|
+
};
|
|
54550
55047
|
}
|
|
54551
|
-
const
|
|
54552
|
-
return {
|
|
55048
|
+
const [measure, ...domainArgs] = args.slice(1);
|
|
55049
|
+
return {
|
|
55050
|
+
type: "VALUE",
|
|
55051
|
+
domain: toPivotDomain(domainArgs.map((x) => `${x}`)),
|
|
55052
|
+
measure: measure?.toString() || "",
|
|
55053
|
+
};
|
|
54553
55054
|
}
|
|
54554
55055
|
getPivot(pivotId) {
|
|
54555
55056
|
return this.pivots[pivotId];
|
|
@@ -63832,6 +64333,9 @@ class SelectionStreamProcessorImpl {
|
|
|
63832
64333
|
getBackToDefault() {
|
|
63833
64334
|
this.stream.getBackToDefault();
|
|
63834
64335
|
}
|
|
64336
|
+
getAnchor() {
|
|
64337
|
+
return this.anchor;
|
|
64338
|
+
}
|
|
63835
64339
|
modifyAnchor(anchor, mode, options) {
|
|
63836
64340
|
const sheetId = this.getters.getActiveSheetId();
|
|
63837
64341
|
anchor = {
|
|
@@ -66795,6 +67299,7 @@ const registries = {
|
|
|
66795
67299
|
chartSidePanelComponentRegistry,
|
|
66796
67300
|
chartComponentRegistry,
|
|
66797
67301
|
chartRegistry,
|
|
67302
|
+
chartSubtypeRegistry,
|
|
66798
67303
|
topbarMenuRegistry,
|
|
66799
67304
|
topbarComponentRegistry,
|
|
66800
67305
|
clickableCellRegistry,
|
|
@@ -66903,6 +67408,7 @@ const components = {
|
|
|
66903
67408
|
GaugeChartDesignPanel,
|
|
66904
67409
|
ScorecardChartConfigPanel,
|
|
66905
67410
|
ScorecardChartDesignPanel,
|
|
67411
|
+
ChartTypePicker,
|
|
66906
67412
|
FigureComponent,
|
|
66907
67413
|
Menu,
|
|
66908
67414
|
Popover,
|
|
@@ -66952,11 +67458,12 @@ const constants = {
|
|
|
66952
67458
|
DEFAULT_LOCALE,
|
|
66953
67459
|
HIGHLIGHT_COLOR,
|
|
66954
67460
|
PIVOT_TABLE_CONFIG,
|
|
67461
|
+
ChartTerms,
|
|
66955
67462
|
};
|
|
66956
67463
|
|
|
66957
67464
|
export { AbstractCellClipboardHandler, AbstractChart, AbstractFigureClipboardHandler, CellErrorType, CommandResult, CorePlugin, DispatchResult, EvaluationError, Model, PivotRuntimeDefinition, Registry, Revision, SPREADSHEET_DIMENSIONS, Spreadsheet, SpreadsheetPivotTable, UIPlugin, __info__, addFunction, addRenderingLayer, astToFormula, compile, compileTokens, components, constants, convertAstNodes, coreTypes, findCellInNewZone, functionCache, helpers, hooks, invalidateCFEvaluationCommands, invalidateDependenciesCommands, invalidateEvaluationCommands, iterateAstNodes, links, load, parse, parseTokens, readonlyAllowedCommands, registries, setDefaultSheetViewSize, setTranslationMethod, stores, tokenColors, tokenize };
|
|
66958
67465
|
|
|
66959
67466
|
|
|
66960
|
-
__info__.version = "17.4.0-alpha.
|
|
66961
|
-
__info__.date = "2024-06-
|
|
66962
|
-
__info__.hash = "
|
|
67467
|
+
__info__.version = "17.4.0-alpha.6";
|
|
67468
|
+
__info__.date = "2024-06-19T13:46:27.157Z";
|
|
67469
|
+
__info__.hash = "a4f22e4";
|