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