@odoo/o-spreadsheet 17.0.23 → 17.0.27

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.
@@ -2,17 +2,18 @@
2
2
  /**
3
3
  * This file is generated by o-spreadsheet build tools. Do not edit it.
4
4
  * @see https://github.com/odoo/o-spreadsheet
5
- * @version 17.0.23
6
- * @date 2024-05-24T11:27:27.416Z
7
- * @hash 6c10fec
5
+ * @version 17.0.27
6
+ * @date 2024-07-02T09:01:48.383Z
7
+ * @hash c1c2d01
8
8
  */
9
9
 
10
10
  (function (exports, owl) {
11
11
  'use strict';
12
12
 
13
- // define a mock translation function, when o-spreadsheet runs in standalone it doesn't translate any string
14
- let _translate = (s) => s;
15
- let _loaded = () => false;
13
+ const defaultTranslate = (s) => s;
14
+ const defaultLoaded = () => false;
15
+ let _translate = defaultTranslate;
16
+ let _loaded = defaultLoaded;
16
17
  function sprintf(s, ...values) {
17
18
  if (values.length === 1 && typeof values[0] === "object" && !(values[0] instanceof String)) {
18
19
  const valuesDict = values[0];
@@ -24,7 +25,8 @@
24
25
  return s;
25
26
  }
26
27
  /***
27
- * Allow to inject a translation function from outside o-spreadsheet.
28
+ * Allow to inject a translation function from outside o-spreadsheet. This should be called before instantiating
29
+ * a model.
28
30
  * @param tfn the function that will do the translation
29
31
  * @param loaded a function that returns true when the translation is loaded
30
32
  */
@@ -32,6 +34,18 @@
32
34
  _translate = tfn;
33
35
  _loaded = loaded;
34
36
  }
37
+ /**
38
+ * If no translation function has been set, this will mark the translation are loaded.
39
+ *
40
+ * By default, the translations should not be set as loaded, otherwise top-level translated constants will never be
41
+ * translated. But if by the time the model is instantiated no custom translation function has been set, we can set
42
+ * the default translation function as loaded so o-spreadsheet can be run in standalone with no translations.
43
+ */
44
+ function setDefaultTranslationMethod() {
45
+ if (_translate === defaultTranslate && _loaded === defaultLoaded) {
46
+ _loaded = () => true;
47
+ }
48
+ }
35
49
  const _t = function (s, ...values) {
36
50
  if (!_loaded()) {
37
51
  return new LazyTranslatedString(s, values);
@@ -393,7 +407,10 @@
393
407
  * Check if the object is a plain old javascript object.
394
408
  */
395
409
  function isPlainObject(obj) {
396
- return typeof obj === "object" && obj?.constructor === Object;
410
+ return (typeof obj === "object" &&
411
+ obj !== null &&
412
+ // obj.constructor can be undefined when there's no prototype (`Object.create(null, {})`)
413
+ (obj?.constructor === Object || obj?.constructor === undefined));
397
414
  }
398
415
  /**
399
416
  * Sanitize the name of a sheet, by eventually removing quotes
@@ -4305,8 +4322,11 @@
4305
4322
  /**
4306
4323
  * Create a range from a xc. If the xc is empty, this function returns undefined.
4307
4324
  */
4308
- function createRange(getters, sheetId, range) {
4309
- return range ? getters.getRangeFromSheetXC(sheetId, range) : undefined;
4325
+ function createValidRange(getters, sheetId, xc) {
4326
+ if (!xc)
4327
+ return;
4328
+ const range = getters.getRangeFromSheetXC(sheetId, xc);
4329
+ return !(range.invalidSheetName || range.invalidXc) ? range : undefined;
4310
4330
  }
4311
4331
  /**
4312
4332
  * Spread multiple colrows zone to one row/col zone and add a many new input range as needed.
@@ -5234,8 +5254,8 @@
5234
5254
  type = "scorecard";
5235
5255
  constructor(definition, sheetId, getters) {
5236
5256
  super(definition, sheetId, getters);
5237
- this.keyValue = createRange(getters, sheetId, definition.keyValue);
5238
- this.baseline = createRange(getters, sheetId, definition.baseline);
5257
+ this.keyValue = createValidRange(getters, sheetId, definition.keyValue);
5258
+ this.baseline = createValidRange(getters, sheetId, definition.baseline);
5239
5259
  this.baselineMode = definition.baselineMode;
5240
5260
  this.baselineDescr = definition.baselineDescr;
5241
5261
  this.background = definition.background;
@@ -6706,6 +6726,7 @@
6706
6726
  };
6707
6727
 
6708
6728
  const macRegex = /Mac/i;
6729
+ const MODIFIER_KEYS$1 = ["Shift", "Control", "Alt", "Meta"];
6709
6730
  /**
6710
6731
  * Return true if the event was triggered from
6711
6732
  * a child element.
@@ -6744,6 +6765,31 @@
6744
6765
  function getOpenedMenus() {
6745
6766
  return Array.from(document.querySelectorAll(".o-spreadsheet .o-menu"));
6746
6767
  }
6768
+ const letterRegex = /^[a-zA-Z]$/;
6769
+ /**
6770
+ * Transform a keyboard event into a shortcut string that represent this event. The letters keys will be uppercased.
6771
+ *
6772
+ * @argument ev - The keyboard event to transform
6773
+ * @argument mode - Use either ev.key of ev.code to get the string shortcut
6774
+ *
6775
+ * @example
6776
+ * event : { ctrlKey: true, key: "a" } => "Ctrl+A"
6777
+ * event : { shift: true, alt: true, key: "Home" } => "Alt+Shift+Home"
6778
+ */
6779
+ function keyboardEventToShortcutString(ev, mode = "key") {
6780
+ let keyDownString = "";
6781
+ if (!MODIFIER_KEYS$1.includes(ev.key)) {
6782
+ if (isCtrlKey(ev))
6783
+ keyDownString += "Ctrl+";
6784
+ if (ev.altKey)
6785
+ keyDownString += "Alt+";
6786
+ if (ev.shiftKey)
6787
+ keyDownString += "Shift+";
6788
+ }
6789
+ const key = mode === "key" ? ev.key : ev.code;
6790
+ keyDownString += letterRegex.test(key) ? key.toUpperCase() : key;
6791
+ return keyDownString;
6792
+ }
6747
6793
  function isMacOS() {
6748
6794
  return Boolean(macRegex.test(navigator.userAgent));
6749
6795
  }
@@ -6762,8 +6808,7 @@
6762
6808
  */
6763
6809
  function useSpreadsheetRect() {
6764
6810
  const position = owl.useState({ x: 0, y: 0, width: 0, height: 0 });
6765
- let spreadsheetElement = document.querySelector(".o-spreadsheet");
6766
- updatePosition();
6811
+ let spreadsheetElement = null;
6767
6812
  function updatePosition() {
6768
6813
  if (!spreadsheetElement) {
6769
6814
  spreadsheetElement = document.querySelector(".o-spreadsheet");
@@ -6901,7 +6946,7 @@
6901
6946
  if (!anchor)
6902
6947
  return;
6903
6948
  const propsMaxSize = { width: this.props.maxWidth, height: this.props.maxHeight };
6904
- const elDims = {
6949
+ let elDims = {
6905
6950
  width: el.getBoundingClientRect().width,
6906
6951
  height: el.getBoundingClientRect().height,
6907
6952
  };
@@ -6909,7 +6954,14 @@
6909
6954
  const popoverPositionHelper = this.props.positioning === "BottomLeft"
6910
6955
  ? new BottomLeftPopoverContext(anchor, this.containerRect, propsMaxSize, spreadsheetRect)
6911
6956
  : new TopRightPopoverContext(anchor, this.containerRect, propsMaxSize, spreadsheetRect);
6912
- const style = popoverPositionHelper.getCss(elDims, this.props.verticalOffset);
6957
+ el.style["max-height"] = popoverPositionHelper.getMaxHeight(elDims.height) + "px";
6958
+ el.style["max-width"] = popoverPositionHelper.getMaxWidth(elDims.width) + "px";
6959
+ // Re-compute the dimensions after setting the max-width and max-height
6960
+ elDims = {
6961
+ width: el.getBoundingClientRect().width,
6962
+ height: el.getBoundingClientRect().height,
6963
+ };
6964
+ let style = popoverPositionHelper.getCss(elDims, this.props.verticalOffset);
6913
6965
  for (const property of Object.keys(style)) {
6914
6966
  el.style[property] = style[property];
6915
6967
  }
@@ -6985,8 +7037,6 @@
6985
7037
  const shouldRenderAtRight = this.shouldRenderAtRight(elDims.width);
6986
7038
  verticalOffset = shouldRenderAtBottom ? verticalOffset : -verticalOffset;
6987
7039
  const cssProperties = {
6988
- "max-height": maxHeight + "px",
6989
- "max-width": maxWidth + "px",
6990
7040
  top: this.getTopCoordinate(actualHeight, shouldRenderAtBottom) -
6991
7041
  this.spreadsheetOffset.y -
6992
7042
  verticalOffset +
@@ -8536,7 +8586,7 @@
8536
8586
  if ("chartJsConfig" in runtime) {
8537
8587
  runtime.chartJsConfig.plugins = [backgroundColorChartJSPlugin];
8538
8588
  // @ts-ignore
8539
- const chart = new window.Chart(canvas, runtime.chartJsConfig);
8589
+ const chart = new window.Chart(canvas, deepCopy(runtime.chartJsConfig));
8540
8590
  const imgContent = chart.toBase64Image();
8541
8591
  chart.destroy();
8542
8592
  div.remove();
@@ -8580,7 +8630,7 @@
8580
8630
  constructor(definition, sheetId, getters) {
8581
8631
  super(definition, sheetId, getters);
8582
8632
  this.dataSets = createDataSets(getters, definition.dataSets, sheetId, definition.dataSetsHaveTitle);
8583
- this.labelRange = createRange(getters, sheetId, definition.labelRange);
8633
+ this.labelRange = createValidRange(getters, sheetId, definition.labelRange);
8584
8634
  this.background = definition.background;
8585
8635
  this.verticalAxisPosition = definition.verticalAxisPosition;
8586
8636
  this.legendPosition = definition.legendPosition;
@@ -8823,7 +8873,7 @@
8823
8873
  type = "gauge";
8824
8874
  constructor(definition, sheetId, getters) {
8825
8875
  super(definition, sheetId, getters);
8826
- this.dataRange = createRange(this.getters, this.sheetId, definition.dataRange);
8876
+ this.dataRange = createValidRange(this.getters, this.sheetId, definition.dataRange);
8827
8877
  this.sectionRule = definition.sectionRule;
8828
8878
  this.background = definition.background;
8829
8879
  }
@@ -9157,7 +9207,7 @@
9157
9207
  constructor(definition, sheetId, getters) {
9158
9208
  super(definition, sheetId, getters);
9159
9209
  this.dataSets = createDataSets(this.getters, definition.dataSets, sheetId, definition.dataSetsHaveTitle);
9160
- this.labelRange = createRange(this.getters, sheetId, definition.labelRange);
9210
+ this.labelRange = createValidRange(this.getters, sheetId, definition.labelRange);
9161
9211
  this.background = definition.background;
9162
9212
  this.verticalAxisPosition = definition.verticalAxisPosition;
9163
9213
  this.legendPosition = definition.legendPosition;
@@ -9477,7 +9527,7 @@
9477
9527
  constructor(definition, sheetId, getters) {
9478
9528
  super(definition, sheetId, getters);
9479
9529
  this.dataSets = createDataSets(getters, definition.dataSets, sheetId, definition.dataSetsHaveTitle);
9480
- this.labelRange = createRange(getters, sheetId, definition.labelRange);
9530
+ this.labelRange = createValidRange(getters, sheetId, definition.labelRange);
9481
9531
  this.background = definition.background;
9482
9532
  this.legendPosition = definition.legendPosition;
9483
9533
  this.aggregated = definition.aggregated;
@@ -21724,7 +21774,7 @@
21724
21774
  }
21725
21775
  const getters = this.env.model.getters;
21726
21776
  const sheetId = getters.getActiveSheetId();
21727
- const labelRange = createRange(getters, sheetId, this.labelRange);
21777
+ const labelRange = createValidRange(getters, sheetId, this.labelRange);
21728
21778
  const dataSets = createDataSets(getters, this.dataSeriesRanges, sheetId, this.props.definition.dataSetsHaveTitle);
21729
21779
  if (dataSets.length) {
21730
21780
  return dataSets[0].dataRange.zone.top + 1;
@@ -24945,13 +24995,17 @@
24945
24995
  class SelectMenu extends owl.Component {
24946
24996
  static template = "o-spreadsheet-SelectMenu";
24947
24997
  static components = { Menu };
24998
+ menuId = new UuidGenerator().uuidv4();
24948
24999
  selectRef = owl.useRef("select");
24949
25000
  selectRect = useAbsoluteBoundingRect(this.selectRef);
24950
25001
  state = owl.useState({
24951
25002
  isMenuOpen: false,
24952
25003
  });
24953
- onClick() {
24954
- this.state.isMenuOpen = true;
25004
+ onClick(ev) {
25005
+ if (ev.closedMenuId === this.menuId) {
25006
+ return;
25007
+ }
25008
+ this.state.isMenuOpen = !this.state.isMenuOpen;
24955
25009
  }
24956
25010
  onMenuClosed() {
24957
25011
  this.state.isMenuOpen = false;
@@ -24959,7 +25013,7 @@
24959
25013
  get menuPosition() {
24960
25014
  return {
24961
25015
  x: this.selectRect.x,
24962
- y: this.selectRect.y,
25016
+ y: this.selectRect.y + this.selectRect.height,
24963
25017
  };
24964
25018
  }
24965
25019
  }
@@ -25766,14 +25820,14 @@
25766
25820
  }
25767
25821
  onKeyDown(ev) {
25768
25822
  const figure = this.props.figure;
25769
- switch (ev.key) {
25823
+ const keyDownShortcut = keyboardEventToShortcutString(ev);
25824
+ switch (keyDownShortcut) {
25770
25825
  case "Delete":
25771
25826
  this.env.model.dispatch("DELETE_FIGURE", {
25772
25827
  sheetId: this.env.model.getters.getActiveSheetId(),
25773
25828
  id: figure.id,
25774
25829
  });
25775
25830
  this.props.onFigureDeleted();
25776
- ev.stopPropagation();
25777
25831
  ev.preventDefault();
25778
25832
  ev.stopPropagation();
25779
25833
  break;
@@ -25794,7 +25848,22 @@
25794
25848
  x: figure.x + delta[0],
25795
25849
  y: figure.y + delta[1],
25796
25850
  });
25851
+ ev.preventDefault();
25852
+ ev.stopPropagation();
25853
+ break;
25854
+ case "Ctrl+A":
25855
+ // Maybe in the future we will implement a way to select all figures
25856
+ ev.preventDefault();
25797
25857
  ev.stopPropagation();
25858
+ break;
25859
+ case "Ctrl+Y":
25860
+ case "Ctrl+Z":
25861
+ if (keyDownShortcut === "Ctrl+Y") {
25862
+ this.env.model.dispatch("REQUEST_REDO");
25863
+ }
25864
+ else if (keyDownShortcut === "Ctrl+Z") {
25865
+ this.env.model.dispatch("REQUEST_UNDO");
25866
+ }
25798
25867
  ev.preventDefault();
25799
25868
  ev.stopPropagation();
25800
25869
  break;
@@ -26814,8 +26883,15 @@
26814
26883
  }
26815
26884
  onPaste(ev) {
26816
26885
  if (this.env.model.getters.getEditionMode() !== "inactive") {
26886
+ // let the browser clipboard work
26817
26887
  ev.stopPropagation();
26818
26888
  }
26889
+ else {
26890
+ // the user meant to paste in the sheet, not open the composer with the pasted content
26891
+ // While we're not editing, we still have the focus and should therefore prevent
26892
+ // the native "paste" to occur.
26893
+ ev.preventDefault();
26894
+ }
26819
26895
  }
26820
26896
  /*
26821
26897
  * Triggered automatically by the content-editable between the keydown and key up
@@ -26824,10 +26900,6 @@
26824
26900
  if (!this.shouldProcessInputEvents) {
26825
26901
  return;
26826
26902
  }
26827
- if (ev.inputType === "insertFromPaste" &&
26828
- this.env.model.getters.getEditionMode() === "inactive") {
26829
- return;
26830
- }
26831
26903
  ev.stopPropagation();
26832
26904
  let content;
26833
26905
  if (this.env.model.getters.getEditionMode() === "inactive") {
@@ -27275,10 +27347,7 @@
27275
27347
  }
27276
27348
  get containerStyle() {
27277
27349
  if (this.env.model.getters.getEditionMode() === "inactive" || !this.rect) {
27278
- return `
27279
- position: absolute;
27280
- z-index: -1000;
27281
- `;
27350
+ return `z-index: -1000;`;
27282
27351
  }
27283
27352
  const isFormula = this.env.model.getters.getCurrentContent().startsWith("=");
27284
27353
  const cell = this.env.model.getters.getActiveCell();
@@ -27975,7 +28044,7 @@
27975
28044
  });
27976
28045
  }
27977
28046
  getContainerRect(container) {
27978
- const { width: viewWidth, height: viewHeight } = this.env.model.getters.getMainViewportRect();
28047
+ const { width: viewWidth, height: viewHeight } = this.env.model.getters.getSheetViewDimension();
27979
28048
  const { x: viewportX, y: viewportY } = this.env.model.getters.getMainViewportCoordinates();
27980
28049
  const x = ["bottomRight", "topRight"].includes(container) ? viewportX : 0;
27981
28050
  const width = viewWidth - x;
@@ -38571,6 +38640,9 @@
38571
38640
  if (range.invalidXc) {
38572
38641
  return range.invalidXc;
38573
38642
  }
38643
+ if (!this.getters.tryGetSheet(range.sheetId)) {
38644
+ return INCORRECT_RANGE_STRING;
38645
+ }
38574
38646
  if (range.zone.bottom - range.zone.top < 0 || range.zone.right - range.zone.left < 0) {
38575
38647
  return INCORRECT_RANGE_STRING;
38576
38648
  }
@@ -42022,16 +42094,22 @@
42022
42094
  let newContent = undefined;
42023
42095
  let newFormat = undefined;
42024
42096
  let isExported = true;
42097
+ const exportedSheetData = data.sheets.find((sheet) => sheet.id === position.sheetId);
42025
42098
  const formulaCell = this.getCorrespondingFormulaCell(position);
42026
42099
  if (formulaCell) {
42027
42100
  isExported = isExportableToExcel(formulaCell.compiledFormula.tokens);
42028
42101
  isFormula = isExported;
42029
42102
  if (!isExported) {
42030
- newContent = value.toString();
42031
- newFormat = evaluatedCell.format;
42103
+ // If the cell contains a non-exported formula and that is evaluates to
42104
+ // nothing* ,we don't export it.
42105
+ // * non-falsy value are relevant and so are 0 and FALSE, which only leaves
42106
+ // the empty string.
42107
+ if (value !== "") {
42108
+ newContent = value.toString();
42109
+ newFormat = evaluatedCell.format;
42110
+ }
42032
42111
  }
42033
42112
  }
42034
- const exportedSheetData = data.sheets.find((sheet) => sheet.id === position.sheetId);
42035
42113
  const exportedCellData = exportedSheetData.cells[xc] || {};
42036
42114
  const format = newFormat
42037
42115
  ? getItemId(newFormat, data.formats)
@@ -44291,7 +44369,10 @@
44291
44369
  /**
44292
44370
  * Notify the server that the user client left the collaborative session
44293
44371
  */
44294
- leave() {
44372
+ leave(data) {
44373
+ if (Object.keys(this.clients).length === 1 && this.processedRevisions.size) {
44374
+ this.snapshot(data);
44375
+ }
44295
44376
  delete this.clients[this.clientId];
44296
44377
  this.transportService.leave(this.clientId);
44297
44378
  this.transportService.sendMessage({
@@ -44304,6 +44385,9 @@
44304
44385
  * Send a snapshot of the spreadsheet to the collaboration server
44305
44386
  */
44306
44387
  snapshot(data) {
44388
+ if (this.pendingMessages.length !== 0) {
44389
+ return;
44390
+ }
44307
44391
  const snapshotId = this.uuidGenerator.uuidv4();
44308
44392
  this.transportService.sendMessage({
44309
44393
  type: "SNAPSHOT",
@@ -49084,10 +49168,8 @@
49084
49168
  const refRange = this.getters.getRangeFromSheetXC(activeSheetId, xc);
49085
49169
  return isEqual(this.getters.expandZone(activeSheetId, refRange.zone), oldZone);
49086
49170
  });
49087
- // this function assumes that the previous range is always found because
49088
- // it's called when changing a highlight, which exists by definition
49089
49171
  if (!previousRefToken) {
49090
- throw new Error("Previous range not found");
49172
+ return;
49091
49173
  }
49092
49174
  const previousRange = this.getters.getRangeFromSheetXC(activeSheetId, previousRefToken.value);
49093
49175
  this.selectionStart = previousRefToken.start;
@@ -51643,12 +51725,14 @@
51643
51725
  if (ev.key === "Enter") {
51644
51726
  ev.preventDefault();
51645
51727
  this.stopEdition();
51728
+ this.env.focusableElement.focus();
51646
51729
  }
51647
51730
  if (ev.key === "Escape") {
51648
51731
  this.cancelEdition();
51732
+ this.env.focusableElement.focus();
51649
51733
  }
51650
51734
  }
51651
- onClickSheetName(ev) {
51735
+ onMouseEventSheetName(ev) {
51652
51736
  if (this.state.isEditing)
51653
51737
  ev.stopPropagation();
51654
51738
  }
@@ -56394,7 +56478,11 @@
56394
56478
  let cellNode = escapeXml ``;
56395
56479
  // Either formula or static value inside the cell
56396
56480
  if (cell.isFormula) {
56397
- ({ attrs: additionalAttrs, node: cellNode } = addFormula(cell));
56481
+ const res = addFormula(cell);
56482
+ if (!res) {
56483
+ continue;
56484
+ }
56485
+ ({ attrs: additionalAttrs, node: cellNode } = res);
56398
56486
  }
56399
56487
  else if (cell.content && isMarkdownLink(cell.content)) {
56400
56488
  const { label } = parseMarkdownLink(cell.content);
@@ -56837,6 +56925,7 @@
56837
56925
  coreHandlers = [];
56838
56926
  constructor(data = {}, config = {}, stateUpdateMessages = [], uuidGenerator = new UuidGenerator(), verboseImport = true) {
56839
56927
  super();
56928
+ setDefaultTranslationMethod();
56840
56929
  stateUpdateMessages = repairInitialMessages(data, stateUpdateMessages);
56841
56930
  const workbookData = load(data, verboseImport);
56842
56931
  this.state = new StateObserver();
@@ -56912,7 +57001,7 @@
56912
57001
  this.session.join(this.config.client);
56913
57002
  }
56914
57003
  leaveSession() {
56915
- this.session.leave();
57004
+ this.session.leave(this.exportData());
56916
57005
  }
56917
57006
  setupUiPlugin(Plugin) {
56918
57007
  const plugin = new Plugin(this.uiPluginConfig);
@@ -57336,6 +57425,7 @@
57336
57425
  createAction,
57337
57426
  createActions,
57338
57427
  transformRangeData,
57428
+ deepEquals,
57339
57429
  };
57340
57430
  const links = {
57341
57431
  isMarkdownLink,
@@ -57414,9 +57504,9 @@
57414
57504
  exports.tokenize = tokenize;
57415
57505
 
57416
57506
 
57417
- __info__.version = "17.0.23";
57418
- __info__.date = "2024-05-24T11:27:27.416Z";
57419
- __info__.hash = "6c10fec";
57507
+ __info__.version = "17.0.27";
57508
+ __info__.date = "2024-07-02T09:01:48.383Z";
57509
+ __info__.hash = "c1c2d01";
57420
57510
 
57421
57511
 
57422
57512
  })(this.o_spreadsheet = this.o_spreadsheet || {}, owl);