@odoo/o-spreadsheet 17.4.0-alpha.1 → 17.4.0-alpha.2

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.
@@ -3,9 +3,9 @@
3
3
  /**
4
4
  * This file is generated by o-spreadsheet build tools. Do not edit it.
5
5
  * @see https://github.com/odoo/o-spreadsheet
6
- * @version 17.4.0-alpha.1
7
- * @date 2024-06-03T15:30:28.283Z
8
- * @hash cb56c37
6
+ * @version 17.4.0-alpha.2
7
+ * @date 2024-06-06T13:31:21.327Z
8
+ * @hash e07794d
9
9
  */
10
10
 
11
11
  'use strict';
@@ -18377,6 +18377,131 @@ var logical = /*#__PURE__*/Object.freeze({
18377
18377
  const supportedPivotExplodedFormulaRegistry = new Registry();
18378
18378
  supportedPivotExplodedFormulaRegistry.add("SPREADSHEET", false);
18379
18379
 
18380
+ const AGGREGATOR_NAMES = {
18381
+ count: _t("Count"),
18382
+ count_distinct: _t("Count Distinct"),
18383
+ bool_and: _t("Boolean And"),
18384
+ bool_or: _t("Boolean Or"),
18385
+ max: _t("Maximum"),
18386
+ min: _t("Minimum"),
18387
+ avg: _t("Average"),
18388
+ sum: _t("Sum"),
18389
+ };
18390
+ const NUMBER_CHAR_AGGREGATORS = ["max", "min", "avg", "sum", "count_distinct", "count"];
18391
+ const AGGREGATORS_BY_FIELD_TYPE = {
18392
+ integer: NUMBER_CHAR_AGGREGATORS,
18393
+ char: NUMBER_CHAR_AGGREGATORS,
18394
+ //TODO Support for date and boolean
18395
+ };
18396
+ const AGGREGATORS = {};
18397
+ for (const type in AGGREGATORS_BY_FIELD_TYPE) {
18398
+ AGGREGATORS[type] = {};
18399
+ for (const aggregator of AGGREGATORS_BY_FIELD_TYPE[type]) {
18400
+ AGGREGATORS[type][aggregator] = AGGREGATOR_NAMES[aggregator];
18401
+ }
18402
+ }
18403
+ const AGGREGATORS_FN = {
18404
+ count: {
18405
+ fn: (args) => countAny([args]),
18406
+ format: () => "0",
18407
+ },
18408
+ count_distinct: {
18409
+ fn: (args) => countUnique([args]),
18410
+ format: () => "0",
18411
+ },
18412
+ bool_and: {
18413
+ fn: (args) => boolAnd([args]).result,
18414
+ format: () => undefined,
18415
+ },
18416
+ bool_or: {
18417
+ fn: (args) => boolOr([args]).result,
18418
+ format: () => undefined,
18419
+ },
18420
+ max: {
18421
+ fn: (args, locale) => max([args], locale),
18422
+ format: inferFormat,
18423
+ },
18424
+ min: {
18425
+ fn: (args, locale) => min([args], locale),
18426
+ format: inferFormat,
18427
+ },
18428
+ avg: {
18429
+ fn: (args, locale) => average([args], locale),
18430
+ format: inferFormat,
18431
+ },
18432
+ sum: {
18433
+ fn: (args, locale) => sum([args], locale),
18434
+ format: inferFormat,
18435
+ },
18436
+ };
18437
+ /**
18438
+ * Build a pivot formula expression
18439
+ */
18440
+ function makePivotFormula(formula, args) {
18441
+ return `=${formula}(${args
18442
+ .map((arg) => {
18443
+ const stringIsNumber = typeof arg == "string" && !isNaN(Number(arg)) && Number(arg).toString() === arg;
18444
+ const convertToNumber = typeof arg == "number" || stringIsNumber;
18445
+ return convertToNumber ? `${arg}` : `"${arg.toString().replace(/"/g, '\\"')}"`;
18446
+ })
18447
+ .join(",")})`;
18448
+ }
18449
+ /**
18450
+ * Given an object of form {"1": {...}, "2": {...}, ...} get the maximum ID used
18451
+ * in this object
18452
+ * If the object has no keys, return 0
18453
+ *
18454
+ */
18455
+ function getMaxObjectId(o) {
18456
+ const keys = Object.keys(o);
18457
+ if (!keys.length) {
18458
+ return 0;
18459
+ }
18460
+ const nums = keys.map((id) => parseInt(id, 10));
18461
+ const max = Math.max(...nums);
18462
+ return max;
18463
+ }
18464
+ const ALL_PERIODS = {
18465
+ year: _t("Year"),
18466
+ quarter: _t("Quarter"),
18467
+ month: _t("Month"),
18468
+ week: _t("Week"),
18469
+ day: _t("Day"),
18470
+ year_number: _t("Year"),
18471
+ quarter_number: _t("Quarter"),
18472
+ month_number: _t("Month"),
18473
+ iso_week_number: _t("Week"),
18474
+ day_of_month: _t("Day of Month"),
18475
+ };
18476
+ const DATE_FIELDS = ["date", "datetime"];
18477
+ /**
18478
+ * Parse a dimension string into a pivot dimension definition.
18479
+ * e.g "create_date:month" => { name: "create_date", granularity: "month" }
18480
+ */
18481
+ function parseDimension(dimension) {
18482
+ const [name, granularity] = dimension.split(":");
18483
+ if (granularity) {
18484
+ return { name, granularity };
18485
+ }
18486
+ return { name };
18487
+ }
18488
+ function isDateField(field) {
18489
+ return DATE_FIELDS.includes(field.type);
18490
+ }
18491
+ function toPivotDomain(domainStr) {
18492
+ if (domainStr.length % 2 !== 0) {
18493
+ throw new Error("Invalid domain: odd number of elements");
18494
+ }
18495
+ const domain = [];
18496
+ for (let i = 0; i < domainStr.length - 1; i += 2) {
18497
+ domain.push({ field: domainStr[i], value: domainStr[i + 1] });
18498
+ }
18499
+ return domain;
18500
+ }
18501
+ function flatPivotDomain(domain) {
18502
+ return domain.flatMap((arg) => [arg.field, arg.value]);
18503
+ }
18504
+
18380
18505
  /**
18381
18506
  * Get the pivot ID from the formula pivot ID.
18382
18507
  */
@@ -18856,13 +18981,13 @@ const PIVOT_VALUE = {
18856
18981
  arg("domain_field_name (string,optional,repeating)", _t("Field name.")),
18857
18982
  arg("domain_value (string,optional,repeating)", _t("Value.")),
18858
18983
  ],
18859
- compute: function (formulaId, measureName, ...domain) {
18984
+ compute: function (formulaId, measureName, ...domainArgs) {
18860
18985
  const _pivotFormulaId = toString(formulaId);
18861
- const measure = toString(measureName);
18862
- const domainArgs = domain.map(toString);
18986
+ const _measure = toString(measureName);
18987
+ const _domainArgs = domainArgs.map(toString);
18863
18988
  const pivotId = getPivotId(_pivotFormulaId, this.getters);
18864
- assertMeasureExist(pivotId, measure, this.getters);
18865
- assertDomainLength(domainArgs);
18989
+ assertMeasureExist(pivotId, _measure, this.getters);
18990
+ assertDomainLength(_domainArgs);
18866
18991
  const pivot = this.getters.getPivot(pivotId);
18867
18992
  const coreDefinition = this.getters.getPivotCoreDefinition(pivotId);
18868
18993
  if (!supportedPivotExplodedFormulaRegistry.get(coreDefinition.type)) {
@@ -18876,8 +19001,9 @@ const PIVOT_VALUE = {
18876
19001
  if (error) {
18877
19002
  return error;
18878
19003
  }
18879
- const { value, format } = pivot.getPivotCellValueAndFormat(measure, domainArgs);
18880
- if (!value && !this.getters.areDomainArgsFieldsValid(pivotId, domainArgs)) {
19004
+ const domain = toPivotDomain(_domainArgs);
19005
+ const { value, format } = pivot.getPivotCellValueAndFormat(_measure, domain);
19006
+ if (!value && !this.getters.areDomainArgsFieldsValid(pivotId, domain)) {
18881
19007
  return {
18882
19008
  value: CellErrorType.GenericError,
18883
19009
  message: _t("Dimensions don't match the pivot definition"),
@@ -18894,11 +19020,11 @@ const PIVOT_HEADER = {
18894
19020
  arg("domain_field_name (string,optional,repeating)", _t("Field name.")),
18895
19021
  arg("domain_value (string,optional,repeating)", _t("Value.")),
18896
19022
  ],
18897
- compute: function (pivotId, ...domain) {
19023
+ compute: function (pivotId, ...domainArgs) {
18898
19024
  const _pivotFormulaId = toString(pivotId);
18899
- const domainArgs = domain.map(toString);
19025
+ const _domainArgs = domainArgs.map(toString);
18900
19026
  const _pivotId = getPivotId(_pivotFormulaId, this.getters);
18901
- assertDomainLength(domainArgs);
19027
+ assertDomainLength(_domainArgs);
18902
19028
  const pivot = this.getters.getPivot(_pivotId);
18903
19029
  const coreDefinition = this.getters.getPivotCoreDefinition(_pivotId);
18904
19030
  if (!supportedPivotExplodedFormulaRegistry.get(coreDefinition.type)) {
@@ -18912,18 +19038,23 @@ const PIVOT_HEADER = {
18912
19038
  if (error) {
18913
19039
  return error;
18914
19040
  }
18915
- const fieldName = domainArgs.at(-2);
18916
- const valueArg = domainArgs.at(-1);
18917
- if (!this.getters.areDomainArgsFieldsValid(_pivotId, fieldName === "measure" ? domainArgs.slice(0, -2) : domainArgs)) {
19041
+ const domain = toPivotDomain(_domainArgs);
19042
+ const lastNode = domain.at(-1);
19043
+ if (!this.getters.areDomainArgsFieldsValid(_pivotId, lastNode?.field === "measure" ? domain.slice(0, -1) : domain)) {
18918
19044
  return {
18919
19045
  value: CellErrorType.GenericError,
18920
19046
  message: _t("Dimensions don't match the pivot definition"),
18921
19047
  };
18922
19048
  }
18923
- const { value, format } = pivot.getPivotHeaderValueAndFormat(domainArgs);
19049
+ if (lastNode?.field === "measure") {
19050
+ return pivot.getPivotMeasureValue(toString(lastNode.value), domain);
19051
+ }
19052
+ const { value, format } = pivot.getPivotHeaderValueAndFormat(domain);
18924
19053
  return {
18925
19054
  value,
18926
- format: !fieldName || fieldName === "measure" || valueArg === "false" ? undefined : format,
19055
+ format: !lastNode || lastNode.field === "measure" || lastNode.value === "false"
19056
+ ? undefined
19057
+ : format,
18927
19058
  };
18928
19059
  },
18929
19060
  returns: ["NUMBER", "STRING"],
@@ -18936,11 +19067,14 @@ const PIVOT = {
18936
19067
  arg("include_total (boolean, default=TRUE)", _t("Whether to include total/sub-totals or not.")),
18937
19068
  arg("include_column_titles (boolean, default=TRUE)", _t("Whether to include the column titles or not.")),
18938
19069
  ],
18939
- compute: function (pivotId, rowCount = { value: 10000 }, includeTotal = { value: true }, includeColumnHeaders = { value: true }) {
18940
- const _pivotFormulaId = toString(pivotId);
18941
- const _pivotId = getPivotId(_pivotFormulaId, this.getters);
18942
- const pivot = this.getters.getPivot(_pivotId);
18943
- const coreDefinition = this.getters.getPivotCoreDefinition(_pivotId);
19070
+ compute: function (pivotFormulaId, rowCount = { value: 10000 }, includeTotal = { value: true }, includeColumnHeaders = { value: true }) {
19071
+ const _pivotFormulaId = toString(pivotFormulaId);
19072
+ const _rowCount = toNumber(rowCount, this.locale);
19073
+ const _includeColumnHeaders = toBoolean(includeColumnHeaders);
19074
+ const _includedTotal = toBoolean(includeTotal);
19075
+ const pivotId = getPivotId(_pivotFormulaId, this.getters);
19076
+ const pivot = this.getters.getPivot(pivotId);
19077
+ const coreDefinition = this.getters.getPivotCoreDefinition(pivotId);
18944
19078
  addPivotDependencies(this, coreDefinition);
18945
19079
  pivot.init({ reload: pivot.needsReevaluation });
18946
19080
  const error = pivot.assertIsValid({ throwOnError: false });
@@ -18948,11 +19082,9 @@ const PIVOT = {
18948
19082
  return error;
18949
19083
  }
18950
19084
  const table = pivot.getTableStructure();
18951
- const _includeColumnHeaders = toBoolean(includeColumnHeaders);
18952
- const cells = table.getPivotCells(toBoolean(includeTotal), _includeColumnHeaders);
19085
+ const cells = table.getPivotCells(_includedTotal, _includeColumnHeaders);
18953
19086
  const headerRows = _includeColumnHeaders ? table.columns.length : 0;
18954
- const pivotTitle = this.getters.getPivotDisplayName(_pivotId);
18955
- const _rowCount = toNumber(rowCount, this.locale);
19087
+ const pivotTitle = this.getters.getPivotDisplayName(pivotId);
18956
19088
  if (_rowCount < 0) {
18957
19089
  throw new EvaluationError(_t("The number of rows must be positive."));
18958
19090
  }
@@ -18967,17 +19099,23 @@ const PIVOT = {
18967
19099
  result[col] = [];
18968
19100
  for (const row of tableRows) {
18969
19101
  const pivotCell = cells[col][row];
18970
- if (!pivotCell.domain) {
18971
- result[col].push({ value: "", format: undefined });
18972
- }
18973
- else if (pivotCell.isHeader) {
18974
- result[col].push(pivot.getPivotHeaderValueAndFormat(pivotCell.domain));
18975
- }
18976
- else {
18977
- if (!pivotCell.measure) {
18978
- throw new Error("Measure is missing");
18979
- }
18980
- result[col].push(pivot.getPivotCellValueAndFormat(pivotCell.measure, pivotCell.domain));
19102
+ switch (pivotCell.type) {
19103
+ case "EMPTY":
19104
+ result[col].push({ value: "" });
19105
+ break;
19106
+ case "HEADER":
19107
+ const domain = pivotCell.domain;
19108
+ const lastNode = domain.at(-1);
19109
+ if (lastNode?.field === "measure") {
19110
+ result[col].push(pivot.getPivotMeasureValue(toString(lastNode.value), domain));
19111
+ }
19112
+ else {
19113
+ result[col].push(pivot.getPivotHeaderValueAndFormat(domain));
19114
+ }
19115
+ break;
19116
+ case "VALUE":
19117
+ result[col].push(pivot.getPivotCellValueAndFormat(pivotCell.measure, pivotCell.domain));
19118
+ break;
18981
19119
  }
18982
19120
  }
18983
19121
  }
@@ -20902,8 +21040,15 @@ class Composer extends owl.Component {
20902
21040
  }
20903
21041
  onPaste(ev) {
20904
21042
  if (this.composerStore.editionMode !== "inactive") {
21043
+ // let the browser clipboard work
20905
21044
  ev.stopPropagation();
20906
21045
  }
21046
+ else {
21047
+ // the user meant to paste in the sheet, not open the composer with the pasted content
21048
+ // While we're not editing, we still have the focus and should therefore prevent
21049
+ // the native "paste" to occur.
21050
+ ev.preventDefault();
21051
+ }
20907
21052
  }
20908
21053
  /*
20909
21054
  * Triggered automatically by the content-editable between the keydown and key up
@@ -20912,9 +21057,6 @@ class Composer extends owl.Component {
20912
21057
  if (!this.shouldProcessInputEvents) {
20913
21058
  return;
20914
21059
  }
20915
- if (ev.inputType === "insertFromPaste" && this.composerStore.editionMode === "inactive") {
20916
- return;
20917
- }
20918
21060
  ev.stopPropagation();
20919
21061
  let content;
20920
21062
  if (this.composerStore.editionMode === "inactive") {
@@ -21567,132 +21709,8 @@ function getFunctionsFromAST(ast, functionNames) {
21567
21709
  }
21568
21710
 
21569
21711
  const PIVOT_FUNCTIONS = ["PIVOT.VALUE", "PIVOT.HEADER", "PIVOT"];
21570
- const AGGREGATOR_NAMES = {
21571
- count: _t("Count"),
21572
- count_distinct: _t("Count Distinct"),
21573
- bool_and: _t("Boolean And"),
21574
- bool_or: _t("Boolean Or"),
21575
- max: _t("Maximum"),
21576
- min: _t("Minimum"),
21577
- avg: _t("Average"),
21578
- sum: _t("Sum"),
21579
- };
21580
- const NUMBER_CHAR_AGGREGATORS = ["max", "min", "avg", "sum", "count_distinct", "count"];
21581
- const AGGREGATORS_BY_FIELD_TYPE = {
21582
- integer: NUMBER_CHAR_AGGREGATORS,
21583
- char: NUMBER_CHAR_AGGREGATORS,
21584
- //TODO Support for date and boolean
21585
- };
21586
- const AGGREGATORS = {};
21587
- for (const type in AGGREGATORS_BY_FIELD_TYPE) {
21588
- AGGREGATORS[type] = {};
21589
- for (const aggregator of AGGREGATORS_BY_FIELD_TYPE[type]) {
21590
- AGGREGATORS[type][aggregator] = AGGREGATOR_NAMES[aggregator];
21591
- }
21592
- }
21593
- const AGGREGATORS_FN = {
21594
- count: {
21595
- fn: (args) => countAny([args]),
21596
- format: () => "0",
21597
- },
21598
- count_distinct: {
21599
- fn: (args) => countUnique([args]),
21600
- format: () => "0",
21601
- },
21602
- bool_and: {
21603
- fn: (args) => boolAnd([args]).result,
21604
- format: () => undefined,
21605
- },
21606
- bool_or: {
21607
- fn: (args) => boolOr([args]).result,
21608
- format: () => undefined,
21609
- },
21610
- max: {
21611
- fn: (args, locale) => max([args], locale),
21612
- format: inferFormat,
21613
- },
21614
- min: {
21615
- fn: (args, locale) => min([args], locale),
21616
- format: inferFormat,
21617
- },
21618
- avg: {
21619
- fn: (args, locale) => average([args], locale),
21620
- format: inferFormat,
21621
- },
21622
- sum: {
21623
- fn: (args, locale) => sum([args], locale),
21624
- format: inferFormat,
21625
- },
21626
- };
21627
- /**
21628
- * Build a pivot formula expression
21629
- */
21630
- function makePivotFormula(formula, args) {
21631
- return `=${formula}(${args
21632
- .map((arg) => {
21633
- const stringIsNumber = typeof arg == "string" && !isNaN(Number(arg)) && Number(arg).toString() === arg;
21634
- const convertToNumber = typeof arg == "number" || stringIsNumber;
21635
- return convertToNumber ? `${arg}` : `"${arg.toString().replace(/"/g, '\\"')}"`;
21636
- })
21637
- .join(",")})`;
21638
- }
21639
- /**
21640
- * Given an object of form {"1": {...}, "2": {...}, ...} get the maximum ID used
21641
- * in this object
21642
- * If the object has no keys, return 0
21643
- *
21644
- */
21645
- function getMaxObjectId(o) {
21646
- const keys = Object.keys(o);
21647
- if (!keys.length) {
21648
- return 0;
21649
- }
21650
- const nums = keys.map((id) => parseInt(id, 10));
21651
- const max = Math.max(...nums);
21652
- return max;
21653
- }
21654
- /**
21655
- * Get the first Pivot function description of the given formula.
21656
- */
21657
- function getFirstPivotFunction(tokens) {
21658
- return getFunctionsFromTokens(tokens, PIVOT_FUNCTIONS)[0];
21659
- }
21660
- /**
21661
- * Parse a spreadsheet formula and detect the number of PIVOT functions that are
21662
- * present in the given formula.
21663
- */
21664
- function getNumberOfPivotFunctions(tokens) {
21665
- return getFunctionsFromTokens(tokens, PIVOT_FUNCTIONS).length;
21666
- }
21667
- const ALL_PERIODS = {
21668
- year: _t("Year"),
21669
- quarter: _t("Quarter"),
21670
- month: _t("Month"),
21671
- week: _t("Week"),
21672
- day: _t("Day"),
21673
- year_number: _t("Year"),
21674
- quarter_number: _t("Quarter"),
21675
- month_number: _t("Month"),
21676
- iso_week_number: _t("Week"),
21677
- day_of_month: _t("Day of Month"),
21678
- };
21679
- const DATE_FIELDS = ["date", "datetime"];
21680
- /**
21681
- * Parse a dimension string into a pivot dimension definition.
21682
- * e.g "create_date:month" => { name: "create_date", granularity: "month" }
21683
- */
21684
- function parseDimension(dimension) {
21685
- const [name, granularity] = dimension.split(":");
21686
- if (granularity) {
21687
- return { name, granularity };
21688
- }
21689
- return { name };
21690
- }
21691
- function isDateField(field) {
21692
- return DATE_FIELDS.includes(field.type);
21693
- }
21694
21712
  /**
21695
- * Create a proposal entry for the compose autocomplete
21713
+ * Create a proposal entry for the compose autowcomplete
21696
21714
  * to insert a field name string in a formula.
21697
21715
  */
21698
21716
  function makeFieldProposal(field, granularity) {
@@ -21748,15 +21766,18 @@ function extractFormulaIdFromToken(tokenAtCursor) {
21748
21766
  }
21749
21767
  return idAst.value;
21750
21768
  }
21751
- function toDomainArgs(domainStr) {
21752
- if (domainStr.length % 2 !== 0) {
21753
- throw new Error("Invalid domain: odd number of elements");
21754
- }
21755
- const domain = [];
21756
- for (let i = 0; i < domainStr.length - 1; i += 2) {
21757
- domain.push({ field: domainStr[i], value: domainStr[i + 1] });
21758
- }
21759
- return domain;
21769
+ /**
21770
+ * Get the first Pivot function description of the given formula.
21771
+ */
21772
+ function getFirstPivotFunction(tokens) {
21773
+ return getFunctionsFromTokens(tokens, PIVOT_FUNCTIONS)[0];
21774
+ }
21775
+ /**
21776
+ * Parse a spreadsheet formula and detect the number of PIVOT functions that are
21777
+ * present in the given formula.
21778
+ */
21779
+ function getNumberOfPivotFunctions(tokens) {
21780
+ return getFunctionsFromTokens(tokens, PIVOT_FUNCTIONS).length;
21760
21781
  }
21761
21782
 
21762
21783
  autoCompleteProviders.add("pivot_ids", {
@@ -32784,7 +32805,7 @@ css /* scss */ `
32784
32805
  vertical-align: middle;
32785
32806
  }
32786
32807
  .o_cf_radio_item {
32787
- margin-right: 10%;
32808
+ margin-right: 30px;
32788
32809
  }
32789
32810
  .radio input:checked {
32790
32811
  color: #e9ecef;
@@ -32804,6 +32825,9 @@ css /* scss */ `
32804
32825
  }
32805
32826
  margin-top: 10px;
32806
32827
  display: flex;
32828
+ .form-check {
32829
+ padding-left: 1rem;
32830
+ }
32807
32831
  }
32808
32832
  .o-section-subtitle:first-child {
32809
32833
  margin-top: 0px;
@@ -32812,7 +32836,7 @@ css /* scss */ `
32812
32836
  font-size: 12px;
32813
32837
  line-height: 1.5;
32814
32838
  .o-selection-cf {
32815
- margin-bottom: 3%;
32839
+ margin-bottom: 9px;
32816
32840
  }
32817
32841
  .o-cell-content {
32818
32842
  font-size: 12px;
@@ -32853,7 +32877,7 @@ css /* scss */ `
32853
32877
  width: 100%;
32854
32878
  }
32855
32879
  .o-threshold-value {
32856
- margin-left: 2%;
32880
+ margin-left: 6px;
32857
32881
  width: 20%;
32858
32882
  min-width: 0px; // input overflows in Firefox otherwise
32859
32883
  }
@@ -32882,8 +32906,8 @@ css /* scss */ `
32882
32906
  justify-content: space-between;
32883
32907
  .o-cf-icon {
32884
32908
  display: inline;
32885
- margin-left: 1%;
32886
- margin-right: 1%;
32909
+ margin-left: 3px;
32910
+ margin-right: 3px;
32887
32911
  }
32888
32912
  svg {
32889
32913
  vertical-align: baseline;
@@ -32906,7 +32930,7 @@ css /* scss */ `
32906
32930
  }
32907
32931
  table {
32908
32932
  table-layout: fixed;
32909
- margin-top: 2%;
32933
+ margin-top: 6px;
32910
32934
  display: table;
32911
32935
  text-align: left;
32912
32936
  font-size: 12px;
@@ -32936,8 +32960,8 @@ css /* scss */ `
32936
32960
  }
32937
32961
  }
32938
32962
  .o-cf-iconset-reverse {
32939
- margin-bottom: 2%;
32940
- margin-top: 2%;
32963
+ margin-bottom: 6px;
32964
+ margin-top: 6px;
32941
32965
  .o-cf-label {
32942
32966
  display: inline-block;
32943
32967
  vertical-align: bottom;
@@ -34644,6 +34668,10 @@ css /* scss */ `
34644
34668
  select > option {
34645
34669
  background-color: white;
34646
34670
  }
34671
+
34672
+ .pivot-dim-operator-label {
34673
+ min-width: 120px;
34674
+ }
34647
34675
  }
34648
34676
  `;
34649
34677
  class PivotDimension extends owl.Component {
@@ -34884,7 +34912,7 @@ class PivotRuntimeDefinition {
34884
34912
  getMeasure(name) {
34885
34913
  const measure = this.measures.find((measure) => measure.name === name);
34886
34914
  if (!measure) {
34887
- throw new EvaluationError(_t("Field %s does not exist", name));
34915
+ throw new EvaluationError(_t("Field %s is not a measure", name));
34888
34916
  }
34889
34917
  return measure;
34890
34918
  }
@@ -35010,10 +35038,9 @@ class SpreadsheetPivotTable {
35010
35038
  columns;
35011
35039
  rows;
35012
35040
  measures;
35013
- rowTitle;
35014
35041
  maxIndent;
35015
35042
  pivotCells = {};
35016
- constructor(columns, rows, measures, rowTitle = "") {
35043
+ constructor(columns, rows, measures) {
35017
35044
  this.columns = columns.map((row) => {
35018
35045
  // offset in the pivot table
35019
35046
  // starts at 1 because the first column is the row title
@@ -35026,7 +35053,6 @@ class SpreadsheetPivotTable {
35026
35053
  });
35027
35054
  this.rows = rows;
35028
35055
  this.measures = measures;
35029
- this.rowTitle = rowTitle;
35030
35056
  this.maxIndent = Math.max(...this.rows.map((row) => row.indent));
35031
35057
  }
35032
35058
  /**
@@ -35068,26 +35094,23 @@ class SpreadsheetPivotTable {
35068
35094
  }
35069
35095
  getPivotCell(col, row, includeTotal = true) {
35070
35096
  const colHeadersHeight = this.columns.length;
35071
- if (col === 0 && row === colHeadersHeight - 1) {
35072
- return { content: this.rowTitle, isHeader: true };
35073
- }
35074
- else if (row <= colHeadersHeight - 1) {
35097
+ if (row <= colHeadersHeight - 1) {
35075
35098
  const domain = this.getColHeaderDomain(col, row);
35076
- return { domain, isHeader: true };
35099
+ return domain ? { type: "HEADER", domain } : { type: "EMPTY" };
35077
35100
  }
35078
35101
  else if (col === 0) {
35079
35102
  const rowIndex = row - colHeadersHeight;
35080
35103
  const domain = this.getRowDomain(rowIndex);
35081
- return { domain, isHeader: true };
35104
+ return { type: "HEADER", domain };
35082
35105
  }
35083
35106
  else {
35084
35107
  const rowIndex = row - colHeadersHeight;
35085
35108
  if (!includeTotal && this.isTotalRow(rowIndex)) {
35086
- return { isHeader: false };
35109
+ return { type: "EMPTY" };
35087
35110
  }
35088
35111
  const domain = [...this.getRowDomain(rowIndex), ...this.getColDomain(col)];
35089
35112
  const measure = this.getColMeasure(col);
35090
- return { domain, isHeader: false, measure };
35113
+ return { type: "VALUE", domain, measure };
35091
35114
  }
35092
35115
  }
35093
35116
  getColHeaderDomain(col, row) {
@@ -35100,24 +35123,32 @@ class SpreadsheetPivotTable {
35100
35123
  return undefined;
35101
35124
  }
35102
35125
  for (let i = 0; i < pivotCol.fields.length; i++) {
35103
- domain.push(pivotCol.fields[i]);
35104
- domain.push(pivotCol.values[i]);
35126
+ domain.push({
35127
+ field: pivotCol.fields[i],
35128
+ value: pivotCol.values[i],
35129
+ });
35105
35130
  }
35106
35131
  return domain;
35107
35132
  }
35108
35133
  getColDomain(col) {
35109
35134
  const domain = this.getColHeaderDomain(col, this.columns.length - 1);
35110
- return domain ? domain.slice(0, -2) : []; // slice: remove measure and value
35135
+ return domain ? domain.slice(0, -1) : []; // slice: remove measure and value
35111
35136
  }
35112
35137
  getColMeasure(col) {
35113
35138
  const domain = this.getColHeaderDomain(col, this.columns.length - 1);
35114
- return domain?.at(-1);
35139
+ const measure = domain?.at(-1)?.value;
35140
+ if (measure === undefined) {
35141
+ throw new Error("Measure isd missing");
35142
+ }
35143
+ return measure.toString();
35115
35144
  }
35116
35145
  getRowDomain(row) {
35117
35146
  const domain = [];
35118
35147
  for (let i = 0; i < this.rows[row].fields.length; i++) {
35119
- domain.push(this.rows[row].fields[i]);
35120
- domain.push(this.rows[row].values[i]);
35148
+ domain.push({
35149
+ field: this.rows[row].fields[i],
35150
+ value: this.rows[row].values[i],
35151
+ });
35121
35152
  }
35122
35153
  return domain;
35123
35154
  }
@@ -35126,7 +35157,6 @@ class SpreadsheetPivotTable {
35126
35157
  cols: this.columns,
35127
35158
  rows: this.rows,
35128
35159
  measures: this.measures,
35129
- rowTitle: this.rowTitle,
35130
35160
  };
35131
35161
  }
35132
35162
  }
@@ -35146,8 +35176,7 @@ function dataEntriesToSpreadsheetPivotTable(dataEntries, definition) {
35146
35176
  indent: 0,
35147
35177
  });
35148
35178
  const measureNames = definition.measures.map((m) => m.name);
35149
- const rowTitle = rows.length > 0 ? rows[0].values[0] : "";
35150
- return new SpreadsheetPivotTable(cols, rows, measureNames, rowTitle);
35179
+ return new SpreadsheetPivotTable(cols, rows, measureNames);
35151
35180
  }
35152
35181
  // -----------------------------------------------------------------------------
35153
35182
  // ROWS
@@ -35490,7 +35519,10 @@ class SpreadsheetPivot {
35490
35519
  }
35491
35520
  get definition() {
35492
35521
  if (!this._definition) {
35493
- throw new Error("Pivot not loaded yet");
35522
+ this.init();
35523
+ }
35524
+ if (!this._definition) {
35525
+ throw new Error("Pivot definition should be defined at this point.");
35494
35526
  }
35495
35527
  return this._definition;
35496
35528
  }
@@ -35539,15 +35571,16 @@ class SpreadsheetPivot {
35539
35571
  getMeasure(name) {
35540
35572
  return this.definition.getMeasure(name);
35541
35573
  }
35542
- getPivotHeaderValueAndFormat(domainStr) {
35543
- const domain = toDomainArgs(domainStr);
35574
+ getPivotMeasureValue(name) {
35575
+ return {
35576
+ value: this.getMeasure(name).displayName,
35577
+ };
35578
+ }
35579
+ getPivotHeaderValueAndFormat(domain) {
35544
35580
  const lastNode = domain.at(-1);
35545
35581
  if (!lastNode) {
35546
35582
  return { value: _t("Total") };
35547
35583
  }
35548
- if (lastNode.field === "measure") {
35549
- return { value: this.getMeasure(lastNode.value).displayName };
35550
- }
35551
35584
  const dimension = this.getDimension(lastNode.field);
35552
35585
  const cells = this.filterDataEntriesFromDomain(this.dataEntries, domain);
35553
35586
  const finalCell = cells[0]?.[dimension.nameWithGranularity];
@@ -35575,8 +35608,7 @@ class SpreadsheetPivot {
35575
35608
  format: finalCell.format,
35576
35609
  };
35577
35610
  }
35578
- getPivotCellValueAndFormat(measure, domainStr) {
35579
- const domain = toDomainArgs(domainStr);
35611
+ getPivotCellValueAndFormat(measure, domain) {
35580
35612
  const dataEntries = this.filterDataEntriesFromDomain(this.dataEntries, domain);
35581
35613
  if (dataEntries.length === 0) {
35582
35614
  return { value: "" };
@@ -35977,7 +36009,6 @@ class PivotSpreadsheetSidePanel extends owl.Component {
35977
36009
  state;
35978
36010
  setup() {
35979
36011
  this.store = useLocalStore(PivotSidePanelStore, this.props.pivotId);
35980
- this.pivot.init();
35981
36012
  this.state = owl.useState({
35982
36013
  range: undefined,
35983
36014
  rangeHasChanged: false,
@@ -36854,7 +36885,7 @@ css /* scss */ `
36854
36885
  }
36855
36886
 
36856
36887
  .o-table-style-list-item {
36857
- padding: 3px;
36888
+ padding: 3px 2px;
36858
36889
  margin: 2px 1px;
36859
36890
 
36860
36891
  .o-table-style-picker-preview {
@@ -36875,10 +36906,10 @@ class TableStylePicker extends owl.Component {
36875
36906
  const styles = Object.keys(allStyles).filter((key) => allStyles[key].category === selectedStyleCategory);
36876
36907
  const selectedStyleIndex = styles.indexOf(this.props.table.config.styleId);
36877
36908
  if (selectedStyleIndex === -1) {
36878
- return styles.slice(0, 4);
36909
+ return selectedStyleIndex;
36879
36910
  }
36880
36911
  const index = Math.floor(selectedStyleIndex / 4) * 4;
36881
- return styles.slice(index, index + 4);
36912
+ return styles.slice(index);
36882
36913
  }
36883
36914
  onStylePicked(styleId) {
36884
36915
  const sheetId = this.env.model.getters.getActiveSheetId();
@@ -37918,10 +37949,7 @@ class GridComposer extends owl.Component {
37918
37949
  }
37919
37950
  get containerStyle() {
37920
37951
  if (this.composerStore.editionMode === "inactive") {
37921
- return `
37922
- position: absolute;
37923
- z-index: -1000;
37924
- `;
37952
+ return `z-index: -1000;`;
37925
37953
  }
37926
37954
  const isFormula = this.composerStore.currentContent.startsWith("=");
37927
37955
  const cell = this.env.model.getters.getActiveCell();
@@ -40765,10 +40793,13 @@ class VerticalScrollBar extends owl.Component {
40765
40793
  }
40766
40794
  }
40767
40795
 
40796
+ const DEFAULT_SIDE_PANEL_SIZE = 350;
40797
+ const MIN_SHEET_VIEW_WIDTH = 150;
40768
40798
  class SidePanelStore extends SpreadsheetStore {
40769
- mutators = ["open", "toggle", "close"];
40799
+ mutators = ["open", "toggle", "close", "changePanelSize", "resetPanelSize"];
40770
40800
  initialPanelProps = {};
40771
40801
  componentTag = "";
40802
+ panelSize = DEFAULT_SIDE_PANEL_SIZE;
40772
40803
  get isOpen() {
40773
40804
  if (!this.componentTag) {
40774
40805
  return false;
@@ -40813,6 +40844,20 @@ class SidePanelStore extends SpreadsheetStore {
40813
40844
  this.initialPanelProps = {};
40814
40845
  this.componentTag = "";
40815
40846
  }
40847
+ changePanelSize(size, spreadsheetElWidth) {
40848
+ if (size < DEFAULT_SIDE_PANEL_SIZE) {
40849
+ this.panelSize = DEFAULT_SIDE_PANEL_SIZE;
40850
+ }
40851
+ else if (size > spreadsheetElWidth - MIN_SHEET_VIEW_WIDTH) {
40852
+ this.panelSize = Math.max(spreadsheetElWidth - MIN_SHEET_VIEW_WIDTH, DEFAULT_SIDE_PANEL_SIZE);
40853
+ }
40854
+ else {
40855
+ this.panelSize = size;
40856
+ }
40857
+ }
40858
+ resetPanelSize() {
40859
+ this.panelSize = DEFAULT_SIDE_PANEL_SIZE;
40860
+ }
40816
40861
  computeState(componentTag, panelProps) {
40817
40862
  const customComputeState = sidePanelRegistry.get(componentTag).computeState;
40818
40863
  if (!customComputeState) {
@@ -41933,7 +41978,7 @@ class XLSXImportWarningManager {
41933
41978
  }
41934
41979
  }
41935
41980
 
41936
- const SUPPORTED_BORDER_STYLES = ["thin"];
41981
+ const SUPPORTED_BORDER_STYLES = ["thin", "medium", "thick", "dashed", "dotted"];
41937
41982
  const SUPPORTED_HORIZONTAL_ALIGNMENTS = [
41938
41983
  "general",
41939
41984
  "left",
@@ -51072,8 +51117,8 @@ class PivotCorePlugin extends CorePlugin {
51072
51117
  case "INSERT_PIVOT": {
51073
51118
  const { sheetId, col, row, pivotId, table } = cmd;
51074
51119
  const position = { sheetId, col, row };
51075
- const { cols, rows, measures, rowTitle } = table;
51076
- const spTable = new SpreadsheetPivotTable(cols, rows, measures, rowTitle);
51120
+ const { cols, rows, measures } = table;
51121
+ const spTable = new SpreadsheetPivotTable(cols, rows, measures);
51077
51122
  const formulaId = this.getPivotFormulaId(pivotId);
51078
51123
  this.insertPivot(position, formulaId, spTable);
51079
51124
  break;
@@ -51202,13 +51247,18 @@ class PivotCorePlugin extends CorePlugin {
51202
51247
  }
51203
51248
  }
51204
51249
  addPivotFormula(position, formulaId, pivotCell) {
51205
- const formula = pivotCell.isHeader ? "PIVOT.HEADER" : "PIVOT.VALUE";
51206
- const args = pivotCell.domain
51207
- ? [formulaId, pivotCell.measure, ...pivotCell.domain].filter(isDefined)
51208
- : undefined;
51250
+ let content = undefined;
51251
+ switch (pivotCell.type) {
51252
+ case "HEADER":
51253
+ content = makePivotFormula("PIVOT.HEADER", [formulaId, ...flatPivotDomain(pivotCell.domain)].filter(isDefined));
51254
+ break;
51255
+ case "VALUE":
51256
+ content = makePivotFormula("PIVOT.VALUE", [formulaId, pivotCell.measure, ...flatPivotDomain(pivotCell.domain)].filter(isDefined));
51257
+ break;
51258
+ }
51209
51259
  this.dispatch("UPDATE_CELL", {
51210
51260
  ...position,
51211
- content: pivotCell.content || (args ? makePivotFormula(formula, args) : undefined),
51261
+ content,
51212
51262
  });
51213
51263
  }
51214
51264
  getPivotCore(pivotId) {
@@ -51227,7 +51277,7 @@ class PivotCorePlugin extends CorePlugin {
51227
51277
  import(data) {
51228
51278
  if (data.pivots) {
51229
51279
  for (const [id, pivot] of Object.entries(data.pivots)) {
51230
- this.addPivot(id, deepCopy(pivot), pivot.formulaId);
51280
+ this.addPivot(id, pivot, pivot.formulaId);
51231
51281
  }
51232
51282
  }
51233
51283
  this.history.update("nextFormulaId", data.pivotNextId || getMaxObjectId(this.pivots) + 1);
@@ -54568,15 +54618,18 @@ class PivotUIPlugin extends UIPlugin {
54568
54618
  const pivotCol = position.col - mainPosition.col;
54569
54619
  const pivotRow = position.row - mainPosition.row;
54570
54620
  const pivotCell = pivotCells[pivotCol][pivotRow];
54621
+ if (pivotCell.type === "EMPTY") {
54622
+ return undefined;
54623
+ }
54571
54624
  const domain = pivotCell.domain;
54572
- if (domain?.at(-2) === "measure") {
54573
- return domain.slice(0, -2);
54625
+ if (domain.at(-1)?.field === "measure") {
54626
+ return domain.slice(0, -1);
54574
54627
  }
54575
54628
  return domain;
54576
54629
  }
54577
- const domain = args.slice(functionName === "PIVOT.VALUE" ? 2 : 1);
54578
- if (domain.at(-2) === "measure") {
54579
- return domain.slice(0, -2);
54630
+ const domain = toPivotDomain(args.slice(functionName === "PIVOT.VALUE" ? 2 : 1).map((x) => `${x}`));
54631
+ if (domain.at(-1)?.field === "measure") {
54632
+ return domain.slice(0, -1);
54580
54633
  }
54581
54634
  return domain;
54582
54635
  }
@@ -54591,9 +54644,9 @@ class PivotUIPlugin extends UIPlugin {
54591
54644
  * a pivot function are valid according to the pivot definition.
54592
54645
  * e.g. =PIVOT.VALUE(1,"revenue","country_id",...,"create_date:month",...,"source_id",...)
54593
54646
  */
54594
- areDomainArgsFieldsValid(pivotId, domainArgs) {
54595
- const dimensions = domainArgs
54596
- .filter((arg, index) => index % 2 === 0)
54647
+ areDomainArgsFieldsValid(pivotId, domain) {
54648
+ const dimensions = domain
54649
+ .map((node) => node.field)
54597
54650
  .map((name) => (name.startsWith("#") ? name.slice(1) : name));
54598
54651
  let argIndex = 0;
54599
54652
  let definitionIndex = 0;
@@ -61607,6 +61660,10 @@ css /* scss */ `
61607
61660
  }
61608
61661
  }
61609
61662
  }
61663
+ .o-sidePanelBody-container {
61664
+ /* This overwrites the min-height: auto; of flex. Without this, a flex div cannot be smaller than its children */
61665
+ min-height: 0;
61666
+ }
61610
61667
  .o-sidePanelBody {
61611
61668
  overflow: auto;
61612
61669
  width: 100%;
@@ -61685,24 +61742,6 @@ css /* scss */ `
61685
61742
  text-align: left;
61686
61743
  }
61687
61744
 
61688
- .o-inflection {
61689
- table {
61690
- table-layout: fixed;
61691
- margin-top: 2%;
61692
- display: table;
61693
- text-align: left;
61694
- font-size: 12px;
61695
- line-height: 18px;
61696
- width: 100%;
61697
- }
61698
- input,
61699
- select {
61700
- width: 100%;
61701
- height: 100%;
61702
- box-sizing: border-box;
61703
- }
61704
- }
61705
-
61706
61745
  .o-sidePanel-tools {
61707
61746
  color: #333;
61708
61747
  font-size: 13px;
@@ -61721,12 +61760,25 @@ css /* scss */ `
61721
61760
  }
61722
61761
  }
61723
61762
  }
61763
+
61764
+ .o-sidePanel-handle-container {
61765
+ width: 8px;
61766
+ }
61767
+ .o-sidePanel-handle {
61768
+ cursor: col-resize;
61769
+ color: #a9a9a9;
61770
+ .o-icon {
61771
+ height: 25px;
61772
+ margin-left: -5px;
61773
+ }
61774
+ }
61724
61775
  }
61725
61776
  `;
61726
61777
  class SidePanel extends owl.Component {
61727
61778
  static template = "o-spreadsheet-SidePanel";
61728
61779
  static props = {};
61729
61780
  sidePanelStore;
61781
+ spreadsheetRect = useSpreadsheetRect();
61730
61782
  setup() {
61731
61783
  this.sidePanelStore = useStore(SidePanelStore);
61732
61784
  owl.useEffect((isOpen) => {
@@ -61747,6 +61799,20 @@ class SidePanel extends owl.Component {
61747
61799
  ? panel.title(this.env, this.sidePanelStore.panelProps)
61748
61800
  : panel.title;
61749
61801
  }
61802
+ startHandleDrag(ev) {
61803
+ const startingCursor = document.body.style.cursor;
61804
+ const startSize = this.sidePanelStore.panelSize;
61805
+ const startPosition = ev.clientX;
61806
+ const onMouseMove = (ev) => {
61807
+ document.body.style.cursor = "col-resize";
61808
+ const newSize = startSize + startPosition - ev.clientX;
61809
+ this.sidePanelStore.changePanelSize(newSize, this.spreadsheetRect.width);
61810
+ };
61811
+ const cleanUp = () => {
61812
+ document.body.style.cursor = startingCursor;
61813
+ };
61814
+ startDnd(onMouseMove, cleanUp);
61815
+ }
61750
61816
  }
61751
61817
 
61752
61818
  css /* scss */ `
@@ -62530,7 +62596,6 @@ css /* scss */ `
62530
62596
  .o-spreadsheet {
62531
62597
  position: relative;
62532
62598
  display: grid;
62533
- grid-template-columns: auto 350px;
62534
62599
  color: #333;
62535
62600
  font-size: 14px;
62536
62601
 
@@ -62728,6 +62793,7 @@ class Spreadsheet extends owl.Component {
62728
62793
  };
62729
62794
  sidePanel;
62730
62795
  spreadsheetRef = owl.useRef("spreadsheet");
62796
+ spreadsheetRect = useSpreadsheetRect();
62731
62797
  _focusGrid;
62732
62798
  keyDownMapping;
62733
62799
  isViewportTooSmall = false;
@@ -62737,10 +62803,15 @@ class Spreadsheet extends owl.Component {
62737
62803
  return this.props.model;
62738
62804
  }
62739
62805
  getStyle() {
62806
+ const properties = {};
62740
62807
  if (this.env.isDashboard()) {
62741
- return `grid-template-rows: auto;`;
62808
+ properties["grid-template-rows"] = `auto`;
62742
62809
  }
62743
- return `grid-template-rows: ${TOPBAR_HEIGHT}px auto ${BOTTOMBAR_HEIGHT + 1}px`;
62810
+ else {
62811
+ properties["grid-template-rows"] = `${TOPBAR_HEIGHT}px auto ${BOTTOMBAR_HEIGHT + 1}px`;
62812
+ }
62813
+ properties["grid-template-columns"] = `auto ${this.sidePanel.panelSize}px`;
62814
+ return cssPropertiesToCss(properties);
62744
62815
  }
62745
62816
  setup() {
62746
62817
  const stores = useStoreProvider();
@@ -62800,14 +62871,19 @@ class Spreadsheet extends owl.Component {
62800
62871
  owl.onMounted(() => {
62801
62872
  this.checkViewportSize();
62802
62873
  stores.on("store-updated", this, render);
62874
+ resizeObserver.observe(this.spreadsheetRef.el);
62803
62875
  });
62804
62876
  owl.onWillUnmount(() => {
62805
62877
  this.unbindModelEvents();
62806
62878
  stores.off("store-updated", this);
62879
+ resizeObserver.disconnect();
62807
62880
  });
62808
62881
  owl.onPatched(() => {
62809
62882
  this.checkViewportSize();
62810
62883
  });
62884
+ const resizeObserver = new ResizeObserver(() => {
62885
+ this.sidePanel.changePanelSize(this.sidePanel.panelSize, this.spreadsheetRect.width);
62886
+ });
62811
62887
  }
62812
62888
  bindModelEvents() {
62813
62889
  this.model.on("update", this, () => this.render(true));
@@ -66862,6 +66938,8 @@ const helpers = {
66862
66938
  insertTokenAfterLeftParenthesis,
66863
66939
  mergeContiguousZones,
66864
66940
  getPivotHighlights,
66941
+ toPivotDomain,
66942
+ flatPivotDomain,
66865
66943
  pivotTimeAdapter,
66866
66944
  UNDO_REDO_PIVOT_COMMANDS,
66867
66945
  };
@@ -66989,6 +67067,6 @@ exports.tokenColors = tokenColors;
66989
67067
  exports.tokenize = tokenize;
66990
67068
 
66991
67069
 
66992
- __info__.version = "17.4.0-alpha.1";
66993
- __info__.date = "2024-06-03T15:30:28.283Z";
66994
- __info__.hash = "cb56c37";
67070
+ __info__.version = "17.4.0-alpha.2";
67071
+ __info__.date = "2024-06-06T13:31:21.327Z";
67072
+ __info__.hash = "e07794d";