@odoo/o-spreadsheet 16.3.0-alpha.7 → 16.3.0-alpha.8

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.
@@ -237,8 +237,8 @@
237
237
  const DEFAULT_FONT = "'Roboto', arial";
238
238
  const DEFAULT_VERTICAL_ALIGN = "bottom";
239
239
  // Borders
240
- const DEFAULT_BORDER_DESC = ["thin", "#000"];
241
- const DEFAULT_FILTER_BORDER_DESC = ["thin", FILTERS_COLOR];
240
+ const DEFAULT_BORDER_DESC = { style: "thin", color: "#000000" };
241
+ const DEFAULT_FILTER_BORDER_DESC = { style: "thin", color: FILTERS_COLOR };
242
242
  // Ranges
243
243
  const INCORRECT_RANGE_STRING = CellErrorType.InvalidReference;
244
244
  // Max Number of history steps kept in memory
@@ -1102,6 +1102,18 @@
1102
1102
  text = text.replace(whiteSpaceRegexp, " ");
1103
1103
  text = text.replace(newLineRegex, NEWLINE);
1104
1104
  return text;
1105
+ }
1106
+ /**
1107
+ * Determine if the numbers are consecutive.
1108
+ */
1109
+ function isConsecutive(iterable) {
1110
+ const array = Array.from(iterable).sort((a, b) => a - b); // sort numerically rather than lexicographically
1111
+ for (let i = 1; i < array.length; i++) {
1112
+ if (array[i] - array[i - 1] !== 1) {
1113
+ return false;
1114
+ }
1115
+ }
1116
+ return true;
1105
1117
  }
1106
1118
 
1107
1119
  const RBA_REGEX = /rgba?\(|\s+|\)/gi;
@@ -4244,6 +4256,7 @@
4244
4256
  "SET_FORMATTING",
4245
4257
  "CLEAR_FORMATTING",
4246
4258
  "SET_BORDER",
4259
+ "SET_ZONE_BORDERS",
4247
4260
  /** CHART */
4248
4261
  "CREATE_CHART",
4249
4262
  "UPDATE_CHART",
@@ -4386,6 +4399,7 @@
4386
4399
  CommandResult[CommandResult["NoSplitSeparatorInSelection"] = 91] = "NoSplitSeparatorInSelection";
4387
4400
  })(exports.CommandResult || (exports.CommandResult = {}));
4388
4401
 
4402
+ const borderStyles = ["thin", "medium", "thick", "dashed", "dotted"];
4389
4403
  var DIRECTION;
4390
4404
  (function (DIRECTION) {
4391
4405
  DIRECTION["UP"] = "up";
@@ -5479,12 +5493,6 @@
5479
5493
  cursor: pointer;
5480
5494
  user-select: none;
5481
5495
 
5482
- .o-menu-item-name {
5483
- overflow: hidden;
5484
- white-space: nowrap;
5485
- text-overflow: ellipsis;
5486
- }
5487
-
5488
5496
  &.o-menu-root {
5489
5497
  display: flex;
5490
5498
  justify-content: space-between;
@@ -5495,10 +5503,6 @@
5495
5503
  margin: 0px 8px 0px 0px;
5496
5504
  width: ${MENU_ITEM_HEIGHT - 2 * MENU_ITEM_PADDING_VERTICAL}px;
5497
5505
  line-height: ${MENU_ITEM_HEIGHT - 2 * MENU_ITEM_PADDING_VERTICAL}px;
5498
-
5499
- .o-icon {
5500
- color: ${ICONS_COLOR};
5501
- }
5502
5506
  }
5503
5507
  .o-menu-item-root {
5504
5508
  width: 10px;
@@ -5512,6 +5516,11 @@
5512
5516
  .o-menu-item-description {
5513
5517
  color: grey;
5514
5518
  }
5519
+ .o-menu-item-icon {
5520
+ .o-icon {
5521
+ color: ${ICONS_COLOR};
5522
+ }
5523
+ }
5515
5524
  }
5516
5525
  &.disabled {
5517
5526
  color: ${MENU_ITEM_DISABLED_COLOR};
@@ -6328,8 +6337,14 @@
6328
6337
  : undefined));
6329
6338
  }
6330
6339
  }
6340
+ else if (zone.left === zone.right && zone.top === zone.bottom) {
6341
+ // A single cell. If it's only the title, the dataset is not added.
6342
+ if (!dataSetsHaveTitle) {
6343
+ dataSets.push(createDataSet(getters, dataSetSheetId, zone, undefined));
6344
+ }
6345
+ }
6331
6346
  else {
6332
- /* 1 cell, 1 row or 1 column */
6347
+ /* 1 row or 1 column */
6333
6348
  dataSets.push(createDataSet(getters, dataSetSheetId, zone, dataSetsHaveTitle
6334
6349
  ? {
6335
6350
  top: zone.top,
@@ -8940,7 +8955,7 @@
8940
8955
  handlePasteResult(env, result);
8941
8956
  };
8942
8957
  const MENU_INSERT_ROWS_NAME = (env) => {
8943
- const number = getColumnsNumber(env);
8958
+ const number = getRowsNumber(env);
8944
8959
  return number === 1 ? _lt("Insert row") : _lt("Insert %s rows", number.toString());
8945
8960
  };
8946
8961
  const MENU_INSERT_ROWS_BEFORE_NAME = (env) => {
@@ -15305,7 +15320,11 @@
15305
15320
  : getNormalizedValueFromRowRange;
15306
15321
  const rangeLength = verticalSearch ? nbRow : nbCol;
15307
15322
  const index = dichotomicSearch(searchArray, _searchKey, "nextSmaller", "asc", rangeLength, getElement);
15308
- assertAvailable(searchArray[0][index], searchKey);
15323
+ if (index === -1)
15324
+ assertAvailable(undefined, searchKey);
15325
+ verticalSearch
15326
+ ? assertAvailable(searchArray[0][index], searchKey)
15327
+ : assertAvailable(searchArray[index][nbRow - 1], searchKey);
15309
15328
  if (resultRange === undefined) {
15310
15329
  return (verticalSearch ? searchArray[nbCol - 1][index] : searchArray[index][nbRow - 1]);
15311
15330
  }
@@ -16212,75 +16231,97 @@
16212
16231
 
16213
16232
  const insertRow = {
16214
16233
  name: MENU_INSERT_ROWS_NAME,
16215
- isVisible: IS_ONLY_ONE_RANGE,
16234
+ isVisible: (env) => isConsecutive(env.model.getters.getActiveRows()) &&
16235
+ IS_ONLY_ONE_RANGE(env) &&
16236
+ env.model.getters.getActiveCols().size === 0,
16216
16237
  icon: "o-spreadsheet-Icon.INSERT_ROW",
16217
16238
  };
16218
16239
  const rowInsertRowBefore = {
16219
16240
  name: ROW_INSERT_ROWS_BEFORE_NAME,
16220
16241
  execute: INSERT_ROWS_BEFORE_ACTION,
16242
+ isVisible: (env) => isConsecutive(env.model.getters.getActiveRows()) &&
16243
+ IS_ONLY_ONE_RANGE(env) &&
16244
+ env.model.getters.getActiveCols().size === 0,
16245
+ icon: "o-spreadsheet-Icon.INSERT_ROW_BEFORE",
16221
16246
  };
16222
16247
  const topBarInsertRowsBefore = {
16223
16248
  ...rowInsertRowBefore,
16224
16249
  name: MENU_INSERT_ROWS_BEFORE_NAME,
16225
- isVisible: (env) => env.model.getters.getActiveCols().size === 0,
16226
16250
  };
16227
16251
  const cellInsertRowsBefore = {
16228
16252
  ...rowInsertRowBefore,
16229
16253
  name: CELL_INSERT_ROWS_BEFORE_NAME,
16230
16254
  isVisible: IS_ONLY_ONE_RANGE,
16231
- icon: "o-spreadsheet-Icon.INSERT_ROW",
16255
+ icon: "o-spreadsheet-Icon.INSERT_ROW_BEFORE",
16232
16256
  };
16233
16257
  const rowInsertRowsAfter = {
16234
16258
  execute: INSERT_ROWS_AFTER_ACTION,
16235
16259
  name: ROW_INSERT_ROWS_AFTER_NAME,
16260
+ isVisible: (env) => isConsecutive(env.model.getters.getActiveRows()) &&
16261
+ IS_ONLY_ONE_RANGE(env) &&
16262
+ env.model.getters.getActiveCols().size === 0,
16263
+ icon: "o-spreadsheet-Icon.INSERT_ROW_AFTER",
16236
16264
  };
16237
16265
  const topBarInsertRowsAfter = {
16238
16266
  ...rowInsertRowsAfter,
16239
16267
  name: MENU_INSERT_ROWS_AFTER_NAME,
16240
- isVisible: (env) => env.model.getters.getActiveCols().size === 0,
16241
16268
  };
16242
16269
  const insertCol = {
16243
16270
  name: MENU_INSERT_COLUMNS_NAME,
16244
- isVisible: IS_ONLY_ONE_RANGE,
16271
+ isVisible: (env) => isConsecutive(env.model.getters.getActiveCols()) &&
16272
+ IS_ONLY_ONE_RANGE(env) &&
16273
+ env.model.getters.getActiveRows().size === 0,
16245
16274
  icon: "o-spreadsheet-Icon.INSERT_COL",
16246
16275
  };
16247
16276
  const colInsertColsBefore = {
16248
16277
  name: COLUMN_INSERT_COLUMNS_BEFORE_NAME,
16249
16278
  execute: INSERT_COLUMNS_BEFORE_ACTION,
16279
+ isVisible: (env) => isConsecutive(env.model.getters.getActiveCols()) &&
16280
+ IS_ONLY_ONE_RANGE(env) &&
16281
+ env.model.getters.getActiveRows().size === 0,
16282
+ icon: "o-spreadsheet-Icon.INSERT_COL_BEFORE",
16250
16283
  };
16251
16284
  const topBarInsertColsBefore = {
16252
16285
  ...colInsertColsBefore,
16253
16286
  name: MENU_INSERT_COLUMNS_BEFORE_NAME,
16254
- isVisible: (env) => env.model.getters.getActiveRows().size === 0,
16255
16287
  };
16256
16288
  const cellInsertColsBefore = {
16257
16289
  ...colInsertColsBefore,
16258
16290
  name: CELL_INSERT_COLUMNS_BEFORE_NAME,
16259
16291
  isVisible: IS_ONLY_ONE_RANGE,
16260
- icon: "o-spreadsheet-Icon.INSERT_COL",
16292
+ icon: "o-spreadsheet-Icon.INSERT_COL_BEFORE",
16261
16293
  };
16262
16294
  const colInsertColsAfter = {
16263
16295
  name: COLUMN_INSERT_COLUMNS_AFTER_NAME,
16264
16296
  execute: INSERT_COLUMNS_AFTER_ACTION,
16297
+ isVisible: (env) => isConsecutive(env.model.getters.getActiveCols()) &&
16298
+ IS_ONLY_ONE_RANGE(env) &&
16299
+ env.model.getters.getActiveRows().size === 0,
16300
+ icon: "o-spreadsheet-Icon.INSERT_COL_AFTER",
16265
16301
  };
16266
16302
  const topBarInsertColsAfter = {
16267
16303
  ...colInsertColsAfter,
16268
16304
  name: MENU_INSERT_COLUMNS_AFTER_NAME,
16269
16305
  execute: INSERT_COLUMNS_AFTER_ACTION,
16270
- isVisible: (env) => env.model.getters.getActiveRows().size === 0,
16271
16306
  };
16272
16307
  const insertCell = {
16273
16308
  name: _lt("Insert cells"),
16274
- isVisible: IS_ONLY_ONE_RANGE,
16309
+ isVisible: (env) => IS_ONLY_ONE_RANGE(env) &&
16310
+ env.model.getters.getActiveCols().size === 0 &&
16311
+ env.model.getters.getActiveRows().size === 0,
16275
16312
  icon: "o-spreadsheet-Icon.INSERT_CELL",
16276
16313
  };
16277
16314
  const insertCellShiftDown = {
16278
16315
  name: _lt("Insert cells and shift down"),
16279
16316
  execute: INSERT_CELL_SHIFT_DOWN,
16317
+ isVisible: (env) => env.model.getters.getActiveRows().size === 0 && env.model.getters.getActiveCols().size === 0,
16318
+ icon: "o-spreadsheet-Icon.INSERT_CELL_SHIFT_DOWN",
16280
16319
  };
16281
16320
  const insertCellShiftRight = {
16282
16321
  name: _lt("Insert cells and shift right"),
16283
16322
  execute: INSERT_CELL_SHIFT_RIGHT,
16323
+ isVisible: (env) => env.model.getters.getActiveRows().size === 0 && env.model.getters.getActiveCols().size === 0,
16324
+ icon: "o-spreadsheet-Icon.INSERT_CELL_SHIFT_RIGHT",
16284
16325
  };
16285
16326
  const insertChart = {
16286
16327
  name: _lt("Chart"),
@@ -16478,6 +16519,7 @@
16478
16519
  sequence: 1,
16479
16520
  execute: (env) => env.openSidePanel("SplitToColumns", {}),
16480
16521
  isEnabled: (env) => env.model.getters.isSingleColSelected(),
16522
+ icon: "o-spreadsheet-Icon.SPLIT_TEXT",
16481
16523
  };
16482
16524
 
16483
16525
  const formatNumberAutomatic = {
@@ -16693,60 +16735,6 @@
16693
16735
  execute: FORMAT_CLEARFORMAT_ACTION,
16694
16736
  icon: "o-spreadsheet-Icon.CLEAR_FORMAT",
16695
16737
  };
16696
- const borders = {
16697
- name: _lt("Borders"),
16698
- icon: "o-spreadsheet-Icon.BORDERS",
16699
- };
16700
- const bordersAll = {
16701
- name: _lt("All borders"),
16702
- execute: (env) => setBorder(env, "all"),
16703
- icon: "o-spreadsheet-Icon.BORDERS",
16704
- };
16705
- const bordersInner = {
16706
- name: _lt("Inner borders"),
16707
- execute: (env) => setBorder(env, "hv"),
16708
- icon: "o-spreadsheet-Icon.BORDER_HV",
16709
- };
16710
- const bordersHorizontal = {
16711
- name: _lt("Horizontal borders"),
16712
- execute: (env) => setBorder(env, "h"),
16713
- icon: "o-spreadsheet-Icon.BORDER_H",
16714
- };
16715
- const bordersVertical = {
16716
- name: _lt("Vertical borders"),
16717
- execute: (env) => setBorder(env, "v"),
16718
- icon: "o-spreadsheet-Icon.BORDER_V",
16719
- };
16720
- const bordersExternal = {
16721
- name: _lt("External borders"),
16722
- execute: (env) => setBorder(env, "external"),
16723
- icon: "o-spreadsheet-Icon.BORDER_EXTERNAL",
16724
- };
16725
- const bordersLeft = {
16726
- name: _lt("Left borders"),
16727
- execute: (env) => setBorder(env, "left"),
16728
- icon: "o-spreadsheet-Icon.BORDER_LEFT",
16729
- };
16730
- const bordersTop = {
16731
- name: _lt("Top borders"),
16732
- execute: (env) => setBorder(env, "top"),
16733
- icon: "o-spreadsheet-Icon.BORDER_TOP",
16734
- };
16735
- const bordersRight = {
16736
- name: _lt("Right borders"),
16737
- execute: (env) => setBorder(env, "right"),
16738
- icon: "o-spreadsheet-Icon.BORDER_RIGHT",
16739
- };
16740
- const bordersBottom = {
16741
- name: _lt("Bottom borders"),
16742
- execute: (env) => setBorder(env, "bottom"),
16743
- icon: "o-spreadsheet-Icon.BORDER_BOTTOM",
16744
- };
16745
- const bordersClear = {
16746
- name: _lt("Clear borders"),
16747
- execute: (env) => setBorder(env, "clear"),
16748
- icon: "o-spreadsheet-Icon.BORDER_CLEAR",
16749
- };
16750
16738
  function fontSizeMenuBuilder() {
16751
16739
  return FONT_SIZES.map((fs) => {
16752
16740
  return {
@@ -16759,24 +16747,17 @@
16759
16747
  });
16760
16748
  }
16761
16749
  function isAutomaticFormatSelected(env) {
16762
- const activeCell = env.model.getters.getActiveCell();
16750
+ const activeCell = env.model.getters.getCell(env.model.getters.getActivePosition());
16763
16751
  return !activeCell || !activeCell.format;
16764
16752
  }
16765
16753
  function isFormatSelected(env, format) {
16766
- const activeCell = env.model.getters.getActiveCell();
16767
- return activeCell && activeCell.format === format;
16754
+ const activeCell = env.model.getters.getCell(env.model.getters.getActivePosition());
16755
+ return activeCell?.format === format;
16768
16756
  }
16769
16757
  function isFontSizeSelected(env, fontSize) {
16770
16758
  const currentFontSize = env.model.getters.getCurrentStyle().fontSize || DEFAULT_FONT_SIZE;
16771
16759
  return currentFontSize === fontSize;
16772
16760
  }
16773
- function setBorder(env, command) {
16774
- env.model.dispatch("SET_FORMATTING", {
16775
- sheetId: env.model.getters.getActiveSheetId(),
16776
- target: env.model.getters.getSelectedZones(),
16777
- border: command,
16778
- });
16779
- }
16780
16761
  function getHorizontalAlign(env) {
16781
16762
  const style = env.model.getters.getCurrentStyle();
16782
16763
  if (style.align) {
@@ -16823,18 +16804,7 @@
16823
16804
  fillColor: fillColor,
16824
16805
  formatCF: formatCF,
16825
16806
  paintFormat: paintFormat,
16826
- clearFormat: clearFormat,
16827
- borders: borders,
16828
- bordersAll: bordersAll,
16829
- bordersInner: bordersInner,
16830
- bordersHorizontal: bordersHorizontal,
16831
- bordersVertical: bordersVertical,
16832
- bordersExternal: bordersExternal,
16833
- bordersLeft: bordersLeft,
16834
- bordersTop: bordersTop,
16835
- bordersRight: bordersRight,
16836
- bordersBottom: bordersBottom,
16837
- bordersClear: bordersClear
16807
+ clearFormat: clearFormat
16838
16808
  });
16839
16809
 
16840
16810
  function interactiveFreezeColumnsRows(env, dimension, base) {
@@ -16850,6 +16820,7 @@
16850
16820
  name: HIDE_COLUMNS_NAME,
16851
16821
  execute: HIDE_COLUMNS_ACTION,
16852
16822
  isVisible: NOT_ALL_VISIBLE_COLS_SELECTED,
16823
+ icon: "o-spreadsheet-Icon.HIDE_COL",
16853
16824
  };
16854
16825
  const unhideCols = {
16855
16826
  name: _lt("Unhide columns"),
@@ -16871,6 +16842,7 @@
16871
16842
  name: HIDE_ROWS_NAME,
16872
16843
  execute: HIDE_ROWS_ACTION,
16873
16844
  isVisible: NOT_ALL_VISIBLE_ROWS_SELECTED,
16845
+ icon: "o-spreadsheet-Icon.HIDE_ROW",
16874
16846
  };
16875
16847
  const unhideRows = {
16876
16848
  name: _lt("Unhide rows"),
@@ -17074,19 +17046,21 @@
17074
17046
  .add("delete_column", {
17075
17047
  ...deleteCols,
17076
17048
  sequence: 90,
17049
+ icon: "o-spreadsheet-Icon.DELETE",
17077
17050
  })
17078
17051
  .add("clear_column", {
17079
17052
  ...clearCols,
17080
17053
  sequence: 100,
17054
+ icon: "o-spreadsheet-Icon.CLEAR",
17081
17055
  })
17082
17056
  .add("hide_columns", {
17083
17057
  ...hideCols,
17084
- sequence: 85,
17058
+ sequence: 105,
17085
17059
  separator: true,
17086
17060
  })
17087
17061
  .add("unhide_columns", {
17088
17062
  ...unhideCols,
17089
- sequence: 86,
17063
+ sequence: 106,
17090
17064
  separator: true,
17091
17065
  })
17092
17066
  .add("conditional_formatting", {
@@ -17184,10 +17158,12 @@
17184
17158
  .add("delete_row", {
17185
17159
  ...deleteRows,
17186
17160
  sequence: 70,
17161
+ icon: "o-spreadsheet-Icon.DELETE",
17187
17162
  })
17188
17163
  .add("clear_row", {
17189
17164
  ...clearRows,
17190
17165
  sequence: 80,
17166
+ icon: "o-spreadsheet-Icon.CLEAR",
17191
17167
  })
17192
17168
  .add("hide_rows", {
17193
17169
  ...hideRows,
@@ -17680,6 +17656,12 @@
17680
17656
  height: 25px;
17681
17657
  }
17682
17658
  }
17659
+ /** Make the character a bit bigger
17660
+ compared to its neighbor INPUT box */
17661
+ .o-remove-selection {
17662
+ font-weight: bold;
17663
+ font-size: calc(100% + 4px);
17664
+ }
17683
17665
  }
17684
17666
  `;
17685
17667
  /**
@@ -17775,6 +17757,9 @@
17775
17757
  this.confirm();
17776
17758
  }
17777
17759
  }
17760
+ extractRanges(value) {
17761
+ return this.props.hasSingleRange ? value.split(",")[0] : value;
17762
+ }
17778
17763
  focus(rangeId) {
17779
17764
  this.state.isMissing = false;
17780
17765
  this.state.mode = "select-range";
@@ -17793,10 +17778,11 @@
17793
17778
  }
17794
17779
  onInputChanged(rangeId, ev) {
17795
17780
  const target = ev.target;
17781
+ const value = this.extractRanges(target.value);
17796
17782
  this.env.model.dispatch("CHANGE_RANGE", {
17797
17783
  id: this.id,
17798
17784
  rangeId,
17799
- value: target.value,
17785
+ value,
17800
17786
  });
17801
17787
  this.triggerChange();
17802
17788
  }
@@ -20565,14 +20551,31 @@
20565
20551
  * */
20566
20552
  getCurrentSelection() {
20567
20553
  let { startElement, endElement, startSelectionOffset, endSelectionOffset } = this.getStartAndEndSelection();
20568
- let startSizeBefore = this.findSizeBeforeElement(startElement);
20569
- let endSizeBefore = this.findSizeBeforeElement(endElement);
20554
+ let startSizeBefore = this.findSelectionIndex(startElement, startSelectionOffset);
20555
+ let endSizeBefore = this.findSelectionIndex(endElement, endSelectionOffset);
20570
20556
  return {
20571
- start: startSizeBefore + startSelectionOffset,
20572
- end: endSizeBefore + endSelectionOffset,
20557
+ start: startSizeBefore,
20558
+ end: endSizeBefore,
20573
20559
  };
20574
20560
  }
20575
- findSizeBeforeElement(nodeToFind) {
20561
+ /**
20562
+ * Computes the text 'index' inside this.el based on the currently selected node and its offset.
20563
+ * The selected node is either a Text node or an Element node.
20564
+ *
20565
+ * case 1 -Text node:
20566
+ * the offset is the number of characters from the start of the node. We have to add this offset to the
20567
+ * content length of all previous nodes.
20568
+ *
20569
+ * case 2 - Element node:
20570
+ * the offset is the number of child nodes before the selected node. We have to add the content length of
20571
+ * all the bnodes prior to the selected node as well as the content of the child node before the offset.
20572
+ *
20573
+ * See the MDN documentation for more details.
20574
+ * https://developer.mozilla.org/en-US/docs/Web/API/Range/startOffset
20575
+ * https://developer.mozilla.org/en-US/docs/Web/API/Range/endOffset
20576
+ *
20577
+ */
20578
+ findSelectionIndex(nodeToFind, nodeOffset) {
20576
20579
  let usedCharacters = 0;
20577
20580
  let it = iterateChildren(this.el);
20578
20581
  let current = it.next();
@@ -20599,6 +20602,28 @@
20599
20602
  if (current.value !== nodeToFind) {
20600
20603
  throw new Error("Cannot find the node in the children of the element");
20601
20604
  }
20605
+ else {
20606
+ if (!current.value.hasChildNodes()) {
20607
+ usedCharacters += nodeOffset;
20608
+ }
20609
+ else {
20610
+ const children = [...current.value.childNodes].slice(0, nodeOffset);
20611
+ usedCharacters += children.reduce((acc, child, index) => {
20612
+ if (child.textContent !== null) {
20613
+ // need to account for paragraph nodes that implicitely add a new line
20614
+ // except for the last paragraph
20615
+ let chars = child.textContent.length;
20616
+ if (child.nodeName === "P" && index !== children.length - 1) {
20617
+ chars++;
20618
+ }
20619
+ return acc + chars;
20620
+ }
20621
+ else {
20622
+ return acc;
20623
+ }
20624
+ }, 0);
20625
+ }
20626
+ }
20602
20627
  if (nodeToFind.nodeName === "P" && !isFirstParagraph && nodeToFind.textContent == "") {
20603
20628
  usedCharacters++;
20604
20629
  }
@@ -21399,13 +21424,13 @@
21399
21424
 
21400
21425
  const CSS$1 = css /* scss */ `
21401
21426
  .o-filter-icon {
21427
+ color: ${FILTERS_COLOR};
21402
21428
  position: absolute;
21403
21429
  display: flex;
21404
21430
  align-items: center;
21405
21431
  justify-content: center;
21406
21432
  width: ${FILTER_ICON_EDGE_LENGTH}px;
21407
21433
  height: ${FILTER_ICON_EDGE_LENGTH}px;
21408
- color: #4a4f59;
21409
21434
  }
21410
21435
  .o-filter-icon:hover {
21411
21436
  background: ${FILTERS_COLOR};
@@ -24136,17 +24161,17 @@
24136
24161
  const BORDER_STYLE_CONVERSION_MAP = {
24137
24162
  dashDot: "thin",
24138
24163
  dashDotDot: "thin",
24139
- dashed: "thin",
24140
- dotted: "thin",
24164
+ dashed: "dashed",
24165
+ dotted: "dotted",
24141
24166
  double: "thin",
24142
24167
  hair: "thin",
24143
- medium: "thin",
24168
+ medium: "medium",
24144
24169
  mediumDashDot: "thin",
24145
24170
  mediumDashDotDot: "thin",
24146
24171
  mediumDashed: "thin",
24147
24172
  none: undefined,
24148
24173
  slantDashDot: "thin",
24149
- thick: "thin",
24174
+ thick: "thick",
24150
24175
  thin: "thin",
24151
24176
  };
24152
24177
  /** Conversion map Horizontal Alignment in XLSX <=> Horizontal Alignment in o_spreadsheet*/
@@ -24645,7 +24670,7 @@
24645
24670
  return undefined;
24646
24671
  addBorderDescrWarnings(borderDescr, warningManager);
24647
24672
  const style = BORDER_STYLE_CONVERSION_MAP[borderDescr.style];
24648
- return style ? [style, convertColor(borderDescr.color)] : undefined;
24673
+ return style ? { style, color: convertColor(borderDescr.color) } : undefined;
24649
24674
  }
24650
24675
  function convertStyles(data, warningManager) {
24651
24676
  const stylesArray = data.styles.map((style) => {
@@ -26043,8 +26068,8 @@
26043
26068
  return undefined;
26044
26069
  }
26045
26070
  return {
26046
- style: descr[0],
26047
- color: { rgb: descr[1] },
26071
+ style: descr.style,
26072
+ color: { rgb: descr.color },
26048
26073
  };
26049
26074
  }
26050
26075
  function extractStyle(cell, data) {
@@ -26622,7 +26647,7 @@
26622
26647
  const TABLE_HIGHLIGHTED_CELL_STYLE = {
26623
26648
  bold: true,
26624
26649
  };
26625
- const TABLE_BORDER_STYLE = ["thin", "#000000FF"];
26650
+ const TABLE_BORDER_STYLE = { style: "thin", color: "#000000FF" };
26626
26651
  /**
26627
26652
  * Convert the imported XLSX tables.
26628
26653
  *
@@ -28191,7 +28216,7 @@
28191
28216
  * a breaking change is made in the way the state is handled, and an upgrade
28192
28217
  * function should be defined
28193
28218
  */
28194
- const CURRENT_VERSION = 12;
28219
+ const CURRENT_VERSION = 13;
28195
28220
  const INITIAL_SHEET_ID = "Sheet1";
28196
28221
  /**
28197
28222
  * This function tries to load anything that could look like a valid
@@ -28455,6 +28480,25 @@
28455
28480
  return data;
28456
28481
  },
28457
28482
  },
28483
+ {
28484
+ description: "Change Border description structure",
28485
+ from: 12,
28486
+ to: 13,
28487
+ applyMigration(data) {
28488
+ for (const borderId in data.borders) {
28489
+ const border = data.borders[borderId];
28490
+ for (const position in border) {
28491
+ if (Array.isArray(border[position])) {
28492
+ border[position] = {
28493
+ style: border[position][0],
28494
+ color: border[position][1],
28495
+ };
28496
+ }
28497
+ }
28498
+ }
28499
+ return data;
28500
+ },
28501
+ },
28458
28502
  ];
28459
28503
  /**
28460
28504
  * This function is used to repair faulty data independently of the migration.
@@ -28790,7 +28834,7 @@
28790
28834
  * - borders
28791
28835
  */
28792
28836
  class BordersPlugin extends CorePlugin {
28793
- static getters = ["getCellBorder"];
28837
+ static getters = ["getCellBorder", "getBordersColors"];
28794
28838
  borders = {};
28795
28839
  // ---------------------------------------------------------------------------
28796
28840
  // Command Handling
@@ -28821,10 +28865,15 @@
28821
28865
  case "SET_BORDER":
28822
28866
  this.setBorder(cmd.sheetId, cmd.col, cmd.row, cmd.border);
28823
28867
  break;
28824
- case "SET_FORMATTING":
28868
+ case "SET_ZONE_BORDERS":
28825
28869
  if (cmd.border) {
28826
28870
  const target = cmd.target.map((zone) => this.getters.expandZone(cmd.sheetId, zone));
28827
- this.setBorders(cmd.sheetId, target, cmd.border);
28871
+ this.setBorders(cmd.sheetId, target, cmd.border.position, cmd.border.color === ""
28872
+ ? undefined
28873
+ : {
28874
+ style: cmd.border.style || DEFAULT_BORDER_DESC.style,
28875
+ color: cmd.border.color || DEFAULT_BORDER_DESC.color,
28876
+ });
28828
28877
  }
28829
28878
  break;
28830
28879
  case "CLEAR_FORMATTING":
@@ -28913,6 +28962,23 @@
28913
28962
  }
28914
28963
  return border;
28915
28964
  }
28965
+ getBordersColors(sheetId) {
28966
+ const colors = [];
28967
+ const sheetBorders = this.borders[sheetId];
28968
+ if (sheetBorders) {
28969
+ for (const borders of sheetBorders.filter(isDefined$1)) {
28970
+ for (const cellBorder of borders) {
28971
+ if (cellBorder?.horizontal) {
28972
+ colors.push(cellBorder.horizontal.color);
28973
+ }
28974
+ if (cellBorder?.vertical) {
28975
+ colors.push(cellBorder.vertical.color);
28976
+ }
28977
+ }
28978
+ }
28979
+ }
28980
+ return colors;
28981
+ }
28916
28982
  // ---------------------------------------------------------------------------
28917
28983
  // Private
28918
28984
  // ---------------------------------------------------------------------------
@@ -29124,43 +29190,43 @@
29124
29190
  * Set the borders of a zone by computing the borders to add from the given
29125
29191
  * command
29126
29192
  */
29127
- setBorders(sheetId, zones, command) {
29128
- if (command === "clear") {
29193
+ setBorders(sheetId, zones, position, border) {
29194
+ if (position === "clear") {
29129
29195
  return this.clearBorders(sheetId, zones);
29130
29196
  }
29131
29197
  for (let zone of zones) {
29132
- if (command === "h" || command === "hv" || command === "all") {
29198
+ if (position === "h" || position === "hv" || position === "all") {
29133
29199
  for (let row = zone.top + 1; row <= zone.bottom; row++) {
29134
29200
  for (let col = zone.left; col <= zone.right; col++) {
29135
- this.addBorder(sheetId, col, row, { top: DEFAULT_BORDER_DESC });
29201
+ this.addBorder(sheetId, col, row, { top: border });
29136
29202
  }
29137
29203
  }
29138
29204
  }
29139
- if (command === "v" || command === "hv" || command === "all") {
29205
+ if (position === "v" || position === "hv" || position === "all") {
29140
29206
  for (let row = zone.top; row <= zone.bottom; row++) {
29141
29207
  for (let col = zone.left + 1; col <= zone.right; col++) {
29142
- this.addBorder(sheetId, col, row, { left: DEFAULT_BORDER_DESC });
29208
+ this.addBorder(sheetId, col, row, { left: border });
29143
29209
  }
29144
29210
  }
29145
29211
  }
29146
- if (command === "left" || command === "all" || command === "external") {
29212
+ if (position === "left" || position === "all" || position === "external") {
29147
29213
  for (let row = zone.top; row <= zone.bottom; row++) {
29148
- this.addBorder(sheetId, zone.left, row, { left: DEFAULT_BORDER_DESC });
29214
+ this.addBorder(sheetId, zone.left, row, { left: border });
29149
29215
  }
29150
29216
  }
29151
- if (command === "right" || command === "all" || command === "external") {
29217
+ if (position === "right" || position === "all" || position === "external") {
29152
29218
  for (let row = zone.top; row <= zone.bottom; row++) {
29153
- this.addBorder(sheetId, zone.right + 1, row, { left: DEFAULT_BORDER_DESC });
29219
+ this.addBorder(sheetId, zone.right + 1, row, { left: border });
29154
29220
  }
29155
29221
  }
29156
- if (command === "top" || command === "all" || command === "external") {
29222
+ if (position === "top" || position === "all" || position === "external") {
29157
29223
  for (let col = zone.left; col <= zone.right; col++) {
29158
- this.addBorder(sheetId, col, zone.top, { top: DEFAULT_BORDER_DESC });
29224
+ this.addBorder(sheetId, col, zone.top, { top: border });
29159
29225
  }
29160
29226
  }
29161
- if (command === "bottom" || command === "all" || command === "external") {
29227
+ if (position === "bottom" || position === "all" || position === "external") {
29162
29228
  for (let col = zone.left; col <= zone.right; col++) {
29163
- this.addBorder(sheetId, col, zone.bottom + 1, { top: DEFAULT_BORDER_DESC });
29229
+ this.addBorder(sheetId, col, zone.bottom + 1, { top: border });
29164
29230
  }
29165
29231
  }
29166
29232
  }
@@ -29174,16 +29240,22 @@
29174
29240
  const bordersBottomRight = this.getCellBorder({ sheetId, col: right, row: bottom });
29175
29241
  this.clearBorders(sheetId, [zone]);
29176
29242
  if (bordersTopLeft?.top) {
29177
- this.setBorders(sheetId, [{ ...zone, bottom: top }], "top");
29243
+ this.setBorders(sheetId, [{ ...zone, bottom: top }], "top", bordersTopLeft.top);
29178
29244
  }
29179
29245
  if (bordersTopLeft?.left) {
29180
- this.setBorders(sheetId, [{ ...zone, right: left }], "left");
29246
+ this.setBorders(sheetId, [{ ...zone, right: left }], "left", bordersTopLeft.left);
29247
+ }
29248
+ if (bordersBottomRight?.bottom) {
29249
+ this.setBorders(sheetId, [{ ...zone, top: bottom }], "bottom", bordersBottomRight.bottom);
29250
+ }
29251
+ else if (bordersTopLeft?.bottom) {
29252
+ this.setBorders(sheetId, [{ ...zone, top: bottom }], "bottom", bordersTopLeft.bottom);
29181
29253
  }
29182
- if (bordersBottomRight?.bottom || bordersTopLeft?.bottom) {
29183
- this.setBorders(sheetId, [{ ...zone, top: bottom }], "bottom");
29254
+ if (bordersBottomRight?.right) {
29255
+ this.setBorders(sheetId, [{ ...zone, left: right }], "right", bordersBottomRight.right);
29184
29256
  }
29185
- if (bordersBottomRight?.right || bordersTopLeft?.right) {
29186
- this.setBorders(sheetId, [{ ...zone, left: right }], "right");
29257
+ else if (bordersTopLeft?.right) {
29258
+ this.setBorders(sheetId, [{ ...zone, left: right }], "right", bordersTopLeft.right);
29187
29259
  }
29188
29260
  }
29189
29261
  // ---------------------------------------------------------------------------
@@ -33036,6 +33108,9 @@
33036
33108
  case "UPDATE_CHART":
33037
33109
  case "CREATE_CHART":
33038
33110
  case "ADD_CONDITIONAL_FORMAT":
33111
+ case "SET_BORDER":
33112
+ case "SET_ZONE_BORDERS":
33113
+ case "SET_FORMATTING":
33039
33114
  this.shouldUpdateColors = true;
33040
33115
  }
33041
33116
  }
@@ -33050,8 +33125,7 @@
33050
33125
  getCustomColors() {
33051
33126
  let usedColors = [];
33052
33127
  for (const sheetId of this.getters.getSheetIds()) {
33053
- const cells = Object.values(this.getters.getCells(sheetId));
33054
- usedColors = usedColors.concat(this.getColorsFromCells(cells), this.getFormattingColors(sheetId), this.getChartColors(sheetId));
33128
+ usedColors = usedColors.concat(this.getColorsFromCells(sheetId), this.getFormattingColors(sheetId), this.getChartColors(sheetId));
33055
33129
  }
33056
33130
  return sortWithClusters([
33057
33131
  ...new Set(
@@ -33061,7 +33135,8 @@
33061
33135
  [...new Set([...usedColors, ...this.customColors])].filter(isColorValid).map(toHex)),
33062
33136
  ]).filter((color) => !COLOR_PICKER_DEFAULTS.includes(color));
33063
33137
  }
33064
- getColorsFromCells(cells) {
33138
+ getColorsFromCells(sheetId) {
33139
+ const cells = Object.values(this.getters.getCells(sheetId));
33065
33140
  const colors = new Set();
33066
33141
  for (const cell of cells) {
33067
33142
  if (cell.style?.textColor) {
@@ -33071,6 +33146,9 @@
33071
33146
  colors.add(cell.style.fillColor);
33072
33147
  }
33073
33148
  }
33149
+ for (const color of this.getters.getBordersColors(sheetId)) {
33150
+ colors.add(color);
33151
+ }
33074
33152
  return [...colors];
33075
33153
  }
33076
33154
  getFormattingColors(sheetId) {
@@ -33344,7 +33422,7 @@
33344
33422
  // magic "empty" value
33345
33423
  // Returning {value: null} instead of undefined will ensure that we don't
33346
33424
  // fall back on the default value of the argument provided to the formula's compute function
33347
- return { value: null };
33425
+ return { value: null, format: cell?.format };
33348
33426
  }
33349
33427
  return getEvaluatedCell(cell);
33350
33428
  }
@@ -36173,6 +36251,7 @@
36173
36251
 
36174
36252
  class Revision {
36175
36253
  rootCommand;
36254
+ timestamp;
36176
36255
  id;
36177
36256
  clientId;
36178
36257
  _commands = [];
@@ -36186,8 +36265,9 @@
36186
36265
  * - clientId: Client who initiated the action
36187
36266
  * - changes: List of changes applied on the state.
36188
36267
  */
36189
- constructor(id, clientId, commands, rootCommand, changes) {
36268
+ constructor(id, clientId, commands, rootCommand, changes, timestamp) {
36190
36269
  this.rootCommand = rootCommand;
36270
+ this.timestamp = timestamp;
36191
36271
  this.id = id;
36192
36272
  this.clientId = clientId;
36193
36273
  this._commands = [...commands];
@@ -36259,7 +36339,7 @@
36259
36339
  save(rootCommand, commands, changes) {
36260
36340
  if (!commands.length || !changes.length || !this.canApplyOptimisticUpdate())
36261
36341
  return;
36262
- const revision = new Revision(this.uuidGenerator.uuidv4(), this.clientId, commands, rootCommand, changes);
36342
+ const revision = new Revision(this.uuidGenerator.uuidv4(), this.clientId, commands, rootCommand, changes, Date.now());
36263
36343
  this.revisions.append(revision.id, revision);
36264
36344
  this.trigger("new-local-state-update", { id: revision.id });
36265
36345
  this.sendUpdateMessage({
@@ -36310,13 +36390,9 @@
36310
36390
  }
36311
36391
  loadInitialMessages(messages) {
36312
36392
  this.isReplayingInitialRevisions = true;
36313
- this.on("unexpected-revision-id", this, ({ revisionId }) => {
36314
- throw new Error(`The spreadsheet could not be loaded. Revision ${revisionId} is corrupted.`);
36315
- });
36316
36393
  for (const message of messages) {
36317
36394
  this.onMessageReceived(message);
36318
36395
  }
36319
- this.off("unexpected-revision-id", this);
36320
36396
  this.isReplayingInitialRevisions = false;
36321
36397
  }
36322
36398
  /**
@@ -36402,6 +36478,10 @@
36402
36478
  onMessageReceived(message) {
36403
36479
  if (this.isAlreadyProcessed(message))
36404
36480
  return;
36481
+ if (this.isWrongServerRevisionId(message)) {
36482
+ this.trigger("unexpected-revision-id");
36483
+ return;
36484
+ }
36405
36485
  switch (message.type) {
36406
36486
  case "CLIENT_MOVED":
36407
36487
  this.onClientMoved(message);
@@ -36428,12 +36508,8 @@
36428
36508
  });
36429
36509
  break;
36430
36510
  case "REMOTE_REVISION":
36431
- if (message.serverRevisionId !== this.serverRevisionId) {
36432
- this.trigger("unexpected-revision-id", { revisionId: message.serverRevisionId });
36433
- return;
36434
- }
36435
- const { clientId, commands } = message;
36436
- const revision = new Revision(message.nextRevisionId, clientId, commands, "REMOTE");
36511
+ const { clientId, commands, timestamp } = message;
36512
+ const revision = new Revision(message.nextRevisionId, clientId, commands, "REMOTE", undefined, timestamp);
36437
36513
  if (revision.clientId !== this.clientId) {
36438
36514
  this.revisions.insert(revision.id, revision, message.serverRevisionId);
36439
36515
  const pendingCommands = this.pendingMessages
@@ -36446,7 +36522,7 @@
36446
36522
  }
36447
36523
  break;
36448
36524
  case "SNAPSHOT_CREATED": {
36449
- const revision = new Revision(message.nextRevisionId, "server", [], "SNAPSHOT");
36525
+ const revision = new Revision(message.nextRevisionId, "server", [], "SNAPSHOT", undefined, Date.now());
36450
36526
  this.revisions.insert(revision.id, revision, message.serverRevisionId);
36451
36527
  this.dropPendingHistoryMessages();
36452
36528
  this.trigger("snapshot");
@@ -36563,6 +36639,17 @@
36563
36639
  return false;
36564
36640
  }
36565
36641
  }
36642
+ isWrongServerRevisionId(message) {
36643
+ switch (message.type) {
36644
+ case "REMOTE_REVISION":
36645
+ case "REVISION_REDONE":
36646
+ case "REVISION_UNDONE":
36647
+ case "SNAPSHOT_CREATED":
36648
+ return message.serverRevisionId !== this.serverRevisionId;
36649
+ default:
36650
+ return false;
36651
+ }
36652
+ }
36566
36653
  dropPendingHistoryMessages() {
36567
36654
  this.waitingUndoRedoAck = false;
36568
36655
  this.pendingMessages = this.pendingMessages.filter(({ type }) => type !== "REVISION_REDONE" && type !== "REVISION_UNDONE");
@@ -37278,7 +37365,7 @@
37278
37365
  }
37279
37366
  }
37280
37367
  drawBorders(renderingContext) {
37281
- const { ctx, thinLineWidth } = renderingContext;
37368
+ const { ctx } = renderingContext;
37282
37369
  for (let box of this.boxes) {
37283
37370
  const border = box.border;
37284
37371
  if (border) {
@@ -37297,13 +37384,66 @@
37297
37384
  }
37298
37385
  }
37299
37386
  }
37300
- function drawBorder([style, color], x1, y1, x2, y2) {
37387
+ /**
37388
+ * Following https://usefulangle.com/post/17/html5-canvas-drawing-1px-crisp-straight-lines,
37389
+ * we need to make sure that a "single" pixel line is drawn on a "half" pixel coordinate,
37390
+ * while a "double" pixel line is drawn on a "full" pixel coordinate. As, in the rendering
37391
+ * process, we always had 0.5 before rendering line (to make sure it is drawn on a "half"
37392
+ * pixel), we need to correct this behavior for the "medium" and the "dotted" styles, as
37393
+ * they are drawing a two pixels width line.
37394
+ * We also adapt here the coordinates of the line to make sure corner are correctly drawn,
37395
+ * avoiding a "round corners" effect. This is done by subtracting 1 pixel to the origin of
37396
+ * each line and adding 1 pixel to the end of each line (depending on the direction of the
37397
+ * line).
37398
+ */
37399
+ function drawBorder({ style, color }, x1, y1, x2, y2) {
37301
37400
  ctx.strokeStyle = color;
37302
- ctx.lineWidth = (style === "thin" ? 2 : 3) * thinLineWidth;
37401
+ switch (style) {
37402
+ case "medium":
37403
+ ctx.lineWidth = 2;
37404
+ x1 += y1 === y2 ? -0.5 : 0.5;
37405
+ x2 += y1 === y2 ? 1.5 : 0.5;
37406
+ y1 += x1 === x2 ? -0.5 : 0.5;
37407
+ y2 += x1 === x2 ? 1.5 : 0.5;
37408
+ break;
37409
+ case "thick":
37410
+ ctx.lineWidth = 3;
37411
+ if (y1 === y2) {
37412
+ x1--;
37413
+ x2++;
37414
+ }
37415
+ if (x1 === x2) {
37416
+ y1--;
37417
+ y2++;
37418
+ }
37419
+ break;
37420
+ case "dashed":
37421
+ ctx.lineWidth = 1;
37422
+ ctx.setLineDash([1, 3]);
37423
+ break;
37424
+ case "dotted":
37425
+ ctx.lineWidth = 1;
37426
+ if (y1 === y2) {
37427
+ x1 += 0.5;
37428
+ x2 += 0.5;
37429
+ }
37430
+ if (x1 === x2) {
37431
+ y1 += 0.5;
37432
+ y2 += 0.5;
37433
+ }
37434
+ ctx.setLineDash([1, 1]);
37435
+ break;
37436
+ case "thin":
37437
+ default:
37438
+ ctx.lineWidth = 1;
37439
+ break;
37440
+ }
37303
37441
  ctx.beginPath();
37304
37442
  ctx.moveTo(x1, y1);
37305
37443
  ctx.lineTo(x2, y2);
37306
37444
  ctx.stroke();
37445
+ ctx.lineWidth = 1;
37446
+ ctx.setLineDash([]);
37307
37447
  }
37308
37448
  }
37309
37449
  drawTexts(renderingContext) {
@@ -37781,6 +37921,9 @@
37781
37921
  activeSheet;
37782
37922
  willAddNewRange = false;
37783
37923
  constructor(config, initialRanges, inputHasSingleRange) {
37924
+ if (inputHasSingleRange && initialRanges.length > 1) {
37925
+ throw new Error("Input with a single range cannot be instantiated with several range references.");
37926
+ }
37784
37927
  super(config);
37785
37928
  this.inputHasSingleRange = inputHasSingleRange;
37786
37929
  this.insertNewRange(0, initialRanges);
@@ -37800,6 +37943,11 @@
37800
37943
  return 30 /* CommandResult.MaximumRangesReached */;
37801
37944
  }
37802
37945
  break;
37946
+ case "CHANGE_RANGE":
37947
+ if (this.inputHasSingleRange && cmd.value.split(",").length > 1) {
37948
+ return 30 /* CommandResult.MaximumRangesReached */;
37949
+ }
37950
+ break;
37803
37951
  }
37804
37952
  return 0 /* CommandResult.Success */;
37805
37953
  }
@@ -41286,7 +41434,8 @@
41286
41434
  },
41287
41435
  zone: selectedZone,
41288
41436
  };
41289
- this.setSelectionMixin(anchor, [selectedZone]);
41437
+ const selections = this.gridSelection.zones.map((zone) => updateSelectionOnDeletion(zone, "left", [...cmd.elements]));
41438
+ this.setSelectionMixin(anchor, selections);
41290
41439
  }
41291
41440
  onRowsRemoved(cmd) {
41292
41441
  const { cell, zone } = this.gridSelection.anchor;
@@ -41300,13 +41449,18 @@
41300
41449
  },
41301
41450
  zone: selectedZone,
41302
41451
  };
41303
- this.setSelectionMixin(anchor, [selectedZone]);
41452
+ const selections = this.gridSelection.zones.map((zone) => updateSelectionOnDeletion(zone, "top", [...cmd.elements]));
41453
+ this.setSelectionMixin(anchor, selections);
41304
41454
  }
41305
41455
  onAddElements(cmd) {
41306
- const selection = this.gridSelection.anchor.zone;
41307
- const zone = updateSelectionOnInsertion(selection, cmd.dimension === "COL" ? "left" : "top", cmd.base, cmd.position, cmd.quantity);
41308
- const anchor = { cell: { col: zone.left, row: zone.top }, zone };
41309
- this.setSelectionMixin(anchor, [zone]);
41456
+ const start = cmd.dimension === "COL" ? "left" : "top";
41457
+ const anchorZone = updateSelectionOnInsertion(this.gridSelection.anchor.zone, start, cmd.base, cmd.position, cmd.quantity);
41458
+ const selection = this.gridSelection.zones.map((zone) => updateSelectionOnInsertion(zone, start, cmd.base, cmd.position, cmd.quantity));
41459
+ const anchor = {
41460
+ cell: { col: anchorZone.left, row: anchorZone.top },
41461
+ zone: anchorZone,
41462
+ };
41463
+ this.setSelectionMixin(anchor, selection);
41310
41464
  }
41311
41465
  onMoveElements(cmd) {
41312
41466
  const thickness = cmd.elements.length;
@@ -42552,7 +42706,6 @@
42552
42706
  color: dimgrey;
42553
42707
  }
42554
42708
  .o-sidePanelClose {
42555
- font-size: 1.5rem;
42556
42709
  padding: 5px 10px;
42557
42710
  cursor: pointer;
42558
42711
  &:hover {
@@ -42737,7 +42890,6 @@
42737
42890
  justify-content: center;
42738
42891
  align-items: center;
42739
42892
  margin: 2px;
42740
- padding: 0px 3px;
42741
42893
  border-radius: 2px;
42742
42894
  min-width: 20px;
42743
42895
  }
@@ -42794,7 +42946,228 @@
42794
42946
  onClick: { type: Function, optional: true },
42795
42947
  };
42796
42948
 
42949
+ /**
42950
+ * List the available borders positions and the corresponding icons.
42951
+ * The structure of this array is defined to match the order/lines we want
42952
+ * to display in the topbar's border tool.
42953
+ */
42954
+ const BORDER_POSITIONS = [
42955
+ [
42956
+ ["all", "o-spreadsheet-Icon.BORDERS"],
42957
+ ["hv", "o-spreadsheet-Icon.BORDER_HV"],
42958
+ ["h", "o-spreadsheet-Icon.BORDER_H"],
42959
+ ["v", "o-spreadsheet-Icon.BORDER_V"],
42960
+ ["external", "o-spreadsheet-Icon.BORDER_EXTERNAL"],
42961
+ ],
42962
+ [
42963
+ ["left", "o-spreadsheet-Icon.BORDER_LEFT"],
42964
+ ["top", "o-spreadsheet-Icon.BORDER_TOP"],
42965
+ ["right", "o-spreadsheet-Icon.BORDER_RIGHT"],
42966
+ ["bottom", "o-spreadsheet-Icon.BORDER_BOTTOM"],
42967
+ ["clear", "o-spreadsheet-Icon.BORDER_CLEAR"],
42968
+ ],
42969
+ ];
42970
+ // -----------------------------------------------------------------------------
42971
+ // Border Editor
42972
+ // -----------------------------------------------------------------------------
42973
+ css /* scss */ `
42974
+ .o-border-selector {
42975
+ padding: 4px;
42976
+ }
42977
+ .o-divider {
42978
+ border-right: 1px solid #e0e2e4;
42979
+ margin: 0 6px;
42980
+ }
42981
+ .o-border-selector-section {
42982
+ .o-dropdown-line {
42983
+ height: 30px;
42984
+ margin: 1px;
42985
+ .o-line-item {
42986
+ padding: 4px;
42987
+ width: 18px;
42988
+ height: 18px;
42989
+ }
42990
+ }
42991
+ .o-border-style-tool {
42992
+ padding: 0px 3px;
42993
+ margin: 2px;
42994
+ height: 25px;
42995
+ }
42996
+ }
42997
+ .o-style-preview {
42998
+ margin: 7px 5px 7px 5px;
42999
+ width: 60px;
43000
+ height: 5px;
43001
+ }
43002
+ .o-style-thin {
43003
+ border-bottom: 1px solid #000000;
43004
+ }
43005
+ .o-style-medium {
43006
+ border-bottom: 2px solid #000000;
43007
+ }
43008
+ .o-style-thick {
43009
+ border-bottom: 3px solid #000000;
43010
+ }
43011
+ .o-style-dashed {
43012
+ border-bottom: 1px dashed #000000;
43013
+ }
43014
+ .o-style-dotted {
43015
+ border-bottom: 1px dotted #000000;
43016
+ }
43017
+ .o-dropdown-border-type {
43018
+ &:not(.o-disabled):not(.active):hover {
43019
+ background-color: ${BG_HOVER_COLOR};
43020
+ }
43021
+ }
43022
+ .o-dropdown-border-check {
43023
+ width: 20px;
43024
+ font-size: 12px;
43025
+ }
43026
+ .o-border-style-dropdown {
43027
+ background: #ffffff;
43028
+ padding: 4px;
43029
+ .o-dropdown-line {
43030
+ .o-line-item.active {
43031
+ background-color: rgba(0, 0, 0, 0.2);
43032
+ }
43033
+ }
43034
+ }
43035
+ .o-border-picker-button {
43036
+ padding: 0px !important;
43037
+ margin: 5px 0px 0px 0px !important;
43038
+ height: 25px !important;
43039
+ }
43040
+ `;
43041
+ class BorderEditor extends owl.Component {
43042
+ static template = "o-spreadsheet-BorderEditor";
43043
+ static components = { ColorPickerWidget, Popover };
43044
+ BORDER_POSITIONS = BORDER_POSITIONS;
43045
+ lineStyleButtonRef = owl.useRef("lineStyleButton");
43046
+ borderStyles = borderStyles;
43047
+ state = owl.useState({
43048
+ activeTool: undefined,
43049
+ });
43050
+ toggleDropdownTool(tool) {
43051
+ const isOpen = this.state.activeTool === tool;
43052
+ this.state.activeTool = isOpen ? undefined : tool;
43053
+ }
43054
+ closeDropdown() {
43055
+ this.state.activeTool = undefined;
43056
+ }
43057
+ setBorderPosition(position) {
43058
+ this.props.onBorderPositionPicked(position);
43059
+ this.closeDropdown();
43060
+ }
43061
+ setBorderColor(color) {
43062
+ this.props.onBorderColorPicked(color);
43063
+ this.closeDropdown();
43064
+ }
43065
+ setBorderStyle(style) {
43066
+ this.props.onBorderStylePicked(style);
43067
+ this.closeDropdown();
43068
+ }
43069
+ get lineStylePickerPopoverProps() {
43070
+ return {
43071
+ anchorRect: this.lineStylePickerAnchorRect,
43072
+ positioning: "BottomLeft",
43073
+ verticalOffset: 0,
43074
+ };
43075
+ }
43076
+ get popoverProps() {
43077
+ return {
43078
+ anchorRect: this.props.anchorRect,
43079
+ maxHeight: this.props.maxHeight,
43080
+ positioning: "BottomLeft",
43081
+ verticalOffset: 0,
43082
+ };
43083
+ }
43084
+ get lineStylePickerAnchorRect() {
43085
+ const button = this.lineStyleButtonRef.el;
43086
+ if (button === null) {
43087
+ return { x: 0, y: 0, width: 0, height: 0 };
43088
+ }
43089
+ const buttonRect = button.getBoundingClientRect();
43090
+ return {
43091
+ x: buttonRect.x,
43092
+ y: buttonRect.y,
43093
+ width: buttonRect.width,
43094
+ height: buttonRect.height,
43095
+ };
43096
+ }
43097
+ }
43098
+ BorderEditor.props = {
43099
+ class: { type: String, optional: true },
43100
+ currentBorderColor: { type: String, optional: false },
43101
+ currentBorderStyle: { type: String, optional: false },
43102
+ currentBorderPosition: { type: String, optional: true },
43103
+ onBorderColorPicked: Function,
43104
+ onBorderStylePicked: Function,
43105
+ onBorderPositionPicked: Function,
43106
+ maxHeight: { type: Number, optional: true },
43107
+ anchorRect: Object,
43108
+ };
43109
+
43110
+ class BorderEditorWidget extends owl.Component {
43111
+ static template = "o-spreadsheet-BorderEditorWidget";
43112
+ static components = { BorderEditor };
43113
+ borderEditorButtonRef = owl.useRef("borderEditorButton");
43114
+ state = owl.useState({
43115
+ currentColor: DEFAULT_BORDER_DESC.color,
43116
+ currentStyle: DEFAULT_BORDER_DESC.style,
43117
+ currentPosition: undefined,
43118
+ });
43119
+ get borderEditorAnchorRect() {
43120
+ const button = this.borderEditorButtonRef.el;
43121
+ const buttonRect = button.getBoundingClientRect();
43122
+ return {
43123
+ x: buttonRect.x,
43124
+ y: buttonRect.y,
43125
+ width: buttonRect.width,
43126
+ height: buttonRect.height,
43127
+ };
43128
+ }
43129
+ onBorderPositionPicked(position) {
43130
+ this.state.currentPosition = position;
43131
+ this.updateBorder();
43132
+ }
43133
+ onBorderColorPicked(color) {
43134
+ this.state.currentColor = color;
43135
+ this.updateBorder();
43136
+ }
43137
+ onBorderStylePicked(style) {
43138
+ this.state.currentStyle = style;
43139
+ this.updateBorder();
43140
+ }
43141
+ updateBorder() {
43142
+ if (this.state.currentPosition === undefined) {
43143
+ return;
43144
+ }
43145
+ this.env.model.dispatch("SET_ZONE_BORDERS", {
43146
+ sheetId: this.env.model.getters.getActiveSheetId(),
43147
+ target: this.env.model.getters.getSelectedZones(),
43148
+ border: {
43149
+ position: this.state.currentPosition,
43150
+ color: this.state.currentColor,
43151
+ style: this.state.currentStyle,
43152
+ },
43153
+ });
43154
+ }
43155
+ }
43156
+ BorderEditorWidget.props = {
43157
+ toggleBorderEditor: Function,
43158
+ showBorderEditor: Boolean,
43159
+ disabled: { type: Boolean, optional: true },
43160
+ dropdownMaxHeight: { type: Number, optional: true },
43161
+ class: { type: String, optional: true },
43162
+ };
43163
+
42797
43164
  const COMPOSER_MAX_HEIGHT = 100;
43165
+ /* svg free of use from https://uxwing.com/formula-fx-icon/ */
43166
+ const FX_SVG = /*xml*/ `
43167
+ <svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 121.8 122.9' width='16' height='16' focusable='false'>
43168
+ <path d='m28 34-4 5v2h10l-6 40c-4 22-6 28-7 30-2 2-3 3-5 3-3 0-7-2-9-4H4c-2 2-4 4-4 7s4 6 8 6 9-2 15-8c8-7 13-17 18-39l7-35 13-1 3-6H49c4-23 7-27 11-27 2 0 5 2 8 6h4c1-1 4-4 4-7 0-2-3-6-9-6-5 0-13 4-20 10-6 7-9 14-11 24h-8zm41 16c4-5 7-7 8-7s2 1 5 9l3 12c-7 11-12 17-16 17l-3-1-2-1c-3 0-6 3-6 7s3 7 7 7c6 0 12-6 22-23l3 10c3 9 6 13 10 13 5 0 11-4 18-15l-3-4c-4 6-7 8-8 8-2 0-4-3-6-10l-5-15 8-10 6-4 3 1 3 2c2 0 6-3 6-7s-2-7-6-7c-6 0-11 5-21 20l-2-6c-3-9-5-14-9-14-5 0-12 6-18 15l3 3z' fill='#BDBDBD'/>
43169
+ </svg>
43170
+ `;
42798
43171
  css /* scss */ `
42799
43172
  .o-topbar-composer {
42800
43173
  height: fit-content;
@@ -42802,9 +43175,10 @@
42802
43175
  border: 1px solid;
42803
43176
  z-index: ${ComponentsImportance.TopBarComposer};
42804
43177
 
42805
- .o-composer:empty:not(:focus)::before {
42806
- /* svg free of use from https://uxwing.com/formula-fx-icon/ */
42807
- content: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 -10 121.8 142.9' width='24' height='24' focusable='false'%3E%3Cpath d='m28 34-4 5v2h10l-6 40c-4 22-6 28-7 30-2 2-3 3-5 3-3 0-7-2-9-4H4c-2 2-4 4-4 7s4 6 8 6 9-2 15-8c8-7 13-17 18-39l7-35 13-1 3-6H49c4-23 7-27 11-27 2 0 5 2 8 6h4c1-1 4-4 4-7 0-2-3-6-9-6-5 0-13 4-20 10-6 7-9 14-11 24h-8zm41 16c4-5 7-7 8-7s2 1 5 9l3 12c-7 11-12 17-16 17l-3-1-2-1c-3 0-6 3-6 7s3 7 7 7c6 0 12-6 22-23l3 10c3 9 6 13 10 13 5 0 11-4 18-15l-3-4c-4 6-7 8-8 8-2 0-4-3-6-10l-5-15 8-10 6-4 3 1 3 2c2 0 6-3 6-7s-2-7-6-7c-6 0-11 5-21 20l-2-6c-3-9-5-14-9-14-5 0-12 6-18 15l3 3z' fill='%23BDBDBD' /%3E%3C/svg%3E");
43178
+ .o-composer:empty:not(:focus):not(.active)::before {
43179
+ content: url("data:image/svg+xml,${encodeURIComponent(FX_SVG)}");
43180
+ position: relative;
43181
+ top: 20%;
42808
43182
  }
42809
43183
  }
42810
43184
  `;
@@ -42927,11 +43301,6 @@
42927
43301
  dropdownStyle: String,
42928
43302
  };
42929
43303
 
42930
- // If we ever change these colors, make sure the filter tool stays green to match the icon in the grid
42931
- const ACTIVE_BG_COLOR = BACKGROUND_HEADER_FILTER_COLOR;
42932
- const ACTIVE_FONT_COLOR = FILTERS_COLOR;
42933
- const HOVERED_BG_COLOR = BG_HOVER_COLOR;
42934
- const HOVERED_FONT_COLOR = "#000";
42935
43304
  // -----------------------------------------------------------------------------
42936
43305
  // TopBar
42937
43306
  // -----------------------------------------------------------------------------
@@ -42941,27 +43310,6 @@
42941
43310
  font-size: 13px;
42942
43311
  font-weight: 500;
42943
43312
 
42944
- .o-topbar-hoverable {
42945
- cursor: pointer;
42946
- .o-icon {
42947
- color: ${ICONS_COLOR};
42948
- }
42949
- &:not(.o-disabled):not(.active):hover {
42950
- background-color: ${HOVERED_BG_COLOR};
42951
- color: ${HOVERED_FONT_COLOR};
42952
- .o-icon {
42953
- color: ${HOVERED_FONT_COLOR};
42954
- }
42955
- }
42956
- &.active {
42957
- background-color: ${ACTIVE_BG_COLOR};
42958
- color: ${ACTIVE_FONT_COLOR};
42959
- .o-icon {
42960
- color: ${ACTIVE_FONT_COLOR};
42961
- }
42962
- }
42963
- }
42964
-
42965
43313
  .o-topbar-top {
42966
43314
  border-bottom: 1px solid ${SEPARATOR_COLOR};
42967
43315
  padding: 2px 10px;
@@ -42971,6 +43319,11 @@
42971
43319
  .o-topbar-menu {
42972
43320
  padding: 4px 6px;
42973
43321
  margin: 0 2px;
43322
+
43323
+ &.active {
43324
+ background-color: ${BG_HOVER_COLOR};
43325
+ color: #000;
43326
+ }
42974
43327
  }
42975
43328
  }
42976
43329
  }
@@ -43048,6 +43401,7 @@
43048
43401
  TopBarComposer,
43049
43402
  FontSizeEditor,
43050
43403
  ActionButton,
43404
+ BorderEditorWidget,
43051
43405
  };
43052
43406
  state = owl.useState({
43053
43407
  menuState: { isOpen: false, position: null, menuItems: [] },
@@ -43123,7 +43477,9 @@
43123
43477
  this.state.activeTool = "";
43124
43478
  this.state.menuState.isOpen = true;
43125
43479
  this.state.menuState.position = { x: left, y: top + height };
43126
- this.state.menuState.menuItems = menu.children(this.env);
43480
+ this.state.menuState.menuItems = menu
43481
+ .children(this.env)
43482
+ .sort((a, b) => a.sequence - b.sequence);
43127
43483
  this.state.menuState.parentMenu = menu;
43128
43484
  this.isSelectingMenu = true;
43129
43485
  this.openedEl = ev.target;
@@ -43209,6 +43565,11 @@
43209
43565
  }
43210
43566
  }
43211
43567
 
43568
+ // If we ever change these colors, make sure the filter tool stays green to match the icon in the grid
43569
+ const ACTIVE_BG_COLOR = BACKGROUND_HEADER_FILTER_COLOR;
43570
+ const ACTIVE_FONT_COLOR = FILTERS_COLOR;
43571
+ const HOVERED_BG_COLOR = BG_HOVER_COLOR;
43572
+ const HOVERED_FONT_COLOR = "#000";
43212
43573
  css /* scss */ `
43213
43574
  .o-spreadsheet {
43214
43575
  position: relative;
@@ -43241,6 +43602,27 @@
43241
43602
  margin-top: ${MENU_SEPARATOR_PADDING}px;
43242
43603
  margin-bottom: ${MENU_SEPARATOR_PADDING}px;
43243
43604
  }
43605
+ .o-hoverable-button {
43606
+ border-radius: 2px;
43607
+ cursor: pointer;
43608
+ .o-icon {
43609
+ color: ${ICONS_COLOR};
43610
+ }
43611
+ &:not(.o-disabled):not(.active):hover {
43612
+ background-color: ${HOVERED_BG_COLOR};
43613
+ color: ${HOVERED_FONT_COLOR};
43614
+ .o-icon {
43615
+ color: ${HOVERED_FONT_COLOR};
43616
+ }
43617
+ }
43618
+ &.active {
43619
+ background-color: ${ACTIVE_BG_COLOR};
43620
+ color: ${ACTIVE_FONT_COLOR};
43621
+ .o-icon {
43622
+ color: ${ACTIVE_FONT_COLOR};
43623
+ }
43624
+ }
43625
+ }
43244
43626
  }
43245
43627
 
43246
43628
  .o-two-columns {
@@ -43390,6 +43772,9 @@
43390
43772
  }
43391
43773
  }
43392
43774
  openSidePanel(panel, panelProps) {
43775
+ if (this.sidePanel.isOpen && panel !== this.sidePanel.component) {
43776
+ this.sidePanel.panelProps?.onCloseSidePanel?.();
43777
+ }
43393
43778
  this.sidePanel.component = panel;
43394
43779
  this.sidePanel.panelProps = panelProps;
43395
43780
  this.sidePanel.isOpen = true;
@@ -44099,13 +44484,13 @@
44099
44484
  }
44100
44485
 
44101
44486
  class SelectiveHistory {
44487
+ HEAD_BRANCH;
44488
+ HEAD_OPERATION;
44489
+ tree;
44102
44490
  applyOperation;
44103
44491
  revertOperation;
44104
44492
  buildEmpty;
44105
44493
  buildTransformation;
44106
- HEAD_BRANCH;
44107
- HEAD_OPERATION;
44108
- tree;
44109
44494
  /**
44110
44495
  * The selective history is a data structure used to register changes/updates of a state.
44111
44496
  * Each change/update is called an "operation".
@@ -44126,14 +44511,15 @@
44126
44511
  * (used for internal implementation)
44127
44512
  * @param buildTransformation Factory used to build transformations
44128
44513
  */
44129
- constructor(initialOperationId, applyOperation, revertOperation, buildEmpty, buildTransformation) {
44130
- this.applyOperation = applyOperation;
44131
- this.revertOperation = revertOperation;
44132
- this.buildEmpty = buildEmpty;
44133
- this.buildTransformation = buildTransformation;
44514
+ constructor(args) {
44515
+ this.applyOperation = args.applyOperation;
44516
+ this.revertOperation = args.revertOperation;
44517
+ this.buildEmpty = args.buildEmpty;
44518
+ this.buildTransformation = args.buildTransformation;
44134
44519
  this.HEAD_BRANCH = new Branch(this.buildTransformation);
44135
- this.tree = new Tree(buildTransformation, this.HEAD_BRANCH);
44136
- const initial = new Operation(initialOperationId, buildEmpty(initialOperationId));
44520
+ this.tree = new Tree(this.buildTransformation, this.HEAD_BRANCH);
44521
+ const initialOperationId = args.initialOperationId;
44522
+ const initial = new Operation(initialOperationId, this.buildEmpty(initialOperationId));
44137
44523
  this.tree.insertOperationLast(this.HEAD_BRANCH, initial);
44138
44524
  this.HEAD_OPERATION = initial;
44139
44525
  }
@@ -44247,21 +44633,27 @@
44247
44633
  }
44248
44634
  }
44249
44635
 
44250
- function buildRevisionLog(initialRevisionId, recordChanges, dispatch) {
44251
- return new SelectiveHistory(initialRevisionId, (revision) => {
44252
- const commands = revision.commands.slice();
44253
- const { changes } = recordChanges(() => {
44254
- for (const command of commands) {
44255
- dispatch(command);
44256
- }
44257
- });
44258
- revision.setChanges(changes);
44259
- }, (revision) => revertChanges([revision]), (id) => new Revision(id, "empty", []), {
44260
- with: (revision) => (toTransform) => {
44261
- return new Revision(toTransform.id, toTransform.clientId, transformAll(toTransform.commands, revision.commands), toTransform.rootCommand);
44636
+ function buildRevisionLog(args) {
44637
+ return new SelectiveHistory({
44638
+ initialOperationId: args.initialRevisionId,
44639
+ applyOperation: (revision) => {
44640
+ const commands = revision.commands.slice();
44641
+ const { changes } = args.recordChanges(() => {
44642
+ for (const command of commands) {
44643
+ args.dispatch(command);
44644
+ }
44645
+ });
44646
+ revision.setChanges(changes);
44262
44647
  },
44263
- without: (revision) => (toTransform) => {
44264
- return new Revision(toTransform.id, toTransform.clientId, transformAll(toTransform.commands, revision.commands.map(inverseCommand).flat()), toTransform.rootCommand);
44648
+ revertOperation: (revision) => revertChanges([revision]),
44649
+ buildEmpty: (id) => new Revision(id, "empty", []),
44650
+ buildTransformation: {
44651
+ with: (revision) => (toTransform) => {
44652
+ return new Revision(toTransform.id, toTransform.clientId, transformAll(toTransform.commands, revision.commands), toTransform.rootCommand, undefined, toTransform.timestamp);
44653
+ },
44654
+ without: (revision) => (toTransform) => {
44655
+ return new Revision(toTransform.id, toTransform.clientId, transformAll(toTransform.commands, revision.commands.map(inverseCommand).flat()), toTransform.rootCommand, undefined, toTransform.timestamp);
44656
+ },
44265
44657
  },
44266
44658
  });
44267
44659
  }
@@ -45934,11 +46326,21 @@
45934
46326
  for (let border of Object.values(borders)) {
45935
46327
  borderNodes.push(escapeXml /*xml*/ `
45936
46328
  <border>
45937
- <left ${formatBorderAttribute(border["left"])} />
45938
- <right ${formatBorderAttribute(border["right"])} />
45939
- <top ${formatBorderAttribute(border["top"])} />
45940
- <bottom ${formatBorderAttribute(border["bottom"])} />
45941
- <diagonal ${formatBorderAttribute(border["diagonal"])} />
46329
+ <left ${formatBorderAttribute(border["left"])}>
46330
+ ${addBorderColor(border["left"])}
46331
+ </left>
46332
+ <right ${formatBorderAttribute(border["right"])}>
46333
+ ${addBorderColor(border["right"])}
46334
+ </right>
46335
+ <top ${formatBorderAttribute(border["top"])}>
46336
+ ${addBorderColor(border["top"])}
46337
+ </top>
46338
+ <bottom ${formatBorderAttribute(border["bottom"])}>
46339
+ ${addBorderColor(border["bottom"])}
46340
+ </bottom>
46341
+ <diagonal ${formatBorderAttribute(border["diagonal"])}>
46342
+ ${addBorderColor(border["diagonal"])}
46343
+ </diagonal>
45942
46344
  </border>
45943
46345
  `);
45944
46346
  }
@@ -45952,10 +46354,15 @@
45952
46354
  if (!description) {
45953
46355
  return escapeXml ``;
45954
46356
  }
45955
- return formatAttributes([
45956
- ["style", description.style],
45957
- ["color", toXlsxHexColor(description.color.rgb)],
45958
- ]);
46357
+ return formatAttributes([["style", description.style]]);
46358
+ }
46359
+ function addBorderColor(description) {
46360
+ if (!description) {
46361
+ return escapeXml ``;
46362
+ }
46363
+ return escapeXml /*xml*/ `
46364
+ <color ${formatAttributes([["rgb", toXlsxHexColor(description.color.rgb)]])}/>
46365
+ `;
45959
46366
  }
45960
46367
  function addStyles(styles) {
45961
46368
  const styleNodes = [];
@@ -46599,6 +47006,7 @@
46599
47006
  this.setupCorePlugin(Plugin, workbookData);
46600
47007
  }
46601
47008
  Object.assign(this.getters, this.coreGetters);
47009
+ this.session.loadInitialMessages(stateUpdateMessages);
46602
47010
  for (let Plugin of statefulUIPluginRegistry.getAll()) {
46603
47011
  const plugin = this.setupUiPlugin(Plugin);
46604
47012
  this.statefulUIPlugins.push(plugin);
@@ -46626,8 +47034,6 @@
46626
47034
  // This should be done after construction of LocalHistory due to order of
46627
47035
  // events
46628
47036
  this.setupSessionEvents();
46629
- // Load the initial revisions
46630
- this.session.loadInitialMessages(stateUpdateMessages);
46631
47037
  this.joinSession();
46632
47038
  if (config.snapshotRequested) {
46633
47039
  this.session.snapshot(this.exportData());
@@ -46691,14 +47097,18 @@
46691
47097
  this.finalize();
46692
47098
  }
46693
47099
  setupSession(revisionId) {
46694
- const session = new Session(buildRevisionLog(revisionId, this.state.recordChanges.bind(this.state), (command) => {
46695
- const result = this.checkDispatchAllowed(command);
46696
- if (!result.isSuccessful) {
46697
- return;
46698
- }
46699
- this.isReplayingCommand = true;
46700
- this.dispatchToHandlers(this.coreHandlers, command);
46701
- this.isReplayingCommand = false;
47100
+ const session = new Session(buildRevisionLog({
47101
+ initialRevisionId: revisionId,
47102
+ recordChanges: this.state.recordChanges.bind(this.state),
47103
+ dispatch: (command) => {
47104
+ const result = this.checkDispatchAllowed(command);
47105
+ if (!result.isSuccessful) {
47106
+ return;
47107
+ }
47108
+ this.isReplayingCommand = true;
47109
+ this.dispatchToHandlers(this.coreHandlers, command);
47110
+ this.isReplayingCommand = false;
47111
+ },
46702
47112
  }), this.config.transportService, revisionId);
46703
47113
  return session;
46704
47114
  }
@@ -47099,9 +47509,9 @@
47099
47509
  Object.defineProperty(exports, '__esModule', { value: true });
47100
47510
 
47101
47511
 
47102
- __info__.version = '16.3.0-alpha.7';
47103
- __info__.date = '2023-05-02T13:08:09.275Z';
47104
- __info__.hash = '77c8f5f';
47512
+ __info__.version = '16.3.0-alpha.8';
47513
+ __info__.date = '2023-05-12T10:59:42.550Z';
47514
+ __info__.hash = 'afee7d5';
47105
47515
 
47106
47516
 
47107
47517
  })(this.o_spreadsheet = this.o_spreadsheet || {}, owl);