@odoo/o-spreadsheet 16.1.5 → 16.1.7

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.
@@ -115,88 +115,88 @@ const LINK_COLOR = "#01666b";
115
115
  const FILTERS_COLOR = "#188038";
116
116
  const BACKGROUND_HEADER_FILTER_COLOR = "#E6F4EA";
117
117
  const BACKGROUND_HEADER_SELECTED_FILTER_COLOR = "#CEEAD6";
118
- // Color picker
118
+ // Color picker defaults as upper case HEX to match `toHex`helper
119
119
  const COLOR_PICKER_DEFAULTS = [
120
120
  "#000000",
121
121
  "#434343",
122
122
  "#666666",
123
123
  "#999999",
124
- "#b7b7b7",
125
- "#cccccc",
126
- "#d9d9d9",
127
- "#efefef",
128
- "#f3f3f3",
129
- "#ffffff",
124
+ "#B7B7B7",
125
+ "#CCCCCC",
126
+ "#D9D9D9",
127
+ "#EFEFEF",
128
+ "#F3F3F3",
129
+ "#FFFFFF",
130
130
  "#980000",
131
- "#ff0000",
132
- "#ff9900",
133
- "#ffff00",
134
- "#00ff00",
135
- "#00ffff",
136
- "#4a86e8",
137
- "#0000ff",
138
- "#9900ff",
139
- "#ff00ff",
140
- "#e6b8af",
141
- "#f4cccc",
142
- "#fce5cd",
143
- "#fff2cc",
144
- "#d9ead3",
145
- "#d0e0e3",
146
- "#c9daf8",
147
- "#cfe2f3",
148
- "#d9d2e9",
149
- "#ead1dc",
150
- "#dd7e6b",
151
- "#ea9999",
152
- "#f9cb9c",
153
- "#ffe599",
154
- "#b6d7a8",
155
- "#a2c4c9",
156
- "#a4c2f4",
157
- "#9fc5e8",
158
- "#b4a7d6",
159
- "#d5a6bd",
160
- "#cc4125",
161
- "#e06666",
162
- "#f6b26b",
163
- "#ffd966",
164
- "#93c47d",
165
- "#76a5af",
166
- "#6d9eeb",
167
- "#6fa8dc",
168
- "#8e7cc3",
169
- "#c27ba0",
170
- "#a61c00",
171
- "#cc0000",
172
- "#e69138",
173
- "#f1c232",
174
- "#6aa84f",
175
- "#45818e",
176
- "#3c78d8",
177
- "#3d85c6",
178
- "#674ea7",
179
- "#a64d79",
180
- "#85200c",
131
+ "#FF0000",
132
+ "#FF9900",
133
+ "#FFFF00",
134
+ "#00FF00",
135
+ "#00FFFF",
136
+ "#4A86E8",
137
+ "#0000FF",
138
+ "#9900FF",
139
+ "#FF00FF",
140
+ "#E6B8AF",
141
+ "#F4CCCC",
142
+ "#FCE5CD",
143
+ "#FFF2CC",
144
+ "#D9EAD3",
145
+ "#D0E0E3",
146
+ "#C9DAF8",
147
+ "#CFE2F3",
148
+ "#D9D2E9",
149
+ "#EAD1DC",
150
+ "#DD7E6B",
151
+ "#EA9999",
152
+ "#F9CB9C",
153
+ "#FFE599",
154
+ "#B6D7A8",
155
+ "#A2C4C9",
156
+ "#A4C2F4",
157
+ "#9FC5E8",
158
+ "#B4A7D6",
159
+ "#D5A6BD",
160
+ "#CC4125",
161
+ "#E06666",
162
+ "#F6B26B",
163
+ "#FFD966",
164
+ "#93C47D",
165
+ "#76A5AF",
166
+ "#6D9EEB",
167
+ "#6FA8DC",
168
+ "#8E7CC3",
169
+ "#C27BA0",
170
+ "#A61C00",
171
+ "#CC0000",
172
+ "#E69138",
173
+ "#F1C232",
174
+ "#6AA84F",
175
+ "#45818E",
176
+ "#3C78D8",
177
+ "#3D85C6",
178
+ "#674EA7",
179
+ "#A64D79",
180
+ "#85200C",
181
181
  "#990000",
182
- "#b45f06",
183
- "#bf9000",
184
- "#38761d",
185
- "#134f5c",
186
- "#1155cc",
187
- "#0b5394",
188
- "#351c75",
189
- "#741b47",
190
- "#5b0f00",
182
+ "#B45F06",
183
+ "#BF9000",
184
+ "#38761D",
185
+ "#134F5C",
186
+ "#1155CC",
187
+ "#0B5394",
188
+ "#351C75",
189
+ "#741B47",
190
+ "#5B0F00",
191
191
  "#660000",
192
- "#783f04",
193
- "#7f6000",
194
- "#274e13",
195
- "#0c343d",
196
- "#1c4587",
192
+ "#783F04",
193
+ "#7F6000",
194
+ "#274E13",
195
+ "#0C343D",
196
+ "#1C4587",
197
197
  "#073763",
198
- "#20124d",
199
- "#4c1130",
198
+ "#20124D",
199
+ "#4C1130",
200
200
  ];
201
201
  // Dimensions
202
202
  const MIN_ROW_HEIGHT = 10;
@@ -235,7 +235,7 @@ const DEFAULT_FONT = "'Roboto', arial";
235
235
  const DEFAULT_BORDER_DESC = ["thin", "#000"];
236
236
  const DEFAULT_FILTER_BORDER_DESC = ["thin", FILTERS_COLOR];
237
237
  // DateTimeRegex
238
- const DATETIME_FORMAT = /[ymd:]/;
238
+ const DATETIME_FORMAT = /[ymdhs:]/;
239
239
  // Ranges
240
240
  const INCORRECT_RANGE_STRING = CellErrorType.InvalidReference;
241
241
  // Max Number of history steps kept in memory
@@ -272,11 +272,13 @@ var ComponentsImportance;
272
272
  ComponentsImportance[ComponentsImportance["Highlight"] = 5] = "Highlight";
273
273
  ComponentsImportance[ComponentsImportance["Figure"] = 10] = "Figure";
274
274
  ComponentsImportance[ComponentsImportance["ScrollBar"] = 15] = "ScrollBar";
275
- ComponentsImportance[ComponentsImportance["Dropdown"] = 16] = "Dropdown";
276
- ComponentsImportance[ComponentsImportance["Composer"] = 20] = "Composer";
275
+ ComponentsImportance[ComponentsImportance["GridPopover"] = 19] = "GridPopover";
276
+ ComponentsImportance[ComponentsImportance["GridComposer"] = 20] = "GridComposer";
277
+ ComponentsImportance[ComponentsImportance["Dropdown"] = 21] = "Dropdown";
277
278
  ComponentsImportance[ComponentsImportance["ColorPicker"] = 25] = "ColorPicker";
278
279
  ComponentsImportance[ComponentsImportance["IconPicker"] = 25] = "IconPicker";
279
- ComponentsImportance[ComponentsImportance["Popover"] = 30] = "Popover";
280
+ ComponentsImportance[ComponentsImportance["TopBarComposer"] = 30] = "TopBarComposer";
281
+ ComponentsImportance[ComponentsImportance["Popover"] = 35] = "Popover";
280
282
  ComponentsImportance[ComponentsImportance["ChartAnchor"] = 1000] = "ChartAnchor";
281
283
  })(ComponentsImportance || (ComponentsImportance = {}));
282
284
  const DEFAULT_SHEETVIEW_SIZE = 1000;
@@ -1039,6 +1041,8 @@ function transpose2dArray(matrix) {
1039
1041
  return matrix[0].map((_, i) => matrix.map((row) => row[i]));
1040
1042
  }
1041
1043
 
1044
+ const RBA_REGEX = /rgba?\(|\s+|\)/gi;
1045
+ const HEX_MATCH = /^#([A-F\d]{2}){3,4}$/g;
1042
1046
  const colors$1 = [
1043
1047
  "#eb6d00",
1044
1048
  "#0074d9",
@@ -1064,7 +1068,10 @@ function colorNumberString(color) {
1064
1068
  }
1065
1069
  /**
1066
1070
  * Converts any CSS color value to a standardized hex6 value.
1067
- * Accepts: hex3, hex6, hex8 and rgb (rgba is not supported)
1071
+ * Accepts: hex3, hex6, hex8, rgb[1] and rgba[1].
1072
+ *
1073
+ * [1] under the form rgb(r, g, b, a?) or rgba(r, g, b, a?)
1074
+ * with r,g,b ∈ [0, 255] and a ∈ [0, 1]
1068
1075
  *
1069
1076
  * toHex("#ABC")
1070
1077
  * >> "#AABBCC"
@@ -1075,27 +1082,31 @@ function colorNumberString(color) {
1075
1082
  * toHex("rgb(30, 80, 16)")
1076
1083
  * >> "#1E5010"
1077
1084
  *
1085
+ * * toHex("rgb(30, 80, 16, 0.5)")
1086
+ * >> "#1E501080"
1087
+ *
1078
1088
  */
1079
1089
  function toHex(color) {
1080
- if (color.includes("rgba")) {
1081
- throw new Error(`rgba() conversion currently not supported: ${color}`);
1090
+ let hexColor = color;
1091
+ if (color.startsWith("rgb")) {
1092
+ hexColor = rgbaStringToHex(color);
1082
1093
  }
1083
- if (color.includes("rgb")) {
1084
- return rgbToHex6(color);
1085
- }
1086
- color = color.replace("#", "").toUpperCase();
1087
- if (color.length === 3 || color.length === 4) {
1088
- color = color.split("").reduce((acc, h) => acc + h + h, "");
1094
+ else {
1095
+ hexColor = color.replace("#", "").toUpperCase();
1096
+ if (hexColor.length === 3 || hexColor.length === 4) {
1097
+ hexColor = hexColor.split("").reduce((acc, h) => acc + h + h, "");
1098
+ }
1099
+ hexColor = `#${hexColor}`;
1089
1100
  }
1090
- if (color.replace(/[a-f0-9]/gi, "") !== "") {
1091
- throw new Error("invalid color");
1101
+ if (!hexColor.match(HEX_MATCH)) {
1102
+ throw new Error(`invalid color input: ${color}`);
1092
1103
  }
1093
- return "#" + color;
1104
+ return hexColor;
1094
1105
  }
1095
1106
  function isColorValid(color) {
1096
1107
  try {
1097
- const { r, g, b, a } = colorToRGBA(color);
1098
- return (isColorValueValid(r) && isColorValueValid(g) && isColorValueValid(b) && isColorValueValid(a));
1108
+ toHex(color);
1109
+ return true;
1099
1110
  }
1100
1111
  catch (error) {
1101
1112
  return false;
@@ -1128,15 +1139,29 @@ function relativeLuminance(color) {
1128
1139
  /**
1129
1140
  * Convert a CSS rgb color string to a standardized hex6 color value.
1130
1141
  *
1131
- * rgbToHex6("rgb(30, 80, 16)")
1142
+ * rgbaStringToHex("rgb(30, 80, 16)")
1132
1143
  * >> "#1E5010"
1144
+ *
1145
+ * rgbaStringToHex("rgba(30, 80, 16, 0.5)")
1146
+ * >> "#1E501080"
1147
+ *
1148
+ * DOES NOT SUPPORT NON INTEGER RGB VALUES
1133
1149
  */
1134
- function rgbToHex6(color) {
1135
- return ("#" +
1136
- concat(color
1137
- .slice(4, -1)
1138
- .split(",")
1139
- .map((valueString) => parseInt(valueString, 10).toString(16).padStart(2, "0"))).toUpperCase());
1150
+ function rgbaStringToHex(color) {
1151
+ const stringVals = color.replace(RBA_REGEX, "").split(",");
1152
+ let alphaHex = 255;
1153
+ if (stringVals.length !== 3 && stringVals.length !== 4) {
1154
+ throw new Error("invalid color");
1155
+ }
1156
+ else if (stringVals.length === 4) {
1157
+ const alpha = parseFloat(stringVals.pop() || "1");
1158
+ alphaHex = Math.round((alpha || 1) * 255);
1159
+ }
1160
+ const vals = stringVals.map((val) => parseInt(val, 10));
1161
+ if (alphaHex !== 255) {
1162
+ vals.push(alphaHex);
1163
+ }
1164
+ return "#" + concat(vals.map((value) => value.toString(16).padStart(2, "0"))).toUpperCase();
1140
1165
  }
1141
1166
  /**
1142
1167
  * RGBA to HEX representation (#RRGGBBAA).
@@ -1158,7 +1183,7 @@ function rgbaToHex(rgba) {
1158
1183
  a = "0" + a;
1159
1184
  if (a === "ff")
1160
1185
  a = "";
1161
- return "#" + r + g + b + a;
1186
+ return ("#" + r + g + b + a).toUpperCase();
1162
1187
  }
1163
1188
  /**
1164
1189
  * Color string to RGBA representation
@@ -1279,6 +1304,9 @@ function rgbaToHSLA(rgba) {
1279
1304
  s = +(s * 100).toFixed(1);
1280
1305
  l = +(l * 100).toFixed(1);
1281
1306
  return { a: rgba.a, h, s, l };
1307
+ }
1308
+ function isSameColor(color1, color2) {
1309
+ return isColorValid(color1) && isColorValid(color2) && toHex(color1) === toHex(color2);
1282
1310
  }
1283
1311
 
1284
1312
  /** Reference of a cell (eg. A1, $B$5) */
@@ -1430,7 +1458,7 @@ function scrollDelay(value) {
1430
1458
  * formula, commas are used to separate arguments
1431
1459
  * - it does not support % symbol, in formulas % is an operator
1432
1460
  */
1433
- const formulaNumberRegexp = /^-?\d+(\.?\d*(e\d+)?)?|^-?\.\d+/;
1461
+ const formulaNumberRegexp = /(^-?\d+(\.?\d*(e\d+)?)?|^-?\.\d+)(?!\w|!)/;
1434
1462
  const pIntegerAndDecimals = "(\\d+(,\\d{3,})*(\\.\\d*)?)"; // pattern that match integer number with or without decimal digits
1435
1463
  const pOnlyDecimals = "(\\.\\d+)"; // pattern that match only expression with decimal digits
1436
1464
  const pScientificFormat = "(e(\\+|-)?\\d+)?"; // pattern that match scientific format between zero and one time (should be placed before pPercentFormat)
@@ -3025,6 +3053,7 @@ const CHART_PADDING_RATIO = 0.02;
3025
3053
  const LINE_HEIGHT = 1.2;
3026
3054
  css /* scss */ `
3027
3055
  div.o-scorecard {
3056
+ font-family: ${DEFAULT_FONT};
3028
3057
  user-select: none;
3029
3058
  background-color: white;
3030
3059
  display: flex;
@@ -5649,6 +5678,11 @@ class Popover extends owl.Component {
5649
5678
  this.currentPosition = newPosition;
5650
5679
  });
5651
5680
  }
5681
+ get popoverStyle() {
5682
+ return cssPropertiesToCss({
5683
+ "z-index": `${this.props.zIndex}`,
5684
+ });
5685
+ }
5652
5686
  }
5653
5687
  Popover.template = "o-spreadsheet-Popover";
5654
5688
  Popover.defaultProps = {
@@ -5657,6 +5691,7 @@ Popover.defaultProps = {
5657
5691
  onMouseWheel: () => { },
5658
5692
  onPopoverMoved: () => { },
5659
5693
  onPopoverHidden: () => { },
5694
+ zIndex: ComponentsImportance.Popover,
5660
5695
  };
5661
5696
  Popover.props = {
5662
5697
  anchorRect: Object,
@@ -5668,6 +5703,7 @@ Popover.props = {
5668
5703
  onMouseWheel: { type: Function, optional: true },
5669
5704
  onPopoverHidden: { type: Function, optional: true },
5670
5705
  onPopoverMoved: { type: Function, optional: true },
5706
+ zIndex: { type: Number, optional: true },
5671
5707
  slots: Object,
5672
5708
  };
5673
5709
  class PopoverPositionContext {
@@ -16525,6 +16561,7 @@ class EditionPlugin extends UIPlugin {
16525
16561
  this.setContent(this.initialContent || "");
16526
16562
  }
16527
16563
  setContent(text, selection, raise) {
16564
+ text = text.replace(/[\r\n]/g, "");
16528
16565
  const isNewCurrentContent = this.currentContent !== text;
16529
16566
  this.currentContent = text;
16530
16567
  if (selection) {
@@ -16809,6 +16846,8 @@ class ContentEditableHelper {
16809
16846
  start = 0;
16810
16847
  if (end > this.el.textContent.length)
16811
16848
  end = this.el.textContent.length;
16849
+ if (start > this.el.textContent.length)
16850
+ start = this.el.textContent.length;
16812
16851
  }
16813
16852
  let startNode = this.findChildAtCharacterIndex(start);
16814
16853
  let endNode = this.findChildAtCharacterIndex(end);
@@ -17035,10 +17074,10 @@ css /* scss */ `
17035
17074
  padding: 0;
17036
17075
  margin: 0;
17037
17076
  border: 0;
17038
- z-index: ${ComponentsImportance.Composer};
17039
17077
  flex-grow: 1;
17040
17078
  max-height: inherit;
17041
17079
  .o-composer {
17080
+ font-family: ${DEFAULT_FONT};
17042
17081
  caret-color: black;
17043
17082
  padding-left: 3px;
17044
17083
  padding-right: 3px;
@@ -17073,6 +17112,10 @@ css /* scss */ `
17073
17112
  .o-topbar-toolbar .o-composer-container:focus-within {
17074
17113
  border: 1px solid ${SELECTION_BORDER_COLOR};
17075
17114
  }
17115
+
17116
+ .o-topbar-toolbar .o-composer-container {
17117
+ z-index: ${ComponentsImportance.TopBarComposer};
17118
+ }
17076
17119
  `;
17077
17120
  class Composer extends owl.Component {
17078
17121
  constructor() {
@@ -17521,7 +17564,7 @@ const SCROLLBAR_HIGHT = 15;
17521
17564
  const COMPOSER_BORDER_WIDTH = 3 * 0.4 * window.devicePixelRatio || 1;
17522
17565
  css /* scss */ `
17523
17566
  div.o-grid-composer {
17524
- z-index: ${ComponentsImportance.Composer};
17567
+ z-index: ${ComponentsImportance.GridComposer};
17525
17568
  box-sizing: border-box;
17526
17569
  position: absolute;
17527
17570
  border: ${COMPOSER_BORDER_WIDTH}px solid ${SELECTION_BORDER_COLOR};
@@ -20057,6 +20100,9 @@ class ColorPicker extends owl.Component {
20057
20100
  const { display, background, left, top } = this.state.style;
20058
20101
  return `display:${display};${display === "block" ? `background-color:${background};left:${left};top:${top};` : ""}`;
20059
20102
  }
20103
+ isSameColor(color1, color2) {
20104
+ return isSameColor(color1, color2);
20105
+ }
20060
20106
  }
20061
20107
  ColorPicker.template = "o-spreadsheet-ColorPicker";
20062
20108
  ColorPicker.defaultProps = {
@@ -20201,6 +20247,12 @@ class GaugeChartDesignPanel extends owl.Component {
20201
20247
  const cancelledReasons = [...(((_a = this.state.sectionRuleDispatchResult) === null || _a === void 0 ? void 0 : _a.reasons) || [])];
20202
20248
  return cancelledReasons.map((error) => ChartTerms.Errors[error] || ChartTerms.Errors.Unexpected);
20203
20249
  }
20250
+ onClick(ev) {
20251
+ this.state.openedMenu = undefined;
20252
+ }
20253
+ setup() {
20254
+ owl.useExternalListener(window, "click", this.onClick);
20255
+ }
20204
20256
  updateBackgroundColor(color) {
20205
20257
  this.state.openedMenu = undefined;
20206
20258
  this.props.updateChart({
@@ -20384,6 +20436,12 @@ class ScorecardChartDesignPanel extends owl.Component {
20384
20436
  openedColorPicker: undefined,
20385
20437
  });
20386
20438
  }
20439
+ onClick(ev) {
20440
+ this.state.openedColorPicker = undefined;
20441
+ }
20442
+ setup() {
20443
+ owl.useExternalListener(window, "click", this.onClick);
20444
+ }
20387
20445
  updateTitle(ev) {
20388
20446
  this.props.updateChart({
20389
20447
  title: ev.target.value,
@@ -22287,6 +22345,9 @@ class GridOverlay extends owl.Component {
22287
22345
  owl.onMounted(() => {
22288
22346
  resizeObserver.observe(this.gridOverlayEl);
22289
22347
  });
22348
+ owl.onWillUnmount(() => {
22349
+ resizeObserver.disconnect();
22350
+ });
22290
22351
  useTouchMove(this.gridOverlay, this.props.onGridMoved, () => {
22291
22352
  const { scrollY } = this.env.model.getters.getActiveSheetDOMScrollInfo();
22292
22353
  return scrollY > 0;
@@ -22343,6 +22404,10 @@ GridOverlay.props = {
22343
22404
  };
22344
22405
 
22345
22406
  class GridPopover extends owl.Component {
22407
+ constructor() {
22408
+ super(...arguments);
22409
+ this.zIndex = ComponentsImportance.GridPopover;
22410
+ }
22346
22411
  get cellPopover() {
22347
22412
  const popover = this.env.model.getters.getCellPopover(this.props.hoveredCell);
22348
22413
  if (!popover.isOpen) {
@@ -23707,6 +23772,10 @@ class Grid extends owl.Component {
23707
23772
  }
23708
23773
  }
23709
23774
  onInput(ev) {
23775
+ // the user meant to paste in the sheet, not open the composer with the pasted content
23776
+ if (!ev.isComposing && ev.inputType === "insertFromPaste") {
23777
+ return;
23778
+ }
23710
23779
  if (ev.data) {
23711
23780
  // if the user types a character on the grid, it means he wants to start composing the selected cell with that
23712
23781
  // character
@@ -24556,6 +24625,70 @@ function fixXlsxUnicode(str) {
24556
24625
  });
24557
24626
  }
24558
24627
 
24628
+ const XLSX_DATE_FORMAT_REGEX = /^(yy|yyyy|m{1,5}|d{1,4}|h{1,2}|s{1,2}|am\/pm|a\/m|\s|-|\/|\.|:)+$/i;
24629
+ /**
24630
+ * Convert excel format to o_spreadsheet format
24631
+ *
24632
+ * Excel format are defined in openXML §18.8.31
24633
+ */
24634
+ function convertXlsxFormat(numFmtId, formats, warningManager) {
24635
+ var _a, _b, _c;
24636
+ if (numFmtId === 0) {
24637
+ return undefined;
24638
+ }
24639
+ // Format is either defined in the imported data, or the formatId is defined in openXML §18.8.30
24640
+ let format = XLSX_FORMATS_CONVERSION_MAP[numFmtId] || ((_a = formats.find((f) => f.id === numFmtId)) === null || _a === void 0 ? void 0 : _a.format);
24641
+ if (format) {
24642
+ try {
24643
+ let convertedFormat = format.replace(/(.*?);.*/, "$1"); // only take first part of multi-part format
24644
+ convertedFormat = convertedFormat.replace(/\[(.*)-[A-Z0-9]{3}\]/g, "[$1]"); // remove currency and locale/date system/number system info (ECMA §18.8.31)
24645
+ convertedFormat = convertedFormat.replace(/\[\$\]/g, ""); // remove empty bocks
24646
+ // Quotes in format escape sequences of characters. ATM we only support [$...] blocks to escape characters, and only one of them per format
24647
+ const numberOfQuotes = ((_b = convertedFormat.match(/"/g)) === null || _b === void 0 ? void 0 : _b.length) || 0;
24648
+ const numberOfOpenBrackets = ((_c = convertedFormat.match(/\[/g)) === null || _c === void 0 ? void 0 : _c.length) || 0;
24649
+ if (numberOfQuotes / 2 + numberOfOpenBrackets > 1) {
24650
+ throw new Error("Multiple escaped blocks in format");
24651
+ }
24652
+ convertedFormat = convertedFormat.replace(/"(.*)"/g, "[$$$1]"); // replace '"..."' by '[$...]'
24653
+ convertedFormat = convertedFormat.replace(/_.{1}/g, ""); // _ == ignore width of next char for align purposes. Not supported ATM
24654
+ convertedFormat = convertedFormat.replace(/\*.{1}/g, ""); // * == repeat next character enough to fill the line. Not supported ATM
24655
+ convertedFormat = convertedFormat.replace(/\\ /g, " "); // unescape spaces
24656
+ convertedFormat = convertedFormat.replace(/\\./g, (match) => match[1]); // unescape other characters
24657
+ if (isXlsxDateFormat(convertedFormat)) {
24658
+ convertedFormat = convertDateFormat$1(convertedFormat);
24659
+ }
24660
+ if (isFormatSupported(convertedFormat)) {
24661
+ return convertedFormat;
24662
+ }
24663
+ }
24664
+ catch (e) { }
24665
+ }
24666
+ warningManager.generateNotSupportedWarning(WarningTypes.NumFmtIdNotSupported, format || `nmFmtId ${numFmtId}`);
24667
+ return undefined;
24668
+ }
24669
+ function isFormatSupported(format) {
24670
+ try {
24671
+ formatValue(0, format);
24672
+ return true;
24673
+ }
24674
+ catch (e) {
24675
+ return false;
24676
+ }
24677
+ }
24678
+ function isXlsxDateFormat(format) {
24679
+ return format.match(XLSX_DATE_FORMAT_REGEX) !== null;
24680
+ }
24681
+ function convertDateFormat$1(format) {
24682
+ // Some of these aren't defined neither in the OpenXML spec not the Xlsx extension of OpenXML,
24683
+ // but can still occur and are supported by Excel/Google sheets
24684
+ format = format.toLowerCase();
24685
+ format = format.replace(/mmmmm/g, "mmm");
24686
+ format = format.replace(/am\/pm|a\/m/g, "a");
24687
+ format = format.replace(/hhhh/g, "hh");
24688
+ format = format.replace(/\bh\b/g, "hh");
24689
+ return format;
24690
+ }
24691
+
24559
24692
  function convertBorders(data, warningManager) {
24560
24693
  const borderArray = data.borders.map((border) => {
24561
24694
  addBorderWarnings(border, warningManager);
@@ -24620,41 +24753,6 @@ function convertFormats(data, warningManager) {
24620
24753
  }
24621
24754
  return arrayToObject(formats, 1);
24622
24755
  }
24623
- /**
24624
- * Convert excel format to o_spreadsheet format
24625
- *
24626
- * Excel format are defined in openXML §18.8.31
24627
- */
24628
- function convertXlsxFormat(numFmtId, formats, warningManager) {
24629
- var _a, _b, _c;
24630
- if (numFmtId === 0) {
24631
- return undefined;
24632
- }
24633
- // Format is either defined in the imported data, or the formatId is defined in openXML §18.8.30
24634
- let format = XLSX_FORMATS_CONVERSION_MAP[numFmtId] || ((_a = formats.find((f) => f.id === numFmtId)) === null || _a === void 0 ? void 0 : _a.format);
24635
- if (format) {
24636
- try {
24637
- let convertedFormat = format.replace(/(.*?);.*/, "$1"); // only take first part of multi-part format
24638
- convertedFormat = convertedFormat.replace(/\[(.*)-[A-Z0-9]{3}\]/g, "[$1]"); // remove currency and locale/date system/number system info (ECMA §18.8.31)
24639
- convertedFormat = convertedFormat.replace(/\[\$\]/g, ""); // remove empty bocks
24640
- // Quotes in format escape sequences of characters. ATM we only support [$...] blocks to escape characters, and only one of them per format
24641
- const numberOfQuotes = ((_b = convertedFormat.match(/"/g)) === null || _b === void 0 ? void 0 : _b.length) || 0;
24642
- const numberOfOpenBrackets = ((_c = convertedFormat.match(/\[/g)) === null || _c === void 0 ? void 0 : _c.length) || 0;
24643
- if (numberOfQuotes / 2 + numberOfOpenBrackets > 1) {
24644
- throw new Error("Multiple escaped blocks in format");
24645
- }
24646
- convertedFormat = convertedFormat.replace(/"(.*)"/g, "[$$$1]"); // replace '"..."' by '[$...]'
24647
- convertedFormat = convertedFormat.replace(/_.{1}/g, ""); // _ == ignore with of next char for align purposes. Not supported ATM
24648
- convertedFormat = convertedFormat.replace(/\*.{1}/g, ""); // * == repeat next character enough to fill the line. Not supported ATM
24649
- convertedFormat = convertedFormat.replace(/\\ /g, " "); // unescape spaces
24650
- formatValue(0, convertedFormat);
24651
- return convertedFormat;
24652
- }
24653
- catch (e) { }
24654
- }
24655
- warningManager.generateNotSupportedWarning(WarningTypes.NumFmtIdNotSupported, format || `nmFmtId ${numFmtId}`);
24656
- return undefined;
24657
- }
24658
24756
  /**
24659
24757
  * We currently only support only a set of font sizes, we cannot define new font sizes.
24660
24758
  * This function adapts an arbitrary font size to the closest supported font size.
@@ -30519,6 +30617,7 @@ class RangeAdapter {
30519
30617
  }
30520
30618
  else if (range.zone[start] >= min && range.zone[end] <= max) {
30521
30619
  changeType = "REMOVE";
30620
+ newRange = range.clone({ ...this.getInvalidRange() });
30522
30621
  }
30523
30622
  else if (range.zone[start] <= max && range.zone[end] >= max) {
30524
30623
  const toRemove = max - range.zone[start] + 1;
@@ -30584,8 +30683,10 @@ class RangeAdapter {
30584
30683
  return { changeType: "NONE" };
30585
30684
  }
30586
30685
  const invalidSheetName = this.getters.getSheetName(cmd.sheetId);
30587
- const sheetId = "";
30588
- range = range.clone({ sheetId, invalidSheetName });
30686
+ range = range.clone({
30687
+ ...this.getInvalidRange(),
30688
+ invalidSheetName,
30689
+ });
30589
30690
  return { changeType: "REMOVE", range };
30590
30691
  }, cmd.sheetId);
30591
30692
  break;
@@ -30845,6 +30946,15 @@ class RangeAdapter {
30845
30946
  }
30846
30947
  return str;
30847
30948
  }
30949
+ getInvalidRange() {
30950
+ return {
30951
+ parts: [],
30952
+ prefixSheet: false,
30953
+ zone: { left: -1, top: -1, right: -1, bottom: -1 },
30954
+ sheetId: "",
30955
+ invalidXc: INCORRECT_RANGE_STRING,
30956
+ };
30957
+ }
30848
30958
  }
30849
30959
  RangeAdapter.getters = [
30850
30960
  "getRangeString",
@@ -31809,9 +31919,7 @@ class CustomColorsPlugin extends UIPlugin {
31809
31919
  // remove duplicates first to check validity on a reduced
31810
31920
  // set of colors, then normalize to HEX and remove duplicates
31811
31921
  // again
31812
- [...new Set([...usedColors, ...this.customColors])]
31813
- .filter(isColorValid)
31814
- .map((c) => toHex(c).toLowerCase())),
31922
+ [...new Set([...usedColors, ...this.customColors])].filter(isColorValid).map(toHex)),
31815
31923
  ]).filter((color) => !COLOR_PICKER_DEFAULTS.includes(color));
31816
31924
  }
31817
31925
  getColorsFromCells(cells) {
@@ -31872,7 +31980,7 @@ class CustomColorsPlugin extends UIPlugin {
31872
31980
  return [...chartsColors];
31873
31981
  }
31874
31982
  tryToAddColor(color) {
31875
- const formattedColor = toHex(color).toLowerCase();
31983
+ const formattedColor = toHex(color);
31876
31984
  if (color && !COLOR_PICKER_DEFAULTS.includes(formattedColor)) {
31877
31985
  this.customColors.add(formattedColor);
31878
31986
  }
@@ -32877,8 +32985,8 @@ class InternalViewport {
32877
32985
  this.getters = getters;
32878
32986
  this.sheetId = sheetId;
32879
32987
  this.boundaries = boundaries;
32880
- this.width = sizeInGrid.width;
32881
- this.height = sizeInGrid.height;
32988
+ this.viewportWidth = sizeInGrid.width;
32989
+ this.viewportHeight = sizeInGrid.height;
32882
32990
  this.offsetScrollbarX = offsets.x;
32883
32991
  this.offsetScrollbarY = offsets.y;
32884
32992
  this.canScrollVertically = options.canScrollVertically;
@@ -32889,6 +32997,10 @@ class InternalViewport {
32889
32997
  this.adjustViewportOffsetY();
32890
32998
  }
32891
32999
  // PUBLIC
33000
+ /** Returns the maximum size (in Pixels) of the viewport relative to its allocated client size
33001
+ * When the viewport grid size is smaller than its client width (resp. height), it will return
33002
+ * the client width (resp. height).
33003
+ */
32892
33004
  getMaxSize() {
32893
33005
  const lastCol = this.getters.findLastVisibleColRowIndex(this.sheetId, "COL", {
32894
33006
  first: this.boundaries.left,
@@ -32900,20 +33012,24 @@ class InternalViewport {
32900
33012
  });
32901
33013
  const { end: lastColEnd, size: lastColSize } = this.getters.getColDimensions(this.sheetId, lastCol);
32902
33014
  const { end: lastRowEnd, size: lastRowSize } = this.getters.getRowDimensions(this.sheetId, lastRow);
32903
- const leftColIndex = this.searchHeaderIndex("COL", lastColEnd - this.width, 0);
33015
+ const leftColIndex = this.searchHeaderIndex("COL", lastColEnd - this.viewportWidth, 0);
32904
33016
  const leftColSize = this.getters.getColSize(this.sheetId, leftColIndex);
32905
- const leftRowIndex = this.searchHeaderIndex("ROW", lastRowEnd - this.height, 0);
33017
+ const leftRowIndex = this.searchHeaderIndex("ROW", lastRowEnd - this.viewportHeight, 0);
32906
33018
  const topRowSize = this.getters.getRowSize(this.sheetId, leftRowIndex);
32907
- const width = lastColEnd -
32908
- this.offsetCorrectionX +
32909
- (this.canScrollHorizontally
32910
- ? Math.max(DEFAULT_CELL_WIDTH, Math.min(leftColSize, this.width - lastColSize))
32911
- : 0);
32912
- const height = lastRowEnd -
32913
- this.offsetCorrectionY +
32914
- (this.canScrollVertically
32915
- ? Math.max(DEFAULT_CELL_HEIGHT + 5, Math.min(topRowSize, this.height - lastRowSize))
32916
- : 0);
33019
+ let width = lastColEnd - this.offsetCorrectionX;
33020
+ if (this.canScrollHorizontally) {
33021
+ width += Math.max(DEFAULT_CELL_WIDTH, // leave some minimal space to let the user know they scrolled all the way
33022
+ Math.min(leftColSize, this.viewportWidth - lastColSize) // Add pixels that allows the snapping at maximum horizontal scroll
33023
+ );
33024
+ width = Math.max(width, this.viewportWidth); // if the viewport grid size is smaller than its client width, return client width
33025
+ }
33026
+ let height = lastRowEnd - this.offsetCorrectionY;
33027
+ if (this.canScrollVertically) {
33028
+ height += Math.max(DEFAULT_CELL_HEIGHT + 5, // leave some space to let the user know they scrolled all the way
33029
+ Math.min(topRowSize, this.viewportHeight - lastRowSize) // Add pixels that allows the snapping at maximum vertical scroll
33030
+ );
33031
+ height = Math.max(height, this.viewportHeight); // if the viewport grid size is smaller than its client height, return client height
33032
+ }
32917
33033
  return { width, height };
32918
33034
  }
32919
33035
  /**
@@ -32922,7 +33038,7 @@ class InternalViewport {
32922
33038
  * It returns -1 if no column is found.
32923
33039
  */
32924
33040
  getColIndex(x, absolute = false) {
32925
- if (x < this.offsetCorrectionX || x > this.offsetCorrectionX + this.width) {
33041
+ if (x < this.offsetCorrectionX || x > this.offsetCorrectionX + this.viewportWidth) {
32926
33042
  return -1;
32927
33043
  }
32928
33044
  return this.searchHeaderIndex("COL", x - this.offsetCorrectionX, this.left, absolute);
@@ -32933,7 +33049,7 @@ class InternalViewport {
32933
33049
  * It returns -1 if no row is found.
32934
33050
  */
32935
33051
  getRowIndex(y, absolute = false) {
32936
- if (y < this.offsetCorrectionY || y > this.offsetCorrectionY + this.height) {
33052
+ if (y < this.offsetCorrectionY || y > this.offsetCorrectionY + this.viewportHeight) {
32937
33053
  return -1;
32938
33054
  }
32939
33055
  return this.searchHeaderIndex("ROW", y - this.offsetCorrectionY, this.top, absolute);
@@ -32960,7 +33076,7 @@ class InternalViewport {
32960
33076
  adjustPositionX(col) {
32961
33077
  const sheetId = this.sheetId;
32962
33078
  const { start, end } = this.getters.getColDimensions(sheetId, col);
32963
- while (end > this.offsetX + this.offsetCorrectionX + this.width &&
33079
+ while (end > this.offsetX + this.offsetCorrectionX + this.viewportWidth &&
32964
33080
  this.offsetX + this.offsetCorrectionX < start) {
32965
33081
  this.offsetX = this.getters.getColDimensions(sheetId, this.left).end - this.offsetCorrectionX;
32966
33082
  this.offsetScrollbarX = this.offsetX;
@@ -32982,7 +33098,7 @@ class InternalViewport {
32982
33098
  adjustPositionY(row) {
32983
33099
  const sheetId = this.sheetId;
32984
33100
  while (this.getters.getRowDimensions(sheetId, row).end >
32985
- this.offsetY + this.height + this.offsetCorrectionY &&
33101
+ this.offsetY + this.viewportHeight + this.offsetCorrectionY &&
32986
33102
  this.offsetY + this.offsetCorrectionY < this.getters.getRowDimensions(sheetId, row).start) {
32987
33103
  this.offsetY = this.getters.getRowDimensions(sheetId, this.top).end - this.offsetCorrectionY;
32988
33104
  this.offsetScrollbarY = this.offsetY;
@@ -33020,8 +33136,8 @@ class InternalViewport {
33020
33136
  const x = this.getters.getColRowOffset("COL", this.zone.left, targetZone.left) +
33021
33137
  this.offsetCorrectionX;
33022
33138
  const y = this.getters.getColRowOffset("ROW", this.zone.top, targetZone.top) + this.offsetCorrectionY;
33023
- const width = Math.min(this.getters.getColRowOffset("COL", targetZone.left, targetZone.right + 1), this.width);
33024
- const height = Math.min(this.getters.getColRowOffset("ROW", targetZone.top, targetZone.bottom + 1), this.height);
33139
+ const width = Math.min(this.getters.getColRowOffset("COL", targetZone.left, targetZone.right + 1), this.viewportWidth);
33140
+ const height = Math.min(this.getters.getColRowOffset("ROW", targetZone.top, targetZone.bottom + 1), this.viewportHeight);
33025
33141
  return {
33026
33142
  x,
33027
33143
  y,
@@ -33084,12 +33200,12 @@ class InternalViewport {
33084
33200
  adjustViewportOffsetX() {
33085
33201
  if (this.canScrollHorizontally) {
33086
33202
  const { width: viewportWidth } = this.getMaxSize();
33087
- if (this.width + this.offsetScrollbarX > viewportWidth) {
33088
- this.offsetScrollbarX = Math.max(0, viewportWidth - this.width);
33203
+ if (this.viewportWidth + this.offsetScrollbarX > viewportWidth) {
33204
+ this.offsetScrollbarX = Math.max(0, viewportWidth - this.viewportWidth);
33089
33205
  }
33090
33206
  }
33091
33207
  this.left = this.getColIndex(this.offsetScrollbarX, true);
33092
- this.right = this.getColIndex(this.offsetScrollbarX + this.width, true);
33208
+ this.right = this.getColIndex(this.offsetScrollbarX + this.viewportWidth, true);
33093
33209
  if (this.right === -1) {
33094
33210
  this.right = this.boundaries.right;
33095
33211
  }
@@ -33101,12 +33217,12 @@ class InternalViewport {
33101
33217
  adjustViewportOffsetY() {
33102
33218
  if (this.canScrollVertically) {
33103
33219
  const { height: paneHeight } = this.getMaxSize();
33104
- if (this.height + this.offsetScrollbarY > paneHeight) {
33105
- this.offsetScrollbarY = Math.max(0, paneHeight - this.height);
33220
+ if (this.viewportHeight + this.offsetScrollbarY > paneHeight) {
33221
+ this.offsetScrollbarY = Math.max(0, paneHeight - this.viewportHeight);
33106
33222
  }
33107
33223
  }
33108
33224
  this.top = this.getRowIndex(this.offsetScrollbarY, true);
33109
- this.bottom = this.getRowIndex(this.offsetScrollbarY + this.width, true);
33225
+ this.bottom = this.getRowIndex(this.offsetScrollbarY + this.viewportWidth, true);
33110
33226
  if (this.bottom === -1) {
33111
33227
  this.bottom = this.boundaries.bottom;
33112
33228
  }
@@ -33117,7 +33233,7 @@ class InternalViewport {
33117
33233
  adjustViewportZoneX() {
33118
33234
  const sheetId = this.sheetId;
33119
33235
  this.left = this.searchHeaderIndex("COL", this.offsetScrollbarX, this.boundaries.left);
33120
- this.right = Math.min(this.boundaries.right, this.searchHeaderIndex("COL", this.width, this.left));
33236
+ this.right = Math.min(this.boundaries.right, this.searchHeaderIndex("COL", this.viewportWidth, this.left));
33121
33237
  if (this.right === -1) {
33122
33238
  this.right = this.getters.getNumberCols(sheetId) - 1;
33123
33239
  }
@@ -33130,7 +33246,7 @@ class InternalViewport {
33130
33246
  adjustViewportZoneY() {
33131
33247
  const sheetId = this.sheetId;
33132
33248
  this.top = this.searchHeaderIndex("ROW", this.offsetScrollbarY, this.boundaries.top);
33133
- this.bottom = Math.min(this.boundaries.bottom, this.searchHeaderIndex("ROW", this.height, this.top));
33249
+ this.bottom = Math.min(this.boundaries.bottom, this.searchHeaderIndex("ROW", this.viewportHeight, this.top));
33134
33250
  if (this.bottom === -1) {
33135
33251
  this.bottom = this.getters.getNumberRows(sheetId) - 1;
33136
33252
  }
@@ -33392,8 +33508,7 @@ class SheetViewPlugin extends UIPlugin {
33392
33508
  return [...new Set(viewports.map((v) => range(v.top, v.bottom + 1)).flat())].filter((row) => !this.getters.isHeaderHidden(sheetId, "ROW", row));
33393
33509
  }
33394
33510
  /**
33395
- * Return the main viewport maximum size. That is the zone dimension
33396
- * with some bottom and right padding.
33511
+ * Return the main viewport maximum size relative to the client size.
33397
33512
  */
33398
33513
  getMainViewportRect() {
33399
33514
  const sheetId = this.getters.getActiveSheetId();
@@ -33409,8 +33524,8 @@ class SheetViewPlugin extends UIPlugin {
33409
33524
  const { width, height } = this.getMainViewportRect();
33410
33525
  const viewport = this.getMainInternalViewport(sheetId);
33411
33526
  return {
33412
- maxOffsetX: Math.max(0, width - viewport.width + 1),
33413
- maxOffsetY: Math.max(0, height - viewport.height + 1),
33527
+ maxOffsetX: Math.max(0, width - viewport.viewportWidth + 1),
33528
+ maxOffsetY: Math.max(0, height - viewport.viewportHeight + 1),
33414
33529
  };
33415
33530
  }
33416
33531
  getColRowOffsetInViewport(dimension, referenceIndex, index) {
@@ -39894,6 +40009,7 @@ class TopBar extends owl.Component {
39894
40009
  this.state.menuState.parentMenu = menu;
39895
40010
  this.isSelectingMenu = true;
39896
40011
  this.openedEl = ev.target;
40012
+ this.env.model.dispatch("STOP_EDITION");
39897
40013
  }
39898
40014
  closeMenus() {
39899
40015
  this.state.activeTool = "";
@@ -40113,9 +40229,6 @@ css /* scss */ `
40113
40229
  color: #333;
40114
40230
  }
40115
40231
 
40116
- * {
40117
- font-family: "Roboto", "RobotoDraft", Helvetica, Arial, sans-serif;
40118
- }
40119
40232
  &,
40120
40233
  *,
40121
40234
  *:before,
@@ -43911,6 +44024,6 @@ exports.setTranslationMethod = setTranslationMethod;
43911
44024
  exports.tokenize = tokenize;
43912
44025
 
43913
44026
 
43914
- __info__.version = '16.1.5';
43915
- __info__.date = '2023-03-16T12:12:08.747Z';
43916
- __info__.hash = '12f7a8e';
44027
+ __info__.version = '16.1.7';
44028
+ __info__.date = '2023-04-13T08:29:52.140Z';
44029
+ __info__.hash = '3141a6d';