@odoo/o-spreadsheet 16.5.0-alpha.11 → 16.5.0-alpha.13

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.
@@ -2,9 +2,9 @@
2
2
  /**
3
3
  * This file is generated by o-spreadsheet build tools. Do not edit it.
4
4
  * @see https://github.com/odoo/o-spreadsheet
5
- * @version 16.5.0-alpha.11
6
- * @date 2023-10-19T14:26:01.790Z
7
- * @hash 4b42059
5
+ * @version 16.5.0-alpha.13
6
+ * @date 2023-10-25T10:04:45.428Z
7
+ * @hash 18a08b1
8
8
  */
9
9
 
10
10
  'use strict';
@@ -2060,6 +2060,7 @@ exports.CommandResult = void 0;
2060
2060
  CommandResult["InvalidDataValidationCriterionValue"] = "InvalidDataValidationCriterionValue";
2061
2061
  CommandResult["InvalidNumberOfCriterionValues"] = "InvalidNumberOfCriterionValues";
2062
2062
  CommandResult["BlockingValidationRule"] = "BlockingValidationRule";
2063
+ CommandResult["InvalidCopyPasteSelection"] = "InvalidCopyPasteSelection";
2063
2064
  })(exports.CommandResult || (exports.CommandResult = {}));
2064
2065
 
2065
2066
  const DEFAULT_LOCALES = [
@@ -4661,7 +4662,7 @@ class ChartJsComponent extends owl.Component {
4661
4662
  return `background-color: ${this.background}`;
4662
4663
  }
4663
4664
  get chartRuntime() {
4664
- const runtime = this.env.model.getters.getChartRuntime(this.props.figureId);
4665
+ const runtime = this.env.model.getters.getChartRuntime(this.props.figure.id);
4665
4666
  if (!("chartJsConfig" in runtime)) {
4666
4667
  throw new Error("Unsupported chart runtime");
4667
4668
  }
@@ -4705,7 +4706,7 @@ class ChartJsComponent extends owl.Component {
4705
4706
  }
4706
4707
  }
4707
4708
  ChartJsComponent.props = {
4708
- figureId: String,
4709
+ figure: Object,
4709
4710
  };
4710
4711
 
4711
4712
  /**
@@ -5107,6 +5108,8 @@ function checkBaseline(definition) {
5107
5108
  ? "InvalidScorecardBaseline" /* CommandResult.InvalidScorecardBaseline */
5108
5109
  : "Success" /* CommandResult.Success */;
5109
5110
  }
5111
+ const arrowDownPath = new window.Path2D("M8.6 4.8a.5.5 0 0 1 0 .75l-3.9 3.9a.5 .5 0 0 1 -.75 0l-3.8 -3.9a.5 .5 0 0 1 0 -.75l.4-.4a.5.5 0 0 1 .75 0l2.3 2.4v-5.7c0-.25.25-.5.5-.5h.6c.25 0 .5.25.5.5v5.8l2.3 -2.4a.5.5 0 0 1 .75 0z");
5112
+ const arrowUpPath = new window.Path2D("M8.7 5.5a.5.5 0 0 0 0-.75l-3.8-4a.5.5 0 0 0-.75 0l-3.8 4a.5.5 0 0 0 0 .75l.4.4a.5.5 0 0 0 .75 0l2.3-2.4v5.8c0 .25.25.5.5.5h.6c.25 0 .5-.25.5-.5v-5.8l2.2 2.4a.5.5 0 0 0 .75 0z");
5110
5113
  let ScorecardChart$1 = class ScorecardChart extends AbstractChart {
5111
5114
  keyValue;
5112
5115
  baseline;
@@ -5227,10 +5230,24 @@ function drawScoreChart(structure, canvas) {
5227
5230
  ctx.fillStyle = structure.baseline.style.color;
5228
5231
  drawDecoratedText(ctx, structure.baseline.text, structure.baseline.position, structure.baseline.style.underline, structure.baseline.style.strikethrough);
5229
5232
  }
5230
- if (structure.baselineArrow) {
5231
- ctx.font = structure.baselineArrow.style.font;
5233
+ if (structure.baselineArrow && structure.baselineArrow.style.size > 0) {
5234
+ ctx.save();
5232
5235
  ctx.fillStyle = structure.baselineArrow.style.color;
5233
- ctx.fillText(structure.baselineArrow.text, structure.baselineArrow.position.x, structure.baselineArrow.position.y);
5236
+ ctx.translate(structure.baselineArrow.position.x, structure.baselineArrow.position.y);
5237
+ // This ratio is computed according to the original svg size and the final size we want
5238
+ const ratio = structure.baselineArrow.style.size / 10;
5239
+ ctx.scale(ratio, ratio);
5240
+ switch (structure.baselineArrow.direction) {
5241
+ case "down": {
5242
+ ctx.fill(arrowDownPath);
5243
+ break;
5244
+ }
5245
+ case "up": {
5246
+ ctx.fill(arrowUpPath);
5247
+ break;
5248
+ }
5249
+ }
5250
+ ctx.restore();
5234
5251
  }
5235
5252
  if (structure.baselineDescr) {
5236
5253
  ctx.font = structure.baselineDescr.style.font;
@@ -5353,25 +5370,29 @@ class ScorecardChartConfigBuilder {
5353
5370
  },
5354
5371
  };
5355
5372
  }
5356
- const { height: baselineArrowHeight, width: baselineArrowWidth } = this.getTextDimensions(this.baselineArrow, style.baselineArrow.font);
5373
+ const baselineArrowSize = style.baselineArrow?.size ?? 0;
5357
5374
  const { height: baselineHeight, width: baselineWidth } = this.getTextDimensions(this.baseline, style.baselineValue.font);
5358
5375
  const { width: baselineDescrWidth } = this.getTextDimensions(this.baselineDescr, style.baselineDescr.font);
5359
5376
  structure.baseline = {
5360
5377
  text: this.baseline,
5361
5378
  style: style.baselineValue,
5362
5379
  position: {
5363
- x: (this.width - baselineWidth - baselineDescrWidth + baselineArrowWidth) / 2,
5364
- y: this.height - 2 * this.chartPadding,
5365
- },
5366
- };
5367
- structure.baselineArrow = {
5368
- text: this.baselineArrow,
5369
- style: style.baselineArrow,
5370
- position: {
5371
- x: structure.baseline.position.x - baselineArrowWidth,
5372
- y: structure.baseline.position.y - (baselineHeight - baselineArrowHeight) / 2,
5380
+ x: (this.width - baselineWidth - baselineDescrWidth + baselineArrowSize) / 2,
5381
+ y: this.keyValue
5382
+ ? this.height - 2 * this.chartPadding
5383
+ : this.height - (this.height - titleHeight - baselineHeight) / 2 - this.chartPadding,
5373
5384
  },
5374
5385
  };
5386
+ if (style.baselineArrow) {
5387
+ structure.baselineArrow = {
5388
+ direction: this.baselineArrow,
5389
+ style: style.baselineArrow,
5390
+ position: {
5391
+ x: structure.baseline.position.x - baselineArrowSize,
5392
+ y: structure.baseline.position.y - (baselineHeight + baselineArrowSize) / 2,
5393
+ },
5394
+ };
5395
+ }
5375
5396
  if (this.baselineDescr) {
5376
5397
  structure.baselineDescr = {
5377
5398
  text: this.baselineDescr,
@@ -5408,13 +5429,7 @@ class ScorecardChartConfigBuilder {
5408
5429
  return formatBaselineDescr(this.runtime.baselineDescr, this.baseline);
5409
5430
  }
5410
5431
  get baselineArrow() {
5411
- switch (this.runtime.baselineArrow) {
5412
- case "up":
5413
- return "\u{1F871}";
5414
- case "down":
5415
- return "\u{1F873}";
5416
- }
5417
- return "";
5432
+ return this.runtime.baselineArrow;
5418
5433
  }
5419
5434
  get backgroundColor() {
5420
5435
  return this.runtime.background;
@@ -5466,10 +5481,12 @@ class ScorecardChartConfigBuilder {
5466
5481
  font: getDefaultContextFont(baselineFontSize * BASELINE_DESCR_FONT_RATIO),
5467
5482
  color: this.secondaryFontColor,
5468
5483
  },
5469
- baselineArrow: {
5470
- font: getDefaultContextFont(0.8 * baselineFontSize),
5471
- color: this.runtime.baselineColor || this.secondaryFontColor,
5472
- },
5484
+ baselineArrow: this.baselineArrow === "neutral"
5485
+ ? undefined
5486
+ : {
5487
+ size: this.keyValue ? 0.8 * baselineFontSize : 0,
5488
+ color: this.runtime.baselineColor || this.secondaryFontColor,
5489
+ },
5473
5490
  };
5474
5491
  }
5475
5492
  /** Get the height of the chart minus all the vertical paddings */
@@ -5506,7 +5523,7 @@ class BaselineElement extends ScorecardScalableElement {
5506
5523
  }
5507
5524
  const baselineStr = chart.baseline;
5508
5525
  // Put mock text to simulate the width of the up/down arrow
5509
- const largeText = chart.baselineArrow !== "" ? "A " + baselineStr : baselineStr;
5526
+ const largeText = chart.baselineArrow !== "neutral" ? "A " + baselineStr : baselineStr;
5510
5527
  let textWidth = this.measureTextWidth(ctx, largeText, fontSize);
5511
5528
  // Baseline descr font size should be smaller than baseline font size
5512
5529
  textWidth += this.measureTextWidth(ctx, chart.baselineDescr, fontSize * BASELINE_DESCR_FONT_RATIO);
@@ -5543,7 +5560,7 @@ class ScorecardChart extends owl.Component {
5543
5560
  static template = "o-spreadsheet-ScorecardChart";
5544
5561
  canvas = owl.useRef("chartContainer");
5545
5562
  get runtime() {
5546
- return this.env.model.getters.getChartRuntime(this.props.figureId);
5563
+ return this.env.model.getters.getChartRuntime(this.props.figure.id);
5547
5564
  }
5548
5565
  setup() {
5549
5566
  owl.useEffect(this.createChart.bind(this), () => {
@@ -5559,7 +5576,7 @@ class ScorecardChart extends owl.Component {
5559
5576
  }
5560
5577
  }
5561
5578
  ScorecardChart.props = {
5562
- figureId: String,
5579
+ figure: Object,
5563
5580
  };
5564
5581
 
5565
5582
  /**
@@ -7568,6 +7585,18 @@ function multiplyMatrices(matrix1, matrix2) {
7568
7585
  }
7569
7586
  return result;
7570
7587
  }
7588
+ /**
7589
+ * Return the input if it's a scalar or the first element of the input if it's a matrix.
7590
+ */
7591
+ function toScalar(matrix) {
7592
+ if (!isMatrix(matrix)) {
7593
+ return matrix;
7594
+ }
7595
+ if (matrix.length !== 1 || matrix[0].length !== 1) {
7596
+ throw new Error("toScalar: matrix should be a scalar or a 1x1 matrix");
7597
+ }
7598
+ return matrix[0][0];
7599
+ }
7571
7600
 
7572
7601
  // -----------------------------------------------------------------------------
7573
7602
  // ARRAY_CONSTRAIN
@@ -10281,6 +10310,42 @@ const QUARTILE_INC = {
10281
10310
  },
10282
10311
  isExported: true,
10283
10312
  };
10313
+ // RANK
10314
+ // -----------------------------------------------------------------------------
10315
+ const RANK = {
10316
+ description: _t("Returns the rank of a specified value in a dataset."),
10317
+ args: [
10318
+ arg("value (number)", _t("The value whose rank will be determined.")),
10319
+ arg("data (range)", _t("The range containing the dataset to consider.")),
10320
+ arg("is_ascending (boolean, default=FALSE)", _t("Whether to consider the values in data in descending or ascending order.")),
10321
+ ],
10322
+ returns: ["ANY"],
10323
+ compute: function (value, data, isAscending = false) {
10324
+ const _isAscending = toBoolean(isAscending);
10325
+ const _value = toNumber(value, this.locale);
10326
+ let rank = 1;
10327
+ let found = false;
10328
+ for (const row of data) {
10329
+ for (const cell of row) {
10330
+ if (typeof cell !== "number") {
10331
+ continue;
10332
+ }
10333
+ const _cell = toNumber(cell, this.locale);
10334
+ if (_cell === _value) {
10335
+ found = true;
10336
+ }
10337
+ else if (_cell > _value !== _isAscending) {
10338
+ rank++;
10339
+ }
10340
+ }
10341
+ }
10342
+ if (!found) {
10343
+ throw new NotAvailableError(_t("Value not found in the given data."));
10344
+ }
10345
+ return rank;
10346
+ },
10347
+ isExported: true,
10348
+ };
10284
10349
  // -----------------------------------------------------------------------------
10285
10350
  // RSQ
10286
10351
  // -----------------------------------------------------------------------------
@@ -10625,6 +10690,7 @@ var statistical = /*#__PURE__*/Object.freeze({
10625
10690
  QUARTILE: QUARTILE,
10626
10691
  QUARTILE_EXC: QUARTILE_EXC,
10627
10692
  QUARTILE_INC: QUARTILE_INC,
10693
+ RANK: RANK,
10628
10694
  RSQ: RSQ,
10629
10695
  SLOPE: SLOPE,
10630
10696
  SMALL: SMALL,
@@ -11771,6 +11837,172 @@ var engineering = /*#__PURE__*/Object.freeze({
11771
11837
  DELTA: DELTA
11772
11838
  });
11773
11839
 
11840
+ const SORT_TYPES = [
11841
+ CellValueType.number,
11842
+ CellValueType.error,
11843
+ CellValueType.text,
11844
+ CellValueType.boolean,
11845
+ ];
11846
+ function cellsSortingCriterion(sortingOrder) {
11847
+ const inverse = sortingOrder === "ascending" ? 1 : -1;
11848
+ return (left, right) => {
11849
+ if (left.type === CellValueType.empty) {
11850
+ return right.type === CellValueType.empty ? 0 : 1;
11851
+ }
11852
+ else if (right.type === CellValueType.empty) {
11853
+ return -1;
11854
+ }
11855
+ let typeOrder = SORT_TYPES.indexOf(left.type) - SORT_TYPES.indexOf(right.type);
11856
+ if (typeOrder === 0) {
11857
+ if (left.type === CellValueType.text || left.type === CellValueType.error) {
11858
+ typeOrder = left.value.localeCompare(right.value);
11859
+ }
11860
+ else {
11861
+ typeOrder = left.value - right.value;
11862
+ }
11863
+ }
11864
+ return inverse * typeOrder;
11865
+ };
11866
+ }
11867
+ function sortCells(cells, sortDirection, emptyCellAsZero) {
11868
+ const cellsWithIndex = cells.map((cell, index) => ({
11869
+ index,
11870
+ type: cell.type,
11871
+ value: cell.value,
11872
+ }));
11873
+ const cellsToSort = emptyCellAsZero
11874
+ ? cellsWithIndex.map((cell) => cell.type === CellValueType.empty ? { ...cell, type: CellValueType.number, value: 0 } : cell)
11875
+ : cellsWithIndex;
11876
+ return cellsToSort.sort(cellsSortingCriterion(sortDirection));
11877
+ }
11878
+ function interactiveSortSelection(env, sheetId, anchor, zone, sortDirection) {
11879
+ let result = DispatchResult.Success;
11880
+ //several columns => bypass the contiguity check
11881
+ let multiColumns = zone.right > zone.left;
11882
+ if (env.model.getters.doesIntersectMerge(sheetId, zone)) {
11883
+ multiColumns = false;
11884
+ let table;
11885
+ for (let row = zone.top; row <= zone.bottom; row++) {
11886
+ table = [];
11887
+ for (let col = zone.left; col <= zone.right; col++) {
11888
+ let merge = env.model.getters.getMerge({ sheetId, col, row });
11889
+ if (merge && !table.includes(merge.id.toString())) {
11890
+ table.push(merge.id.toString());
11891
+ }
11892
+ }
11893
+ if (table.length >= 2) {
11894
+ multiColumns = true;
11895
+ break;
11896
+ }
11897
+ }
11898
+ }
11899
+ const { col, row } = anchor;
11900
+ if (multiColumns) {
11901
+ result = env.model.dispatch("SORT_CELLS", { sheetId, col, row, zone, sortDirection });
11902
+ }
11903
+ else {
11904
+ // check contiguity
11905
+ const contiguousZone = env.model.getters.getContiguousZone(sheetId, zone);
11906
+ if (isEqual(contiguousZone, zone)) {
11907
+ // merge as it is
11908
+ result = env.model.dispatch("SORT_CELLS", {
11909
+ sheetId,
11910
+ col,
11911
+ row,
11912
+ zone,
11913
+ sortDirection,
11914
+ });
11915
+ }
11916
+ else {
11917
+ env.askConfirmation(_t("We found data next to your selection. Since this data was not selected, it will not be sorted. Do you want to extend your selection?"), () => {
11918
+ zone = contiguousZone;
11919
+ result = env.model.dispatch("SORT_CELLS", {
11920
+ sheetId,
11921
+ col,
11922
+ row,
11923
+ zone,
11924
+ sortDirection,
11925
+ });
11926
+ }, () => {
11927
+ result = env.model.dispatch("SORT_CELLS", {
11928
+ sheetId,
11929
+ col,
11930
+ row,
11931
+ zone,
11932
+ sortDirection,
11933
+ });
11934
+ });
11935
+ }
11936
+ }
11937
+ if (result.isCancelledBecause("InvalidSortZone" /* CommandResult.InvalidSortZone */)) {
11938
+ const { col, row } = anchor;
11939
+ env.model.selection.selectZone({ cell: { col, row }, zone });
11940
+ env.raiseError(_t("Cannot sort. To sort, select only cells or only merges that have the same size."));
11941
+ }
11942
+ }
11943
+
11944
+ function sortMatrix(matrix, locale, ...criteria) {
11945
+ for (const [i, value] of criteria.entries()) {
11946
+ assert(() => value !== undefined, _t(`Value for parameter %d is missing, while the function [[FUNCTION_NAME]] expect a number or a range.`, i + 1));
11947
+ }
11948
+ const sortingOrders = [];
11949
+ const sortColumns = [];
11950
+ const nRows = matrix.length;
11951
+ for (let i = 0; i < criteria.length; i += 2) {
11952
+ sortingOrders.push(toBoolean(toScalar(criteria[i + 1])?.value) ? "ascending" : "descending");
11953
+ const sortColumn = criteria[i];
11954
+ if (isMatrix(sortColumn) && (sortColumn.length > 1 || sortColumn[0].length > 1)) {
11955
+ assert(() => sortColumn.length === 1 && sortColumn[0].length === nRows, _t(`Wrong size for %s. Expected a range of size 1x%s. Got %sx%s.`, `sort_column{i + 1}`, nRows, sortColumn.length, sortColumn[0].length));
11956
+ sortColumns.push(sortColumn.flat().map((c) => c.value));
11957
+ }
11958
+ else {
11959
+ const colIndex = toNumber(toScalar(sortColumn)?.value, locale);
11960
+ if (colIndex < 1 || colIndex > matrix[0].length) {
11961
+ return matrix;
11962
+ }
11963
+ sortColumns.push(matrix.map((row) => row[colIndex - 1].value));
11964
+ }
11965
+ }
11966
+ if (sortColumns.length === 0) {
11967
+ for (let i = 0; i < matrix[0].length; i++) {
11968
+ sortColumns.push(matrix.map((row) => row[i].value));
11969
+ sortingOrders.push("ascending");
11970
+ }
11971
+ }
11972
+ const sortingCriteria = {
11973
+ descending: cellsSortingCriterion("descending"),
11974
+ ascending: cellsSortingCriterion("ascending"),
11975
+ };
11976
+ const indexes = range(0, matrix.length);
11977
+ indexes.sort((a, b) => {
11978
+ for (const [i, sortColumn] of sortColumns.entries()) {
11979
+ const left = sortColumn[a];
11980
+ const right = sortColumn[b];
11981
+ const leftCell = {
11982
+ value: left,
11983
+ type: left === null
11984
+ ? CellValueType.empty
11985
+ : typeof left === "string"
11986
+ ? CellValueType.text
11987
+ : typeof left,
11988
+ };
11989
+ const rightCell = {
11990
+ value: right,
11991
+ type: right === null
11992
+ ? CellValueType.empty
11993
+ : typeof right === "string"
11994
+ ? CellValueType.text
11995
+ : typeof right,
11996
+ };
11997
+ const result = sortingCriteria[sortingOrders[i]](leftCell, rightCell);
11998
+ if (result !== 0) {
11999
+ return result;
12000
+ }
12001
+ }
12002
+ return 0;
12003
+ });
12004
+ return indexes.map((i) => matrix[i]);
12005
+ }
11774
12006
  // -----------------------------------------------------------------------------
11775
12007
  // FILTER
11776
12008
  // -----------------------------------------------------------------------------
@@ -11807,6 +12039,93 @@ const FILTER = {
11807
12039
  isExported: true,
11808
12040
  };
11809
12041
  // -----------------------------------------------------------------------------
12042
+ // SORT
12043
+ // -----------------------------------------------------------------------------
12044
+ const SORT = {
12045
+ description: _t("Sorts the rows of a given array or range by the values in one or more columns."),
12046
+ args: [
12047
+ arg("range (range)", _t("The data to be sorted.")),
12048
+ arg("sort_column (any, range<number>, repeating)", _t("The index of the column in range or a range outside of range containing the values by which to sort.")),
12049
+ arg("is_ascending (boolean, repeating)", _t("TRUE or FALSE indicating whether to sort sort_column in ascending order. FALSE sorts in descending order.")),
12050
+ ],
12051
+ returns: ["RANGE"],
12052
+ computeValueAndFormat: function (range, ...sortingCriteria) {
12053
+ const _range = transposeMatrix(range);
12054
+ return transposeMatrix(sortMatrix(_range, this.locale, ...sortingCriteria));
12055
+ },
12056
+ isExported: true,
12057
+ };
12058
+ // -----------------------------------------------------------------------------
12059
+ // SORTN
12060
+ // -----------------------------------------------------------------------------
12061
+ const SORTN = {
12062
+ description: _t("Returns the first n items in a data set after performing a sort."),
12063
+ args: [
12064
+ arg("range (range)", _t("The data to be sorted.")),
12065
+ arg("n (number, default=1)", _t("The number of items to return.")),
12066
+ arg("display_ties_mode (number, default=0)", _t("A number representing the way to display ties.")),
12067
+ arg("sort_column (number, range<number>, repeating)", _t("The index of the column in range or a range outside of range containing the values by which to sort.")),
12068
+ arg("is_ascending (boolean, repeating)", _t("TRUE or FALSE indicating whether to sort sort_column in ascending order. FALSE sorts in descending order.")),
12069
+ ],
12070
+ returns: ["RANGE"],
12071
+ computeValueAndFormat: function (range, n, displayTiesMode, ...sortingCriteria) {
12072
+ const _n = toNumber(n?.value ?? 1, this.locale);
12073
+ assert(() => _n >= 0, _t(`Wrong value of 'n'. Expected a positive number. Got %s.`, _n));
12074
+ const _displayTiesMode = toNumber(displayTiesMode?.value ?? 0, this.locale);
12075
+ assert(() => _displayTiesMode >= 0 && _displayTiesMode <= 3, _t(`Wrong value of 'display_ties_mode'. Expected a positive number between 0 and 3. Got %s.`, _displayTiesMode));
12076
+ const sortedData = sortMatrix(transposeMatrix(range), this.locale, ...sortingCriteria);
12077
+ const sameRows = (i, j) => JSON.stringify(sortedData[i].map((c) => c.value)) ===
12078
+ JSON.stringify(sortedData[j].map((c) => c.value));
12079
+ /*
12080
+ * displayTiesMode determine how ties (equal values) are dealt with:
12081
+ * 0 - ignore ties and show first n rows only
12082
+ * 1 - show first n rows plus any additional ties with nth row
12083
+ * 2 - show n rows but remove duplicates
12084
+ * 3 - show first n unique rows and all duplicates of these rows
12085
+ */
12086
+ switch (_displayTiesMode) {
12087
+ case 0:
12088
+ return transposeMatrix(sortedData.slice(0, _n));
12089
+ case 1:
12090
+ for (let i = _n; i < sortedData.length; i++) {
12091
+ if (!sameRows(i, _n - 1)) {
12092
+ return transposeMatrix(sortedData.slice(0, i));
12093
+ }
12094
+ }
12095
+ return transposeMatrix(sortedData);
12096
+ case 2: {
12097
+ const uniques = [sortedData[0]];
12098
+ for (let i = 1; i < sortedData.length; i++) {
12099
+ for (let j = 0; j < i; j++) {
12100
+ if (sameRows(i, j)) {
12101
+ break;
12102
+ }
12103
+ if (j === i - 1) {
12104
+ uniques.push(sortedData[i]);
12105
+ }
12106
+ }
12107
+ }
12108
+ return transposeMatrix(uniques.slice(0, _n));
12109
+ }
12110
+ case 3: {
12111
+ const uniques = [sortedData[0]];
12112
+ let counter = 1;
12113
+ for (let i = 1; i < sortedData.length; i++) {
12114
+ if (!sameRows(i, i - 1)) {
12115
+ counter++;
12116
+ }
12117
+ if (counter > _n) {
12118
+ break;
12119
+ }
12120
+ uniques.push(sortedData[i]);
12121
+ }
12122
+ return transposeMatrix(uniques);
12123
+ }
12124
+ }
12125
+ },
12126
+ isExported: true,
12127
+ };
12128
+ // -----------------------------------------------------------------------------
11810
12129
  // UNIQUE
11811
12130
  // -----------------------------------------------------------------------------
11812
12131
  const UNIQUE = {
@@ -11854,6 +12173,8 @@ const UNIQUE = {
11854
12173
  var filter = /*#__PURE__*/Object.freeze({
11855
12174
  __proto__: null,
11856
12175
  FILTER: FILTER,
12176
+ SORT: SORT,
12177
+ SORTN: SORTN,
11857
12178
  UNIQUE: UNIQUE
11858
12179
  });
11859
12180
 
@@ -17089,7 +17410,7 @@ const PasteInteractiveContent = {
17089
17410
  wrongPasteSelection: _t("This operation is not allowed with multiple selections."),
17090
17411
  willRemoveExistingMerge: _t("This operation is not possible due to a merge. Please remove the merges first than try again."),
17091
17412
  wrongFigurePasteOption: _t("Cannot do a special paste of a figure."),
17092
- frozenPaneOverlap: _t("Cannot paste merged cells over a frozen pane."),
17413
+ frozenPaneOverlap: _t("This operation is not allowed due to an overlapping frozen pane."),
17093
17414
  };
17094
17415
  function handlePasteResult(env, result) {
17095
17416
  if (!result.isSuccessful) {
@@ -18629,11 +18950,11 @@ class ChartFigure extends owl.Component {
18629
18950
  static template = "o-spreadsheet-ChartFigure";
18630
18951
  static components = {};
18631
18952
  onDoubleClick() {
18632
- this.env.model.dispatch("SELECT_FIGURE", { id: this.props.figureId });
18953
+ this.env.model.dispatch("SELECT_FIGURE", { id: this.props.figure.id });
18633
18954
  this.env.openSidePanel("ChartPanel");
18634
18955
  }
18635
18956
  get chartType() {
18636
- return this.env.model.getters.getChartType(this.props.figureId);
18957
+ return this.env.model.getters.getChartType(this.props.figure.id);
18637
18958
  }
18638
18959
  get chartComponent() {
18639
18960
  const type = this.chartType;
@@ -18645,7 +18966,7 @@ class ChartFigure extends owl.Component {
18645
18966
  }
18646
18967
  }
18647
18968
  ChartFigure.props = {
18648
- figureId: String,
18969
+ figure: Object,
18649
18970
  onFigureDeleted: Function,
18650
18971
  };
18651
18972
 
@@ -18656,14 +18977,14 @@ class ImageFigure extends owl.Component {
18656
18977
  // Getters
18657
18978
  // ---------------------------------------------------------------------------
18658
18979
  get figureId() {
18659
- return this.props.figureId;
18980
+ return this.props.figure.id;
18660
18981
  }
18661
18982
  get getImagePath() {
18662
18983
  return this.env.model.getters.getImagePath(this.figureId);
18663
18984
  }
18664
18985
  }
18665
18986
  ImageFigure.props = {
18666
- figureId: String,
18987
+ figure: Object,
18667
18988
  onFigureDeleted: Function,
18668
18989
  };
18669
18990
 
@@ -20268,105 +20589,6 @@ cellMenuRegistry
20268
20589
  separator: true,
20269
20590
  });
20270
20591
 
20271
- const SORT_TYPES = [
20272
- CellValueType.number,
20273
- CellValueType.error,
20274
- CellValueType.text,
20275
- CellValueType.boolean,
20276
- ];
20277
- function sortCells(cells, sortDirection, emptyCellAsZero) {
20278
- const cellsWithIndex = cells.map((cell, index) => ({
20279
- index,
20280
- type: cell.type,
20281
- value: cell.value,
20282
- }));
20283
- let emptyCells = cellsWithIndex.filter((x) => x.type === CellValueType.empty);
20284
- let nonEmptyCells = cellsWithIndex.filter((x) => x.type !== CellValueType.empty);
20285
- if (emptyCellAsZero) {
20286
- nonEmptyCells.push(...emptyCells.map((emptyCell) => ({ ...emptyCell, type: CellValueType.number, value: 0 })));
20287
- emptyCells = [];
20288
- }
20289
- const inverse = sortDirection === "descending" ? -1 : 1;
20290
- return nonEmptyCells
20291
- .sort((left, right) => {
20292
- let typeOrder = SORT_TYPES.indexOf(left.type) - SORT_TYPES.indexOf(right.type);
20293
- if (typeOrder === 0) {
20294
- if (left.type === CellValueType.text || left.type === CellValueType.error) {
20295
- typeOrder = left.value.localeCompare(right.value);
20296
- }
20297
- else
20298
- typeOrder = left.value - right.value;
20299
- }
20300
- return inverse * typeOrder;
20301
- })
20302
- .concat(emptyCells);
20303
- }
20304
- function interactiveSortSelection(env, sheetId, anchor, zone, sortDirection) {
20305
- let result = DispatchResult.Success;
20306
- //several columns => bypass the contiguity check
20307
- let multiColumns = zone.right > zone.left;
20308
- if (env.model.getters.doesIntersectMerge(sheetId, zone)) {
20309
- multiColumns = false;
20310
- let table;
20311
- for (let row = zone.top; row <= zone.bottom; row++) {
20312
- table = [];
20313
- for (let col = zone.left; col <= zone.right; col++) {
20314
- let merge = env.model.getters.getMerge({ sheetId, col, row });
20315
- if (merge && !table.includes(merge.id.toString())) {
20316
- table.push(merge.id.toString());
20317
- }
20318
- }
20319
- if (table.length >= 2) {
20320
- multiColumns = true;
20321
- break;
20322
- }
20323
- }
20324
- }
20325
- const { col, row } = anchor;
20326
- if (multiColumns) {
20327
- result = env.model.dispatch("SORT_CELLS", { sheetId, col, row, zone, sortDirection });
20328
- }
20329
- else {
20330
- // check contiguity
20331
- const contiguousZone = env.model.getters.getContiguousZone(sheetId, zone);
20332
- if (isEqual(contiguousZone, zone)) {
20333
- // merge as it is
20334
- result = env.model.dispatch("SORT_CELLS", {
20335
- sheetId,
20336
- col,
20337
- row,
20338
- zone,
20339
- sortDirection,
20340
- });
20341
- }
20342
- else {
20343
- env.askConfirmation(_t("We found data next to your selection. Since this data was not selected, it will not be sorted. Do you want to extend your selection?"), () => {
20344
- zone = contiguousZone;
20345
- result = env.model.dispatch("SORT_CELLS", {
20346
- sheetId,
20347
- col,
20348
- row,
20349
- zone,
20350
- sortDirection,
20351
- });
20352
- }, () => {
20353
- result = env.model.dispatch("SORT_CELLS", {
20354
- sheetId,
20355
- col,
20356
- row,
20357
- zone,
20358
- sortDirection,
20359
- });
20360
- });
20361
- }
20362
- }
20363
- if (result.isCancelledBecause("InvalidSortZone" /* CommandResult.InvalidSortZone */)) {
20364
- const { col, row } = anchor;
20365
- env.model.selection.selectZone({ cell: { col, row }, zone });
20366
- env.raiseError(_t("Cannot sort. To sort, select only cells or only merges that have the same size."));
20367
- }
20368
- }
20369
-
20370
20592
  const AddFilterInteractiveContent = {
20371
20593
  filterOverlap: _t("You cannot create overlapping filters."),
20372
20594
  nonContinuousTargets: _t("A filter can only be created on a continuous selection."),
@@ -26549,7 +26771,6 @@ css /* scss */ `
26549
26771
  border-top-right-radius: 4px;
26550
26772
  font-size: ${DEFAULT_FONT_SIZE};
26551
26773
  color: white;
26552
- opacity: 0;
26553
26774
  pointer-events: none;
26554
26775
  }
26555
26776
  `;
@@ -26569,7 +26790,6 @@ class ClientTag extends owl.Component {
26569
26790
  left: `${x - 1}px`,
26570
26791
  border: `1px solid ${color}`,
26571
26792
  "background-color": color,
26572
- opacity: this.props.active ? "opacity:1 !important" : undefined,
26573
26793
  });
26574
26794
  }
26575
26795
  }
@@ -30115,6 +30335,8 @@ class Grid extends owl.Component {
30115
30335
  const position = this.env.model.getters.getActivePosition();
30116
30336
  this.env.model.selection.selectZone({ cell: position, zone: newZone });
30117
30337
  },
30338
+ "CTRL+D": async () => this.env.model.dispatch("COPY_PASTE_CELLS_ABOVE"),
30339
+ "CTRL+R": async () => this.env.model.dispatch("COPY_PASTE_CELLS_ON_LEFT"),
30118
30340
  "CTRL+SHIFT+E": () => this.setHorizontalAlign("center"),
30119
30341
  "CTRL+SHIFT+L": () => this.setHorizontalAlign("left"),
30120
30342
  "CTRL+SHIFT+R": () => this.setHorizontalAlign("right"),
@@ -40744,7 +40966,7 @@ class EvaluationConditionalFormatPlugin extends UIPlugin {
40744
40966
  return percentile(rangeValues, Number(threshold.value) / 100, true);
40745
40967
  case "formula":
40746
40968
  const value = threshold.value && this.getters.evaluateFormula(sheetId, threshold.value);
40747
- return !(value instanceof Promise) ? value : null;
40969
+ return typeof value === "number" ? value : null;
40748
40970
  default:
40749
40971
  return null;
40750
40972
  }
@@ -40870,10 +41092,13 @@ class EvaluationConditionalFormatPlugin extends UIPlugin {
40870
41092
  }
40871
41093
  const [value0, value1] = rule.values.map((val) => {
40872
41094
  if (val.startsWith("=")) {
40873
- return this.getters.evaluateFormula(target.sheetId, val);
41095
+ return this.getters.evaluateFormula(target.sheetId, val) ?? "";
40874
41096
  }
40875
41097
  return parseLiteral(val, DEFAULT_LOCALE);
40876
41098
  });
41099
+ if (isMatrix(value0) || isMatrix(value1)) {
41100
+ return false;
41101
+ }
40877
41102
  switch (rule.operator) {
40878
41103
  case "IsEmpty":
40879
41104
  return cell.value.toString().trim() === "";
@@ -41062,7 +41287,7 @@ class EvaluationDataValidationPlugin extends UIPlugin {
41062
41287
  const formula = compile(value);
41063
41288
  const translatedFormula = this.getters.getTranslatedCellFormula(sheetId, offset.col, offset.row, formula, formula.dependencies.map((d) => this.getters.getRangeFromSheetXC(sheetId, d)));
41064
41289
  const evaluated = this.getters.evaluateFormula(sheetId, translatedFormula);
41065
- return evaluated ? evaluated.toString() : "";
41290
+ return evaluated && !isMatrix(evaluated) ? evaluated.toString() : "";
41066
41291
  }
41067
41292
  catch (e) {
41068
41293
  return e instanceof EvaluationError ? e.errorType : CellErrorType.GenericError;
@@ -46517,6 +46742,20 @@ class ClipboardPlugin extends UIPlugin {
46517
46742
  const state = new ClipboardOsState(cmd.text, this.getters, this.dispatch, this.selection);
46518
46743
  return state.isPasteAllowed(cmd.target, { pasteOption: cmd.pasteOption });
46519
46744
  }
46745
+ case "COPY_PASTE_CELLS_ABOVE": {
46746
+ const zones = this.getters.getSelectedZones();
46747
+ if (zones.length > 1 || (zones[0].top === 0 && zones[0].bottom === 0)) {
46748
+ return "InvalidCopyPasteSelection" /* CommandResult.InvalidCopyPasteSelection */;
46749
+ }
46750
+ break;
46751
+ }
46752
+ case "COPY_PASTE_CELLS_ON_LEFT": {
46753
+ const zones = this.getters.getSelectedZones();
46754
+ if (zones.length > 1 || (zones[0].left === 0 && zones[0].right === 0)) {
46755
+ return "InvalidCopyPasteSelection" /* CommandResult.InvalidCopyPasteSelection */;
46756
+ }
46757
+ break;
46758
+ }
46520
46759
  case "INSERT_CELL": {
46521
46760
  const { cut, paste } = this.getInsertCellsTargets(cmd.zone, cmd.shiftDimension);
46522
46761
  const state = this.getClipboardStateForCopyCells(cut, "CUT");
@@ -46556,6 +46795,40 @@ class ClipboardPlugin extends UIPlugin {
46556
46795
  this.status = "invisible";
46557
46796
  }
46558
46797
  break;
46798
+ case "COPY_PASTE_CELLS_ABOVE":
46799
+ {
46800
+ const zone = this.getters.getSelectedZone();
46801
+ const multipleRowsInSelection = zone.top !== zone.bottom;
46802
+ const copyTarget = {
46803
+ ...zone,
46804
+ bottom: multipleRowsInSelection ? zone.top : zone.top - 1,
46805
+ top: multipleRowsInSelection ? zone.top : zone.top - 1,
46806
+ };
46807
+ const state = this.getClipboardStateForCopyCells([copyTarget], "COPY");
46808
+ state.paste([zone], {
46809
+ pasteOption: undefined,
46810
+ shouldPasteCF: true,
46811
+ selectTarget: true,
46812
+ });
46813
+ }
46814
+ break;
46815
+ case "COPY_PASTE_CELLS_ON_LEFT":
46816
+ {
46817
+ const zone = this.getters.getSelectedZone();
46818
+ const multipleColsInSelection = zone.left !== zone.right;
46819
+ const copyTarget = {
46820
+ ...zone,
46821
+ right: multipleColsInSelection ? zone.left : zone.left - 1,
46822
+ left: multipleColsInSelection ? zone.left : zone.left - 1,
46823
+ };
46824
+ const state = this.getClipboardStateForCopyCells([copyTarget], "COPY");
46825
+ state.paste([zone], {
46826
+ pasteOption: undefined,
46827
+ shouldPasteCF: true,
46828
+ selectTarget: true,
46829
+ });
46830
+ }
46831
+ break;
46559
46832
  case "CLEAN_CLIPBOARD_HIGHLIGHT":
46560
46833
  this.status = "invisible";
46561
46834
  break;
@@ -47509,6 +47782,9 @@ class EditionPlugin extends UIPlugin {
47509
47782
  const cellValue = content.startsWith("=")
47510
47783
  ? this.getters.evaluateFormula(this.sheetId, content)
47511
47784
  : parseLiteral(content, this.getters.getLocale());
47785
+ if (isMatrix(cellValue)) {
47786
+ return "Success" /* CommandResult.Success */;
47787
+ }
47512
47788
  const validationResult = this.getters.getValidationResultForCellValue(cellValue, cellPosition);
47513
47789
  if (!validationResult.isValid && validationResult.rule.isBlocking) {
47514
47790
  return "BlockingValidationRule" /* CommandResult.BlockingValidationRule */;
@@ -48095,11 +48371,19 @@ class GridSelectionPlugin extends UIPlugin {
48095
48371
  }
48096
48372
  }
48097
48373
  getStatisticFnResults() {
48098
- // get deduplicated cells in zones
48099
- const cells = new Set(this.gridSelection.zones
48100
- .map((zone) => this.getters.getEvaluatedCellsInZone(this.getters.getActiveSheetId(), zone))
48101
- .flat()
48102
- .filter((cell) => cell.type !== CellValueType.empty));
48374
+ const sheetId = this.getters.getActiveSheetId();
48375
+ const cells = new Set();
48376
+ for (const zone of this.gridSelection.zones) {
48377
+ for (const { col, row } of positions(zone)) {
48378
+ if (this.getters.isRowHidden(sheetId, row) || this.getters.isColHidden(sheetId, col)) {
48379
+ continue; // Skip hidden cells
48380
+ }
48381
+ const evaluatedCell = this.getters.getEvaluatedCell({ sheetId, col, row });
48382
+ if (evaluatedCell.type !== CellValueType.empty) {
48383
+ cells.add(evaluatedCell);
48384
+ }
48385
+ }
48386
+ }
48103
48387
  let cellsTypes = new Set();
48104
48388
  let cellsValues = [];
48105
48389
  for (let cell of cells) {
@@ -50740,6 +51024,7 @@ const BORDER_POSITIONS = [
50740
51024
  css /* scss */ `
50741
51025
  .o-border-selector {
50742
51026
  padding: 4px;
51027
+ background-color: white;
50743
51028
  }
50744
51029
  .o-divider {
50745
51030
  border-right: 1px solid #e0e2e4;
@@ -55455,6 +55740,6 @@ exports.setTranslationMethod = setTranslationMethod;
55455
55740
  exports.tokenize = tokenize;
55456
55741
 
55457
55742
 
55458
- __info__.version = "16.5.0-alpha.11";
55459
- __info__.date = "2023-10-19T14:26:01.790Z";
55460
- __info__.hash = "4b42059";
55743
+ __info__.version = "16.5.0-alpha.13";
55744
+ __info__.date = "2023-10-25T10:04:45.428Z";
55745
+ __info__.hash = "18a08b1";