@odoo/o-spreadsheet 17.4.0-alpha.4 → 17.4.0-alpha.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/o-spreadsheet.cjs.js +1233 -745
- package/dist/o-spreadsheet.d.ts +231 -109
- package/dist/o-spreadsheet.esm.js +1233 -745
- package/dist/o-spreadsheet.iife.js +1233 -745
- package/dist/o-spreadsheet.iife.min.js +383 -350
- package/dist/o_spreadsheet.xml +257 -24
- package/package.json +1 -1
|
@@ -3,9 +3,9 @@
|
|
|
3
3
|
/**
|
|
4
4
|
* This file is generated by o-spreadsheet build tools. Do not edit it.
|
|
5
5
|
* @see https://github.com/odoo/o-spreadsheet
|
|
6
|
-
* @version 17.4.0-alpha.
|
|
7
|
-
* @date 2024-06-
|
|
8
|
-
* @hash
|
|
6
|
+
* @version 17.4.0-alpha.6
|
|
7
|
+
* @date 2024-06-19T13:46:27.157Z
|
|
8
|
+
* @hash a4f22e4
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
11
|
(function (exports, owl) {
|
|
@@ -5283,7 +5283,7 @@
|
|
|
5283
5283
|
return text;
|
|
5284
5284
|
}
|
|
5285
5285
|
const ellipsis = "…";
|
|
5286
|
-
const ellipsisWidth = computeCachedTextWidth(ctx,
|
|
5286
|
+
const ellipsisWidth = computeCachedTextWidth(ctx, ellipsis);
|
|
5287
5287
|
if (width <= ellipsisWidth) {
|
|
5288
5288
|
return text;
|
|
5289
5289
|
}
|
|
@@ -6201,7 +6201,7 @@
|
|
|
6201
6201
|
};
|
|
6202
6202
|
}
|
|
6203
6203
|
isPasteAllowed(sheetId, target, content, clipboardOptions) {
|
|
6204
|
-
if (!
|
|
6204
|
+
if (!content.cells) {
|
|
6205
6205
|
return "Success" /* CommandResult.Success */;
|
|
6206
6206
|
}
|
|
6207
6207
|
if (clipboardOptions?.isCutOperation && clipboardOptions?.pasteOption !== undefined) {
|
|
@@ -6215,6 +6215,17 @@
|
|
|
6215
6215
|
return "WrongPasteSelection" /* CommandResult.WrongPasteSelection */;
|
|
6216
6216
|
}
|
|
6217
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
|
+
}
|
|
6218
6229
|
return "Success" /* CommandResult.Success */;
|
|
6219
6230
|
}
|
|
6220
6231
|
/**
|
|
@@ -6454,13 +6465,22 @@
|
|
|
6454
6465
|
}
|
|
6455
6466
|
const { rowsIndexes, columnsIndexes } = data;
|
|
6456
6467
|
const sheetId = data.sheetId;
|
|
6457
|
-
const
|
|
6458
|
-
|
|
6459
|
-
|
|
6460
|
-
|
|
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 };
|
|
6461
6481
|
}
|
|
6462
6482
|
paste(target, clippedContent, options) {
|
|
6463
|
-
if (!clippedContent?.
|
|
6483
|
+
if (!clippedContent?.cfRules ||
|
|
6464
6484
|
options?.pasteOption === "asValue" ||
|
|
6465
6485
|
!("zones" in target) ||
|
|
6466
6486
|
!target.zones.length) {
|
|
@@ -6469,7 +6489,7 @@
|
|
|
6469
6489
|
const zones = target.zones;
|
|
6470
6490
|
const sheetId = target.sheetId;
|
|
6471
6491
|
if (!options?.isCutOperation) {
|
|
6472
|
-
this.pasteFromCopy(sheetId, zones, clippedContent.
|
|
6492
|
+
this.pasteFromCopy(sheetId, zones, clippedContent.cfRules, options);
|
|
6473
6493
|
}
|
|
6474
6494
|
else {
|
|
6475
6495
|
this.pasteFromCut(sheetId, zones, clippedContent);
|
|
@@ -6477,12 +6497,12 @@
|
|
|
6477
6497
|
}
|
|
6478
6498
|
pasteFromCut(sheetId, target, content) {
|
|
6479
6499
|
const selection = target[0];
|
|
6480
|
-
this.pasteZone(sheetId, selection.left, selection.top, content.
|
|
6500
|
+
this.pasteZone(sheetId, selection.left, selection.top, content.cfRules, {
|
|
6481
6501
|
isCutOperation: true,
|
|
6482
6502
|
});
|
|
6483
6503
|
}
|
|
6484
|
-
pasteZone(sheetId, col, row,
|
|
6485
|
-
for (const [r, rowCells] of
|
|
6504
|
+
pasteZone(sheetId, col, row, cfRules, clipboardOptions) {
|
|
6505
|
+
for (const [r, rowCells] of cfRules.entries()) {
|
|
6486
6506
|
for (const [c, origin] of rowCells.entries()) {
|
|
6487
6507
|
const position = { col: col + c, row: row + r, sheetId };
|
|
6488
6508
|
this.pasteCf(origin, position, clipboardOptions?.isCutOperation);
|
|
@@ -6490,23 +6510,21 @@
|
|
|
6490
6510
|
}
|
|
6491
6511
|
}
|
|
6492
6512
|
pasteCf(origin, target, isCutOperation) {
|
|
6493
|
-
|
|
6494
|
-
|
|
6495
|
-
for (const
|
|
6496
|
-
|
|
6497
|
-
|
|
6498
|
-
|
|
6499
|
-
|
|
6500
|
-
|
|
6501
|
-
|
|
6502
|
-
|
|
6503
|
-
|
|
6504
|
-
|
|
6505
|
-
|
|
6506
|
-
|
|
6507
|
-
|
|
6508
|
-
this.adaptCFRules(target.sheetId, cfToCopyTo, [zone], []);
|
|
6509
|
-
}
|
|
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], []);
|
|
6510
6528
|
}
|
|
6511
6529
|
}
|
|
6512
6530
|
}
|
|
@@ -6549,13 +6567,20 @@
|
|
|
6549
6567
|
}
|
|
6550
6568
|
const { rowsIndexes, columnsIndexes } = data;
|
|
6551
6569
|
const sheetId = data.sheetId;
|
|
6552
|
-
const
|
|
6553
|
-
|
|
6554
|
-
|
|
6555
|
-
|
|
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 };
|
|
6556
6581
|
}
|
|
6557
6582
|
paste(target, clippedContent, options) {
|
|
6558
|
-
if (!clippedContent?.
|
|
6583
|
+
if (!clippedContent?.dvRules) {
|
|
6559
6584
|
return;
|
|
6560
6585
|
}
|
|
6561
6586
|
if (options?.pasteOption) {
|
|
@@ -6567,7 +6592,7 @@
|
|
|
6567
6592
|
const zones = target.zones;
|
|
6568
6593
|
const sheetId = target.sheetId;
|
|
6569
6594
|
if (!options?.isCutOperation) {
|
|
6570
|
-
this.pasteFromCopy(sheetId, zones, clippedContent.
|
|
6595
|
+
this.pasteFromCopy(sheetId, zones, clippedContent.dvRules);
|
|
6571
6596
|
}
|
|
6572
6597
|
else {
|
|
6573
6598
|
this.pasteFromCut(sheetId, zones, clippedContent);
|
|
@@ -6575,12 +6600,12 @@
|
|
|
6575
6600
|
}
|
|
6576
6601
|
pasteFromCut(sheetId, target, content) {
|
|
6577
6602
|
const selection = target[0];
|
|
6578
|
-
this.pasteZone(sheetId, selection.left, selection.top, content.
|
|
6603
|
+
this.pasteZone(sheetId, selection.left, selection.top, content.dvRules, {
|
|
6579
6604
|
isCutOperation: true,
|
|
6580
6605
|
});
|
|
6581
6606
|
}
|
|
6582
|
-
pasteZone(sheetId, col, row,
|
|
6583
|
-
for (const [r, rowCells] of
|
|
6607
|
+
pasteZone(sheetId, col, row, dvRules, clipboardOptions) {
|
|
6608
|
+
for (const [r, rowCells] of dvRules.entries()) {
|
|
6584
6609
|
for (const [c, origin] of rowCells.entries()) {
|
|
6585
6610
|
const position = { col: col + c, row: row + r, sheetId };
|
|
6586
6611
|
this.pasteDataValidation(origin, position, clipboardOptions?.isCutOperation);
|
|
@@ -6588,41 +6613,43 @@
|
|
|
6588
6613
|
}
|
|
6589
6614
|
}
|
|
6590
6615
|
pasteDataValidation(origin, target, isCutOperation) {
|
|
6591
|
-
|
|
6592
|
-
|
|
6593
|
-
const
|
|
6594
|
-
if (
|
|
6595
|
-
|
|
6596
|
-
|
|
6597
|
-
|
|
6598
|
-
|
|
6599
|
-
}
|
|
6600
|
-
const zone = positionToZone(target);
|
|
6601
|
-
for (const range of rule.ranges) {
|
|
6602
|
-
if (isInside(origin.col, origin.row, range.zone)) {
|
|
6603
|
-
const toRemoveZone = [];
|
|
6604
|
-
if (isCutOperation) {
|
|
6605
|
-
toRemoveZone.push(positionToZone(origin));
|
|
6606
|
-
}
|
|
6607
|
-
if (origin.sheetId === target.sheetId) {
|
|
6608
|
-
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]);
|
|
6609
6624
|
}
|
|
6610
|
-
|
|
6611
|
-
|
|
6612
|
-
|
|
6613
|
-
|
|
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);
|
|
6614
6639
|
}
|
|
6640
|
+
const copyToRule = this.getDataValidationRuleToCopyTo(target.sheetId, rule);
|
|
6641
|
+
this.adaptDataValidationRule(target.sheetId, copyToRule, [zone], []);
|
|
6615
6642
|
}
|
|
6616
6643
|
}
|
|
6617
6644
|
}
|
|
6618
|
-
getDataValidationRuleToCopyTo(targetSheetId, originRule) {
|
|
6645
|
+
getDataValidationRuleToCopyTo(targetSheetId, originRule, newId = true) {
|
|
6619
6646
|
const ruleInTargetSheet = this.getters
|
|
6620
6647
|
.getDataValidationRules(targetSheetId)
|
|
6621
6648
|
.find((rule) => deepEquals(originRule.criterion, rule.criterion) &&
|
|
6622
6649
|
originRule.isBlocking === rule.isBlocking);
|
|
6623
6650
|
return ruleInTargetSheet
|
|
6624
6651
|
? ruleInTargetSheet
|
|
6625
|
-
: { ...originRule, id: this.uuidGenerator.uuidv4(), ranges: [] };
|
|
6652
|
+
: { ...originRule, id: newId ? this.uuidGenerator.uuidv4() : originRule.id, ranges: [] };
|
|
6626
6653
|
}
|
|
6627
6654
|
/**
|
|
6628
6655
|
* Add or remove XCs to a given data validation rule.
|
|
@@ -6714,69 +6741,63 @@
|
|
|
6714
6741
|
}
|
|
6715
6742
|
|
|
6716
6743
|
class MergeClipboardHandler extends AbstractCellClipboardHandler {
|
|
6717
|
-
|
|
6718
|
-
if (!
|
|
6719
|
-
return
|
|
6744
|
+
copy(data) {
|
|
6745
|
+
if (!data.zones.length) {
|
|
6746
|
+
return;
|
|
6720
6747
|
}
|
|
6721
|
-
const
|
|
6722
|
-
const
|
|
6723
|
-
|
|
6724
|
-
|
|
6725
|
-
|
|
6726
|
-
|
|
6727
|
-
|
|
6728
|
-
|
|
6729
|
-
}
|
|
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));
|
|
6730
6756
|
}
|
|
6757
|
+
merges.push(mergesInRow);
|
|
6731
6758
|
}
|
|
6732
|
-
return
|
|
6759
|
+
return { merges };
|
|
6733
6760
|
}
|
|
6734
6761
|
/**
|
|
6735
6762
|
* Paste the clipboard content in the given target
|
|
6736
6763
|
*/
|
|
6737
6764
|
paste(target, content, options) {
|
|
6738
|
-
if (
|
|
6765
|
+
if (!content.merges ||
|
|
6766
|
+
options?.isCutOperation ||
|
|
6767
|
+
!("zones" in target) ||
|
|
6768
|
+
!target.zones.length) {
|
|
6739
6769
|
return;
|
|
6740
6770
|
}
|
|
6741
|
-
this.pasteFromCopy(target.sheetId, target.zones, content.
|
|
6771
|
+
this.pasteFromCopy(target.sheetId, target.zones, content.merges, options);
|
|
6742
6772
|
}
|
|
6743
|
-
pasteZone(sheetId, col, row,
|
|
6744
|
-
for (const [r,
|
|
6745
|
-
for (const [c,
|
|
6746
|
-
if (!origin.position) {
|
|
6747
|
-
continue;
|
|
6748
|
-
}
|
|
6773
|
+
pasteZone(sheetId, col, row, merges) {
|
|
6774
|
+
for (const [r, rowMerges] of merges.entries()) {
|
|
6775
|
+
for (const [c, originMerge] of rowMerges.entries()) {
|
|
6749
6776
|
const position = { col: col + c, row: row + r, sheetId };
|
|
6750
|
-
this.
|
|
6777
|
+
this.pasteMerge(originMerge, position);
|
|
6751
6778
|
}
|
|
6752
6779
|
}
|
|
6753
6780
|
}
|
|
6754
|
-
|
|
6755
|
-
|
|
6756
|
-
|
|
6757
|
-
|
|
6758
|
-
|
|
6759
|
-
|
|
6760
|
-
const { col: mainCellColOrigin, row: mainCellRowOrigin } = this.getters.getMainCellPosition(origin);
|
|
6761
|
-
if (mainCellColOrigin === col && mainCellRowOrigin === row) {
|
|
6762
|
-
const merge = this.getters.getMerge(origin);
|
|
6763
|
-
if (!merge) {
|
|
6764
|
-
return;
|
|
6765
|
-
}
|
|
6766
|
-
({ sheetId, col, row } = target);
|
|
6767
|
-
this.dispatch("ADD_MERGE", {
|
|
6768
|
-
sheetId,
|
|
6769
|
-
force: true,
|
|
6770
|
-
target: [
|
|
6771
|
-
{
|
|
6772
|
-
left: col,
|
|
6773
|
-
top: row,
|
|
6774
|
-
right: col + merge.right - merge.left,
|
|
6775
|
-
bottom: row + merge.bottom - merge.top,
|
|
6776
|
-
},
|
|
6777
|
-
],
|
|
6778
|
-
});
|
|
6781
|
+
pasteMerge(originMerge, target) {
|
|
6782
|
+
if (!originMerge) {
|
|
6783
|
+
return;
|
|
6784
|
+
}
|
|
6785
|
+
if (this.getters.isInMerge(target)) {
|
|
6786
|
+
return;
|
|
6779
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
|
+
});
|
|
6780
6801
|
}
|
|
6781
6802
|
}
|
|
6782
6803
|
|
|
@@ -7756,6 +7777,486 @@
|
|
|
7756
7777
|
};
|
|
7757
7778
|
}
|
|
7758
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
|
+
|
|
7759
8260
|
/**
|
|
7760
8261
|
* Change the reference types inside the given token, if the token represent a range or a cell
|
|
7761
8262
|
*
|
|
@@ -7954,6 +8455,11 @@
|
|
|
7954
8455
|
const ChartTerms = {
|
|
7955
8456
|
Series: _t("Series"),
|
|
7956
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"),
|
|
7957
8463
|
Errors: {
|
|
7958
8464
|
Unexpected: _t("The chart definition is invalid for an unknown reason"),
|
|
7959
8465
|
// BASIC CHART ERRORS (LINE | BAR | PIE)
|
|
@@ -8886,6 +9392,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
8886
9392
|
}
|
|
8887
9393
|
const color = highlight.color || HIGHLIGHT_COLOR;
|
|
8888
9394
|
const { ctx } = renderingContext;
|
|
9395
|
+
ctx.save();
|
|
8889
9396
|
if (!highlight.noBorder) {
|
|
8890
9397
|
if (highlight.dashed) {
|
|
8891
9398
|
ctx.setLineDash([5, 3]);
|
|
@@ -8905,6 +9412,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
8905
9412
|
ctx.fillStyle = setColorAlpha(toHex(color), highlight.fillAlpha ?? 0.12);
|
|
8906
9413
|
ctx.fillRect(x, y, width, height);
|
|
8907
9414
|
}
|
|
9415
|
+
ctx.restore();
|
|
8908
9416
|
}
|
|
8909
9417
|
|
|
8910
9418
|
class HighlightStore extends SpreadsheetStore {
|
|
@@ -9435,8 +9943,20 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
9435
9943
|
replaceSelectedRange(zone) {
|
|
9436
9944
|
const ref = this.getZoneReference(zone);
|
|
9437
9945
|
const currentToken = this.tokenAtCursor;
|
|
9438
|
-
|
|
9439
|
-
|
|
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);
|
|
9440
9960
|
}
|
|
9441
9961
|
/**
|
|
9442
9962
|
* Replace the reference of the old zone by the new one.
|
|
@@ -9456,10 +9976,8 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
9456
9976
|
const refRange = this.getters.getRangeFromSheetXC(activeSheetId, xc);
|
|
9457
9977
|
return isEqual(this.getters.expandZone(activeSheetId, refRange.zone), oldZone);
|
|
9458
9978
|
});
|
|
9459
|
-
// this function assumes that the previous range is always found because
|
|
9460
|
-
// it's called when changing a highlight, which exists by definition
|
|
9461
9979
|
if (!previousRefToken) {
|
|
9462
|
-
|
|
9980
|
+
return;
|
|
9463
9981
|
}
|
|
9464
9982
|
const previousRange = this.getters.getRangeFromSheetXC(activeSheetId, previousRefToken.value);
|
|
9465
9983
|
this.selectionStart = previousRefToken.start;
|
|
@@ -9471,6 +9989,17 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
9471
9989
|
getZoneReference(zone) {
|
|
9472
9990
|
const inputSheetId = this.currentEditedCell.sheetId;
|
|
9473
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
|
+
}
|
|
9474
10003
|
const range = this.getters.getRangeFromZone(sheetId, zone);
|
|
9475
10004
|
return this.getters.getSelectionRangeString(range, inputSheetId);
|
|
9476
10005
|
}
|
|
@@ -9573,19 +10102,35 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
9573
10102
|
const colorIndex = this.colorIndexByRange[rangeString];
|
|
9574
10103
|
return colors$1[colorIndex % colors$1.length];
|
|
9575
10104
|
};
|
|
9576
|
-
|
|
10105
|
+
const highlights = [];
|
|
10106
|
+
for (const range of this.getReferencedRanges()) {
|
|
9577
10107
|
const rangeString = this.getters.getRangeString(range, editionSheetId);
|
|
9578
10108
|
const { numberOfRows, numberOfCols } = zoneToDimension(range.zone);
|
|
9579
10109
|
const zone = numberOfRows * numberOfCols === 1
|
|
9580
10110
|
? this.getters.expandZone(range.sheetId, range.zone)
|
|
9581
10111
|
: range.zone;
|
|
9582
|
-
|
|
10112
|
+
highlights.push({
|
|
9583
10113
|
zone,
|
|
9584
10114
|
color: rangeColor(rangeString),
|
|
9585
10115
|
sheetId: range.sheetId,
|
|
9586
10116
|
interactive: true,
|
|
9587
|
-
};
|
|
9588
|
-
}
|
|
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;
|
|
9589
10134
|
}
|
|
9590
10135
|
/**
|
|
9591
10136
|
* Return ranges currently referenced in the composer
|
|
@@ -9844,8 +10389,14 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
9844
10389
|
owl.useEffect(() => {
|
|
9845
10390
|
const runtime = this.chartRuntime;
|
|
9846
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
|
+
}
|
|
9847
10399
|
this.currentRuntime = runtime;
|
|
9848
|
-
this.updateChartJs(deepCopy(runtime));
|
|
9849
10400
|
}
|
|
9850
10401
|
});
|
|
9851
10402
|
}
|
|
@@ -10213,6 +10764,9 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
10213
10764
|
}
|
|
10214
10765
|
function getDefinedAxis(definition) {
|
|
10215
10766
|
let useLeftAxis = false, useRightAxis = false;
|
|
10767
|
+
if ("horizontal" in definition && definition.horizontal) {
|
|
10768
|
+
return { useLeftAxis: true, useRightAxis: false };
|
|
10769
|
+
}
|
|
10216
10770
|
for (const design of definition.dataSets || []) {
|
|
10217
10771
|
if (design.yAxisId === "y1") {
|
|
10218
10772
|
useRightAxis = true;
|
|
@@ -10423,6 +10977,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
10423
10977
|
function drawScoreChart(structure, canvas) {
|
|
10424
10978
|
const ctx = canvas.getContext("2d");
|
|
10425
10979
|
canvas.width = structure.canvas.width;
|
|
10980
|
+
const availableWidth = canvas.width - DEFAULT_CHART_PADDING;
|
|
10426
10981
|
canvas.height = structure.canvas.height;
|
|
10427
10982
|
ctx.fillStyle = structure.canvas.backgroundColor;
|
|
10428
10983
|
ctx.fillRect(0, 0, structure.canvas.width, structure.canvas.height);
|
|
@@ -10431,7 +10986,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
10431
10986
|
ctx.fillStyle = structure.title.style.color;
|
|
10432
10987
|
const baseline = ctx.textBaseline;
|
|
10433
10988
|
ctx.textBaseline = "middle";
|
|
10434
|
-
ctx.fillText(clipTextWithEllipsis(ctx, structure.title.text,
|
|
10989
|
+
ctx.fillText(clipTextWithEllipsis(ctx, structure.title.text, availableWidth - structure.title.position.x), structure.title.position.x, structure.title.position.y);
|
|
10435
10990
|
ctx.textBaseline = baseline;
|
|
10436
10991
|
}
|
|
10437
10992
|
if (structure.baseline) {
|
|
@@ -10521,13 +11076,16 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
10521
11076
|
return {
|
|
10522
11077
|
title: {
|
|
10523
11078
|
...chart.title,
|
|
11079
|
+
// chart titles are extracted from .json files and they are translated at runtime here
|
|
10524
11080
|
text: _t(chart.title.text ?? ""),
|
|
10525
11081
|
},
|
|
10526
11082
|
keyValue: formattedKeyValue,
|
|
10527
11083
|
baselineDisplay,
|
|
10528
11084
|
baselineArrow: getBaselineArrowDirection(baselineCell, keyValueCell, chart.baselineMode),
|
|
10529
11085
|
baselineColor: getBaselineColor(baselineCell, chart.baselineMode, keyValueCell, chart.baselineColorUp, chart.baselineColorDown),
|
|
10530
|
-
baselineDescr: chart.baselineMode !== "progress" && chart.baselineDescr
|
|
11086
|
+
baselineDescr: chart.baselineMode !== "progress" && chart.baselineDescr
|
|
11087
|
+
? _t(chart.baselineDescr) // descriptions are extracted from .json files and they are translated at runtime here
|
|
11088
|
+
: "",
|
|
10531
11089
|
fontColor,
|
|
10532
11090
|
background,
|
|
10533
11091
|
baselineStyle: chart.baselineMode !== "percentage" && chart.baselineMode !== "progress" && baseline
|
|
@@ -10556,7 +11114,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
10556
11114
|
/* Sizes of boxes containing the texts, in percentage of the Chart size */
|
|
10557
11115
|
const KEY_BOX_HEIGHT_RATIO = 0.8;
|
|
10558
11116
|
/* Padding at the border of the chart */
|
|
10559
|
-
const CHART_PADDING =
|
|
11117
|
+
const CHART_PADDING = 10;
|
|
10560
11118
|
const BOTTOM_PADDING_RATIO = 0.05;
|
|
10561
11119
|
/**
|
|
10562
11120
|
* Line height (in em)
|
|
@@ -10706,6 +11264,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
10706
11264
|
position: {
|
|
10707
11265
|
x: (this.width - keyWidth) / 2,
|
|
10708
11266
|
y: this.height * (0.5 - BOTTOM_PADDING_RATIO * 2) +
|
|
11267
|
+
CHART_PADDING / 2 +
|
|
10709
11268
|
(titleHeight + keyHeight / (this.baseline || this.baselineDescr ? 2 : 1.2)) / 2,
|
|
10710
11269
|
},
|
|
10711
11270
|
};
|
|
@@ -10765,7 +11324,8 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
10765
11324
|
const remainingWidth = maxLineWidth - baselineValueWidth;
|
|
10766
11325
|
let baselineDescrFontSize = getFontSizeMatchingWidth(remainingWidth, baselineValueFontSize, (fontSize) => computeTextWidth(this.context, this.baselineDescr, { fontSize }));
|
|
10767
11326
|
let isBaselineSplit = false;
|
|
10768
|
-
if (baselineDescrFontSize < baselineValueFontSize / 2.5
|
|
11327
|
+
if (baselineDescrFontSize < baselineValueFontSize / 2.5 &&
|
|
11328
|
+
this.baselineDescr.trim().includes(" ")) {
|
|
10769
11329
|
isBaselineSplit = true;
|
|
10770
11330
|
baselineDescrFontSize = Math.floor(baselineValueFontSize / 2.5);
|
|
10771
11331
|
for (const line of splitTextInTwoLines(this.baselineDescr)) {
|
|
@@ -10814,7 +11374,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
10814
11374
|
/** Get the height of the chart minus all the vertical paddings */
|
|
10815
11375
|
getDrawableHeight() {
|
|
10816
11376
|
const verticalPadding = CHART_PADDING + this.height * BOTTOM_PADDING_RATIO;
|
|
10817
|
-
let availableHeight = this.height -
|
|
11377
|
+
let availableHeight = this.height - verticalPadding;
|
|
10818
11378
|
availableHeight -= this.title ? DEFAULT_CHART_FONT_SIZE * LINE_HEIGHT : 0;
|
|
10819
11379
|
return availableHeight;
|
|
10820
11380
|
}
|
|
@@ -10856,6 +11416,11 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
10856
11416
|
get runtime() {
|
|
10857
11417
|
return this.env.model.getters.getChartRuntime(this.props.figure.id);
|
|
10858
11418
|
}
|
|
11419
|
+
get title() {
|
|
11420
|
+
const title = this.env.model.getters.getChartDefinition(this.props.figure.id).title.text ?? "";
|
|
11421
|
+
// chart titles are extracted from .json files and they are translated at runtime here
|
|
11422
|
+
return _t(title);
|
|
11423
|
+
}
|
|
10859
11424
|
setup() {
|
|
10860
11425
|
owl.useEffect(this.createChart.bind(this), () => {
|
|
10861
11426
|
const canvas = this.canvas.el;
|
|
@@ -11769,13 +12334,6 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
11769
12334
|
FORMAT_LARGE_NUMBER: FORMAT_LARGE_NUMBER
|
|
11770
12335
|
});
|
|
11771
12336
|
|
|
11772
|
-
function sum(values, locale) {
|
|
11773
|
-
return reduceNumbers(values, (acc, a) => acc + a, 0, locale);
|
|
11774
|
-
}
|
|
11775
|
-
function countUnique(args) {
|
|
11776
|
-
return reduceAny(args, (acc, a) => (isDataNonEmpty(a) ? acc.add(a?.value) : acc), new Set()).size;
|
|
11777
|
-
}
|
|
11778
|
-
|
|
11779
12337
|
const DEFAULT_FACTOR = 1;
|
|
11780
12338
|
const DEFAULT_MODE = 0;
|
|
11781
12339
|
const DEFAULT_PLACES = 0;
|
|
@@ -12889,53 +13447,6 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
12889
13447
|
TRUNC: TRUNC
|
|
12890
13448
|
});
|
|
12891
13449
|
|
|
12892
|
-
function assertSameNumberOfElements(...args) {
|
|
12893
|
-
const dims = args[0].length;
|
|
12894
|
-
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())));
|
|
12895
|
-
}
|
|
12896
|
-
function average(values, locale) {
|
|
12897
|
-
let count = 0;
|
|
12898
|
-
const sum = reduceNumbers(values, (acc, a) => {
|
|
12899
|
-
count += 1;
|
|
12900
|
-
return acc + a;
|
|
12901
|
-
}, 0, locale);
|
|
12902
|
-
assertNotZero(count);
|
|
12903
|
-
return sum / count;
|
|
12904
|
-
}
|
|
12905
|
-
function countNumbers(values, locale) {
|
|
12906
|
-
let count = 0;
|
|
12907
|
-
for (let n of values) {
|
|
12908
|
-
if (isMatrix(n)) {
|
|
12909
|
-
for (let i of n) {
|
|
12910
|
-
for (let j of i) {
|
|
12911
|
-
if (typeof j.value === "number") {
|
|
12912
|
-
count += 1;
|
|
12913
|
-
}
|
|
12914
|
-
}
|
|
12915
|
-
}
|
|
12916
|
-
}
|
|
12917
|
-
else {
|
|
12918
|
-
const value = n?.value;
|
|
12919
|
-
if (!isEvaluationError(value) &&
|
|
12920
|
-
(typeof value !== "string" || isNumber(value, locale) || parseDateTime(value, locale))) {
|
|
12921
|
-
count += 1;
|
|
12922
|
-
}
|
|
12923
|
-
}
|
|
12924
|
-
}
|
|
12925
|
-
return count;
|
|
12926
|
-
}
|
|
12927
|
-
function countAny(values) {
|
|
12928
|
-
return reduceAny(values, (acc, a) => (a !== undefined && a.value !== null ? acc + 1 : acc), 0);
|
|
12929
|
-
}
|
|
12930
|
-
function max(values, locale) {
|
|
12931
|
-
const result = reduceNumbers(values, (acc, a) => (acc < a ? a : acc), -Infinity, locale);
|
|
12932
|
-
return result === -Infinity ? 0 : result;
|
|
12933
|
-
}
|
|
12934
|
-
function min(values, locale) {
|
|
12935
|
-
const result = reduceNumbers(values, (acc, a) => (a < acc ? a : acc), Infinity, locale);
|
|
12936
|
-
return result === Infinity ? 0 : result;
|
|
12937
|
-
}
|
|
12938
|
-
|
|
12939
13450
|
function filterAndFlatData(dataY, dataX) {
|
|
12940
13451
|
const _flatDataY = [];
|
|
12941
13452
|
const _flatDataX = [];
|
|
@@ -13491,7 +14002,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
13491
14002
|
// LINEST
|
|
13492
14003
|
// -----------------------------------------------------------------------------
|
|
13493
14004
|
const LINEST = {
|
|
13494
|
-
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."),
|
|
13495
14006
|
args: [
|
|
13496
14007
|
arg("data_y (range<number>)", _t("The range representing the array or matrix of dependent data.")),
|
|
13497
14008
|
arg("data_x (range<number>, default={1;2;3;...})", _t("The range representing the array or matrix of independent data.")),
|
|
@@ -13507,7 +14018,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
13507
14018
|
// LOGEST
|
|
13508
14019
|
// -----------------------------------------------------------------------------
|
|
13509
14020
|
const LOGEST = {
|
|
13510
|
-
description: _t("
|
|
14021
|
+
description: _t("Given partial data about an exponential growth curve, calculates various parameters about the best fit ideal exponential growth curve."),
|
|
13511
14022
|
args: [
|
|
13512
14023
|
arg("data_y (range<number>)", _t("The range representing the array or matrix of dependent data.")),
|
|
13513
14024
|
arg("data_x (range<number>, optional, default={1;2;3;...})", _t("The range representing the array or matrix of independent data.")),
|
|
@@ -17910,33 +18421,6 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
17910
18421
|
NA: NA
|
|
17911
18422
|
});
|
|
17912
18423
|
|
|
17913
|
-
function boolAnd(args) {
|
|
17914
|
-
let foundBoolean = false;
|
|
17915
|
-
let acc = true;
|
|
17916
|
-
conditionalVisitBoolean(args, (arg) => {
|
|
17917
|
-
foundBoolean = true;
|
|
17918
|
-
acc = acc && arg;
|
|
17919
|
-
return acc;
|
|
17920
|
-
});
|
|
17921
|
-
return {
|
|
17922
|
-
foundBoolean,
|
|
17923
|
-
result: acc,
|
|
17924
|
-
};
|
|
17925
|
-
}
|
|
17926
|
-
function boolOr(args) {
|
|
17927
|
-
let foundBoolean = false;
|
|
17928
|
-
let acc = false;
|
|
17929
|
-
conditionalVisitBoolean(args, (arg) => {
|
|
17930
|
-
foundBoolean = true;
|
|
17931
|
-
acc = acc || arg;
|
|
17932
|
-
return !acc;
|
|
17933
|
-
});
|
|
17934
|
-
return {
|
|
17935
|
-
foundBoolean,
|
|
17936
|
-
result: acc,
|
|
17937
|
-
};
|
|
17938
|
-
}
|
|
17939
|
-
|
|
17940
18424
|
// -----------------------------------------------------------------------------
|
|
17941
18425
|
// AND
|
|
17942
18426
|
// -----------------------------------------------------------------------------
|
|
@@ -18134,393 +18618,6 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
18134
18618
|
XOR: XOR
|
|
18135
18619
|
});
|
|
18136
18620
|
|
|
18137
|
-
const pivotTimeAdapterRegistry = new Registry();
|
|
18138
|
-
function pivotTimeAdapter(granularity) {
|
|
18139
|
-
return pivotTimeAdapterRegistry.get(granularity);
|
|
18140
|
-
}
|
|
18141
|
-
/**
|
|
18142
|
-
* The Time Adapter: Managing Time Periods for Pivot Functions
|
|
18143
|
-
*
|
|
18144
|
-
* Overview:
|
|
18145
|
-
* A time adapter is responsible for managing time periods associated with pivot functions.
|
|
18146
|
-
* Each type of period (day, week, month, quarter, etc.) has its own dedicated adapter.
|
|
18147
|
-
* The adapter's primary role is to normalize period values between spreadsheet functions,
|
|
18148
|
-
* and the pivot.
|
|
18149
|
-
* By normalizing the period value, it can be stored consistently in the pivot.
|
|
18150
|
-
*
|
|
18151
|
-
* Normalization Process:
|
|
18152
|
-
* When working with functions in the spreadsheet, the time adapter normalizes
|
|
18153
|
-
* the provided period to facilitate accurate lookup of values in the pivot.
|
|
18154
|
-
* For instance, if the spreadsheet function represents a day period as a number generated
|
|
18155
|
-
* by the DATE function (DATE(2023, 12, 25)), the time adapter will normalize it accordingly.
|
|
18156
|
-
*
|
|
18157
|
-
*/
|
|
18158
|
-
/**
|
|
18159
|
-
* Normalized value: "12/25/2023"
|
|
18160
|
-
*
|
|
18161
|
-
* Note: Those two format are equivalent:
|
|
18162
|
-
* - "MM/dd/yyyy" (luxon format)
|
|
18163
|
-
* - "mm/dd/yyyy" (spreadsheet format)
|
|
18164
|
-
**/
|
|
18165
|
-
const dayAdapter = {
|
|
18166
|
-
normalizeFunctionValue(value) {
|
|
18167
|
-
const date = toNumber(value, DEFAULT_LOCALE);
|
|
18168
|
-
return formatValue(date, { locale: DEFAULT_LOCALE, format: "mm/dd/yyyy" });
|
|
18169
|
-
},
|
|
18170
|
-
getFormat(locale) {
|
|
18171
|
-
return (locale ?? DEFAULT_LOCALE).dateFormat;
|
|
18172
|
-
},
|
|
18173
|
-
formatValue(normalizedValue, locale) {
|
|
18174
|
-
locale = locale ?? DEFAULT_LOCALE;
|
|
18175
|
-
const value = toNumber(normalizedValue, DEFAULT_LOCALE);
|
|
18176
|
-
return formatValue(value, { locale, format: this.getFormat(locale) });
|
|
18177
|
-
},
|
|
18178
|
-
toCellValue(normalizedValue) {
|
|
18179
|
-
return toNumber(normalizedValue, DEFAULT_LOCALE);
|
|
18180
|
-
},
|
|
18181
|
-
};
|
|
18182
|
-
/**
|
|
18183
|
-
* normalizes day of month number
|
|
18184
|
-
*/
|
|
18185
|
-
const dayOfMonthAdapter = {
|
|
18186
|
-
normalizeFunctionValue(value) {
|
|
18187
|
-
const day = toNumber(value, DEFAULT_LOCALE);
|
|
18188
|
-
if (day < 1 || day > 31) {
|
|
18189
|
-
throw new EvaluationError(_t("%s is not a valid day of month (it should be a number between 1 and 31)", day));
|
|
18190
|
-
}
|
|
18191
|
-
return day;
|
|
18192
|
-
},
|
|
18193
|
-
getFormat() {
|
|
18194
|
-
return "0";
|
|
18195
|
-
},
|
|
18196
|
-
formatValue(normalizedValue, locale) {
|
|
18197
|
-
locale = locale ?? DEFAULT_LOCALE;
|
|
18198
|
-
const value = toNumber(normalizedValue, DEFAULT_LOCALE);
|
|
18199
|
-
return formatValue(value, { locale, format: this.getFormat(locale) });
|
|
18200
|
-
},
|
|
18201
|
-
toCellValue(normalizedValue) {
|
|
18202
|
-
return toNumber(normalizedValue, DEFAULT_LOCALE);
|
|
18203
|
-
},
|
|
18204
|
-
};
|
|
18205
|
-
/**
|
|
18206
|
-
* Normalized value: "2/2023" for week 2 of 2023
|
|
18207
|
-
*/
|
|
18208
|
-
const weekAdapter = {
|
|
18209
|
-
normalizeFunctionValue(value) {
|
|
18210
|
-
const [week, year] = value.split("/");
|
|
18211
|
-
return `${Number(week)}/${Number(year)}`;
|
|
18212
|
-
},
|
|
18213
|
-
getFormat() {
|
|
18214
|
-
return undefined;
|
|
18215
|
-
},
|
|
18216
|
-
formatValue(normalizedValue) {
|
|
18217
|
-
const [week, year] = normalizedValue.split("/");
|
|
18218
|
-
return _t("W%(week)s %(year)s", { week, year });
|
|
18219
|
-
},
|
|
18220
|
-
toCellValue(normalizedValue) {
|
|
18221
|
-
return this.formatValue(normalizedValue);
|
|
18222
|
-
},
|
|
18223
|
-
};
|
|
18224
|
-
/**
|
|
18225
|
-
* normalizes iso week number
|
|
18226
|
-
*/
|
|
18227
|
-
const isoWeekNumberAdapter = {
|
|
18228
|
-
normalizeFunctionValue(value) {
|
|
18229
|
-
const isoWeek = toNumber(value, DEFAULT_LOCALE);
|
|
18230
|
-
if (isoWeek < 0 || isoWeek > 53) {
|
|
18231
|
-
throw new EvaluationError(_t("%s is not a valid week (it should be a number between 0 and 53)", isoWeek));
|
|
18232
|
-
}
|
|
18233
|
-
return isoWeek;
|
|
18234
|
-
},
|
|
18235
|
-
getFormat() {
|
|
18236
|
-
return "0";
|
|
18237
|
-
},
|
|
18238
|
-
formatValue(normalizedValue, locale) {
|
|
18239
|
-
locale = locale ?? DEFAULT_LOCALE;
|
|
18240
|
-
const value = toNumber(normalizedValue, DEFAULT_LOCALE);
|
|
18241
|
-
return formatValue(value, { locale, format: this.getFormat(locale) });
|
|
18242
|
-
},
|
|
18243
|
-
toCellValue(normalizedValue) {
|
|
18244
|
-
return toNumber(normalizedValue, DEFAULT_LOCALE);
|
|
18245
|
-
},
|
|
18246
|
-
};
|
|
18247
|
-
/**
|
|
18248
|
-
* normalized month value is a string formatted as "MM/yyyy" (luxon format)
|
|
18249
|
-
* e.g. "01/2020" for January 2020
|
|
18250
|
-
*/
|
|
18251
|
-
const monthAdapter = {
|
|
18252
|
-
normalizeFunctionValue(value) {
|
|
18253
|
-
const date = toNumber(value, DEFAULT_LOCALE);
|
|
18254
|
-
return formatValue(date, { locale: DEFAULT_LOCALE, format: "mm/yyyy" });
|
|
18255
|
-
},
|
|
18256
|
-
getFormat() {
|
|
18257
|
-
return "mmmm yyyy";
|
|
18258
|
-
},
|
|
18259
|
-
formatValue(normalizedValue, locale) {
|
|
18260
|
-
locale = locale ?? DEFAULT_LOCALE;
|
|
18261
|
-
const value = toNumber(normalizedValue, DEFAULT_LOCALE);
|
|
18262
|
-
return formatValue(value, { locale, format: this.getFormat(locale) });
|
|
18263
|
-
},
|
|
18264
|
-
toCellValue(normalizedValue) {
|
|
18265
|
-
return toNumber(normalizedValue, DEFAULT_LOCALE);
|
|
18266
|
-
},
|
|
18267
|
-
};
|
|
18268
|
-
/**
|
|
18269
|
-
* normalizes month number
|
|
18270
|
-
*/
|
|
18271
|
-
const monthNumberAdapter = {
|
|
18272
|
-
normalizeFunctionValue(value) {
|
|
18273
|
-
const month = toNumber(value, DEFAULT_LOCALE);
|
|
18274
|
-
if (month < 1 || month > 12) {
|
|
18275
|
-
throw new EvaluationError(_t("%s is not a valid month (it should be a number between 1 and 12)", month));
|
|
18276
|
-
}
|
|
18277
|
-
return month;
|
|
18278
|
-
},
|
|
18279
|
-
getFormat() {
|
|
18280
|
-
return "0";
|
|
18281
|
-
},
|
|
18282
|
-
formatValue(normalizedValue, locale) {
|
|
18283
|
-
locale = locale ?? DEFAULT_LOCALE;
|
|
18284
|
-
const value = toNumber(normalizedValue, DEFAULT_LOCALE);
|
|
18285
|
-
return formatValue(value, { locale, format: this.getFormat(locale) });
|
|
18286
|
-
},
|
|
18287
|
-
toCellValue(normalizedValue) {
|
|
18288
|
-
return MONTHS[toNumber(normalizedValue, DEFAULT_LOCALE) - 1].toString();
|
|
18289
|
-
},
|
|
18290
|
-
};
|
|
18291
|
-
/**
|
|
18292
|
-
* normalized quarter value is "quarter/year"
|
|
18293
|
-
* e.g. "1/2020" for Q1 2020
|
|
18294
|
-
*/
|
|
18295
|
-
const quarterAdapter = {
|
|
18296
|
-
normalizeFunctionValue(value) {
|
|
18297
|
-
const [quarter, year] = value.split("/");
|
|
18298
|
-
return `${quarter}/${year}`;
|
|
18299
|
-
},
|
|
18300
|
-
getFormat() {
|
|
18301
|
-
return undefined;
|
|
18302
|
-
},
|
|
18303
|
-
formatValue(normalizedValue) {
|
|
18304
|
-
const [quarter, year] = normalizedValue.split("/");
|
|
18305
|
-
return _t("Q%(quarter)s %(year)s", { quarter, year });
|
|
18306
|
-
},
|
|
18307
|
-
toCellValue(normalizedValue) {
|
|
18308
|
-
return this.formatValue(normalizedValue);
|
|
18309
|
-
},
|
|
18310
|
-
};
|
|
18311
|
-
/**
|
|
18312
|
-
* normalizes quarter number
|
|
18313
|
-
*/
|
|
18314
|
-
const quarterNumberAdapter = {
|
|
18315
|
-
normalizeFunctionValue(value) {
|
|
18316
|
-
const quarter = toNumber(value, DEFAULT_LOCALE);
|
|
18317
|
-
if (quarter < 1 || quarter > 4) {
|
|
18318
|
-
throw new EvaluationError(_t("%s is not a valid quarter (it should be a number between 1 and 4)", quarter));
|
|
18319
|
-
}
|
|
18320
|
-
return quarter;
|
|
18321
|
-
},
|
|
18322
|
-
getFormat() {
|
|
18323
|
-
return "0";
|
|
18324
|
-
},
|
|
18325
|
-
formatValue(normalizedValue, locale) {
|
|
18326
|
-
locale = locale ?? DEFAULT_LOCALE;
|
|
18327
|
-
const value = toNumber(normalizedValue, DEFAULT_LOCALE);
|
|
18328
|
-
return formatValue(value, { locale, format: this.getFormat(locale) });
|
|
18329
|
-
},
|
|
18330
|
-
toCellValue(normalizedValue) {
|
|
18331
|
-
return toNumber(normalizedValue, DEFAULT_LOCALE);
|
|
18332
|
-
},
|
|
18333
|
-
};
|
|
18334
|
-
const yearAdapter = {
|
|
18335
|
-
normalizeFunctionValue(value) {
|
|
18336
|
-
return toNumber(value, DEFAULT_LOCALE);
|
|
18337
|
-
},
|
|
18338
|
-
getFormat() {
|
|
18339
|
-
return "0";
|
|
18340
|
-
},
|
|
18341
|
-
formatValue(normalizedValue, locale) {
|
|
18342
|
-
locale = locale ?? DEFAULT_LOCALE;
|
|
18343
|
-
return formatValue(normalizedValue, { locale, format: "0" });
|
|
18344
|
-
},
|
|
18345
|
-
toCellValue(normalizedValue) {
|
|
18346
|
-
return toNumber(normalizedValue, DEFAULT_LOCALE);
|
|
18347
|
-
},
|
|
18348
|
-
};
|
|
18349
|
-
pivotTimeAdapterRegistry
|
|
18350
|
-
.add("day", dayAdapter)
|
|
18351
|
-
.add("week", weekAdapter)
|
|
18352
|
-
.add("month", monthAdapter)
|
|
18353
|
-
.add("quarter", quarterAdapter)
|
|
18354
|
-
.add("year", yearAdapter)
|
|
18355
|
-
.add("day_of_month", dayOfMonthAdapter)
|
|
18356
|
-
.add("iso_week_number", isoWeekNumberAdapter)
|
|
18357
|
-
.add("month_number", monthNumberAdapter)
|
|
18358
|
-
.add("quarter_number", quarterNumberAdapter)
|
|
18359
|
-
.add("year_number", yearAdapter);
|
|
18360
|
-
|
|
18361
|
-
const AGGREGATOR_NAMES = {
|
|
18362
|
-
count: _t("Count"),
|
|
18363
|
-
count_distinct: _t("Count Distinct"),
|
|
18364
|
-
bool_and: _t("Boolean And"),
|
|
18365
|
-
bool_or: _t("Boolean Or"),
|
|
18366
|
-
max: _t("Maximum"),
|
|
18367
|
-
min: _t("Minimum"),
|
|
18368
|
-
avg: _t("Average"),
|
|
18369
|
-
sum: _t("Sum"),
|
|
18370
|
-
};
|
|
18371
|
-
const NUMBER_CHAR_AGGREGATORS = ["max", "min", "avg", "sum", "count_distinct", "count"];
|
|
18372
|
-
const AGGREGATORS_BY_FIELD_TYPE = {
|
|
18373
|
-
integer: NUMBER_CHAR_AGGREGATORS,
|
|
18374
|
-
char: NUMBER_CHAR_AGGREGATORS,
|
|
18375
|
-
boolean: ["count_distinct", "count", "bool_and", "bool_or"],
|
|
18376
|
-
};
|
|
18377
|
-
const AGGREGATORS = {};
|
|
18378
|
-
for (const type in AGGREGATORS_BY_FIELD_TYPE) {
|
|
18379
|
-
AGGREGATORS[type] = {};
|
|
18380
|
-
for (const aggregator of AGGREGATORS_BY_FIELD_TYPE[type]) {
|
|
18381
|
-
AGGREGATORS[type][aggregator] = AGGREGATOR_NAMES[aggregator];
|
|
18382
|
-
}
|
|
18383
|
-
}
|
|
18384
|
-
const AGGREGATORS_FN = {
|
|
18385
|
-
count: {
|
|
18386
|
-
fn: (args) => countAny([args]),
|
|
18387
|
-
format: () => "0",
|
|
18388
|
-
},
|
|
18389
|
-
count_distinct: {
|
|
18390
|
-
fn: (args) => countUnique([args]),
|
|
18391
|
-
format: () => "0",
|
|
18392
|
-
},
|
|
18393
|
-
bool_and: {
|
|
18394
|
-
fn: (args) => boolAnd([args]).result,
|
|
18395
|
-
format: () => undefined,
|
|
18396
|
-
},
|
|
18397
|
-
bool_or: {
|
|
18398
|
-
fn: (args) => boolOr([args]).result,
|
|
18399
|
-
format: () => undefined,
|
|
18400
|
-
},
|
|
18401
|
-
max: {
|
|
18402
|
-
fn: (args, locale) => max([args], locale),
|
|
18403
|
-
format: inferFormat,
|
|
18404
|
-
},
|
|
18405
|
-
min: {
|
|
18406
|
-
fn: (args, locale) => min([args], locale),
|
|
18407
|
-
format: inferFormat,
|
|
18408
|
-
},
|
|
18409
|
-
avg: {
|
|
18410
|
-
fn: (args, locale) => average([args], locale),
|
|
18411
|
-
format: inferFormat,
|
|
18412
|
-
},
|
|
18413
|
-
sum: {
|
|
18414
|
-
fn: (args, locale) => sum([args], locale),
|
|
18415
|
-
format: inferFormat,
|
|
18416
|
-
},
|
|
18417
|
-
};
|
|
18418
|
-
/**
|
|
18419
|
-
* Build a pivot formula expression
|
|
18420
|
-
*/
|
|
18421
|
-
function makePivotFormula(formula, args) {
|
|
18422
|
-
return `=${formula}(${args
|
|
18423
|
-
.map((arg) => {
|
|
18424
|
-
const stringIsNumber = typeof arg == "string" && !isNaN(Number(arg)) && Number(arg).toString() === arg;
|
|
18425
|
-
const convertToNumber = typeof arg == "number" || stringIsNumber;
|
|
18426
|
-
return convertToNumber ? `${arg}` : `"${arg.toString().replace(/"/g, '\\"')}"`;
|
|
18427
|
-
})
|
|
18428
|
-
.join(",")})`;
|
|
18429
|
-
}
|
|
18430
|
-
/**
|
|
18431
|
-
* Given an object of form {"1": {...}, "2": {...}, ...} get the maximum ID used
|
|
18432
|
-
* in this object
|
|
18433
|
-
* If the object has no keys, return 0
|
|
18434
|
-
*
|
|
18435
|
-
*/
|
|
18436
|
-
function getMaxObjectId(o) {
|
|
18437
|
-
const keys = Object.keys(o);
|
|
18438
|
-
if (!keys.length) {
|
|
18439
|
-
return 0;
|
|
18440
|
-
}
|
|
18441
|
-
const nums = keys.map((id) => parseInt(id, 10));
|
|
18442
|
-
const max = Math.max(...nums);
|
|
18443
|
-
return max;
|
|
18444
|
-
}
|
|
18445
|
-
const ALL_PERIODS = {
|
|
18446
|
-
year: _t("Year"),
|
|
18447
|
-
quarter: _t("Quarter"),
|
|
18448
|
-
month: _t("Month"),
|
|
18449
|
-
week: _t("Week"),
|
|
18450
|
-
day: _t("Day"),
|
|
18451
|
-
year_number: _t("Year"),
|
|
18452
|
-
quarter_number: _t("Quarter"),
|
|
18453
|
-
month_number: _t("Month"),
|
|
18454
|
-
iso_week_number: _t("Week"),
|
|
18455
|
-
day_of_month: _t("Day of Month"),
|
|
18456
|
-
};
|
|
18457
|
-
const DATE_FIELDS = ["date", "datetime"];
|
|
18458
|
-
/**
|
|
18459
|
-
* Parse a dimension string into a pivot dimension definition.
|
|
18460
|
-
* e.g "create_date:month" => { name: "create_date", granularity: "month" }
|
|
18461
|
-
*/
|
|
18462
|
-
function parseDimension(dimension) {
|
|
18463
|
-
const [name, granularity] = dimension.split(":");
|
|
18464
|
-
if (granularity) {
|
|
18465
|
-
return { name, granularity };
|
|
18466
|
-
}
|
|
18467
|
-
return { name };
|
|
18468
|
-
}
|
|
18469
|
-
function isDateField(field) {
|
|
18470
|
-
return DATE_FIELDS.includes(field.type);
|
|
18471
|
-
}
|
|
18472
|
-
function toPivotDomain(domainStr) {
|
|
18473
|
-
if (domainStr.length % 2 !== 0) {
|
|
18474
|
-
throw new Error("Invalid domain: odd number of elements");
|
|
18475
|
-
}
|
|
18476
|
-
const domain = [];
|
|
18477
|
-
for (let i = 0; i < domainStr.length - 1; i += 2) {
|
|
18478
|
-
domain.push({ field: domainStr[i], value: domainStr[i + 1] });
|
|
18479
|
-
}
|
|
18480
|
-
return domain;
|
|
18481
|
-
}
|
|
18482
|
-
function flatPivotDomain(domain) {
|
|
18483
|
-
return domain.flatMap((arg) => [arg.field, arg.value]);
|
|
18484
|
-
}
|
|
18485
|
-
/**
|
|
18486
|
-
* Parses the value defining a pivot group in a PIVOT formula
|
|
18487
|
-
* e.g. given the following formula PIVOT.VALUE("1", "stage_id", "42", "status", "won"),
|
|
18488
|
-
* the two group values are "42" and "won".
|
|
18489
|
-
*/
|
|
18490
|
-
function toNormalizedPivotValue(dimension, groupValue) {
|
|
18491
|
-
if (groupValue === null || groupValue === "null") {
|
|
18492
|
-
return null;
|
|
18493
|
-
}
|
|
18494
|
-
const groupValueString = typeof groupValue === "boolean"
|
|
18495
|
-
? toString(groupValue).toLocaleLowerCase()
|
|
18496
|
-
: toString(groupValue);
|
|
18497
|
-
if (!pivotNormalizationValueRegistry.contains(dimension.type)) {
|
|
18498
|
-
throw new EvaluationError(_t("Field %(field)s is not supported because of its type (%(type)s)", {
|
|
18499
|
-
field: dimension.displayName,
|
|
18500
|
-
type: dimension.type,
|
|
18501
|
-
}));
|
|
18502
|
-
}
|
|
18503
|
-
// represents a field which is not set (=False server side)
|
|
18504
|
-
if (groupValueString === "false") {
|
|
18505
|
-
return false;
|
|
18506
|
-
}
|
|
18507
|
-
const normalizer = pivotNormalizationValueRegistry.get(dimension.type);
|
|
18508
|
-
return normalizer(groupValueString, dimension.granularity);
|
|
18509
|
-
}
|
|
18510
|
-
function normalizeDateTime(value, granularity) {
|
|
18511
|
-
if (!granularity) {
|
|
18512
|
-
throw "";
|
|
18513
|
-
}
|
|
18514
|
-
return pivotTimeAdapter(granularity).normalizeFunctionValue(value);
|
|
18515
|
-
}
|
|
18516
|
-
const pivotNormalizationValueRegistry = new Registry();
|
|
18517
|
-
pivotNormalizationValueRegistry
|
|
18518
|
-
.add("date", normalizeDateTime)
|
|
18519
|
-
.add("datetime", normalizeDateTime)
|
|
18520
|
-
.add("integer", (value) => toNumber(value, DEFAULT_LOCALE))
|
|
18521
|
-
.add("boolean", (value) => toBoolean(value))
|
|
18522
|
-
.add("char", (value) => toString(value));
|
|
18523
|
-
|
|
18524
18621
|
/**
|
|
18525
18622
|
* Get the pivot ID from the formula pivot ID.
|
|
18526
18623
|
*/
|
|
@@ -19097,14 +19194,10 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
19097
19194
|
result[col].push({ value: "" });
|
|
19098
19195
|
break;
|
|
19099
19196
|
case "HEADER":
|
|
19100
|
-
|
|
19101
|
-
|
|
19102
|
-
|
|
19103
|
-
|
|
19104
|
-
}
|
|
19105
|
-
else {
|
|
19106
|
-
result[col].push(pivot.getPivotHeaderValueAndFormat(domain));
|
|
19107
|
-
}
|
|
19197
|
+
result[col].push(pivot.getPivotHeaderValueAndFormat(pivotCell.domain));
|
|
19198
|
+
break;
|
|
19199
|
+
case "MEASURE_HEADER":
|
|
19200
|
+
result[col].push(pivot.getPivotMeasureValue(pivotCell.measure, pivotCell.domain));
|
|
19108
19201
|
break;
|
|
19109
19202
|
case "VALUE":
|
|
19110
19203
|
result[col].push(pivot.getPivotCellValueAndFormat(pivotCell.measure, pivotCell.domain));
|
|
@@ -20143,6 +20236,8 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
20143
20236
|
* a child element.
|
|
20144
20237
|
*/
|
|
20145
20238
|
function isChildEvent(parent, ev) {
|
|
20239
|
+
if (!parent)
|
|
20240
|
+
return false;
|
|
20146
20241
|
return !!ev.target && parent.contains(ev.target);
|
|
20147
20242
|
}
|
|
20148
20243
|
function gridOverlayPosition() {
|
|
@@ -22682,7 +22777,8 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
22682
22777
|
/**
|
|
22683
22778
|
* Get a default chart js configuration
|
|
22684
22779
|
*/
|
|
22685
|
-
function getDefaultChartJsRuntime(chart, labels, fontColor,
|
|
22780
|
+
function getDefaultChartJsRuntime(chart, labels, fontColor, args) {
|
|
22781
|
+
const { format, locale, truncateLabels, horizontalChart } = args;
|
|
22686
22782
|
const chartTitle = chart.title.text ? chart.title : { ...chart.title, content: "" };
|
|
22687
22783
|
const options = {
|
|
22688
22784
|
// https://www.chartjs.org/docs/latest/general/responsive.html
|
|
@@ -22726,8 +22822,11 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
22726
22822
|
callbacks: {
|
|
22727
22823
|
label: function (tooltipItem) {
|
|
22728
22824
|
const xLabel = tooltipItem.dataset?.label || tooltipItem.label;
|
|
22729
|
-
// tooltipItem.parsed
|
|
22730
|
-
|
|
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
|
+
}
|
|
22731
22830
|
const toolTipFormat = !format && Math.abs(yLabel) >= 1000 ? "#,##" : format;
|
|
22732
22831
|
const yLabelStr = formatValue(yLabel, { format: toolTipFormat, locale });
|
|
22733
22832
|
return xLabel ? `${xLabel}: ${yLabelStr}` : yLabelStr;
|
|
@@ -22901,6 +23000,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
22901
23000
|
dataSetsHaveTitle;
|
|
22902
23001
|
dataSetDesign;
|
|
22903
23002
|
axesDesign;
|
|
23003
|
+
horizontal;
|
|
22904
23004
|
constructor(definition, sheetId, getters) {
|
|
22905
23005
|
super(definition, sheetId, getters);
|
|
22906
23006
|
this.dataSets = createDataSets(getters, definition.dataSets, sheetId, definition.dataSetsHaveTitle);
|
|
@@ -22912,6 +23012,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
22912
23012
|
this.dataSetsHaveTitle = definition.dataSetsHaveTitle;
|
|
22913
23013
|
this.dataSetDesign = definition.dataSets;
|
|
22914
23014
|
this.axesDesign = definition.axesDesign;
|
|
23015
|
+
this.horizontal = definition.horizontal;
|
|
22915
23016
|
}
|
|
22916
23017
|
static transformDefinition(definition, executed) {
|
|
22917
23018
|
return transformChartDefinitionWithDataSetsWithZone(definition, executed);
|
|
@@ -22983,6 +23084,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
22983
23084
|
stacked: this.stacked,
|
|
22984
23085
|
aggregated: this.aggregated,
|
|
22985
23086
|
axesDesign: this.axesDesign,
|
|
23087
|
+
horizontal: this.horizontal,
|
|
22986
23088
|
};
|
|
22987
23089
|
}
|
|
22988
23090
|
getDefinitionForExcel() {
|
|
@@ -23014,7 +23116,10 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
23014
23116
|
}
|
|
23015
23117
|
function getBarConfiguration(chart, labels, localeFormat) {
|
|
23016
23118
|
const fontColor = chartFontColor(chart.background);
|
|
23017
|
-
const config = getDefaultChartJsRuntime(chart, labels, fontColor,
|
|
23119
|
+
const config = getDefaultChartJsRuntime(chart, labels, fontColor, {
|
|
23120
|
+
...localeFormat,
|
|
23121
|
+
horizontalChart: chart.horizontal,
|
|
23122
|
+
});
|
|
23018
23123
|
const legend = {
|
|
23019
23124
|
labels: { color: fontColor },
|
|
23020
23125
|
};
|
|
@@ -23028,16 +23133,10 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
23028
23133
|
config.options.layout = {
|
|
23029
23134
|
padding: { left: 20, right: 20, top: chart.title ? 10 : 25, bottom: 10 },
|
|
23030
23135
|
};
|
|
23031
|
-
config.options.
|
|
23032
|
-
|
|
23033
|
-
|
|
23034
|
-
|
|
23035
|
-
color: fontColor,
|
|
23036
|
-
},
|
|
23037
|
-
title: getChartAxisTitleRuntime(chart.axesDesign?.x),
|
|
23038
|
-
},
|
|
23039
|
-
};
|
|
23040
|
-
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 = {
|
|
23041
23140
|
beginAtZero: true, // the origin of the y axis is always zero
|
|
23042
23141
|
ticks: {
|
|
23043
23142
|
color: fontColor,
|
|
@@ -23053,7 +23152,10 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
23053
23152
|
},
|
|
23054
23153
|
},
|
|
23055
23154
|
};
|
|
23155
|
+
const xAxis = chart.horizontal ? valuesAxis : labelsAxis;
|
|
23156
|
+
const yAxis = chart.horizontal ? labelsAxis : valuesAxis;
|
|
23056
23157
|
const { useLeftAxis, useRightAxis } = getDefinedAxis(chart.getDefinition());
|
|
23158
|
+
config.options.scales.x = { ...xAxis, title: getChartAxisTitleRuntime(chart.axesDesign?.x) };
|
|
23057
23159
|
if (useLeftAxis) {
|
|
23058
23160
|
config.options.scales.y = {
|
|
23059
23161
|
...yAxis,
|
|
@@ -23120,7 +23222,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
23120
23222
|
const label = definition.dataSets[index].label;
|
|
23121
23223
|
dataset.label = label;
|
|
23122
23224
|
}
|
|
23123
|
-
if (definition.dataSets?.[index]?.yAxisId) {
|
|
23225
|
+
if (definition.dataSets?.[index]?.yAxisId && !chart.horizontal) {
|
|
23124
23226
|
dataset["yAxisID"] = definition.dataSets[index].yAxisId;
|
|
23125
23227
|
}
|
|
23126
23228
|
}
|
|
@@ -24111,6 +24213,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
24111
24213
|
type = "pie";
|
|
24112
24214
|
aggregated;
|
|
24113
24215
|
dataSetsHaveTitle;
|
|
24216
|
+
isDoughnut;
|
|
24114
24217
|
constructor(definition, sheetId, getters) {
|
|
24115
24218
|
super(definition, sheetId, getters);
|
|
24116
24219
|
this.dataSets = createDataSets(getters, definition.dataSets, sheetId, definition.dataSetsHaveTitle);
|
|
@@ -24119,6 +24222,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
24119
24222
|
this.legendPosition = definition.legendPosition;
|
|
24120
24223
|
this.aggregated = definition.aggregated;
|
|
24121
24224
|
this.dataSetsHaveTitle = definition.dataSetsHaveTitle;
|
|
24225
|
+
this.isDoughnut = definition.isDoughnut;
|
|
24122
24226
|
}
|
|
24123
24227
|
static transformDefinition(definition, executed) {
|
|
24124
24228
|
return transformChartDefinitionWithDataSetsWithZone(definition, executed);
|
|
@@ -24136,6 +24240,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
24136
24240
|
type: "pie",
|
|
24137
24241
|
labelRange: context.auxiliaryRange || undefined,
|
|
24138
24242
|
aggregated: context.aggregated ?? false,
|
|
24243
|
+
isDoughnut: false,
|
|
24139
24244
|
};
|
|
24140
24245
|
}
|
|
24141
24246
|
getDefinition() {
|
|
@@ -24166,6 +24271,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
24166
24271
|
: undefined,
|
|
24167
24272
|
title: this.title,
|
|
24168
24273
|
aggregated: this.aggregated,
|
|
24274
|
+
isDoughnut: this.isDoughnut,
|
|
24169
24275
|
};
|
|
24170
24276
|
}
|
|
24171
24277
|
copyForSheetId(sheetId) {
|
|
@@ -24300,6 +24406,141 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
24300
24406
|
};
|
|
24301
24407
|
config.data.datasets.push(dataset);
|
|
24302
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
|
+
};
|
|
24303
24544
|
return { chartJsConfig: config, background: chart.background || BACKGROUND_CHART_COLOR };
|
|
24304
24545
|
}
|
|
24305
24546
|
|
|
@@ -24742,7 +24983,6 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
24742
24983
|
validateChartDefinition: BarChart.validateChartDefinition,
|
|
24743
24984
|
transformDefinition: BarChart.transformDefinition,
|
|
24744
24985
|
getChartDefinitionFromContextCreation: BarChart.getDefinitionFromContextCreation,
|
|
24745
|
-
name: _t("Bar"),
|
|
24746
24986
|
sequence: 10,
|
|
24747
24987
|
});
|
|
24748
24988
|
chartRegistry.add("combo", {
|
|
@@ -24752,7 +24992,6 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
24752
24992
|
validateChartDefinition: ComboChart.validateChartDefinition,
|
|
24753
24993
|
transformDefinition: ComboChart.transformDefinition,
|
|
24754
24994
|
getChartDefinitionFromContextCreation: ComboChart.getDefinitionFromContextCreation,
|
|
24755
|
-
name: _t("Combo"),
|
|
24756
24995
|
sequence: 15,
|
|
24757
24996
|
});
|
|
24758
24997
|
chartRegistry.add("line", {
|
|
@@ -24762,7 +25001,6 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
24762
25001
|
validateChartDefinition: LineChart.validateChartDefinition,
|
|
24763
25002
|
transformDefinition: LineChart.transformDefinition,
|
|
24764
25003
|
getChartDefinitionFromContextCreation: LineChart.getDefinitionFromContextCreation,
|
|
24765
|
-
name: _t("Line"),
|
|
24766
25004
|
sequence: 20,
|
|
24767
25005
|
});
|
|
24768
25006
|
chartRegistry.add("pie", {
|
|
@@ -24772,7 +25010,6 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
24772
25010
|
validateChartDefinition: PieChart.validateChartDefinition,
|
|
24773
25011
|
transformDefinition: PieChart.transformDefinition,
|
|
24774
25012
|
getChartDefinitionFromContextCreation: PieChart.getDefinitionFromContextCreation,
|
|
24775
|
-
name: _t("Pie"),
|
|
24776
25013
|
sequence: 30,
|
|
24777
25014
|
});
|
|
24778
25015
|
chartRegistry.add("scorecard", {
|
|
@@ -24782,7 +25019,6 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
24782
25019
|
validateChartDefinition: ScorecardChart$1.validateChartDefinition,
|
|
24783
25020
|
transformDefinition: ScorecardChart$1.transformDefinition,
|
|
24784
25021
|
getChartDefinitionFromContextCreation: ScorecardChart$1.getDefinitionFromContextCreation,
|
|
24785
|
-
name: _t("Scorecard"),
|
|
24786
25022
|
sequence: 40,
|
|
24787
25023
|
});
|
|
24788
25024
|
chartRegistry.add("gauge", {
|
|
@@ -24792,7 +25028,6 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
24792
25028
|
validateChartDefinition: GaugeChart.validateChartDefinition,
|
|
24793
25029
|
transformDefinition: GaugeChart.transformDefinition,
|
|
24794
25030
|
getChartDefinitionFromContextCreation: GaugeChart.getDefinitionFromContextCreation,
|
|
24795
|
-
name: _t("Gauge"),
|
|
24796
25031
|
sequence: 50,
|
|
24797
25032
|
});
|
|
24798
25033
|
chartRegistry.add("scatter", {
|
|
@@ -24802,7 +25037,6 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
24802
25037
|
validateChartDefinition: ScatterChart.validateChartDefinition,
|
|
24803
25038
|
transformDefinition: ScatterChart.transformDefinition,
|
|
24804
25039
|
getChartDefinitionFromContextCreation: ScatterChart.getDefinitionFromContextCreation,
|
|
24805
|
-
name: _t("Scatter"),
|
|
24806
25040
|
sequence: 60,
|
|
24807
25041
|
});
|
|
24808
25042
|
chartRegistry.add("waterfall", {
|
|
@@ -24812,9 +25046,17 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
24812
25046
|
validateChartDefinition: WaterfallChart.validateChartDefinition,
|
|
24813
25047
|
transformDefinition: WaterfallChart.transformDefinition,
|
|
24814
25048
|
getChartDefinitionFromContextCreation: WaterfallChart.getDefinitionFromContextCreation,
|
|
24815
|
-
name: _t("Waterfall"),
|
|
24816
25049
|
sequence: 70,
|
|
24817
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
|
+
});
|
|
24818
25060
|
const chartComponentRegistry = new Registry();
|
|
24819
25061
|
chartComponentRegistry.add("line", ChartJsComponent);
|
|
24820
25062
|
chartComponentRegistry.add("bar", ChartJsComponent);
|
|
@@ -24824,6 +25066,130 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
24824
25066
|
chartComponentRegistry.add("scatter", ChartJsComponent);
|
|
24825
25067
|
chartComponentRegistry.add("scorecard", ScorecardChart);
|
|
24826
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
|
+
});
|
|
24827
25193
|
|
|
24828
25194
|
/**
|
|
24829
25195
|
* Registry intended to support usual currencies. It is mainly used to create
|
|
@@ -26680,20 +27046,6 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
26680
27046
|
}
|
|
26681
27047
|
return transformation.transformDefinition(definition, executed);
|
|
26682
27048
|
}
|
|
26683
|
-
/**
|
|
26684
|
-
* Get an empty definition based on the given context and the given type
|
|
26685
|
-
*/
|
|
26686
|
-
function getChartDefinitionFromContextCreation(context, type) {
|
|
26687
|
-
const chartClass = chartRegistry.get(type);
|
|
26688
|
-
return chartClass.getChartDefinitionFromContextCreation(context);
|
|
26689
|
-
}
|
|
26690
|
-
function getChartTypes() {
|
|
26691
|
-
const result = {};
|
|
26692
|
-
for (const key of chartRegistry.getKeys()) {
|
|
26693
|
-
result[key] = chartRegistry.get(key).name;
|
|
26694
|
-
}
|
|
26695
|
-
return result;
|
|
26696
|
-
}
|
|
26697
27049
|
/**
|
|
26698
27050
|
* Return a "smart" chart definition in the given zone. The definition is "smart" because it will
|
|
26699
27051
|
* use the best type of chart to display the data of the zone.
|
|
@@ -28153,6 +28505,40 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
28153
28505
|
},
|
|
28154
28506
|
icon: "o-spreadsheet-Icon.PIVOT",
|
|
28155
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
|
+
};
|
|
28156
28542
|
|
|
28157
28543
|
//------------------------------------------------------------------------------
|
|
28158
28544
|
// Context Menu Registry
|
|
@@ -28251,6 +28637,10 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
28251
28637
|
name: INSERT_LINK_NAME,
|
|
28252
28638
|
sequence: 150,
|
|
28253
28639
|
separator: true,
|
|
28640
|
+
})
|
|
28641
|
+
.add("pivot_fix_formulas", {
|
|
28642
|
+
...FIX_FORMULAS,
|
|
28643
|
+
sequence: 155,
|
|
28254
28644
|
})
|
|
28255
28645
|
.add("pivot_properties", {
|
|
28256
28646
|
...pivotProperties,
|
|
@@ -30473,6 +30863,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
30473
30863
|
});
|
|
30474
30864
|
dataSeriesRanges = [];
|
|
30475
30865
|
labelRange;
|
|
30866
|
+
chartTerms = ChartTerms;
|
|
30476
30867
|
setup() {
|
|
30477
30868
|
this.dataSeriesRanges = this.props.definition.dataSets;
|
|
30478
30869
|
this.labelRange = this.props.definition.labelRange;
|
|
@@ -30497,7 +30888,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
30497
30888
|
return [
|
|
30498
30889
|
{
|
|
30499
30890
|
name: "aggregated",
|
|
30500
|
-
label:
|
|
30891
|
+
label: this.chartTerms.AggregatedChart,
|
|
30501
30892
|
value: this.props.definition.aggregated ?? false,
|
|
30502
30893
|
onChange: this.onUpdateAggregated.bind(this),
|
|
30503
30894
|
},
|
|
@@ -30573,9 +30964,6 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
30573
30964
|
|
|
30574
30965
|
class BarConfigPanel extends GenericChartConfigPanel {
|
|
30575
30966
|
static template = "o-spreadsheet-BarConfigPanel";
|
|
30576
|
-
get stackedLabel() {
|
|
30577
|
-
return _t("Stacked barchart");
|
|
30578
|
-
}
|
|
30579
30967
|
onUpdateStacked(stacked) {
|
|
30580
30968
|
this.props.updateChart(this.props.figureId, {
|
|
30581
30969
|
stacked,
|
|
@@ -31565,6 +31953,9 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
31565
31953
|
return "left";
|
|
31566
31954
|
return dataSets[this.state.index].yAxisId === "y1" ? "right" : "left";
|
|
31567
31955
|
}
|
|
31956
|
+
get canHaveTwoVerticalAxis() {
|
|
31957
|
+
return "horizontal" in this.props.definition ? !this.props.definition.horizontal : true;
|
|
31958
|
+
}
|
|
31568
31959
|
updateDataSeriesLabel(ev) {
|
|
31569
31960
|
const label = ev.target.value;
|
|
31570
31961
|
const dataSets = this.props.definition.dataSets;
|
|
@@ -31679,14 +32070,6 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
31679
32070
|
const cancelledReasons = [...(this.state.sectionRuleDispatchResult?.reasons || [])];
|
|
31680
32071
|
return cancelledReasons.map((error) => ChartTerms.Errors[error] || ChartTerms.Errors.Unexpected);
|
|
31681
32072
|
}
|
|
31682
|
-
updateBackgroundColor(color) {
|
|
31683
|
-
this.props.updateChart(this.props.figureId, {
|
|
31684
|
-
background: color,
|
|
31685
|
-
});
|
|
31686
|
-
}
|
|
31687
|
-
updateTitle(content) {
|
|
31688
|
-
this.props.updateChart(this.props.figureId, { title: { text: content } });
|
|
31689
|
-
}
|
|
31690
32073
|
isRangeMinInvalid() {
|
|
31691
32074
|
return !!(this.state.sectionRuleDispatchResult?.isCancelledBecause("EmptyGaugeRangeMin" /* CommandResult.EmptyGaugeRangeMin */) ||
|
|
31692
32075
|
this.state.sectionRuleDispatchResult?.isCancelledBecause("GaugeRangeMinNaN" /* CommandResult.GaugeRangeMinNaN */) ||
|
|
@@ -31726,9 +32109,6 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
31726
32109
|
sectionRule,
|
|
31727
32110
|
});
|
|
31728
32111
|
}
|
|
31729
|
-
get backgroundColorTitle() {
|
|
31730
|
-
return ChartTerms.BackgroundColor;
|
|
31731
|
-
}
|
|
31732
32112
|
}
|
|
31733
32113
|
|
|
31734
32114
|
class LineConfigPanel extends GenericChartConfigPanel {
|
|
@@ -31740,19 +32120,13 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
31740
32120
|
}
|
|
31741
32121
|
return false;
|
|
31742
32122
|
}
|
|
31743
|
-
get stackedLabel() {
|
|
31744
|
-
return _t("Stacked linechart");
|
|
31745
|
-
}
|
|
31746
|
-
get cumulativeLabel() {
|
|
31747
|
-
return _t("Cumulative data");
|
|
31748
|
-
}
|
|
31749
32123
|
getLabelRangeOptions() {
|
|
31750
32124
|
const options = super.getLabelRangeOptions();
|
|
31751
32125
|
if (this.canTreatLabelsAsText) {
|
|
31752
32126
|
options.push({
|
|
31753
32127
|
name: "labelsAsText",
|
|
31754
32128
|
value: this.props.definition.labelsAsText,
|
|
31755
|
-
label:
|
|
32129
|
+
label: this.chartTerms.TreatLabelsAsText,
|
|
31756
32130
|
onChange: this.onUpdateLabelsAsText.bind(this),
|
|
31757
32131
|
});
|
|
31758
32132
|
}
|
|
@@ -31819,7 +32193,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
31819
32193
|
options.push({
|
|
31820
32194
|
name: "labelsAsText",
|
|
31821
32195
|
value: this.props.definition.labelsAsText,
|
|
31822
|
-
label:
|
|
32196
|
+
label: this.chartTerms.TreatLabelsAsText,
|
|
31823
32197
|
onChange: this.onUpdateLabelsAsText.bind(this),
|
|
31824
32198
|
});
|
|
31825
32199
|
}
|
|
@@ -31911,9 +32285,6 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
31911
32285
|
get humanizeNumbersLabel() {
|
|
31912
32286
|
return _t("Humanize numbers");
|
|
31913
32287
|
}
|
|
31914
|
-
updateTitle(content) {
|
|
31915
|
-
this.props.updateChart(this.props.figureId, { title: { text: content } });
|
|
31916
|
-
}
|
|
31917
32288
|
updateHumanizeNumbers(humanize) {
|
|
31918
32289
|
this.props.updateChart(this.props.figureId, { humanize });
|
|
31919
32290
|
}
|
|
@@ -31936,9 +32307,6 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
31936
32307
|
break;
|
|
31937
32308
|
}
|
|
31938
32309
|
}
|
|
31939
|
-
get backgroundColorTitle() {
|
|
31940
|
-
return ChartTerms.BackgroundColor;
|
|
31941
|
-
}
|
|
31942
32310
|
}
|
|
31943
32311
|
|
|
31944
32312
|
class WaterfallChartDesignPanel extends owl.Component {
|
|
@@ -32032,8 +32400,106 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
32032
32400
|
.add("waterfall", {
|
|
32033
32401
|
configuration: GenericChartConfigPanel,
|
|
32034
32402
|
design: WaterfallChartDesignPanel,
|
|
32403
|
+
})
|
|
32404
|
+
.add("pyramid", {
|
|
32405
|
+
configuration: GenericChartConfigPanel,
|
|
32406
|
+
design: ChartWithAxisDesignPanel,
|
|
32035
32407
|
});
|
|
32036
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
|
+
|
|
32037
32503
|
class MainChartPanelStore extends SpreadsheetStore {
|
|
32038
32504
|
mutators = ["activatePanel", "changeChartType"];
|
|
32039
32505
|
panel = "configuration";
|
|
@@ -32041,7 +32507,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
32041
32507
|
activatePanel(panel) {
|
|
32042
32508
|
this.panel = panel;
|
|
32043
32509
|
}
|
|
32044
|
-
changeChartType(figureId,
|
|
32510
|
+
changeChartType(figureId, newDisplayType) {
|
|
32045
32511
|
this.creationContext = {
|
|
32046
32512
|
...this.creationContext,
|
|
32047
32513
|
...this.getters.getContextCreationChart(figureId),
|
|
@@ -32050,13 +32516,25 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
32050
32516
|
if (!sheetId) {
|
|
32051
32517
|
return;
|
|
32052
32518
|
}
|
|
32053
|
-
const definition = getChartDefinitionFromContextCreation(
|
|
32519
|
+
const definition = this.getChartDefinitionFromContextCreation(figureId, newDisplayType);
|
|
32054
32520
|
this.model.dispatch("UPDATE_CHART", {
|
|
32055
32521
|
definition,
|
|
32056
32522
|
id: figureId,
|
|
32057
32523
|
sheetId,
|
|
32058
32524
|
});
|
|
32059
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
|
+
}
|
|
32060
32538
|
}
|
|
32061
32539
|
|
|
32062
32540
|
css /* scss */ `
|
|
@@ -32085,7 +32563,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
32085
32563
|
`;
|
|
32086
32564
|
class ChartPanel extends owl.Component {
|
|
32087
32565
|
static template = "o-spreadsheet-ChartPanel";
|
|
32088
|
-
static components = { Section };
|
|
32566
|
+
static components = { Section, ChartTypePicker };
|
|
32089
32567
|
static props = { onCloseSidePanel: Function, figureId: String };
|
|
32090
32568
|
store;
|
|
32091
32569
|
get figureId() {
|
|
@@ -32145,9 +32623,6 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
32145
32623
|
getChartDefinition(figureId) {
|
|
32146
32624
|
return this.env.model.getters.getChartDefinition(figureId);
|
|
32147
32625
|
}
|
|
32148
|
-
get chartTypes() {
|
|
32149
|
-
return getChartTypes();
|
|
32150
|
-
}
|
|
32151
32626
|
}
|
|
32152
32627
|
|
|
32153
32628
|
css /* scss */ `
|
|
@@ -35221,9 +35696,17 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
35221
35696
|
}
|
|
35222
35697
|
getPivotCell(col, row, includeTotal = true) {
|
|
35223
35698
|
const colHeadersHeight = this.columns.length;
|
|
35224
|
-
if (row
|
|
35699
|
+
if (col > 0 && row === colHeadersHeight - 1) {
|
|
35225
35700
|
const domain = this.getColHeaderDomain(col, row);
|
|
35226
|
-
|
|
35701
|
+
if (!domain) {
|
|
35702
|
+
return EMPTY_PIVOT_CELL;
|
|
35703
|
+
}
|
|
35704
|
+
const measure = domain.at(-1)?.value.toString() || "";
|
|
35705
|
+
return { type: "MEASURE_HEADER", domain: domain.slice(0, -1), measure };
|
|
35706
|
+
}
|
|
35707
|
+
else if (row <= colHeadersHeight - 1) {
|
|
35708
|
+
const domain = this.getColHeaderDomain(col, row);
|
|
35709
|
+
return domain ? { type: "HEADER", domain } : EMPTY_PIVOT_CELL;
|
|
35227
35710
|
}
|
|
35228
35711
|
else if (col === 0) {
|
|
35229
35712
|
const rowIndex = row - colHeadersHeight;
|
|
@@ -35233,7 +35716,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
35233
35716
|
else {
|
|
35234
35717
|
const rowIndex = row - colHeadersHeight;
|
|
35235
35718
|
if (!includeTotal && this.isTotalRow(rowIndex)) {
|
|
35236
|
-
return
|
|
35719
|
+
return EMPTY_PIVOT_CELL;
|
|
35237
35720
|
}
|
|
35238
35721
|
const domain = [...this.getRowDomain(rowIndex), ...this.getColDomain(col)];
|
|
35239
35722
|
const measure = this.getColMeasure(col);
|
|
@@ -35287,6 +35770,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
35287
35770
|
};
|
|
35288
35771
|
}
|
|
35289
35772
|
}
|
|
35773
|
+
const EMPTY_PIVOT_CELL = { type: "EMPTY" };
|
|
35290
35774
|
|
|
35291
35775
|
/**
|
|
35292
35776
|
* This function converts a list of data entry into a spreadsheet pivot table.
|
|
@@ -35484,6 +35968,9 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
35484
35968
|
if (!(granularity in MAP_VALUE_DIMENSION_DATE)) {
|
|
35485
35969
|
throw new Error(`Unknown date granularity: ${granularity}`);
|
|
35486
35970
|
}
|
|
35971
|
+
if (value === null) {
|
|
35972
|
+
return null;
|
|
35973
|
+
}
|
|
35487
35974
|
if (!MAP_VALUE_DIMENSION_DATE[granularity].set.has(value)) {
|
|
35488
35975
|
MAP_VALUE_DIMENSION_DATE[granularity].set.add(value);
|
|
35489
35976
|
const date = toJsDate(value, locale);
|
|
@@ -35714,14 +36201,17 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
35714
36201
|
if (dimension.type === "date") {
|
|
35715
36202
|
const adapter = pivotTimeAdapter(dimension.granularity);
|
|
35716
36203
|
return {
|
|
35717
|
-
value:
|
|
36204
|
+
value: lastNode.value !== "null"
|
|
36205
|
+
? adapter.toCellValue(toNormalizedPivotValue(dimension, lastNode.value))
|
|
36206
|
+
: _t("(Undefined)"),
|
|
35718
36207
|
format: adapter.getFormat(this.getters.getLocale()),
|
|
35719
36208
|
};
|
|
35720
36209
|
}
|
|
35721
36210
|
if (!finalCell) {
|
|
35722
36211
|
return { value: "" };
|
|
35723
36212
|
}
|
|
35724
|
-
|
|
36213
|
+
// Value can be null but stringified (e.g. an empty date, as for now every date is stringified)
|
|
36214
|
+
if (finalCell.value === null || finalCell.value === `${null}`) {
|
|
35725
36215
|
return { value: _t("(Undefined)") };
|
|
35726
36216
|
}
|
|
35727
36217
|
return {
|
|
@@ -35743,10 +36233,15 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
35743
36233
|
if (!operator) {
|
|
35744
36234
|
throw new Error(`Aggregator ${aggregator} does not exist`);
|
|
35745
36235
|
}
|
|
35746
|
-
|
|
35747
|
-
|
|
35748
|
-
|
|
35749
|
-
|
|
36236
|
+
try {
|
|
36237
|
+
return {
|
|
36238
|
+
value: values.length ? operator.fn([values], this.getters.getLocale()) : "",
|
|
36239
|
+
format: operator.format(values[0]),
|
|
36240
|
+
};
|
|
36241
|
+
}
|
|
36242
|
+
catch (e) {
|
|
36243
|
+
return handleError(e, aggregator.toUpperCase());
|
|
36244
|
+
}
|
|
35750
36245
|
}
|
|
35751
36246
|
getPossibleFieldValues(dimension) {
|
|
35752
36247
|
const values = [];
|
|
@@ -38620,7 +39115,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
38620
39115
|
});
|
|
38621
39116
|
}
|
|
38622
39117
|
getContainerRect(container) {
|
|
38623
|
-
const { width: viewWidth, height: viewHeight } = this.env.model.getters.
|
|
39118
|
+
const { width: viewWidth, height: viewHeight } = this.env.model.getters.getSheetViewDimension();
|
|
38624
39119
|
const { x: viewportX, y: viewportY } = this.env.model.getters.getMainViewportCoordinates();
|
|
38625
39120
|
const x = ["bottomRight", "topRight"].includes(container) ? viewportX : 0;
|
|
38626
39121
|
const width = viewWidth - x;
|
|
@@ -51113,30 +51608,14 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
51113
51608
|
for (let col = 0; col < pivotCells.length; col++) {
|
|
51114
51609
|
for (let row = 0; row < pivotCells[col].length; row++) {
|
|
51115
51610
|
const pivotCell = pivotCells[col][row];
|
|
51116
|
-
|
|
51611
|
+
this.dispatch("UPDATE_CELL", {
|
|
51117
51612
|
sheetId: position.sheetId,
|
|
51118
51613
|
col: position.col + col,
|
|
51119
51614
|
row: position.row + row,
|
|
51120
|
-
|
|
51121
|
-
|
|
51615
|
+
content: makePivotFormulaFromPivotCell(formulaId, pivotCell),
|
|
51616
|
+
});
|
|
51122
51617
|
}
|
|
51123
51618
|
}
|
|
51124
|
-
const pivotZone = {
|
|
51125
|
-
top: position.row,
|
|
51126
|
-
bottom: position.row + pivotCells[0].length - 1,
|
|
51127
|
-
left: position.col,
|
|
51128
|
-
right: position.col + pivotCells.length - 1,
|
|
51129
|
-
};
|
|
51130
|
-
const numberOfHeaders = table.columns.length - 1;
|
|
51131
|
-
const cmdContent = {
|
|
51132
|
-
sheetId: position.sheetId,
|
|
51133
|
-
ranges: [this.getters.getRangeDataFromZone(position.sheetId, pivotZone)],
|
|
51134
|
-
config: { ...PIVOT_TABLE_CONFIG, numberOfHeaders },
|
|
51135
|
-
tableType: "static",
|
|
51136
|
-
};
|
|
51137
|
-
if (this.canDispatch("CREATE_TABLE", cmdContent).isSuccessful) {
|
|
51138
|
-
this.dispatch("CREATE_TABLE", cmdContent);
|
|
51139
|
-
}
|
|
51140
51619
|
}
|
|
51141
51620
|
resizeSheet(sheetId, { col, row }, table) {
|
|
51142
51621
|
const colLimit = table.getNumberOfDataColumns() + 1; // +1 for the Top-Left
|
|
@@ -51164,21 +51643,6 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
51164
51643
|
});
|
|
51165
51644
|
}
|
|
51166
51645
|
}
|
|
51167
|
-
addPivotFormula(position, formulaId, pivotCell) {
|
|
51168
|
-
let content = undefined;
|
|
51169
|
-
switch (pivotCell.type) {
|
|
51170
|
-
case "HEADER":
|
|
51171
|
-
content = makePivotFormula("PIVOT.HEADER", [formulaId, ...flatPivotDomain(pivotCell.domain)].filter(isDefined));
|
|
51172
|
-
break;
|
|
51173
|
-
case "VALUE":
|
|
51174
|
-
content = makePivotFormula("PIVOT.VALUE", [formulaId, pivotCell.measure, ...flatPivotDomain(pivotCell.domain)].filter(isDefined));
|
|
51175
|
-
break;
|
|
51176
|
-
}
|
|
51177
|
-
this.dispatch("UPDATE_CELL", {
|
|
51178
|
-
...position,
|
|
51179
|
-
content,
|
|
51180
|
-
});
|
|
51181
|
-
}
|
|
51182
51646
|
getPivotCore(pivotId) {
|
|
51183
51647
|
const pivot = this.pivots[pivotId];
|
|
51184
51648
|
if (!pivot) {
|
|
@@ -52843,7 +53307,6 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
52843
53307
|
if (!this.blockedArrayFormulas.has(position)) {
|
|
52844
53308
|
this.invalidateSpreading(position);
|
|
52845
53309
|
}
|
|
52846
|
-
this.spreadingRelations.removeNode(position);
|
|
52847
53310
|
const cell = this.getters.getCell(position);
|
|
52848
53311
|
if (cell === undefined) {
|
|
52849
53312
|
return EMPTY_CELL;
|
|
@@ -52885,6 +53348,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
52885
53348
|
this.assertSheetHasEnoughSpaceToSpreadFormulaResult(formulaPosition, formulaReturn);
|
|
52886
53349
|
const nbColumns = formulaReturn.length;
|
|
52887
53350
|
const nbRows = formulaReturn[0].length;
|
|
53351
|
+
this.spreadingRelations.removeNode(formulaPosition);
|
|
52888
53352
|
forEachSpreadPositionInMatrix(nbColumns, nbRows, this.updateSpreadRelation(formulaPosition));
|
|
52889
53353
|
this.assertNoMergedCellsInSpreadZone(formulaPosition, formulaReturn);
|
|
52890
53354
|
forEachSpreadPositionInMatrix(nbColumns, nbRows, this.checkCollision(formulaPosition));
|
|
@@ -54412,9 +54876,10 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
54412
54876
|
"getPivot",
|
|
54413
54877
|
"getFirstPivotFunction",
|
|
54414
54878
|
"getPivotIdFromPosition",
|
|
54415
|
-
"
|
|
54879
|
+
"getPivotCellFromPosition",
|
|
54416
54880
|
"isPivotUnused",
|
|
54417
54881
|
"areDomainArgsFieldsValid",
|
|
54882
|
+
"isSpillPivotFormula",
|
|
54418
54883
|
];
|
|
54419
54884
|
pivots = {};
|
|
54420
54885
|
unusedPivots;
|
|
@@ -54493,6 +54958,14 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
54493
54958
|
}
|
|
54494
54959
|
return undefined;
|
|
54495
54960
|
}
|
|
54961
|
+
isSpillPivotFormula(position) {
|
|
54962
|
+
const cell = this.getters.getCorrespondingFormulaCell(position);
|
|
54963
|
+
if (cell && cell.isFormula) {
|
|
54964
|
+
const pivotFunction = this.getFirstPivotFunction(cell.compiledFormula.tokens);
|
|
54965
|
+
return pivotFunction?.functionName === "PIVOT";
|
|
54966
|
+
}
|
|
54967
|
+
return false;
|
|
54968
|
+
}
|
|
54496
54969
|
getFirstPivotFunction(tokens) {
|
|
54497
54970
|
const pivotFunction = getFirstPivotFunction(tokens);
|
|
54498
54971
|
if (!pivotFunction) {
|
|
@@ -54526,29 +54999,29 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
54526
54999
|
* If the cell is the result of PIVOT, the result is the domain of the cell
|
|
54527
55000
|
* as if it was the individual pivot formula
|
|
54528
55001
|
*/
|
|
54529
|
-
|
|
55002
|
+
getPivotCellFromPosition(position) {
|
|
54530
55003
|
const cell = this.getters.getCorrespondingFormulaCell(position);
|
|
54531
55004
|
if (!cell || !cell.isFormula || getNumberOfPivotFunctions(cell.compiledFormula.tokens) === 0) {
|
|
54532
|
-
return
|
|
55005
|
+
return EMPTY_PIVOT_CELL;
|
|
54533
55006
|
}
|
|
54534
55007
|
const mainPosition = this.getters.getCellPosition(cell.id);
|
|
54535
55008
|
const result = this.getters.getFirstPivotFunction(cell.compiledFormula.tokens);
|
|
54536
55009
|
if (!result) {
|
|
54537
|
-
return
|
|
55010
|
+
return EMPTY_PIVOT_CELL;
|
|
54538
55011
|
}
|
|
54539
55012
|
const { functionName, args } = result;
|
|
54540
55013
|
if (functionName === "PIVOT") {
|
|
54541
55014
|
const formulaId = args[0];
|
|
54542
55015
|
if (!formulaId) {
|
|
54543
|
-
return
|
|
55016
|
+
return EMPTY_PIVOT_CELL;
|
|
54544
55017
|
}
|
|
54545
55018
|
const pivotId = this.getters.getPivotId(formulaId.toString());
|
|
54546
55019
|
if (!pivotId) {
|
|
54547
|
-
return
|
|
55020
|
+
return EMPTY_PIVOT_CELL;
|
|
54548
55021
|
}
|
|
54549
55022
|
const pivot = this.getPivot(pivotId);
|
|
54550
55023
|
if (!pivot.isValid()) {
|
|
54551
|
-
return
|
|
55024
|
+
return EMPTY_PIVOT_CELL;
|
|
54552
55025
|
}
|
|
54553
55026
|
const includeTotal = args[2] === false ? false : undefined;
|
|
54554
55027
|
const includeColumnHeaders = args[3] === false ? false : undefined;
|
|
@@ -54557,22 +55030,28 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
54557
55030
|
.getPivotCells(includeTotal, includeColumnHeaders);
|
|
54558
55031
|
const pivotCol = position.col - mainPosition.col;
|
|
54559
55032
|
const pivotRow = position.row - mainPosition.row;
|
|
54560
|
-
|
|
54561
|
-
if (pivotCell.type === "EMPTY") {
|
|
54562
|
-
return undefined;
|
|
54563
|
-
}
|
|
54564
|
-
let domain = pivotCell.domain;
|
|
54565
|
-
if (domain.at(-1)?.field === "measure") {
|
|
54566
|
-
domain = domain.slice(0, -1);
|
|
54567
|
-
}
|
|
54568
|
-
return { domainArgs: domain, isHeader: pivotCell.type === "HEADER" };
|
|
55033
|
+
return pivotCells[pivotCol][pivotRow];
|
|
54569
55034
|
}
|
|
54570
|
-
|
|
54571
|
-
|
|
54572
|
-
|
|
55035
|
+
if (functionName === "PIVOT.HEADER" && args.at(-2) === "measure") {
|
|
55036
|
+
const domain = toPivotDomain(args.slice(1, -2).map((x) => `${x}`));
|
|
55037
|
+
return {
|
|
55038
|
+
type: "MEASURE_HEADER",
|
|
55039
|
+
domain,
|
|
55040
|
+
measure: args.at(-1)?.toString() || "",
|
|
55041
|
+
};
|
|
54573
55042
|
}
|
|
54574
|
-
|
|
54575
|
-
|
|
55043
|
+
else if (functionName === "PIVOT.HEADER") {
|
|
55044
|
+
return {
|
|
55045
|
+
type: "HEADER",
|
|
55046
|
+
domain: toPivotDomain(args.slice(1).map((x) => `${x}`)),
|
|
55047
|
+
};
|
|
55048
|
+
}
|
|
55049
|
+
const [measure, ...domainArgs] = args.slice(1);
|
|
55050
|
+
return {
|
|
55051
|
+
type: "VALUE",
|
|
55052
|
+
domain: toPivotDomain(domainArgs.map((x) => `${x}`)),
|
|
55053
|
+
measure: measure?.toString() || "",
|
|
55054
|
+
};
|
|
54576
55055
|
}
|
|
54577
55056
|
getPivot(pivotId) {
|
|
54578
55057
|
return this.pivots[pivotId];
|
|
@@ -55908,6 +56387,9 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
55908
56387
|
* Send a snapshot of the spreadsheet to the collaboration server
|
|
55909
56388
|
*/
|
|
55910
56389
|
snapshot(data) {
|
|
56390
|
+
if (this.pendingMessages.length !== 0) {
|
|
56391
|
+
return;
|
|
56392
|
+
}
|
|
55911
56393
|
const snapshotId = this.uuidGenerator.uuidv4();
|
|
55912
56394
|
this.transportService.sendMessage({
|
|
55913
56395
|
type: "SNAPSHOT",
|
|
@@ -63852,6 +64334,9 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
63852
64334
|
getBackToDefault() {
|
|
63853
64335
|
this.stream.getBackToDefault();
|
|
63854
64336
|
}
|
|
64337
|
+
getAnchor() {
|
|
64338
|
+
return this.anchor;
|
|
64339
|
+
}
|
|
63855
64340
|
modifyAnchor(anchor, mode, options) {
|
|
63856
64341
|
const sheetId = this.getters.getActiveSheetId();
|
|
63857
64342
|
anchor = {
|
|
@@ -66815,6 +67300,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
66815
67300
|
chartSidePanelComponentRegistry,
|
|
66816
67301
|
chartComponentRegistry,
|
|
66817
67302
|
chartRegistry,
|
|
67303
|
+
chartSubtypeRegistry,
|
|
66818
67304
|
topbarMenuRegistry,
|
|
66819
67305
|
topbarComponentRegistry,
|
|
66820
67306
|
clickableCellRegistry,
|
|
@@ -66923,6 +67409,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
66923
67409
|
GaugeChartDesignPanel,
|
|
66924
67410
|
ScorecardChartConfigPanel,
|
|
66925
67411
|
ScorecardChartDesignPanel,
|
|
67412
|
+
ChartTypePicker,
|
|
66926
67413
|
FigureComponent,
|
|
66927
67414
|
Menu,
|
|
66928
67415
|
Popover,
|
|
@@ -66972,6 +67459,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
66972
67459
|
DEFAULT_LOCALE,
|
|
66973
67460
|
HIGHLIGHT_COLOR,
|
|
66974
67461
|
PIVOT_TABLE_CONFIG,
|
|
67462
|
+
ChartTerms,
|
|
66975
67463
|
};
|
|
66976
67464
|
|
|
66977
67465
|
exports.AbstractCellClipboardHandler = AbstractCellClipboardHandler;
|
|
@@ -67020,9 +67508,9 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
67020
67508
|
exports.tokenize = tokenize;
|
|
67021
67509
|
|
|
67022
67510
|
|
|
67023
|
-
__info__.version = "17.4.0-alpha.
|
|
67024
|
-
__info__.date = "2024-06-
|
|
67025
|
-
__info__.hash = "
|
|
67511
|
+
__info__.version = "17.4.0-alpha.6";
|
|
67512
|
+
__info__.date = "2024-06-19T13:46:27.157Z";
|
|
67513
|
+
__info__.hash = "a4f22e4";
|
|
67026
67514
|
|
|
67027
67515
|
|
|
67028
67516
|
})(this.o_spreadsheet = this.o_spreadsheet || {}, owl);
|