@odoo/o-spreadsheet 17.4.0-alpha.5 → 17.4.0-alpha.7
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 +1234 -747
- package/dist/o-spreadsheet.d.ts +232 -120
- package/dist/o-spreadsheet.esm.js +1234 -747
- package/dist/o-spreadsheet.iife.js +1234 -747
- package/dist/o-spreadsheet.iife.min.js +377 -344
- package/dist/o_spreadsheet.xml +250 -23
- package/package.json +6 -4
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
|
|
2
|
-
// @odoo-module ignore
|
|
3
2
|
/**
|
|
4
3
|
* This file is generated by o-spreadsheet build tools. Do not edit it.
|
|
5
4
|
* @see https://github.com/odoo/o-spreadsheet
|
|
6
|
-
* @version 17.4.0-alpha.
|
|
7
|
-
* @date 2024-06-
|
|
8
|
-
* @hash
|
|
5
|
+
* @version 17.4.0-alpha.7
|
|
6
|
+
* @date 2024-06-21T08:42:22.370Z
|
|
7
|
+
* @hash a99db9a
|
|
9
8
|
*/
|
|
10
9
|
|
|
11
10
|
'use strict';
|
|
@@ -6202,7 +6201,7 @@ class CellClipboardHandler extends AbstractCellClipboardHandler {
|
|
|
6202
6201
|
};
|
|
6203
6202
|
}
|
|
6204
6203
|
isPasteAllowed(sheetId, target, content, clipboardOptions) {
|
|
6205
|
-
if (!
|
|
6204
|
+
if (!content.cells) {
|
|
6206
6205
|
return "Success" /* CommandResult.Success */;
|
|
6207
6206
|
}
|
|
6208
6207
|
if (clipboardOptions?.isCutOperation && clipboardOptions?.pasteOption !== undefined) {
|
|
@@ -6216,6 +6215,17 @@ class CellClipboardHandler extends AbstractCellClipboardHandler {
|
|
|
6216
6215
|
return "WrongPasteSelection" /* CommandResult.WrongPasteSelection */;
|
|
6217
6216
|
}
|
|
6218
6217
|
}
|
|
6218
|
+
const clipboardHeight = content.cells.length;
|
|
6219
|
+
const clipboardWidth = content.cells[0].length;
|
|
6220
|
+
for (const zone of getPasteZones(target, content.cells)) {
|
|
6221
|
+
if (this.getters.doesIntersectMerge(sheetId, zone)) {
|
|
6222
|
+
if (target.length > 1 ||
|
|
6223
|
+
!this.getters.isSingleCellOrMerge(sheetId, target[0]) ||
|
|
6224
|
+
clipboardHeight * clipboardWidth !== 1) {
|
|
6225
|
+
return "WillRemoveExistingMerge" /* CommandResult.WillRemoveExistingMerge */;
|
|
6226
|
+
}
|
|
6227
|
+
}
|
|
6228
|
+
}
|
|
6219
6229
|
return "Success" /* CommandResult.Success */;
|
|
6220
6230
|
}
|
|
6221
6231
|
/**
|
|
@@ -6455,13 +6465,22 @@ class ConditionalFormatClipboardHandler extends AbstractCellClipboardHandler {
|
|
|
6455
6465
|
}
|
|
6456
6466
|
const { rowsIndexes, columnsIndexes } = data;
|
|
6457
6467
|
const sheetId = data.sheetId;
|
|
6458
|
-
const
|
|
6459
|
-
|
|
6460
|
-
|
|
6461
|
-
|
|
6468
|
+
const cfRules = [];
|
|
6469
|
+
for (const row of rowsIndexes) {
|
|
6470
|
+
const cfRuleInRow = [];
|
|
6471
|
+
for (const col of columnsIndexes) {
|
|
6472
|
+
const cfRules = Array.from(this.getters.getRulesByCell(sheetId, col, row));
|
|
6473
|
+
cfRuleInRow.push({
|
|
6474
|
+
position: { col, row, sheetId },
|
|
6475
|
+
rules: cfRules,
|
|
6476
|
+
});
|
|
6477
|
+
}
|
|
6478
|
+
cfRules.push(cfRuleInRow);
|
|
6479
|
+
}
|
|
6480
|
+
return { cfRules };
|
|
6462
6481
|
}
|
|
6463
6482
|
paste(target, clippedContent, options) {
|
|
6464
|
-
if (!clippedContent?.
|
|
6483
|
+
if (!clippedContent?.cfRules ||
|
|
6465
6484
|
options?.pasteOption === "asValue" ||
|
|
6466
6485
|
!("zones" in target) ||
|
|
6467
6486
|
!target.zones.length) {
|
|
@@ -6470,7 +6489,7 @@ class ConditionalFormatClipboardHandler extends AbstractCellClipboardHandler {
|
|
|
6470
6489
|
const zones = target.zones;
|
|
6471
6490
|
const sheetId = target.sheetId;
|
|
6472
6491
|
if (!options?.isCutOperation) {
|
|
6473
|
-
this.pasteFromCopy(sheetId, zones, clippedContent.
|
|
6492
|
+
this.pasteFromCopy(sheetId, zones, clippedContent.cfRules, options);
|
|
6474
6493
|
}
|
|
6475
6494
|
else {
|
|
6476
6495
|
this.pasteFromCut(sheetId, zones, clippedContent);
|
|
@@ -6478,12 +6497,12 @@ class ConditionalFormatClipboardHandler extends AbstractCellClipboardHandler {
|
|
|
6478
6497
|
}
|
|
6479
6498
|
pasteFromCut(sheetId, target, content) {
|
|
6480
6499
|
const selection = target[0];
|
|
6481
|
-
this.pasteZone(sheetId, selection.left, selection.top, content.
|
|
6500
|
+
this.pasteZone(sheetId, selection.left, selection.top, content.cfRules, {
|
|
6482
6501
|
isCutOperation: true,
|
|
6483
6502
|
});
|
|
6484
6503
|
}
|
|
6485
|
-
pasteZone(sheetId, col, row,
|
|
6486
|
-
for (const [r, rowCells] of
|
|
6504
|
+
pasteZone(sheetId, col, row, cfRules, clipboardOptions) {
|
|
6505
|
+
for (const [r, rowCells] of cfRules.entries()) {
|
|
6487
6506
|
for (const [c, origin] of rowCells.entries()) {
|
|
6488
6507
|
const position = { col: col + c, row: row + r, sheetId };
|
|
6489
6508
|
this.pasteCf(origin, position, clipboardOptions?.isCutOperation);
|
|
@@ -6491,23 +6510,21 @@ class ConditionalFormatClipboardHandler extends AbstractCellClipboardHandler {
|
|
|
6491
6510
|
}
|
|
6492
6511
|
}
|
|
6493
6512
|
pasteCf(origin, target, isCutOperation) {
|
|
6494
|
-
|
|
6495
|
-
|
|
6496
|
-
for (const
|
|
6497
|
-
|
|
6498
|
-
|
|
6499
|
-
|
|
6500
|
-
|
|
6501
|
-
|
|
6502
|
-
|
|
6503
|
-
|
|
6504
|
-
|
|
6505
|
-
|
|
6506
|
-
|
|
6507
|
-
|
|
6508
|
-
|
|
6509
|
-
this.adaptCFRules(target.sheetId, cfToCopyTo, [zone], []);
|
|
6510
|
-
}
|
|
6513
|
+
if (origin?.rules && origin.rules.length > 0) {
|
|
6514
|
+
const zone = positionToZone(target);
|
|
6515
|
+
for (const rule of origin.rules) {
|
|
6516
|
+
const toRemoveZones = [];
|
|
6517
|
+
if (isCutOperation) {
|
|
6518
|
+
//remove from current rule
|
|
6519
|
+
toRemoveZones.push(positionToZone(origin.position));
|
|
6520
|
+
}
|
|
6521
|
+
if (origin.position.sheetId === target.sheetId) {
|
|
6522
|
+
this.adaptCFRules(origin.position.sheetId, rule, [zone], toRemoveZones);
|
|
6523
|
+
}
|
|
6524
|
+
else {
|
|
6525
|
+
this.adaptCFRules(origin.position.sheetId, rule, [], toRemoveZones);
|
|
6526
|
+
const cfToCopyTo = this.getCFToCopyTo(target.sheetId, rule);
|
|
6527
|
+
this.adaptCFRules(target.sheetId, cfToCopyTo, [zone], []);
|
|
6511
6528
|
}
|
|
6512
6529
|
}
|
|
6513
6530
|
}
|
|
@@ -6550,13 +6567,20 @@ class DataValidationClipboardHandler extends AbstractCellClipboardHandler {
|
|
|
6550
6567
|
}
|
|
6551
6568
|
const { rowsIndexes, columnsIndexes } = data;
|
|
6552
6569
|
const sheetId = data.sheetId;
|
|
6553
|
-
const
|
|
6554
|
-
|
|
6555
|
-
|
|
6556
|
-
|
|
6570
|
+
const dvRules = [];
|
|
6571
|
+
for (const row of rowsIndexes) {
|
|
6572
|
+
const dvRuleInRow = [];
|
|
6573
|
+
for (const col of columnsIndexes) {
|
|
6574
|
+
const position = { sheetId, col, row };
|
|
6575
|
+
const rule = this.getters.getValidationRuleForCell(position);
|
|
6576
|
+
dvRuleInRow.push({ position, rule });
|
|
6577
|
+
}
|
|
6578
|
+
dvRules.push(dvRuleInRow);
|
|
6579
|
+
}
|
|
6580
|
+
return { dvRules };
|
|
6557
6581
|
}
|
|
6558
6582
|
paste(target, clippedContent, options) {
|
|
6559
|
-
if (!clippedContent?.
|
|
6583
|
+
if (!clippedContent?.dvRules) {
|
|
6560
6584
|
return;
|
|
6561
6585
|
}
|
|
6562
6586
|
if (options?.pasteOption) {
|
|
@@ -6568,7 +6592,7 @@ class DataValidationClipboardHandler extends AbstractCellClipboardHandler {
|
|
|
6568
6592
|
const zones = target.zones;
|
|
6569
6593
|
const sheetId = target.sheetId;
|
|
6570
6594
|
if (!options?.isCutOperation) {
|
|
6571
|
-
this.pasteFromCopy(sheetId, zones, clippedContent.
|
|
6595
|
+
this.pasteFromCopy(sheetId, zones, clippedContent.dvRules);
|
|
6572
6596
|
}
|
|
6573
6597
|
else {
|
|
6574
6598
|
this.pasteFromCut(sheetId, zones, clippedContent);
|
|
@@ -6576,12 +6600,12 @@ class DataValidationClipboardHandler extends AbstractCellClipboardHandler {
|
|
|
6576
6600
|
}
|
|
6577
6601
|
pasteFromCut(sheetId, target, content) {
|
|
6578
6602
|
const selection = target[0];
|
|
6579
|
-
this.pasteZone(sheetId, selection.left, selection.top, content.
|
|
6603
|
+
this.pasteZone(sheetId, selection.left, selection.top, content.dvRules, {
|
|
6580
6604
|
isCutOperation: true,
|
|
6581
6605
|
});
|
|
6582
6606
|
}
|
|
6583
|
-
pasteZone(sheetId, col, row,
|
|
6584
|
-
for (const [r, rowCells] of
|
|
6607
|
+
pasteZone(sheetId, col, row, dvRules, clipboardOptions) {
|
|
6608
|
+
for (const [r, rowCells] of dvRules.entries()) {
|
|
6585
6609
|
for (const [c, origin] of rowCells.entries()) {
|
|
6586
6610
|
const position = { col: col + c, row: row + r, sheetId };
|
|
6587
6611
|
this.pasteDataValidation(origin, position, clipboardOptions?.isCutOperation);
|
|
@@ -6589,41 +6613,43 @@ class DataValidationClipboardHandler extends AbstractCellClipboardHandler {
|
|
|
6589
6613
|
}
|
|
6590
6614
|
}
|
|
6591
6615
|
pasteDataValidation(origin, target, isCutOperation) {
|
|
6592
|
-
|
|
6593
|
-
|
|
6594
|
-
const
|
|
6595
|
-
if (
|
|
6596
|
-
|
|
6597
|
-
|
|
6598
|
-
|
|
6599
|
-
|
|
6600
|
-
}
|
|
6601
|
-
const zone = positionToZone(target);
|
|
6602
|
-
for (const range of rule.ranges) {
|
|
6603
|
-
if (isInside(origin.col, origin.row, range.zone)) {
|
|
6604
|
-
const toRemoveZone = [];
|
|
6605
|
-
if (isCutOperation) {
|
|
6606
|
-
toRemoveZone.push(positionToZone(origin));
|
|
6607
|
-
}
|
|
6608
|
-
if (origin.sheetId === target.sheetId) {
|
|
6609
|
-
this.adaptDataValidationRule(origin.sheetId, rule, [zone], toRemoveZone);
|
|
6616
|
+
if (origin) {
|
|
6617
|
+
const zone = positionToZone(target);
|
|
6618
|
+
const rule = origin.rule;
|
|
6619
|
+
if (!rule) {
|
|
6620
|
+
const targetRule = this.getters.getValidationRuleForCell(target);
|
|
6621
|
+
if (targetRule) {
|
|
6622
|
+
// Remove the data validation rule on the target cell
|
|
6623
|
+
this.adaptDataValidationRule(target.sheetId, targetRule, [], [zone]);
|
|
6610
6624
|
}
|
|
6611
|
-
|
|
6612
|
-
|
|
6613
|
-
|
|
6614
|
-
|
|
6625
|
+
return;
|
|
6626
|
+
}
|
|
6627
|
+
const toRemoveZone = [];
|
|
6628
|
+
if (isCutOperation) {
|
|
6629
|
+
toRemoveZone.push(positionToZone(origin.position));
|
|
6630
|
+
}
|
|
6631
|
+
if (origin.position.sheetId === target.sheetId) {
|
|
6632
|
+
const copyToRule = this.getDataValidationRuleToCopyTo(target.sheetId, rule, false);
|
|
6633
|
+
this.adaptDataValidationRule(origin.position.sheetId, copyToRule, [zone], toRemoveZone);
|
|
6634
|
+
}
|
|
6635
|
+
else {
|
|
6636
|
+
const originRule = this.getters.getValidationRuleForCell(origin.position);
|
|
6637
|
+
if (originRule) {
|
|
6638
|
+
this.adaptDataValidationRule(origin.position.sheetId, originRule, [], toRemoveZone);
|
|
6615
6639
|
}
|
|
6640
|
+
const copyToRule = this.getDataValidationRuleToCopyTo(target.sheetId, rule);
|
|
6641
|
+
this.adaptDataValidationRule(target.sheetId, copyToRule, [zone], []);
|
|
6616
6642
|
}
|
|
6617
6643
|
}
|
|
6618
6644
|
}
|
|
6619
|
-
getDataValidationRuleToCopyTo(targetSheetId, originRule) {
|
|
6645
|
+
getDataValidationRuleToCopyTo(targetSheetId, originRule, newId = true) {
|
|
6620
6646
|
const ruleInTargetSheet = this.getters
|
|
6621
6647
|
.getDataValidationRules(targetSheetId)
|
|
6622
6648
|
.find((rule) => deepEquals(originRule.criterion, rule.criterion) &&
|
|
6623
6649
|
originRule.isBlocking === rule.isBlocking);
|
|
6624
6650
|
return ruleInTargetSheet
|
|
6625
6651
|
? ruleInTargetSheet
|
|
6626
|
-
: { ...originRule, id: this.uuidGenerator.uuidv4(), ranges: [] };
|
|
6652
|
+
: { ...originRule, id: newId ? this.uuidGenerator.uuidv4() : originRule.id, ranges: [] };
|
|
6627
6653
|
}
|
|
6628
6654
|
/**
|
|
6629
6655
|
* Add or remove XCs to a given data validation rule.
|
|
@@ -6715,69 +6741,63 @@ class ImageClipboardHandler extends AbstractFigureClipboardHandler {
|
|
|
6715
6741
|
}
|
|
6716
6742
|
|
|
6717
6743
|
class MergeClipboardHandler extends AbstractCellClipboardHandler {
|
|
6718
|
-
|
|
6719
|
-
if (!
|
|
6720
|
-
return
|
|
6744
|
+
copy(data) {
|
|
6745
|
+
if (!data.zones.length) {
|
|
6746
|
+
return;
|
|
6721
6747
|
}
|
|
6722
|
-
const
|
|
6723
|
-
const
|
|
6724
|
-
|
|
6725
|
-
|
|
6726
|
-
|
|
6727
|
-
|
|
6728
|
-
|
|
6729
|
-
|
|
6730
|
-
}
|
|
6748
|
+
const sheetId = this.getters.getActiveSheetId();
|
|
6749
|
+
const { rowsIndexes, columnsIndexes } = data;
|
|
6750
|
+
const merges = [];
|
|
6751
|
+
for (const row of rowsIndexes) {
|
|
6752
|
+
const mergesInRow = [];
|
|
6753
|
+
for (const col of columnsIndexes) {
|
|
6754
|
+
const position = { col, row, sheetId };
|
|
6755
|
+
mergesInRow.push(this.getters.getMerge(position));
|
|
6731
6756
|
}
|
|
6757
|
+
merges.push(mergesInRow);
|
|
6732
6758
|
}
|
|
6733
|
-
return
|
|
6759
|
+
return { merges };
|
|
6734
6760
|
}
|
|
6735
6761
|
/**
|
|
6736
6762
|
* Paste the clipboard content in the given target
|
|
6737
6763
|
*/
|
|
6738
6764
|
paste(target, content, options) {
|
|
6739
|
-
if (
|
|
6765
|
+
if (!content.merges ||
|
|
6766
|
+
options?.isCutOperation ||
|
|
6767
|
+
!("zones" in target) ||
|
|
6768
|
+
!target.zones.length) {
|
|
6740
6769
|
return;
|
|
6741
6770
|
}
|
|
6742
|
-
this.pasteFromCopy(target.sheetId, target.zones, content.
|
|
6771
|
+
this.pasteFromCopy(target.sheetId, target.zones, content.merges, options);
|
|
6743
6772
|
}
|
|
6744
|
-
pasteZone(sheetId, col, row,
|
|
6745
|
-
for (const [r,
|
|
6746
|
-
for (const [c,
|
|
6747
|
-
if (!origin.position) {
|
|
6748
|
-
continue;
|
|
6749
|
-
}
|
|
6773
|
+
pasteZone(sheetId, col, row, merges) {
|
|
6774
|
+
for (const [r, rowMerges] of merges.entries()) {
|
|
6775
|
+
for (const [c, originMerge] of rowMerges.entries()) {
|
|
6750
6776
|
const position = { col: col + c, row: row + r, sheetId };
|
|
6751
|
-
this.
|
|
6777
|
+
this.pasteMerge(originMerge, position);
|
|
6752
6778
|
}
|
|
6753
6779
|
}
|
|
6754
6780
|
}
|
|
6755
|
-
|
|
6756
|
-
|
|
6757
|
-
|
|
6758
|
-
|
|
6759
|
-
|
|
6760
|
-
|
|
6761
|
-
const { col: mainCellColOrigin, row: mainCellRowOrigin } = this.getters.getMainCellPosition(origin);
|
|
6762
|
-
if (mainCellColOrigin === col && mainCellRowOrigin === row) {
|
|
6763
|
-
const merge = this.getters.getMerge(origin);
|
|
6764
|
-
if (!merge) {
|
|
6765
|
-
return;
|
|
6766
|
-
}
|
|
6767
|
-
({ sheetId, col, row } = target);
|
|
6768
|
-
this.dispatch("ADD_MERGE", {
|
|
6769
|
-
sheetId,
|
|
6770
|
-
force: true,
|
|
6771
|
-
target: [
|
|
6772
|
-
{
|
|
6773
|
-
left: col,
|
|
6774
|
-
top: row,
|
|
6775
|
-
right: col + merge.right - merge.left,
|
|
6776
|
-
bottom: row + merge.bottom - merge.top,
|
|
6777
|
-
},
|
|
6778
|
-
],
|
|
6779
|
-
});
|
|
6781
|
+
pasteMerge(originMerge, target) {
|
|
6782
|
+
if (!originMerge) {
|
|
6783
|
+
return;
|
|
6784
|
+
}
|
|
6785
|
+
if (this.getters.isInMerge(target)) {
|
|
6786
|
+
return;
|
|
6780
6787
|
}
|
|
6788
|
+
const { sheetId, col, row } = target;
|
|
6789
|
+
this.dispatch("ADD_MERGE", {
|
|
6790
|
+
sheetId,
|
|
6791
|
+
force: true,
|
|
6792
|
+
target: [
|
|
6793
|
+
{
|
|
6794
|
+
left: col,
|
|
6795
|
+
top: row,
|
|
6796
|
+
right: col + originMerge.right - originMerge.left,
|
|
6797
|
+
bottom: row + originMerge.bottom - originMerge.top,
|
|
6798
|
+
},
|
|
6799
|
+
],
|
|
6800
|
+
});
|
|
6781
6801
|
}
|
|
6782
6802
|
}
|
|
6783
6803
|
|
|
@@ -7757,6 +7777,486 @@ function errorCell(value, message) {
|
|
|
7757
7777
|
};
|
|
7758
7778
|
}
|
|
7759
7779
|
|
|
7780
|
+
function boolAnd(args) {
|
|
7781
|
+
let foundBoolean = false;
|
|
7782
|
+
let acc = true;
|
|
7783
|
+
conditionalVisitBoolean(args, (arg) => {
|
|
7784
|
+
foundBoolean = true;
|
|
7785
|
+
acc = acc && arg;
|
|
7786
|
+
return acc;
|
|
7787
|
+
});
|
|
7788
|
+
return {
|
|
7789
|
+
foundBoolean,
|
|
7790
|
+
result: acc,
|
|
7791
|
+
};
|
|
7792
|
+
}
|
|
7793
|
+
function boolOr(args) {
|
|
7794
|
+
let foundBoolean = false;
|
|
7795
|
+
let acc = false;
|
|
7796
|
+
conditionalVisitBoolean(args, (arg) => {
|
|
7797
|
+
foundBoolean = true;
|
|
7798
|
+
acc = acc || arg;
|
|
7799
|
+
return !acc;
|
|
7800
|
+
});
|
|
7801
|
+
return {
|
|
7802
|
+
foundBoolean,
|
|
7803
|
+
result: acc,
|
|
7804
|
+
};
|
|
7805
|
+
}
|
|
7806
|
+
|
|
7807
|
+
function sum(values, locale) {
|
|
7808
|
+
return reduceNumbers(values, (acc, a) => acc + a, 0, locale);
|
|
7809
|
+
}
|
|
7810
|
+
function countUnique(args) {
|
|
7811
|
+
return reduceAny(args, (acc, a) => (isDataNonEmpty(a) ? acc.add(a?.value) : acc), new Set()).size;
|
|
7812
|
+
}
|
|
7813
|
+
|
|
7814
|
+
function assertSameNumberOfElements(...args) {
|
|
7815
|
+
const dims = args[0].length;
|
|
7816
|
+
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())));
|
|
7817
|
+
}
|
|
7818
|
+
function average(values, locale) {
|
|
7819
|
+
let count = 0;
|
|
7820
|
+
const sum = reduceNumbers(values, (acc, a) => {
|
|
7821
|
+
count += 1;
|
|
7822
|
+
return acc + a;
|
|
7823
|
+
}, 0, locale);
|
|
7824
|
+
assertNotZero(count);
|
|
7825
|
+
return sum / count;
|
|
7826
|
+
}
|
|
7827
|
+
function countNumbers(values, locale) {
|
|
7828
|
+
let count = 0;
|
|
7829
|
+
for (let n of values) {
|
|
7830
|
+
if (isMatrix(n)) {
|
|
7831
|
+
for (let i of n) {
|
|
7832
|
+
for (let j of i) {
|
|
7833
|
+
if (typeof j.value === "number") {
|
|
7834
|
+
count += 1;
|
|
7835
|
+
}
|
|
7836
|
+
}
|
|
7837
|
+
}
|
|
7838
|
+
}
|
|
7839
|
+
else {
|
|
7840
|
+
const value = n?.value;
|
|
7841
|
+
if (!isEvaluationError(value) &&
|
|
7842
|
+
(typeof value !== "string" || isNumber(value, locale) || parseDateTime(value, locale))) {
|
|
7843
|
+
count += 1;
|
|
7844
|
+
}
|
|
7845
|
+
}
|
|
7846
|
+
}
|
|
7847
|
+
return count;
|
|
7848
|
+
}
|
|
7849
|
+
function countAny(values) {
|
|
7850
|
+
return reduceAny(values, (acc, a) => (a !== undefined && a.value !== null ? acc + 1 : acc), 0);
|
|
7851
|
+
}
|
|
7852
|
+
function max(values, locale) {
|
|
7853
|
+
const result = reduceNumbers(values, (acc, a) => (acc < a ? a : acc), -Infinity, locale);
|
|
7854
|
+
return result === -Infinity ? 0 : result;
|
|
7855
|
+
}
|
|
7856
|
+
function min(values, locale) {
|
|
7857
|
+
const result = reduceNumbers(values, (acc, a) => (a < acc ? a : acc), Infinity, locale);
|
|
7858
|
+
return result === Infinity ? 0 : result;
|
|
7859
|
+
}
|
|
7860
|
+
|
|
7861
|
+
const pivotTimeAdapterRegistry = new Registry();
|
|
7862
|
+
function pivotTimeAdapter(granularity) {
|
|
7863
|
+
return pivotTimeAdapterRegistry.get(granularity);
|
|
7864
|
+
}
|
|
7865
|
+
/**
|
|
7866
|
+
* The Time Adapter: Managing Time Periods for Pivot Functions
|
|
7867
|
+
*
|
|
7868
|
+
* Overview:
|
|
7869
|
+
* A time adapter is responsible for managing time periods associated with pivot functions.
|
|
7870
|
+
* Each type of period (day, week, month, quarter, etc.) has its own dedicated adapter.
|
|
7871
|
+
* The adapter's primary role is to normalize period values between spreadsheet functions,
|
|
7872
|
+
* and the pivot.
|
|
7873
|
+
* By normalizing the period value, it can be stored consistently in the pivot.
|
|
7874
|
+
*
|
|
7875
|
+
* Normalization Process:
|
|
7876
|
+
* When working with functions in the spreadsheet, the time adapter normalizes
|
|
7877
|
+
* the provided period to facilitate accurate lookup of values in the pivot.
|
|
7878
|
+
* For instance, if the spreadsheet function represents a day period as a number generated
|
|
7879
|
+
* by the DATE function (DATE(2023, 12, 25)), the time adapter will normalize it accordingly.
|
|
7880
|
+
*
|
|
7881
|
+
*/
|
|
7882
|
+
/**
|
|
7883
|
+
* Normalized value: "12/25/2023"
|
|
7884
|
+
*
|
|
7885
|
+
* Note: Those two format are equivalent:
|
|
7886
|
+
* - "MM/dd/yyyy" (luxon format)
|
|
7887
|
+
* - "mm/dd/yyyy" (spreadsheet format)
|
|
7888
|
+
**/
|
|
7889
|
+
const dayAdapter = {
|
|
7890
|
+
normalizeFunctionValue(value) {
|
|
7891
|
+
const date = toNumber(value, DEFAULT_LOCALE);
|
|
7892
|
+
return formatValue(date, { locale: DEFAULT_LOCALE, format: "mm/dd/yyyy" });
|
|
7893
|
+
},
|
|
7894
|
+
getFormat(locale) {
|
|
7895
|
+
return (locale ?? DEFAULT_LOCALE).dateFormat;
|
|
7896
|
+
},
|
|
7897
|
+
formatValue(normalizedValue, locale) {
|
|
7898
|
+
locale = locale ?? DEFAULT_LOCALE;
|
|
7899
|
+
const value = toNumber(normalizedValue, DEFAULT_LOCALE);
|
|
7900
|
+
return formatValue(value, { locale, format: this.getFormat(locale) });
|
|
7901
|
+
},
|
|
7902
|
+
toCellValue(normalizedValue) {
|
|
7903
|
+
return toNumber(normalizedValue, DEFAULT_LOCALE);
|
|
7904
|
+
},
|
|
7905
|
+
};
|
|
7906
|
+
/**
|
|
7907
|
+
* normalizes day of month number
|
|
7908
|
+
*/
|
|
7909
|
+
const dayOfMonthAdapter = {
|
|
7910
|
+
normalizeFunctionValue(value) {
|
|
7911
|
+
const day = toNumber(value, DEFAULT_LOCALE);
|
|
7912
|
+
if (day < 1 || day > 31) {
|
|
7913
|
+
throw new EvaluationError(_t("%s is not a valid day of month (it should be a number between 1 and 31)", day));
|
|
7914
|
+
}
|
|
7915
|
+
return day;
|
|
7916
|
+
},
|
|
7917
|
+
getFormat() {
|
|
7918
|
+
return "0";
|
|
7919
|
+
},
|
|
7920
|
+
formatValue(normalizedValue, locale) {
|
|
7921
|
+
locale = locale ?? DEFAULT_LOCALE;
|
|
7922
|
+
const value = toNumber(normalizedValue, DEFAULT_LOCALE);
|
|
7923
|
+
return formatValue(value, { locale, format: this.getFormat(locale) });
|
|
7924
|
+
},
|
|
7925
|
+
toCellValue(normalizedValue) {
|
|
7926
|
+
return toNumber(normalizedValue, DEFAULT_LOCALE);
|
|
7927
|
+
},
|
|
7928
|
+
};
|
|
7929
|
+
/**
|
|
7930
|
+
* Normalized value: "2/2023" for week 2 of 2023
|
|
7931
|
+
*/
|
|
7932
|
+
const weekAdapter = {
|
|
7933
|
+
normalizeFunctionValue(value) {
|
|
7934
|
+
const [week, year] = value.split("/");
|
|
7935
|
+
return `${Number(week)}/${Number(year)}`;
|
|
7936
|
+
},
|
|
7937
|
+
getFormat() {
|
|
7938
|
+
return undefined;
|
|
7939
|
+
},
|
|
7940
|
+
formatValue(normalizedValue) {
|
|
7941
|
+
const [week, year] = normalizedValue.split("/");
|
|
7942
|
+
return _t("W%(week)s %(year)s", { week, year });
|
|
7943
|
+
},
|
|
7944
|
+
toCellValue(normalizedValue) {
|
|
7945
|
+
return this.formatValue(normalizedValue);
|
|
7946
|
+
},
|
|
7947
|
+
};
|
|
7948
|
+
/**
|
|
7949
|
+
* normalizes iso week number
|
|
7950
|
+
*/
|
|
7951
|
+
const isoWeekNumberAdapter = {
|
|
7952
|
+
normalizeFunctionValue(value) {
|
|
7953
|
+
const isoWeek = toNumber(value, DEFAULT_LOCALE);
|
|
7954
|
+
if (isoWeek < 0 || isoWeek > 53) {
|
|
7955
|
+
throw new EvaluationError(_t("%s is not a valid week (it should be a number between 0 and 53)", isoWeek));
|
|
7956
|
+
}
|
|
7957
|
+
return isoWeek;
|
|
7958
|
+
},
|
|
7959
|
+
getFormat() {
|
|
7960
|
+
return "0";
|
|
7961
|
+
},
|
|
7962
|
+
formatValue(normalizedValue, locale) {
|
|
7963
|
+
locale = locale ?? DEFAULT_LOCALE;
|
|
7964
|
+
const value = toNumber(normalizedValue, DEFAULT_LOCALE);
|
|
7965
|
+
return formatValue(value, { locale, format: this.getFormat(locale) });
|
|
7966
|
+
},
|
|
7967
|
+
toCellValue(normalizedValue) {
|
|
7968
|
+
return toNumber(normalizedValue, DEFAULT_LOCALE);
|
|
7969
|
+
},
|
|
7970
|
+
};
|
|
7971
|
+
/**
|
|
7972
|
+
* normalized month value is a string formatted as "MM/yyyy" (luxon format)
|
|
7973
|
+
* e.g. "01/2020" for January 2020
|
|
7974
|
+
*/
|
|
7975
|
+
const monthAdapter = {
|
|
7976
|
+
normalizeFunctionValue(value) {
|
|
7977
|
+
const date = toNumber(value, DEFAULT_LOCALE);
|
|
7978
|
+
return formatValue(date, { locale: DEFAULT_LOCALE, format: "mm/yyyy" });
|
|
7979
|
+
},
|
|
7980
|
+
getFormat() {
|
|
7981
|
+
return "mmmm yyyy";
|
|
7982
|
+
},
|
|
7983
|
+
formatValue(normalizedValue, locale) {
|
|
7984
|
+
locale = locale ?? DEFAULT_LOCALE;
|
|
7985
|
+
const value = toNumber(normalizedValue, DEFAULT_LOCALE);
|
|
7986
|
+
return formatValue(value, { locale, format: this.getFormat(locale) });
|
|
7987
|
+
},
|
|
7988
|
+
toCellValue(normalizedValue) {
|
|
7989
|
+
return toNumber(normalizedValue, DEFAULT_LOCALE);
|
|
7990
|
+
},
|
|
7991
|
+
};
|
|
7992
|
+
/**
|
|
7993
|
+
* normalizes month number
|
|
7994
|
+
*/
|
|
7995
|
+
const monthNumberAdapter = {
|
|
7996
|
+
normalizeFunctionValue(value) {
|
|
7997
|
+
const month = toNumber(value, DEFAULT_LOCALE);
|
|
7998
|
+
if (month < 1 || month > 12) {
|
|
7999
|
+
throw new EvaluationError(_t("%s is not a valid month (it should be a number between 1 and 12)", month));
|
|
8000
|
+
}
|
|
8001
|
+
return month;
|
|
8002
|
+
},
|
|
8003
|
+
getFormat() {
|
|
8004
|
+
return "0";
|
|
8005
|
+
},
|
|
8006
|
+
formatValue(normalizedValue, locale) {
|
|
8007
|
+
locale = locale ?? DEFAULT_LOCALE;
|
|
8008
|
+
const value = toNumber(normalizedValue, DEFAULT_LOCALE);
|
|
8009
|
+
return formatValue(value, { locale, format: this.getFormat(locale) });
|
|
8010
|
+
},
|
|
8011
|
+
toCellValue(normalizedValue) {
|
|
8012
|
+
return MONTHS[toNumber(normalizedValue, DEFAULT_LOCALE) - 1].toString();
|
|
8013
|
+
},
|
|
8014
|
+
};
|
|
8015
|
+
/**
|
|
8016
|
+
* normalized quarter value is "quarter/year"
|
|
8017
|
+
* e.g. "1/2020" for Q1 2020
|
|
8018
|
+
*/
|
|
8019
|
+
const quarterAdapter = {
|
|
8020
|
+
normalizeFunctionValue(value) {
|
|
8021
|
+
const [quarter, year] = value.split("/");
|
|
8022
|
+
return `${quarter}/${year}`;
|
|
8023
|
+
},
|
|
8024
|
+
getFormat() {
|
|
8025
|
+
return undefined;
|
|
8026
|
+
},
|
|
8027
|
+
formatValue(normalizedValue) {
|
|
8028
|
+
const [quarter, year] = normalizedValue.split("/");
|
|
8029
|
+
return _t("Q%(quarter)s %(year)s", { quarter, year });
|
|
8030
|
+
},
|
|
8031
|
+
toCellValue(normalizedValue) {
|
|
8032
|
+
return this.formatValue(normalizedValue);
|
|
8033
|
+
},
|
|
8034
|
+
};
|
|
8035
|
+
/**
|
|
8036
|
+
* normalizes quarter number
|
|
8037
|
+
*/
|
|
8038
|
+
const quarterNumberAdapter = {
|
|
8039
|
+
normalizeFunctionValue(value) {
|
|
8040
|
+
const quarter = toNumber(value, DEFAULT_LOCALE);
|
|
8041
|
+
if (quarter < 1 || quarter > 4) {
|
|
8042
|
+
throw new EvaluationError(_t("%s is not a valid quarter (it should be a number between 1 and 4)", quarter));
|
|
8043
|
+
}
|
|
8044
|
+
return quarter;
|
|
8045
|
+
},
|
|
8046
|
+
getFormat() {
|
|
8047
|
+
return "0";
|
|
8048
|
+
},
|
|
8049
|
+
formatValue(normalizedValue, locale) {
|
|
8050
|
+
locale = locale ?? DEFAULT_LOCALE;
|
|
8051
|
+
const value = toNumber(normalizedValue, DEFAULT_LOCALE);
|
|
8052
|
+
return formatValue(value, { locale, format: this.getFormat(locale) });
|
|
8053
|
+
},
|
|
8054
|
+
toCellValue(normalizedValue) {
|
|
8055
|
+
return toNumber(normalizedValue, DEFAULT_LOCALE);
|
|
8056
|
+
},
|
|
8057
|
+
};
|
|
8058
|
+
const yearAdapter = {
|
|
8059
|
+
normalizeFunctionValue(value) {
|
|
8060
|
+
return toNumber(value, DEFAULT_LOCALE);
|
|
8061
|
+
},
|
|
8062
|
+
getFormat() {
|
|
8063
|
+
return "0";
|
|
8064
|
+
},
|
|
8065
|
+
formatValue(normalizedValue, locale) {
|
|
8066
|
+
locale = locale ?? DEFAULT_LOCALE;
|
|
8067
|
+
return formatValue(normalizedValue, { locale, format: "0" });
|
|
8068
|
+
},
|
|
8069
|
+
toCellValue(normalizedValue) {
|
|
8070
|
+
return toNumber(normalizedValue, DEFAULT_LOCALE);
|
|
8071
|
+
},
|
|
8072
|
+
};
|
|
8073
|
+
pivotTimeAdapterRegistry
|
|
8074
|
+
.add("day", dayAdapter)
|
|
8075
|
+
.add("week", weekAdapter)
|
|
8076
|
+
.add("month", monthAdapter)
|
|
8077
|
+
.add("quarter", quarterAdapter)
|
|
8078
|
+
.add("year", yearAdapter)
|
|
8079
|
+
.add("day_of_month", dayOfMonthAdapter)
|
|
8080
|
+
.add("iso_week_number", isoWeekNumberAdapter)
|
|
8081
|
+
.add("month_number", monthNumberAdapter)
|
|
8082
|
+
.add("quarter_number", quarterNumberAdapter)
|
|
8083
|
+
.add("year_number", yearAdapter);
|
|
8084
|
+
|
|
8085
|
+
const AGGREGATOR_NAMES = {
|
|
8086
|
+
count: _t("Count"),
|
|
8087
|
+
count_distinct: _t("Count Distinct"),
|
|
8088
|
+
bool_and: _t("Boolean And"),
|
|
8089
|
+
bool_or: _t("Boolean Or"),
|
|
8090
|
+
max: _t("Maximum"),
|
|
8091
|
+
min: _t("Minimum"),
|
|
8092
|
+
avg: _t("Average"),
|
|
8093
|
+
sum: _t("Sum"),
|
|
8094
|
+
};
|
|
8095
|
+
const NUMBER_CHAR_AGGREGATORS = ["max", "min", "avg", "sum", "count_distinct", "count"];
|
|
8096
|
+
const AGGREGATORS_BY_FIELD_TYPE = {
|
|
8097
|
+
integer: NUMBER_CHAR_AGGREGATORS,
|
|
8098
|
+
char: NUMBER_CHAR_AGGREGATORS,
|
|
8099
|
+
boolean: ["count_distinct", "count", "bool_and", "bool_or"],
|
|
8100
|
+
};
|
|
8101
|
+
const AGGREGATORS = {};
|
|
8102
|
+
for (const type in AGGREGATORS_BY_FIELD_TYPE) {
|
|
8103
|
+
AGGREGATORS[type] = {};
|
|
8104
|
+
for (const aggregator of AGGREGATORS_BY_FIELD_TYPE[type]) {
|
|
8105
|
+
AGGREGATORS[type][aggregator] = AGGREGATOR_NAMES[aggregator];
|
|
8106
|
+
}
|
|
8107
|
+
}
|
|
8108
|
+
const AGGREGATORS_FN = {
|
|
8109
|
+
count: {
|
|
8110
|
+
fn: (args) => countAny([args]),
|
|
8111
|
+
format: () => "0",
|
|
8112
|
+
},
|
|
8113
|
+
count_distinct: {
|
|
8114
|
+
fn: (args) => countUnique([args]),
|
|
8115
|
+
format: () => "0",
|
|
8116
|
+
},
|
|
8117
|
+
bool_and: {
|
|
8118
|
+
fn: (args) => boolAnd([args]).result,
|
|
8119
|
+
format: () => undefined,
|
|
8120
|
+
},
|
|
8121
|
+
bool_or: {
|
|
8122
|
+
fn: (args) => boolOr([args]).result,
|
|
8123
|
+
format: () => undefined,
|
|
8124
|
+
},
|
|
8125
|
+
max: {
|
|
8126
|
+
fn: (args, locale) => max([args], locale),
|
|
8127
|
+
format: inferFormat,
|
|
8128
|
+
},
|
|
8129
|
+
min: {
|
|
8130
|
+
fn: (args, locale) => min([args], locale),
|
|
8131
|
+
format: inferFormat,
|
|
8132
|
+
},
|
|
8133
|
+
avg: {
|
|
8134
|
+
fn: (args, locale) => average([args], locale),
|
|
8135
|
+
format: inferFormat,
|
|
8136
|
+
},
|
|
8137
|
+
sum: {
|
|
8138
|
+
fn: (args, locale) => sum([args], locale),
|
|
8139
|
+
format: inferFormat,
|
|
8140
|
+
},
|
|
8141
|
+
};
|
|
8142
|
+
function makePivotFormulaFromPivotCell(pivotFormulaId, pivotCell) {
|
|
8143
|
+
switch (pivotCell.type) {
|
|
8144
|
+
case "HEADER":
|
|
8145
|
+
return makePivotFormula("PIVOT.HEADER", [pivotFormulaId, ...flatPivotDomain(pivotCell.domain)].filter(isDefined));
|
|
8146
|
+
case "MEASURE_HEADER":
|
|
8147
|
+
return makePivotFormula("PIVOT.HEADER", [pivotFormulaId, ...flatPivotDomain(pivotCell.domain), "measure", pivotCell.measure].filter(isDefined));
|
|
8148
|
+
case "VALUE":
|
|
8149
|
+
return makePivotFormula("PIVOT.VALUE", [pivotFormulaId, pivotCell.measure, ...flatPivotDomain(pivotCell.domain)].filter(isDefined));
|
|
8150
|
+
case "EMPTY":
|
|
8151
|
+
return "";
|
|
8152
|
+
}
|
|
8153
|
+
}
|
|
8154
|
+
/**
|
|
8155
|
+
* Build a pivot formula expression
|
|
8156
|
+
*/
|
|
8157
|
+
function makePivotFormula(formula, args) {
|
|
8158
|
+
return `=${formula}(${args
|
|
8159
|
+
.map((arg) => {
|
|
8160
|
+
const stringIsNumber = typeof arg == "string" && !isNaN(Number(arg)) && Number(arg).toString() === arg;
|
|
8161
|
+
const convertToNumber = typeof arg == "number" || stringIsNumber;
|
|
8162
|
+
return convertToNumber ? `${arg}` : `"${arg.toString().replace(/"/g, '\\"')}"`;
|
|
8163
|
+
})
|
|
8164
|
+
.join(",")})`;
|
|
8165
|
+
}
|
|
8166
|
+
/**
|
|
8167
|
+
* Given an object of form {"1": {...}, "2": {...}, ...} get the maximum ID used
|
|
8168
|
+
* in this object
|
|
8169
|
+
* If the object has no keys, return 0
|
|
8170
|
+
*
|
|
8171
|
+
*/
|
|
8172
|
+
function getMaxObjectId(o) {
|
|
8173
|
+
const keys = Object.keys(o);
|
|
8174
|
+
if (!keys.length) {
|
|
8175
|
+
return 0;
|
|
8176
|
+
}
|
|
8177
|
+
const nums = keys.map((id) => parseInt(id, 10));
|
|
8178
|
+
const max = Math.max(...nums);
|
|
8179
|
+
return max;
|
|
8180
|
+
}
|
|
8181
|
+
const ALL_PERIODS = {
|
|
8182
|
+
year: _t("Year"),
|
|
8183
|
+
quarter: _t("Quarter"),
|
|
8184
|
+
month: _t("Month"),
|
|
8185
|
+
week: _t("Week"),
|
|
8186
|
+
day: _t("Day"),
|
|
8187
|
+
year_number: _t("Year"),
|
|
8188
|
+
quarter_number: _t("Quarter"),
|
|
8189
|
+
month_number: _t("Month"),
|
|
8190
|
+
iso_week_number: _t("Week"),
|
|
8191
|
+
day_of_month: _t("Day of Month"),
|
|
8192
|
+
};
|
|
8193
|
+
const DATE_FIELDS = ["date", "datetime"];
|
|
8194
|
+
/**
|
|
8195
|
+
* Parse a dimension string into a pivot dimension definition.
|
|
8196
|
+
* e.g "create_date:month" => { name: "create_date", granularity: "month" }
|
|
8197
|
+
*/
|
|
8198
|
+
function parseDimension(dimension) {
|
|
8199
|
+
const [name, granularity] = dimension.split(":");
|
|
8200
|
+
if (granularity) {
|
|
8201
|
+
return { name, granularity };
|
|
8202
|
+
}
|
|
8203
|
+
return { name };
|
|
8204
|
+
}
|
|
8205
|
+
function isDateField(field) {
|
|
8206
|
+
return DATE_FIELDS.includes(field.type);
|
|
8207
|
+
}
|
|
8208
|
+
function toPivotDomain(domainStr) {
|
|
8209
|
+
if (domainStr.length % 2 !== 0) {
|
|
8210
|
+
throw new Error("Invalid domain: odd number of elements");
|
|
8211
|
+
}
|
|
8212
|
+
const domain = [];
|
|
8213
|
+
for (let i = 0; i < domainStr.length - 1; i += 2) {
|
|
8214
|
+
domain.push({ field: domainStr[i], value: domainStr[i + 1] });
|
|
8215
|
+
}
|
|
8216
|
+
return domain;
|
|
8217
|
+
}
|
|
8218
|
+
function flatPivotDomain(domain) {
|
|
8219
|
+
return domain.flatMap((arg) => [arg.field, arg.value]);
|
|
8220
|
+
}
|
|
8221
|
+
/**
|
|
8222
|
+
* Parses the value defining a pivot group in a PIVOT formula
|
|
8223
|
+
* e.g. given the following formula PIVOT.VALUE("1", "stage_id", "42", "status", "won"),
|
|
8224
|
+
* the two group values are "42" and "won".
|
|
8225
|
+
*/
|
|
8226
|
+
function toNormalizedPivotValue(dimension, groupValue) {
|
|
8227
|
+
if (groupValue === null || groupValue === "null") {
|
|
8228
|
+
return null;
|
|
8229
|
+
}
|
|
8230
|
+
const groupValueString = typeof groupValue === "boolean"
|
|
8231
|
+
? toString(groupValue).toLocaleLowerCase()
|
|
8232
|
+
: toString(groupValue);
|
|
8233
|
+
if (!pivotNormalizationValueRegistry.contains(dimension.type)) {
|
|
8234
|
+
throw new EvaluationError(_t("Field %(field)s is not supported because of its type (%(type)s)", {
|
|
8235
|
+
field: dimension.displayName,
|
|
8236
|
+
type: dimension.type,
|
|
8237
|
+
}));
|
|
8238
|
+
}
|
|
8239
|
+
// represents a field which is not set (=False server side)
|
|
8240
|
+
if (groupValueString === "false") {
|
|
8241
|
+
return false;
|
|
8242
|
+
}
|
|
8243
|
+
const normalizer = pivotNormalizationValueRegistry.get(dimension.type);
|
|
8244
|
+
return normalizer(groupValueString, dimension.granularity);
|
|
8245
|
+
}
|
|
8246
|
+
function normalizeDateTime(value, granularity) {
|
|
8247
|
+
if (!granularity) {
|
|
8248
|
+
throw "";
|
|
8249
|
+
}
|
|
8250
|
+
return pivotTimeAdapter(granularity).normalizeFunctionValue(value);
|
|
8251
|
+
}
|
|
8252
|
+
const pivotNormalizationValueRegistry = new Registry();
|
|
8253
|
+
pivotNormalizationValueRegistry
|
|
8254
|
+
.add("date", normalizeDateTime)
|
|
8255
|
+
.add("datetime", normalizeDateTime)
|
|
8256
|
+
.add("integer", (value) => toNumber(value, DEFAULT_LOCALE))
|
|
8257
|
+
.add("boolean", (value) => toBoolean(value))
|
|
8258
|
+
.add("char", (value) => toString(value));
|
|
8259
|
+
|
|
7760
8260
|
/**
|
|
7761
8261
|
* Change the reference types inside the given token, if the token represent a range or a cell
|
|
7762
8262
|
*
|
|
@@ -7955,6 +8455,11 @@ const CellIsOperators = {
|
|
|
7955
8455
|
const ChartTerms = {
|
|
7956
8456
|
Series: _t("Series"),
|
|
7957
8457
|
BackgroundColor: _t("Background color"),
|
|
8458
|
+
StackedBarChart: _t("Stacked bar chart"),
|
|
8459
|
+
StackedLineChart: _t("Stacked line chart"),
|
|
8460
|
+
CumulativeData: _t("Cumulative data"),
|
|
8461
|
+
TreatLabelsAsText: _t("Treat labels as text"),
|
|
8462
|
+
AggregatedChart: _t("Aggregate"),
|
|
7958
8463
|
Errors: {
|
|
7959
8464
|
Unexpected: _t("The chart definition is invalid for an unknown reason"),
|
|
7960
8465
|
// BASIC CHART ERRORS (LINE | BAR | PIE)
|
|
@@ -8887,6 +9392,7 @@ function drawHighlight(renderingContext, highlight, rect) {
|
|
|
8887
9392
|
}
|
|
8888
9393
|
const color = highlight.color || HIGHLIGHT_COLOR;
|
|
8889
9394
|
const { ctx } = renderingContext;
|
|
9395
|
+
ctx.save();
|
|
8890
9396
|
if (!highlight.noBorder) {
|
|
8891
9397
|
if (highlight.dashed) {
|
|
8892
9398
|
ctx.setLineDash([5, 3]);
|
|
@@ -8906,6 +9412,7 @@ function drawHighlight(renderingContext, highlight, rect) {
|
|
|
8906
9412
|
ctx.fillStyle = setColorAlpha(toHex(color), highlight.fillAlpha ?? 0.12);
|
|
8907
9413
|
ctx.fillRect(x, y, width, height);
|
|
8908
9414
|
}
|
|
9415
|
+
ctx.restore();
|
|
8909
9416
|
}
|
|
8910
9417
|
|
|
8911
9418
|
class HighlightStore extends SpreadsheetStore {
|
|
@@ -9436,8 +9943,20 @@ class ComposerStore extends SpreadsheetStore {
|
|
|
9436
9943
|
replaceSelectedRange(zone) {
|
|
9437
9944
|
const ref = this.getZoneReference(zone);
|
|
9438
9945
|
const currentToken = this.tokenAtCursor;
|
|
9439
|
-
|
|
9440
|
-
|
|
9946
|
+
let replaceStart = this.selectionStart;
|
|
9947
|
+
if (currentToken?.type === "REFERENCE") {
|
|
9948
|
+
replaceStart = currentToken.start;
|
|
9949
|
+
}
|
|
9950
|
+
else if (currentToken?.type === "RIGHT_PAREN") {
|
|
9951
|
+
// match left parenthesis
|
|
9952
|
+
const leftParenthesisIndex = this.currentTokens.findIndex((token) => token.type === "LEFT_PAREN" && token.parenIndex === currentToken.parenIndex);
|
|
9953
|
+
const functionToken = this.currentTokens[leftParenthesisIndex - 1];
|
|
9954
|
+
if (functionToken === undefined) {
|
|
9955
|
+
return;
|
|
9956
|
+
}
|
|
9957
|
+
replaceStart = functionToken.start;
|
|
9958
|
+
}
|
|
9959
|
+
this.replaceText(ref, replaceStart, this.selectionEnd);
|
|
9441
9960
|
}
|
|
9442
9961
|
/**
|
|
9443
9962
|
* Replace the reference of the old zone by the new one.
|
|
@@ -9457,10 +9976,8 @@ class ComposerStore extends SpreadsheetStore {
|
|
|
9457
9976
|
const refRange = this.getters.getRangeFromSheetXC(activeSheetId, xc);
|
|
9458
9977
|
return isEqual(this.getters.expandZone(activeSheetId, refRange.zone), oldZone);
|
|
9459
9978
|
});
|
|
9460
|
-
// this function assumes that the previous range is always found because
|
|
9461
|
-
// it's called when changing a highlight, which exists by definition
|
|
9462
9979
|
if (!previousRefToken) {
|
|
9463
|
-
|
|
9980
|
+
return;
|
|
9464
9981
|
}
|
|
9465
9982
|
const previousRange = this.getters.getRangeFromSheetXC(activeSheetId, previousRefToken.value);
|
|
9466
9983
|
this.selectionStart = previousRefToken.start;
|
|
@@ -9472,6 +9989,17 @@ class ComposerStore extends SpreadsheetStore {
|
|
|
9472
9989
|
getZoneReference(zone) {
|
|
9473
9990
|
const inputSheetId = this.currentEditedCell.sheetId;
|
|
9474
9991
|
const sheetId = this.getters.getActiveSheetId();
|
|
9992
|
+
if (zone.top === zone.bottom && zone.left === zone.right) {
|
|
9993
|
+
const position = { sheetId, col: zone.left, row: zone.top };
|
|
9994
|
+
const pivotId = this.getters.getPivotIdFromPosition(position);
|
|
9995
|
+
const pivotCell = this.getters.getPivotCellFromPosition(position);
|
|
9996
|
+
const cell = this.getters.getCell(position);
|
|
9997
|
+
if (pivotId && pivotCell.type !== "EMPTY" && !cell?.isFormula) {
|
|
9998
|
+
const formulaPivotId = this.getters.getPivotFormulaId(pivotId);
|
|
9999
|
+
const formula = makePivotFormulaFromPivotCell(formulaPivotId, pivotCell);
|
|
10000
|
+
return formula.slice(1); // strip leading =
|
|
10001
|
+
}
|
|
10002
|
+
}
|
|
9475
10003
|
const range = this.getters.getRangeFromZone(sheetId, zone);
|
|
9476
10004
|
return this.getters.getSelectionRangeString(range, inputSheetId);
|
|
9477
10005
|
}
|
|
@@ -9574,19 +10102,35 @@ class ComposerStore extends SpreadsheetStore {
|
|
|
9574
10102
|
const colorIndex = this.colorIndexByRange[rangeString];
|
|
9575
10103
|
return colors$1[colorIndex % colors$1.length];
|
|
9576
10104
|
};
|
|
9577
|
-
|
|
10105
|
+
const highlights = [];
|
|
10106
|
+
for (const range of this.getReferencedRanges()) {
|
|
9578
10107
|
const rangeString = this.getters.getRangeString(range, editionSheetId);
|
|
9579
10108
|
const { numberOfRows, numberOfCols } = zoneToDimension(range.zone);
|
|
9580
10109
|
const zone = numberOfRows * numberOfCols === 1
|
|
9581
10110
|
? this.getters.expandZone(range.sheetId, range.zone)
|
|
9582
10111
|
: range.zone;
|
|
9583
|
-
|
|
10112
|
+
highlights.push({
|
|
9584
10113
|
zone,
|
|
9585
10114
|
color: rangeColor(rangeString),
|
|
9586
10115
|
sheetId: range.sheetId,
|
|
9587
10116
|
interactive: true,
|
|
9588
|
-
};
|
|
9589
|
-
}
|
|
10117
|
+
});
|
|
10118
|
+
}
|
|
10119
|
+
const activeSheetId = this.getters.getActiveSheetId();
|
|
10120
|
+
const selectionZone = this.model.selection.getAnchor().zone;
|
|
10121
|
+
const isSelectionHightlighted = highlights.find((highlight) => highlight.sheetId === activeSheetId && isEqual(highlight.zone, selectionZone));
|
|
10122
|
+
if (this.editionMode === "selecting" && !isSelectionHightlighted) {
|
|
10123
|
+
highlights.push({
|
|
10124
|
+
zone: selectionZone,
|
|
10125
|
+
color: "#445566",
|
|
10126
|
+
sheetId: activeSheetId,
|
|
10127
|
+
dashed: true,
|
|
10128
|
+
interactive: false,
|
|
10129
|
+
noFill: true,
|
|
10130
|
+
thinLine: true,
|
|
10131
|
+
});
|
|
10132
|
+
}
|
|
10133
|
+
return highlights;
|
|
9590
10134
|
}
|
|
9591
10135
|
/**
|
|
9592
10136
|
* Return ranges currently referenced in the composer
|
|
@@ -9845,8 +10389,14 @@ class ChartJsComponent extends owl.Component {
|
|
|
9845
10389
|
owl.useEffect(() => {
|
|
9846
10390
|
const runtime = this.chartRuntime;
|
|
9847
10391
|
if (!deepEquals(runtime, this.currentRuntime, "ignoreFunctions")) {
|
|
10392
|
+
if (runtime.chartJsConfig.type !== this.currentRuntime.chartJsConfig.type) {
|
|
10393
|
+
this.chart?.destroy();
|
|
10394
|
+
this.createChart(deepCopy(runtime.chartJsConfig));
|
|
10395
|
+
}
|
|
10396
|
+
else {
|
|
10397
|
+
this.updateChartJs(deepCopy(runtime));
|
|
10398
|
+
}
|
|
9848
10399
|
this.currentRuntime = runtime;
|
|
9849
|
-
this.updateChartJs(deepCopy(runtime));
|
|
9850
10400
|
}
|
|
9851
10401
|
});
|
|
9852
10402
|
}
|
|
@@ -10214,6 +10764,9 @@ function getChartAxisTitleRuntime(design) {
|
|
|
10214
10764
|
}
|
|
10215
10765
|
function getDefinedAxis(definition) {
|
|
10216
10766
|
let useLeftAxis = false, useRightAxis = false;
|
|
10767
|
+
if ("horizontal" in definition && definition.horizontal) {
|
|
10768
|
+
return { useLeftAxis: true, useRightAxis: false };
|
|
10769
|
+
}
|
|
10217
10770
|
for (const design of definition.dataSets || []) {
|
|
10218
10771
|
if (design.yAxisId === "y1") {
|
|
10219
10772
|
useRightAxis = true;
|
|
@@ -11781,13 +12334,6 @@ var misc = /*#__PURE__*/Object.freeze({
|
|
|
11781
12334
|
FORMAT_LARGE_NUMBER: FORMAT_LARGE_NUMBER
|
|
11782
12335
|
});
|
|
11783
12336
|
|
|
11784
|
-
function sum(values, locale) {
|
|
11785
|
-
return reduceNumbers(values, (acc, a) => acc + a, 0, locale);
|
|
11786
|
-
}
|
|
11787
|
-
function countUnique(args) {
|
|
11788
|
-
return reduceAny(args, (acc, a) => (isDataNonEmpty(a) ? acc.add(a?.value) : acc), new Set()).size;
|
|
11789
|
-
}
|
|
11790
|
-
|
|
11791
12337
|
const DEFAULT_FACTOR = 1;
|
|
11792
12338
|
const DEFAULT_MODE = 0;
|
|
11793
12339
|
const DEFAULT_PLACES = 0;
|
|
@@ -12901,53 +13447,6 @@ var math = /*#__PURE__*/Object.freeze({
|
|
|
12901
13447
|
TRUNC: TRUNC
|
|
12902
13448
|
});
|
|
12903
13449
|
|
|
12904
|
-
function assertSameNumberOfElements(...args) {
|
|
12905
|
-
const dims = args[0].length;
|
|
12906
|
-
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())));
|
|
12907
|
-
}
|
|
12908
|
-
function average(values, locale) {
|
|
12909
|
-
let count = 0;
|
|
12910
|
-
const sum = reduceNumbers(values, (acc, a) => {
|
|
12911
|
-
count += 1;
|
|
12912
|
-
return acc + a;
|
|
12913
|
-
}, 0, locale);
|
|
12914
|
-
assertNotZero(count);
|
|
12915
|
-
return sum / count;
|
|
12916
|
-
}
|
|
12917
|
-
function countNumbers(values, locale) {
|
|
12918
|
-
let count = 0;
|
|
12919
|
-
for (let n of values) {
|
|
12920
|
-
if (isMatrix(n)) {
|
|
12921
|
-
for (let i of n) {
|
|
12922
|
-
for (let j of i) {
|
|
12923
|
-
if (typeof j.value === "number") {
|
|
12924
|
-
count += 1;
|
|
12925
|
-
}
|
|
12926
|
-
}
|
|
12927
|
-
}
|
|
12928
|
-
}
|
|
12929
|
-
else {
|
|
12930
|
-
const value = n?.value;
|
|
12931
|
-
if (!isEvaluationError(value) &&
|
|
12932
|
-
(typeof value !== "string" || isNumber(value, locale) || parseDateTime(value, locale))) {
|
|
12933
|
-
count += 1;
|
|
12934
|
-
}
|
|
12935
|
-
}
|
|
12936
|
-
}
|
|
12937
|
-
return count;
|
|
12938
|
-
}
|
|
12939
|
-
function countAny(values) {
|
|
12940
|
-
return reduceAny(values, (acc, a) => (a !== undefined && a.value !== null ? acc + 1 : acc), 0);
|
|
12941
|
-
}
|
|
12942
|
-
function max(values, locale) {
|
|
12943
|
-
const result = reduceNumbers(values, (acc, a) => (acc < a ? a : acc), -Infinity, locale);
|
|
12944
|
-
return result === -Infinity ? 0 : result;
|
|
12945
|
-
}
|
|
12946
|
-
function min(values, locale) {
|
|
12947
|
-
const result = reduceNumbers(values, (acc, a) => (a < acc ? a : acc), Infinity, locale);
|
|
12948
|
-
return result === Infinity ? 0 : result;
|
|
12949
|
-
}
|
|
12950
|
-
|
|
12951
13450
|
function filterAndFlatData(dataY, dataX) {
|
|
12952
13451
|
const _flatDataY = [];
|
|
12953
13452
|
const _flatDataX = [];
|
|
@@ -13503,7 +14002,7 @@ const LARGE = {
|
|
|
13503
14002
|
// LINEST
|
|
13504
14003
|
// -----------------------------------------------------------------------------
|
|
13505
14004
|
const LINEST = {
|
|
13506
|
-
description: _t("
|
|
14005
|
+
description: _t("Given partial data about a linear trend, calculates various parameters about the ideal linear trend using the least-squares method."),
|
|
13507
14006
|
args: [
|
|
13508
14007
|
arg("data_y (range<number>)", _t("The range representing the array or matrix of dependent data.")),
|
|
13509
14008
|
arg("data_x (range<number>, default={1;2;3;...})", _t("The range representing the array or matrix of independent data.")),
|
|
@@ -13519,7 +14018,7 @@ const LINEST = {
|
|
|
13519
14018
|
// LOGEST
|
|
13520
14019
|
// -----------------------------------------------------------------------------
|
|
13521
14020
|
const LOGEST = {
|
|
13522
|
-
description: _t("
|
|
14021
|
+
description: _t("Given partial data about an exponential growth curve, calculates various parameters about the best fit ideal exponential growth curve."),
|
|
13523
14022
|
args: [
|
|
13524
14023
|
arg("data_y (range<number>)", _t("The range representing the array or matrix of dependent data.")),
|
|
13525
14024
|
arg("data_x (range<number>, optional, default={1;2;3;...})", _t("The range representing the array or matrix of independent data.")),
|
|
@@ -17922,33 +18421,6 @@ var info = /*#__PURE__*/Object.freeze({
|
|
|
17922
18421
|
NA: NA
|
|
17923
18422
|
});
|
|
17924
18423
|
|
|
17925
|
-
function boolAnd(args) {
|
|
17926
|
-
let foundBoolean = false;
|
|
17927
|
-
let acc = true;
|
|
17928
|
-
conditionalVisitBoolean(args, (arg) => {
|
|
17929
|
-
foundBoolean = true;
|
|
17930
|
-
acc = acc && arg;
|
|
17931
|
-
return acc;
|
|
17932
|
-
});
|
|
17933
|
-
return {
|
|
17934
|
-
foundBoolean,
|
|
17935
|
-
result: acc,
|
|
17936
|
-
};
|
|
17937
|
-
}
|
|
17938
|
-
function boolOr(args) {
|
|
17939
|
-
let foundBoolean = false;
|
|
17940
|
-
let acc = false;
|
|
17941
|
-
conditionalVisitBoolean(args, (arg) => {
|
|
17942
|
-
foundBoolean = true;
|
|
17943
|
-
acc = acc || arg;
|
|
17944
|
-
return !acc;
|
|
17945
|
-
});
|
|
17946
|
-
return {
|
|
17947
|
-
foundBoolean,
|
|
17948
|
-
result: acc,
|
|
17949
|
-
};
|
|
17950
|
-
}
|
|
17951
|
-
|
|
17952
18424
|
// -----------------------------------------------------------------------------
|
|
17953
18425
|
// AND
|
|
17954
18426
|
// -----------------------------------------------------------------------------
|
|
@@ -18146,393 +18618,6 @@ var logical = /*#__PURE__*/Object.freeze({
|
|
|
18146
18618
|
XOR: XOR
|
|
18147
18619
|
});
|
|
18148
18620
|
|
|
18149
|
-
const pivotTimeAdapterRegistry = new Registry();
|
|
18150
|
-
function pivotTimeAdapter(granularity) {
|
|
18151
|
-
return pivotTimeAdapterRegistry.get(granularity);
|
|
18152
|
-
}
|
|
18153
|
-
/**
|
|
18154
|
-
* The Time Adapter: Managing Time Periods for Pivot Functions
|
|
18155
|
-
*
|
|
18156
|
-
* Overview:
|
|
18157
|
-
* A time adapter is responsible for managing time periods associated with pivot functions.
|
|
18158
|
-
* Each type of period (day, week, month, quarter, etc.) has its own dedicated adapter.
|
|
18159
|
-
* The adapter's primary role is to normalize period values between spreadsheet functions,
|
|
18160
|
-
* and the pivot.
|
|
18161
|
-
* By normalizing the period value, it can be stored consistently in the pivot.
|
|
18162
|
-
*
|
|
18163
|
-
* Normalization Process:
|
|
18164
|
-
* When working with functions in the spreadsheet, the time adapter normalizes
|
|
18165
|
-
* the provided period to facilitate accurate lookup of values in the pivot.
|
|
18166
|
-
* For instance, if the spreadsheet function represents a day period as a number generated
|
|
18167
|
-
* by the DATE function (DATE(2023, 12, 25)), the time adapter will normalize it accordingly.
|
|
18168
|
-
*
|
|
18169
|
-
*/
|
|
18170
|
-
/**
|
|
18171
|
-
* Normalized value: "12/25/2023"
|
|
18172
|
-
*
|
|
18173
|
-
* Note: Those two format are equivalent:
|
|
18174
|
-
* - "MM/dd/yyyy" (luxon format)
|
|
18175
|
-
* - "mm/dd/yyyy" (spreadsheet format)
|
|
18176
|
-
**/
|
|
18177
|
-
const dayAdapter = {
|
|
18178
|
-
normalizeFunctionValue(value) {
|
|
18179
|
-
const date = toNumber(value, DEFAULT_LOCALE);
|
|
18180
|
-
return formatValue(date, { locale: DEFAULT_LOCALE, format: "mm/dd/yyyy" });
|
|
18181
|
-
},
|
|
18182
|
-
getFormat(locale) {
|
|
18183
|
-
return (locale ?? DEFAULT_LOCALE).dateFormat;
|
|
18184
|
-
},
|
|
18185
|
-
formatValue(normalizedValue, locale) {
|
|
18186
|
-
locale = locale ?? DEFAULT_LOCALE;
|
|
18187
|
-
const value = toNumber(normalizedValue, DEFAULT_LOCALE);
|
|
18188
|
-
return formatValue(value, { locale, format: this.getFormat(locale) });
|
|
18189
|
-
},
|
|
18190
|
-
toCellValue(normalizedValue) {
|
|
18191
|
-
return toNumber(normalizedValue, DEFAULT_LOCALE);
|
|
18192
|
-
},
|
|
18193
|
-
};
|
|
18194
|
-
/**
|
|
18195
|
-
* normalizes day of month number
|
|
18196
|
-
*/
|
|
18197
|
-
const dayOfMonthAdapter = {
|
|
18198
|
-
normalizeFunctionValue(value) {
|
|
18199
|
-
const day = toNumber(value, DEFAULT_LOCALE);
|
|
18200
|
-
if (day < 1 || day > 31) {
|
|
18201
|
-
throw new EvaluationError(_t("%s is not a valid day of month (it should be a number between 1 and 31)", day));
|
|
18202
|
-
}
|
|
18203
|
-
return day;
|
|
18204
|
-
},
|
|
18205
|
-
getFormat() {
|
|
18206
|
-
return "0";
|
|
18207
|
-
},
|
|
18208
|
-
formatValue(normalizedValue, locale) {
|
|
18209
|
-
locale = locale ?? DEFAULT_LOCALE;
|
|
18210
|
-
const value = toNumber(normalizedValue, DEFAULT_LOCALE);
|
|
18211
|
-
return formatValue(value, { locale, format: this.getFormat(locale) });
|
|
18212
|
-
},
|
|
18213
|
-
toCellValue(normalizedValue) {
|
|
18214
|
-
return toNumber(normalizedValue, DEFAULT_LOCALE);
|
|
18215
|
-
},
|
|
18216
|
-
};
|
|
18217
|
-
/**
|
|
18218
|
-
* Normalized value: "2/2023" for week 2 of 2023
|
|
18219
|
-
*/
|
|
18220
|
-
const weekAdapter = {
|
|
18221
|
-
normalizeFunctionValue(value) {
|
|
18222
|
-
const [week, year] = value.split("/");
|
|
18223
|
-
return `${Number(week)}/${Number(year)}`;
|
|
18224
|
-
},
|
|
18225
|
-
getFormat() {
|
|
18226
|
-
return undefined;
|
|
18227
|
-
},
|
|
18228
|
-
formatValue(normalizedValue) {
|
|
18229
|
-
const [week, year] = normalizedValue.split("/");
|
|
18230
|
-
return _t("W%(week)s %(year)s", { week, year });
|
|
18231
|
-
},
|
|
18232
|
-
toCellValue(normalizedValue) {
|
|
18233
|
-
return this.formatValue(normalizedValue);
|
|
18234
|
-
},
|
|
18235
|
-
};
|
|
18236
|
-
/**
|
|
18237
|
-
* normalizes iso week number
|
|
18238
|
-
*/
|
|
18239
|
-
const isoWeekNumberAdapter = {
|
|
18240
|
-
normalizeFunctionValue(value) {
|
|
18241
|
-
const isoWeek = toNumber(value, DEFAULT_LOCALE);
|
|
18242
|
-
if (isoWeek < 0 || isoWeek > 53) {
|
|
18243
|
-
throw new EvaluationError(_t("%s is not a valid week (it should be a number between 0 and 53)", isoWeek));
|
|
18244
|
-
}
|
|
18245
|
-
return isoWeek;
|
|
18246
|
-
},
|
|
18247
|
-
getFormat() {
|
|
18248
|
-
return "0";
|
|
18249
|
-
},
|
|
18250
|
-
formatValue(normalizedValue, locale) {
|
|
18251
|
-
locale = locale ?? DEFAULT_LOCALE;
|
|
18252
|
-
const value = toNumber(normalizedValue, DEFAULT_LOCALE);
|
|
18253
|
-
return formatValue(value, { locale, format: this.getFormat(locale) });
|
|
18254
|
-
},
|
|
18255
|
-
toCellValue(normalizedValue) {
|
|
18256
|
-
return toNumber(normalizedValue, DEFAULT_LOCALE);
|
|
18257
|
-
},
|
|
18258
|
-
};
|
|
18259
|
-
/**
|
|
18260
|
-
* normalized month value is a string formatted as "MM/yyyy" (luxon format)
|
|
18261
|
-
* e.g. "01/2020" for January 2020
|
|
18262
|
-
*/
|
|
18263
|
-
const monthAdapter = {
|
|
18264
|
-
normalizeFunctionValue(value) {
|
|
18265
|
-
const date = toNumber(value, DEFAULT_LOCALE);
|
|
18266
|
-
return formatValue(date, { locale: DEFAULT_LOCALE, format: "mm/yyyy" });
|
|
18267
|
-
},
|
|
18268
|
-
getFormat() {
|
|
18269
|
-
return "mmmm yyyy";
|
|
18270
|
-
},
|
|
18271
|
-
formatValue(normalizedValue, locale) {
|
|
18272
|
-
locale = locale ?? DEFAULT_LOCALE;
|
|
18273
|
-
const value = toNumber(normalizedValue, DEFAULT_LOCALE);
|
|
18274
|
-
return formatValue(value, { locale, format: this.getFormat(locale) });
|
|
18275
|
-
},
|
|
18276
|
-
toCellValue(normalizedValue) {
|
|
18277
|
-
return toNumber(normalizedValue, DEFAULT_LOCALE);
|
|
18278
|
-
},
|
|
18279
|
-
};
|
|
18280
|
-
/**
|
|
18281
|
-
* normalizes month number
|
|
18282
|
-
*/
|
|
18283
|
-
const monthNumberAdapter = {
|
|
18284
|
-
normalizeFunctionValue(value) {
|
|
18285
|
-
const month = toNumber(value, DEFAULT_LOCALE);
|
|
18286
|
-
if (month < 1 || month > 12) {
|
|
18287
|
-
throw new EvaluationError(_t("%s is not a valid month (it should be a number between 1 and 12)", month));
|
|
18288
|
-
}
|
|
18289
|
-
return month;
|
|
18290
|
-
},
|
|
18291
|
-
getFormat() {
|
|
18292
|
-
return "0";
|
|
18293
|
-
},
|
|
18294
|
-
formatValue(normalizedValue, locale) {
|
|
18295
|
-
locale = locale ?? DEFAULT_LOCALE;
|
|
18296
|
-
const value = toNumber(normalizedValue, DEFAULT_LOCALE);
|
|
18297
|
-
return formatValue(value, { locale, format: this.getFormat(locale) });
|
|
18298
|
-
},
|
|
18299
|
-
toCellValue(normalizedValue) {
|
|
18300
|
-
return MONTHS[toNumber(normalizedValue, DEFAULT_LOCALE) - 1].toString();
|
|
18301
|
-
},
|
|
18302
|
-
};
|
|
18303
|
-
/**
|
|
18304
|
-
* normalized quarter value is "quarter/year"
|
|
18305
|
-
* e.g. "1/2020" for Q1 2020
|
|
18306
|
-
*/
|
|
18307
|
-
const quarterAdapter = {
|
|
18308
|
-
normalizeFunctionValue(value) {
|
|
18309
|
-
const [quarter, year] = value.split("/");
|
|
18310
|
-
return `${quarter}/${year}`;
|
|
18311
|
-
},
|
|
18312
|
-
getFormat() {
|
|
18313
|
-
return undefined;
|
|
18314
|
-
},
|
|
18315
|
-
formatValue(normalizedValue) {
|
|
18316
|
-
const [quarter, year] = normalizedValue.split("/");
|
|
18317
|
-
return _t("Q%(quarter)s %(year)s", { quarter, year });
|
|
18318
|
-
},
|
|
18319
|
-
toCellValue(normalizedValue) {
|
|
18320
|
-
return this.formatValue(normalizedValue);
|
|
18321
|
-
},
|
|
18322
|
-
};
|
|
18323
|
-
/**
|
|
18324
|
-
* normalizes quarter number
|
|
18325
|
-
*/
|
|
18326
|
-
const quarterNumberAdapter = {
|
|
18327
|
-
normalizeFunctionValue(value) {
|
|
18328
|
-
const quarter = toNumber(value, DEFAULT_LOCALE);
|
|
18329
|
-
if (quarter < 1 || quarter > 4) {
|
|
18330
|
-
throw new EvaluationError(_t("%s is not a valid quarter (it should be a number between 1 and 4)", quarter));
|
|
18331
|
-
}
|
|
18332
|
-
return quarter;
|
|
18333
|
-
},
|
|
18334
|
-
getFormat() {
|
|
18335
|
-
return "0";
|
|
18336
|
-
},
|
|
18337
|
-
formatValue(normalizedValue, locale) {
|
|
18338
|
-
locale = locale ?? DEFAULT_LOCALE;
|
|
18339
|
-
const value = toNumber(normalizedValue, DEFAULT_LOCALE);
|
|
18340
|
-
return formatValue(value, { locale, format: this.getFormat(locale) });
|
|
18341
|
-
},
|
|
18342
|
-
toCellValue(normalizedValue) {
|
|
18343
|
-
return toNumber(normalizedValue, DEFAULT_LOCALE);
|
|
18344
|
-
},
|
|
18345
|
-
};
|
|
18346
|
-
const yearAdapter = {
|
|
18347
|
-
normalizeFunctionValue(value) {
|
|
18348
|
-
return toNumber(value, DEFAULT_LOCALE);
|
|
18349
|
-
},
|
|
18350
|
-
getFormat() {
|
|
18351
|
-
return "0";
|
|
18352
|
-
},
|
|
18353
|
-
formatValue(normalizedValue, locale) {
|
|
18354
|
-
locale = locale ?? DEFAULT_LOCALE;
|
|
18355
|
-
return formatValue(normalizedValue, { locale, format: "0" });
|
|
18356
|
-
},
|
|
18357
|
-
toCellValue(normalizedValue) {
|
|
18358
|
-
return toNumber(normalizedValue, DEFAULT_LOCALE);
|
|
18359
|
-
},
|
|
18360
|
-
};
|
|
18361
|
-
pivotTimeAdapterRegistry
|
|
18362
|
-
.add("day", dayAdapter)
|
|
18363
|
-
.add("week", weekAdapter)
|
|
18364
|
-
.add("month", monthAdapter)
|
|
18365
|
-
.add("quarter", quarterAdapter)
|
|
18366
|
-
.add("year", yearAdapter)
|
|
18367
|
-
.add("day_of_month", dayOfMonthAdapter)
|
|
18368
|
-
.add("iso_week_number", isoWeekNumberAdapter)
|
|
18369
|
-
.add("month_number", monthNumberAdapter)
|
|
18370
|
-
.add("quarter_number", quarterNumberAdapter)
|
|
18371
|
-
.add("year_number", yearAdapter);
|
|
18372
|
-
|
|
18373
|
-
const AGGREGATOR_NAMES = {
|
|
18374
|
-
count: _t("Count"),
|
|
18375
|
-
count_distinct: _t("Count Distinct"),
|
|
18376
|
-
bool_and: _t("Boolean And"),
|
|
18377
|
-
bool_or: _t("Boolean Or"),
|
|
18378
|
-
max: _t("Maximum"),
|
|
18379
|
-
min: _t("Minimum"),
|
|
18380
|
-
avg: _t("Average"),
|
|
18381
|
-
sum: _t("Sum"),
|
|
18382
|
-
};
|
|
18383
|
-
const NUMBER_CHAR_AGGREGATORS = ["max", "min", "avg", "sum", "count_distinct", "count"];
|
|
18384
|
-
const AGGREGATORS_BY_FIELD_TYPE = {
|
|
18385
|
-
integer: NUMBER_CHAR_AGGREGATORS,
|
|
18386
|
-
char: NUMBER_CHAR_AGGREGATORS,
|
|
18387
|
-
boolean: ["count_distinct", "count", "bool_and", "bool_or"],
|
|
18388
|
-
};
|
|
18389
|
-
const AGGREGATORS = {};
|
|
18390
|
-
for (const type in AGGREGATORS_BY_FIELD_TYPE) {
|
|
18391
|
-
AGGREGATORS[type] = {};
|
|
18392
|
-
for (const aggregator of AGGREGATORS_BY_FIELD_TYPE[type]) {
|
|
18393
|
-
AGGREGATORS[type][aggregator] = AGGREGATOR_NAMES[aggregator];
|
|
18394
|
-
}
|
|
18395
|
-
}
|
|
18396
|
-
const AGGREGATORS_FN = {
|
|
18397
|
-
count: {
|
|
18398
|
-
fn: (args) => countAny([args]),
|
|
18399
|
-
format: () => "0",
|
|
18400
|
-
},
|
|
18401
|
-
count_distinct: {
|
|
18402
|
-
fn: (args) => countUnique([args]),
|
|
18403
|
-
format: () => "0",
|
|
18404
|
-
},
|
|
18405
|
-
bool_and: {
|
|
18406
|
-
fn: (args) => boolAnd([args]).result,
|
|
18407
|
-
format: () => undefined,
|
|
18408
|
-
},
|
|
18409
|
-
bool_or: {
|
|
18410
|
-
fn: (args) => boolOr([args]).result,
|
|
18411
|
-
format: () => undefined,
|
|
18412
|
-
},
|
|
18413
|
-
max: {
|
|
18414
|
-
fn: (args, locale) => max([args], locale),
|
|
18415
|
-
format: inferFormat,
|
|
18416
|
-
},
|
|
18417
|
-
min: {
|
|
18418
|
-
fn: (args, locale) => min([args], locale),
|
|
18419
|
-
format: inferFormat,
|
|
18420
|
-
},
|
|
18421
|
-
avg: {
|
|
18422
|
-
fn: (args, locale) => average([args], locale),
|
|
18423
|
-
format: inferFormat,
|
|
18424
|
-
},
|
|
18425
|
-
sum: {
|
|
18426
|
-
fn: (args, locale) => sum([args], locale),
|
|
18427
|
-
format: inferFormat,
|
|
18428
|
-
},
|
|
18429
|
-
};
|
|
18430
|
-
/**
|
|
18431
|
-
* Build a pivot formula expression
|
|
18432
|
-
*/
|
|
18433
|
-
function makePivotFormula(formula, args) {
|
|
18434
|
-
return `=${formula}(${args
|
|
18435
|
-
.map((arg) => {
|
|
18436
|
-
const stringIsNumber = typeof arg == "string" && !isNaN(Number(arg)) && Number(arg).toString() === arg;
|
|
18437
|
-
const convertToNumber = typeof arg == "number" || stringIsNumber;
|
|
18438
|
-
return convertToNumber ? `${arg}` : `"${arg.toString().replace(/"/g, '\\"')}"`;
|
|
18439
|
-
})
|
|
18440
|
-
.join(",")})`;
|
|
18441
|
-
}
|
|
18442
|
-
/**
|
|
18443
|
-
* Given an object of form {"1": {...}, "2": {...}, ...} get the maximum ID used
|
|
18444
|
-
* in this object
|
|
18445
|
-
* If the object has no keys, return 0
|
|
18446
|
-
*
|
|
18447
|
-
*/
|
|
18448
|
-
function getMaxObjectId(o) {
|
|
18449
|
-
const keys = Object.keys(o);
|
|
18450
|
-
if (!keys.length) {
|
|
18451
|
-
return 0;
|
|
18452
|
-
}
|
|
18453
|
-
const nums = keys.map((id) => parseInt(id, 10));
|
|
18454
|
-
const max = Math.max(...nums);
|
|
18455
|
-
return max;
|
|
18456
|
-
}
|
|
18457
|
-
const ALL_PERIODS = {
|
|
18458
|
-
year: _t("Year"),
|
|
18459
|
-
quarter: _t("Quarter"),
|
|
18460
|
-
month: _t("Month"),
|
|
18461
|
-
week: _t("Week"),
|
|
18462
|
-
day: _t("Day"),
|
|
18463
|
-
year_number: _t("Year"),
|
|
18464
|
-
quarter_number: _t("Quarter"),
|
|
18465
|
-
month_number: _t("Month"),
|
|
18466
|
-
iso_week_number: _t("Week"),
|
|
18467
|
-
day_of_month: _t("Day of Month"),
|
|
18468
|
-
};
|
|
18469
|
-
const DATE_FIELDS = ["date", "datetime"];
|
|
18470
|
-
/**
|
|
18471
|
-
* Parse a dimension string into a pivot dimension definition.
|
|
18472
|
-
* e.g "create_date:month" => { name: "create_date", granularity: "month" }
|
|
18473
|
-
*/
|
|
18474
|
-
function parseDimension(dimension) {
|
|
18475
|
-
const [name, granularity] = dimension.split(":");
|
|
18476
|
-
if (granularity) {
|
|
18477
|
-
return { name, granularity };
|
|
18478
|
-
}
|
|
18479
|
-
return { name };
|
|
18480
|
-
}
|
|
18481
|
-
function isDateField(field) {
|
|
18482
|
-
return DATE_FIELDS.includes(field.type);
|
|
18483
|
-
}
|
|
18484
|
-
function toPivotDomain(domainStr) {
|
|
18485
|
-
if (domainStr.length % 2 !== 0) {
|
|
18486
|
-
throw new Error("Invalid domain: odd number of elements");
|
|
18487
|
-
}
|
|
18488
|
-
const domain = [];
|
|
18489
|
-
for (let i = 0; i < domainStr.length - 1; i += 2) {
|
|
18490
|
-
domain.push({ field: domainStr[i], value: domainStr[i + 1] });
|
|
18491
|
-
}
|
|
18492
|
-
return domain;
|
|
18493
|
-
}
|
|
18494
|
-
function flatPivotDomain(domain) {
|
|
18495
|
-
return domain.flatMap((arg) => [arg.field, arg.value]);
|
|
18496
|
-
}
|
|
18497
|
-
/**
|
|
18498
|
-
* Parses the value defining a pivot group in a PIVOT formula
|
|
18499
|
-
* e.g. given the following formula PIVOT.VALUE("1", "stage_id", "42", "status", "won"),
|
|
18500
|
-
* the two group values are "42" and "won".
|
|
18501
|
-
*/
|
|
18502
|
-
function toNormalizedPivotValue(dimension, groupValue) {
|
|
18503
|
-
if (groupValue === null || groupValue === "null") {
|
|
18504
|
-
return null;
|
|
18505
|
-
}
|
|
18506
|
-
const groupValueString = typeof groupValue === "boolean"
|
|
18507
|
-
? toString(groupValue).toLocaleLowerCase()
|
|
18508
|
-
: toString(groupValue);
|
|
18509
|
-
if (!pivotNormalizationValueRegistry.contains(dimension.type)) {
|
|
18510
|
-
throw new EvaluationError(_t("Field %(field)s is not supported because of its type (%(type)s)", {
|
|
18511
|
-
field: dimension.displayName,
|
|
18512
|
-
type: dimension.type,
|
|
18513
|
-
}));
|
|
18514
|
-
}
|
|
18515
|
-
// represents a field which is not set (=False server side)
|
|
18516
|
-
if (groupValueString === "false") {
|
|
18517
|
-
return false;
|
|
18518
|
-
}
|
|
18519
|
-
const normalizer = pivotNormalizationValueRegistry.get(dimension.type);
|
|
18520
|
-
return normalizer(groupValueString, dimension.granularity);
|
|
18521
|
-
}
|
|
18522
|
-
function normalizeDateTime(value, granularity) {
|
|
18523
|
-
if (!granularity) {
|
|
18524
|
-
throw "";
|
|
18525
|
-
}
|
|
18526
|
-
return pivotTimeAdapter(granularity).normalizeFunctionValue(value);
|
|
18527
|
-
}
|
|
18528
|
-
const pivotNormalizationValueRegistry = new Registry();
|
|
18529
|
-
pivotNormalizationValueRegistry
|
|
18530
|
-
.add("date", normalizeDateTime)
|
|
18531
|
-
.add("datetime", normalizeDateTime)
|
|
18532
|
-
.add("integer", (value) => toNumber(value, DEFAULT_LOCALE))
|
|
18533
|
-
.add("boolean", (value) => toBoolean(value))
|
|
18534
|
-
.add("char", (value) => toString(value));
|
|
18535
|
-
|
|
18536
18621
|
/**
|
|
18537
18622
|
* Get the pivot ID from the formula pivot ID.
|
|
18538
18623
|
*/
|
|
@@ -19109,14 +19194,10 @@ const PIVOT = {
|
|
|
19109
19194
|
result[col].push({ value: "" });
|
|
19110
19195
|
break;
|
|
19111
19196
|
case "HEADER":
|
|
19112
|
-
|
|
19113
|
-
|
|
19114
|
-
|
|
19115
|
-
|
|
19116
|
-
}
|
|
19117
|
-
else {
|
|
19118
|
-
result[col].push(pivot.getPivotHeaderValueAndFormat(domain));
|
|
19119
|
-
}
|
|
19197
|
+
result[col].push(pivot.getPivotHeaderValueAndFormat(pivotCell.domain));
|
|
19198
|
+
break;
|
|
19199
|
+
case "MEASURE_HEADER":
|
|
19200
|
+
result[col].push(pivot.getPivotMeasureValue(pivotCell.measure, pivotCell.domain));
|
|
19120
19201
|
break;
|
|
19121
19202
|
case "VALUE":
|
|
19122
19203
|
result[col].push(pivot.getPivotCellValueAndFormat(pivotCell.measure, pivotCell.domain));
|
|
@@ -20155,6 +20236,8 @@ const MODIFIER_KEYS = ["Shift", "Control", "Alt", "Meta"];
|
|
|
20155
20236
|
* a child element.
|
|
20156
20237
|
*/
|
|
20157
20238
|
function isChildEvent(parent, ev) {
|
|
20239
|
+
if (!parent)
|
|
20240
|
+
return false;
|
|
20158
20241
|
return !!ev.target && parent.contains(ev.target);
|
|
20159
20242
|
}
|
|
20160
20243
|
function gridOverlayPosition() {
|
|
@@ -22694,7 +22777,8 @@ function truncateLabel(label) {
|
|
|
22694
22777
|
/**
|
|
22695
22778
|
* Get a default chart js configuration
|
|
22696
22779
|
*/
|
|
22697
|
-
function getDefaultChartJsRuntime(chart, labels, fontColor,
|
|
22780
|
+
function getDefaultChartJsRuntime(chart, labels, fontColor, args) {
|
|
22781
|
+
const { format, locale, truncateLabels, horizontalChart } = args;
|
|
22698
22782
|
const chartTitle = chart.title.text ? chart.title : { ...chart.title, content: "" };
|
|
22699
22783
|
const options = {
|
|
22700
22784
|
// https://www.chartjs.org/docs/latest/general/responsive.html
|
|
@@ -22738,8 +22822,11 @@ function getDefaultChartJsRuntime(chart, labels, fontColor, { format, locale, tr
|
|
|
22738
22822
|
callbacks: {
|
|
22739
22823
|
label: function (tooltipItem) {
|
|
22740
22824
|
const xLabel = tooltipItem.dataset?.label || tooltipItem.label;
|
|
22741
|
-
// tooltipItem.parsed
|
|
22742
|
-
|
|
22825
|
+
// tooltipItem.parsed can be an object or a number for pie charts
|
|
22826
|
+
let yLabel = horizontalChart ? tooltipItem.parsed.x : tooltipItem.parsed.y;
|
|
22827
|
+
if (!yLabel) {
|
|
22828
|
+
yLabel = tooltipItem.parsed;
|
|
22829
|
+
}
|
|
22743
22830
|
const toolTipFormat = !format && Math.abs(yLabel) >= 1000 ? "#,##" : format;
|
|
22744
22831
|
const yLabelStr = formatValue(yLabel, { format: toolTipFormat, locale });
|
|
22745
22832
|
return xLabel ? `${xLabel}: ${yLabelStr}` : yLabelStr;
|
|
@@ -22913,6 +23000,7 @@ class BarChart extends AbstractChart {
|
|
|
22913
23000
|
dataSetsHaveTitle;
|
|
22914
23001
|
dataSetDesign;
|
|
22915
23002
|
axesDesign;
|
|
23003
|
+
horizontal;
|
|
22916
23004
|
constructor(definition, sheetId, getters) {
|
|
22917
23005
|
super(definition, sheetId, getters);
|
|
22918
23006
|
this.dataSets = createDataSets(getters, definition.dataSets, sheetId, definition.dataSetsHaveTitle);
|
|
@@ -22924,6 +23012,7 @@ class BarChart extends AbstractChart {
|
|
|
22924
23012
|
this.dataSetsHaveTitle = definition.dataSetsHaveTitle;
|
|
22925
23013
|
this.dataSetDesign = definition.dataSets;
|
|
22926
23014
|
this.axesDesign = definition.axesDesign;
|
|
23015
|
+
this.horizontal = definition.horizontal;
|
|
22927
23016
|
}
|
|
22928
23017
|
static transformDefinition(definition, executed) {
|
|
22929
23018
|
return transformChartDefinitionWithDataSetsWithZone(definition, executed);
|
|
@@ -22995,6 +23084,7 @@ class BarChart extends AbstractChart {
|
|
|
22995
23084
|
stacked: this.stacked,
|
|
22996
23085
|
aggregated: this.aggregated,
|
|
22997
23086
|
axesDesign: this.axesDesign,
|
|
23087
|
+
horizontal: this.horizontal,
|
|
22998
23088
|
};
|
|
22999
23089
|
}
|
|
23000
23090
|
getDefinitionForExcel() {
|
|
@@ -23026,7 +23116,10 @@ class BarChart extends AbstractChart {
|
|
|
23026
23116
|
}
|
|
23027
23117
|
function getBarConfiguration(chart, labels, localeFormat) {
|
|
23028
23118
|
const fontColor = chartFontColor(chart.background);
|
|
23029
|
-
const config = getDefaultChartJsRuntime(chart, labels, fontColor,
|
|
23119
|
+
const config = getDefaultChartJsRuntime(chart, labels, fontColor, {
|
|
23120
|
+
...localeFormat,
|
|
23121
|
+
horizontalChart: chart.horizontal,
|
|
23122
|
+
});
|
|
23030
23123
|
const legend = {
|
|
23031
23124
|
labels: { color: fontColor },
|
|
23032
23125
|
};
|
|
@@ -23040,16 +23133,10 @@ function getBarConfiguration(chart, labels, localeFormat) {
|
|
|
23040
23133
|
config.options.layout = {
|
|
23041
23134
|
padding: { left: 20, right: 20, top: chart.title ? 10 : 25, bottom: 10 },
|
|
23042
23135
|
};
|
|
23043
|
-
config.options.
|
|
23044
|
-
|
|
23045
|
-
|
|
23046
|
-
|
|
23047
|
-
color: fontColor,
|
|
23048
|
-
},
|
|
23049
|
-
title: getChartAxisTitleRuntime(chart.axesDesign?.x),
|
|
23050
|
-
},
|
|
23051
|
-
};
|
|
23052
|
-
const yAxis = {
|
|
23136
|
+
config.options.indexAxis = chart.horizontal ? "y" : "x";
|
|
23137
|
+
config.options.scales = {};
|
|
23138
|
+
const labelsAxis = { ticks: { padding: 5, color: fontColor } };
|
|
23139
|
+
const valuesAxis = {
|
|
23053
23140
|
beginAtZero: true, // the origin of the y axis is always zero
|
|
23054
23141
|
ticks: {
|
|
23055
23142
|
color: fontColor,
|
|
@@ -23065,7 +23152,10 @@ function getBarConfiguration(chart, labels, localeFormat) {
|
|
|
23065
23152
|
},
|
|
23066
23153
|
},
|
|
23067
23154
|
};
|
|
23155
|
+
const xAxis = chart.horizontal ? valuesAxis : labelsAxis;
|
|
23156
|
+
const yAxis = chart.horizontal ? labelsAxis : valuesAxis;
|
|
23068
23157
|
const { useLeftAxis, useRightAxis } = getDefinedAxis(chart.getDefinition());
|
|
23158
|
+
config.options.scales.x = { ...xAxis, title: getChartAxisTitleRuntime(chart.axesDesign?.x) };
|
|
23069
23159
|
if (useLeftAxis) {
|
|
23070
23160
|
config.options.scales.y = {
|
|
23071
23161
|
...yAxis,
|
|
@@ -23132,7 +23222,7 @@ function createBarChartRuntime(chart, getters) {
|
|
|
23132
23222
|
const label = definition.dataSets[index].label;
|
|
23133
23223
|
dataset.label = label;
|
|
23134
23224
|
}
|
|
23135
|
-
if (definition.dataSets?.[index]?.yAxisId) {
|
|
23225
|
+
if (definition.dataSets?.[index]?.yAxisId && !chart.horizontal) {
|
|
23136
23226
|
dataset["yAxisID"] = definition.dataSets[index].yAxisId;
|
|
23137
23227
|
}
|
|
23138
23228
|
}
|
|
@@ -24123,6 +24213,7 @@ class PieChart extends AbstractChart {
|
|
|
24123
24213
|
type = "pie";
|
|
24124
24214
|
aggregated;
|
|
24125
24215
|
dataSetsHaveTitle;
|
|
24216
|
+
isDoughnut;
|
|
24126
24217
|
constructor(definition, sheetId, getters) {
|
|
24127
24218
|
super(definition, sheetId, getters);
|
|
24128
24219
|
this.dataSets = createDataSets(getters, definition.dataSets, sheetId, definition.dataSetsHaveTitle);
|
|
@@ -24131,6 +24222,7 @@ class PieChart extends AbstractChart {
|
|
|
24131
24222
|
this.legendPosition = definition.legendPosition;
|
|
24132
24223
|
this.aggregated = definition.aggregated;
|
|
24133
24224
|
this.dataSetsHaveTitle = definition.dataSetsHaveTitle;
|
|
24225
|
+
this.isDoughnut = definition.isDoughnut;
|
|
24134
24226
|
}
|
|
24135
24227
|
static transformDefinition(definition, executed) {
|
|
24136
24228
|
return transformChartDefinitionWithDataSetsWithZone(definition, executed);
|
|
@@ -24148,6 +24240,7 @@ class PieChart extends AbstractChart {
|
|
|
24148
24240
|
type: "pie",
|
|
24149
24241
|
labelRange: context.auxiliaryRange || undefined,
|
|
24150
24242
|
aggregated: context.aggregated ?? false,
|
|
24243
|
+
isDoughnut: false,
|
|
24151
24244
|
};
|
|
24152
24245
|
}
|
|
24153
24246
|
getDefinition() {
|
|
@@ -24178,6 +24271,7 @@ class PieChart extends AbstractChart {
|
|
|
24178
24271
|
: undefined,
|
|
24179
24272
|
title: this.title,
|
|
24180
24273
|
aggregated: this.aggregated,
|
|
24274
|
+
isDoughnut: this.isDoughnut,
|
|
24181
24275
|
};
|
|
24182
24276
|
}
|
|
24183
24277
|
copyForSheetId(sheetId) {
|
|
@@ -24312,6 +24406,141 @@ function createPieChartRuntime(chart, getters) {
|
|
|
24312
24406
|
};
|
|
24313
24407
|
config.data.datasets.push(dataset);
|
|
24314
24408
|
}
|
|
24409
|
+
if (chart.isDoughnut) {
|
|
24410
|
+
config.type = "doughnut";
|
|
24411
|
+
}
|
|
24412
|
+
return { chartJsConfig: config, background: chart.background || BACKGROUND_CHART_COLOR };
|
|
24413
|
+
}
|
|
24414
|
+
|
|
24415
|
+
class PyramidChart extends AbstractChart {
|
|
24416
|
+
dataSets;
|
|
24417
|
+
labelRange;
|
|
24418
|
+
background;
|
|
24419
|
+
legendPosition;
|
|
24420
|
+
aggregated;
|
|
24421
|
+
type = "pyramid";
|
|
24422
|
+
dataSetsHaveTitle;
|
|
24423
|
+
dataSetDesign;
|
|
24424
|
+
axesDesign;
|
|
24425
|
+
horizontal = true;
|
|
24426
|
+
stacked = true;
|
|
24427
|
+
constructor(definition, sheetId, getters) {
|
|
24428
|
+
super(definition, sheetId, getters);
|
|
24429
|
+
this.dataSets = createDataSets(getters, definition.dataSets, sheetId, definition.dataSetsHaveTitle).slice(0, 2);
|
|
24430
|
+
this.labelRange = createValidRange(getters, sheetId, definition.labelRange);
|
|
24431
|
+
this.background = definition.background;
|
|
24432
|
+
this.legendPosition = definition.legendPosition;
|
|
24433
|
+
this.aggregated = definition.aggregated;
|
|
24434
|
+
this.dataSetsHaveTitle = definition.dataSetsHaveTitle;
|
|
24435
|
+
this.dataSetDesign = definition.dataSets;
|
|
24436
|
+
this.axesDesign = definition.axesDesign;
|
|
24437
|
+
}
|
|
24438
|
+
static transformDefinition(definition, executed) {
|
|
24439
|
+
return transformChartDefinitionWithDataSetsWithZone(definition, executed);
|
|
24440
|
+
}
|
|
24441
|
+
static validateChartDefinition(validator, definition) {
|
|
24442
|
+
return validator.checkValidations(definition, checkDataset, checkLabelRange);
|
|
24443
|
+
}
|
|
24444
|
+
static getDefinitionFromContextCreation(context) {
|
|
24445
|
+
return {
|
|
24446
|
+
background: context.background,
|
|
24447
|
+
dataSets: context.range ?? [],
|
|
24448
|
+
dataSetsHaveTitle: context.dataSetsHaveTitle ?? false,
|
|
24449
|
+
aggregated: context.aggregated ?? false,
|
|
24450
|
+
legendPosition: context.legendPosition ?? "top",
|
|
24451
|
+
title: context.title || { text: "" },
|
|
24452
|
+
type: "pyramid",
|
|
24453
|
+
labelRange: context.auxiliaryRange || undefined,
|
|
24454
|
+
axesDesign: context.axesDesign,
|
|
24455
|
+
horizontal: true,
|
|
24456
|
+
stacked: true,
|
|
24457
|
+
};
|
|
24458
|
+
}
|
|
24459
|
+
getContextCreation() {
|
|
24460
|
+
const range = [];
|
|
24461
|
+
for (const [i, dataSet] of this.dataSets.entries()) {
|
|
24462
|
+
range.push({
|
|
24463
|
+
...this.dataSetDesign?.[i],
|
|
24464
|
+
dataRange: this.getters.getRangeString(dataSet.dataRange, this.sheetId),
|
|
24465
|
+
});
|
|
24466
|
+
}
|
|
24467
|
+
return {
|
|
24468
|
+
...this,
|
|
24469
|
+
range,
|
|
24470
|
+
auxiliaryRange: this.labelRange
|
|
24471
|
+
? this.getters.getRangeString(this.labelRange, this.sheetId)
|
|
24472
|
+
: undefined,
|
|
24473
|
+
};
|
|
24474
|
+
}
|
|
24475
|
+
copyForSheetId(sheetId) {
|
|
24476
|
+
const dataSets = copyDataSetsWithNewSheetId(this.sheetId, sheetId, this.dataSets);
|
|
24477
|
+
const labelRange = copyLabelRangeWithNewSheetId(this.sheetId, sheetId, this.labelRange);
|
|
24478
|
+
const definition = this.getDefinitionWithSpecificDataSets(dataSets, labelRange, sheetId);
|
|
24479
|
+
return new PyramidChart(definition, sheetId, this.getters);
|
|
24480
|
+
}
|
|
24481
|
+
copyInSheetId(sheetId) {
|
|
24482
|
+
const definition = this.getDefinitionWithSpecificDataSets(this.dataSets, this.labelRange, sheetId);
|
|
24483
|
+
return new PyramidChart(definition, sheetId, this.getters);
|
|
24484
|
+
}
|
|
24485
|
+
getDefinition() {
|
|
24486
|
+
return this.getDefinitionWithSpecificDataSets(this.dataSets, this.labelRange);
|
|
24487
|
+
}
|
|
24488
|
+
getDefinitionWithSpecificDataSets(dataSets, labelRange, targetSheetId) {
|
|
24489
|
+
const ranges = [];
|
|
24490
|
+
for (const [i, dataSet] of dataSets.entries()) {
|
|
24491
|
+
ranges.push({
|
|
24492
|
+
...this.dataSetDesign?.[i],
|
|
24493
|
+
dataRange: this.getters.getRangeString(dataSet.dataRange, targetSheetId || this.sheetId),
|
|
24494
|
+
});
|
|
24495
|
+
}
|
|
24496
|
+
return {
|
|
24497
|
+
type: "pyramid",
|
|
24498
|
+
dataSetsHaveTitle: dataSets.length ? Boolean(dataSets[0].labelCell) : false,
|
|
24499
|
+
background: this.background,
|
|
24500
|
+
dataSets: ranges,
|
|
24501
|
+
legendPosition: this.legendPosition,
|
|
24502
|
+
labelRange: labelRange
|
|
24503
|
+
? this.getters.getRangeString(labelRange, targetSheetId || this.sheetId)
|
|
24504
|
+
: undefined,
|
|
24505
|
+
title: this.title,
|
|
24506
|
+
aggregated: this.aggregated,
|
|
24507
|
+
axesDesign: this.axesDesign,
|
|
24508
|
+
horizontal: true,
|
|
24509
|
+
stacked: true,
|
|
24510
|
+
};
|
|
24511
|
+
}
|
|
24512
|
+
getDefinitionForExcel() {
|
|
24513
|
+
return undefined;
|
|
24514
|
+
}
|
|
24515
|
+
updateRanges(applyChange) {
|
|
24516
|
+
const { dataSets, labelRange, isStale } = updateChartRangesWithDataSets(this.getters, applyChange, this.dataSets, this.labelRange);
|
|
24517
|
+
if (!isStale) {
|
|
24518
|
+
return this;
|
|
24519
|
+
}
|
|
24520
|
+
const definition = this.getDefinitionWithSpecificDataSets(dataSets, labelRange);
|
|
24521
|
+
return new PyramidChart(definition, this.sheetId, this.getters);
|
|
24522
|
+
}
|
|
24523
|
+
}
|
|
24524
|
+
function createPyramidChartRuntime(chart, getters) {
|
|
24525
|
+
const barDef = { ...chart.getDefinition(), type: "bar" };
|
|
24526
|
+
const barChart = new BarChart(barDef, chart.sheetId, getters);
|
|
24527
|
+
const barRuntime = createBarChartRuntime(barChart, getters);
|
|
24528
|
+
const config = barRuntime.chartJsConfig;
|
|
24529
|
+
let datasets = config.data?.datasets;
|
|
24530
|
+
if (datasets && datasets[0]) {
|
|
24531
|
+
datasets[0].data = datasets[0].data.map((value) => (value > 0 ? value : 0));
|
|
24532
|
+
}
|
|
24533
|
+
if (datasets && datasets[1]) {
|
|
24534
|
+
datasets[1].data = datasets[1].data.map((value) => (value > 0 ? -value : 0));
|
|
24535
|
+
}
|
|
24536
|
+
const scales = config.options.scales;
|
|
24537
|
+
const scalesXCallback = scales.x.ticks.callback;
|
|
24538
|
+
scales.x.ticks.callback = (value) => scalesXCallback(Math.abs(value));
|
|
24539
|
+
const tooltipLabelCallback = config.options.plugins.tooltip.callbacks.label;
|
|
24540
|
+
config.options.plugins.tooltip.callbacks.label = (item) => {
|
|
24541
|
+
const tooltipItem = { ...item, parsed: { y: item.parsed.y, x: Math.abs(item.parsed.x) } };
|
|
24542
|
+
return tooltipLabelCallback(tooltipItem);
|
|
24543
|
+
};
|
|
24315
24544
|
return { chartJsConfig: config, background: chart.background || BACKGROUND_CHART_COLOR };
|
|
24316
24545
|
}
|
|
24317
24546
|
|
|
@@ -24754,7 +24983,6 @@ chartRegistry.add("bar", {
|
|
|
24754
24983
|
validateChartDefinition: BarChart.validateChartDefinition,
|
|
24755
24984
|
transformDefinition: BarChart.transformDefinition,
|
|
24756
24985
|
getChartDefinitionFromContextCreation: BarChart.getDefinitionFromContextCreation,
|
|
24757
|
-
name: _t("Bar"),
|
|
24758
24986
|
sequence: 10,
|
|
24759
24987
|
});
|
|
24760
24988
|
chartRegistry.add("combo", {
|
|
@@ -24764,7 +24992,6 @@ chartRegistry.add("combo", {
|
|
|
24764
24992
|
validateChartDefinition: ComboChart.validateChartDefinition,
|
|
24765
24993
|
transformDefinition: ComboChart.transformDefinition,
|
|
24766
24994
|
getChartDefinitionFromContextCreation: ComboChart.getDefinitionFromContextCreation,
|
|
24767
|
-
name: _t("Combo"),
|
|
24768
24995
|
sequence: 15,
|
|
24769
24996
|
});
|
|
24770
24997
|
chartRegistry.add("line", {
|
|
@@ -24774,7 +25001,6 @@ chartRegistry.add("line", {
|
|
|
24774
25001
|
validateChartDefinition: LineChart.validateChartDefinition,
|
|
24775
25002
|
transformDefinition: LineChart.transformDefinition,
|
|
24776
25003
|
getChartDefinitionFromContextCreation: LineChart.getDefinitionFromContextCreation,
|
|
24777
|
-
name: _t("Line"),
|
|
24778
25004
|
sequence: 20,
|
|
24779
25005
|
});
|
|
24780
25006
|
chartRegistry.add("pie", {
|
|
@@ -24784,7 +25010,6 @@ chartRegistry.add("pie", {
|
|
|
24784
25010
|
validateChartDefinition: PieChart.validateChartDefinition,
|
|
24785
25011
|
transformDefinition: PieChart.transformDefinition,
|
|
24786
25012
|
getChartDefinitionFromContextCreation: PieChart.getDefinitionFromContextCreation,
|
|
24787
|
-
name: _t("Pie"),
|
|
24788
25013
|
sequence: 30,
|
|
24789
25014
|
});
|
|
24790
25015
|
chartRegistry.add("scorecard", {
|
|
@@ -24794,7 +25019,6 @@ chartRegistry.add("scorecard", {
|
|
|
24794
25019
|
validateChartDefinition: ScorecardChart$1.validateChartDefinition,
|
|
24795
25020
|
transformDefinition: ScorecardChart$1.transformDefinition,
|
|
24796
25021
|
getChartDefinitionFromContextCreation: ScorecardChart$1.getDefinitionFromContextCreation,
|
|
24797
|
-
name: _t("Scorecard"),
|
|
24798
25022
|
sequence: 40,
|
|
24799
25023
|
});
|
|
24800
25024
|
chartRegistry.add("gauge", {
|
|
@@ -24804,7 +25028,6 @@ chartRegistry.add("gauge", {
|
|
|
24804
25028
|
validateChartDefinition: GaugeChart.validateChartDefinition,
|
|
24805
25029
|
transformDefinition: GaugeChart.transformDefinition,
|
|
24806
25030
|
getChartDefinitionFromContextCreation: GaugeChart.getDefinitionFromContextCreation,
|
|
24807
|
-
name: _t("Gauge"),
|
|
24808
25031
|
sequence: 50,
|
|
24809
25032
|
});
|
|
24810
25033
|
chartRegistry.add("scatter", {
|
|
@@ -24814,7 +25037,6 @@ chartRegistry.add("scatter", {
|
|
|
24814
25037
|
validateChartDefinition: ScatterChart.validateChartDefinition,
|
|
24815
25038
|
transformDefinition: ScatterChart.transformDefinition,
|
|
24816
25039
|
getChartDefinitionFromContextCreation: ScatterChart.getDefinitionFromContextCreation,
|
|
24817
|
-
name: _t("Scatter"),
|
|
24818
25040
|
sequence: 60,
|
|
24819
25041
|
});
|
|
24820
25042
|
chartRegistry.add("waterfall", {
|
|
@@ -24824,9 +25046,17 @@ chartRegistry.add("waterfall", {
|
|
|
24824
25046
|
validateChartDefinition: WaterfallChart.validateChartDefinition,
|
|
24825
25047
|
transformDefinition: WaterfallChart.transformDefinition,
|
|
24826
25048
|
getChartDefinitionFromContextCreation: WaterfallChart.getDefinitionFromContextCreation,
|
|
24827
|
-
name: _t("Waterfall"),
|
|
24828
25049
|
sequence: 70,
|
|
24829
25050
|
});
|
|
25051
|
+
chartRegistry.add("pyramid", {
|
|
25052
|
+
match: (type) => type === "pyramid",
|
|
25053
|
+
createChart: (definition, sheetId, getters) => new PyramidChart(definition, sheetId, getters),
|
|
25054
|
+
getChartRuntime: createPyramidChartRuntime,
|
|
25055
|
+
validateChartDefinition: PyramidChart.validateChartDefinition,
|
|
25056
|
+
transformDefinition: PyramidChart.transformDefinition,
|
|
25057
|
+
getChartDefinitionFromContextCreation: PyramidChart.getDefinitionFromContextCreation,
|
|
25058
|
+
sequence: 80,
|
|
25059
|
+
});
|
|
24830
25060
|
const chartComponentRegistry = new Registry();
|
|
24831
25061
|
chartComponentRegistry.add("line", ChartJsComponent);
|
|
24832
25062
|
chartComponentRegistry.add("bar", ChartJsComponent);
|
|
@@ -24836,6 +25066,130 @@ chartComponentRegistry.add("gauge", GaugeChartComponent);
|
|
|
24836
25066
|
chartComponentRegistry.add("scatter", ChartJsComponent);
|
|
24837
25067
|
chartComponentRegistry.add("scorecard", ScorecardChart);
|
|
24838
25068
|
chartComponentRegistry.add("waterfall", ChartJsComponent);
|
|
25069
|
+
chartComponentRegistry.add("pyramid", ChartJsComponent);
|
|
25070
|
+
const chartCategories = {
|
|
25071
|
+
line: _t("Line"),
|
|
25072
|
+
column: _t("Column"),
|
|
25073
|
+
bar: _t("Bar"),
|
|
25074
|
+
pie: _t("Pie"),
|
|
25075
|
+
misc: _t("Miscellaneous"),
|
|
25076
|
+
};
|
|
25077
|
+
const chartSubtypeRegistry = new Registry();
|
|
25078
|
+
chartSubtypeRegistry
|
|
25079
|
+
.add("line", {
|
|
25080
|
+
matcher: (definition) => definition.type === "line" && !definition.stacked,
|
|
25081
|
+
displayName: _t("Line"),
|
|
25082
|
+
chartType: "line",
|
|
25083
|
+
chartSubtype: "line",
|
|
25084
|
+
subtypeDefinition: { stacked: false },
|
|
25085
|
+
category: "line",
|
|
25086
|
+
preview: "o-spreadsheet-ChartPreview.LINE_CHART",
|
|
25087
|
+
})
|
|
25088
|
+
.add("stacked_line", {
|
|
25089
|
+
matcher: (definition) => definition.type === "line" && definition.stacked,
|
|
25090
|
+
displayName: _t("Stacked Line"),
|
|
25091
|
+
chartType: "line",
|
|
25092
|
+
chartSubtype: "stacked_line",
|
|
25093
|
+
subtypeDefinition: { stacked: true },
|
|
25094
|
+
category: "line",
|
|
25095
|
+
preview: "o-spreadsheet-ChartPreview.STACKED_AREA_CHART",
|
|
25096
|
+
})
|
|
25097
|
+
.add("scatter", {
|
|
25098
|
+
displayName: _t("Scatter"),
|
|
25099
|
+
chartType: "scatter",
|
|
25100
|
+
chartSubtype: "scatter",
|
|
25101
|
+
category: "misc",
|
|
25102
|
+
preview: "o-spreadsheet-ChartPreview.SCATTER_CHART",
|
|
25103
|
+
})
|
|
25104
|
+
.add("column", {
|
|
25105
|
+
matcher: (definition) => definition.type === "bar" && !definition.stacked && !definition.horizontal,
|
|
25106
|
+
displayName: _t("Column"),
|
|
25107
|
+
chartType: "bar",
|
|
25108
|
+
chartSubtype: "column",
|
|
25109
|
+
subtypeDefinition: { stacked: false, horizontal: false },
|
|
25110
|
+
category: "column",
|
|
25111
|
+
preview: "o-spreadsheet-ChartPreview.COLUMN_CHART",
|
|
25112
|
+
})
|
|
25113
|
+
.add("stacked_column", {
|
|
25114
|
+
matcher: (definition) => definition.type === "bar" && definition.stacked && !definition.horizontal,
|
|
25115
|
+
displayName: _t("Stacked Column"),
|
|
25116
|
+
chartType: "bar",
|
|
25117
|
+
chartSubtype: "stacked_column",
|
|
25118
|
+
subtypeDefinition: { stacked: true, horizontal: false },
|
|
25119
|
+
category: "column",
|
|
25120
|
+
preview: "o-spreadsheet-ChartPreview.STACKED_COLUMN_CHART",
|
|
25121
|
+
})
|
|
25122
|
+
.add("bar", {
|
|
25123
|
+
matcher: (definition) => definition.type === "bar" && !definition.stacked && !!definition.horizontal,
|
|
25124
|
+
displayName: _t("Bar"),
|
|
25125
|
+
chartType: "bar",
|
|
25126
|
+
chartSubtype: "bar",
|
|
25127
|
+
subtypeDefinition: { horizontal: true, stacked: false },
|
|
25128
|
+
category: "bar",
|
|
25129
|
+
preview: "o-spreadsheet-ChartPreview.BAR_CHART",
|
|
25130
|
+
})
|
|
25131
|
+
.add("stacked_bar", {
|
|
25132
|
+
matcher: (definition) => definition.type === "bar" && definition.stacked && !!definition.horizontal,
|
|
25133
|
+
displayName: _t("Stacked Bar"),
|
|
25134
|
+
chartType: "bar",
|
|
25135
|
+
chartSubtype: "stacked_bar",
|
|
25136
|
+
subtypeDefinition: { horizontal: true, stacked: true },
|
|
25137
|
+
category: "bar",
|
|
25138
|
+
preview: "o-spreadsheet-ChartPreview.STACKED_BAR_CHART",
|
|
25139
|
+
})
|
|
25140
|
+
.add("combo", {
|
|
25141
|
+
displayName: _t("Combo"),
|
|
25142
|
+
chartSubtype: "combo",
|
|
25143
|
+
chartType: "combo",
|
|
25144
|
+
category: "line",
|
|
25145
|
+
preview: "o-spreadsheet-ChartPreview.COMBO_CHART",
|
|
25146
|
+
})
|
|
25147
|
+
.add("pie", {
|
|
25148
|
+
matcher: (definition) => definition.type === "pie" && !definition.isDoughnut,
|
|
25149
|
+
displayName: _t("Pie"),
|
|
25150
|
+
chartSubtype: "pie",
|
|
25151
|
+
chartType: "pie",
|
|
25152
|
+
subtypeDefinition: { isDoughnut: false },
|
|
25153
|
+
category: "pie",
|
|
25154
|
+
preview: "o-spreadsheet-ChartPreview.PIE_CHART",
|
|
25155
|
+
})
|
|
25156
|
+
.add("doughnut", {
|
|
25157
|
+
matcher: (definition) => definition.type === "pie" && !!definition.isDoughnut,
|
|
25158
|
+
displayName: _t("Doughnut"),
|
|
25159
|
+
chartSubtype: "doughnut",
|
|
25160
|
+
chartType: "pie",
|
|
25161
|
+
subtypeDefinition: { isDoughnut: true },
|
|
25162
|
+
category: "pie",
|
|
25163
|
+
preview: "o-spreadsheet-ChartPreview.DOUGHNUT_CHART",
|
|
25164
|
+
})
|
|
25165
|
+
.add("gauge", {
|
|
25166
|
+
displayName: _t("Gauge"),
|
|
25167
|
+
chartSubtype: "gauge",
|
|
25168
|
+
chartType: "gauge",
|
|
25169
|
+
category: "misc",
|
|
25170
|
+
preview: "o-spreadsheet-ChartPreview.GAUGE_CHART",
|
|
25171
|
+
})
|
|
25172
|
+
.add("scorecard", {
|
|
25173
|
+
displayName: _t("Scorecard"),
|
|
25174
|
+
chartSubtype: "scorecard",
|
|
25175
|
+
chartType: "scorecard",
|
|
25176
|
+
category: "misc",
|
|
25177
|
+
preview: "o-spreadsheet-ChartPreview.SCORECARD_CHART",
|
|
25178
|
+
})
|
|
25179
|
+
.add("waterfall", {
|
|
25180
|
+
displayName: _t("Waterfall"),
|
|
25181
|
+
chartSubtype: "waterfall",
|
|
25182
|
+
chartType: "waterfall",
|
|
25183
|
+
category: "misc",
|
|
25184
|
+
preview: "o-spreadsheet-ChartPreview.WATERFALL_CHART",
|
|
25185
|
+
})
|
|
25186
|
+
.add("pyramid", {
|
|
25187
|
+
displayName: _t("Population Pyramid"),
|
|
25188
|
+
chartSubtype: "pyramid",
|
|
25189
|
+
chartType: "pyramid",
|
|
25190
|
+
category: "misc",
|
|
25191
|
+
preview: "o-spreadsheet-ChartPreview.POPULATION_PYRAMID_CHART",
|
|
25192
|
+
});
|
|
24839
25193
|
|
|
24840
25194
|
/**
|
|
24841
25195
|
* Registry intended to support usual currencies. It is mainly used to create
|
|
@@ -26692,20 +27046,6 @@ function transformDefinition(definition, executed) {
|
|
|
26692
27046
|
}
|
|
26693
27047
|
return transformation.transformDefinition(definition, executed);
|
|
26694
27048
|
}
|
|
26695
|
-
/**
|
|
26696
|
-
* Get an empty definition based on the given context and the given type
|
|
26697
|
-
*/
|
|
26698
|
-
function getChartDefinitionFromContextCreation(context, type) {
|
|
26699
|
-
const chartClass = chartRegistry.get(type);
|
|
26700
|
-
return chartClass.getChartDefinitionFromContextCreation(context);
|
|
26701
|
-
}
|
|
26702
|
-
function getChartTypes() {
|
|
26703
|
-
const result = {};
|
|
26704
|
-
for (const key of chartRegistry.getKeys()) {
|
|
26705
|
-
result[key] = chartRegistry.get(key).name;
|
|
26706
|
-
}
|
|
26707
|
-
return result;
|
|
26708
|
-
}
|
|
26709
27049
|
/**
|
|
26710
27050
|
* Return a "smart" chart definition in the given zone. The definition is "smart" because it will
|
|
26711
27051
|
* use the best type of chart to display the data of the zone.
|
|
@@ -28165,6 +28505,40 @@ const pivotProperties = {
|
|
|
28165
28505
|
},
|
|
28166
28506
|
icon: "o-spreadsheet-Icon.PIVOT",
|
|
28167
28507
|
};
|
|
28508
|
+
const FIX_FORMULAS = {
|
|
28509
|
+
name: _t("Convert to individual formulas"),
|
|
28510
|
+
execute(env) {
|
|
28511
|
+
const position = env.model.getters.getActivePosition();
|
|
28512
|
+
const cell = env.model.getters.getCorrespondingFormulaCell(position);
|
|
28513
|
+
const pivotId = env.model.getters.getPivotIdFromPosition(position);
|
|
28514
|
+
if (!cell || !pivotId) {
|
|
28515
|
+
return;
|
|
28516
|
+
}
|
|
28517
|
+
const { sheetId, col, row } = env.model.getters.getCellPosition(cell.id);
|
|
28518
|
+
const pivot = env.model.getters.getPivot(pivotId);
|
|
28519
|
+
pivot.init();
|
|
28520
|
+
if (!pivot.isValid()) {
|
|
28521
|
+
return;
|
|
28522
|
+
}
|
|
28523
|
+
env.model.dispatch("INSERT_PIVOT", {
|
|
28524
|
+
sheetId,
|
|
28525
|
+
col,
|
|
28526
|
+
row,
|
|
28527
|
+
pivotId,
|
|
28528
|
+
table: pivot.getTableStructure().export(),
|
|
28529
|
+
});
|
|
28530
|
+
},
|
|
28531
|
+
isVisible: (env) => {
|
|
28532
|
+
const position = env.model.getters.getActivePosition();
|
|
28533
|
+
const pivotId = env.model.getters.getPivotIdFromPosition(position);
|
|
28534
|
+
if (!pivotId) {
|
|
28535
|
+
return false;
|
|
28536
|
+
}
|
|
28537
|
+
const pivot = env.model.getters.getPivot(pivotId);
|
|
28538
|
+
return pivot.isValid() && env.model.getters.isSpillPivotFormula(position);
|
|
28539
|
+
},
|
|
28540
|
+
icon: "o-spreadsheet-Icon.PIVOT",
|
|
28541
|
+
};
|
|
28168
28542
|
|
|
28169
28543
|
//------------------------------------------------------------------------------
|
|
28170
28544
|
// Context Menu Registry
|
|
@@ -28263,6 +28637,10 @@ cellMenuRegistry
|
|
|
28263
28637
|
name: INSERT_LINK_NAME,
|
|
28264
28638
|
sequence: 150,
|
|
28265
28639
|
separator: true,
|
|
28640
|
+
})
|
|
28641
|
+
.add("pivot_fix_formulas", {
|
|
28642
|
+
...FIX_FORMULAS,
|
|
28643
|
+
sequence: 155,
|
|
28266
28644
|
})
|
|
28267
28645
|
.add("pivot_properties", {
|
|
28268
28646
|
...pivotProperties,
|
|
@@ -30485,6 +30863,7 @@ class GenericChartConfigPanel extends owl.Component {
|
|
|
30485
30863
|
});
|
|
30486
30864
|
dataSeriesRanges = [];
|
|
30487
30865
|
labelRange;
|
|
30866
|
+
chartTerms = ChartTerms;
|
|
30488
30867
|
setup() {
|
|
30489
30868
|
this.dataSeriesRanges = this.props.definition.dataSets;
|
|
30490
30869
|
this.labelRange = this.props.definition.labelRange;
|
|
@@ -30509,7 +30888,7 @@ class GenericChartConfigPanel extends owl.Component {
|
|
|
30509
30888
|
return [
|
|
30510
30889
|
{
|
|
30511
30890
|
name: "aggregated",
|
|
30512
|
-
label:
|
|
30891
|
+
label: this.chartTerms.AggregatedChart,
|
|
30513
30892
|
value: this.props.definition.aggregated ?? false,
|
|
30514
30893
|
onChange: this.onUpdateAggregated.bind(this),
|
|
30515
30894
|
},
|
|
@@ -30585,9 +30964,6 @@ class GenericChartConfigPanel extends owl.Component {
|
|
|
30585
30964
|
|
|
30586
30965
|
class BarConfigPanel extends GenericChartConfigPanel {
|
|
30587
30966
|
static template = "o-spreadsheet-BarConfigPanel";
|
|
30588
|
-
get stackedLabel() {
|
|
30589
|
-
return _t("Stacked barchart");
|
|
30590
|
-
}
|
|
30591
30967
|
onUpdateStacked(stacked) {
|
|
30592
30968
|
this.props.updateChart(this.props.figureId, {
|
|
30593
30969
|
stacked,
|
|
@@ -31577,6 +31953,9 @@ class ChartWithAxisDesignPanel extends owl.Component {
|
|
|
31577
31953
|
return "left";
|
|
31578
31954
|
return dataSets[this.state.index].yAxisId === "y1" ? "right" : "left";
|
|
31579
31955
|
}
|
|
31956
|
+
get canHaveTwoVerticalAxis() {
|
|
31957
|
+
return "horizontal" in this.props.definition ? !this.props.definition.horizontal : true;
|
|
31958
|
+
}
|
|
31580
31959
|
updateDataSeriesLabel(ev) {
|
|
31581
31960
|
const label = ev.target.value;
|
|
31582
31961
|
const dataSets = this.props.definition.dataSets;
|
|
@@ -31741,19 +32120,13 @@ class LineConfigPanel extends GenericChartConfigPanel {
|
|
|
31741
32120
|
}
|
|
31742
32121
|
return false;
|
|
31743
32122
|
}
|
|
31744
|
-
get stackedLabel() {
|
|
31745
|
-
return _t("Stacked linechart");
|
|
31746
|
-
}
|
|
31747
|
-
get cumulativeLabel() {
|
|
31748
|
-
return _t("Cumulative data");
|
|
31749
|
-
}
|
|
31750
32123
|
getLabelRangeOptions() {
|
|
31751
32124
|
const options = super.getLabelRangeOptions();
|
|
31752
32125
|
if (this.canTreatLabelsAsText) {
|
|
31753
32126
|
options.push({
|
|
31754
32127
|
name: "labelsAsText",
|
|
31755
32128
|
value: this.props.definition.labelsAsText,
|
|
31756
|
-
label:
|
|
32129
|
+
label: this.chartTerms.TreatLabelsAsText,
|
|
31757
32130
|
onChange: this.onUpdateLabelsAsText.bind(this),
|
|
31758
32131
|
});
|
|
31759
32132
|
}
|
|
@@ -31820,7 +32193,7 @@ class ScatterConfigPanel extends GenericChartConfigPanel {
|
|
|
31820
32193
|
options.push({
|
|
31821
32194
|
name: "labelsAsText",
|
|
31822
32195
|
value: this.props.definition.labelsAsText,
|
|
31823
|
-
label:
|
|
32196
|
+
label: this.chartTerms.TreatLabelsAsText,
|
|
31824
32197
|
onChange: this.onUpdateLabelsAsText.bind(this),
|
|
31825
32198
|
});
|
|
31826
32199
|
}
|
|
@@ -32027,8 +32400,106 @@ chartSidePanelComponentRegistry
|
|
|
32027
32400
|
.add("waterfall", {
|
|
32028
32401
|
configuration: GenericChartConfigPanel,
|
|
32029
32402
|
design: WaterfallChartDesignPanel,
|
|
32403
|
+
})
|
|
32404
|
+
.add("pyramid", {
|
|
32405
|
+
configuration: GenericChartConfigPanel,
|
|
32406
|
+
design: ChartWithAxisDesignPanel,
|
|
32030
32407
|
});
|
|
32031
32408
|
|
|
32409
|
+
css /* scss */ `
|
|
32410
|
+
.o-section .o-type-selector {
|
|
32411
|
+
height: 30px;
|
|
32412
|
+
padding-left: 30px;
|
|
32413
|
+
}
|
|
32414
|
+
.o-type-selector-preview {
|
|
32415
|
+
left: 5px;
|
|
32416
|
+
top: 3px;
|
|
32417
|
+
.o-chart-preview {
|
|
32418
|
+
width: 24px;
|
|
32419
|
+
height: 24px;
|
|
32420
|
+
}
|
|
32421
|
+
}
|
|
32422
|
+
|
|
32423
|
+
.o-popover .o-chart-select-popover {
|
|
32424
|
+
box-sizing: border-box;
|
|
32425
|
+
background: #fff;
|
|
32426
|
+
.o-chart-type-item {
|
|
32427
|
+
cursor: pointer;
|
|
32428
|
+
padding: 3px 6px;
|
|
32429
|
+
margin: 1px 2px;
|
|
32430
|
+
&.selected,
|
|
32431
|
+
&:hover {
|
|
32432
|
+
background: #f5f5f5;
|
|
32433
|
+
border: 1px solid #ccc;
|
|
32434
|
+
padding: 2px 5px;
|
|
32435
|
+
}
|
|
32436
|
+
.o-chart-preview {
|
|
32437
|
+
width: 48px;
|
|
32438
|
+
height: 48px;
|
|
32439
|
+
}
|
|
32440
|
+
}
|
|
32441
|
+
}
|
|
32442
|
+
`;
|
|
32443
|
+
class ChartTypePicker extends owl.Component {
|
|
32444
|
+
static template = "o-spreadsheet-ChartTypePicker";
|
|
32445
|
+
static components = { Section, Popover };
|
|
32446
|
+
static props = { figureId: String, chartPanelStore: Object };
|
|
32447
|
+
categories = chartCategories;
|
|
32448
|
+
chartTypeByCategories = {};
|
|
32449
|
+
popoverRef = owl.useRef("popoverRef");
|
|
32450
|
+
selectRef = owl.useRef("selectRef");
|
|
32451
|
+
state = owl.useState({ popoverProps: undefined, popoverStyle: "" });
|
|
32452
|
+
setup() {
|
|
32453
|
+
owl.useExternalListener(window, "pointerdown", this.onExternalClick, { capture: true });
|
|
32454
|
+
for (const subtypeProperties of chartSubtypeRegistry.getAll()) {
|
|
32455
|
+
if (this.chartTypeByCategories[subtypeProperties.category]) {
|
|
32456
|
+
this.chartTypeByCategories[subtypeProperties.category].push(subtypeProperties);
|
|
32457
|
+
}
|
|
32458
|
+
else {
|
|
32459
|
+
this.chartTypeByCategories[subtypeProperties.category] = [subtypeProperties];
|
|
32460
|
+
}
|
|
32461
|
+
}
|
|
32462
|
+
}
|
|
32463
|
+
onExternalClick(ev) {
|
|
32464
|
+
if (isChildEvent(this.popoverRef.el?.parentElement, ev) ||
|
|
32465
|
+
isChildEvent(this.selectRef.el, ev)) {
|
|
32466
|
+
return;
|
|
32467
|
+
}
|
|
32468
|
+
this.closePopover();
|
|
32469
|
+
}
|
|
32470
|
+
onTypeChange(type) {
|
|
32471
|
+
this.props.chartPanelStore.changeChartType(this.props.figureId, type);
|
|
32472
|
+
this.closePopover();
|
|
32473
|
+
}
|
|
32474
|
+
getChartDefinition(figureId) {
|
|
32475
|
+
return this.env.model.getters.getChartDefinition(figureId);
|
|
32476
|
+
}
|
|
32477
|
+
getSelectedChartSubtypeProperties() {
|
|
32478
|
+
const definition = this.getChartDefinition(this.props.figureId);
|
|
32479
|
+
const matchedChart = chartSubtypeRegistry
|
|
32480
|
+
.getAll()
|
|
32481
|
+
.find((c) => c.matcher?.(definition) || false);
|
|
32482
|
+
return matchedChart || chartSubtypeRegistry.get(definition.type);
|
|
32483
|
+
}
|
|
32484
|
+
onPointerDown(ev) {
|
|
32485
|
+
if (this.state.popoverProps) {
|
|
32486
|
+
this.closePopover();
|
|
32487
|
+
return;
|
|
32488
|
+
}
|
|
32489
|
+
const target = ev.currentTarget;
|
|
32490
|
+
const { bottom, right, width } = target.getBoundingClientRect();
|
|
32491
|
+
this.state.popoverProps = {
|
|
32492
|
+
anchorRect: { x: right, y: bottom, width: 0, height: 0 },
|
|
32493
|
+
positioning: "TopRight",
|
|
32494
|
+
verticalOffset: 0,
|
|
32495
|
+
};
|
|
32496
|
+
this.state.popoverStyle = cssPropertiesToCss({ width: `${width}px` });
|
|
32497
|
+
}
|
|
32498
|
+
closePopover() {
|
|
32499
|
+
this.state.popoverProps = undefined;
|
|
32500
|
+
}
|
|
32501
|
+
}
|
|
32502
|
+
|
|
32032
32503
|
class MainChartPanelStore extends SpreadsheetStore {
|
|
32033
32504
|
mutators = ["activatePanel", "changeChartType"];
|
|
32034
32505
|
panel = "configuration";
|
|
@@ -32036,7 +32507,7 @@ class MainChartPanelStore extends SpreadsheetStore {
|
|
|
32036
32507
|
activatePanel(panel) {
|
|
32037
32508
|
this.panel = panel;
|
|
32038
32509
|
}
|
|
32039
|
-
changeChartType(figureId,
|
|
32510
|
+
changeChartType(figureId, newDisplayType) {
|
|
32040
32511
|
this.creationContext = {
|
|
32041
32512
|
...this.creationContext,
|
|
32042
32513
|
...this.getters.getContextCreationChart(figureId),
|
|
@@ -32045,13 +32516,25 @@ class MainChartPanelStore extends SpreadsheetStore {
|
|
|
32045
32516
|
if (!sheetId) {
|
|
32046
32517
|
return;
|
|
32047
32518
|
}
|
|
32048
|
-
const definition = getChartDefinitionFromContextCreation(
|
|
32519
|
+
const definition = this.getChartDefinitionFromContextCreation(figureId, newDisplayType);
|
|
32049
32520
|
this.model.dispatch("UPDATE_CHART", {
|
|
32050
32521
|
definition,
|
|
32051
32522
|
id: figureId,
|
|
32052
32523
|
sheetId,
|
|
32053
32524
|
});
|
|
32054
32525
|
}
|
|
32526
|
+
getChartDefinitionFromContextCreation(figureId, newDisplayType) {
|
|
32527
|
+
const newChartInfo = chartSubtypeRegistry.get(newDisplayType);
|
|
32528
|
+
const ChartClass = chartRegistry.get(newChartInfo.chartType);
|
|
32529
|
+
const contextCreation = {
|
|
32530
|
+
...this.creationContext,
|
|
32531
|
+
...this.getters.getContextCreationChart(figureId),
|
|
32532
|
+
};
|
|
32533
|
+
return {
|
|
32534
|
+
...ChartClass.getChartDefinitionFromContextCreation(contextCreation),
|
|
32535
|
+
...newChartInfo.subtypeDefinition,
|
|
32536
|
+
};
|
|
32537
|
+
}
|
|
32055
32538
|
}
|
|
32056
32539
|
|
|
32057
32540
|
css /* scss */ `
|
|
@@ -32080,7 +32563,7 @@ css /* scss */ `
|
|
|
32080
32563
|
`;
|
|
32081
32564
|
class ChartPanel extends owl.Component {
|
|
32082
32565
|
static template = "o-spreadsheet-ChartPanel";
|
|
32083
|
-
static components = { Section };
|
|
32566
|
+
static components = { Section, ChartTypePicker };
|
|
32084
32567
|
static props = { onCloseSidePanel: Function, figureId: String };
|
|
32085
32568
|
store;
|
|
32086
32569
|
get figureId() {
|
|
@@ -32140,9 +32623,6 @@ class ChartPanel extends owl.Component {
|
|
|
32140
32623
|
getChartDefinition(figureId) {
|
|
32141
32624
|
return this.env.model.getters.getChartDefinition(figureId);
|
|
32142
32625
|
}
|
|
32143
|
-
get chartTypes() {
|
|
32144
|
-
return getChartTypes();
|
|
32145
|
-
}
|
|
32146
32626
|
}
|
|
32147
32627
|
|
|
32148
32628
|
css /* scss */ `
|
|
@@ -34147,6 +34627,10 @@ class FindAndReplaceStore extends SpreadsheetStore {
|
|
|
34147
34627
|
case "ACTIVATE_SHEET":
|
|
34148
34628
|
this.isSearchDirty = true;
|
|
34149
34629
|
break;
|
|
34630
|
+
case "REPLACE_SEARCH":
|
|
34631
|
+
for (const match of cmd.matches) {
|
|
34632
|
+
this.replaceMatch(match, cmd.searchString, cmd.replaceWith, cmd.searchOptions);
|
|
34633
|
+
}
|
|
34150
34634
|
}
|
|
34151
34635
|
}
|
|
34152
34636
|
finalize() {
|
|
@@ -34320,6 +34804,21 @@ class FindAndReplaceStore extends SpreadsheetStore {
|
|
|
34320
34804
|
searchOptions: this.searchOptions,
|
|
34321
34805
|
});
|
|
34322
34806
|
}
|
|
34807
|
+
replaceMatch(selectedMatch, searchString, replaceWith, searchOptions) {
|
|
34808
|
+
const cell = this.getters.getCell(selectedMatch);
|
|
34809
|
+
if (!cell?.content) {
|
|
34810
|
+
return;
|
|
34811
|
+
}
|
|
34812
|
+
if (cell?.isFormula && !searchOptions.searchFormulas) {
|
|
34813
|
+
return;
|
|
34814
|
+
}
|
|
34815
|
+
const searchRegex = getSearchRegex(searchString, searchOptions);
|
|
34816
|
+
const replaceRegex = new RegExp(searchRegex.source, searchRegex.flags + "g");
|
|
34817
|
+
const toReplace = this.getters.getCellText(selectedMatch, searchOptions.searchFormulas);
|
|
34818
|
+
const content = toReplace.replace(replaceRegex, replaceWith);
|
|
34819
|
+
const canonicalContent = canonicalizeNumberContent(content, this.getters.getLocale());
|
|
34820
|
+
this.model.dispatch("UPDATE_CELL", { ...selectedMatch, content: canonicalContent });
|
|
34821
|
+
}
|
|
34323
34822
|
getSearchableString(position) {
|
|
34324
34823
|
return this.getters.getCellText(position, this.searchOptions.searchFormulas);
|
|
34325
34824
|
}
|
|
@@ -35071,7 +35570,7 @@ function createMeasure(fields, measure) {
|
|
|
35071
35570
|
function createPivotDimension(fields, dimension) {
|
|
35072
35571
|
const field = fields[dimension.name];
|
|
35073
35572
|
const type = field?.type ?? "integer";
|
|
35074
|
-
const granularity = field && isDateField(field) ? dimension.granularity
|
|
35573
|
+
const granularity = field && isDateField(field) ? dimension.granularity : undefined;
|
|
35075
35574
|
return {
|
|
35076
35575
|
/**
|
|
35077
35576
|
* Get the display name of the dimension
|
|
@@ -35216,9 +35715,17 @@ class SpreadsheetPivotTable {
|
|
|
35216
35715
|
}
|
|
35217
35716
|
getPivotCell(col, row, includeTotal = true) {
|
|
35218
35717
|
const colHeadersHeight = this.columns.length;
|
|
35219
|
-
if (row
|
|
35718
|
+
if (col > 0 && row === colHeadersHeight - 1) {
|
|
35719
|
+
const domain = this.getColHeaderDomain(col, row);
|
|
35720
|
+
if (!domain) {
|
|
35721
|
+
return EMPTY_PIVOT_CELL;
|
|
35722
|
+
}
|
|
35723
|
+
const measure = domain.at(-1)?.value.toString() || "";
|
|
35724
|
+
return { type: "MEASURE_HEADER", domain: domain.slice(0, -1), measure };
|
|
35725
|
+
}
|
|
35726
|
+
else if (row <= colHeadersHeight - 1) {
|
|
35220
35727
|
const domain = this.getColHeaderDomain(col, row);
|
|
35221
|
-
return domain ? { type: "HEADER", domain } :
|
|
35728
|
+
return domain ? { type: "HEADER", domain } : EMPTY_PIVOT_CELL;
|
|
35222
35729
|
}
|
|
35223
35730
|
else if (col === 0) {
|
|
35224
35731
|
const rowIndex = row - colHeadersHeight;
|
|
@@ -35228,7 +35735,7 @@ class SpreadsheetPivotTable {
|
|
|
35228
35735
|
else {
|
|
35229
35736
|
const rowIndex = row - colHeadersHeight;
|
|
35230
35737
|
if (!includeTotal && this.isTotalRow(rowIndex)) {
|
|
35231
|
-
return
|
|
35738
|
+
return EMPTY_PIVOT_CELL;
|
|
35232
35739
|
}
|
|
35233
35740
|
const domain = [...this.getRowDomain(rowIndex), ...this.getColDomain(col)];
|
|
35234
35741
|
const measure = this.getColMeasure(col);
|
|
@@ -35282,6 +35789,7 @@ class SpreadsheetPivotTable {
|
|
|
35282
35789
|
};
|
|
35283
35790
|
}
|
|
35284
35791
|
}
|
|
35792
|
+
const EMPTY_PIVOT_CELL = { type: "EMPTY" };
|
|
35285
35793
|
|
|
35286
35794
|
/**
|
|
35287
35795
|
* This function converts a list of data entry into a spreadsheet pivot table.
|
|
@@ -35475,10 +35983,13 @@ function compareDimensionValues(dimension, a, b) {
|
|
|
35475
35983
|
}
|
|
35476
35984
|
|
|
35477
35985
|
function createDate(dimension, value, locale) {
|
|
35478
|
-
const granularity = dimension.granularity
|
|
35479
|
-
if (!(granularity in MAP_VALUE_DIMENSION_DATE)) {
|
|
35986
|
+
const granularity = dimension.granularity;
|
|
35987
|
+
if (!granularity || !(granularity in MAP_VALUE_DIMENSION_DATE)) {
|
|
35480
35988
|
throw new Error(`Unknown date granularity: ${granularity}`);
|
|
35481
35989
|
}
|
|
35990
|
+
if (value === null) {
|
|
35991
|
+
return null;
|
|
35992
|
+
}
|
|
35482
35993
|
if (!MAP_VALUE_DIMENSION_DATE[granularity].set.has(value)) {
|
|
35483
35994
|
MAP_VALUE_DIMENSION_DATE[granularity].set.add(value);
|
|
35484
35995
|
const date = toJsDate(value, locale);
|
|
@@ -35709,14 +36220,17 @@ class SpreadsheetPivot {
|
|
|
35709
36220
|
if (dimension.type === "date") {
|
|
35710
36221
|
const adapter = pivotTimeAdapter(dimension.granularity);
|
|
35711
36222
|
return {
|
|
35712
|
-
value:
|
|
36223
|
+
value: lastNode.value !== "null"
|
|
36224
|
+
? adapter.toCellValue(toNormalizedPivotValue(dimension, lastNode.value))
|
|
36225
|
+
: _t("(Undefined)"),
|
|
35713
36226
|
format: adapter.getFormat(this.getters.getLocale()),
|
|
35714
36227
|
};
|
|
35715
36228
|
}
|
|
35716
36229
|
if (!finalCell) {
|
|
35717
36230
|
return { value: "" };
|
|
35718
36231
|
}
|
|
35719
|
-
|
|
36232
|
+
// Value can be null but stringified (e.g. an empty date, as for now every date is stringified)
|
|
36233
|
+
if (finalCell.value === null || finalCell.value === `${null}`) {
|
|
35720
36234
|
return { value: _t("(Undefined)") };
|
|
35721
36235
|
}
|
|
35722
36236
|
return {
|
|
@@ -35738,10 +36252,15 @@ class SpreadsheetPivot {
|
|
|
35738
36252
|
if (!operator) {
|
|
35739
36253
|
throw new Error(`Aggregator ${aggregator} does not exist`);
|
|
35740
36254
|
}
|
|
35741
|
-
|
|
35742
|
-
|
|
35743
|
-
|
|
35744
|
-
|
|
36255
|
+
try {
|
|
36256
|
+
return {
|
|
36257
|
+
value: values.length ? operator.fn([values], this.getters.getLocale()) : "",
|
|
36258
|
+
format: operator.format(values[0]),
|
|
36259
|
+
};
|
|
36260
|
+
}
|
|
36261
|
+
catch (e) {
|
|
36262
|
+
return handleError(e, aggregator.toUpperCase());
|
|
36263
|
+
}
|
|
35745
36264
|
}
|
|
35746
36265
|
getPossibleFieldValues(dimension) {
|
|
35747
36266
|
const values = [];
|
|
@@ -51108,12 +51627,12 @@ class PivotCorePlugin extends CorePlugin {
|
|
|
51108
51627
|
for (let col = 0; col < pivotCells.length; col++) {
|
|
51109
51628
|
for (let row = 0; row < pivotCells[col].length; row++) {
|
|
51110
51629
|
const pivotCell = pivotCells[col][row];
|
|
51111
|
-
|
|
51630
|
+
this.dispatch("UPDATE_CELL", {
|
|
51112
51631
|
sheetId: position.sheetId,
|
|
51113
51632
|
col: position.col + col,
|
|
51114
51633
|
row: position.row + row,
|
|
51115
|
-
|
|
51116
|
-
|
|
51634
|
+
content: makePivotFormulaFromPivotCell(formulaId, pivotCell),
|
|
51635
|
+
});
|
|
51117
51636
|
}
|
|
51118
51637
|
}
|
|
51119
51638
|
}
|
|
@@ -51143,21 +51662,6 @@ class PivotCorePlugin extends CorePlugin {
|
|
|
51143
51662
|
});
|
|
51144
51663
|
}
|
|
51145
51664
|
}
|
|
51146
|
-
addPivotFormula(position, formulaId, pivotCell) {
|
|
51147
|
-
let content = undefined;
|
|
51148
|
-
switch (pivotCell.type) {
|
|
51149
|
-
case "HEADER":
|
|
51150
|
-
content = makePivotFormula("PIVOT.HEADER", [formulaId, ...flatPivotDomain(pivotCell.domain)].filter(isDefined));
|
|
51151
|
-
break;
|
|
51152
|
-
case "VALUE":
|
|
51153
|
-
content = makePivotFormula("PIVOT.VALUE", [formulaId, pivotCell.measure, ...flatPivotDomain(pivotCell.domain)].filter(isDefined));
|
|
51154
|
-
break;
|
|
51155
|
-
}
|
|
51156
|
-
this.dispatch("UPDATE_CELL", {
|
|
51157
|
-
...position,
|
|
51158
|
-
content,
|
|
51159
|
-
});
|
|
51160
|
-
}
|
|
51161
51665
|
getPivotCore(pivotId) {
|
|
51162
51666
|
const pivot = this.pivots[pivotId];
|
|
51163
51667
|
if (!pivot) {
|
|
@@ -52822,7 +53326,6 @@ class Evaluator {
|
|
|
52822
53326
|
if (!this.blockedArrayFormulas.has(position)) {
|
|
52823
53327
|
this.invalidateSpreading(position);
|
|
52824
53328
|
}
|
|
52825
|
-
this.spreadingRelations.removeNode(position);
|
|
52826
53329
|
const cell = this.getters.getCell(position);
|
|
52827
53330
|
if (cell === undefined) {
|
|
52828
53331
|
return EMPTY_CELL;
|
|
@@ -52864,6 +53367,7 @@ class Evaluator {
|
|
|
52864
53367
|
this.assertSheetHasEnoughSpaceToSpreadFormulaResult(formulaPosition, formulaReturn);
|
|
52865
53368
|
const nbColumns = formulaReturn.length;
|
|
52866
53369
|
const nbRows = formulaReturn[0].length;
|
|
53370
|
+
this.spreadingRelations.removeNode(formulaPosition);
|
|
52867
53371
|
forEachSpreadPositionInMatrix(nbColumns, nbRows, this.updateSpreadRelation(formulaPosition));
|
|
52868
53372
|
this.assertNoMergedCellsInSpreadZone(formulaPosition, formulaReturn);
|
|
52869
53373
|
forEachSpreadPositionInMatrix(nbColumns, nbRows, this.checkCollision(formulaPosition));
|
|
@@ -54391,9 +54895,10 @@ class PivotUIPlugin extends UIPlugin {
|
|
|
54391
54895
|
"getPivot",
|
|
54392
54896
|
"getFirstPivotFunction",
|
|
54393
54897
|
"getPivotIdFromPosition",
|
|
54394
|
-
"
|
|
54898
|
+
"getPivotCellFromPosition",
|
|
54395
54899
|
"isPivotUnused",
|
|
54396
54900
|
"areDomainArgsFieldsValid",
|
|
54901
|
+
"isSpillPivotFormula",
|
|
54397
54902
|
];
|
|
54398
54903
|
pivots = {};
|
|
54399
54904
|
unusedPivots;
|
|
@@ -54472,6 +54977,14 @@ class PivotUIPlugin extends UIPlugin {
|
|
|
54472
54977
|
}
|
|
54473
54978
|
return undefined;
|
|
54474
54979
|
}
|
|
54980
|
+
isSpillPivotFormula(position) {
|
|
54981
|
+
const cell = this.getters.getCorrespondingFormulaCell(position);
|
|
54982
|
+
if (cell && cell.isFormula) {
|
|
54983
|
+
const pivotFunction = this.getFirstPivotFunction(cell.compiledFormula.tokens);
|
|
54984
|
+
return pivotFunction?.functionName === "PIVOT";
|
|
54985
|
+
}
|
|
54986
|
+
return false;
|
|
54987
|
+
}
|
|
54475
54988
|
getFirstPivotFunction(tokens) {
|
|
54476
54989
|
const pivotFunction = getFirstPivotFunction(tokens);
|
|
54477
54990
|
if (!pivotFunction) {
|
|
@@ -54505,29 +55018,29 @@ class PivotUIPlugin extends UIPlugin {
|
|
|
54505
55018
|
* If the cell is the result of PIVOT, the result is the domain of the cell
|
|
54506
55019
|
* as if it was the individual pivot formula
|
|
54507
55020
|
*/
|
|
54508
|
-
|
|
55021
|
+
getPivotCellFromPosition(position) {
|
|
54509
55022
|
const cell = this.getters.getCorrespondingFormulaCell(position);
|
|
54510
55023
|
if (!cell || !cell.isFormula || getNumberOfPivotFunctions(cell.compiledFormula.tokens) === 0) {
|
|
54511
|
-
return
|
|
55024
|
+
return EMPTY_PIVOT_CELL;
|
|
54512
55025
|
}
|
|
54513
55026
|
const mainPosition = this.getters.getCellPosition(cell.id);
|
|
54514
55027
|
const result = this.getters.getFirstPivotFunction(cell.compiledFormula.tokens);
|
|
54515
55028
|
if (!result) {
|
|
54516
|
-
return
|
|
55029
|
+
return EMPTY_PIVOT_CELL;
|
|
54517
55030
|
}
|
|
54518
55031
|
const { functionName, args } = result;
|
|
54519
55032
|
if (functionName === "PIVOT") {
|
|
54520
55033
|
const formulaId = args[0];
|
|
54521
55034
|
if (!formulaId) {
|
|
54522
|
-
return
|
|
55035
|
+
return EMPTY_PIVOT_CELL;
|
|
54523
55036
|
}
|
|
54524
55037
|
const pivotId = this.getters.getPivotId(formulaId.toString());
|
|
54525
55038
|
if (!pivotId) {
|
|
54526
|
-
return
|
|
55039
|
+
return EMPTY_PIVOT_CELL;
|
|
54527
55040
|
}
|
|
54528
55041
|
const pivot = this.getPivot(pivotId);
|
|
54529
55042
|
if (!pivot.isValid()) {
|
|
54530
|
-
return
|
|
55043
|
+
return EMPTY_PIVOT_CELL;
|
|
54531
55044
|
}
|
|
54532
55045
|
const includeTotal = args[2] === false ? false : undefined;
|
|
54533
55046
|
const includeColumnHeaders = args[3] === false ? false : undefined;
|
|
@@ -54536,22 +55049,28 @@ class PivotUIPlugin extends UIPlugin {
|
|
|
54536
55049
|
.getPivotCells(includeTotal, includeColumnHeaders);
|
|
54537
55050
|
const pivotCol = position.col - mainPosition.col;
|
|
54538
55051
|
const pivotRow = position.row - mainPosition.row;
|
|
54539
|
-
|
|
54540
|
-
|
|
54541
|
-
|
|
54542
|
-
}
|
|
54543
|
-
|
|
54544
|
-
|
|
54545
|
-
domain
|
|
54546
|
-
|
|
54547
|
-
|
|
55052
|
+
return pivotCells[pivotCol][pivotRow];
|
|
55053
|
+
}
|
|
55054
|
+
if (functionName === "PIVOT.HEADER" && args.at(-2) === "measure") {
|
|
55055
|
+
const domain = toPivotDomain(args.slice(1, -2).map((x) => `${x}`));
|
|
55056
|
+
return {
|
|
55057
|
+
type: "MEASURE_HEADER",
|
|
55058
|
+
domain,
|
|
55059
|
+
measure: args.at(-1)?.toString() || "",
|
|
55060
|
+
};
|
|
54548
55061
|
}
|
|
54549
|
-
|
|
54550
|
-
|
|
54551
|
-
|
|
55062
|
+
else if (functionName === "PIVOT.HEADER") {
|
|
55063
|
+
return {
|
|
55064
|
+
type: "HEADER",
|
|
55065
|
+
domain: toPivotDomain(args.slice(1).map((x) => `${x}`)),
|
|
55066
|
+
};
|
|
54552
55067
|
}
|
|
54553
|
-
const
|
|
54554
|
-
return {
|
|
55068
|
+
const [measure, ...domainArgs] = args.slice(1);
|
|
55069
|
+
return {
|
|
55070
|
+
type: "VALUE",
|
|
55071
|
+
domain: toPivotDomain(domainArgs.map((x) => `${x}`)),
|
|
55072
|
+
measure: measure?.toString() || "",
|
|
55073
|
+
};
|
|
54555
55074
|
}
|
|
54556
55075
|
getPivot(pivotId) {
|
|
54557
55076
|
return this.pivots[pivotId];
|
|
@@ -56422,43 +56941,6 @@ class DataCleanupPlugin extends UIPlugin {
|
|
|
56422
56941
|
}
|
|
56423
56942
|
}
|
|
56424
56943
|
|
|
56425
|
-
/**
|
|
56426
|
-
* Find and Replace Plugin
|
|
56427
|
-
*
|
|
56428
|
-
* This plugin is used in combination with the find_and_replace sidePanel
|
|
56429
|
-
* It is used to 'highlight' cells that match an input string according to
|
|
56430
|
-
* the given searchOptions. The second part of this plugin makes it possible
|
|
56431
|
-
* (again with the find_and_replace sidePanel), to replace the values that match
|
|
56432
|
-
* the search with a new value.
|
|
56433
|
-
*/
|
|
56434
|
-
class FindAndReplacePlugin extends UIPlugin {
|
|
56435
|
-
static getters = [];
|
|
56436
|
-
handle(cmd) {
|
|
56437
|
-
switch (cmd.type) {
|
|
56438
|
-
case "REPLACE_SEARCH":
|
|
56439
|
-
for (const match of cmd.matches) {
|
|
56440
|
-
this.replaceMatch(match, cmd.searchString, cmd.replaceWith, cmd.searchOptions);
|
|
56441
|
-
}
|
|
56442
|
-
break;
|
|
56443
|
-
}
|
|
56444
|
-
}
|
|
56445
|
-
replaceMatch(selectedMatch, searchString, replaceWith, searchOptions) {
|
|
56446
|
-
const cell = this.getters.getCell(selectedMatch);
|
|
56447
|
-
if (!cell?.content) {
|
|
56448
|
-
return;
|
|
56449
|
-
}
|
|
56450
|
-
if (cell?.isFormula && !searchOptions.searchFormulas) {
|
|
56451
|
-
return;
|
|
56452
|
-
}
|
|
56453
|
-
const searchRegex = getSearchRegex(searchString, searchOptions);
|
|
56454
|
-
const replaceRegex = new RegExp(searchRegex.source, searchRegex.flags + "g");
|
|
56455
|
-
const toReplace = this.getters.getCellText(selectedMatch, searchOptions.searchFormulas);
|
|
56456
|
-
const content = toReplace.replace(replaceRegex, replaceWith);
|
|
56457
|
-
const canonicalContent = canonicalizeNumberContent(content, this.getters.getLocale());
|
|
56458
|
-
this.dispatch("UPDATE_CELL", { ...selectedMatch, content: canonicalContent });
|
|
56459
|
-
}
|
|
56460
|
-
}
|
|
56461
|
-
|
|
56462
56944
|
class FormatPlugin extends UIPlugin {
|
|
56463
56945
|
// ---------------------------------------------------------------------------
|
|
56464
56946
|
// Command Handling
|
|
@@ -60273,7 +60755,6 @@ const featurePluginRegistry = new Registry()
|
|
|
60273
60755
|
.add("ui_sheet", SheetUIPlugin)
|
|
60274
60756
|
.add("ui_options", UIOptionsPlugin)
|
|
60275
60757
|
.add("autofill", AutofillPlugin)
|
|
60276
|
-
.add("find_and_replace", FindAndReplacePlugin)
|
|
60277
60758
|
.add("sort", SortPlugin)
|
|
60278
60759
|
.add("automatic_sum", AutomaticSumPlugin)
|
|
60279
60760
|
.add("format", FormatPlugin)
|
|
@@ -63834,6 +64315,9 @@ class SelectionStreamProcessorImpl {
|
|
|
63834
64315
|
getBackToDefault() {
|
|
63835
64316
|
this.stream.getBackToDefault();
|
|
63836
64317
|
}
|
|
64318
|
+
getAnchor() {
|
|
64319
|
+
return this.anchor;
|
|
64320
|
+
}
|
|
63837
64321
|
modifyAnchor(anchor, mode, options) {
|
|
63838
64322
|
const sheetId = this.getters.getActiveSheetId();
|
|
63839
64323
|
anchor = {
|
|
@@ -66797,6 +67281,7 @@ const registries = {
|
|
|
66797
67281
|
chartSidePanelComponentRegistry,
|
|
66798
67282
|
chartComponentRegistry,
|
|
66799
67283
|
chartRegistry,
|
|
67284
|
+
chartSubtypeRegistry,
|
|
66800
67285
|
topbarMenuRegistry,
|
|
66801
67286
|
topbarComponentRegistry,
|
|
66802
67287
|
clickableCellRegistry,
|
|
@@ -66905,6 +67390,7 @@ const components = {
|
|
|
66905
67390
|
GaugeChartDesignPanel,
|
|
66906
67391
|
ScorecardChartConfigPanel,
|
|
66907
67392
|
ScorecardChartDesignPanel,
|
|
67393
|
+
ChartTypePicker,
|
|
66908
67394
|
FigureComponent,
|
|
66909
67395
|
Menu,
|
|
66910
67396
|
Popover,
|
|
@@ -66954,6 +67440,7 @@ const constants = {
|
|
|
66954
67440
|
DEFAULT_LOCALE,
|
|
66955
67441
|
HIGHLIGHT_COLOR,
|
|
66956
67442
|
PIVOT_TABLE_CONFIG,
|
|
67443
|
+
ChartTerms,
|
|
66957
67444
|
};
|
|
66958
67445
|
|
|
66959
67446
|
exports.AbstractCellClipboardHandler = AbstractCellClipboardHandler;
|
|
@@ -67002,6 +67489,6 @@ exports.tokenColors = tokenColors;
|
|
|
67002
67489
|
exports.tokenize = tokenize;
|
|
67003
67490
|
|
|
67004
67491
|
|
|
67005
|
-
__info__.version = "17.4.0-alpha.
|
|
67006
|
-
__info__.date = "2024-06-
|
|
67007
|
-
__info__.hash = "
|
|
67492
|
+
__info__.version = "17.4.0-alpha.7";
|
|
67493
|
+
__info__.date = "2024-06-21T08:42:22.370Z";
|
|
67494
|
+
__info__.hash = "a99db9a";
|