@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.
@@ -116,88 +116,88 @@ const FILTERS_COLOR = "#188038";
116
116
  const BACKGROUND_HEADER_FILTER_COLOR = "#E6F4EA";
117
117
  const BACKGROUND_HEADER_SELECTED_FILTER_COLOR = "#CEEAD6";
118
118
  const SEPARATOR_COLOR = "#E0E2E4";
119
- // Color picker
119
+ // Color picker defaults as upper case HEX to match `toHex`helper
120
120
  const COLOR_PICKER_DEFAULTS = [
121
121
  "#000000",
122
122
  "#434343",
123
123
  "#666666",
124
124
  "#999999",
125
- "#b7b7b7",
126
- "#cccccc",
127
- "#d9d9d9",
128
- "#efefef",
129
- "#f3f3f3",
130
- "#ffffff",
125
+ "#B7B7B7",
126
+ "#CCCCCC",
127
+ "#D9D9D9",
128
+ "#EFEFEF",
129
+ "#F3F3F3",
130
+ "#FFFFFF",
131
131
  "#980000",
132
- "#ff0000",
133
- "#ff9900",
134
- "#ffff00",
135
- "#00ff00",
136
- "#00ffff",
137
- "#4a86e8",
138
- "#0000ff",
139
- "#9900ff",
140
- "#ff00ff",
141
- "#e6b8af",
142
- "#f4cccc",
143
- "#fce5cd",
144
- "#fff2cc",
145
- "#d9ead3",
146
- "#d0e0e3",
147
- "#c9daf8",
148
- "#cfe2f3",
149
- "#d9d2e9",
150
- "#ead1dc",
151
- "#dd7e6b",
152
- "#ea9999",
153
- "#f9cb9c",
154
- "#ffe599",
155
- "#b6d7a8",
156
- "#a2c4c9",
157
- "#a4c2f4",
158
- "#9fc5e8",
159
- "#b4a7d6",
160
- "#d5a6bd",
161
- "#cc4125",
162
- "#e06666",
163
- "#f6b26b",
164
- "#ffd966",
165
- "#93c47d",
166
- "#76a5af",
167
- "#6d9eeb",
168
- "#6fa8dc",
169
- "#8e7cc3",
170
- "#c27ba0",
171
- "#a61c00",
172
- "#cc0000",
173
- "#e69138",
174
- "#f1c232",
175
- "#6aa84f",
176
- "#45818e",
177
- "#3c78d8",
178
- "#3d85c6",
179
- "#674ea7",
180
- "#a64d79",
181
- "#85200c",
132
+ "#FF0000",
133
+ "#FF9900",
134
+ "#FFFF00",
135
+ "#00FF00",
136
+ "#00FFFF",
137
+ "#4A86E8",
138
+ "#0000FF",
139
+ "#9900FF",
140
+ "#FF00FF",
141
+ "#E6B8AF",
142
+ "#F4CCCC",
143
+ "#FCE5CD",
144
+ "#FFF2CC",
145
+ "#D9EAD3",
146
+ "#D0E0E3",
147
+ "#C9DAF8",
148
+ "#CFE2F3",
149
+ "#D9D2E9",
150
+ "#EAD1DC",
151
+ "#DD7E6B",
152
+ "#EA9999",
153
+ "#F9CB9C",
154
+ "#FFE599",
155
+ "#B6D7A8",
156
+ "#A2C4C9",
157
+ "#A4C2F4",
158
+ "#9FC5E8",
159
+ "#B4A7D6",
160
+ "#D5A6BD",
161
+ "#CC4125",
162
+ "#E06666",
163
+ "#F6B26B",
164
+ "#FFD966",
165
+ "#93C47D",
166
+ "#76A5AF",
167
+ "#6D9EEB",
168
+ "#6FA8DC",
169
+ "#8E7CC3",
170
+ "#C27BA0",
171
+ "#A61C00",
172
+ "#CC0000",
173
+ "#E69138",
174
+ "#F1C232",
175
+ "#6AA84F",
176
+ "#45818E",
177
+ "#3C78D8",
178
+ "#3D85C6",
179
+ "#674EA7",
180
+ "#A64D79",
181
+ "#85200C",
182
182
  "#990000",
183
- "#b45f06",
184
- "#bf9000",
185
- "#38761d",
186
- "#134f5c",
187
- "#1155cc",
188
- "#0b5394",
189
- "#351c75",
190
- "#741b47",
191
- "#5b0f00",
183
+ "#B45F06",
184
+ "#BF9000",
185
+ "#38761D",
186
+ "#134F5C",
187
+ "#1155CC",
188
+ "#0B5394",
189
+ "#351C75",
190
+ "#741B47",
191
+ "#5B0F00",
192
192
  "#660000",
193
- "#783f04",
194
- "#7f6000",
195
- "#274e13",
196
- "#0c343d",
197
- "#1c4587",
193
+ "#783F04",
194
+ "#7F6000",
195
+ "#274E13",
196
+ "#0C343D",
197
+ "#1C4587",
198
198
  "#073763",
199
- "#20124d",
200
- "#4c1130",
199
+ "#20124D",
200
+ "#4C1130",
201
201
  ];
202
202
  // Dimensions
203
203
  const MIN_ROW_HEIGHT = 10;
@@ -237,7 +237,7 @@ const DEFAULT_FONT = "'Roboto', arial";
237
237
  const DEFAULT_BORDER_DESC = ["thin", "#000"];
238
238
  const DEFAULT_FILTER_BORDER_DESC = ["thin", FILTERS_COLOR];
239
239
  // DateTimeRegex
240
- const DATETIME_FORMAT = /[ymd:]/;
240
+ const DATETIME_FORMAT = /[ymdhs:]/;
241
241
  // Ranges
242
242
  const INCORRECT_RANGE_STRING = CellErrorType.InvalidReference;
243
243
  // Max Number of history steps kept in memory
@@ -274,11 +274,12 @@ var ComponentsImportance;
274
274
  ComponentsImportance[ComponentsImportance["Highlight"] = 5] = "Highlight";
275
275
  ComponentsImportance[ComponentsImportance["Figure"] = 10] = "Figure";
276
276
  ComponentsImportance[ComponentsImportance["ScrollBar"] = 15] = "ScrollBar";
277
- ComponentsImportance[ComponentsImportance["Dropdown"] = 16] = "Dropdown";
277
+ ComponentsImportance[ComponentsImportance["GridPopover"] = 19] = "GridPopover";
278
278
  ComponentsImportance[ComponentsImportance["GridComposer"] = 20] = "GridComposer";
279
- ComponentsImportance[ComponentsImportance["TopBarComposer"] = 21] = "TopBarComposer";
279
+ ComponentsImportance[ComponentsImportance["Dropdown"] = 21] = "Dropdown";
280
280
  ComponentsImportance[ComponentsImportance["IconPicker"] = 25] = "IconPicker";
281
- ComponentsImportance[ComponentsImportance["Popover"] = 30] = "Popover";
281
+ ComponentsImportance[ComponentsImportance["TopBarComposer"] = 30] = "TopBarComposer";
282
+ ComponentsImportance[ComponentsImportance["Popover"] = 35] = "Popover";
282
283
  ComponentsImportance[ComponentsImportance["ChartAnchor"] = 1000] = "ChartAnchor";
283
284
  })(ComponentsImportance || (ComponentsImportance = {}));
284
285
  const DEFAULT_SHEETVIEW_SIZE = 1000;
@@ -705,8 +706,9 @@ function getDefaultCellHeight(cell) {
705
706
  return DEFAULT_CELL_HEIGHT;
706
707
  }
707
708
  const fontSize = computeTextFontSizeInPixels(cell.style);
708
- const multiLineText = cell.content.split(NEWLINE);
709
- return computeTextLinesHeight(fontSize, multiLineText.length) + 2 * PADDING_AUTORESIZE_VERTICAL;
709
+ // the number of lines should be computed from the formula result, but it's not evaluated at this point
710
+ const numberOfLines = cell.isFormula ? 1 : cell.content.split(NEWLINE).length;
711
+ return computeTextLinesHeight(fontSize, numberOfLines) + 2 * PADDING_AUTORESIZE_VERTICAL;
710
712
  }
711
713
  function computeTextWidth(context, text, style) {
712
714
  context.save();
@@ -1058,6 +1060,8 @@ function replaceSpecialSpaces(text) {
1058
1060
  return text;
1059
1061
  }
1060
1062
 
1063
+ const RBA_REGEX = /rgba?\(|\s+|\)/gi;
1064
+ const HEX_MATCH = /^#([A-F\d]{2}){3,4}$/g;
1061
1065
  const colors$1 = [
1062
1066
  "#eb6d00",
1063
1067
  "#0074d9",
@@ -1083,7 +1087,10 @@ function colorNumberString(color) {
1083
1087
  }
1084
1088
  /**
1085
1089
  * Converts any CSS color value to a standardized hex6 value.
1086
- * Accepts: hex3, hex6, hex8 and rgb (rgba is not supported)
1090
+ * Accepts: hex3, hex6, hex8, rgb[1] and rgba[1].
1091
+ *
1092
+ * [1] under the form rgb(r, g, b, a?) or rgba(r, g, b, a?)
1093
+ * with r,g,b ∈ [0, 255] and a ∈ [0, 1]
1087
1094
  *
1088
1095
  * toHex("#ABC")
1089
1096
  * >> "#AABBCC"
@@ -1094,27 +1101,31 @@ function colorNumberString(color) {
1094
1101
  * toHex("rgb(30, 80, 16)")
1095
1102
  * >> "#1E5010"
1096
1103
  *
1104
+ * * toHex("rgb(30, 80, 16, 0.5)")
1105
+ * >> "#1E501080"
1106
+ *
1097
1107
  */
1098
1108
  function toHex(color) {
1099
- if (color.includes("rgba")) {
1100
- throw new Error(`rgba() conversion currently not supported: ${color}`);
1109
+ let hexColor = color;
1110
+ if (color.startsWith("rgb")) {
1111
+ hexColor = rgbaStringToHex(color);
1101
1112
  }
1102
- if (color.includes("rgb")) {
1103
- return rgbToHex6(color);
1104
- }
1105
- color = color.replace("#", "").toUpperCase();
1106
- if (color.length === 3 || color.length === 4) {
1107
- color = color.split("").reduce((acc, h) => acc + h + h, "");
1113
+ else {
1114
+ hexColor = color.replace("#", "").toUpperCase();
1115
+ if (hexColor.length === 3 || hexColor.length === 4) {
1116
+ hexColor = hexColor.split("").reduce((acc, h) => acc + h + h, "");
1117
+ }
1118
+ hexColor = `#${hexColor}`;
1108
1119
  }
1109
- if (color.replace(/[a-f0-9]/gi, "") !== "") {
1110
- throw new Error("invalid color");
1120
+ if (!hexColor.match(HEX_MATCH)) {
1121
+ throw new Error(`invalid color input: ${color}`);
1111
1122
  }
1112
- return "#" + color;
1123
+ return hexColor;
1113
1124
  }
1114
1125
  function isColorValid(color) {
1115
1126
  try {
1116
- const { r, g, b, a } = colorToRGBA(color);
1117
- return (isColorValueValid(r) && isColorValueValid(g) && isColorValueValid(b) && isColorValueValid(a));
1127
+ toHex(color);
1128
+ return true;
1118
1129
  }
1119
1130
  catch (error) {
1120
1131
  return false;
@@ -1147,15 +1158,29 @@ function relativeLuminance(color) {
1147
1158
  /**
1148
1159
  * Convert a CSS rgb color string to a standardized hex6 color value.
1149
1160
  *
1150
- * rgbToHex6("rgb(30, 80, 16)")
1161
+ * rgbaStringToHex("rgb(30, 80, 16)")
1151
1162
  * >> "#1E5010"
1163
+ *
1164
+ * rgbaStringToHex("rgba(30, 80, 16, 0.5)")
1165
+ * >> "#1E501080"
1166
+ *
1167
+ * DOES NOT SUPPORT NON INTEGER RGB VALUES
1152
1168
  */
1153
- function rgbToHex6(color) {
1154
- return ("#" +
1155
- concat(color
1156
- .slice(4, -1)
1157
- .split(",")
1158
- .map((valueString) => parseInt(valueString, 10).toString(16).padStart(2, "0"))).toUpperCase());
1169
+ function rgbaStringToHex(color) {
1170
+ const stringVals = color.replace(RBA_REGEX, "").split(",");
1171
+ let alphaHex = 255;
1172
+ if (stringVals.length !== 3 && stringVals.length !== 4) {
1173
+ throw new Error("invalid color");
1174
+ }
1175
+ else if (stringVals.length === 4) {
1176
+ const alpha = parseFloat(stringVals.pop() || "1");
1177
+ alphaHex = Math.round((alpha || 1) * 255);
1178
+ }
1179
+ const vals = stringVals.map((val) => parseInt(val, 10));
1180
+ if (alphaHex !== 255) {
1181
+ vals.push(alphaHex);
1182
+ }
1183
+ return "#" + concat(vals.map((value) => value.toString(16).padStart(2, "0"))).toUpperCase();
1159
1184
  }
1160
1185
  /**
1161
1186
  * RGBA to HEX representation (#RRGGBBAA).
@@ -1177,7 +1202,7 @@ function rgbaToHex(rgba) {
1177
1202
  a = "0" + a;
1178
1203
  if (a === "ff")
1179
1204
  a = "";
1180
- return "#" + r + g + b + a;
1205
+ return ("#" + r + g + b + a).toUpperCase();
1181
1206
  }
1182
1207
  /**
1183
1208
  * Color string to RGBA representation
@@ -1298,6 +1323,9 @@ function rgbaToHSLA(rgba) {
1298
1323
  s = +(s * 100).toFixed(1);
1299
1324
  l = +(l * 100).toFixed(1);
1300
1325
  return { a: rgba.a, h, s, l };
1326
+ }
1327
+ function isSameColor(color1, color2) {
1328
+ return isColorValid(color1) && isColorValid(color2) && toHex(color1) === toHex(color2);
1301
1329
  }
1302
1330
 
1303
1331
  /** Reference of a cell (eg. A1, $B$5) */
@@ -1449,7 +1477,7 @@ function scrollDelay(value) {
1449
1477
  * formula, commas are used to separate arguments
1450
1478
  * - it does not support % symbol, in formulas % is an operator
1451
1479
  */
1452
- const formulaNumberRegexp = /^-?\d+(\.?\d*(e\d+)?)?|^-?\.\d+/;
1480
+ const formulaNumberRegexp = /(^-?\d+(\.?\d*(e\d+)?)?|^-?\.\d+)(?!\w|!)/;
1453
1481
  const pIntegerAndDecimals = "(\\d+(,\\d{3,})*(\\.\\d*)?)"; // pattern that match integer number with or without decimal digits
1454
1482
  const pOnlyDecimals = "(\\.\\d+)"; // pattern that match only expression with decimal digits
1455
1483
  const pScientificFormat = "(e(\\+|-)?\\d+)?"; // pattern that match scientific format between zero and one time (should be placed before pPercentFormat)
@@ -3055,6 +3083,7 @@ const CHART_PADDING_RATIO = 0.02;
3055
3083
  const LINE_HEIGHT = 1.2;
3056
3084
  css /* scss */ `
3057
3085
  div.o-scorecard {
3086
+ font-family: ${DEFAULT_FONT};
3058
3087
  user-select: none;
3059
3088
  background-color: white;
3060
3089
  display: flex;
@@ -5105,6 +5134,11 @@ class Popover extends owl.Component {
5105
5134
  this.currentPosition = newPosition;
5106
5135
  });
5107
5136
  }
5137
+ get popoverStyle() {
5138
+ return cssPropertiesToCss({
5139
+ "z-index": `${this.props.zIndex}`,
5140
+ });
5141
+ }
5108
5142
  }
5109
5143
  Popover.template = "o-spreadsheet-Popover";
5110
5144
  Popover.defaultProps = {
@@ -5113,6 +5147,7 @@ Popover.defaultProps = {
5113
5147
  onMouseWheel: () => { },
5114
5148
  onPopoverMoved: () => { },
5115
5149
  onPopoverHidden: () => { },
5150
+ zIndex: ComponentsImportance.Popover,
5116
5151
  };
5117
5152
  Popover.props = {
5118
5153
  anchorRect: Object,
@@ -5124,6 +5159,7 @@ Popover.props = {
5124
5159
  onMouseWheel: { type: Function, optional: true },
5125
5160
  onPopoverHidden: { type: Function, optional: true },
5126
5161
  onPopoverMoved: { type: Function, optional: true },
5162
+ zIndex: { type: Number, optional: true },
5127
5163
  slots: Object,
5128
5164
  };
5129
5165
  class PopoverPositionContext {
@@ -17014,6 +17050,9 @@ class ColorPicker extends owl.Component {
17014
17050
  top,
17015
17051
  });
17016
17052
  }
17053
+ isSameColor(color1, color2) {
17054
+ return isSameColor(color1, color2);
17055
+ }
17017
17056
  }
17018
17057
  ColorPicker.template = "o-spreadsheet-ColorPicker";
17019
17058
  ColorPicker.defaultProps = {
@@ -19255,6 +19294,8 @@ class ContentEditableHelper {
19255
19294
  start = 0;
19256
19295
  if (end > textLength)
19257
19296
  end = textLength;
19297
+ if (start > textLength)
19298
+ start = textLength;
19258
19299
  }
19259
19300
  let startNode = this.findChildAtCharacterIndex(start);
19260
19301
  let endNode = this.findChildAtCharacterIndex(end);
@@ -19524,6 +19565,7 @@ css /* scss */ `
19524
19565
  padding-right: 2px;
19525
19566
 
19526
19567
  box-sizing: border-box;
19568
+ font-family: ${DEFAULT_FONT};
19527
19569
 
19528
19570
  caret-color: black;
19529
19571
  padding-left: 3px;
@@ -20734,6 +20776,9 @@ class GridOverlay extends owl.Component {
20734
20776
  owl.onMounted(() => {
20735
20777
  resizeObserver.observe(this.gridOverlayEl);
20736
20778
  });
20779
+ owl.onWillUnmount(() => {
20780
+ resizeObserver.disconnect();
20781
+ });
20737
20782
  useTouchMove(this.gridOverlay, this.props.onGridMoved, () => {
20738
20783
  const { scrollY } = this.env.model.getters.getActiveSheetDOMScrollInfo();
20739
20784
  return scrollY > 0;
@@ -20790,6 +20835,10 @@ GridOverlay.props = {
20790
20835
  };
20791
20836
 
20792
20837
  class GridPopover extends owl.Component {
20838
+ constructor() {
20839
+ super(...arguments);
20840
+ this.zIndex = ComponentsImportance.GridPopover;
20841
+ }
20793
20842
  get cellPopover() {
20794
20843
  const popover = this.env.model.getters.getCellPopover(this.props.hoveredCell);
20795
20844
  if (!popover.isOpen) {
@@ -22154,6 +22203,10 @@ class Grid extends owl.Component {
22154
22203
  }
22155
22204
  }
22156
22205
  onInput(ev) {
22206
+ // the user meant to paste in the sheet, not open the composer with the pasted content
22207
+ if (!ev.isComposing && ev.inputType === "insertFromPaste") {
22208
+ return;
22209
+ }
22157
22210
  if (ev.data) {
22158
22211
  // if the user types a character on the grid, it means he wants to start composing the selected cell with that
22159
22212
  // character
@@ -23002,6 +23055,70 @@ function fixXlsxUnicode(str) {
23002
23055
  });
23003
23056
  }
23004
23057
 
23058
+ 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;
23059
+ /**
23060
+ * Convert excel format to o_spreadsheet format
23061
+ *
23062
+ * Excel format are defined in openXML §18.8.31
23063
+ */
23064
+ function convertXlsxFormat(numFmtId, formats, warningManager) {
23065
+ var _a, _b, _c;
23066
+ if (numFmtId === 0) {
23067
+ return undefined;
23068
+ }
23069
+ // Format is either defined in the imported data, or the formatId is defined in openXML §18.8.30
23070
+ let format = XLSX_FORMATS_CONVERSION_MAP[numFmtId] || ((_a = formats.find((f) => f.id === numFmtId)) === null || _a === void 0 ? void 0 : _a.format);
23071
+ if (format) {
23072
+ try {
23073
+ let convertedFormat = format.replace(/(.*?);.*/, "$1"); // only take first part of multi-part format
23074
+ convertedFormat = convertedFormat.replace(/\[(.*)-[A-Z0-9]{3}\]/g, "[$1]"); // remove currency and locale/date system/number system info (ECMA §18.8.31)
23075
+ convertedFormat = convertedFormat.replace(/\[\$\]/g, ""); // remove empty bocks
23076
+ // Quotes in format escape sequences of characters. ATM we only support [$...] blocks to escape characters, and only one of them per format
23077
+ const numberOfQuotes = ((_b = convertedFormat.match(/"/g)) === null || _b === void 0 ? void 0 : _b.length) || 0;
23078
+ const numberOfOpenBrackets = ((_c = convertedFormat.match(/\[/g)) === null || _c === void 0 ? void 0 : _c.length) || 0;
23079
+ if (numberOfQuotes / 2 + numberOfOpenBrackets > 1) {
23080
+ throw new Error("Multiple escaped blocks in format");
23081
+ }
23082
+ convertedFormat = convertedFormat.replace(/"(.*)"/g, "[$$$1]"); // replace '"..."' by '[$...]'
23083
+ convertedFormat = convertedFormat.replace(/_.{1}/g, ""); // _ == ignore width of next char for align purposes. Not supported ATM
23084
+ convertedFormat = convertedFormat.replace(/\*.{1}/g, ""); // * == repeat next character enough to fill the line. Not supported ATM
23085
+ convertedFormat = convertedFormat.replace(/\\ /g, " "); // unescape spaces
23086
+ convertedFormat = convertedFormat.replace(/\\./g, (match) => match[1]); // unescape other characters
23087
+ if (isXlsxDateFormat(convertedFormat)) {
23088
+ convertedFormat = convertDateFormat$1(convertedFormat);
23089
+ }
23090
+ if (isFormatSupported(convertedFormat)) {
23091
+ return convertedFormat;
23092
+ }
23093
+ }
23094
+ catch (e) { }
23095
+ }
23096
+ warningManager.generateNotSupportedWarning(WarningTypes.NumFmtIdNotSupported, format || `nmFmtId ${numFmtId}`);
23097
+ return undefined;
23098
+ }
23099
+ function isFormatSupported(format) {
23100
+ try {
23101
+ formatValue(0, format);
23102
+ return true;
23103
+ }
23104
+ catch (e) {
23105
+ return false;
23106
+ }
23107
+ }
23108
+ function isXlsxDateFormat(format) {
23109
+ return format.match(XLSX_DATE_FORMAT_REGEX) !== null;
23110
+ }
23111
+ function convertDateFormat$1(format) {
23112
+ // Some of these aren't defined neither in the OpenXML spec not the Xlsx extension of OpenXML,
23113
+ // but can still occur and are supported by Excel/Google sheets
23114
+ format = format.toLowerCase();
23115
+ format = format.replace(/mmmmm/g, "mmm");
23116
+ format = format.replace(/am\/pm|a\/m/g, "a");
23117
+ format = format.replace(/hhhh/g, "hh");
23118
+ format = format.replace(/\bh\b/g, "hh");
23119
+ return format;
23120
+ }
23121
+
23005
23122
  function convertBorders(data, warningManager) {
23006
23123
  const borderArray = data.borders.map((border) => {
23007
23124
  addBorderWarnings(border, warningManager);
@@ -23064,41 +23181,6 @@ function convertFormats(data, warningManager) {
23064
23181
  }
23065
23182
  return arrayToObject(formats, 1);
23066
23183
  }
23067
- /**
23068
- * Convert excel format to o_spreadsheet format
23069
- *
23070
- * Excel format are defined in openXML §18.8.31
23071
- */
23072
- function convertXlsxFormat(numFmtId, formats, warningManager) {
23073
- var _a, _b, _c;
23074
- if (numFmtId === 0) {
23075
- return undefined;
23076
- }
23077
- // Format is either defined in the imported data, or the formatId is defined in openXML §18.8.30
23078
- let format = XLSX_FORMATS_CONVERSION_MAP[numFmtId] || ((_a = formats.find((f) => f.id === numFmtId)) === null || _a === void 0 ? void 0 : _a.format);
23079
- if (format) {
23080
- try {
23081
- let convertedFormat = format.replace(/(.*?);.*/, "$1"); // only take first part of multi-part format
23082
- convertedFormat = convertedFormat.replace(/\[(.*)-[A-Z0-9]{3}\]/g, "[$1]"); // remove currency and locale/date system/number system info (ECMA §18.8.31)
23083
- convertedFormat = convertedFormat.replace(/\[\$\]/g, ""); // remove empty bocks
23084
- // Quotes in format escape sequences of characters. ATM we only support [$...] blocks to escape characters, and only one of them per format
23085
- const numberOfQuotes = ((_b = convertedFormat.match(/"/g)) === null || _b === void 0 ? void 0 : _b.length) || 0;
23086
- const numberOfOpenBrackets = ((_c = convertedFormat.match(/\[/g)) === null || _c === void 0 ? void 0 : _c.length) || 0;
23087
- if (numberOfQuotes / 2 + numberOfOpenBrackets > 1) {
23088
- throw new Error("Multiple escaped blocks in format");
23089
- }
23090
- convertedFormat = convertedFormat.replace(/"(.*)"/g, "[$$$1]"); // replace '"..."' by '[$...]'
23091
- convertedFormat = convertedFormat.replace(/_.{1}/g, ""); // _ == ignore with of next char for align purposes. Not supported ATM
23092
- convertedFormat = convertedFormat.replace(/\*.{1}/g, ""); // * == repeat next character enough to fill the line. Not supported ATM
23093
- convertedFormat = convertedFormat.replace(/\\ /g, " "); // unescape spaces
23094
- formatValue(0, convertedFormat);
23095
- return convertedFormat;
23096
- }
23097
- catch (e) { }
23098
- }
23099
- warningManager.generateNotSupportedWarning(WarningTypes.NumFmtIdNotSupported, format || `nmFmtId ${numFmtId}`);
23100
- return undefined;
23101
- }
23102
23184
  // ---------------------------------------------------------------------------
23103
23185
  // Warnings
23104
23186
  // ---------------------------------------------------------------------------
@@ -30090,6 +30172,7 @@ class RangeAdapter {
30090
30172
  }
30091
30173
  else if (range.zone[start] >= min && range.zone[end] <= max) {
30092
30174
  changeType = "REMOVE";
30175
+ newRange = range.clone({ ...this.getInvalidRange() });
30093
30176
  }
30094
30177
  else if (range.zone[start] <= max && range.zone[end] >= max) {
30095
30178
  const toRemove = max - range.zone[start] + 1;
@@ -30155,8 +30238,10 @@ class RangeAdapter {
30155
30238
  return { changeType: "NONE" };
30156
30239
  }
30157
30240
  const invalidSheetName = this.getters.getSheetName(cmd.sheetId);
30158
- const sheetId = "";
30159
- range = range.clone({ sheetId, invalidSheetName });
30241
+ range = range.clone({
30242
+ ...this.getInvalidRange(),
30243
+ invalidSheetName,
30244
+ });
30160
30245
  return { changeType: "REMOVE", range };
30161
30246
  }, cmd.sheetId);
30162
30247
  break;
@@ -30416,6 +30501,15 @@ class RangeAdapter {
30416
30501
  }
30417
30502
  return str;
30418
30503
  }
30504
+ getInvalidRange() {
30505
+ return {
30506
+ parts: [],
30507
+ prefixSheet: false,
30508
+ zone: { left: -1, top: -1, right: -1, bottom: -1 },
30509
+ sheetId: "",
30510
+ invalidXc: INCORRECT_RANGE_STRING,
30511
+ };
30512
+ }
30419
30513
  }
30420
30514
  RangeAdapter.getters = [
30421
30515
  "getRangeString",
@@ -31385,9 +31479,7 @@ class CustomColorsPlugin extends UIPlugin {
31385
31479
  // remove duplicates first to check validity on a reduced
31386
31480
  // set of colors, then normalize to HEX and remove duplicates
31387
31481
  // again
31388
- [...new Set([...usedColors, ...this.customColors])]
31389
- .filter(isColorValid)
31390
- .map((c) => toHex(c).toLowerCase())),
31482
+ [...new Set([...usedColors, ...this.customColors])].filter(isColorValid).map(toHex)),
31391
31483
  ]).filter((color) => !COLOR_PICKER_DEFAULTS.includes(color));
31392
31484
  }
31393
31485
  getColorsFromCells(cells) {
@@ -31448,7 +31540,7 @@ class CustomColorsPlugin extends UIPlugin {
31448
31540
  return [...chartsColors];
31449
31541
  }
31450
31542
  tryToAddColor(color) {
31451
- const formattedColor = toHex(color).toLowerCase();
31543
+ const formattedColor = toHex(color);
31452
31544
  if (color && !COLOR_PICKER_DEFAULTS.includes(formattedColor)) {
31453
31545
  this.customColors.add(formattedColor);
31454
31546
  }
@@ -32463,8 +32555,8 @@ class InternalViewport {
32463
32555
  this.getters = getters;
32464
32556
  this.sheetId = sheetId;
32465
32557
  this.boundaries = boundaries;
32466
- this.width = sizeInGrid.width;
32467
- this.height = sizeInGrid.height;
32558
+ this.viewportWidth = sizeInGrid.width;
32559
+ this.viewportHeight = sizeInGrid.height;
32468
32560
  this.offsetScrollbarX = offsets.x;
32469
32561
  this.offsetScrollbarY = offsets.y;
32470
32562
  this.canScrollVertically = options.canScrollVertically;
@@ -32475,6 +32567,10 @@ class InternalViewport {
32475
32567
  this.adjustViewportOffsetY();
32476
32568
  }
32477
32569
  // PUBLIC
32570
+ /** Returns the maximum size (in Pixels) of the viewport relative to its allocated client size
32571
+ * When the viewport grid size is smaller than its client width (resp. height), it will return
32572
+ * the client width (resp. height).
32573
+ */
32478
32574
  getMaxSize() {
32479
32575
  const lastCol = this.getters.findLastVisibleColRowIndex(this.sheetId, "COL", {
32480
32576
  first: this.boundaries.left,
@@ -32486,20 +32582,24 @@ class InternalViewport {
32486
32582
  });
32487
32583
  const { end: lastColEnd, size: lastColSize } = this.getters.getColDimensions(this.sheetId, lastCol);
32488
32584
  const { end: lastRowEnd, size: lastRowSize } = this.getters.getRowDimensions(this.sheetId, lastRow);
32489
- const leftColIndex = this.searchHeaderIndex("COL", lastColEnd - this.width, 0);
32585
+ const leftColIndex = this.searchHeaderIndex("COL", lastColEnd - this.viewportWidth, 0);
32490
32586
  const leftColSize = this.getters.getColSize(this.sheetId, leftColIndex);
32491
- const leftRowIndex = this.searchHeaderIndex("ROW", lastRowEnd - this.height, 0);
32587
+ const leftRowIndex = this.searchHeaderIndex("ROW", lastRowEnd - this.viewportHeight, 0);
32492
32588
  const topRowSize = this.getters.getRowSize(this.sheetId, leftRowIndex);
32493
- const width = lastColEnd -
32494
- this.offsetCorrectionX +
32495
- (this.canScrollHorizontally
32496
- ? Math.max(DEFAULT_CELL_WIDTH, Math.min(leftColSize, this.width - lastColSize))
32497
- : 0);
32498
- const height = lastRowEnd -
32499
- this.offsetCorrectionY +
32500
- (this.canScrollVertically
32501
- ? Math.max(DEFAULT_CELL_HEIGHT + 5, Math.min(topRowSize, this.height - lastRowSize))
32502
- : 0);
32589
+ let width = lastColEnd - this.offsetCorrectionX;
32590
+ if (this.canScrollHorizontally) {
32591
+ width += Math.max(DEFAULT_CELL_WIDTH, // leave some minimal space to let the user know they scrolled all the way
32592
+ Math.min(leftColSize, this.viewportWidth - lastColSize) // Add pixels that allows the snapping at maximum horizontal scroll
32593
+ );
32594
+ width = Math.max(width, this.viewportWidth); // if the viewport grid size is smaller than its client width, return client width
32595
+ }
32596
+ let height = lastRowEnd - this.offsetCorrectionY;
32597
+ if (this.canScrollVertically) {
32598
+ height += Math.max(DEFAULT_CELL_HEIGHT + 5, // leave some space to let the user know they scrolled all the way
32599
+ Math.min(topRowSize, this.viewportHeight - lastRowSize) // Add pixels that allows the snapping at maximum vertical scroll
32600
+ );
32601
+ height = Math.max(height, this.viewportHeight); // if the viewport grid size is smaller than its client height, return client height
32602
+ }
32503
32603
  return { width, height };
32504
32604
  }
32505
32605
  /**
@@ -32508,7 +32608,7 @@ class InternalViewport {
32508
32608
  * It returns -1 if no column is found.
32509
32609
  */
32510
32610
  getColIndex(x, absolute = false) {
32511
- if (x < this.offsetCorrectionX || x > this.offsetCorrectionX + this.width) {
32611
+ if (x < this.offsetCorrectionX || x > this.offsetCorrectionX + this.viewportWidth) {
32512
32612
  return -1;
32513
32613
  }
32514
32614
  return this.searchHeaderIndex("COL", x - this.offsetCorrectionX, this.left, absolute);
@@ -32519,7 +32619,7 @@ class InternalViewport {
32519
32619
  * It returns -1 if no row is found.
32520
32620
  */
32521
32621
  getRowIndex(y, absolute = false) {
32522
- if (y < this.offsetCorrectionY || y > this.offsetCorrectionY + this.height) {
32622
+ if (y < this.offsetCorrectionY || y > this.offsetCorrectionY + this.viewportHeight) {
32523
32623
  return -1;
32524
32624
  }
32525
32625
  return this.searchHeaderIndex("ROW", y - this.offsetCorrectionY, this.top, absolute);
@@ -32546,7 +32646,7 @@ class InternalViewport {
32546
32646
  adjustPositionX(col) {
32547
32647
  const sheetId = this.sheetId;
32548
32648
  const { start, end } = this.getters.getColDimensions(sheetId, col);
32549
- while (end > this.offsetX + this.offsetCorrectionX + this.width &&
32649
+ while (end > this.offsetX + this.offsetCorrectionX + this.viewportWidth &&
32550
32650
  this.offsetX + this.offsetCorrectionX < start) {
32551
32651
  this.offsetX = this.getters.getColDimensions(sheetId, this.left).end - this.offsetCorrectionX;
32552
32652
  this.offsetScrollbarX = this.offsetX;
@@ -32568,7 +32668,7 @@ class InternalViewport {
32568
32668
  adjustPositionY(row) {
32569
32669
  const sheetId = this.sheetId;
32570
32670
  while (this.getters.getRowDimensions(sheetId, row).end >
32571
- this.offsetY + this.height + this.offsetCorrectionY &&
32671
+ this.offsetY + this.viewportHeight + this.offsetCorrectionY &&
32572
32672
  this.offsetY + this.offsetCorrectionY < this.getters.getRowDimensions(sheetId, row).start) {
32573
32673
  this.offsetY = this.getters.getRowDimensions(sheetId, this.top).end - this.offsetCorrectionY;
32574
32674
  this.offsetScrollbarY = this.offsetY;
@@ -32606,8 +32706,8 @@ class InternalViewport {
32606
32706
  const x = this.getters.getColRowOffset("COL", this.zone.left, targetZone.left) +
32607
32707
  this.offsetCorrectionX;
32608
32708
  const y = this.getters.getColRowOffset("ROW", this.zone.top, targetZone.top) + this.offsetCorrectionY;
32609
- const width = Math.min(this.getters.getColRowOffset("COL", targetZone.left, targetZone.right + 1), this.width);
32610
- const height = Math.min(this.getters.getColRowOffset("ROW", targetZone.top, targetZone.bottom + 1), this.height);
32709
+ const width = Math.min(this.getters.getColRowOffset("COL", targetZone.left, targetZone.right + 1), this.viewportWidth);
32710
+ const height = Math.min(this.getters.getColRowOffset("ROW", targetZone.top, targetZone.bottom + 1), this.viewportHeight);
32611
32711
  return {
32612
32712
  x,
32613
32713
  y,
@@ -32670,12 +32770,12 @@ class InternalViewport {
32670
32770
  adjustViewportOffsetX() {
32671
32771
  if (this.canScrollHorizontally) {
32672
32772
  const { width: viewportWidth } = this.getMaxSize();
32673
- if (this.width + this.offsetScrollbarX > viewportWidth) {
32674
- this.offsetScrollbarX = Math.max(0, viewportWidth - this.width);
32773
+ if (this.viewportWidth + this.offsetScrollbarX > viewportWidth) {
32774
+ this.offsetScrollbarX = Math.max(0, viewportWidth - this.viewportWidth);
32675
32775
  }
32676
32776
  }
32677
32777
  this.left = this.getColIndex(this.offsetScrollbarX, true);
32678
- this.right = this.getColIndex(this.offsetScrollbarX + this.width, true);
32778
+ this.right = this.getColIndex(this.offsetScrollbarX + this.viewportWidth, true);
32679
32779
  if (this.right === -1) {
32680
32780
  this.right = this.boundaries.right;
32681
32781
  }
@@ -32687,12 +32787,12 @@ class InternalViewport {
32687
32787
  adjustViewportOffsetY() {
32688
32788
  if (this.canScrollVertically) {
32689
32789
  const { height: paneHeight } = this.getMaxSize();
32690
- if (this.height + this.offsetScrollbarY > paneHeight) {
32691
- this.offsetScrollbarY = Math.max(0, paneHeight - this.height);
32790
+ if (this.viewportHeight + this.offsetScrollbarY > paneHeight) {
32791
+ this.offsetScrollbarY = Math.max(0, paneHeight - this.viewportHeight);
32692
32792
  }
32693
32793
  }
32694
32794
  this.top = this.getRowIndex(this.offsetScrollbarY, true);
32695
- this.bottom = this.getRowIndex(this.offsetScrollbarY + this.width, true);
32795
+ this.bottom = this.getRowIndex(this.offsetScrollbarY + this.viewportWidth, true);
32696
32796
  if (this.bottom === -1) {
32697
32797
  this.bottom = this.boundaries.bottom;
32698
32798
  }
@@ -32703,7 +32803,7 @@ class InternalViewport {
32703
32803
  adjustViewportZoneX() {
32704
32804
  const sheetId = this.sheetId;
32705
32805
  this.left = this.searchHeaderIndex("COL", this.offsetScrollbarX, this.boundaries.left);
32706
- this.right = Math.min(this.boundaries.right, this.searchHeaderIndex("COL", this.width, this.left));
32806
+ this.right = Math.min(this.boundaries.right, this.searchHeaderIndex("COL", this.viewportWidth, this.left));
32707
32807
  if (this.right === -1) {
32708
32808
  this.right = this.getters.getNumberCols(sheetId) - 1;
32709
32809
  }
@@ -32716,7 +32816,7 @@ class InternalViewport {
32716
32816
  adjustViewportZoneY() {
32717
32817
  const sheetId = this.sheetId;
32718
32818
  this.top = this.searchHeaderIndex("ROW", this.offsetScrollbarY, this.boundaries.top);
32719
- this.bottom = Math.min(this.boundaries.bottom, this.searchHeaderIndex("ROW", this.height, this.top));
32819
+ this.bottom = Math.min(this.boundaries.bottom, this.searchHeaderIndex("ROW", this.viewportHeight, this.top));
32720
32820
  if (this.bottom === -1) {
32721
32821
  this.bottom = this.getters.getNumberRows(sheetId) - 1;
32722
32822
  }
@@ -32979,8 +33079,7 @@ class SheetViewPlugin extends UIPlugin {
32979
33079
  return [...new Set(viewports.map((v) => range(v.top, v.bottom + 1)).flat())].filter((row) => !this.getters.isHeaderHidden(sheetId, "ROW", row));
32980
33080
  }
32981
33081
  /**
32982
- * Return the main viewport maximum size. That is the zone dimension
32983
- * with some bottom and right padding.
33082
+ * Return the main viewport maximum size relative to the client size.
32984
33083
  */
32985
33084
  getMainViewportRect() {
32986
33085
  const sheetId = this.getters.getActiveSheetId();
@@ -32996,8 +33095,8 @@ class SheetViewPlugin extends UIPlugin {
32996
33095
  const { width, height } = this.getMainViewportRect();
32997
33096
  const viewport = this.getMainInternalViewport(sheetId);
32998
33097
  return {
32999
- maxOffsetX: Math.max(0, width - viewport.width + 1),
33000
- maxOffsetY: Math.max(0, height - viewport.height + 1),
33098
+ maxOffsetX: Math.max(0, width - viewport.viewportWidth + 1),
33099
+ maxOffsetY: Math.max(0, height - viewport.viewportHeight + 1),
33001
33100
  };
33002
33101
  }
33003
33102
  getColRowOffsetInViewport(dimension, referenceIndex, index) {
@@ -40896,6 +40995,7 @@ class TopBar extends owl.Component {
40896
40995
  this.state.menuState.parentMenu = menu;
40897
40996
  this.isSelectingMenu = true;
40898
40997
  this.openedEl = ev.target;
40998
+ this.env.model.dispatch("STOP_EDITION");
40899
40999
  }
40900
41000
  closeMenus() {
40901
41001
  this.state.activeTool = "";
@@ -41109,9 +41209,6 @@ css /* scss */ `
41109
41209
  color: #333;
41110
41210
  }
41111
41211
 
41112
- * {
41113
- font-family: "Roboto", "RobotoDraft", Helvetica, Arial, sans-serif;
41114
- }
41115
41212
  &,
41116
41213
  *,
41117
41214
  *:before,
@@ -44977,6 +45074,6 @@ exports.setTranslationMethod = setTranslationMethod;
44977
45074
  exports.tokenize = tokenize;
44978
45075
 
44979
45076
 
44980
- __info__.version = '16.2.0';
44981
- __info__.date = '2023-03-15T15:03:34.107Z';
44982
- __info__.hash = 'c74b1d1';
45077
+ __info__.version = '16.2.2';
45078
+ __info__.date = '2023-04-13T08:29:22.236Z';
45079
+ __info__.hash = 'a38f31a';