@odoo/o-spreadsheet 16.0.63 → 16.0.64

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.
@@ -3580,10 +3580,13 @@ autofillRulesRegistry
3580
3580
  })
3581
3581
  .add("increment_number", {
3582
3582
  condition: (cell) => cell.evaluated.type === CellValueType.number,
3583
- generateRule: (cell, cells) => {
3583
+ generateRule: (cell, cells, direction) => {
3584
3584
  const group = getGroup(cell, cells);
3585
3585
  let increment = 1;
3586
- if (group.length == 2) {
3586
+ if (group.length === 1 && ["up", "left"].includes(direction)) {
3587
+ increment = -1;
3588
+ }
3589
+ else if (group.length == 2) {
3587
3590
  increment = (group[1] - group[0]) * 2;
3588
3591
  }
3589
3592
  else if (group.length > 2) {
@@ -22183,6 +22186,9 @@ class ColResizer extends AbstractResizer {
22183
22186
  this.MAX_SIZE_MARGIN = 90;
22184
22187
  this.MIN_ELEMENT_SIZE = MIN_COL_WIDTH;
22185
22188
  }
22189
+ get sheetId() {
22190
+ return this.env.model.getters.getActiveSheetId();
22191
+ }
22186
22192
  _getEvOffset(ev) {
22187
22193
  return ev.offsetX;
22188
22194
  }
@@ -22205,10 +22211,10 @@ class ColResizer extends AbstractResizer {
22205
22211
  return this.env.model.getters.getEdgeScrollCol(position, position, position);
22206
22212
  }
22207
22213
  _getDimensionsInViewport(index) {
22208
- return this.env.model.getters.getColDimensionsInViewport(this.env.model.getters.getActiveSheetId(), index);
22214
+ return this.env.model.getters.getColDimensionsInViewport(this.sheetId, index);
22209
22215
  }
22210
22216
  _getElementSize(index) {
22211
- return this.env.model.getters.getColSize(this.env.model.getters.getActiveSheetId(), index);
22217
+ return this.env.model.getters.getColSize(this.sheetId, index);
22212
22218
  }
22213
22219
  _getMaxSize() {
22214
22220
  return this.colResizerRef.el.clientWidth;
@@ -22219,7 +22225,7 @@ class ColResizer extends AbstractResizer {
22219
22225
  const cols = this.env.model.getters.getActiveCols();
22220
22226
  this.env.model.dispatch("RESIZE_COLUMNS_ROWS", {
22221
22227
  dimension: "COL",
22222
- sheetId: this.env.model.getters.getActiveSheetId(),
22228
+ sheetId: this.sheetId,
22223
22229
  elements: cols.has(index) ? [...cols] : [index],
22224
22230
  size,
22225
22231
  });
@@ -22232,7 +22238,7 @@ class ColResizer extends AbstractResizer {
22232
22238
  elements.push(colIndex);
22233
22239
  }
22234
22240
  const result = this.env.model.dispatch("MOVE_COLUMNS_ROWS", {
22235
- sheetId: this.env.model.getters.getActiveSheetId(),
22241
+ sheetId: this.sheetId,
22236
22242
  dimension: "COL",
22237
22243
  base: this.state.base,
22238
22244
  elements,
@@ -22251,7 +22257,7 @@ class ColResizer extends AbstractResizer {
22251
22257
  _fitElementSize(index) {
22252
22258
  const cols = this.env.model.getters.getActiveCols();
22253
22259
  this.env.model.dispatch("AUTORESIZE_COLUMNS", {
22254
- sheetId: this.env.model.getters.getActiveSheetId(),
22260
+ sheetId: this.sheetId,
22255
22261
  cols: cols.has(index) ? [...cols] : [index],
22256
22262
  });
22257
22263
  }
@@ -22262,7 +22268,7 @@ class ColResizer extends AbstractResizer {
22262
22268
  return this.env.model.getters.getActiveCols();
22263
22269
  }
22264
22270
  _getPreviousVisibleElement(index) {
22265
- const sheetId = this.env.model.getters.getActiveSheetId();
22271
+ const sheetId = this.sheetId;
22266
22272
  let row;
22267
22273
  for (row = index - 1; row >= 0; row--) {
22268
22274
  if (!this.env.model.getters.isColHidden(sheetId, row)) {
@@ -22273,7 +22279,7 @@ class ColResizer extends AbstractResizer {
22273
22279
  }
22274
22280
  unhide(hiddenElements) {
22275
22281
  this.env.model.dispatch("UNHIDE_COLUMNS_ROWS", {
22276
- sheetId: this.env.model.getters.getActiveSheetId(),
22282
+ sheetId: this.sheetId,
22277
22283
  elements: hiddenElements,
22278
22284
  dimension: "COL",
22279
22285
  });
@@ -22290,7 +22296,7 @@ css /* scss */ `
22290
22296
  left: 0;
22291
22297
  right: 0;
22292
22298
  width: ${HEADER_WIDTH}px;
22293
- height: 100%;
22299
+ height: calc(100% - ${HEADER_HEIGHT + SCROLLBAR_WIDTH}px);
22294
22300
  &.o-dragging {
22295
22301
  cursor: grabbing;
22296
22302
  }
@@ -22353,6 +22359,9 @@ class RowResizer extends AbstractResizer {
22353
22359
  this.MAX_SIZE_MARGIN = 60;
22354
22360
  this.MIN_ELEMENT_SIZE = MIN_ROW_HEIGHT;
22355
22361
  }
22362
+ get sheetId() {
22363
+ return this.env.model.getters.getActiveSheetId();
22364
+ }
22356
22365
  _getEvOffset(ev) {
22357
22366
  return ev.offsetY;
22358
22367
  }
@@ -22375,10 +22384,10 @@ class RowResizer extends AbstractResizer {
22375
22384
  return this.env.model.getters.getEdgeScrollRow(position, position, position);
22376
22385
  }
22377
22386
  _getDimensionsInViewport(index) {
22378
- return this.env.model.getters.getRowDimensionsInViewport(this.env.model.getters.getActiveSheetId(), index);
22387
+ return this.env.model.getters.getRowDimensionsInViewport(this.sheetId, index);
22379
22388
  }
22380
22389
  _getElementSize(index) {
22381
- return this.env.model.getters.getRowSize(this.env.model.getters.getActiveSheetId(), index);
22390
+ return this.env.model.getters.getRowSize(this.sheetId, index);
22382
22391
  }
22383
22392
  _getMaxSize() {
22384
22393
  return this.rowResizerRef.el.clientHeight;
@@ -22389,7 +22398,7 @@ class RowResizer extends AbstractResizer {
22389
22398
  const rows = this.env.model.getters.getActiveRows();
22390
22399
  this.env.model.dispatch("RESIZE_COLUMNS_ROWS", {
22391
22400
  dimension: "ROW",
22392
- sheetId: this.env.model.getters.getActiveSheetId(),
22401
+ sheetId: this.sheetId,
22393
22402
  elements: rows.has(index) ? [...rows] : [index],
22394
22403
  size,
22395
22404
  });
@@ -22402,7 +22411,7 @@ class RowResizer extends AbstractResizer {
22402
22411
  elements.push(rowIndex);
22403
22412
  }
22404
22413
  const result = this.env.model.dispatch("MOVE_COLUMNS_ROWS", {
22405
- sheetId: this.env.model.getters.getActiveSheetId(),
22414
+ sheetId: this.sheetId,
22406
22415
  dimension: "ROW",
22407
22416
  base: this.state.base,
22408
22417
  elements,
@@ -22421,7 +22430,7 @@ class RowResizer extends AbstractResizer {
22421
22430
  _fitElementSize(index) {
22422
22431
  const rows = this.env.model.getters.getActiveRows();
22423
22432
  this.env.model.dispatch("AUTORESIZE_ROWS", {
22424
- sheetId: this.env.model.getters.getActiveSheetId(),
22433
+ sheetId: this.sheetId,
22425
22434
  rows: rows.has(index) ? [...rows] : [index],
22426
22435
  });
22427
22436
  }
@@ -22432,7 +22441,7 @@ class RowResizer extends AbstractResizer {
22432
22441
  return this.env.model.getters.getActiveRows();
22433
22442
  }
22434
22443
  _getPreviousVisibleElement(index) {
22435
- const sheetId = this.env.model.getters.getActiveSheetId();
22444
+ const sheetId = this.sheetId;
22436
22445
  let row;
22437
22446
  for (row = index - 1; row >= 0; row--) {
22438
22447
  if (!this.env.model.getters.isRowHidden(sheetId, row)) {
@@ -22443,7 +22452,7 @@ class RowResizer extends AbstractResizer {
22443
22452
  }
22444
22453
  unhide(hiddenElements) {
22445
22454
  this.env.model.dispatch("UNHIDE_COLUMNS_ROWS", {
22446
- sheetId: this.env.model.getters.getActiveSheetId(),
22455
+ sheetId: this.sheetId,
22447
22456
  dimension: "ROW",
22448
22457
  elements: hiddenElements,
22449
22458
  });
@@ -25521,29 +25530,33 @@ function applyStyleToZone(appliedStyle, zone, cells, styles) {
25521
25530
  * In all the sheets, replace the table-only references in the formula cells with standard references.
25522
25531
  */
25523
25532
  function convertTableFormulaReferences(convertedSheets, xlsxSheets) {
25524
- for (let sheet of convertedSheets) {
25525
- const tables = xlsxSheets.find((s) => s.sheetName === sheet.name).tables;
25533
+ for (let tableSheet of convertedSheets) {
25534
+ const tables = xlsxSheets.find((s) => s.sheetName === tableSheet.name).tables;
25526
25535
  for (let table of tables) {
25527
25536
  const tabRef = table.name + "[";
25528
- for (let position of positions(toZone(table.ref))) {
25529
- const xc = toXC(position.col, position.row);
25530
- const cell = sheet.cells[xc];
25531
- if (cell && cell.content && cell.content.startsWith("=")) {
25532
- let refIndex;
25533
- while ((refIndex = cell.content.indexOf(tabRef)) !== -1) {
25534
- let reference = cell.content.slice(refIndex + tabRef.length);
25535
- // Expression can either be tableName[colName] or tableName[[#This Row], [colName]]
25536
- let endIndex = reference.indexOf("]");
25537
- if (reference.startsWith(`[`)) {
25538
- endIndex = reference.indexOf("]", endIndex + 1);
25539
- endIndex = reference.indexOf("]", endIndex + 1);
25537
+ for (let sheet of convertedSheets) {
25538
+ for (let xc in sheet.cells) {
25539
+ const cell = sheet.cells[xc];
25540
+ if (cell && cell.content && cell.content.startsWith("=")) {
25541
+ let refIndex;
25542
+ while ((refIndex = cell.content.indexOf(tabRef)) !== -1) {
25543
+ let endIndex = refIndex + tabRef.length;
25544
+ let openBrackets = 1;
25545
+ while (openBrackets > 0 && endIndex < cell.content.length) {
25546
+ if (cell.content[endIndex] === "[") {
25547
+ openBrackets++;
25548
+ }
25549
+ else if (cell.content[endIndex] === "]") {
25550
+ openBrackets--;
25551
+ }
25552
+ endIndex++;
25553
+ }
25554
+ let reference = cell.content.slice(refIndex + tabRef.length, endIndex - 1);
25555
+ const sheetPrefix = tableSheet.id === sheet.id ? "" : tableSheet.name + "!";
25556
+ const convertedRef = convertTableReference(sheetPrefix, reference, table, xc);
25557
+ cell.content =
25558
+ cell.content.slice(0, refIndex) + convertedRef + cell.content.slice(endIndex);
25540
25559
  }
25541
- reference = reference.slice(0, endIndex);
25542
- const convertedRef = convertTableReference(reference, table, xc);
25543
- cell.content =
25544
- cell.content.slice(0, refIndex) +
25545
- convertedRef +
25546
- cell.content.slice(tabRef.length + refIndex + endIndex + 1);
25547
25560
  }
25548
25561
  }
25549
25562
  }
@@ -25551,11 +25564,17 @@ function convertTableFormulaReferences(convertedSheets, xlsxSheets) {
25551
25564
  }
25552
25565
  }
25553
25566
  /**
25554
- * Convert table-specific references in formulas into standard references.
25567
+ * Convert table-specific references in formulas into standard references. A table reference is composed of columns names,
25568
+ * and of keywords determining the rows of the table to reference.
25555
25569
  *
25556
25570
  * A reference in a table can have the form (only the part between brackets should be given to this function):
25557
25571
  * - tableName[colName] : reference to the whole column "colName"
25572
+ * - tableName[#keyword] : reference to the whatever row the keyword refers to
25558
25573
  * - tableName[[#keyword], [colName]] : reference to some of the element(s) of the column colName
25574
+ * - tableName[[#keyword], [colName]:[col2Name]] : reference to some of the element(s) of the columns colName to col2Name
25575
+ * - tableName[[#keyword1], [#keyword2], [colName]] : reference to all the rows referenced by the keywords in the column colName
25576
+ * - tableName[[#keyword1], [colName], [#keyword2]]: the keywords and colName can be in any order
25577
+ *
25559
25578
  *
25560
25579
  * The available keywords are :
25561
25580
  * - #All : all the column (including totals)
@@ -25563,58 +25582,109 @@ function convertTableFormulaReferences(convertedSheets, xlsxSheets) {
25563
25582
  * - #Headers : only the header of the column
25564
25583
  * - #Totals : only the totals of the column
25565
25584
  * - #This Row : only the element in the same row as the cell
25585
+ *
25586
+ * Note that the only valid combination of multiple keywords are #Data + #Totals and #Headers + #Data.
25566
25587
  */
25567
- function convertTableReference(expr, table, cellXc) {
25568
- const refElements = expr.split(",");
25588
+ function convertTableReference(sheetPrefix, expr, table, cellXc) {
25589
+ // TODO: Ideally we'd want to make a real tokenizer, this simple approach won't work if for example the column name
25590
+ // contain # or , characters. But that's probably an edge case that we can ignore for now.
25591
+ const parts = expr.split(",").map((part) => part.trim());
25569
25592
  const tableZone = toZone(table.ref);
25570
- const refZone = { ...tableZone };
25571
- let isReferencedZoneValid = true;
25572
- // Single column reference
25573
- if (refElements.length === 1) {
25574
- const colRelativeIndex = table.cols.findIndex((col) => col.name === refElements[0]);
25575
- refZone.left = refZone.right = colRelativeIndex + tableZone.left;
25576
- if (table.headerRowCount) {
25577
- refZone.top += table.headerRowCount;
25578
- }
25579
- if (table.totalsRowCount) {
25580
- refZone.bottom -= 1;
25593
+ const colIndexes = [];
25594
+ const rowIndexes = [];
25595
+ const foundKeywords = [];
25596
+ for (const part of parts) {
25597
+ if (removeBrackets(part).startsWith("#")) {
25598
+ const keyWord = removeBrackets(part);
25599
+ foundKeywords.push(keyWord);
25600
+ switch (keyWord) {
25601
+ case "#All":
25602
+ rowIndexes.push(tableZone.top, tableZone.bottom);
25603
+ break;
25604
+ case "#Data":
25605
+ const top = table.headerRowCount ? tableZone.top + table.headerRowCount : tableZone.top;
25606
+ const bottom = table.totalsRowCount
25607
+ ? tableZone.bottom - table.totalsRowCount
25608
+ : tableZone.bottom;
25609
+ rowIndexes.push(top, bottom);
25610
+ break;
25611
+ case "#This Row":
25612
+ rowIndexes.push(toCartesian(cellXc).row);
25613
+ break;
25614
+ case "#Headers":
25615
+ if (!table.headerRowCount) {
25616
+ return INCORRECT_RANGE_STRING;
25617
+ }
25618
+ rowIndexes.push(tableZone.top);
25619
+ break;
25620
+ case "#Totals":
25621
+ if (!table.totalsRowCount) {
25622
+ return INCORRECT_RANGE_STRING;
25623
+ }
25624
+ rowIndexes.push(tableZone.bottom);
25625
+ break;
25626
+ }
25581
25627
  }
25582
- }
25583
- // Other references
25584
- else {
25585
- switch (refElements[0].slice(1, refElements[0].length - 1)) {
25586
- case "#All":
25587
- refZone.top = table.headerRowCount ? tableZone.top + table.headerRowCount : tableZone.top;
25588
- refZone.bottom = tableZone.bottom;
25589
- break;
25590
- case "#Data":
25591
- refZone.top = table.headerRowCount ? tableZone.top + table.headerRowCount : tableZone.top;
25592
- refZone.bottom = table.totalsRowCount ? tableZone.bottom + 1 : tableZone.bottom;
25593
- break;
25594
- case "#This Row":
25595
- refZone.top = refZone.bottom = toCartesian(cellXc).row;
25596
- break;
25597
- case "#Headers":
25598
- refZone.top = refZone.bottom = tableZone.top;
25599
- if (!table.headerRowCount) {
25600
- isReferencedZoneValid = false;
25601
- }
25602
- break;
25603
- case "#Totals":
25604
- refZone.top = refZone.bottom = tableZone.bottom;
25605
- if (!table.totalsRowCount) {
25606
- isReferencedZoneValid = false;
25628
+ else {
25629
+ const columns = part
25630
+ .split(":")
25631
+ .map((part) => part.trim())
25632
+ .map(removeBrackets);
25633
+ if (colIndexes.length) {
25634
+ return INCORRECT_RANGE_STRING;
25635
+ }
25636
+ const colRelativeIndex = table.cols.findIndex((col) => col.name === columns[0]);
25637
+ if (colRelativeIndex === -1) {
25638
+ return INCORRECT_RANGE_STRING;
25639
+ }
25640
+ colIndexes.push(colRelativeIndex + tableZone.left);
25641
+ if (columns[1]) {
25642
+ const colRelativeIndex2 = table.cols.findIndex((col) => col.name === columns[1]);
25643
+ if (colRelativeIndex2 === -1) {
25644
+ return INCORRECT_RANGE_STRING;
25607
25645
  }
25608
- break;
25646
+ colIndexes.push(colRelativeIndex2 + tableZone.left);
25647
+ }
25609
25648
  }
25610
- const colRef = refElements[1].slice(1, refElements[1].length - 1);
25611
- const colRelativeIndex = table.cols.findIndex((col) => col.name === colRef);
25612
- refZone.left = refZone.right = colRelativeIndex + tableZone.left;
25613
25649
  }
25614
- if (!isReferencedZoneValid) {
25650
+ if (!areKeywordsCompatible(foundKeywords)) {
25615
25651
  return INCORRECT_RANGE_STRING;
25616
25652
  }
25617
- return refZone.top !== refZone.bottom ? zoneToXc(refZone) : toXC(refZone.left, refZone.top);
25653
+ if (rowIndexes.length === 0) {
25654
+ const top = table.headerRowCount ? tableZone.top + table.headerRowCount : tableZone.top;
25655
+ const bottom = table.totalsRowCount
25656
+ ? tableZone.bottom - table.totalsRowCount
25657
+ : tableZone.bottom;
25658
+ rowIndexes.push(top, bottom);
25659
+ }
25660
+ if (colIndexes.length === 0) {
25661
+ colIndexes.push(tableZone.left, tableZone.right);
25662
+ }
25663
+ const refZone = {
25664
+ top: Math.min(...rowIndexes),
25665
+ left: Math.min(...colIndexes),
25666
+ bottom: Math.max(...rowIndexes),
25667
+ right: Math.max(...colIndexes),
25668
+ };
25669
+ return sheetPrefix + zoneToXc(refZone);
25670
+ }
25671
+ function removeBrackets(str) {
25672
+ return str.startsWith("[") && str.endsWith("]") ? str.slice(1, str.length - 1) : str;
25673
+ }
25674
+ function areKeywordsCompatible(keywords) {
25675
+ if (keywords.length < 2) {
25676
+ return true;
25677
+ }
25678
+ else if (keywords.length > 2) {
25679
+ return false;
25680
+ }
25681
+ else if (keywords.includes("#Data") && keywords.includes("#Totals")) {
25682
+ return true;
25683
+ }
25684
+ else if (keywords.includes("#Headers") && keywords.includes("#Data")) {
25685
+ return true;
25686
+ }
25687
+ return false;
25618
25688
  }
25619
25689
 
25620
25690
  // -------------------------------------
@@ -31592,7 +31662,7 @@ class AutofillPlugin extends UIPlugin {
31592
31662
  getRule(cell, cells) {
31593
31663
  const rules = autofillRulesRegistry.getAll().sort((a, b) => a.sequence - b.sequence);
31594
31664
  const rule = rules.find((rule) => rule.condition(cell, cells));
31595
- return rule && rule.generateRule(cell, cells);
31665
+ return rule && this.direction && rule.generateRule(cell, cells, this.direction);
31596
31666
  }
31597
31667
  /**
31598
31668
  * Create the generator to be able to autofill the next cells.
@@ -37494,7 +37564,8 @@ class SheetViewPlugin extends UIPlugin {
37494
37564
  ? this.getters.getSheetViewVisibleCols()
37495
37565
  : this.getters.getSheetViewVisibleRows();
37496
37566
  const startIndex = visibleHeaders.findIndex((header) => referenceHeaderIndex >= header);
37497
- const endIndex = visibleHeaders.findIndex((header) => targetHeaderIndex <= header);
37567
+ let endIndex = visibleHeaders.findIndex((header) => targetHeaderIndex <= header);
37568
+ endIndex = endIndex === -1 ? visibleHeaders.length : endIndex;
37498
37569
  const relevantIndexes = visibleHeaders.slice(startIndex, endIndex);
37499
37570
  let offset = 0;
37500
37571
  for (const i of relevantIndexes) {
@@ -43659,6 +43730,6 @@ function addFunction(functionName, functionDescription) {
43659
43730
  export { AbstractChart, CommandResult, CorePlugin, DATETIME_FORMAT, DispatchResult, EvaluationError, Model, Registry, Revision, SPREADSHEET_DIMENSIONS, Spreadsheet, UIPlugin, __info__, addFunction, astToFormula, cellTypes, compile, components, convertAstNodes, coreTypes, findCellInNewZone, functionCache, helpers, invalidateEvaluationCommands, load, parse, readonlyAllowedCommands, registries, setTranslationMethod, tokenize };
43660
43731
 
43661
43732
 
43662
- __info__.version = '16.0.63';
43663
- __info__.date = '2025-02-25T05:54:41.386Z';
43664
- __info__.hash = 'b01aca6';
43733
+ __info__.version = '16.0.64';
43734
+ __info__.date = '2025-03-07T10:29:50.957Z';
43735
+ __info__.hash = '20def15';