@odoo/o-spreadsheet 16.2.0 → 16.2.2

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.
@@ -113,88 +113,88 @@
113
113
  const BACKGROUND_HEADER_FILTER_COLOR = "#E6F4EA";
114
114
  const BACKGROUND_HEADER_SELECTED_FILTER_COLOR = "#CEEAD6";
115
115
  const SEPARATOR_COLOR = "#E0E2E4";
116
- // Color picker
116
+ // Color picker defaults as upper case HEX to match `toHex`helper
117
117
  const COLOR_PICKER_DEFAULTS = [
118
118
  "#000000",
119
119
  "#434343",
120
120
  "#666666",
121
121
  "#999999",
122
- "#b7b7b7",
123
- "#cccccc",
124
- "#d9d9d9",
125
- "#efefef",
126
- "#f3f3f3",
127
- "#ffffff",
122
+ "#B7B7B7",
123
+ "#CCCCCC",
124
+ "#D9D9D9",
125
+ "#EFEFEF",
126
+ "#F3F3F3",
127
+ "#FFFFFF",
128
128
  "#980000",
129
- "#ff0000",
130
- "#ff9900",
131
- "#ffff00",
132
- "#00ff00",
133
- "#00ffff",
134
- "#4a86e8",
135
- "#0000ff",
136
- "#9900ff",
137
- "#ff00ff",
138
- "#e6b8af",
139
- "#f4cccc",
140
- "#fce5cd",
141
- "#fff2cc",
142
- "#d9ead3",
143
- "#d0e0e3",
144
- "#c9daf8",
145
- "#cfe2f3",
146
- "#d9d2e9",
147
- "#ead1dc",
148
- "#dd7e6b",
149
- "#ea9999",
150
- "#f9cb9c",
151
- "#ffe599",
152
- "#b6d7a8",
153
- "#a2c4c9",
154
- "#a4c2f4",
155
- "#9fc5e8",
156
- "#b4a7d6",
157
- "#d5a6bd",
158
- "#cc4125",
159
- "#e06666",
160
- "#f6b26b",
161
- "#ffd966",
162
- "#93c47d",
163
- "#76a5af",
164
- "#6d9eeb",
165
- "#6fa8dc",
166
- "#8e7cc3",
167
- "#c27ba0",
168
- "#a61c00",
169
- "#cc0000",
170
- "#e69138",
171
- "#f1c232",
172
- "#6aa84f",
173
- "#45818e",
174
- "#3c78d8",
175
- "#3d85c6",
176
- "#674ea7",
177
- "#a64d79",
178
- "#85200c",
129
+ "#FF0000",
130
+ "#FF9900",
131
+ "#FFFF00",
132
+ "#00FF00",
133
+ "#00FFFF",
134
+ "#4A86E8",
135
+ "#0000FF",
136
+ "#9900FF",
137
+ "#FF00FF",
138
+ "#E6B8AF",
139
+ "#F4CCCC",
140
+ "#FCE5CD",
141
+ "#FFF2CC",
142
+ "#D9EAD3",
143
+ "#D0E0E3",
144
+ "#C9DAF8",
145
+ "#CFE2F3",
146
+ "#D9D2E9",
147
+ "#EAD1DC",
148
+ "#DD7E6B",
149
+ "#EA9999",
150
+ "#F9CB9C",
151
+ "#FFE599",
152
+ "#B6D7A8",
153
+ "#A2C4C9",
154
+ "#A4C2F4",
155
+ "#9FC5E8",
156
+ "#B4A7D6",
157
+ "#D5A6BD",
158
+ "#CC4125",
159
+ "#E06666",
160
+ "#F6B26B",
161
+ "#FFD966",
162
+ "#93C47D",
163
+ "#76A5AF",
164
+ "#6D9EEB",
165
+ "#6FA8DC",
166
+ "#8E7CC3",
167
+ "#C27BA0",
168
+ "#A61C00",
169
+ "#CC0000",
170
+ "#E69138",
171
+ "#F1C232",
172
+ "#6AA84F",
173
+ "#45818E",
174
+ "#3C78D8",
175
+ "#3D85C6",
176
+ "#674EA7",
177
+ "#A64D79",
178
+ "#85200C",
179
179
  "#990000",
180
- "#b45f06",
181
- "#bf9000",
182
- "#38761d",
183
- "#134f5c",
184
- "#1155cc",
185
- "#0b5394",
186
- "#351c75",
187
- "#741b47",
188
- "#5b0f00",
180
+ "#B45F06",
181
+ "#BF9000",
182
+ "#38761D",
183
+ "#134F5C",
184
+ "#1155CC",
185
+ "#0B5394",
186
+ "#351C75",
187
+ "#741B47",
188
+ "#5B0F00",
189
189
  "#660000",
190
- "#783f04",
191
- "#7f6000",
192
- "#274e13",
193
- "#0c343d",
194
- "#1c4587",
190
+ "#783F04",
191
+ "#7F6000",
192
+ "#274E13",
193
+ "#0C343D",
194
+ "#1C4587",
195
195
  "#073763",
196
- "#20124d",
197
- "#4c1130",
196
+ "#20124D",
197
+ "#4C1130",
198
198
  ];
199
199
  // Dimensions
200
200
  const MIN_ROW_HEIGHT = 10;
@@ -234,7 +234,7 @@
234
234
  const DEFAULT_BORDER_DESC = ["thin", "#000"];
235
235
  const DEFAULT_FILTER_BORDER_DESC = ["thin", FILTERS_COLOR];
236
236
  // DateTimeRegex
237
- const DATETIME_FORMAT = /[ymd:]/;
237
+ const DATETIME_FORMAT = /[ymdhs:]/;
238
238
  // Ranges
239
239
  const INCORRECT_RANGE_STRING = CellErrorType.InvalidReference;
240
240
  // Max Number of history steps kept in memory
@@ -271,11 +271,12 @@
271
271
  ComponentsImportance[ComponentsImportance["Highlight"] = 5] = "Highlight";
272
272
  ComponentsImportance[ComponentsImportance["Figure"] = 10] = "Figure";
273
273
  ComponentsImportance[ComponentsImportance["ScrollBar"] = 15] = "ScrollBar";
274
- ComponentsImportance[ComponentsImportance["Dropdown"] = 16] = "Dropdown";
274
+ ComponentsImportance[ComponentsImportance["GridPopover"] = 19] = "GridPopover";
275
275
  ComponentsImportance[ComponentsImportance["GridComposer"] = 20] = "GridComposer";
276
- ComponentsImportance[ComponentsImportance["TopBarComposer"] = 21] = "TopBarComposer";
276
+ ComponentsImportance[ComponentsImportance["Dropdown"] = 21] = "Dropdown";
277
277
  ComponentsImportance[ComponentsImportance["IconPicker"] = 25] = "IconPicker";
278
- ComponentsImportance[ComponentsImportance["Popover"] = 30] = "Popover";
278
+ ComponentsImportance[ComponentsImportance["TopBarComposer"] = 30] = "TopBarComposer";
279
+ ComponentsImportance[ComponentsImportance["Popover"] = 35] = "Popover";
279
280
  ComponentsImportance[ComponentsImportance["ChartAnchor"] = 1000] = "ChartAnchor";
280
281
  })(ComponentsImportance || (ComponentsImportance = {}));
281
282
  const DEFAULT_SHEETVIEW_SIZE = 1000;
@@ -702,8 +703,9 @@
702
703
  return DEFAULT_CELL_HEIGHT;
703
704
  }
704
705
  const fontSize = computeTextFontSizeInPixels(cell.style);
705
- const multiLineText = cell.content.split(NEWLINE);
706
- return computeTextLinesHeight(fontSize, multiLineText.length) + 2 * PADDING_AUTORESIZE_VERTICAL;
706
+ // the number of lines should be computed from the formula result, but it's not evaluated at this point
707
+ const numberOfLines = cell.isFormula ? 1 : cell.content.split(NEWLINE).length;
708
+ return computeTextLinesHeight(fontSize, numberOfLines) + 2 * PADDING_AUTORESIZE_VERTICAL;
707
709
  }
708
710
  function computeTextWidth(context, text, style) {
709
711
  context.save();
@@ -1055,6 +1057,8 @@
1055
1057
  return text;
1056
1058
  }
1057
1059
 
1060
+ const RBA_REGEX = /rgba?\(|\s+|\)/gi;
1061
+ const HEX_MATCH = /^#([A-F\d]{2}){3,4}$/g;
1058
1062
  const colors$1 = [
1059
1063
  "#eb6d00",
1060
1064
  "#0074d9",
@@ -1080,7 +1084,10 @@
1080
1084
  }
1081
1085
  /**
1082
1086
  * Converts any CSS color value to a standardized hex6 value.
1083
- * Accepts: hex3, hex6, hex8 and rgb (rgba is not supported)
1087
+ * Accepts: hex3, hex6, hex8, rgb[1] and rgba[1].
1088
+ *
1089
+ * [1] under the form rgb(r, g, b, a?) or rgba(r, g, b, a?)
1090
+ * with r,g,b ∈ [0, 255] and a ∈ [0, 1]
1084
1091
  *
1085
1092
  * toHex("#ABC")
1086
1093
  * >> "#AABBCC"
@@ -1091,27 +1098,31 @@
1091
1098
  * toHex("rgb(30, 80, 16)")
1092
1099
  * >> "#1E5010"
1093
1100
  *
1101
+ * * toHex("rgb(30, 80, 16, 0.5)")
1102
+ * >> "#1E501080"
1103
+ *
1094
1104
  */
1095
1105
  function toHex(color) {
1096
- if (color.includes("rgba")) {
1097
- throw new Error(`rgba() conversion currently not supported: ${color}`);
1106
+ let hexColor = color;
1107
+ if (color.startsWith("rgb")) {
1108
+ hexColor = rgbaStringToHex(color);
1098
1109
  }
1099
- if (color.includes("rgb")) {
1100
- return rgbToHex6(color);
1101
- }
1102
- color = color.replace("#", "").toUpperCase();
1103
- if (color.length === 3 || color.length === 4) {
1104
- color = color.split("").reduce((acc, h) => acc + h + h, "");
1110
+ else {
1111
+ hexColor = color.replace("#", "").toUpperCase();
1112
+ if (hexColor.length === 3 || hexColor.length === 4) {
1113
+ hexColor = hexColor.split("").reduce((acc, h) => acc + h + h, "");
1114
+ }
1115
+ hexColor = `#${hexColor}`;
1105
1116
  }
1106
- if (color.replace(/[a-f0-9]/gi, "") !== "") {
1107
- throw new Error("invalid color");
1117
+ if (!hexColor.match(HEX_MATCH)) {
1118
+ throw new Error(`invalid color input: ${color}`);
1108
1119
  }
1109
- return "#" + color;
1120
+ return hexColor;
1110
1121
  }
1111
1122
  function isColorValid(color) {
1112
1123
  try {
1113
- const { r, g, b, a } = colorToRGBA(color);
1114
- return (isColorValueValid(r) && isColorValueValid(g) && isColorValueValid(b) && isColorValueValid(a));
1124
+ toHex(color);
1125
+ return true;
1115
1126
  }
1116
1127
  catch (error) {
1117
1128
  return false;
@@ -1144,15 +1155,29 @@
1144
1155
  /**
1145
1156
  * Convert a CSS rgb color string to a standardized hex6 color value.
1146
1157
  *
1147
- * rgbToHex6("rgb(30, 80, 16)")
1158
+ * rgbaStringToHex("rgb(30, 80, 16)")
1148
1159
  * >> "#1E5010"
1160
+ *
1161
+ * rgbaStringToHex("rgba(30, 80, 16, 0.5)")
1162
+ * >> "#1E501080"
1163
+ *
1164
+ * DOES NOT SUPPORT NON INTEGER RGB VALUES
1149
1165
  */
1150
- function rgbToHex6(color) {
1151
- return ("#" +
1152
- concat(color
1153
- .slice(4, -1)
1154
- .split(",")
1155
- .map((valueString) => parseInt(valueString, 10).toString(16).padStart(2, "0"))).toUpperCase());
1166
+ function rgbaStringToHex(color) {
1167
+ const stringVals = color.replace(RBA_REGEX, "").split(",");
1168
+ let alphaHex = 255;
1169
+ if (stringVals.length !== 3 && stringVals.length !== 4) {
1170
+ throw new Error("invalid color");
1171
+ }
1172
+ else if (stringVals.length === 4) {
1173
+ const alpha = parseFloat(stringVals.pop() || "1");
1174
+ alphaHex = Math.round((alpha || 1) * 255);
1175
+ }
1176
+ const vals = stringVals.map((val) => parseInt(val, 10));
1177
+ if (alphaHex !== 255) {
1178
+ vals.push(alphaHex);
1179
+ }
1180
+ return "#" + concat(vals.map((value) => value.toString(16).padStart(2, "0"))).toUpperCase();
1156
1181
  }
1157
1182
  /**
1158
1183
  * RGBA to HEX representation (#RRGGBBAA).
@@ -1174,7 +1199,7 @@
1174
1199
  a = "0" + a;
1175
1200
  if (a === "ff")
1176
1201
  a = "";
1177
- return "#" + r + g + b + a;
1202
+ return ("#" + r + g + b + a).toUpperCase();
1178
1203
  }
1179
1204
  /**
1180
1205
  * Color string to RGBA representation
@@ -1295,6 +1320,9 @@
1295
1320
  s = +(s * 100).toFixed(1);
1296
1321
  l = +(l * 100).toFixed(1);
1297
1322
  return { a: rgba.a, h, s, l };
1323
+ }
1324
+ function isSameColor(color1, color2) {
1325
+ return isColorValid(color1) && isColorValid(color2) && toHex(color1) === toHex(color2);
1298
1326
  }
1299
1327
 
1300
1328
  /** Reference of a cell (eg. A1, $B$5) */
@@ -1446,7 +1474,7 @@
1446
1474
  * formula, commas are used to separate arguments
1447
1475
  * - it does not support % symbol, in formulas % is an operator
1448
1476
  */
1449
- const formulaNumberRegexp = /^-?\d+(\.?\d*(e\d+)?)?|^-?\.\d+/;
1477
+ const formulaNumberRegexp = /(^-?\d+(\.?\d*(e\d+)?)?|^-?\.\d+)(?!\w|!)/;
1450
1478
  const pIntegerAndDecimals = "(\\d+(,\\d{3,})*(\\.\\d*)?)"; // pattern that match integer number with or without decimal digits
1451
1479
  const pOnlyDecimals = "(\\.\\d+)"; // pattern that match only expression with decimal digits
1452
1480
  const pScientificFormat = "(e(\\+|-)?\\d+)?"; // pattern that match scientific format between zero and one time (should be placed before pPercentFormat)
@@ -3052,6 +3080,7 @@
3052
3080
  const LINE_HEIGHT = 1.2;
3053
3081
  css /* scss */ `
3054
3082
  div.o-scorecard {
3083
+ font-family: ${DEFAULT_FONT};
3055
3084
  user-select: none;
3056
3085
  background-color: white;
3057
3086
  display: flex;
@@ -5102,6 +5131,11 @@
5102
5131
  this.currentPosition = newPosition;
5103
5132
  });
5104
5133
  }
5134
+ get popoverStyle() {
5135
+ return cssPropertiesToCss({
5136
+ "z-index": `${this.props.zIndex}`,
5137
+ });
5138
+ }
5105
5139
  }
5106
5140
  Popover.template = "o-spreadsheet-Popover";
5107
5141
  Popover.defaultProps = {
@@ -5110,6 +5144,7 @@
5110
5144
  onMouseWheel: () => { },
5111
5145
  onPopoverMoved: () => { },
5112
5146
  onPopoverHidden: () => { },
5147
+ zIndex: ComponentsImportance.Popover,
5113
5148
  };
5114
5149
  Popover.props = {
5115
5150
  anchorRect: Object,
@@ -5121,6 +5156,7 @@
5121
5156
  onMouseWheel: { type: Function, optional: true },
5122
5157
  onPopoverHidden: { type: Function, optional: true },
5123
5158
  onPopoverMoved: { type: Function, optional: true },
5159
+ zIndex: { type: Number, optional: true },
5124
5160
  slots: Object,
5125
5161
  };
5126
5162
  class PopoverPositionContext {
@@ -17011,6 +17047,9 @@
17011
17047
  top,
17012
17048
  });
17013
17049
  }
17050
+ isSameColor(color1, color2) {
17051
+ return isSameColor(color1, color2);
17052
+ }
17014
17053
  }
17015
17054
  ColorPicker.template = "o-spreadsheet-ColorPicker";
17016
17055
  ColorPicker.defaultProps = {
@@ -19252,6 +19291,8 @@
19252
19291
  start = 0;
19253
19292
  if (end > textLength)
19254
19293
  end = textLength;
19294
+ if (start > textLength)
19295
+ start = textLength;
19255
19296
  }
19256
19297
  let startNode = this.findChildAtCharacterIndex(start);
19257
19298
  let endNode = this.findChildAtCharacterIndex(end);
@@ -19521,6 +19562,7 @@
19521
19562
  padding-right: 2px;
19522
19563
 
19523
19564
  box-sizing: border-box;
19565
+ font-family: ${DEFAULT_FONT};
19524
19566
 
19525
19567
  caret-color: black;
19526
19568
  padding-left: 3px;
@@ -20731,6 +20773,9 @@
20731
20773
  owl.onMounted(() => {
20732
20774
  resizeObserver.observe(this.gridOverlayEl);
20733
20775
  });
20776
+ owl.onWillUnmount(() => {
20777
+ resizeObserver.disconnect();
20778
+ });
20734
20779
  useTouchMove(this.gridOverlay, this.props.onGridMoved, () => {
20735
20780
  const { scrollY } = this.env.model.getters.getActiveSheetDOMScrollInfo();
20736
20781
  return scrollY > 0;
@@ -20787,6 +20832,10 @@
20787
20832
  };
20788
20833
 
20789
20834
  class GridPopover extends owl.Component {
20835
+ constructor() {
20836
+ super(...arguments);
20837
+ this.zIndex = ComponentsImportance.GridPopover;
20838
+ }
20790
20839
  get cellPopover() {
20791
20840
  const popover = this.env.model.getters.getCellPopover(this.props.hoveredCell);
20792
20841
  if (!popover.isOpen) {
@@ -22151,6 +22200,10 @@
22151
22200
  }
22152
22201
  }
22153
22202
  onInput(ev) {
22203
+ // the user meant to paste in the sheet, not open the composer with the pasted content
22204
+ if (!ev.isComposing && ev.inputType === "insertFromPaste") {
22205
+ return;
22206
+ }
22154
22207
  if (ev.data) {
22155
22208
  // if the user types a character on the grid, it means he wants to start composing the selected cell with that
22156
22209
  // character
@@ -22999,6 +23052,70 @@
22999
23052
  });
23000
23053
  }
23001
23054
 
23055
+ 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;
23056
+ /**
23057
+ * Convert excel format to o_spreadsheet format
23058
+ *
23059
+ * Excel format are defined in openXML §18.8.31
23060
+ */
23061
+ function convertXlsxFormat(numFmtId, formats, warningManager) {
23062
+ var _a, _b, _c;
23063
+ if (numFmtId === 0) {
23064
+ return undefined;
23065
+ }
23066
+ // Format is either defined in the imported data, or the formatId is defined in openXML §18.8.30
23067
+ let format = XLSX_FORMATS_CONVERSION_MAP[numFmtId] || ((_a = formats.find((f) => f.id === numFmtId)) === null || _a === void 0 ? void 0 : _a.format);
23068
+ if (format) {
23069
+ try {
23070
+ let convertedFormat = format.replace(/(.*?);.*/, "$1"); // only take first part of multi-part format
23071
+ convertedFormat = convertedFormat.replace(/\[(.*)-[A-Z0-9]{3}\]/g, "[$1]"); // remove currency and locale/date system/number system info (ECMA §18.8.31)
23072
+ convertedFormat = convertedFormat.replace(/\[\$\]/g, ""); // remove empty bocks
23073
+ // Quotes in format escape sequences of characters. ATM we only support [$...] blocks to escape characters, and only one of them per format
23074
+ const numberOfQuotes = ((_b = convertedFormat.match(/"/g)) === null || _b === void 0 ? void 0 : _b.length) || 0;
23075
+ const numberOfOpenBrackets = ((_c = convertedFormat.match(/\[/g)) === null || _c === void 0 ? void 0 : _c.length) || 0;
23076
+ if (numberOfQuotes / 2 + numberOfOpenBrackets > 1) {
23077
+ throw new Error("Multiple escaped blocks in format");
23078
+ }
23079
+ convertedFormat = convertedFormat.replace(/"(.*)"/g, "[$$$1]"); // replace '"..."' by '[$...]'
23080
+ convertedFormat = convertedFormat.replace(/_.{1}/g, ""); // _ == ignore width of next char for align purposes. Not supported ATM
23081
+ convertedFormat = convertedFormat.replace(/\*.{1}/g, ""); // * == repeat next character enough to fill the line. Not supported ATM
23082
+ convertedFormat = convertedFormat.replace(/\\ /g, " "); // unescape spaces
23083
+ convertedFormat = convertedFormat.replace(/\\./g, (match) => match[1]); // unescape other characters
23084
+ if (isXlsxDateFormat(convertedFormat)) {
23085
+ convertedFormat = convertDateFormat$1(convertedFormat);
23086
+ }
23087
+ if (isFormatSupported(convertedFormat)) {
23088
+ return convertedFormat;
23089
+ }
23090
+ }
23091
+ catch (e) { }
23092
+ }
23093
+ warningManager.generateNotSupportedWarning(WarningTypes.NumFmtIdNotSupported, format || `nmFmtId ${numFmtId}`);
23094
+ return undefined;
23095
+ }
23096
+ function isFormatSupported(format) {
23097
+ try {
23098
+ formatValue(0, format);
23099
+ return true;
23100
+ }
23101
+ catch (e) {
23102
+ return false;
23103
+ }
23104
+ }
23105
+ function isXlsxDateFormat(format) {
23106
+ return format.match(XLSX_DATE_FORMAT_REGEX) !== null;
23107
+ }
23108
+ function convertDateFormat$1(format) {
23109
+ // Some of these aren't defined neither in the OpenXML spec not the Xlsx extension of OpenXML,
23110
+ // but can still occur and are supported by Excel/Google sheets
23111
+ format = format.toLowerCase();
23112
+ format = format.replace(/mmmmm/g, "mmm");
23113
+ format = format.replace(/am\/pm|a\/m/g, "a");
23114
+ format = format.replace(/hhhh/g, "hh");
23115
+ format = format.replace(/\bh\b/g, "hh");
23116
+ return format;
23117
+ }
23118
+
23002
23119
  function convertBorders(data, warningManager) {
23003
23120
  const borderArray = data.borders.map((border) => {
23004
23121
  addBorderWarnings(border, warningManager);
@@ -23061,41 +23178,6 @@
23061
23178
  }
23062
23179
  return arrayToObject(formats, 1);
23063
23180
  }
23064
- /**
23065
- * Convert excel format to o_spreadsheet format
23066
- *
23067
- * Excel format are defined in openXML §18.8.31
23068
- */
23069
- function convertXlsxFormat(numFmtId, formats, warningManager) {
23070
- var _a, _b, _c;
23071
- if (numFmtId === 0) {
23072
- return undefined;
23073
- }
23074
- // Format is either defined in the imported data, or the formatId is defined in openXML §18.8.30
23075
- let format = XLSX_FORMATS_CONVERSION_MAP[numFmtId] || ((_a = formats.find((f) => f.id === numFmtId)) === null || _a === void 0 ? void 0 : _a.format);
23076
- if (format) {
23077
- try {
23078
- let convertedFormat = format.replace(/(.*?);.*/, "$1"); // only take first part of multi-part format
23079
- convertedFormat = convertedFormat.replace(/\[(.*)-[A-Z0-9]{3}\]/g, "[$1]"); // remove currency and locale/date system/number system info (ECMA §18.8.31)
23080
- convertedFormat = convertedFormat.replace(/\[\$\]/g, ""); // remove empty bocks
23081
- // Quotes in format escape sequences of characters. ATM we only support [$...] blocks to escape characters, and only one of them per format
23082
- const numberOfQuotes = ((_b = convertedFormat.match(/"/g)) === null || _b === void 0 ? void 0 : _b.length) || 0;
23083
- const numberOfOpenBrackets = ((_c = convertedFormat.match(/\[/g)) === null || _c === void 0 ? void 0 : _c.length) || 0;
23084
- if (numberOfQuotes / 2 + numberOfOpenBrackets > 1) {
23085
- throw new Error("Multiple escaped blocks in format");
23086
- }
23087
- convertedFormat = convertedFormat.replace(/"(.*)"/g, "[$$$1]"); // replace '"..."' by '[$...]'
23088
- convertedFormat = convertedFormat.replace(/_.{1}/g, ""); // _ == ignore with of next char for align purposes. Not supported ATM
23089
- convertedFormat = convertedFormat.replace(/\*.{1}/g, ""); // * == repeat next character enough to fill the line. Not supported ATM
23090
- convertedFormat = convertedFormat.replace(/\\ /g, " "); // unescape spaces
23091
- formatValue(0, convertedFormat);
23092
- return convertedFormat;
23093
- }
23094
- catch (e) { }
23095
- }
23096
- warningManager.generateNotSupportedWarning(WarningTypes.NumFmtIdNotSupported, format || `nmFmtId ${numFmtId}`);
23097
- return undefined;
23098
- }
23099
23181
  // ---------------------------------------------------------------------------
23100
23182
  // Warnings
23101
23183
  // ---------------------------------------------------------------------------
@@ -30087,6 +30169,7 @@
30087
30169
  }
30088
30170
  else if (range.zone[start] >= min && range.zone[end] <= max) {
30089
30171
  changeType = "REMOVE";
30172
+ newRange = range.clone({ ...this.getInvalidRange() });
30090
30173
  }
30091
30174
  else if (range.zone[start] <= max && range.zone[end] >= max) {
30092
30175
  const toRemove = max - range.zone[start] + 1;
@@ -30152,8 +30235,10 @@
30152
30235
  return { changeType: "NONE" };
30153
30236
  }
30154
30237
  const invalidSheetName = this.getters.getSheetName(cmd.sheetId);
30155
- const sheetId = "";
30156
- range = range.clone({ sheetId, invalidSheetName });
30238
+ range = range.clone({
30239
+ ...this.getInvalidRange(),
30240
+ invalidSheetName,
30241
+ });
30157
30242
  return { changeType: "REMOVE", range };
30158
30243
  }, cmd.sheetId);
30159
30244
  break;
@@ -30413,6 +30498,15 @@
30413
30498
  }
30414
30499
  return str;
30415
30500
  }
30501
+ getInvalidRange() {
30502
+ return {
30503
+ parts: [],
30504
+ prefixSheet: false,
30505
+ zone: { left: -1, top: -1, right: -1, bottom: -1 },
30506
+ sheetId: "",
30507
+ invalidXc: INCORRECT_RANGE_STRING,
30508
+ };
30509
+ }
30416
30510
  }
30417
30511
  RangeAdapter.getters = [
30418
30512
  "getRangeString",
@@ -31382,9 +31476,7 @@
31382
31476
  // remove duplicates first to check validity on a reduced
31383
31477
  // set of colors, then normalize to HEX and remove duplicates
31384
31478
  // again
31385
- [...new Set([...usedColors, ...this.customColors])]
31386
- .filter(isColorValid)
31387
- .map((c) => toHex(c).toLowerCase())),
31479
+ [...new Set([...usedColors, ...this.customColors])].filter(isColorValid).map(toHex)),
31388
31480
  ]).filter((color) => !COLOR_PICKER_DEFAULTS.includes(color));
31389
31481
  }
31390
31482
  getColorsFromCells(cells) {
@@ -31445,7 +31537,7 @@
31445
31537
  return [...chartsColors];
31446
31538
  }
31447
31539
  tryToAddColor(color) {
31448
- const formattedColor = toHex(color).toLowerCase();
31540
+ const formattedColor = toHex(color);
31449
31541
  if (color && !COLOR_PICKER_DEFAULTS.includes(formattedColor)) {
31450
31542
  this.customColors.add(formattedColor);
31451
31543
  }
@@ -32460,8 +32552,8 @@
32460
32552
  this.getters = getters;
32461
32553
  this.sheetId = sheetId;
32462
32554
  this.boundaries = boundaries;
32463
- this.width = sizeInGrid.width;
32464
- this.height = sizeInGrid.height;
32555
+ this.viewportWidth = sizeInGrid.width;
32556
+ this.viewportHeight = sizeInGrid.height;
32465
32557
  this.offsetScrollbarX = offsets.x;
32466
32558
  this.offsetScrollbarY = offsets.y;
32467
32559
  this.canScrollVertically = options.canScrollVertically;
@@ -32472,6 +32564,10 @@
32472
32564
  this.adjustViewportOffsetY();
32473
32565
  }
32474
32566
  // PUBLIC
32567
+ /** Returns the maximum size (in Pixels) of the viewport relative to its allocated client size
32568
+ * When the viewport grid size is smaller than its client width (resp. height), it will return
32569
+ * the client width (resp. height).
32570
+ */
32475
32571
  getMaxSize() {
32476
32572
  const lastCol = this.getters.findLastVisibleColRowIndex(this.sheetId, "COL", {
32477
32573
  first: this.boundaries.left,
@@ -32483,20 +32579,24 @@
32483
32579
  });
32484
32580
  const { end: lastColEnd, size: lastColSize } = this.getters.getColDimensions(this.sheetId, lastCol);
32485
32581
  const { end: lastRowEnd, size: lastRowSize } = this.getters.getRowDimensions(this.sheetId, lastRow);
32486
- const leftColIndex = this.searchHeaderIndex("COL", lastColEnd - this.width, 0);
32582
+ const leftColIndex = this.searchHeaderIndex("COL", lastColEnd - this.viewportWidth, 0);
32487
32583
  const leftColSize = this.getters.getColSize(this.sheetId, leftColIndex);
32488
- const leftRowIndex = this.searchHeaderIndex("ROW", lastRowEnd - this.height, 0);
32584
+ const leftRowIndex = this.searchHeaderIndex("ROW", lastRowEnd - this.viewportHeight, 0);
32489
32585
  const topRowSize = this.getters.getRowSize(this.sheetId, leftRowIndex);
32490
- const width = lastColEnd -
32491
- this.offsetCorrectionX +
32492
- (this.canScrollHorizontally
32493
- ? Math.max(DEFAULT_CELL_WIDTH, Math.min(leftColSize, this.width - lastColSize))
32494
- : 0);
32495
- const height = lastRowEnd -
32496
- this.offsetCorrectionY +
32497
- (this.canScrollVertically
32498
- ? Math.max(DEFAULT_CELL_HEIGHT + 5, Math.min(topRowSize, this.height - lastRowSize))
32499
- : 0);
32586
+ let width = lastColEnd - this.offsetCorrectionX;
32587
+ if (this.canScrollHorizontally) {
32588
+ width += Math.max(DEFAULT_CELL_WIDTH, // leave some minimal space to let the user know they scrolled all the way
32589
+ Math.min(leftColSize, this.viewportWidth - lastColSize) // Add pixels that allows the snapping at maximum horizontal scroll
32590
+ );
32591
+ width = Math.max(width, this.viewportWidth); // if the viewport grid size is smaller than its client width, return client width
32592
+ }
32593
+ let height = lastRowEnd - this.offsetCorrectionY;
32594
+ if (this.canScrollVertically) {
32595
+ height += Math.max(DEFAULT_CELL_HEIGHT + 5, // leave some space to let the user know they scrolled all the way
32596
+ Math.min(topRowSize, this.viewportHeight - lastRowSize) // Add pixels that allows the snapping at maximum vertical scroll
32597
+ );
32598
+ height = Math.max(height, this.viewportHeight); // if the viewport grid size is smaller than its client height, return client height
32599
+ }
32500
32600
  return { width, height };
32501
32601
  }
32502
32602
  /**
@@ -32505,7 +32605,7 @@
32505
32605
  * It returns -1 if no column is found.
32506
32606
  */
32507
32607
  getColIndex(x, absolute = false) {
32508
- if (x < this.offsetCorrectionX || x > this.offsetCorrectionX + this.width) {
32608
+ if (x < this.offsetCorrectionX || x > this.offsetCorrectionX + this.viewportWidth) {
32509
32609
  return -1;
32510
32610
  }
32511
32611
  return this.searchHeaderIndex("COL", x - this.offsetCorrectionX, this.left, absolute);
@@ -32516,7 +32616,7 @@
32516
32616
  * It returns -1 if no row is found.
32517
32617
  */
32518
32618
  getRowIndex(y, absolute = false) {
32519
- if (y < this.offsetCorrectionY || y > this.offsetCorrectionY + this.height) {
32619
+ if (y < this.offsetCorrectionY || y > this.offsetCorrectionY + this.viewportHeight) {
32520
32620
  return -1;
32521
32621
  }
32522
32622
  return this.searchHeaderIndex("ROW", y - this.offsetCorrectionY, this.top, absolute);
@@ -32543,7 +32643,7 @@
32543
32643
  adjustPositionX(col) {
32544
32644
  const sheetId = this.sheetId;
32545
32645
  const { start, end } = this.getters.getColDimensions(sheetId, col);
32546
- while (end > this.offsetX + this.offsetCorrectionX + this.width &&
32646
+ while (end > this.offsetX + this.offsetCorrectionX + this.viewportWidth &&
32547
32647
  this.offsetX + this.offsetCorrectionX < start) {
32548
32648
  this.offsetX = this.getters.getColDimensions(sheetId, this.left).end - this.offsetCorrectionX;
32549
32649
  this.offsetScrollbarX = this.offsetX;
@@ -32565,7 +32665,7 @@
32565
32665
  adjustPositionY(row) {
32566
32666
  const sheetId = this.sheetId;
32567
32667
  while (this.getters.getRowDimensions(sheetId, row).end >
32568
- this.offsetY + this.height + this.offsetCorrectionY &&
32668
+ this.offsetY + this.viewportHeight + this.offsetCorrectionY &&
32569
32669
  this.offsetY + this.offsetCorrectionY < this.getters.getRowDimensions(sheetId, row).start) {
32570
32670
  this.offsetY = this.getters.getRowDimensions(sheetId, this.top).end - this.offsetCorrectionY;
32571
32671
  this.offsetScrollbarY = this.offsetY;
@@ -32603,8 +32703,8 @@
32603
32703
  const x = this.getters.getColRowOffset("COL", this.zone.left, targetZone.left) +
32604
32704
  this.offsetCorrectionX;
32605
32705
  const y = this.getters.getColRowOffset("ROW", this.zone.top, targetZone.top) + this.offsetCorrectionY;
32606
- const width = Math.min(this.getters.getColRowOffset("COL", targetZone.left, targetZone.right + 1), this.width);
32607
- const height = Math.min(this.getters.getColRowOffset("ROW", targetZone.top, targetZone.bottom + 1), this.height);
32706
+ const width = Math.min(this.getters.getColRowOffset("COL", targetZone.left, targetZone.right + 1), this.viewportWidth);
32707
+ const height = Math.min(this.getters.getColRowOffset("ROW", targetZone.top, targetZone.bottom + 1), this.viewportHeight);
32608
32708
  return {
32609
32709
  x,
32610
32710
  y,
@@ -32667,12 +32767,12 @@
32667
32767
  adjustViewportOffsetX() {
32668
32768
  if (this.canScrollHorizontally) {
32669
32769
  const { width: viewportWidth } = this.getMaxSize();
32670
- if (this.width + this.offsetScrollbarX > viewportWidth) {
32671
- this.offsetScrollbarX = Math.max(0, viewportWidth - this.width);
32770
+ if (this.viewportWidth + this.offsetScrollbarX > viewportWidth) {
32771
+ this.offsetScrollbarX = Math.max(0, viewportWidth - this.viewportWidth);
32672
32772
  }
32673
32773
  }
32674
32774
  this.left = this.getColIndex(this.offsetScrollbarX, true);
32675
- this.right = this.getColIndex(this.offsetScrollbarX + this.width, true);
32775
+ this.right = this.getColIndex(this.offsetScrollbarX + this.viewportWidth, true);
32676
32776
  if (this.right === -1) {
32677
32777
  this.right = this.boundaries.right;
32678
32778
  }
@@ -32684,12 +32784,12 @@
32684
32784
  adjustViewportOffsetY() {
32685
32785
  if (this.canScrollVertically) {
32686
32786
  const { height: paneHeight } = this.getMaxSize();
32687
- if (this.height + this.offsetScrollbarY > paneHeight) {
32688
- this.offsetScrollbarY = Math.max(0, paneHeight - this.height);
32787
+ if (this.viewportHeight + this.offsetScrollbarY > paneHeight) {
32788
+ this.offsetScrollbarY = Math.max(0, paneHeight - this.viewportHeight);
32689
32789
  }
32690
32790
  }
32691
32791
  this.top = this.getRowIndex(this.offsetScrollbarY, true);
32692
- this.bottom = this.getRowIndex(this.offsetScrollbarY + this.width, true);
32792
+ this.bottom = this.getRowIndex(this.offsetScrollbarY + this.viewportWidth, true);
32693
32793
  if (this.bottom === -1) {
32694
32794
  this.bottom = this.boundaries.bottom;
32695
32795
  }
@@ -32700,7 +32800,7 @@
32700
32800
  adjustViewportZoneX() {
32701
32801
  const sheetId = this.sheetId;
32702
32802
  this.left = this.searchHeaderIndex("COL", this.offsetScrollbarX, this.boundaries.left);
32703
- this.right = Math.min(this.boundaries.right, this.searchHeaderIndex("COL", this.width, this.left));
32803
+ this.right = Math.min(this.boundaries.right, this.searchHeaderIndex("COL", this.viewportWidth, this.left));
32704
32804
  if (this.right === -1) {
32705
32805
  this.right = this.getters.getNumberCols(sheetId) - 1;
32706
32806
  }
@@ -32713,7 +32813,7 @@
32713
32813
  adjustViewportZoneY() {
32714
32814
  const sheetId = this.sheetId;
32715
32815
  this.top = this.searchHeaderIndex("ROW", this.offsetScrollbarY, this.boundaries.top);
32716
- this.bottom = Math.min(this.boundaries.bottom, this.searchHeaderIndex("ROW", this.height, this.top));
32816
+ this.bottom = Math.min(this.boundaries.bottom, this.searchHeaderIndex("ROW", this.viewportHeight, this.top));
32717
32817
  if (this.bottom === -1) {
32718
32818
  this.bottom = this.getters.getNumberRows(sheetId) - 1;
32719
32819
  }
@@ -32976,8 +33076,7 @@
32976
33076
  return [...new Set(viewports.map((v) => range(v.top, v.bottom + 1)).flat())].filter((row) => !this.getters.isHeaderHidden(sheetId, "ROW", row));
32977
33077
  }
32978
33078
  /**
32979
- * Return the main viewport maximum size. That is the zone dimension
32980
- * with some bottom and right padding.
33079
+ * Return the main viewport maximum size relative to the client size.
32981
33080
  */
32982
33081
  getMainViewportRect() {
32983
33082
  const sheetId = this.getters.getActiveSheetId();
@@ -32993,8 +33092,8 @@
32993
33092
  const { width, height } = this.getMainViewportRect();
32994
33093
  const viewport = this.getMainInternalViewport(sheetId);
32995
33094
  return {
32996
- maxOffsetX: Math.max(0, width - viewport.width + 1),
32997
- maxOffsetY: Math.max(0, height - viewport.height + 1),
33095
+ maxOffsetX: Math.max(0, width - viewport.viewportWidth + 1),
33096
+ maxOffsetY: Math.max(0, height - viewport.viewportHeight + 1),
32998
33097
  };
32999
33098
  }
33000
33099
  getColRowOffsetInViewport(dimension, referenceIndex, index) {
@@ -40893,6 +40992,7 @@
40893
40992
  this.state.menuState.parentMenu = menu;
40894
40993
  this.isSelectingMenu = true;
40895
40994
  this.openedEl = ev.target;
40995
+ this.env.model.dispatch("STOP_EDITION");
40896
40996
  }
40897
40997
  closeMenus() {
40898
40998
  this.state.activeTool = "";
@@ -41106,9 +41206,6 @@
41106
41206
  color: #333;
41107
41207
  }
41108
41208
 
41109
- * {
41110
- font-family: "Roboto", "RobotoDraft", Helvetica, Arial, sans-serif;
41111
- }
41112
41209
  &,
41113
41210
  *,
41114
41211
  *:before,
@@ -44976,9 +45073,9 @@
44976
45073
  Object.defineProperty(exports, '__esModule', { value: true });
44977
45074
 
44978
45075
 
44979
- __info__.version = '16.2.0';
44980
- __info__.date = '2023-03-15T15:03:34.107Z';
44981
- __info__.hash = 'c74b1d1';
45076
+ __info__.version = '16.2.2';
45077
+ __info__.date = '2023-04-13T08:29:22.236Z';
45078
+ __info__.hash = 'a38f31a';
44982
45079
 
44983
45080
 
44984
45081
  })(this.o_spreadsheet = this.o_spreadsheet || {}, owl);