@odoo/o-spreadsheet 17.4.0-alpha.5 → 17.4.0-alpha.7

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