@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.
@@ -240,8 +240,8 @@ const HEADER_FONT_SIZE = 11;
240
240
  const DEFAULT_FONT = "'Roboto', arial";
241
241
  const DEFAULT_VERTICAL_ALIGN = "bottom";
242
242
  // Borders
243
- const DEFAULT_BORDER_DESC = ["thin", "#000"];
244
- const DEFAULT_FILTER_BORDER_DESC = ["thin", FILTERS_COLOR];
243
+ const DEFAULT_BORDER_DESC = { style: "thin", color: "#000000" };
244
+ const DEFAULT_FILTER_BORDER_DESC = { style: "thin", color: FILTERS_COLOR };
245
245
  // Ranges
246
246
  const INCORRECT_RANGE_STRING = CellErrorType.InvalidReference;
247
247
  // Max Number of history steps kept in memory
@@ -1105,6 +1105,18 @@ function replaceSpecialSpaces(text) {
1105
1105
  text = text.replace(whiteSpaceRegexp, " ");
1106
1106
  text = text.replace(newLineRegex, NEWLINE);
1107
1107
  return text;
1108
+ }
1109
+ /**
1110
+ * Determine if the numbers are consecutive.
1111
+ */
1112
+ function isConsecutive(iterable) {
1113
+ const array = Array.from(iterable).sort((a, b) => a - b); // sort numerically rather than lexicographically
1114
+ for (let i = 1; i < array.length; i++) {
1115
+ if (array[i] - array[i - 1] !== 1) {
1116
+ return false;
1117
+ }
1118
+ }
1119
+ return true;
1108
1120
  }
1109
1121
 
1110
1122
  const RBA_REGEX = /rgba?\(|\s+|\)/gi;
@@ -4247,6 +4259,7 @@ const coreTypes = new Set([
4247
4259
  "SET_FORMATTING",
4248
4260
  "CLEAR_FORMATTING",
4249
4261
  "SET_BORDER",
4262
+ "SET_ZONE_BORDERS",
4250
4263
  /** CHART */
4251
4264
  "CREATE_CHART",
4252
4265
  "UPDATE_CHART",
@@ -4389,6 +4402,7 @@ exports.CommandResult = void 0;
4389
4402
  CommandResult[CommandResult["NoSplitSeparatorInSelection"] = 91] = "NoSplitSeparatorInSelection";
4390
4403
  })(exports.CommandResult || (exports.CommandResult = {}));
4391
4404
 
4405
+ const borderStyles = ["thin", "medium", "thick", "dashed", "dotted"];
4392
4406
  var DIRECTION;
4393
4407
  (function (DIRECTION) {
4394
4408
  DIRECTION["UP"] = "up";
@@ -5482,12 +5496,6 @@ css /* scss */ `
5482
5496
  cursor: pointer;
5483
5497
  user-select: none;
5484
5498
 
5485
- .o-menu-item-name {
5486
- overflow: hidden;
5487
- white-space: nowrap;
5488
- text-overflow: ellipsis;
5489
- }
5490
-
5491
5499
  &.o-menu-root {
5492
5500
  display: flex;
5493
5501
  justify-content: space-between;
@@ -5498,10 +5506,6 @@ css /* scss */ `
5498
5506
  margin: 0px 8px 0px 0px;
5499
5507
  width: ${MENU_ITEM_HEIGHT - 2 * MENU_ITEM_PADDING_VERTICAL}px;
5500
5508
  line-height: ${MENU_ITEM_HEIGHT - 2 * MENU_ITEM_PADDING_VERTICAL}px;
5501
-
5502
- .o-icon {
5503
- color: ${ICONS_COLOR};
5504
- }
5505
5509
  }
5506
5510
  .o-menu-item-root {
5507
5511
  width: 10px;
@@ -5515,6 +5519,11 @@ css /* scss */ `
5515
5519
  .o-menu-item-description {
5516
5520
  color: grey;
5517
5521
  }
5522
+ .o-menu-item-icon {
5523
+ .o-icon {
5524
+ color: ${ICONS_COLOR};
5525
+ }
5526
+ }
5518
5527
  }
5519
5528
  &.disabled {
5520
5529
  color: ${MENU_ITEM_DISABLED_COLOR};
@@ -6331,8 +6340,14 @@ function createDataSets(getters, dataSetsString, sheetId, dataSetsHaveTitle) {
6331
6340
  : undefined));
6332
6341
  }
6333
6342
  }
6343
+ else if (zone.left === zone.right && zone.top === zone.bottom) {
6344
+ // A single cell. If it's only the title, the dataset is not added.
6345
+ if (!dataSetsHaveTitle) {
6346
+ dataSets.push(createDataSet(getters, dataSetSheetId, zone, undefined));
6347
+ }
6348
+ }
6334
6349
  else {
6335
- /* 1 cell, 1 row or 1 column */
6350
+ /* 1 row or 1 column */
6336
6351
  dataSets.push(createDataSet(getters, dataSetSheetId, zone, dataSetsHaveTitle
6337
6352
  ? {
6338
6353
  top: zone.top,
@@ -8943,7 +8958,7 @@ const DELETE_CELL_SHIFT_LEFT = (env) => {
8943
8958
  handlePasteResult(env, result);
8944
8959
  };
8945
8960
  const MENU_INSERT_ROWS_NAME = (env) => {
8946
- const number = getColumnsNumber(env);
8961
+ const number = getRowsNumber(env);
8947
8962
  return number === 1 ? _lt("Insert row") : _lt("Insert %s rows", number.toString());
8948
8963
  };
8949
8964
  const MENU_INSERT_ROWS_BEFORE_NAME = (env) => {
@@ -15308,7 +15323,11 @@ const LOOKUP = {
15308
15323
  : getNormalizedValueFromRowRange;
15309
15324
  const rangeLength = verticalSearch ? nbRow : nbCol;
15310
15325
  const index = dichotomicSearch(searchArray, _searchKey, "nextSmaller", "asc", rangeLength, getElement);
15311
- assertAvailable(searchArray[0][index], searchKey);
15326
+ if (index === -1)
15327
+ assertAvailable(undefined, searchKey);
15328
+ verticalSearch
15329
+ ? assertAvailable(searchArray[0][index], searchKey)
15330
+ : assertAvailable(searchArray[index][nbRow - 1], searchKey);
15312
15331
  if (resultRange === undefined) {
15313
15332
  return (verticalSearch ? searchArray[nbCol - 1][index] : searchArray[index][nbRow - 1]);
15314
15333
  }
@@ -16215,75 +16234,97 @@ for (let category of categories) {
16215
16234
 
16216
16235
  const insertRow = {
16217
16236
  name: MENU_INSERT_ROWS_NAME,
16218
- isVisible: IS_ONLY_ONE_RANGE,
16237
+ isVisible: (env) => isConsecutive(env.model.getters.getActiveRows()) &&
16238
+ IS_ONLY_ONE_RANGE(env) &&
16239
+ env.model.getters.getActiveCols().size === 0,
16219
16240
  icon: "o-spreadsheet-Icon.INSERT_ROW",
16220
16241
  };
16221
16242
  const rowInsertRowBefore = {
16222
16243
  name: ROW_INSERT_ROWS_BEFORE_NAME,
16223
16244
  execute: INSERT_ROWS_BEFORE_ACTION,
16245
+ isVisible: (env) => isConsecutive(env.model.getters.getActiveRows()) &&
16246
+ IS_ONLY_ONE_RANGE(env) &&
16247
+ env.model.getters.getActiveCols().size === 0,
16248
+ icon: "o-spreadsheet-Icon.INSERT_ROW_BEFORE",
16224
16249
  };
16225
16250
  const topBarInsertRowsBefore = {
16226
16251
  ...rowInsertRowBefore,
16227
16252
  name: MENU_INSERT_ROWS_BEFORE_NAME,
16228
- isVisible: (env) => env.model.getters.getActiveCols().size === 0,
16229
16253
  };
16230
16254
  const cellInsertRowsBefore = {
16231
16255
  ...rowInsertRowBefore,
16232
16256
  name: CELL_INSERT_ROWS_BEFORE_NAME,
16233
16257
  isVisible: IS_ONLY_ONE_RANGE,
16234
- icon: "o-spreadsheet-Icon.INSERT_ROW",
16258
+ icon: "o-spreadsheet-Icon.INSERT_ROW_BEFORE",
16235
16259
  };
16236
16260
  const rowInsertRowsAfter = {
16237
16261
  execute: INSERT_ROWS_AFTER_ACTION,
16238
16262
  name: ROW_INSERT_ROWS_AFTER_NAME,
16263
+ isVisible: (env) => isConsecutive(env.model.getters.getActiveRows()) &&
16264
+ IS_ONLY_ONE_RANGE(env) &&
16265
+ env.model.getters.getActiveCols().size === 0,
16266
+ icon: "o-spreadsheet-Icon.INSERT_ROW_AFTER",
16239
16267
  };
16240
16268
  const topBarInsertRowsAfter = {
16241
16269
  ...rowInsertRowsAfter,
16242
16270
  name: MENU_INSERT_ROWS_AFTER_NAME,
16243
- isVisible: (env) => env.model.getters.getActiveCols().size === 0,
16244
16271
  };
16245
16272
  const insertCol = {
16246
16273
  name: MENU_INSERT_COLUMNS_NAME,
16247
- isVisible: IS_ONLY_ONE_RANGE,
16274
+ isVisible: (env) => isConsecutive(env.model.getters.getActiveCols()) &&
16275
+ IS_ONLY_ONE_RANGE(env) &&
16276
+ env.model.getters.getActiveRows().size === 0,
16248
16277
  icon: "o-spreadsheet-Icon.INSERT_COL",
16249
16278
  };
16250
16279
  const colInsertColsBefore = {
16251
16280
  name: COLUMN_INSERT_COLUMNS_BEFORE_NAME,
16252
16281
  execute: INSERT_COLUMNS_BEFORE_ACTION,
16282
+ isVisible: (env) => isConsecutive(env.model.getters.getActiveCols()) &&
16283
+ IS_ONLY_ONE_RANGE(env) &&
16284
+ env.model.getters.getActiveRows().size === 0,
16285
+ icon: "o-spreadsheet-Icon.INSERT_COL_BEFORE",
16253
16286
  };
16254
16287
  const topBarInsertColsBefore = {
16255
16288
  ...colInsertColsBefore,
16256
16289
  name: MENU_INSERT_COLUMNS_BEFORE_NAME,
16257
- isVisible: (env) => env.model.getters.getActiveRows().size === 0,
16258
16290
  };
16259
16291
  const cellInsertColsBefore = {
16260
16292
  ...colInsertColsBefore,
16261
16293
  name: CELL_INSERT_COLUMNS_BEFORE_NAME,
16262
16294
  isVisible: IS_ONLY_ONE_RANGE,
16263
- icon: "o-spreadsheet-Icon.INSERT_COL",
16295
+ icon: "o-spreadsheet-Icon.INSERT_COL_BEFORE",
16264
16296
  };
16265
16297
  const colInsertColsAfter = {
16266
16298
  name: COLUMN_INSERT_COLUMNS_AFTER_NAME,
16267
16299
  execute: INSERT_COLUMNS_AFTER_ACTION,
16300
+ isVisible: (env) => isConsecutive(env.model.getters.getActiveCols()) &&
16301
+ IS_ONLY_ONE_RANGE(env) &&
16302
+ env.model.getters.getActiveRows().size === 0,
16303
+ icon: "o-spreadsheet-Icon.INSERT_COL_AFTER",
16268
16304
  };
16269
16305
  const topBarInsertColsAfter = {
16270
16306
  ...colInsertColsAfter,
16271
16307
  name: MENU_INSERT_COLUMNS_AFTER_NAME,
16272
16308
  execute: INSERT_COLUMNS_AFTER_ACTION,
16273
- isVisible: (env) => env.model.getters.getActiveRows().size === 0,
16274
16309
  };
16275
16310
  const insertCell = {
16276
16311
  name: _lt("Insert cells"),
16277
- isVisible: IS_ONLY_ONE_RANGE,
16312
+ isVisible: (env) => IS_ONLY_ONE_RANGE(env) &&
16313
+ env.model.getters.getActiveCols().size === 0 &&
16314
+ env.model.getters.getActiveRows().size === 0,
16278
16315
  icon: "o-spreadsheet-Icon.INSERT_CELL",
16279
16316
  };
16280
16317
  const insertCellShiftDown = {
16281
16318
  name: _lt("Insert cells and shift down"),
16282
16319
  execute: INSERT_CELL_SHIFT_DOWN,
16320
+ isVisible: (env) => env.model.getters.getActiveRows().size === 0 && env.model.getters.getActiveCols().size === 0,
16321
+ icon: "o-spreadsheet-Icon.INSERT_CELL_SHIFT_DOWN",
16283
16322
  };
16284
16323
  const insertCellShiftRight = {
16285
16324
  name: _lt("Insert cells and shift right"),
16286
16325
  execute: INSERT_CELL_SHIFT_RIGHT,
16326
+ isVisible: (env) => env.model.getters.getActiveRows().size === 0 && env.model.getters.getActiveCols().size === 0,
16327
+ icon: "o-spreadsheet-Icon.INSERT_CELL_SHIFT_RIGHT",
16287
16328
  };
16288
16329
  const insertChart = {
16289
16330
  name: _lt("Chart"),
@@ -16481,6 +16522,7 @@ const splitToColumns = {
16481
16522
  sequence: 1,
16482
16523
  execute: (env) => env.openSidePanel("SplitToColumns", {}),
16483
16524
  isEnabled: (env) => env.model.getters.isSingleColSelected(),
16525
+ icon: "o-spreadsheet-Icon.SPLIT_TEXT",
16484
16526
  };
16485
16527
 
16486
16528
  const formatNumberAutomatic = {
@@ -16696,60 +16738,6 @@ const clearFormat = {
16696
16738
  execute: FORMAT_CLEARFORMAT_ACTION,
16697
16739
  icon: "o-spreadsheet-Icon.CLEAR_FORMAT",
16698
16740
  };
16699
- const borders = {
16700
- name: _lt("Borders"),
16701
- icon: "o-spreadsheet-Icon.BORDERS",
16702
- };
16703
- const bordersAll = {
16704
- name: _lt("All borders"),
16705
- execute: (env) => setBorder(env, "all"),
16706
- icon: "o-spreadsheet-Icon.BORDERS",
16707
- };
16708
- const bordersInner = {
16709
- name: _lt("Inner borders"),
16710
- execute: (env) => setBorder(env, "hv"),
16711
- icon: "o-spreadsheet-Icon.BORDER_HV",
16712
- };
16713
- const bordersHorizontal = {
16714
- name: _lt("Horizontal borders"),
16715
- execute: (env) => setBorder(env, "h"),
16716
- icon: "o-spreadsheet-Icon.BORDER_H",
16717
- };
16718
- const bordersVertical = {
16719
- name: _lt("Vertical borders"),
16720
- execute: (env) => setBorder(env, "v"),
16721
- icon: "o-spreadsheet-Icon.BORDER_V",
16722
- };
16723
- const bordersExternal = {
16724
- name: _lt("External borders"),
16725
- execute: (env) => setBorder(env, "external"),
16726
- icon: "o-spreadsheet-Icon.BORDER_EXTERNAL",
16727
- };
16728
- const bordersLeft = {
16729
- name: _lt("Left borders"),
16730
- execute: (env) => setBorder(env, "left"),
16731
- icon: "o-spreadsheet-Icon.BORDER_LEFT",
16732
- };
16733
- const bordersTop = {
16734
- name: _lt("Top borders"),
16735
- execute: (env) => setBorder(env, "top"),
16736
- icon: "o-spreadsheet-Icon.BORDER_TOP",
16737
- };
16738
- const bordersRight = {
16739
- name: _lt("Right borders"),
16740
- execute: (env) => setBorder(env, "right"),
16741
- icon: "o-spreadsheet-Icon.BORDER_RIGHT",
16742
- };
16743
- const bordersBottom = {
16744
- name: _lt("Bottom borders"),
16745
- execute: (env) => setBorder(env, "bottom"),
16746
- icon: "o-spreadsheet-Icon.BORDER_BOTTOM",
16747
- };
16748
- const bordersClear = {
16749
- name: _lt("Clear borders"),
16750
- execute: (env) => setBorder(env, "clear"),
16751
- icon: "o-spreadsheet-Icon.BORDER_CLEAR",
16752
- };
16753
16741
  function fontSizeMenuBuilder() {
16754
16742
  return FONT_SIZES.map((fs) => {
16755
16743
  return {
@@ -16762,24 +16750,17 @@ function fontSizeMenuBuilder() {
16762
16750
  });
16763
16751
  }
16764
16752
  function isAutomaticFormatSelected(env) {
16765
- const activeCell = env.model.getters.getActiveCell();
16753
+ const activeCell = env.model.getters.getCell(env.model.getters.getActivePosition());
16766
16754
  return !activeCell || !activeCell.format;
16767
16755
  }
16768
16756
  function isFormatSelected(env, format) {
16769
- const activeCell = env.model.getters.getActiveCell();
16770
- return activeCell && activeCell.format === format;
16757
+ const activeCell = env.model.getters.getCell(env.model.getters.getActivePosition());
16758
+ return activeCell?.format === format;
16771
16759
  }
16772
16760
  function isFontSizeSelected(env, fontSize) {
16773
16761
  const currentFontSize = env.model.getters.getCurrentStyle().fontSize || DEFAULT_FONT_SIZE;
16774
16762
  return currentFontSize === fontSize;
16775
16763
  }
16776
- function setBorder(env, command) {
16777
- env.model.dispatch("SET_FORMATTING", {
16778
- sheetId: env.model.getters.getActiveSheetId(),
16779
- target: env.model.getters.getSelectedZones(),
16780
- border: command,
16781
- });
16782
- }
16783
16764
  function getHorizontalAlign(env) {
16784
16765
  const style = env.model.getters.getCurrentStyle();
16785
16766
  if (style.align) {
@@ -16826,18 +16807,7 @@ var ACTION_FORMAT = /*#__PURE__*/Object.freeze({
16826
16807
  fillColor: fillColor,
16827
16808
  formatCF: formatCF,
16828
16809
  paintFormat: paintFormat,
16829
- clearFormat: clearFormat,
16830
- borders: borders,
16831
- bordersAll: bordersAll,
16832
- bordersInner: bordersInner,
16833
- bordersHorizontal: bordersHorizontal,
16834
- bordersVertical: bordersVertical,
16835
- bordersExternal: bordersExternal,
16836
- bordersLeft: bordersLeft,
16837
- bordersTop: bordersTop,
16838
- bordersRight: bordersRight,
16839
- bordersBottom: bordersBottom,
16840
- bordersClear: bordersClear
16810
+ clearFormat: clearFormat
16841
16811
  });
16842
16812
 
16843
16813
  function interactiveFreezeColumnsRows(env, dimension, base) {
@@ -16853,6 +16823,7 @@ const hideCols = {
16853
16823
  name: HIDE_COLUMNS_NAME,
16854
16824
  execute: HIDE_COLUMNS_ACTION,
16855
16825
  isVisible: NOT_ALL_VISIBLE_COLS_SELECTED,
16826
+ icon: "o-spreadsheet-Icon.HIDE_COL",
16856
16827
  };
16857
16828
  const unhideCols = {
16858
16829
  name: _lt("Unhide columns"),
@@ -16874,6 +16845,7 @@ const hideRows = {
16874
16845
  name: HIDE_ROWS_NAME,
16875
16846
  execute: HIDE_ROWS_ACTION,
16876
16847
  isVisible: NOT_ALL_VISIBLE_ROWS_SELECTED,
16848
+ icon: "o-spreadsheet-Icon.HIDE_ROW",
16877
16849
  };
16878
16850
  const unhideRows = {
16879
16851
  name: _lt("Unhide rows"),
@@ -17077,19 +17049,21 @@ colMenuRegistry
17077
17049
  .add("delete_column", {
17078
17050
  ...deleteCols,
17079
17051
  sequence: 90,
17052
+ icon: "o-spreadsheet-Icon.DELETE",
17080
17053
  })
17081
17054
  .add("clear_column", {
17082
17055
  ...clearCols,
17083
17056
  sequence: 100,
17057
+ icon: "o-spreadsheet-Icon.CLEAR",
17084
17058
  })
17085
17059
  .add("hide_columns", {
17086
17060
  ...hideCols,
17087
- sequence: 85,
17061
+ sequence: 105,
17088
17062
  separator: true,
17089
17063
  })
17090
17064
  .add("unhide_columns", {
17091
17065
  ...unhideCols,
17092
- sequence: 86,
17066
+ sequence: 106,
17093
17067
  separator: true,
17094
17068
  })
17095
17069
  .add("conditional_formatting", {
@@ -17187,10 +17161,12 @@ rowMenuRegistry
17187
17161
  .add("delete_row", {
17188
17162
  ...deleteRows,
17189
17163
  sequence: 70,
17164
+ icon: "o-spreadsheet-Icon.DELETE",
17190
17165
  })
17191
17166
  .add("clear_row", {
17192
17167
  ...clearRows,
17193
17168
  sequence: 80,
17169
+ icon: "o-spreadsheet-Icon.CLEAR",
17194
17170
  })
17195
17171
  .add("hide_rows", {
17196
17172
  ...hideRows,
@@ -17683,6 +17659,12 @@ css /* scss */ `
17683
17659
  height: 25px;
17684
17660
  }
17685
17661
  }
17662
+ /** Make the character a bit bigger
17663
+ compared to its neighbor INPUT box */
17664
+ .o-remove-selection {
17665
+ font-weight: bold;
17666
+ font-size: calc(100% + 4px);
17667
+ }
17686
17668
  }
17687
17669
  `;
17688
17670
  /**
@@ -17778,6 +17760,9 @@ class SelectionInput extends owl.Component {
17778
17760
  this.confirm();
17779
17761
  }
17780
17762
  }
17763
+ extractRanges(value) {
17764
+ return this.props.hasSingleRange ? value.split(",")[0] : value;
17765
+ }
17781
17766
  focus(rangeId) {
17782
17767
  this.state.isMissing = false;
17783
17768
  this.state.mode = "select-range";
@@ -17796,10 +17781,11 @@ class SelectionInput extends owl.Component {
17796
17781
  }
17797
17782
  onInputChanged(rangeId, ev) {
17798
17783
  const target = ev.target;
17784
+ const value = this.extractRanges(target.value);
17799
17785
  this.env.model.dispatch("CHANGE_RANGE", {
17800
17786
  id: this.id,
17801
17787
  rangeId,
17802
- value: target.value,
17788
+ value,
17803
17789
  });
17804
17790
  this.triggerChange();
17805
17791
  }
@@ -20568,14 +20554,31 @@ class ContentEditableHelper {
20568
20554
  * */
20569
20555
  getCurrentSelection() {
20570
20556
  let { startElement, endElement, startSelectionOffset, endSelectionOffset } = this.getStartAndEndSelection();
20571
- let startSizeBefore = this.findSizeBeforeElement(startElement);
20572
- let endSizeBefore = this.findSizeBeforeElement(endElement);
20557
+ let startSizeBefore = this.findSelectionIndex(startElement, startSelectionOffset);
20558
+ let endSizeBefore = this.findSelectionIndex(endElement, endSelectionOffset);
20573
20559
  return {
20574
- start: startSizeBefore + startSelectionOffset,
20575
- end: endSizeBefore + endSelectionOffset,
20560
+ start: startSizeBefore,
20561
+ end: endSizeBefore,
20576
20562
  };
20577
20563
  }
20578
- findSizeBeforeElement(nodeToFind) {
20564
+ /**
20565
+ * Computes the text 'index' inside this.el based on the currently selected node and its offset.
20566
+ * The selected node is either a Text node or an Element node.
20567
+ *
20568
+ * case 1 -Text node:
20569
+ * the offset is the number of characters from the start of the node. We have to add this offset to the
20570
+ * content length of all previous nodes.
20571
+ *
20572
+ * case 2 - Element node:
20573
+ * the offset is the number of child nodes before the selected node. We have to add the content length of
20574
+ * all the bnodes prior to the selected node as well as the content of the child node before the offset.
20575
+ *
20576
+ * See the MDN documentation for more details.
20577
+ * https://developer.mozilla.org/en-US/docs/Web/API/Range/startOffset
20578
+ * https://developer.mozilla.org/en-US/docs/Web/API/Range/endOffset
20579
+ *
20580
+ */
20581
+ findSelectionIndex(nodeToFind, nodeOffset) {
20579
20582
  let usedCharacters = 0;
20580
20583
  let it = iterateChildren(this.el);
20581
20584
  let current = it.next();
@@ -20602,6 +20605,28 @@ class ContentEditableHelper {
20602
20605
  if (current.value !== nodeToFind) {
20603
20606
  throw new Error("Cannot find the node in the children of the element");
20604
20607
  }
20608
+ else {
20609
+ if (!current.value.hasChildNodes()) {
20610
+ usedCharacters += nodeOffset;
20611
+ }
20612
+ else {
20613
+ const children = [...current.value.childNodes].slice(0, nodeOffset);
20614
+ usedCharacters += children.reduce((acc, child, index) => {
20615
+ if (child.textContent !== null) {
20616
+ // need to account for paragraph nodes that implicitely add a new line
20617
+ // except for the last paragraph
20618
+ let chars = child.textContent.length;
20619
+ if (child.nodeName === "P" && index !== children.length - 1) {
20620
+ chars++;
20621
+ }
20622
+ return acc + chars;
20623
+ }
20624
+ else {
20625
+ return acc;
20626
+ }
20627
+ }, 0);
20628
+ }
20629
+ }
20605
20630
  if (nodeToFind.nodeName === "P" && !isFirstParagraph && nodeToFind.textContent == "") {
20606
20631
  usedCharacters++;
20607
20632
  }
@@ -21402,13 +21427,13 @@ GridComposer.props = {
21402
21427
 
21403
21428
  const CSS$1 = css /* scss */ `
21404
21429
  .o-filter-icon {
21430
+ color: ${FILTERS_COLOR};
21405
21431
  position: absolute;
21406
21432
  display: flex;
21407
21433
  align-items: center;
21408
21434
  justify-content: center;
21409
21435
  width: ${FILTER_ICON_EDGE_LENGTH}px;
21410
21436
  height: ${FILTER_ICON_EDGE_LENGTH}px;
21411
- color: #4a4f59;
21412
21437
  }
21413
21438
  .o-filter-icon:hover {
21414
21439
  background: ${FILTERS_COLOR};
@@ -24139,17 +24164,17 @@ const CELL_TYPE_CONVERSION_MAP = {
24139
24164
  const BORDER_STYLE_CONVERSION_MAP = {
24140
24165
  dashDot: "thin",
24141
24166
  dashDotDot: "thin",
24142
- dashed: "thin",
24143
- dotted: "thin",
24167
+ dashed: "dashed",
24168
+ dotted: "dotted",
24144
24169
  double: "thin",
24145
24170
  hair: "thin",
24146
- medium: "thin",
24171
+ medium: "medium",
24147
24172
  mediumDashDot: "thin",
24148
24173
  mediumDashDotDot: "thin",
24149
24174
  mediumDashed: "thin",
24150
24175
  none: undefined,
24151
24176
  slantDashDot: "thin",
24152
- thick: "thin",
24177
+ thick: "thick",
24153
24178
  thin: "thin",
24154
24179
  };
24155
24180
  /** Conversion map Horizontal Alignment in XLSX <=> Horizontal Alignment in o_spreadsheet*/
@@ -24648,7 +24673,7 @@ function convertBorderDescr$1(borderDescr, warningManager) {
24648
24673
  return undefined;
24649
24674
  addBorderDescrWarnings(borderDescr, warningManager);
24650
24675
  const style = BORDER_STYLE_CONVERSION_MAP[borderDescr.style];
24651
- return style ? [style, convertColor(borderDescr.color)] : undefined;
24676
+ return style ? { style, color: convertColor(borderDescr.color) } : undefined;
24652
24677
  }
24653
24678
  function convertStyles(data, warningManager) {
24654
24679
  const stylesArray = data.styles.map((style) => {
@@ -26046,8 +26071,8 @@ function convertBorderDescr(descr) {
26046
26071
  return undefined;
26047
26072
  }
26048
26073
  return {
26049
- style: descr[0],
26050
- color: { rgb: descr[1] },
26074
+ style: descr.style,
26075
+ color: { rgb: descr.color },
26051
26076
  };
26052
26077
  }
26053
26078
  function extractStyle(cell, data) {
@@ -26625,7 +26650,7 @@ const TABLE_HEADER_STYLE = {
26625
26650
  const TABLE_HIGHLIGHTED_CELL_STYLE = {
26626
26651
  bold: true,
26627
26652
  };
26628
- const TABLE_BORDER_STYLE = ["thin", "#000000FF"];
26653
+ const TABLE_BORDER_STYLE = { style: "thin", color: "#000000FF" };
26629
26654
  /**
26630
26655
  * Convert the imported XLSX tables.
26631
26656
  *
@@ -28194,7 +28219,7 @@ function normalizeV9(formula) {
28194
28219
  * a breaking change is made in the way the state is handled, and an upgrade
28195
28220
  * function should be defined
28196
28221
  */
28197
- const CURRENT_VERSION = 12;
28222
+ const CURRENT_VERSION = 13;
28198
28223
  const INITIAL_SHEET_ID = "Sheet1";
28199
28224
  /**
28200
28225
  * This function tries to load anything that could look like a valid
@@ -28458,6 +28483,25 @@ const MIGRATIONS = [
28458
28483
  return data;
28459
28484
  },
28460
28485
  },
28486
+ {
28487
+ description: "Change Border description structure",
28488
+ from: 12,
28489
+ to: 13,
28490
+ applyMigration(data) {
28491
+ for (const borderId in data.borders) {
28492
+ const border = data.borders[borderId];
28493
+ for (const position in border) {
28494
+ if (Array.isArray(border[position])) {
28495
+ border[position] = {
28496
+ style: border[position][0],
28497
+ color: border[position][1],
28498
+ };
28499
+ }
28500
+ }
28501
+ }
28502
+ return data;
28503
+ },
28504
+ },
28461
28505
  ];
28462
28506
  /**
28463
28507
  * This function is used to repair faulty data independently of the migration.
@@ -28793,7 +28837,7 @@ class CorePlugin extends BasePlugin {
28793
28837
  * - borders
28794
28838
  */
28795
28839
  class BordersPlugin extends CorePlugin {
28796
- static getters = ["getCellBorder"];
28840
+ static getters = ["getCellBorder", "getBordersColors"];
28797
28841
  borders = {};
28798
28842
  // ---------------------------------------------------------------------------
28799
28843
  // Command Handling
@@ -28824,10 +28868,15 @@ class BordersPlugin extends CorePlugin {
28824
28868
  case "SET_BORDER":
28825
28869
  this.setBorder(cmd.sheetId, cmd.col, cmd.row, cmd.border);
28826
28870
  break;
28827
- case "SET_FORMATTING":
28871
+ case "SET_ZONE_BORDERS":
28828
28872
  if (cmd.border) {
28829
28873
  const target = cmd.target.map((zone) => this.getters.expandZone(cmd.sheetId, zone));
28830
- this.setBorders(cmd.sheetId, target, cmd.border);
28874
+ this.setBorders(cmd.sheetId, target, cmd.border.position, cmd.border.color === ""
28875
+ ? undefined
28876
+ : {
28877
+ style: cmd.border.style || DEFAULT_BORDER_DESC.style,
28878
+ color: cmd.border.color || DEFAULT_BORDER_DESC.color,
28879
+ });
28831
28880
  }
28832
28881
  break;
28833
28882
  case "CLEAR_FORMATTING":
@@ -28916,6 +28965,23 @@ class BordersPlugin extends CorePlugin {
28916
28965
  }
28917
28966
  return border;
28918
28967
  }
28968
+ getBordersColors(sheetId) {
28969
+ const colors = [];
28970
+ const sheetBorders = this.borders[sheetId];
28971
+ if (sheetBorders) {
28972
+ for (const borders of sheetBorders.filter(isDefined$1)) {
28973
+ for (const cellBorder of borders) {
28974
+ if (cellBorder?.horizontal) {
28975
+ colors.push(cellBorder.horizontal.color);
28976
+ }
28977
+ if (cellBorder?.vertical) {
28978
+ colors.push(cellBorder.vertical.color);
28979
+ }
28980
+ }
28981
+ }
28982
+ }
28983
+ return colors;
28984
+ }
28919
28985
  // ---------------------------------------------------------------------------
28920
28986
  // Private
28921
28987
  // ---------------------------------------------------------------------------
@@ -29127,43 +29193,43 @@ class BordersPlugin extends CorePlugin {
29127
29193
  * Set the borders of a zone by computing the borders to add from the given
29128
29194
  * command
29129
29195
  */
29130
- setBorders(sheetId, zones, command) {
29131
- if (command === "clear") {
29196
+ setBorders(sheetId, zones, position, border) {
29197
+ if (position === "clear") {
29132
29198
  return this.clearBorders(sheetId, zones);
29133
29199
  }
29134
29200
  for (let zone of zones) {
29135
- if (command === "h" || command === "hv" || command === "all") {
29201
+ if (position === "h" || position === "hv" || position === "all") {
29136
29202
  for (let row = zone.top + 1; row <= zone.bottom; row++) {
29137
29203
  for (let col = zone.left; col <= zone.right; col++) {
29138
- this.addBorder(sheetId, col, row, { top: DEFAULT_BORDER_DESC });
29204
+ this.addBorder(sheetId, col, row, { top: border });
29139
29205
  }
29140
29206
  }
29141
29207
  }
29142
- if (command === "v" || command === "hv" || command === "all") {
29208
+ if (position === "v" || position === "hv" || position === "all") {
29143
29209
  for (let row = zone.top; row <= zone.bottom; row++) {
29144
29210
  for (let col = zone.left + 1; col <= zone.right; col++) {
29145
- this.addBorder(sheetId, col, row, { left: DEFAULT_BORDER_DESC });
29211
+ this.addBorder(sheetId, col, row, { left: border });
29146
29212
  }
29147
29213
  }
29148
29214
  }
29149
- if (command === "left" || command === "all" || command === "external") {
29215
+ if (position === "left" || position === "all" || position === "external") {
29150
29216
  for (let row = zone.top; row <= zone.bottom; row++) {
29151
- this.addBorder(sheetId, zone.left, row, { left: DEFAULT_BORDER_DESC });
29217
+ this.addBorder(sheetId, zone.left, row, { left: border });
29152
29218
  }
29153
29219
  }
29154
- if (command === "right" || command === "all" || command === "external") {
29220
+ if (position === "right" || position === "all" || position === "external") {
29155
29221
  for (let row = zone.top; row <= zone.bottom; row++) {
29156
- this.addBorder(sheetId, zone.right + 1, row, { left: DEFAULT_BORDER_DESC });
29222
+ this.addBorder(sheetId, zone.right + 1, row, { left: border });
29157
29223
  }
29158
29224
  }
29159
- if (command === "top" || command === "all" || command === "external") {
29225
+ if (position === "top" || position === "all" || position === "external") {
29160
29226
  for (let col = zone.left; col <= zone.right; col++) {
29161
- this.addBorder(sheetId, col, zone.top, { top: DEFAULT_BORDER_DESC });
29227
+ this.addBorder(sheetId, col, zone.top, { top: border });
29162
29228
  }
29163
29229
  }
29164
- if (command === "bottom" || command === "all" || command === "external") {
29230
+ if (position === "bottom" || position === "all" || position === "external") {
29165
29231
  for (let col = zone.left; col <= zone.right; col++) {
29166
- this.addBorder(sheetId, col, zone.bottom + 1, { top: DEFAULT_BORDER_DESC });
29232
+ this.addBorder(sheetId, col, zone.bottom + 1, { top: border });
29167
29233
  }
29168
29234
  }
29169
29235
  }
@@ -29177,16 +29243,22 @@ class BordersPlugin extends CorePlugin {
29177
29243
  const bordersBottomRight = this.getCellBorder({ sheetId, col: right, row: bottom });
29178
29244
  this.clearBorders(sheetId, [zone]);
29179
29245
  if (bordersTopLeft?.top) {
29180
- this.setBorders(sheetId, [{ ...zone, bottom: top }], "top");
29246
+ this.setBorders(sheetId, [{ ...zone, bottom: top }], "top", bordersTopLeft.top);
29181
29247
  }
29182
29248
  if (bordersTopLeft?.left) {
29183
- this.setBorders(sheetId, [{ ...zone, right: left }], "left");
29249
+ this.setBorders(sheetId, [{ ...zone, right: left }], "left", bordersTopLeft.left);
29250
+ }
29251
+ if (bordersBottomRight?.bottom) {
29252
+ this.setBorders(sheetId, [{ ...zone, top: bottom }], "bottom", bordersBottomRight.bottom);
29253
+ }
29254
+ else if (bordersTopLeft?.bottom) {
29255
+ this.setBorders(sheetId, [{ ...zone, top: bottom }], "bottom", bordersTopLeft.bottom);
29184
29256
  }
29185
- if (bordersBottomRight?.bottom || bordersTopLeft?.bottom) {
29186
- this.setBorders(sheetId, [{ ...zone, top: bottom }], "bottom");
29257
+ if (bordersBottomRight?.right) {
29258
+ this.setBorders(sheetId, [{ ...zone, left: right }], "right", bordersBottomRight.right);
29187
29259
  }
29188
- if (bordersBottomRight?.right || bordersTopLeft?.right) {
29189
- this.setBorders(sheetId, [{ ...zone, left: right }], "right");
29260
+ else if (bordersTopLeft?.right) {
29261
+ this.setBorders(sheetId, [{ ...zone, left: right }], "right", bordersTopLeft.right);
29190
29262
  }
29191
29263
  }
29192
29264
  // ---------------------------------------------------------------------------
@@ -33039,6 +33111,9 @@ class CustomColorsPlugin extends UIPlugin {
33039
33111
  case "UPDATE_CHART":
33040
33112
  case "CREATE_CHART":
33041
33113
  case "ADD_CONDITIONAL_FORMAT":
33114
+ case "SET_BORDER":
33115
+ case "SET_ZONE_BORDERS":
33116
+ case "SET_FORMATTING":
33042
33117
  this.shouldUpdateColors = true;
33043
33118
  }
33044
33119
  }
@@ -33053,8 +33128,7 @@ class CustomColorsPlugin extends UIPlugin {
33053
33128
  getCustomColors() {
33054
33129
  let usedColors = [];
33055
33130
  for (const sheetId of this.getters.getSheetIds()) {
33056
- const cells = Object.values(this.getters.getCells(sheetId));
33057
- usedColors = usedColors.concat(this.getColorsFromCells(cells), this.getFormattingColors(sheetId), this.getChartColors(sheetId));
33131
+ usedColors = usedColors.concat(this.getColorsFromCells(sheetId), this.getFormattingColors(sheetId), this.getChartColors(sheetId));
33058
33132
  }
33059
33133
  return sortWithClusters([
33060
33134
  ...new Set(
@@ -33064,7 +33138,8 @@ class CustomColorsPlugin extends UIPlugin {
33064
33138
  [...new Set([...usedColors, ...this.customColors])].filter(isColorValid).map(toHex)),
33065
33139
  ]).filter((color) => !COLOR_PICKER_DEFAULTS.includes(color));
33066
33140
  }
33067
- getColorsFromCells(cells) {
33141
+ getColorsFromCells(sheetId) {
33142
+ const cells = Object.values(this.getters.getCells(sheetId));
33068
33143
  const colors = new Set();
33069
33144
  for (const cell of cells) {
33070
33145
  if (cell.style?.textColor) {
@@ -33074,6 +33149,9 @@ class CustomColorsPlugin extends UIPlugin {
33074
33149
  colors.add(cell.style.fillColor);
33075
33150
  }
33076
33151
  }
33152
+ for (const color of this.getters.getBordersColors(sheetId)) {
33153
+ colors.add(color);
33154
+ }
33077
33155
  return [...colors];
33078
33156
  }
33079
33157
  getFormattingColors(sheetId) {
@@ -33347,7 +33425,7 @@ class EvaluationPlugin extends UIPlugin {
33347
33425
  // magic "empty" value
33348
33426
  // Returning {value: null} instead of undefined will ensure that we don't
33349
33427
  // fall back on the default value of the argument provided to the formula's compute function
33350
- return { value: null };
33428
+ return { value: null, format: cell?.format };
33351
33429
  }
33352
33430
  return getEvaluatedCell(cell);
33353
33431
  }
@@ -36176,6 +36254,7 @@ function transformPositionWithMerge(cmd, executed) {
36176
36254
 
36177
36255
  class Revision {
36178
36256
  rootCommand;
36257
+ timestamp;
36179
36258
  id;
36180
36259
  clientId;
36181
36260
  _commands = [];
@@ -36189,8 +36268,9 @@ class Revision {
36189
36268
  * - clientId: Client who initiated the action
36190
36269
  * - changes: List of changes applied on the state.
36191
36270
  */
36192
- constructor(id, clientId, commands, rootCommand, changes) {
36271
+ constructor(id, clientId, commands, rootCommand, changes, timestamp) {
36193
36272
  this.rootCommand = rootCommand;
36273
+ this.timestamp = timestamp;
36194
36274
  this.id = id;
36195
36275
  this.clientId = clientId;
36196
36276
  this._commands = [...commands];
@@ -36262,7 +36342,7 @@ class Session extends EventBus {
36262
36342
  save(rootCommand, commands, changes) {
36263
36343
  if (!commands.length || !changes.length || !this.canApplyOptimisticUpdate())
36264
36344
  return;
36265
- const revision = new Revision(this.uuidGenerator.uuidv4(), this.clientId, commands, rootCommand, changes);
36345
+ const revision = new Revision(this.uuidGenerator.uuidv4(), this.clientId, commands, rootCommand, changes, Date.now());
36266
36346
  this.revisions.append(revision.id, revision);
36267
36347
  this.trigger("new-local-state-update", { id: revision.id });
36268
36348
  this.sendUpdateMessage({
@@ -36313,13 +36393,9 @@ class Session extends EventBus {
36313
36393
  }
36314
36394
  loadInitialMessages(messages) {
36315
36395
  this.isReplayingInitialRevisions = true;
36316
- this.on("unexpected-revision-id", this, ({ revisionId }) => {
36317
- throw new Error(`The spreadsheet could not be loaded. Revision ${revisionId} is corrupted.`);
36318
- });
36319
36396
  for (const message of messages) {
36320
36397
  this.onMessageReceived(message);
36321
36398
  }
36322
- this.off("unexpected-revision-id", this);
36323
36399
  this.isReplayingInitialRevisions = false;
36324
36400
  }
36325
36401
  /**
@@ -36405,6 +36481,10 @@ class Session extends EventBus {
36405
36481
  onMessageReceived(message) {
36406
36482
  if (this.isAlreadyProcessed(message))
36407
36483
  return;
36484
+ if (this.isWrongServerRevisionId(message)) {
36485
+ this.trigger("unexpected-revision-id");
36486
+ return;
36487
+ }
36408
36488
  switch (message.type) {
36409
36489
  case "CLIENT_MOVED":
36410
36490
  this.onClientMoved(message);
@@ -36431,12 +36511,8 @@ class Session extends EventBus {
36431
36511
  });
36432
36512
  break;
36433
36513
  case "REMOTE_REVISION":
36434
- if (message.serverRevisionId !== this.serverRevisionId) {
36435
- this.trigger("unexpected-revision-id", { revisionId: message.serverRevisionId });
36436
- return;
36437
- }
36438
- const { clientId, commands } = message;
36439
- const revision = new Revision(message.nextRevisionId, clientId, commands, "REMOTE");
36514
+ const { clientId, commands, timestamp } = message;
36515
+ const revision = new Revision(message.nextRevisionId, clientId, commands, "REMOTE", undefined, timestamp);
36440
36516
  if (revision.clientId !== this.clientId) {
36441
36517
  this.revisions.insert(revision.id, revision, message.serverRevisionId);
36442
36518
  const pendingCommands = this.pendingMessages
@@ -36449,7 +36525,7 @@ class Session extends EventBus {
36449
36525
  }
36450
36526
  break;
36451
36527
  case "SNAPSHOT_CREATED": {
36452
- const revision = new Revision(message.nextRevisionId, "server", [], "SNAPSHOT");
36528
+ const revision = new Revision(message.nextRevisionId, "server", [], "SNAPSHOT", undefined, Date.now());
36453
36529
  this.revisions.insert(revision.id, revision, message.serverRevisionId);
36454
36530
  this.dropPendingHistoryMessages();
36455
36531
  this.trigger("snapshot");
@@ -36566,6 +36642,17 @@ class Session extends EventBus {
36566
36642
  return false;
36567
36643
  }
36568
36644
  }
36645
+ isWrongServerRevisionId(message) {
36646
+ switch (message.type) {
36647
+ case "REMOTE_REVISION":
36648
+ case "REVISION_REDONE":
36649
+ case "REVISION_UNDONE":
36650
+ case "SNAPSHOT_CREATED":
36651
+ return message.serverRevisionId !== this.serverRevisionId;
36652
+ default:
36653
+ return false;
36654
+ }
36655
+ }
36569
36656
  dropPendingHistoryMessages() {
36570
36657
  this.waitingUndoRedoAck = false;
36571
36658
  this.pendingMessages = this.pendingMessages.filter(({ type }) => type !== "REVISION_REDONE" && type !== "REVISION_UNDONE");
@@ -37281,7 +37368,7 @@ class RendererPlugin extends UIPlugin {
37281
37368
  }
37282
37369
  }
37283
37370
  drawBorders(renderingContext) {
37284
- const { ctx, thinLineWidth } = renderingContext;
37371
+ const { ctx } = renderingContext;
37285
37372
  for (let box of this.boxes) {
37286
37373
  const border = box.border;
37287
37374
  if (border) {
@@ -37300,13 +37387,66 @@ class RendererPlugin extends UIPlugin {
37300
37387
  }
37301
37388
  }
37302
37389
  }
37303
- function drawBorder([style, color], x1, y1, x2, y2) {
37390
+ /**
37391
+ * Following https://usefulangle.com/post/17/html5-canvas-drawing-1px-crisp-straight-lines,
37392
+ * we need to make sure that a "single" pixel line is drawn on a "half" pixel coordinate,
37393
+ * while a "double" pixel line is drawn on a "full" pixel coordinate. As, in the rendering
37394
+ * process, we always had 0.5 before rendering line (to make sure it is drawn on a "half"
37395
+ * pixel), we need to correct this behavior for the "medium" and the "dotted" styles, as
37396
+ * they are drawing a two pixels width line.
37397
+ * We also adapt here the coordinates of the line to make sure corner are correctly drawn,
37398
+ * avoiding a "round corners" effect. This is done by subtracting 1 pixel to the origin of
37399
+ * each line and adding 1 pixel to the end of each line (depending on the direction of the
37400
+ * line).
37401
+ */
37402
+ function drawBorder({ style, color }, x1, y1, x2, y2) {
37304
37403
  ctx.strokeStyle = color;
37305
- ctx.lineWidth = (style === "thin" ? 2 : 3) * thinLineWidth;
37404
+ switch (style) {
37405
+ case "medium":
37406
+ ctx.lineWidth = 2;
37407
+ x1 += y1 === y2 ? -0.5 : 0.5;
37408
+ x2 += y1 === y2 ? 1.5 : 0.5;
37409
+ y1 += x1 === x2 ? -0.5 : 0.5;
37410
+ y2 += x1 === x2 ? 1.5 : 0.5;
37411
+ break;
37412
+ case "thick":
37413
+ ctx.lineWidth = 3;
37414
+ if (y1 === y2) {
37415
+ x1--;
37416
+ x2++;
37417
+ }
37418
+ if (x1 === x2) {
37419
+ y1--;
37420
+ y2++;
37421
+ }
37422
+ break;
37423
+ case "dashed":
37424
+ ctx.lineWidth = 1;
37425
+ ctx.setLineDash([1, 3]);
37426
+ break;
37427
+ case "dotted":
37428
+ ctx.lineWidth = 1;
37429
+ if (y1 === y2) {
37430
+ x1 += 0.5;
37431
+ x2 += 0.5;
37432
+ }
37433
+ if (x1 === x2) {
37434
+ y1 += 0.5;
37435
+ y2 += 0.5;
37436
+ }
37437
+ ctx.setLineDash([1, 1]);
37438
+ break;
37439
+ case "thin":
37440
+ default:
37441
+ ctx.lineWidth = 1;
37442
+ break;
37443
+ }
37306
37444
  ctx.beginPath();
37307
37445
  ctx.moveTo(x1, y1);
37308
37446
  ctx.lineTo(x2, y2);
37309
37447
  ctx.stroke();
37448
+ ctx.lineWidth = 1;
37449
+ ctx.setLineDash([]);
37310
37450
  }
37311
37451
  }
37312
37452
  drawTexts(renderingContext) {
@@ -37784,6 +37924,9 @@ class SelectionInputPlugin extends UIPlugin {
37784
37924
  activeSheet;
37785
37925
  willAddNewRange = false;
37786
37926
  constructor(config, initialRanges, inputHasSingleRange) {
37927
+ if (inputHasSingleRange && initialRanges.length > 1) {
37928
+ throw new Error("Input with a single range cannot be instantiated with several range references.");
37929
+ }
37787
37930
  super(config);
37788
37931
  this.inputHasSingleRange = inputHasSingleRange;
37789
37932
  this.insertNewRange(0, initialRanges);
@@ -37803,6 +37946,11 @@ class SelectionInputPlugin extends UIPlugin {
37803
37946
  return 30 /* CommandResult.MaximumRangesReached */;
37804
37947
  }
37805
37948
  break;
37949
+ case "CHANGE_RANGE":
37950
+ if (this.inputHasSingleRange && cmd.value.split(",").length > 1) {
37951
+ return 30 /* CommandResult.MaximumRangesReached */;
37952
+ }
37953
+ break;
37806
37954
  }
37807
37955
  return 0 /* CommandResult.Success */;
37808
37956
  }
@@ -41289,7 +41437,8 @@ class GridSelectionPlugin extends UIPlugin {
41289
41437
  },
41290
41438
  zone: selectedZone,
41291
41439
  };
41292
- this.setSelectionMixin(anchor, [selectedZone]);
41440
+ const selections = this.gridSelection.zones.map((zone) => updateSelectionOnDeletion(zone, "left", [...cmd.elements]));
41441
+ this.setSelectionMixin(anchor, selections);
41293
41442
  }
41294
41443
  onRowsRemoved(cmd) {
41295
41444
  const { cell, zone } = this.gridSelection.anchor;
@@ -41303,13 +41452,18 @@ class GridSelectionPlugin extends UIPlugin {
41303
41452
  },
41304
41453
  zone: selectedZone,
41305
41454
  };
41306
- this.setSelectionMixin(anchor, [selectedZone]);
41455
+ const selections = this.gridSelection.zones.map((zone) => updateSelectionOnDeletion(zone, "top", [...cmd.elements]));
41456
+ this.setSelectionMixin(anchor, selections);
41307
41457
  }
41308
41458
  onAddElements(cmd) {
41309
- const selection = this.gridSelection.anchor.zone;
41310
- const zone = updateSelectionOnInsertion(selection, cmd.dimension === "COL" ? "left" : "top", cmd.base, cmd.position, cmd.quantity);
41311
- const anchor = { cell: { col: zone.left, row: zone.top }, zone };
41312
- this.setSelectionMixin(anchor, [zone]);
41459
+ const start = cmd.dimension === "COL" ? "left" : "top";
41460
+ const anchorZone = updateSelectionOnInsertion(this.gridSelection.anchor.zone, start, cmd.base, cmd.position, cmd.quantity);
41461
+ const selection = this.gridSelection.zones.map((zone) => updateSelectionOnInsertion(zone, start, cmd.base, cmd.position, cmd.quantity));
41462
+ const anchor = {
41463
+ cell: { col: anchorZone.left, row: anchorZone.top },
41464
+ zone: anchorZone,
41465
+ };
41466
+ this.setSelectionMixin(anchor, selection);
41313
41467
  }
41314
41468
  onMoveElements(cmd) {
41315
41469
  const thickness = cmd.elements.length;
@@ -42555,7 +42709,6 @@ css /* scss */ `
42555
42709
  color: dimgrey;
42556
42710
  }
42557
42711
  .o-sidePanelClose {
42558
- font-size: 1.5rem;
42559
42712
  padding: 5px 10px;
42560
42713
  cursor: pointer;
42561
42714
  &:hover {
@@ -42740,7 +42893,6 @@ css /* scss */ `
42740
42893
  justify-content: center;
42741
42894
  align-items: center;
42742
42895
  margin: 2px;
42743
- padding: 0px 3px;
42744
42896
  border-radius: 2px;
42745
42897
  min-width: 20px;
42746
42898
  }
@@ -42797,7 +42949,228 @@ ActionButton.props = {
42797
42949
  onClick: { type: Function, optional: true },
42798
42950
  };
42799
42951
 
42952
+ /**
42953
+ * List the available borders positions and the corresponding icons.
42954
+ * The structure of this array is defined to match the order/lines we want
42955
+ * to display in the topbar's border tool.
42956
+ */
42957
+ const BORDER_POSITIONS = [
42958
+ [
42959
+ ["all", "o-spreadsheet-Icon.BORDERS"],
42960
+ ["hv", "o-spreadsheet-Icon.BORDER_HV"],
42961
+ ["h", "o-spreadsheet-Icon.BORDER_H"],
42962
+ ["v", "o-spreadsheet-Icon.BORDER_V"],
42963
+ ["external", "o-spreadsheet-Icon.BORDER_EXTERNAL"],
42964
+ ],
42965
+ [
42966
+ ["left", "o-spreadsheet-Icon.BORDER_LEFT"],
42967
+ ["top", "o-spreadsheet-Icon.BORDER_TOP"],
42968
+ ["right", "o-spreadsheet-Icon.BORDER_RIGHT"],
42969
+ ["bottom", "o-spreadsheet-Icon.BORDER_BOTTOM"],
42970
+ ["clear", "o-spreadsheet-Icon.BORDER_CLEAR"],
42971
+ ],
42972
+ ];
42973
+ // -----------------------------------------------------------------------------
42974
+ // Border Editor
42975
+ // -----------------------------------------------------------------------------
42976
+ css /* scss */ `
42977
+ .o-border-selector {
42978
+ padding: 4px;
42979
+ }
42980
+ .o-divider {
42981
+ border-right: 1px solid #e0e2e4;
42982
+ margin: 0 6px;
42983
+ }
42984
+ .o-border-selector-section {
42985
+ .o-dropdown-line {
42986
+ height: 30px;
42987
+ margin: 1px;
42988
+ .o-line-item {
42989
+ padding: 4px;
42990
+ width: 18px;
42991
+ height: 18px;
42992
+ }
42993
+ }
42994
+ .o-border-style-tool {
42995
+ padding: 0px 3px;
42996
+ margin: 2px;
42997
+ height: 25px;
42998
+ }
42999
+ }
43000
+ .o-style-preview {
43001
+ margin: 7px 5px 7px 5px;
43002
+ width: 60px;
43003
+ height: 5px;
43004
+ }
43005
+ .o-style-thin {
43006
+ border-bottom: 1px solid #000000;
43007
+ }
43008
+ .o-style-medium {
43009
+ border-bottom: 2px solid #000000;
43010
+ }
43011
+ .o-style-thick {
43012
+ border-bottom: 3px solid #000000;
43013
+ }
43014
+ .o-style-dashed {
43015
+ border-bottom: 1px dashed #000000;
43016
+ }
43017
+ .o-style-dotted {
43018
+ border-bottom: 1px dotted #000000;
43019
+ }
43020
+ .o-dropdown-border-type {
43021
+ &:not(.o-disabled):not(.active):hover {
43022
+ background-color: ${BG_HOVER_COLOR};
43023
+ }
43024
+ }
43025
+ .o-dropdown-border-check {
43026
+ width: 20px;
43027
+ font-size: 12px;
43028
+ }
43029
+ .o-border-style-dropdown {
43030
+ background: #ffffff;
43031
+ padding: 4px;
43032
+ .o-dropdown-line {
43033
+ .o-line-item.active {
43034
+ background-color: rgba(0, 0, 0, 0.2);
43035
+ }
43036
+ }
43037
+ }
43038
+ .o-border-picker-button {
43039
+ padding: 0px !important;
43040
+ margin: 5px 0px 0px 0px !important;
43041
+ height: 25px !important;
43042
+ }
43043
+ `;
43044
+ class BorderEditor extends owl.Component {
43045
+ static template = "o-spreadsheet-BorderEditor";
43046
+ static components = { ColorPickerWidget, Popover };
43047
+ BORDER_POSITIONS = BORDER_POSITIONS;
43048
+ lineStyleButtonRef = owl.useRef("lineStyleButton");
43049
+ borderStyles = borderStyles;
43050
+ state = owl.useState({
43051
+ activeTool: undefined,
43052
+ });
43053
+ toggleDropdownTool(tool) {
43054
+ const isOpen = this.state.activeTool === tool;
43055
+ this.state.activeTool = isOpen ? undefined : tool;
43056
+ }
43057
+ closeDropdown() {
43058
+ this.state.activeTool = undefined;
43059
+ }
43060
+ setBorderPosition(position) {
43061
+ this.props.onBorderPositionPicked(position);
43062
+ this.closeDropdown();
43063
+ }
43064
+ setBorderColor(color) {
43065
+ this.props.onBorderColorPicked(color);
43066
+ this.closeDropdown();
43067
+ }
43068
+ setBorderStyle(style) {
43069
+ this.props.onBorderStylePicked(style);
43070
+ this.closeDropdown();
43071
+ }
43072
+ get lineStylePickerPopoverProps() {
43073
+ return {
43074
+ anchorRect: this.lineStylePickerAnchorRect,
43075
+ positioning: "BottomLeft",
43076
+ verticalOffset: 0,
43077
+ };
43078
+ }
43079
+ get popoverProps() {
43080
+ return {
43081
+ anchorRect: this.props.anchorRect,
43082
+ maxHeight: this.props.maxHeight,
43083
+ positioning: "BottomLeft",
43084
+ verticalOffset: 0,
43085
+ };
43086
+ }
43087
+ get lineStylePickerAnchorRect() {
43088
+ const button = this.lineStyleButtonRef.el;
43089
+ if (button === null) {
43090
+ return { x: 0, y: 0, width: 0, height: 0 };
43091
+ }
43092
+ const buttonRect = button.getBoundingClientRect();
43093
+ return {
43094
+ x: buttonRect.x,
43095
+ y: buttonRect.y,
43096
+ width: buttonRect.width,
43097
+ height: buttonRect.height,
43098
+ };
43099
+ }
43100
+ }
43101
+ BorderEditor.props = {
43102
+ class: { type: String, optional: true },
43103
+ currentBorderColor: { type: String, optional: false },
43104
+ currentBorderStyle: { type: String, optional: false },
43105
+ currentBorderPosition: { type: String, optional: true },
43106
+ onBorderColorPicked: Function,
43107
+ onBorderStylePicked: Function,
43108
+ onBorderPositionPicked: Function,
43109
+ maxHeight: { type: Number, optional: true },
43110
+ anchorRect: Object,
43111
+ };
43112
+
43113
+ class BorderEditorWidget extends owl.Component {
43114
+ static template = "o-spreadsheet-BorderEditorWidget";
43115
+ static components = { BorderEditor };
43116
+ borderEditorButtonRef = owl.useRef("borderEditorButton");
43117
+ state = owl.useState({
43118
+ currentColor: DEFAULT_BORDER_DESC.color,
43119
+ currentStyle: DEFAULT_BORDER_DESC.style,
43120
+ currentPosition: undefined,
43121
+ });
43122
+ get borderEditorAnchorRect() {
43123
+ const button = this.borderEditorButtonRef.el;
43124
+ const buttonRect = button.getBoundingClientRect();
43125
+ return {
43126
+ x: buttonRect.x,
43127
+ y: buttonRect.y,
43128
+ width: buttonRect.width,
43129
+ height: buttonRect.height,
43130
+ };
43131
+ }
43132
+ onBorderPositionPicked(position) {
43133
+ this.state.currentPosition = position;
43134
+ this.updateBorder();
43135
+ }
43136
+ onBorderColorPicked(color) {
43137
+ this.state.currentColor = color;
43138
+ this.updateBorder();
43139
+ }
43140
+ onBorderStylePicked(style) {
43141
+ this.state.currentStyle = style;
43142
+ this.updateBorder();
43143
+ }
43144
+ updateBorder() {
43145
+ if (this.state.currentPosition === undefined) {
43146
+ return;
43147
+ }
43148
+ this.env.model.dispatch("SET_ZONE_BORDERS", {
43149
+ sheetId: this.env.model.getters.getActiveSheetId(),
43150
+ target: this.env.model.getters.getSelectedZones(),
43151
+ border: {
43152
+ position: this.state.currentPosition,
43153
+ color: this.state.currentColor,
43154
+ style: this.state.currentStyle,
43155
+ },
43156
+ });
43157
+ }
43158
+ }
43159
+ BorderEditorWidget.props = {
43160
+ toggleBorderEditor: Function,
43161
+ showBorderEditor: Boolean,
43162
+ disabled: { type: Boolean, optional: true },
43163
+ dropdownMaxHeight: { type: Number, optional: true },
43164
+ class: { type: String, optional: true },
43165
+ };
43166
+
42800
43167
  const COMPOSER_MAX_HEIGHT = 100;
43168
+ /* svg free of use from https://uxwing.com/formula-fx-icon/ */
43169
+ const FX_SVG = /*xml*/ `
43170
+ <svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 121.8 122.9' width='16' height='16' focusable='false'>
43171
+ <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'/>
43172
+ </svg>
43173
+ `;
42801
43174
  css /* scss */ `
42802
43175
  .o-topbar-composer {
42803
43176
  height: fit-content;
@@ -42805,9 +43178,10 @@ css /* scss */ `
42805
43178
  border: 1px solid;
42806
43179
  z-index: ${ComponentsImportance.TopBarComposer};
42807
43180
 
42808
- .o-composer:empty:not(:focus)::before {
42809
- /* svg free of use from https://uxwing.com/formula-fx-icon/ */
42810
- 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");
43181
+ .o-composer:empty:not(:focus):not(.active)::before {
43182
+ content: url("data:image/svg+xml,${encodeURIComponent(FX_SVG)}");
43183
+ position: relative;
43184
+ top: 20%;
42811
43185
  }
42812
43186
  }
42813
43187
  `;
@@ -42930,11 +43304,6 @@ FontSizeEditor.props = {
42930
43304
  dropdownStyle: String,
42931
43305
  };
42932
43306
 
42933
- // If we ever change these colors, make sure the filter tool stays green to match the icon in the grid
42934
- const ACTIVE_BG_COLOR = BACKGROUND_HEADER_FILTER_COLOR;
42935
- const ACTIVE_FONT_COLOR = FILTERS_COLOR;
42936
- const HOVERED_BG_COLOR = BG_HOVER_COLOR;
42937
- const HOVERED_FONT_COLOR = "#000";
42938
43307
  // -----------------------------------------------------------------------------
42939
43308
  // TopBar
42940
43309
  // -----------------------------------------------------------------------------
@@ -42944,27 +43313,6 @@ css /* scss */ `
42944
43313
  font-size: 13px;
42945
43314
  font-weight: 500;
42946
43315
 
42947
- .o-topbar-hoverable {
42948
- cursor: pointer;
42949
- .o-icon {
42950
- color: ${ICONS_COLOR};
42951
- }
42952
- &:not(.o-disabled):not(.active):hover {
42953
- background-color: ${HOVERED_BG_COLOR};
42954
- color: ${HOVERED_FONT_COLOR};
42955
- .o-icon {
42956
- color: ${HOVERED_FONT_COLOR};
42957
- }
42958
- }
42959
- &.active {
42960
- background-color: ${ACTIVE_BG_COLOR};
42961
- color: ${ACTIVE_FONT_COLOR};
42962
- .o-icon {
42963
- color: ${ACTIVE_FONT_COLOR};
42964
- }
42965
- }
42966
- }
42967
-
42968
43316
  .o-topbar-top {
42969
43317
  border-bottom: 1px solid ${SEPARATOR_COLOR};
42970
43318
  padding: 2px 10px;
@@ -42974,6 +43322,11 @@ css /* scss */ `
42974
43322
  .o-topbar-menu {
42975
43323
  padding: 4px 6px;
42976
43324
  margin: 0 2px;
43325
+
43326
+ &.active {
43327
+ background-color: ${BG_HOVER_COLOR};
43328
+ color: #000;
43329
+ }
42977
43330
  }
42978
43331
  }
42979
43332
  }
@@ -43051,6 +43404,7 @@ class TopBar extends owl.Component {
43051
43404
  TopBarComposer,
43052
43405
  FontSizeEditor,
43053
43406
  ActionButton,
43407
+ BorderEditorWidget,
43054
43408
  };
43055
43409
  state = owl.useState({
43056
43410
  menuState: { isOpen: false, position: null, menuItems: [] },
@@ -43126,7 +43480,9 @@ class TopBar extends owl.Component {
43126
43480
  this.state.activeTool = "";
43127
43481
  this.state.menuState.isOpen = true;
43128
43482
  this.state.menuState.position = { x: left, y: top + height };
43129
- this.state.menuState.menuItems = menu.children(this.env);
43483
+ this.state.menuState.menuItems = menu
43484
+ .children(this.env)
43485
+ .sort((a, b) => a.sequence - b.sequence);
43130
43486
  this.state.menuState.parentMenu = menu;
43131
43487
  this.isSelectingMenu = true;
43132
43488
  this.openedEl = ev.target;
@@ -43212,6 +43568,11 @@ class WebClipboardWrapper {
43212
43568
  }
43213
43569
  }
43214
43570
 
43571
+ // If we ever change these colors, make sure the filter tool stays green to match the icon in the grid
43572
+ const ACTIVE_BG_COLOR = BACKGROUND_HEADER_FILTER_COLOR;
43573
+ const ACTIVE_FONT_COLOR = FILTERS_COLOR;
43574
+ const HOVERED_BG_COLOR = BG_HOVER_COLOR;
43575
+ const HOVERED_FONT_COLOR = "#000";
43215
43576
  css /* scss */ `
43216
43577
  .o-spreadsheet {
43217
43578
  position: relative;
@@ -43244,6 +43605,27 @@ css /* scss */ `
43244
43605
  margin-top: ${MENU_SEPARATOR_PADDING}px;
43245
43606
  margin-bottom: ${MENU_SEPARATOR_PADDING}px;
43246
43607
  }
43608
+ .o-hoverable-button {
43609
+ border-radius: 2px;
43610
+ cursor: pointer;
43611
+ .o-icon {
43612
+ color: ${ICONS_COLOR};
43613
+ }
43614
+ &:not(.o-disabled):not(.active):hover {
43615
+ background-color: ${HOVERED_BG_COLOR};
43616
+ color: ${HOVERED_FONT_COLOR};
43617
+ .o-icon {
43618
+ color: ${HOVERED_FONT_COLOR};
43619
+ }
43620
+ }
43621
+ &.active {
43622
+ background-color: ${ACTIVE_BG_COLOR};
43623
+ color: ${ACTIVE_FONT_COLOR};
43624
+ .o-icon {
43625
+ color: ${ACTIVE_FONT_COLOR};
43626
+ }
43627
+ }
43628
+ }
43247
43629
  }
43248
43630
 
43249
43631
  .o-two-columns {
@@ -43393,6 +43775,9 @@ class Spreadsheet extends owl.Component {
43393
43775
  }
43394
43776
  }
43395
43777
  openSidePanel(panel, panelProps) {
43778
+ if (this.sidePanel.isOpen && panel !== this.sidePanel.component) {
43779
+ this.sidePanel.panelProps?.onCloseSidePanel?.();
43780
+ }
43396
43781
  this.sidePanel.component = panel;
43397
43782
  this.sidePanel.panelProps = panelProps;
43398
43783
  this.sidePanel.isOpen = true;
@@ -44102,13 +44487,13 @@ class Tree {
44102
44487
  }
44103
44488
 
44104
44489
  class SelectiveHistory {
44490
+ HEAD_BRANCH;
44491
+ HEAD_OPERATION;
44492
+ tree;
44105
44493
  applyOperation;
44106
44494
  revertOperation;
44107
44495
  buildEmpty;
44108
44496
  buildTransformation;
44109
- HEAD_BRANCH;
44110
- HEAD_OPERATION;
44111
- tree;
44112
44497
  /**
44113
44498
  * The selective history is a data structure used to register changes/updates of a state.
44114
44499
  * Each change/update is called an "operation".
@@ -44129,14 +44514,15 @@ class SelectiveHistory {
44129
44514
  * (used for internal implementation)
44130
44515
  * @param buildTransformation Factory used to build transformations
44131
44516
  */
44132
- constructor(initialOperationId, applyOperation, revertOperation, buildEmpty, buildTransformation) {
44133
- this.applyOperation = applyOperation;
44134
- this.revertOperation = revertOperation;
44135
- this.buildEmpty = buildEmpty;
44136
- this.buildTransformation = buildTransformation;
44517
+ constructor(args) {
44518
+ this.applyOperation = args.applyOperation;
44519
+ this.revertOperation = args.revertOperation;
44520
+ this.buildEmpty = args.buildEmpty;
44521
+ this.buildTransformation = args.buildTransformation;
44137
44522
  this.HEAD_BRANCH = new Branch(this.buildTransformation);
44138
- this.tree = new Tree(buildTransformation, this.HEAD_BRANCH);
44139
- const initial = new Operation(initialOperationId, buildEmpty(initialOperationId));
44523
+ this.tree = new Tree(this.buildTransformation, this.HEAD_BRANCH);
44524
+ const initialOperationId = args.initialOperationId;
44525
+ const initial = new Operation(initialOperationId, this.buildEmpty(initialOperationId));
44140
44526
  this.tree.insertOperationLast(this.HEAD_BRANCH, initial);
44141
44527
  this.HEAD_OPERATION = initial;
44142
44528
  }
@@ -44250,21 +44636,27 @@ class SelectiveHistory {
44250
44636
  }
44251
44637
  }
44252
44638
 
44253
- function buildRevisionLog(initialRevisionId, recordChanges, dispatch) {
44254
- return new SelectiveHistory(initialRevisionId, (revision) => {
44255
- const commands = revision.commands.slice();
44256
- const { changes } = recordChanges(() => {
44257
- for (const command of commands) {
44258
- dispatch(command);
44259
- }
44260
- });
44261
- revision.setChanges(changes);
44262
- }, (revision) => revertChanges([revision]), (id) => new Revision(id, "empty", []), {
44263
- with: (revision) => (toTransform) => {
44264
- return new Revision(toTransform.id, toTransform.clientId, transformAll(toTransform.commands, revision.commands), toTransform.rootCommand);
44639
+ function buildRevisionLog(args) {
44640
+ return new SelectiveHistory({
44641
+ initialOperationId: args.initialRevisionId,
44642
+ applyOperation: (revision) => {
44643
+ const commands = revision.commands.slice();
44644
+ const { changes } = args.recordChanges(() => {
44645
+ for (const command of commands) {
44646
+ args.dispatch(command);
44647
+ }
44648
+ });
44649
+ revision.setChanges(changes);
44265
44650
  },
44266
- without: (revision) => (toTransform) => {
44267
- return new Revision(toTransform.id, toTransform.clientId, transformAll(toTransform.commands, revision.commands.map(inverseCommand).flat()), toTransform.rootCommand);
44651
+ revertOperation: (revision) => revertChanges([revision]),
44652
+ buildEmpty: (id) => new Revision(id, "empty", []),
44653
+ buildTransformation: {
44654
+ with: (revision) => (toTransform) => {
44655
+ return new Revision(toTransform.id, toTransform.clientId, transformAll(toTransform.commands, revision.commands), toTransform.rootCommand, undefined, toTransform.timestamp);
44656
+ },
44657
+ without: (revision) => (toTransform) => {
44658
+ return new Revision(toTransform.id, toTransform.clientId, transformAll(toTransform.commands, revision.commands.map(inverseCommand).flat()), toTransform.rootCommand, undefined, toTransform.timestamp);
44659
+ },
44268
44660
  },
44269
44661
  });
44270
44662
  }
@@ -45937,11 +46329,21 @@ function addBorders(borders) {
45937
46329
  for (let border of Object.values(borders)) {
45938
46330
  borderNodes.push(escapeXml /*xml*/ `
45939
46331
  <border>
45940
- <left ${formatBorderAttribute(border["left"])} />
45941
- <right ${formatBorderAttribute(border["right"])} />
45942
- <top ${formatBorderAttribute(border["top"])} />
45943
- <bottom ${formatBorderAttribute(border["bottom"])} />
45944
- <diagonal ${formatBorderAttribute(border["diagonal"])} />
46332
+ <left ${formatBorderAttribute(border["left"])}>
46333
+ ${addBorderColor(border["left"])}
46334
+ </left>
46335
+ <right ${formatBorderAttribute(border["right"])}>
46336
+ ${addBorderColor(border["right"])}
46337
+ </right>
46338
+ <top ${formatBorderAttribute(border["top"])}>
46339
+ ${addBorderColor(border["top"])}
46340
+ </top>
46341
+ <bottom ${formatBorderAttribute(border["bottom"])}>
46342
+ ${addBorderColor(border["bottom"])}
46343
+ </bottom>
46344
+ <diagonal ${formatBorderAttribute(border["diagonal"])}>
46345
+ ${addBorderColor(border["diagonal"])}
46346
+ </diagonal>
45945
46347
  </border>
45946
46348
  `);
45947
46349
  }
@@ -45955,10 +46357,15 @@ function formatBorderAttribute(description) {
45955
46357
  if (!description) {
45956
46358
  return escapeXml ``;
45957
46359
  }
45958
- return formatAttributes([
45959
- ["style", description.style],
45960
- ["color", toXlsxHexColor(description.color.rgb)],
45961
- ]);
46360
+ return formatAttributes([["style", description.style]]);
46361
+ }
46362
+ function addBorderColor(description) {
46363
+ if (!description) {
46364
+ return escapeXml ``;
46365
+ }
46366
+ return escapeXml /*xml*/ `
46367
+ <color ${formatAttributes([["rgb", toXlsxHexColor(description.color.rgb)]])}/>
46368
+ `;
45962
46369
  }
45963
46370
  function addStyles(styles) {
45964
46371
  const styleNodes = [];
@@ -46602,6 +47009,7 @@ class Model extends EventBus {
46602
47009
  this.setupCorePlugin(Plugin, workbookData);
46603
47010
  }
46604
47011
  Object.assign(this.getters, this.coreGetters);
47012
+ this.session.loadInitialMessages(stateUpdateMessages);
46605
47013
  for (let Plugin of statefulUIPluginRegistry.getAll()) {
46606
47014
  const plugin = this.setupUiPlugin(Plugin);
46607
47015
  this.statefulUIPlugins.push(plugin);
@@ -46629,8 +47037,6 @@ class Model extends EventBus {
46629
47037
  // This should be done after construction of LocalHistory due to order of
46630
47038
  // events
46631
47039
  this.setupSessionEvents();
46632
- // Load the initial revisions
46633
- this.session.loadInitialMessages(stateUpdateMessages);
46634
47040
  this.joinSession();
46635
47041
  if (config.snapshotRequested) {
46636
47042
  this.session.snapshot(this.exportData());
@@ -46694,14 +47100,18 @@ class Model extends EventBus {
46694
47100
  this.finalize();
46695
47101
  }
46696
47102
  setupSession(revisionId) {
46697
- const session = new Session(buildRevisionLog(revisionId, this.state.recordChanges.bind(this.state), (command) => {
46698
- const result = this.checkDispatchAllowed(command);
46699
- if (!result.isSuccessful) {
46700
- return;
46701
- }
46702
- this.isReplayingCommand = true;
46703
- this.dispatchToHandlers(this.coreHandlers, command);
46704
- this.isReplayingCommand = false;
47103
+ const session = new Session(buildRevisionLog({
47104
+ initialRevisionId: revisionId,
47105
+ recordChanges: this.state.recordChanges.bind(this.state),
47106
+ dispatch: (command) => {
47107
+ const result = this.checkDispatchAllowed(command);
47108
+ if (!result.isSuccessful) {
47109
+ return;
47110
+ }
47111
+ this.isReplayingCommand = true;
47112
+ this.dispatchToHandlers(this.coreHandlers, command);
47113
+ this.isReplayingCommand = false;
47114
+ },
46705
47115
  }), this.config.transportService, revisionId);
46706
47116
  return session;
46707
47117
  }
@@ -47100,6 +47510,6 @@ exports.setTranslationMethod = setTranslationMethod;
47100
47510
  exports.tokenize = tokenize;
47101
47511
 
47102
47512
 
47103
- __info__.version = '16.3.0-alpha.7';
47104
- __info__.date = '2023-05-02T13:08:09.275Z';
47105
- __info__.hash = '77c8f5f';
47513
+ __info__.version = '16.3.0-alpha.8';
47514
+ __info__.date = '2023-05-12T10:59:42.550Z';
47515
+ __info__.hash = 'afee7d5';