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