@odoo/o-spreadsheet 17.2.7 → 17.2.9

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.
@@ -3,9 +3,9 @@
3
3
  /**
4
4
  * This file is generated by o-spreadsheet build tools. Do not edit it.
5
5
  * @see https://github.com/odoo/o-spreadsheet
6
- * @version 17.2.7
7
- * @date 2024-05-15T09:20:44.429Z
8
- * @hash 57e89fa
6
+ * @version 17.2.9
7
+ * @date 2024-06-03T14:56:21.684Z
8
+ * @hash 086af6d
9
9
  */
10
10
 
11
11
  'use strict';
@@ -504,6 +504,26 @@ function debounce(func, wait, immediate) {
504
504
  };
505
505
  return debounced;
506
506
  }
507
+ /**
508
+ * Creates a batched version of a callback so that all calls to it in the same
509
+ * microtick will only call the original callback once.
510
+ *
511
+ * @param callback the callback to batch
512
+ * @returns a batched version of the original callback
513
+ *
514
+ * Copied from odoo/owl repo.
515
+ */
516
+ function batched(callback) {
517
+ let scheduled = false;
518
+ return async (...args) => {
519
+ if (!scheduled) {
520
+ scheduled = true;
521
+ await Promise.resolve();
522
+ scheduled = false;
523
+ callback(...args);
524
+ }
525
+ };
526
+ }
507
527
  /*
508
528
  * Concatenate an array of strings.
509
529
  */
@@ -578,8 +598,9 @@ function deepEquals(o1, o2, ignoreFunctions) {
578
598
  return false;
579
599
  }
580
600
  else {
581
- if (ignoreFunctions && typeOfO1Key === "function")
582
- return true;
601
+ if (ignoreFunctions && typeOfO1Key === "function") {
602
+ continue;
603
+ }
583
604
  if (o1[key] !== o2[key])
584
605
  return false;
585
606
  }
@@ -1830,7 +1851,7 @@ class LazyTranslatedString extends String {
1830
1851
  }
1831
1852
  valueOf() {
1832
1853
  const str = super.valueOf();
1833
- return _loaded() ? sprintf(_translate(str), ...this.values) : str;
1854
+ return _loaded() ? sprintf(_translate(str), ...this.values) : sprintf(str, ...this.values);
1834
1855
  }
1835
1856
  toString() {
1836
1857
  return this.valueOf();
@@ -2639,7 +2660,7 @@ function evaluatePredicate(value, criterion) {
2639
2660
  return false;
2640
2661
  }
2641
2662
  if (typeof operand === "number" && operator === "=") {
2642
- return toString(value) === toString(operand);
2663
+ return value.toString() === operand.toString();
2643
2664
  }
2644
2665
  if (operator === "<>" || operator === "=") {
2645
2666
  let result;
@@ -2699,14 +2720,13 @@ function visitMatchingRanges(args, cb, locale, isQuery = false) {
2699
2720
  if (countArg % 2 === 1) {
2700
2721
  throw new EvaluationError(_t("Function [[FUNCTION_NAME]] expects criteria_range and criterion to be in pairs."));
2701
2722
  }
2702
- const dimRow = args[0].length;
2703
- const dimCol = args[0][0].length;
2723
+ const firstArg = toMatrix(args[0]);
2724
+ const dimRow = firstArg.length;
2725
+ const dimCol = firstArg[0].length;
2704
2726
  let predicates = [];
2705
2727
  for (let i = 0; i < countArg - 1; i += 2) {
2706
- const criteriaRange = args[i];
2707
- if (!isMatrix(criteriaRange) ||
2708
- criteriaRange.length !== dimRow ||
2709
- criteriaRange[0].length !== dimCol) {
2728
+ const criteriaRange = toMatrix(args[i]);
2729
+ if (criteriaRange.length !== dimRow || criteriaRange[0].length !== dimCol) {
2710
2730
  throw new EvaluationError(_t("Function [[FUNCTION_NAME]] expects criteria_range to have the same dimension"));
2711
2731
  }
2712
2732
  const description = toString(args[i + 1]);
@@ -2720,7 +2740,7 @@ function visitMatchingRanges(args, cb, locale, isQuery = false) {
2720
2740
  for (let j = 0; j < dimCol; j++) {
2721
2741
  let validatedPredicates = true;
2722
2742
  for (let k = 0; k < countArg - 1; k += 2) {
2723
- const criteriaValue = args[k][i][j].value;
2743
+ const criteriaValue = toMatrix(args[k])[i][j].value;
2724
2744
  const criterion = predicates[k / 2];
2725
2745
  validatedPredicates = evaluatePredicate(criteriaValue ?? undefined, criterion);
2726
2746
  if (!validatedPredicates) {
@@ -4437,8 +4457,11 @@ function copyRangeWithNewSheetId(sheetIdFrom, sheetIdTo, range) {
4437
4457
  /**
4438
4458
  * Create a range from a xc. If the xc is empty, this function returns undefined.
4439
4459
  */
4440
- function createRange(getters, sheetId, range) {
4441
- return range ? getters.getRangeFromSheetXC(sheetId, range) : undefined;
4460
+ function createValidRange(getters, sheetId, xc) {
4461
+ if (!xc)
4462
+ return;
4463
+ const range = getters.getRangeFromSheetXC(sheetId, xc);
4464
+ return !(range.invalidSheetName || range.invalidXc) ? range : undefined;
4442
4465
  }
4443
4466
  /**
4444
4467
  * Spread multiple colrows zone to one row/col zone and add a many new input range as needed.
@@ -8014,10 +8037,66 @@ function getDateCriterionFormattedValues(criterion, getters) {
8014
8037
  });
8015
8038
  }
8016
8039
 
8040
+ /**
8041
+ * This is a generic event bus based on the Owl event bus.
8042
+ * This bus however ensures type safety across events and subscription callbacks.
8043
+ */
8044
+ class EventBus {
8045
+ subscriptions = {};
8046
+ /**
8047
+ * Add a listener for the 'eventType' events.
8048
+ *
8049
+ * Note that the 'owner' of this event can be anything, but will more likely
8050
+ * be a component or a class. The idea is that the callback will be called with
8051
+ * the proper owner bound.
8052
+ *
8053
+ * Also, the owner should be kind of unique. This will be used to remove the
8054
+ * listener.
8055
+ */
8056
+ on(type, owner, callback) {
8057
+ if (!callback) {
8058
+ throw new Error("Missing callback");
8059
+ }
8060
+ if (!this.subscriptions[type]) {
8061
+ this.subscriptions[type] = [];
8062
+ }
8063
+ this.subscriptions[type].push({
8064
+ owner,
8065
+ callback,
8066
+ });
8067
+ }
8068
+ /**
8069
+ * Emit an event of type 'eventType'. Any extra arguments will be passed to
8070
+ * the listeners callback.
8071
+ */
8072
+ trigger(type, payload) {
8073
+ const subs = this.subscriptions[type] || [];
8074
+ for (let i = 0, iLen = subs.length; i < iLen; i++) {
8075
+ const sub = subs[i];
8076
+ sub.callback.call(sub.owner, payload);
8077
+ }
8078
+ }
8079
+ /**
8080
+ * Remove a listener
8081
+ */
8082
+ off(eventType, owner) {
8083
+ const subs = this.subscriptions[eventType];
8084
+ if (subs) {
8085
+ this.subscriptions[eventType] = subs.filter((s) => s.owner !== owner);
8086
+ }
8087
+ }
8088
+ /**
8089
+ * Remove all subscriptions.
8090
+ */
8091
+ clear() {
8092
+ this.subscriptions = {};
8093
+ }
8094
+ }
8095
+
8017
8096
  /**
8018
8097
  * A type-safe dependency container
8019
8098
  */
8020
- class DependencyContainer {
8099
+ class DependencyContainer extends EventBus {
8021
8100
  dependencies = new Map();
8022
8101
  factory = new StoreFactory(this.get.bind(this));
8023
8102
  /**
@@ -8094,15 +8173,12 @@ stores.inject(MyMetaStore, storeInstance);
8094
8173
  }
8095
8174
  return MetaStore;
8096
8175
  }
8097
- class ReactiveStore {
8176
+ class DisposableStore {
8098
8177
  get;
8178
+ disposeCallbacks = [];
8099
8179
  constructor(get) {
8100
8180
  this.get = get;
8101
- return owl.reactive(this);
8102
8181
  }
8103
- }
8104
- class DisposableStore extends ReactiveStore {
8105
- disposeCallbacks = [];
8106
8182
  onDispose(callback) {
8107
8183
  this.disposeCallbacks.push(callback);
8108
8184
  }
@@ -8122,7 +8198,10 @@ function useStoreProvider() {
8122
8198
  const container = new DependencyContainer();
8123
8199
  owl.useSubEnv({
8124
8200
  __spreadsheet_stores__: container,
8125
- getStore: container.get.bind(container),
8201
+ getStore: (Store) => {
8202
+ const store = container.get(Store);
8203
+ return proxifyStoreMutation(store, () => container.trigger("store-updated"));
8204
+ },
8126
8205
  });
8127
8206
  return container;
8128
8207
  }
@@ -8132,14 +8211,57 @@ function useStoreProvider() {
8132
8211
  function useStore(Store) {
8133
8212
  const env = owl.useEnv();
8134
8213
  const container = getDependencyContainer(env);
8135
- return owl.useState(container.get(Store));
8214
+ const store = container.get(Store);
8215
+ return useStoreRenderProxy(container, store);
8136
8216
  }
8137
8217
  function useLocalStore(Store, ...args) {
8138
8218
  const env = owl.useEnv();
8139
8219
  const container = getDependencyContainer(env);
8140
- const store = owl.useState(container.instantiate(Store, ...args));
8220
+ const store = container.instantiate(Store, ...args);
8141
8221
  owl.onWillUnmount(() => store.dispose());
8142
- return store;
8222
+ return useStoreRenderProxy(container, store);
8223
+ }
8224
+ /**
8225
+ * Trigger an event to re-render the app (deep render) when
8226
+ * a store is mutated by invoking one of its mutator methods.
8227
+ */
8228
+ function useStoreRenderProxy(container, store) {
8229
+ const component = owl.useComponent();
8230
+ const proxy = proxifyStoreMutation(store, () => {
8231
+ if (owl.status(component) === "mounted") {
8232
+ container.trigger("store-updated");
8233
+ }
8234
+ });
8235
+ return proxy;
8236
+ }
8237
+ /**
8238
+ * Creates a proxied version of a store object with mutation tracking.
8239
+ * Whenever a mutator method of the store is called, the provided callback function is invoked.
8240
+ */
8241
+ function proxifyStoreMutation(store, callback) {
8242
+ const proxy = new Proxy(store, {
8243
+ get(target, property, receiver) {
8244
+ const thisStore = target;
8245
+ // The third argument is `thisStore` (target) instead of `receiver`.
8246
+ // The goal is to always have the same `this` value in getter functions
8247
+ // (when `target[property]` is an accessor property).
8248
+ // `thisStore` is always the same object reference. `receiver` however is the
8249
+ // object on which the property is called, which is the Proxy object which is different for each component.
8250
+ const value = Reflect.get(target, property, thisStore);
8251
+ if (store.mutators.includes(property)) {
8252
+ const functionProxy = new Proxy(value, {
8253
+ // trap the function call
8254
+ apply(target, thisArg, argArray) {
8255
+ Reflect.apply(target, thisStore, argArray);
8256
+ callback();
8257
+ },
8258
+ });
8259
+ return functionProxy;
8260
+ }
8261
+ return value;
8262
+ },
8263
+ });
8264
+ return proxy;
8143
8265
  }
8144
8266
  function getDependencyContainer(env) {
8145
8267
  const container = env.__spreadsheet_stores__;
@@ -8151,7 +8273,8 @@ function getDependencyContainer(env) {
8151
8273
 
8152
8274
  const ModelStore = createAbstractStore("Model");
8153
8275
 
8154
- class RendererStore extends ReactiveStore {
8276
+ class RendererStore {
8277
+ mutators = ["register", "unRegister"];
8155
8278
  renderers = {};
8156
8279
  register(renderer) {
8157
8280
  if (!renderer.renderingLayers.length) {
@@ -8185,7 +8308,7 @@ class RendererStore extends ReactiveStore {
8185
8308
  class SpreadsheetStore extends DisposableStore {
8186
8309
  // cast the model store as Model to allow model.dispatch to return the DispatchResult
8187
8310
  model = this.get(ModelStore);
8188
- getters = owl.markRaw(this.model.getters);
8311
+ getters = this.model.getters;
8189
8312
  renderer = this.get(RendererStore);
8190
8313
  constructor(get) {
8191
8314
  super(get);
@@ -8232,6 +8355,7 @@ function drawHighlight(renderingContext, highlight, rect) {
8232
8355
  }
8233
8356
 
8234
8357
  class HighlightStore extends SpreadsheetStore {
8358
+ mutators = ["register", "unRegister"];
8235
8359
  providers = [];
8236
8360
  constructor(get) {
8237
8361
  super(get);
@@ -8262,7 +8386,7 @@ class HighlightStore extends SpreadsheetStore {
8262
8386
  this.providers.push(highlightProvider);
8263
8387
  }
8264
8388
  unRegister(highlightProvider) {
8265
- this.providers = this.providers.filter((h) => owl.toRaw(h) !== owl.toRaw(highlightProvider));
8389
+ this.providers = this.providers.filter((h) => h !== highlightProvider);
8266
8390
  }
8267
8391
  drawLayer(ctx, layer) {
8268
8392
  if (layer === "Highlights") {
@@ -8278,6 +8402,16 @@ const NotificationStore = createAbstractStore("Notifications");
8278
8402
 
8279
8403
  const CELL_DELETED_MESSAGE = _t("The cell you are trying to edit has been deleted.");
8280
8404
  class ComposerStore extends SpreadsheetStore {
8405
+ mutators = [
8406
+ "startEdition",
8407
+ "setCurrentContent",
8408
+ "stopEdition",
8409
+ "stopComposerRangeSelection",
8410
+ "cancelEdition",
8411
+ "cycleReferences",
8412
+ "changeComposerCursorSelection",
8413
+ "replaceComposerCursorSelection",
8414
+ ];
8281
8415
  col = 0;
8282
8416
  row = 0;
8283
8417
  editionMode = "inactive";
@@ -8292,9 +8426,9 @@ class ComposerStore extends SpreadsheetStore {
8292
8426
  highlightStore = this.get(HighlightStore);
8293
8427
  constructor(get) {
8294
8428
  super(get);
8295
- this.highlightStore.register(owl.toRaw(this));
8429
+ this.highlightStore.register(this);
8296
8430
  this.onDispose(() => {
8297
- this.highlightStore.unRegister(owl.toRaw(this));
8431
+ this.highlightStore.unRegister(this);
8298
8432
  });
8299
8433
  }
8300
8434
  canStopEdition() {
@@ -8421,7 +8555,7 @@ class ComposerStore extends SpreadsheetStore {
8421
8555
  if (this.isSelectingRange) {
8422
8556
  this.editionMode = "editing";
8423
8557
  }
8424
- this.model.selection.resetAnchor(owl.toRaw(this), {
8558
+ this.model.selection.resetAnchor(this, {
8425
8559
  cell: { col: left, row: top },
8426
8560
  zone: cmd.zone,
8427
8561
  });
@@ -8439,7 +8573,7 @@ class ComposerStore extends SpreadsheetStore {
8439
8573
  row: activePosition.row,
8440
8574
  });
8441
8575
  const zone = this.getters.expandZone(cmd.sheetIdTo, positionToZone({ col, row }));
8442
- this.model.selection.resetAnchor(owl.toRaw(this), { cell: { col, row }, zone });
8576
+ this.model.selection.resetAnchor(this, { cell: { col, row }, zone });
8443
8577
  }
8444
8578
  break;
8445
8579
  case "DELETE_SHEET":
@@ -8538,7 +8672,7 @@ class ComposerStore extends SpreadsheetStore {
8538
8672
  startComposerRangeSelection() {
8539
8673
  if (this.sheetId === this.getters.getActiveSheetId()) {
8540
8674
  const zone = positionToZone({ col: this.col, row: this.row });
8541
- this.model.selection.resetAnchor(owl.toRaw(this), {
8675
+ this.model.selection.resetAnchor(this, {
8542
8676
  cell: { col: this.col, row: this.row },
8543
8677
  zone,
8544
8678
  });
@@ -8567,7 +8701,7 @@ class ComposerStore extends SpreadsheetStore {
8567
8701
  this.setContent(str || this.initialContent, selection);
8568
8702
  this.colorIndexByRange = {};
8569
8703
  const zone = positionToZone({ col: this.col, row: this.row });
8570
- this.model.selection.capture(owl.toRaw(this), { cell: { col: this.col, row: this.row }, zone }, {
8704
+ this.model.selection.capture(this, { cell: { col: this.col, row: this.row }, zone }, {
8571
8705
  handleEvent: this.handleEvent.bind(this),
8572
8706
  release: () => {
8573
8707
  this._stopEdition();
@@ -8675,7 +8809,7 @@ class ComposerStore extends SpreadsheetStore {
8675
8809
  return;
8676
8810
  }
8677
8811
  this.editionMode = "inactive";
8678
- this.model.selection.release(owl.toRaw(this));
8812
+ this.model.selection.release(this);
8679
8813
  }
8680
8814
  /**
8681
8815
  * Reset the current content to the active cell content
@@ -8997,6 +9131,7 @@ class ComposerStore extends SpreadsheetStore {
8997
9131
  }
8998
9132
 
8999
9133
  class ComposerFocusStore extends SpreadsheetStore {
9134
+ mutators = ["focusTopBarComposer", "focusGridComposerContent", "focusGridComposerCell"];
9000
9135
  composerStore = this.get(ComposerStore);
9001
9136
  topBarFocus = "inactive";
9002
9137
  gridFocusMode = "inactive";
@@ -9513,8 +9648,8 @@ let ScorecardChart$1 = class ScorecardChart extends AbstractChart {
9513
9648
  type = "scorecard";
9514
9649
  constructor(definition, sheetId, getters) {
9515
9650
  super(definition, sheetId, getters);
9516
- this.keyValue = createRange(getters, sheetId, definition.keyValue);
9517
- this.baseline = createRange(getters, sheetId, definition.baseline);
9651
+ this.keyValue = createValidRange(getters, sheetId, definition.keyValue);
9652
+ this.baseline = createValidRange(getters, sheetId, definition.baseline);
9518
9653
  this.baselineMode = definition.baselineMode;
9519
9654
  this.baselineDescr = definition.baselineDescr;
9520
9655
  this.background = definition.background;
@@ -10084,6 +10219,9 @@ function makeArg(str, description) {
10084
10219
  if (types.some((t) => t.startsWith("RANGE"))) {
10085
10220
  result.acceptMatrix = true;
10086
10221
  }
10222
+ if (types.every((t) => t.startsWith("RANGE"))) {
10223
+ result.acceptMatrixOnly = true;
10224
+ }
10087
10225
  return result;
10088
10226
  }
10089
10227
  /**
@@ -10365,11 +10503,16 @@ const CHOOSECOLS = {
10365
10503
  compute: function (array, ...columns) {
10366
10504
  const _array = toMatrix(array);
10367
10505
  const _columns = flattenRowFirst(columns, (item) => toInteger(item?.value, this.locale));
10368
- assert(() => _columns.every((col) => col > 0 && col <= _array.length), _t("The columns arguments must be between 1 and %s (got %s).", _array.length.toString(), (_columns.find((col) => col <= 0 || col > _array.length) || 0).toString()));
10506
+ const argOutOfRange = _columns.filter((col) => col === 0 || _array.length < Math.abs(col));
10507
+ assert(() => argOutOfRange.length === 0, _t("The columns arguments must be between -%s and %s (got %s), excluding 0.", _array.length.toString(), _array.length.toString(), argOutOfRange.join(",")));
10369
10508
  const result = Array(_columns.length);
10370
10509
  for (let col = 0; col < _columns.length; col++) {
10371
- const colIndex = _columns[col] - 1; // -1 because columns arguments are 1-indexed
10372
- result[col] = _array[colIndex];
10510
+ if (_columns[col] > 0) {
10511
+ result[col] = _array[_columns[col] - 1]; // -1 because columns arguments are 1-indexed
10512
+ }
10513
+ else {
10514
+ result[col] = _array[_array.length + _columns[col]];
10515
+ }
10373
10516
  }
10374
10517
  return result;
10375
10518
  },
@@ -10390,8 +10533,14 @@ const CHOOSEROWS = {
10390
10533
  const _array = toMatrix(array);
10391
10534
  const _rows = flattenRowFirst(rows, (item) => toInteger(item?.value, this.locale));
10392
10535
  const _nbColumns = _array.length;
10393
- assert(() => _rows.every((row) => row > 0 && row <= _array[0].length), _t("The rows arguments must be between 1 and %s (got %s).", _array[0].length.toString(), (_rows.find((row) => row <= 0 || row > _array[0].length) || 0).toString()));
10394
- return generateMatrix(_nbColumns, _rows.length, (col, row) => _array[col][_rows[row] - 1]); // -1 because rows arguments are 1-indexed
10536
+ const argOutOfRange = _rows.filter((row) => row === 0 || _array[0].length < Math.abs(row));
10537
+ assert(() => argOutOfRange.length === 0, _t("The rows arguments must be between -%s and %s (got %s), excluding 0.", _array[0].length.toString(), _array[0].length.toString(), argOutOfRange.join(",")));
10538
+ return generateMatrix(_nbColumns, _rows.length, (col, row) => {
10539
+ if (_rows[row] > 0) {
10540
+ return _array[col][_rows[row] - 1]; // -1 because columns arguments are 1-indexed
10541
+ }
10542
+ return _array[col][_array[col].length + _rows[row]];
10543
+ });
10395
10544
  },
10396
10545
  isExported: true,
10397
10546
  };
@@ -11299,7 +11448,7 @@ const COUNTUNIQUEIFS = {
11299
11448
  compute: function (range, ...args) {
11300
11449
  let uniqueValues = new Set();
11301
11450
  visitMatchingRanges(args, (i, j) => {
11302
- const data = range[i][j];
11451
+ const data = range[i]?.[j];
11303
11452
  if (isDefined(data)) {
11304
11453
  uniqueValues.add(data.value);
11305
11454
  }
@@ -11929,7 +12078,7 @@ const SUMIF = {
11929
12078
  }
11930
12079
  let sum = 0;
11931
12080
  visitMatchingRanges([criteriaRange, criterion], (i, j) => {
11932
- const value = sumRange[i][j].value;
12081
+ const value = sumRange[i]?.[j]?.value;
11933
12082
  if (typeof value === "number") {
11934
12083
  sum += value;
11935
12084
  }
@@ -11954,7 +12103,7 @@ const SUMIFS = {
11954
12103
  compute: function (sumRange, ...criters) {
11955
12104
  let sum = 0;
11956
12105
  visitMatchingRanges(criters, (i, j) => {
11957
- const value = sumRange[i][j].value;
12106
+ const value = sumRange[i]?.[j]?.value;
11958
12107
  if (typeof value === "number") {
11959
12108
  sum += value;
11960
12109
  }
@@ -12501,7 +12650,7 @@ const AVERAGEIF = {
12501
12650
  let count = 0;
12502
12651
  let sum = 0;
12503
12652
  visitMatchingRanges([criteriaRange, criterion], (i, j) => {
12504
- const value = _averageRange[i][j].value;
12653
+ const value = _averageRange[i]?.[j]?.value;
12505
12654
  if (typeof value === "number") {
12506
12655
  count += 1;
12507
12656
  sum += value;
@@ -12530,7 +12679,7 @@ const AVERAGEIFS = {
12530
12679
  let count = 0;
12531
12680
  let sum = 0;
12532
12681
  visitMatchingRanges(args, (i, j) => {
12533
- const value = _averageRange[i][j].value;
12682
+ const value = _averageRange[i]?.[j]?.value;
12534
12683
  if (typeof value === "number") {
12535
12684
  count += 1;
12536
12685
  sum += value;
@@ -12835,7 +12984,7 @@ const MAXIFS = {
12835
12984
  compute: function (range, ...args) {
12836
12985
  let result = -Infinity;
12837
12986
  visitMatchingRanges(args, (i, j) => {
12838
- const value = range[i][j].value;
12987
+ const value = range[i]?.[j]?.value;
12839
12988
  if (typeof value === "number") {
12840
12989
  result = result < value ? value : result;
12841
12990
  }
@@ -12918,7 +13067,7 @@ const MINIFS = {
12918
13067
  compute: function (range, ...args) {
12919
13068
  let result = Infinity;
12920
13069
  visitMatchingRanges(args, (i, j) => {
12921
- const value = range[i][j].value;
13070
+ const value = range[i]?.[j]?.value;
12922
13071
  if (typeof value === "number") {
12923
13072
  result = result > value ? value : result;
12924
13073
  }
@@ -18670,6 +18819,9 @@ function addInputHandling(descr) {
18670
18819
  }
18671
18820
  args[i] = arg[0][0];
18672
18821
  }
18822
+ if (!isMatrix(arg) && argDefinition.acceptMatrixOnly) {
18823
+ throw new BadExpressionError(_t("Function [[FUNCTION_NAME]] expects the parameter '%s' to be reference to a cell or range.", (i + 1).toString()));
18824
+ }
18673
18825
  }
18674
18826
  return descr.compute.apply(this, args);
18675
18827
  }
@@ -19672,7 +19824,7 @@ class BarChart extends AbstractChart {
19672
19824
  constructor(definition, sheetId, getters) {
19673
19825
  super(definition, sheetId, getters);
19674
19826
  this.dataSets = createDataSets(getters, definition.dataSets, sheetId, definition.dataSetsHaveTitle);
19675
- this.labelRange = createRange(getters, sheetId, definition.labelRange);
19827
+ this.labelRange = createValidRange(getters, sheetId, definition.labelRange);
19676
19828
  this.background = definition.background;
19677
19829
  this.verticalAxisPosition = definition.verticalAxisPosition;
19678
19830
  this.legendPosition = definition.legendPosition;
@@ -19916,7 +20068,7 @@ class GaugeChart extends AbstractChart {
19916
20068
  type = "gauge";
19917
20069
  constructor(definition, sheetId, getters) {
19918
20070
  super(definition, sheetId, getters);
19919
- this.dataRange = createRange(this.getters, this.sheetId, definition.dataRange);
20071
+ this.dataRange = createValidRange(this.getters, this.sheetId, definition.dataRange);
19920
20072
  this.sectionRule = definition.sectionRule;
19921
20073
  this.background = definition.background;
19922
20074
  }
@@ -20437,7 +20589,7 @@ class LineChart extends AbstractChart {
20437
20589
  constructor(definition, sheetId, getters) {
20438
20590
  super(definition, sheetId, getters);
20439
20591
  this.dataSets = createDataSets(this.getters, definition.dataSets, sheetId, definition.dataSetsHaveTitle);
20440
- this.labelRange = createRange(this.getters, sheetId, definition.labelRange);
20592
+ this.labelRange = createValidRange(this.getters, sheetId, definition.labelRange);
20441
20593
  this.background = definition.background;
20442
20594
  this.verticalAxisPosition = definition.verticalAxisPosition;
20443
20595
  this.legendPosition = definition.legendPosition;
@@ -20552,7 +20704,7 @@ class PieChart extends AbstractChart {
20552
20704
  constructor(definition, sheetId, getters) {
20553
20705
  super(definition, sheetId, getters);
20554
20706
  this.dataSets = createDataSets(getters, definition.dataSets, sheetId, definition.dataSetsHaveTitle);
20555
- this.labelRange = createRange(getters, sheetId, definition.labelRange);
20707
+ this.labelRange = createValidRange(getters, sheetId, definition.labelRange);
20556
20708
  this.background = definition.background;
20557
20709
  this.legendPosition = definition.legendPosition;
20558
20710
  this.aggregated = definition.aggregated;
@@ -20754,7 +20906,7 @@ class ScatterChart extends AbstractChart {
20754
20906
  constructor(definition, sheetId, getters) {
20755
20907
  super(definition, sheetId, getters);
20756
20908
  this.dataSets = createDataSets(this.getters, definition.dataSets, sheetId, definition.dataSetsHaveTitle);
20757
- this.labelRange = createRange(this.getters, sheetId, definition.labelRange);
20909
+ this.labelRange = createValidRange(this.getters, sheetId, definition.labelRange);
20758
20910
  this.background = definition.background;
20759
20911
  this.verticalAxisPosition = definition.verticalAxisPosition;
20760
20912
  this.legendPosition = definition.legendPosition;
@@ -20840,13 +20992,6 @@ function createScatterChartRuntime(chart, getters) {
20840
20992
  // have less options than the line chart (it only works with linear labels)
20841
20993
  chartJsConfig.type = "line";
20842
20994
  const configOptions = chartJsConfig.options;
20843
- configOptions.elements = {
20844
- point: {
20845
- radius: 3,
20846
- hoverRadius: 3,
20847
- hitRadius: 8,
20848
- },
20849
- };
20850
20995
  const locale = getters.getLocale();
20851
20996
  configOptions.plugins.tooltip.callbacks.title = () => "";
20852
20997
  configOptions.plugins.tooltip.callbacks.label = (tooltipItem) => {
@@ -21453,6 +21598,7 @@ function interactiveAddMerge(env, sheetId, target) {
21453
21598
  }
21454
21599
 
21455
21600
  class HoveredCellStore extends SpreadsheetStore {
21601
+ mutators = ["clear", "hover"];
21456
21602
  col;
21457
21603
  row;
21458
21604
  handle(cmd) {
@@ -21472,6 +21618,7 @@ class HoveredCellStore extends SpreadsheetStore {
21472
21618
  }
21473
21619
 
21474
21620
  class CellPopoverStore extends SpreadsheetStore {
21621
+ mutators = ["open", "close"];
21475
21622
  persistentPopover;
21476
21623
  hoveredCell = this.get(HoveredCellStore);
21477
21624
  handle(cmd) {
@@ -25963,12 +26110,13 @@ function updateSelectionWithArrowKeys(ev, selection) {
25963
26110
 
25964
26111
  // The name is misleading and can be confused with the DOM focus.
25965
26112
  class FocusStore {
26113
+ mutators = ["focus", "unfocus"];
25966
26114
  focusedElement = null;
25967
26115
  focus(element) {
25968
26116
  this.focusedElement = element;
25969
26117
  }
25970
26118
  unfocus(element) {
25971
- if (this.focusedElement && owl.toRaw(this.focusedElement) === owl.toRaw(element)) {
26119
+ if (this.focusedElement && this.focusedElement === element) {
25972
26120
  this.focusedElement = null;
25973
26121
  }
25974
26122
  }
@@ -25984,6 +26132,16 @@ class FocusStore {
25984
26132
  class SelectionInputStore extends SpreadsheetStore {
25985
26133
  initialRanges;
25986
26134
  inputHasSingleRange;
26135
+ mutators = [
26136
+ "resetWithRanges",
26137
+ "focusById",
26138
+ "unfocus",
26139
+ "addEmptyRange",
26140
+ "removeRange",
26141
+ "changeRange",
26142
+ "reset",
26143
+ "confirm",
26144
+ ];
25987
26145
  ranges = [];
25988
26146
  focusedRangeIndex = null;
25989
26147
  inputSheetId;
@@ -26043,7 +26201,7 @@ class SelectionInputStore extends SpreadsheetStore {
26043
26201
  row: 0,
26044
26202
  });
26045
26203
  const zone = this.getters.expandZone(cmd.sheetIdTo, positionToZone({ col, row }));
26046
- this.model.selection.resetAnchor(owl.toRaw(this), { cell: { col, row }, zone });
26204
+ this.model.selection.resetAnchor(this, { cell: { col, row }, zone });
26047
26205
  }
26048
26206
  break;
26049
26207
  }
@@ -26062,7 +26220,7 @@ class SelectionInputStore extends SpreadsheetStore {
26062
26220
  if (focusIndex !== -1) {
26063
26221
  this.focus(focusIndex);
26064
26222
  const { left, top } = newZone;
26065
- this.model.selection.resetAnchor(owl.toRaw(this), {
26223
+ this.model.selection.resetAnchor(this, {
26066
26224
  cell: { col: left, row: top },
26067
26225
  zone: newZone,
26068
26226
  });
@@ -26153,7 +26311,7 @@ class SelectionInputStore extends SpreadsheetStore {
26153
26311
  }
26154
26312
  get hasMainFocus() {
26155
26313
  const focusedElement = this.focusStore.focusedElement;
26156
- return !!focusedElement && owl.toRaw(focusedElement) === owl.toRaw(this);
26314
+ return !!focusedElement && focusedElement === this;
26157
26315
  }
26158
26316
  get highlights() {
26159
26317
  if (!this.hasMainFocus) {
@@ -26182,7 +26340,7 @@ class SelectionInputStore extends SpreadsheetStore {
26182
26340
  unfocus() {
26183
26341
  this.focusedRangeIndex = null;
26184
26342
  this.focusStore.unfocus(this);
26185
- this.model.selection.release(owl.toRaw(this));
26343
+ this.model.selection.release(this);
26186
26344
  }
26187
26345
  captureSelection() {
26188
26346
  if (this.focusedRangeIndex === null) {
@@ -26191,7 +26349,7 @@ class SelectionInputStore extends SpreadsheetStore {
26191
26349
  const range = this.ranges[this.focusedRangeIndex];
26192
26350
  const sheetId = this.getters.getActiveSheetId();
26193
26351
  const zone = this.getters.getRangeFromSheetXC(sheetId, range?.xc || "A1").zone;
26194
- this.model.selection.capture(owl.toRaw(this), { cell: { col: zone.left, row: zone.top }, zone }, {
26352
+ this.model.selection.capture(this, { cell: { col: zone.left, row: zone.top }, zone }, {
26195
26353
  handleEvent: this.handleEvent.bind(this),
26196
26354
  release: this.unfocus.bind(this),
26197
26355
  });
@@ -26667,7 +26825,7 @@ class LineBarPieConfigPanel extends owl.Component {
26667
26825
  }
26668
26826
  const getters = this.env.model.getters;
26669
26827
  const sheetId = getters.getActiveSheetId();
26670
- const labelRange = createRange(getters, sheetId, this.labelRange);
26828
+ const labelRange = createValidRange(getters, sheetId, this.labelRange);
26671
26829
  const dataSets = createDataSets(getters, this.dataSeriesRanges, sheetId, this.props.definition.dataSetsHaveTitle);
26672
26830
  if (dataSets.length) {
26673
26831
  return dataSets[0].dataRange.zone.top + 1;
@@ -26696,15 +26854,23 @@ class BarConfigPanel extends LineBarPieConfigPanel {
26696
26854
  }
26697
26855
  }
26698
26856
 
26857
+ /**
26858
+ * Start listening to pointer events and apply the given callbacks.
26859
+ *
26860
+ * @returns A function to remove the listeners.
26861
+ */
26699
26862
  function startDnd(onMouseMove, onMouseUp, onMouseDown = () => { }) {
26700
- const _onMouseUp = (ev) => {
26701
- onMouseUp(ev);
26863
+ const removeListeners = () => {
26702
26864
  window.removeEventListener("pointerdown", onMouseDown);
26703
26865
  window.removeEventListener("pointerup", _onMouseUp);
26704
26866
  window.removeEventListener("dragstart", _onDragStart);
26705
26867
  window.removeEventListener("pointermove", onMouseMove);
26706
26868
  window.removeEventListener("wheel", onMouseMove);
26707
26869
  };
26870
+ const _onMouseUp = (ev) => {
26871
+ onMouseUp(ev);
26872
+ removeListeners();
26873
+ };
26708
26874
  function _onDragStart(ev) {
26709
26875
  ev.preventDefault();
26710
26876
  }
@@ -26716,6 +26882,7 @@ function startDnd(onMouseMove, onMouseUp, onMouseDown = () => { }) {
26716
26882
  // preventDefault() is not allowed in passive event handler.
26717
26883
  // https://chromestatus.com/feature/6662647093133312
26718
26884
  window.addEventListener("wheel", onMouseMove, { passive: false });
26885
+ return removeListeners;
26719
26886
  }
26720
26887
  /**
26721
26888
  * Function to be used during a pointerdown event, this function allows to
@@ -27659,6 +27826,7 @@ chartSidePanelComponentRegistry
27659
27826
  });
27660
27827
 
27661
27828
  class MainChartPanelStore extends SpreadsheetStore {
27829
+ mutators = ["activatePanel"];
27662
27830
  panel = "configuration";
27663
27831
  activatePanel(panel) {
27664
27832
  this.panel = panel;
@@ -27909,6 +28077,7 @@ function useDragAndDropListItems() {
27909
28077
  state.itemsStyle = {};
27910
28078
  document.body.style.cursor = previousCursor;
27911
28079
  args.onCancel?.();
28080
+ cleanUp();
27912
28081
  };
27913
28082
  const onDragEnd = (itemId, indexAtEnd) => {
27914
28083
  state.draggedItemId = undefined;
@@ -27929,7 +28098,8 @@ function useDragAndDropListItems() {
27929
28098
  onDragEnd,
27930
28099
  onCancel: state.cancel,
27931
28100
  });
27932
- startDnd(dndHelper.onMouseMove.bind(dndHelper), dndHelper.onMouseUp.bind(dndHelper));
28101
+ const stopListening = startDnd(dndHelper.onMouseMove.bind(dndHelper), dndHelper.onMouseUp.bind(dndHelper));
28102
+ cleanupFns.push(stopListening);
27933
28103
  const onScroll = dndHelper.onScroll.bind(dndHelper);
27934
28104
  args.containerEl.addEventListener("scroll", onScroll);
27935
28105
  cleanupFns.push(() => args.containerEl.removeEventListener("scroll", onScroll));
@@ -28006,7 +28176,7 @@ class DOMDndHelper {
28006
28176
  this.moveDraggedItemToPosition(this.currentMousePosition + this.scrollOffset);
28007
28177
  }
28008
28178
  onMouseMove(ev) {
28009
- if (ev.button !== -1) {
28179
+ if (ev.button > 1) {
28010
28180
  this.onCancel();
28011
28181
  return;
28012
28182
  }
@@ -28197,14 +28367,14 @@ function useHoveredElement(ref) {
28197
28367
 
28198
28368
  function useHighlightsOnHover(ref, highlightProvider) {
28199
28369
  const hoverState = useHoveredElement(ref);
28200
- const env = owl.useEnv();
28370
+ const stores = useStoreProvider();
28201
28371
  useHighlights({
28202
28372
  get highlights() {
28203
28373
  return hoverState.hovered ? highlightProvider.highlights : [];
28204
28374
  },
28205
28375
  });
28206
28376
  owl.useEffect(() => {
28207
- env.model.dispatch("RENDER_CANVAS");
28377
+ stores.trigger("store-updated");
28208
28378
  }, () => [hoverState.hovered]);
28209
28379
  }
28210
28380
  function useHighlights(highlightProvider) {
@@ -29660,6 +29830,14 @@ var Direction;
29660
29830
  Direction[Direction["next"] = 1] = "next";
29661
29831
  })(Direction || (Direction = {}));
29662
29832
  class FindAndReplaceStore extends SpreadsheetStore {
29833
+ mutators = [
29834
+ "updateSearchOptions",
29835
+ "updateSearchContent",
29836
+ "searchFormulas",
29837
+ "selectPreviousMatch",
29838
+ "selectNextMatch",
29839
+ "replace",
29840
+ ];
29663
29841
  allSheetsMatches = [];
29664
29842
  activeSheetMatches = [];
29665
29843
  specificRangeMatches = [];
@@ -29684,11 +29862,11 @@ class FindAndReplaceStore extends SpreadsheetStore {
29684
29862
  this.initialShowFormulaState = this.model.getters.shouldShowFormulas();
29685
29863
  this.searchOptions.searchFormulas = this.initialShowFormulaState;
29686
29864
  const highlightStore = get(HighlightStore);
29687
- highlightStore.register(owl.toRaw(this));
29865
+ highlightStore.register(this);
29688
29866
  this.onDispose(() => {
29689
29867
  this.model.dispatch("SET_FORMULA_VISIBILITY", { show: this.initialShowFormulaState });
29690
29868
  this.updateSearchContent.stopDebounce();
29691
- highlightStore.unRegister(owl.toRaw(this));
29869
+ highlightStore.unRegister(this);
29692
29870
  });
29693
29871
  }
29694
29872
  get searchMatches() {
@@ -31168,14 +31346,14 @@ class FigureComponent extends owl.Component {
31168
31346
  }
31169
31347
  onKeyDown(ev) {
31170
31348
  const figure = this.props.figure;
31171
- switch (ev.key) {
31349
+ const keyDownShortcut = keyboardEventToShortcutString(ev);
31350
+ switch (keyDownShortcut) {
31172
31351
  case "Delete":
31173
31352
  this.env.model.dispatch("DELETE_FIGURE", {
31174
31353
  sheetId: this.env.model.getters.getActiveSheetId(),
31175
31354
  id: figure.id,
31176
31355
  });
31177
31356
  this.props.onFigureDeleted();
31178
- ev.stopPropagation();
31179
31357
  ev.preventDefault();
31180
31358
  ev.stopPropagation();
31181
31359
  break;
@@ -31196,7 +31374,22 @@ class FigureComponent extends owl.Component {
31196
31374
  x: figure.x + delta[0],
31197
31375
  y: figure.y + delta[1],
31198
31376
  });
31377
+ ev.preventDefault();
31378
+ ev.stopPropagation();
31379
+ break;
31380
+ case "Ctrl+A":
31381
+ // Maybe in the future we will implement a way to select all figures
31382
+ ev.preventDefault();
31199
31383
  ev.stopPropagation();
31384
+ break;
31385
+ case "Ctrl+Y":
31386
+ case "Ctrl+Z":
31387
+ if (keyDownShortcut === "Ctrl+Y") {
31388
+ this.env.model.dispatch("REQUEST_REDO");
31389
+ }
31390
+ else if (keyDownShortcut === "Ctrl+Z") {
31391
+ this.env.model.dispatch("REQUEST_UNDO");
31392
+ }
31200
31393
  ev.preventDefault();
31201
31394
  ev.stopPropagation();
31202
31395
  break;
@@ -31331,6 +31524,7 @@ unGroupHeadersMenuRegistry
31331
31524
  });
31332
31525
 
31333
31526
  class DOMFocusableElementStore {
31527
+ mutators = ["setFocusableElement", "focus"];
31334
31528
  focusableElement = undefined;
31335
31529
  setFocusableElement(element) {
31336
31530
  this.focusableElement = element;
@@ -32067,7 +32261,7 @@ class Composer extends owl.Component {
32067
32261
  "Ctrl+Enter": this.processNewLineEvent,
32068
32262
  Escape: this.processEscapeKey,
32069
32263
  F2: () => console.warn("Not implemented"),
32070
- F4: this.processF4Key,
32264
+ F4: (ev) => this.processF4Key(ev),
32071
32265
  Tab: (ev) => this.processTabKey(ev, "right"),
32072
32266
  "Shift+Tab": (ev) => this.processTabKey(ev, "left"),
32073
32267
  };
@@ -32189,7 +32383,8 @@ class Composer extends owl.Component {
32189
32383
  processEscapeKey() {
32190
32384
  this.composerStore.cancelEdition();
32191
32385
  }
32192
- processF4Key() {
32386
+ processF4Key(ev) {
32387
+ ev.stopPropagation();
32193
32388
  this.composerStore.cycleReferences();
32194
32389
  this.processContent();
32195
32390
  }
@@ -34392,13 +34587,6 @@ class GridRenderer {
34392
34587
  this.getters = get(ModelStore).getters;
34393
34588
  this.renderer = get(RendererStore);
34394
34589
  this.renderer.register(this);
34395
- /**
34396
- * Mark the instance as raw to avoid reactivity as this class is instanciated
34397
- * as a Store by `useGridDrawing` (which casts it as reactive).
34398
- *
34399
- * Calling `this.` on a reactive instance is significantly slower than on a raw object.
34400
- */
34401
- owl.markRaw(this);
34402
34590
  }
34403
34591
  get renderingLayers() {
34404
34592
  return ["Background", "Headers"];
@@ -35028,7 +35216,7 @@ class GridRenderer {
35028
35216
  function useGridDrawing(refName, model, canvasSize) {
35029
35217
  const canvasRef = owl.useRef(refName);
35030
35218
  owl.useEffect(drawGrid);
35031
- const rendererManager = useStore(RendererStore);
35219
+ const rendererStore = useStore(RendererStore);
35032
35220
  useStore(GridRenderer);
35033
35221
  function drawGrid() {
35034
35222
  const canvas = canvasRef.el;
@@ -35056,7 +35244,11 @@ function useGridDrawing(refName, model, canvasSize) {
35056
35244
  ctx.scale(dpr, dpr);
35057
35245
  for (const layer of OrderedLayers()) {
35058
35246
  model.drawLayer(renderingContext, layer);
35059
- rendererManager.drawLayer(renderingContext, layer);
35247
+ // @ts-ignore 'drawLayer' is not declated as a mutator because:
35248
+ // it does not mutate anything. Most importantly it's used
35249
+ // during rendering. Invoking a mutator during rendering would
35250
+ // trigger another rendering, ultimately resulting in an infinite loop.
35251
+ rendererStore.drawLayer(renderingContext, layer);
35060
35252
  }
35061
35253
  }
35062
35254
  }
@@ -35458,6 +35650,7 @@ class VerticalScrollBar extends owl.Component {
35458
35650
  }
35459
35651
 
35460
35652
  class SidePanelStore extends SpreadsheetStore {
35653
+ mutators = ["open", "toggle", "close"];
35461
35654
  initialPanelProps = {};
35462
35655
  componentTag = "";
35463
35656
  get isOpen() {
@@ -40984,12 +41177,6 @@ function compileTokens(tokens) {
40984
41177
  // detect when an argument need to be evaluated as a meta argument
40985
41178
  const isMeta = argTypes.includes("META");
40986
41179
  const hasRange = argTypes.some((t) => isRangeType(t));
40987
- const isRangeOnly = argTypes.every((t) => isRangeType(t));
40988
- if (isRangeOnly) {
40989
- if (!isRangeInput(currentArg)) {
40990
- throw new BadExpressionError(_t("Function %s expects the parameter %s to be reference to a cell or range, not a %s.", functionName, (i + 1).toString(), currentArg.type.toLowerCase()));
40991
- }
40992
- }
40993
41180
  compiledArgs.push(compileAST(currentArg, isMeta, hasRange, {
40994
41181
  functionName,
40995
41182
  paramIndex: i + 1,
@@ -41160,16 +41347,6 @@ function assertEnoughArgs(ast) {
41160
41347
  function isRangeType(type) {
41161
41348
  return type.startsWith("RANGE");
41162
41349
  }
41163
- function isRangeInput(arg) {
41164
- if (arg.type === "REFERENCE") {
41165
- return true;
41166
- }
41167
- if (arg.type === "FUNCALL") {
41168
- const fnDef = functions$1[arg.value.toUpperCase()];
41169
- return fnDef && isRangeType(fnDef.returns[0]);
41170
- }
41171
- return false;
41172
- }
41173
41350
 
41174
41351
  const functions = functionRegistry.content;
41175
41352
  function isExportableToExcel(tokens) {
@@ -43286,7 +43463,12 @@ class MergePlugin extends CorePlugin {
43286
43463
  * if they have at least a common cell
43287
43464
  */
43288
43465
  doesIntersectMerge(sheetId, zone) {
43289
- return positions(zone).some(({ col, row }) => this.getMerge({ sheetId, col, row }) !== undefined);
43466
+ for (const merge of this.getMerges(sheetId)) {
43467
+ if (overlap(zone, merge)) {
43468
+ return true;
43469
+ }
43470
+ }
43471
+ return false;
43290
43472
  }
43291
43473
  /**
43292
43474
  * Returns true if two columns have at least one merge in common
@@ -43902,6 +44084,9 @@ class RangeAdapter {
43902
44084
  if (range.invalidXc) {
43903
44085
  return range.invalidXc;
43904
44086
  }
44087
+ if (!this.getters.tryGetSheet(range.sheetId)) {
44088
+ return CellErrorType.InvalidReference;
44089
+ }
43905
44090
  if (range.zone.bottom - range.zone.top < 0 || range.zone.right - range.zone.left < 0) {
43906
44091
  return CellErrorType.InvalidReference;
43907
44092
  }
@@ -47286,7 +47471,6 @@ class PositionSet {
47286
47471
  *
47287
47472
  */
47288
47473
  class SpreadingRelation {
47289
- createEmptyPositionSet;
47290
47474
  /**
47291
47475
  * Internal structure:
47292
47476
  * For something like
@@ -47317,9 +47501,6 @@ class SpreadingRelation {
47317
47501
  */
47318
47502
  resultsToArrayFormulas = new PositionMap();
47319
47503
  arrayFormulasToResults = new PositionMap();
47320
- constructor(createEmptyPositionSet) {
47321
- this.createEmptyPositionSet = createEmptyPositionSet;
47322
- }
47323
47504
  getFormulaPositionsSpreadingOn(resultPosition) {
47324
47505
  return this.resultsToArrayFormulas.get(resultPosition) || EMPTY_ARRAY;
47325
47506
  }
@@ -47338,13 +47519,13 @@ class SpreadingRelation {
47338
47519
  */
47339
47520
  addRelation({ arrayFormulaPosition, resultPosition, }) {
47340
47521
  if (!this.resultsToArrayFormulas.has(resultPosition)) {
47341
- this.resultsToArrayFormulas.set(resultPosition, this.createEmptyPositionSet());
47522
+ this.resultsToArrayFormulas.set(resultPosition, []);
47342
47523
  }
47343
- this.resultsToArrayFormulas.get(resultPosition)?.add(arrayFormulaPosition);
47524
+ this.resultsToArrayFormulas.get(resultPosition)?.push(arrayFormulaPosition);
47344
47525
  if (!this.arrayFormulasToResults.has(arrayFormulaPosition)) {
47345
- this.arrayFormulasToResults.set(arrayFormulaPosition, this.createEmptyPositionSet());
47526
+ this.arrayFormulasToResults.set(arrayFormulaPosition, []);
47346
47527
  }
47347
- this.arrayFormulasToResults.get(arrayFormulaPosition)?.add(resultPosition);
47528
+ this.arrayFormulasToResults.get(arrayFormulaPosition)?.push(resultPosition);
47348
47529
  }
47349
47530
  hasArrayFormulaResult(position) {
47350
47531
  return this.resultsToArrayFormulas.has(position);
@@ -47365,7 +47546,7 @@ class Evaluator {
47365
47546
  evaluatedCells = new PositionMap();
47366
47547
  formulaDependencies = lazy(new FormulaDependencyGraph(this.createEmptyPositionSet.bind(this)));
47367
47548
  blockedArrayFormulas = new PositionSet({});
47368
- spreadingRelations = new SpreadingRelation(this.createEmptyPositionSet.bind(this));
47549
+ spreadingRelations = new SpreadingRelation();
47369
47550
  constructor(context, getters) {
47370
47551
  this.context = context;
47371
47552
  this.getters = getters;
@@ -47442,7 +47623,7 @@ class Evaluator {
47442
47623
  }
47443
47624
  buildDependencyGraph() {
47444
47625
  this.blockedArrayFormulas = this.createEmptyPositionSet();
47445
- this.spreadingRelations = new SpreadingRelation(this.createEmptyPositionSet.bind(this));
47626
+ this.spreadingRelations = new SpreadingRelation();
47446
47627
  this.formulaDependencies = lazy(() => {
47447
47628
  const dependencies = [...this.getAllCells()].flatMap((position) => this.getDirectDependencies(position)
47448
47629
  .filter((range) => !range.invalidSheetName && !range.invalidXc)
@@ -47931,16 +48112,22 @@ class EvaluationPlugin extends UIPlugin {
47931
48112
  let newContent = undefined;
47932
48113
  let newFormat = undefined;
47933
48114
  let isExported = true;
48115
+ const exportedSheetData = data.sheets.find((sheet) => sheet.id === position.sheetId);
47934
48116
  const formulaCell = this.getCorrespondingFormulaCell(position);
47935
48117
  if (formulaCell) {
47936
48118
  isExported = isExportableToExcel(formulaCell.compiledFormula.tokens);
47937
48119
  isFormula = isExported;
47938
48120
  if (!isExported) {
47939
- newContent = (value ?? "").toString();
47940
- newFormat = evaluatedCell.format;
48121
+ // If the cell contains a non-exported formula and that is evaluates to
48122
+ // nothing* ,we don't export it.
48123
+ // * non-falsy value are relevant and so are 0 and FALSE, which only leaves
48124
+ // the empty string.
48125
+ if (value !== "") {
48126
+ newContent = (value ?? "").toString();
48127
+ newFormat = evaluatedCell.format;
48128
+ }
47941
48129
  }
47942
48130
  }
47943
- const exportedSheetData = data.sheets.find((sheet) => sheet.id === position.sheetId);
47944
48131
  const exportedCellData = exportedSheetData.cells[xc] || {};
47945
48132
  const format = newFormat
47946
48133
  ? getItemId(newFormat, data.formats)
@@ -49606,62 +49793,6 @@ class AutomaticSumPlugin extends UIPlugin {
49606
49793
  }
49607
49794
  }
49608
49795
 
49609
- /**
49610
- * This is a generic event bus based on the Owl event bus.
49611
- * This bus however ensures type safety across events and subscription callbacks.
49612
- */
49613
- class EventBus {
49614
- subscriptions = {};
49615
- /**
49616
- * Add a listener for the 'eventType' events.
49617
- *
49618
- * Note that the 'owner' of this event can be anything, but will more likely
49619
- * be a component or a class. The idea is that the callback will be called with
49620
- * the proper owner bound.
49621
- *
49622
- * Also, the owner should be kind of unique. This will be used to remove the
49623
- * listener.
49624
- */
49625
- on(type, owner, callback) {
49626
- if (!callback) {
49627
- throw new Error("Missing callback");
49628
- }
49629
- if (!this.subscriptions[type]) {
49630
- this.subscriptions[type] = [];
49631
- }
49632
- this.subscriptions[type].push({
49633
- owner,
49634
- callback,
49635
- });
49636
- }
49637
- /**
49638
- * Emit an event of type 'eventType'. Any extra arguments will be passed to
49639
- * the listeners callback.
49640
- */
49641
- trigger(type, payload) {
49642
- const subs = this.subscriptions[type] || [];
49643
- for (let i = 0, iLen = subs.length; i < iLen; i++) {
49644
- const sub = subs[i];
49645
- sub.callback.call(sub.owner, payload);
49646
- }
49647
- }
49648
- /**
49649
- * Remove a listener
49650
- */
49651
- off(eventType, owner) {
49652
- const subs = this.subscriptions[eventType];
49653
- if (subs) {
49654
- this.subscriptions[eventType] = subs.filter((s) => s.owner !== owner);
49655
- }
49656
- }
49657
- /**
49658
- * Remove all subscriptions.
49659
- */
49660
- clear() {
49661
- this.subscriptions = {};
49662
- }
49663
- }
49664
-
49665
49796
  /*
49666
49797
  * This file contains the specifics transformations
49667
49798
  */
@@ -51986,7 +52117,7 @@ class ClipboardPlugin extends UIPlugin {
51986
52117
  paintFormatStatus = "inactive";
51987
52118
  originSheetId;
51988
52119
  copiedData;
51989
- _isCutOperation;
52120
+ _isCutOperation = false;
51990
52121
  // ---------------------------------------------------------------------------
51991
52122
  // Command Handling
51992
52123
  // ---------------------------------------------------------------------------
@@ -51998,14 +52129,17 @@ class ClipboardPlugin extends UIPlugin {
51998
52129
  case "PASTE_FROM_OS_CLIPBOARD": {
51999
52130
  const copiedData = this.convertOSClipboardData(cmd.text);
52000
52131
  const pasteOption = cmd.pasteOption || (this.paintFormatStatus !== "inactive" ? "onlyFormat" : undefined);
52001
- return this.isPasteAllowed(cmd.target, copiedData, { pasteOption });
52132
+ return this.isPasteAllowed(cmd.target, copiedData, { pasteOption, isCutOperation: false });
52002
52133
  }
52003
52134
  case "PASTE": {
52004
52135
  if (!this.copiedData) {
52005
52136
  return "EmptyClipboard" /* CommandResult.EmptyClipboard */;
52006
52137
  }
52007
52138
  const pasteOption = cmd.pasteOption || (this.paintFormatStatus !== "inactive" ? "onlyFormat" : undefined);
52008
- return this.isPasteAllowed(cmd.target, this.copiedData, { pasteOption });
52139
+ return this.isPasteAllowed(cmd.target, this.copiedData, {
52140
+ pasteOption: pasteOption,
52141
+ isCutOperation: this._isCutOperation,
52142
+ });
52009
52143
  }
52010
52144
  case "COPY_PASTE_CELLS_ABOVE": {
52011
52145
  const zones = this.getters.getSelectedZones();
@@ -52023,13 +52157,13 @@ class ClipboardPlugin extends UIPlugin {
52023
52157
  }
52024
52158
  case "INSERT_CELL": {
52025
52159
  const { cut, paste } = this.getInsertCellsTargets(cmd.zone, cmd.shiftDimension);
52026
- const copiedData = this.copy("CUT", cut);
52027
- return this.isPasteAllowed(paste, copiedData, {});
52160
+ const copiedData = this.copy(cut);
52161
+ return this.isPasteAllowed(paste, copiedData, { isCutOperation: true });
52028
52162
  }
52029
52163
  case "DELETE_CELL": {
52030
52164
  const { cut, paste } = this.getDeleteCellsTargets(cmd.zone, cmd.shiftDimension);
52031
- const copiedData = this.copy("CUT", cut);
52032
- return this.isPasteAllowed(paste, copiedData, {});
52165
+ const copiedData = this.copy(cut);
52166
+ return this.isPasteAllowed(paste, copiedData, { isCutOperation: true });
52033
52167
  }
52034
52168
  case "ACTIVATE_PAINT_FORMAT": {
52035
52169
  if (this.paintFormatStatus !== "inactive") {
@@ -52047,23 +52181,27 @@ class ClipboardPlugin extends UIPlugin {
52047
52181
  const zones = this.getters.getSelectedZones();
52048
52182
  this.status = "visible";
52049
52183
  this.originSheetId = this.getters.getActiveSheetId();
52050
- this.copiedData = this.copy(cmd.type, zones);
52184
+ this.copiedData = this.copy(zones);
52185
+ this._isCutOperation = cmd.type === "CUT";
52051
52186
  break;
52052
52187
  case "PASTE_FROM_OS_CLIPBOARD": {
52188
+ this._isCutOperation = false;
52053
52189
  this.copiedData = this.convertOSClipboardData(cmd.text);
52054
52190
  const pasteOption = cmd.pasteOption || (this.paintFormatStatus !== "inactive" ? "onlyFormat" : undefined);
52055
- this.paste(cmd.target, {
52191
+ this.paste(cmd.target, this.copiedData, {
52056
52192
  pasteOption,
52057
52193
  selectTarget: true,
52194
+ isCutOperation: false,
52058
52195
  });
52059
52196
  this.status = "invisible";
52060
52197
  break;
52061
52198
  }
52062
52199
  case "PASTE": {
52063
52200
  const pasteOption = cmd.pasteOption || (this.paintFormatStatus !== "inactive" ? "onlyFormat" : undefined);
52064
- this.paste(cmd.target, {
52201
+ this.paste(cmd.target, this.copiedData, {
52065
52202
  pasteOption,
52066
52203
  selectTarget: true,
52204
+ isCutOperation: this._isCutOperation,
52067
52205
  });
52068
52206
  if (this.paintFormatStatus === "oneOff") {
52069
52207
  this.paintFormatStatus = "inactive";
@@ -52084,9 +52222,9 @@ class ClipboardPlugin extends UIPlugin {
52084
52222
  top: multipleRowsInSelection ? zone.top : zone.top - 1,
52085
52223
  };
52086
52224
  this.originSheetId = this.getters.getActiveSheetId();
52087
- this.copiedData = this.copy("COPY", [copyTarget]);
52088
- this.paste([zone], {
52089
- pasteOption: undefined,
52225
+ const copiedData = this.copy([copyTarget]);
52226
+ this.paste([zone], copiedData, {
52227
+ isCutOperation: false,
52090
52228
  selectTarget: true,
52091
52229
  });
52092
52230
  }
@@ -52101,9 +52239,9 @@ class ClipboardPlugin extends UIPlugin {
52101
52239
  left: multipleColsInSelection ? zone.left : zone.left - 1,
52102
52240
  };
52103
52241
  this.originSheetId = this.getters.getActiveSheetId();
52104
- this.copiedData = this.copy("COPY", [copyTarget]);
52105
- this.paste([zone], {
52106
- pasteOption: undefined,
52242
+ const copiedData = this.copy([copyTarget]);
52243
+ this.paste([zone], copiedData, {
52244
+ isCutOperation: false,
52107
52245
  selectTarget: true,
52108
52246
  });
52109
52247
  }
@@ -52119,20 +52257,20 @@ class ClipboardPlugin extends UIPlugin {
52119
52257
  }
52120
52258
  break;
52121
52259
  }
52122
- this.copiedData = this.copy("CUT", cut);
52123
- this.paste(paste, {});
52260
+ const copiedData = this.copy(cut);
52261
+ this.paste(paste, copiedData, { isCutOperation: true });
52124
52262
  break;
52125
52263
  }
52126
52264
  case "INSERT_CELL": {
52127
52265
  const { cut, paste } = this.getInsertCellsTargets(cmd.zone, cmd.shiftDimension);
52128
- this.copiedData = this.copy("CUT", cut);
52129
- this.paste(paste, {});
52266
+ const copiedData = this.copy(cut);
52267
+ this.paste(paste, copiedData, { isCutOperation: true });
52130
52268
  break;
52131
52269
  }
52132
52270
  case "ADD_COLUMNS_ROWS": {
52133
52271
  this.status = "invisible";
52134
52272
  // If we add a col/row inside or before the cut area, we invalidate the clipboard
52135
- if (this._isCutOperation !== true) {
52273
+ if (this._isCutOperation !== true || cmd.sheetId !== this.copiedData?.sheetId) {
52136
52274
  return;
52137
52275
  }
52138
52276
  const isClipboardDirty = this.isColRowDirtyingClipboard(cmd.position === "before" ? cmd.base : cmd.base + 1, cmd.dimension);
@@ -52144,7 +52282,7 @@ class ClipboardPlugin extends UIPlugin {
52144
52282
  case "REMOVE_COLUMNS_ROWS": {
52145
52283
  this.status = "invisible";
52146
52284
  // If we remove a col/row inside or before the cut area, we invalidate the clipboard
52147
- if (this._isCutOperation !== true) {
52285
+ if (this._isCutOperation !== true || cmd.sheetId !== this.copiedData?.sheetId) {
52148
52286
  return;
52149
52287
  }
52150
52288
  for (let el of cmd.elements) {
@@ -52158,7 +52296,8 @@ class ClipboardPlugin extends UIPlugin {
52158
52296
  break;
52159
52297
  }
52160
52298
  case "REPEAT_PASTE": {
52161
- this.paste(cmd.target, {
52299
+ this.paste(cmd.target, this.copiedData, {
52300
+ isCutOperation: false,
52162
52301
  pasteOption: cmd.pasteOption,
52163
52302
  selectTarget: true,
52164
52303
  });
@@ -52166,7 +52305,7 @@ class ClipboardPlugin extends UIPlugin {
52166
52305
  }
52167
52306
  case "ACTIVATE_PAINT_FORMAT": {
52168
52307
  const zones = this.getters.getSelectedZones();
52169
- this.copiedData = this.copy("COPY", zones);
52308
+ this.copiedData = this.copy(zones);
52170
52309
  this.status = "visible";
52171
52310
  if (cmd.persistent) {
52172
52311
  this.paintFormatStatus = "persistent";
@@ -52197,7 +52336,6 @@ class ClipboardPlugin extends UIPlugin {
52197
52336
  }
52198
52337
  }
52199
52338
  convertOSClipboardData(clipboardData) {
52200
- this._isCutOperation = false;
52201
52339
  const handlers = clipboardHandlersRegistries.figureHandlers
52202
52340
  .getAll()
52203
52341
  .map((handler) => new handler(this.getters, this.dispatch));
@@ -52235,7 +52373,6 @@ class ClipboardPlugin extends UIPlugin {
52235
52373
  for (const handler of this.selectClipboardHandlers(copiedData)) {
52236
52374
  const result = handler.isPasteAllowed(this.getters.getActiveSheetId(), target, copiedData, {
52237
52375
  ...options,
52238
- isCutOperation: this.isCutOperation(),
52239
52376
  });
52240
52377
  if (result !== "Success" /* CommandResult.Success */) {
52241
52378
  return result;
@@ -52258,9 +52395,8 @@ class ClipboardPlugin extends UIPlugin {
52258
52395
  }
52259
52396
  return false;
52260
52397
  }
52261
- copy(operation, zones) {
52398
+ copy(zones) {
52262
52399
  let copiedData = {};
52263
- this._isCutOperation = operation === "CUT";
52264
52400
  const clipboardData = this.getClipboardData(zones);
52265
52401
  for (const handler of this.selectClipboardHandlers(clipboardData)) {
52266
52402
  const data = handler.copy(clipboardData);
@@ -52268,8 +52404,8 @@ class ClipboardPlugin extends UIPlugin {
52268
52404
  }
52269
52405
  return copiedData;
52270
52406
  }
52271
- paste(zones, options) {
52272
- if (!this.copiedData) {
52407
+ paste(zones, copiedData, options) {
52408
+ if (!copiedData) {
52273
52409
  return;
52274
52410
  }
52275
52411
  let zone = undefined;
@@ -52277,12 +52413,9 @@ class ClipboardPlugin extends UIPlugin {
52277
52413
  let target = {
52278
52414
  zones,
52279
52415
  };
52280
- const handlers = this.selectClipboardHandlers(this.copiedData);
52416
+ const handlers = this.selectClipboardHandlers(copiedData);
52281
52417
  for (const handler of handlers) {
52282
- const currentTarget = handler.getPasteTarget(zones, this.copiedData, {
52283
- ...options,
52284
- isCutOperation: this.isCutOperation(),
52285
- });
52418
+ const currentTarget = handler.getPasteTarget(zones, copiedData, options);
52286
52419
  if (currentTarget.figureId) {
52287
52420
  target.figureId = currentTarget.figureId;
52288
52421
  }
@@ -52298,7 +52431,7 @@ class ClipboardPlugin extends UIPlugin {
52298
52431
  if (zone !== undefined) {
52299
52432
  this.addMissingDimensions(this.getters.getActiveSheetId(), zone.right - zone.left + 1, zone.bottom - zone.top + 1, zone.left, zone.top);
52300
52433
  }
52301
- handlers.forEach((handler) => handler.paste(target, this.copiedData, { ...options, isCutOperation: this.isCutOperation() }));
52434
+ handlers.forEach((handler) => handler.paste(target, copiedData, options));
52302
52435
  if (!options?.selectTarget) {
52303
52436
  return;
52304
52437
  }
@@ -54768,6 +54901,7 @@ class BottomBarSheet extends owl.Component {
54768
54901
  sheetDivRef = owl.useRef("sheetDiv");
54769
54902
  sheetNameRef = owl.useRef("sheetNameSpan");
54770
54903
  editionState = "initializing";
54904
+ DOMFocusableElementStore;
54771
54905
  setup() {
54772
54906
  owl.onMounted(() => {
54773
54907
  if (this.isSheetActive) {
@@ -54780,6 +54914,7 @@ class BottomBarSheet extends owl.Component {
54780
54914
  this.focusInputAndSelectContent();
54781
54915
  }
54782
54916
  });
54917
+ this.DOMFocusableElementStore = useStore(DOMFocusableElementStore);
54783
54918
  }
54784
54919
  focusInputAndSelectContent() {
54785
54920
  if (!this.state.isEditing || !this.sheetNameRef.el)
@@ -54821,9 +54956,11 @@ class BottomBarSheet extends owl.Component {
54821
54956
  if (ev.key === "Enter") {
54822
54957
  ev.preventDefault();
54823
54958
  this.stopEdition();
54959
+ this.DOMFocusableElementStore.focus();
54824
54960
  }
54825
54961
  if (ev.key === "Escape") {
54826
54962
  this.cancelEdition();
54963
+ this.DOMFocusableElementStore.focus();
54827
54964
  }
54828
54965
  }
54829
54966
  onClickSheetName(ev) {
@@ -56779,16 +56916,25 @@ class Spreadsheet extends owl.Component {
56779
56916
  }
56780
56917
  }, () => [this.env.model.getters.getActiveSheetId()]);
56781
56918
  owl.useExternalListener(window, "resize", () => this.render(true));
56919
+ // For some reason, the wheel event is not properly registered inside templates
56920
+ // in Chromium-based browsers based on chromium 125
56921
+ // This hack ensures the event declared in the template is properly registered/working
56922
+ owl.useExternalListener(document.body, "wheel", () => { });
56782
56923
  this.bindModelEvents();
56783
56924
  owl.onWillUpdateProps((nextProps) => {
56784
56925
  if (nextProps.model !== this.props.model) {
56785
56926
  throw new Error("Changing the props model is not supported at the moment.");
56786
56927
  }
56787
56928
  });
56929
+ const render = batched(this.render.bind(this, true));
56788
56930
  owl.onMounted(() => {
56789
56931
  this.checkViewportSize();
56932
+ stores.on("store-updated", this, render);
56933
+ });
56934
+ owl.onWillUnmount(() => {
56935
+ this.unbindModelEvents();
56936
+ stores.off("store-updated", this);
56790
56937
  });
56791
- owl.onWillUnmount(() => this.unbindModelEvents());
56792
56938
  owl.onPatched(() => {
56793
56939
  this.checkViewportSize();
56794
56940
  });
@@ -59537,7 +59683,11 @@ function addRows(construct, data, sheet) {
59537
59683
  let cellNode = escapeXml ``;
59538
59684
  // Either formula or static value inside the cell
59539
59685
  if (cell.isFormula) {
59540
- ({ attrs: additionalAttrs, node: cellNode } = addFormula(cell));
59686
+ const res = addFormula(cell);
59687
+ if (!res) {
59688
+ continue;
59689
+ }
59690
+ ({ attrs: additionalAttrs, node: cellNode } = res);
59541
59691
  }
59542
59692
  else if (cell.content && isMarkdownLink(cell.content)) {
59543
59693
  const { label } = parseMarkdownLink(cell.content);
@@ -60617,6 +60767,6 @@ exports.tokenColors = tokenColors;
60617
60767
  exports.tokenize = tokenize;
60618
60768
 
60619
60769
 
60620
- __info__.version = "17.2.7";
60621
- __info__.date = "2024-05-15T09:20:44.429Z";
60622
- __info__.hash = "57e89fa";
60770
+ __info__.version = "17.2.9";
60771
+ __info__.date = "2024-06-03T14:56:21.684Z";
60772
+ __info__.hash = "086af6d";