@odoo/o-spreadsheet 19.3.14 → 19.3.16

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,9 +2,9 @@
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 19.3.14
6
- * @date 2026-07-30T06:55:43.749Z
7
- * @hash 3dd5632
5
+ * @version 19.3.16
6
+ * @date 2026-08-12T09:15:31.627Z
7
+ * @hash 464be42
8
8
  */
9
9
 
10
10
  Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
@@ -4259,19 +4259,58 @@ function applyVectorization(formula, args, acceptToVectorize = void 0) {
4259
4259
  }
4260
4260
  }
4261
4261
  if (countVectorizedCol === 1 && countVectorizedRow === 1) return formula(...args);
4262
- const getArgOffset = (i, j) => args.map((arg, index) => {
4263
- switch (vectorArgsType?.[index]) {
4264
- case "matrix": return arg[i][j];
4265
- case "horizontal": return arg[i][0];
4266
- case "vertical": return arg[0][j];
4267
- case void 0: return arg;
4262
+ const argsBuffer = new Array(args.length);
4263
+ const argGetters = [];
4264
+ const vectorizedIndices = [];
4265
+ for (let k = 0; k < args.length; k++) {
4266
+ const arg = args[k];
4267
+ switch (vectorArgsType?.[k]) {
4268
+ case "matrix":
4269
+ argGetters.push((i, j) => arg[i][j]);
4270
+ vectorizedIndices.push(k);
4271
+ break;
4272
+ case "horizontal":
4273
+ argGetters.push((i) => arg[i][0]);
4274
+ vectorizedIndices.push(k);
4275
+ break;
4276
+ case "vertical":
4277
+ argGetters.push((_i, j) => arg[0][j]);
4278
+ vectorizedIndices.push(k);
4279
+ break;
4280
+ case void 0:
4281
+ argsBuffer[k] = arg;
4282
+ break;
4268
4283
  }
4269
- });
4270
- return generateMatrix(countVectorizedCol, countVectorizedRow, (col, row) => {
4271
- if (col > vectorizedColLimit - 1 || row > vectorizedRowLimit - 1) return new NotAvailableError(_t("Array arguments to [[FUNCTION_NAME]] are of different size."));
4272
- const singleCellComputeResult = formula(...getArgOffset(col, row));
4273
- return isMatrix(singleCellComputeResult) ? singleCellComputeResult[0][0] : singleCellComputeResult;
4274
- });
4284
+ }
4285
+ const nbVectorized = vectorizedIndices.length;
4286
+ let callFormula;
4287
+ switch (argsBuffer.length) {
4288
+ case 1:
4289
+ callFormula = () => formula(argsBuffer[0]);
4290
+ break;
4291
+ case 2:
4292
+ callFormula = () => formula(argsBuffer[0], argsBuffer[1]);
4293
+ break;
4294
+ case 3:
4295
+ callFormula = () => formula(argsBuffer[0], argsBuffer[1], argsBuffer[2]);
4296
+ break;
4297
+ default: callFormula = () => formula(...argsBuffer);
4298
+ }
4299
+ const result = new Array(countVectorizedCol);
4300
+ for (let col = 0; col < countVectorizedCol; col++) {
4301
+ const column = new Array(countVectorizedRow);
4302
+ result[col] = column;
4303
+ for (let row = 0; row < countVectorizedRow; row++) {
4304
+ if (col > vectorizedColLimit - 1 || row > vectorizedRowLimit - 1) {
4305
+ column[row] = new NotAvailableError(_t("Array arguments to [[FUNCTION_NAME]] are of different size."));
4306
+ continue;
4307
+ }
4308
+ for (let k = 0; k < nbVectorized; k++) argsBuffer[vectorizedIndices[k]] = argGetters[k](col, row);
4309
+ const singleCellComputeResult = callFormula();
4310
+ column[row] = isMatrix(singleCellComputeResult) ? singleCellComputeResult[0][0] : singleCellComputeResult;
4311
+ }
4312
+ }
4313
+ return result;
4275
4314
  }
4276
4315
  /**
4277
4316
  * This function allows to visit arguments and stop the visit if necessary.
@@ -4618,12 +4657,15 @@ function emptyDataErrorMessage(argName) {
4618
4657
  //#endregion
4619
4658
  //#region src/functions/create_compute_function.ts
4620
4659
  function createComputeFunction(descr) {
4660
+ let currentArgDefinitions = [];
4621
4661
  function vectorizedCompute(...args) {
4622
4662
  const acceptToVectorize = [];
4663
+ currentArgDefinitions = new Array(args.length);
4623
4664
  const argsToFocus = argTargeting(descr, args.length);
4624
4665
  for (let i = 0; i < args.length; i++) {
4625
4666
  const argIndex = argsToFocus[i].index;
4626
4667
  const argDefinition = descr.args[argIndex];
4668
+ currentArgDefinitions[i] = argDefinition;
4627
4669
  const arg = args[i];
4628
4670
  if (!isMatrix(arg) && argDefinition.acceptMatrixOnly) throw new BadExpressionError(_t("Function %s expects the parameter '%s' to be reference to a cell or range.", descr.name, (i + 1).toString()));
4629
4671
  acceptToVectorize.push(!argDefinition.acceptMatrix);
@@ -4636,10 +4678,9 @@ function createComputeFunction(descr) {
4636
4678
  return result;
4637
4679
  }
4638
4680
  function errorHandlingCompute(...args) {
4639
- const argsToFocus = argTargeting(descr, args.length);
4640
4681
  for (let i = 0; i < args.length; i++) {
4641
4682
  const arg = args[i];
4642
- if (!descr.args[argsToFocus[i].index].acceptErrors && !isMatrix(arg) && isEvaluationError(arg?.value)) return arg;
4683
+ if (!currentArgDefinitions[i].acceptErrors && !isMatrix(arg) && isEvaluationError(arg?.value)) return arg;
4643
4684
  }
4644
4685
  try {
4645
4686
  return computeFunctionToObject.apply(this, args);
@@ -4703,6 +4744,9 @@ var FunctionRegistry = class extends Registry {
4703
4744
  }
4704
4745
  };
4705
4746
  const functionRegistry = new FunctionRegistry();
4747
+ /** Formulas using one of these functions are never squished: they are always exported
4748
+ * as a full formula string, and they don't become the base formula of the next cells. */
4749
+ const NonSquishableFunctionRegistry = new Registry();
4706
4750
 
4707
4751
  //#endregion
4708
4752
  //#region src/types/locale.ts
@@ -8968,6 +9012,25 @@ const funnelTooltipPositioner = function(elements) {
8968
9012
  };
8969
9013
  };
8970
9014
 
9015
+ //#endregion
9016
+ //#region src/components/figures/chart/chartJs/chartjs_geo_projection_plugin.ts
9017
+ /**
9018
+ * ChartJS plugin to apply custom changes to a d3 projection.
9019
+ *
9020
+ * We pass the projection as a string instead of a customized d3 object so
9021
+ * Chart.js creates a fresh projection instance. Custom changes (e.g. rotation)
9022
+ * are then applied in `beforeUpdate`.
9023
+ *
9024
+ * This is important because Chart.js mutates the projection instance at runtime,
9025
+ * and a customize d3 projection object would not be copied during deepCopy.
9026
+ */
9027
+ const geoProjectionPlugin = {
9028
+ id: "geoProjection",
9029
+ beforeUpdate(chart) {
9030
+ if (chart.options?.scales?.projection?.projection === "conicConformal") chart.scales?.projection?.projection?.rotate([100, 0]);
9031
+ }
9032
+ };
9033
+
8971
9034
  //#endregion
8972
9035
  //#region src/components/figures/chart/chartJs/chartjs_minor_grid_plugin.ts
8973
9036
  const chartMinorGridPlugin = {
@@ -10025,7 +10088,7 @@ function getGeoChartScales(definition, args) {
10025
10088
  const format = axisFormats?.y || axisFormats?.y1;
10026
10089
  return {
10027
10090
  projection: {
10028
- projection: getGeoChartProjection(region?.defaultProjection || "mercator"),
10091
+ projection: region?.defaultProjection || "mercator",
10029
10092
  axis: "x"
10030
10093
  },
10031
10094
  color: {
@@ -10077,10 +10140,6 @@ function getFunnelChartScales(definition, args) {
10077
10140
  }
10078
10141
  };
10079
10142
  }
10080
- function getGeoChartProjection(projection) {
10081
- if (projection === "conicConformal") return globalThis.ChartGeo.geoConicConformal().rotate([100, 0]);
10082
- return projection;
10083
- }
10084
10143
  function getChartAxisTitleRuntime(design) {
10085
10144
  if (design?.title?.text) {
10086
10145
  const { text, color, align, italic, bold } = design.title;
@@ -10965,6 +11024,10 @@ chartJsExtensionRegistry.add("chartBackgroundPlugin", {
10965
11024
  register: (Chart) => Chart.register(chartBackgroundPlugin),
10966
11025
  unregister: (Chart) => Chart.unregister(chartBackgroundPlugin)
10967
11026
  });
11027
+ chartJsExtensionRegistry.add("geoProjectionPlugin", {
11028
+ register: (Chart) => Chart.register(geoProjectionPlugin),
11029
+ unregister: (Chart) => Chart.unregister(geoProjectionPlugin)
11030
+ });
10968
11031
  var ChartJsComponent = class extends _odoo_owl.Component {
10969
11032
  static template = "o-spreadsheet-ChartJsComponent";
10970
11033
  static props = {
@@ -18292,7 +18355,7 @@ var FigureComponent = class extends _odoo_owl.Component {
18292
18355
  case "ArrowLeft":
18293
18356
  case "ArrowRight":
18294
18357
  case "ArrowUp":
18295
- const { col, row, offset } = this.postionInBoundary(this.props.figureUI, ev.key);
18358
+ const { col, row, offset } = this.postionInBoundary(this.env.model.getters.getActiveSheetId(), this.props.figureUI, ev.key);
18296
18359
  this.env.model.dispatch("UPDATE_FIGURE", {
18297
18360
  sheetId: this.env.model.getters.getActiveSheetId(),
18298
18361
  figureId: this.props.figureUI.id,
@@ -18316,34 +18379,52 @@ var FigureComponent = class extends _odoo_owl.Component {
18316
18379
  break;
18317
18380
  }
18318
18381
  }
18319
- postionInBoundary(position, key) {
18320
- const sheetId = this.env.model.getters.getActiveSheetId();
18321
- let { col, row, offset } = position;
18382
+ postionInBoundary(sheetId, figure, key) {
18383
+ let { col, row, offset } = figure;
18322
18384
  offset = { ...offset };
18385
+ const maxAnchor = this.env.model.getters.getMaxAnchorOffset(sheetId, figure.height, figure.width);
18323
18386
  switch (key) {
18324
18387
  case "ArrowUp":
18325
18388
  if (offset.y === 0) {
18326
18389
  row--;
18390
+ while (row > 0 && this.env.model.getters.isRowHiddenByUser(sheetId, row)) row--;
18327
18391
  offset.y = this.env.model.getters.getRowSize(sheetId, row) - 1;
18328
18392
  } else offset.y--;
18329
18393
  break;
18330
18394
  case "ArrowLeft":
18331
18395
  if (offset.x === 0) {
18332
18396
  col--;
18397
+ while (col > 0 && this.env.model.getters.isColHiddenByUser(sheetId, col)) col--;
18333
18398
  offset.x = this.env.model.getters.getColSize(sheetId, col) - 1;
18334
18399
  } else offset.x--;
18335
18400
  break;
18336
18401
  case "ArrowDown":
18337
18402
  if (offset.y === this.env.model.getters.getRowSize(sheetId, row)) {
18338
18403
  row++;
18404
+ while (row <= maxAnchor.row && this.env.model.getters.isRowHiddenByUser(sheetId, row)) row++;
18339
18405
  offset.y = 0;
18340
18406
  } else offset.y++;
18341
18407
  break;
18342
18408
  case "ArrowRight": if (offset.x === this.env.model.getters.getColSize(sheetId, row)) {
18343
18409
  col++;
18410
+ while (col <= maxAnchor.col && this.env.model.getters.isColHiddenByUser(sheetId, col)) col++;
18344
18411
  offset.x = 0;
18345
18412
  } else offset.x++;
18346
18413
  }
18414
+ if (col < 0) {
18415
+ col = 0;
18416
+ offset.x = 0;
18417
+ } else if (col > maxAnchor.col || col === maxAnchor.col && offset.x > maxAnchor.offset.x) {
18418
+ col = maxAnchor.col;
18419
+ offset.x = maxAnchor.offset.x;
18420
+ }
18421
+ if (row < 0) {
18422
+ row = 0;
18423
+ offset.y = 0;
18424
+ } else if (row > maxAnchor.row || row === maxAnchor.row && offset.y > maxAnchor.offset.y) {
18425
+ row = maxAnchor.row;
18426
+ offset.y = maxAnchor.offset.y;
18427
+ }
18347
18428
  return {
18348
18429
  col,
18349
18430
  row,
@@ -44683,7 +44764,13 @@ var CellComposerStore = class extends AbstractComposerStore {
44683
44764
  const cell = this.getters.getEvaluatedCell(position);
44684
44765
  if (cell.link && !isFormula(content)) content = markdownLink(content, cell.link.url);
44685
44766
  const currentFormat = this.getters.getCell(position)?.format;
44686
- const afterFormat = currentFormat === "@" || currentFormat && isDateTimeFormat(currentFormat) ? void 0 : detectDateFormat(content, this.getters.getLocale());
44767
+ const isCurrentFormatDate = currentFormat && isDateTimeFormat(currentFormat);
44768
+ const contentDateFormat = detectDateFormat(content, this.getters.getLocale());
44769
+ let afterFormat;
44770
+ if (currentFormat !== "@") {
44771
+ if (contentDateFormat && !isCurrentFormatDate) afterFormat = contentDateFormat;
44772
+ else if (isCurrentFormatDate && isNumber(content, this.getters.getLocale())) afterFormat = "";
44773
+ }
44687
44774
  this.addHeadersForSpreadingFormula(content);
44688
44775
  result = this.model.dispatch("UPDATE_CELL", {
44689
44776
  ...this.currentEditedCell,
@@ -49780,7 +49867,8 @@ var Squisher = class {
49780
49867
  * The result of this method is:
49781
49868
  * - if the cell is not a formula, returns the content as is and resets the base formula
49782
49869
  * - if the cell is a formula:
49783
- * - if there is no previous formula, or the normalized formula is different from the previous one, resets the base formula to this one and returns the full formula string
49870
+ * - if there is no previous formula, or the normalized formula is different from the previous one or it contains blacklisted functions (see NonSquishableFunctionRegistry),
49871
+ * resets the base formula to this one and returns the full formula string
49784
49872
  * - else, compares the literal values and range dependencies to the previous formula, and for each parameter:
49785
49873
  * - for numbers: returns a relative change (+N or -N) if possible, else the full number or "=" if unchanged
49786
49874
  * - for strings: returns the full string if changed, else "="
@@ -49788,6 +49876,10 @@ var Squisher = class {
49788
49876
  * */
49789
49877
  squish(cell, forSheetId) {
49790
49878
  if (cell.isFormula) {
49879
+ if (NonSquishableFunctionRegistry.getAll().some((functionName) => cell.compiledFormula.usesSymbol(functionName))) {
49880
+ this.resetBaseFormula();
49881
+ return cell.compiledFormula.toFormulaString(this.getters, this.rangeToStringOptions);
49882
+ }
49791
49883
  let numbers = [];
49792
49884
  let strings = [];
49793
49885
  let references = [];
@@ -61153,7 +61245,7 @@ var Session = class extends EventBus {
61153
61245
  }
61154
61246
  message = {
61155
61247
  ...message,
61156
- commands: this.commandSquisher.squish(revision.commands)
61248
+ commands: revision.commands
61157
61249
  };
61158
61250
  }
61159
61251
  if (this.isReplayingInitialRevisions) throw new Error(`Trying to send a new revision while replaying initial revision. This can lead to endless dispatches every time the spreadsheet is open.
@@ -66397,6 +66489,10 @@ function inverseCreateFigure(cmd) {
66397
66489
  }
66398
66490
  function inverseCreateChart(cmd) {
66399
66491
  return [{
66492
+ type: "DELETE_CHART",
66493
+ chartId: cmd.chartId,
66494
+ sheetId: cmd.sheetId
66495
+ }, {
66400
66496
  type: "DELETE_FIGURE",
66401
66497
  figureId: cmd.figureId,
66402
66498
  sheetId: cmd.sheetId
@@ -84608,7 +84704,8 @@ const registries = {
84608
84704
  supportedPivotPositionalFormulaRegistry,
84609
84705
  pivotToFunctionValueRegistry,
84610
84706
  migrationStepRegistry,
84611
- chartJsExtensionRegistry
84707
+ chartJsExtensionRegistry,
84708
+ NonSquishableFunctionRegistry
84612
84709
  };
84613
84710
  const helpers = {
84614
84711
  arg,
@@ -84861,6 +84958,6 @@ exports.stores = stores;
84861
84958
  exports.tokenColors = tokenColors;
84862
84959
  exports.tokenize = tokenize;
84863
84960
 
84864
- __info__.version = "19.3.14";
84865
- __info__.date = "2026-07-30T06:55:43.749Z";
84866
- __info__.hash = "3dd5632";
84961
+ __info__.version = "19.3.16";
84962
+ __info__.date = "2026-08-12T09:15:31.627Z";
84963
+ __info__.hash = "464be42";
@@ -2,9 +2,9 @@
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 19.3.14
6
- * @date 2026-07-30T06:55:45.398Z
7
- * @hash 3dd5632
5
+ * @version 19.3.16
6
+ * @date 2026-08-12T09:15:33.329Z
7
+ * @hash 464be42
8
8
  */
9
9
  :root {
10
10
  --os-gray-100: light-dark(#f9fafb, #1b1d26);
@@ -2,9 +2,9 @@
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 19.3.14
6
- * @date 2026-07-30T06:55:43.749Z
7
- * @hash 3dd5632
5
+ * @version 19.3.16
6
+ * @date 2026-08-12T09:15:31.627Z
7
+ * @hash 464be42
8
8
  */
9
9
 
10
10
  import { App, Component, blockDom, markRaw, onMounted, onPatched, onWillPatch, onWillStart, onWillUnmount, onWillUpdateProps, status, toRaw, useChildSubEnv, useComponent, useEffect, useEnv, useExternalListener, useRef, useState, useSubEnv, whenReady, xml } from "@odoo/owl";
@@ -4258,19 +4258,58 @@ function applyVectorization(formula, args, acceptToVectorize = void 0) {
4258
4258
  }
4259
4259
  }
4260
4260
  if (countVectorizedCol === 1 && countVectorizedRow === 1) return formula(...args);
4261
- const getArgOffset = (i, j) => args.map((arg, index) => {
4262
- switch (vectorArgsType?.[index]) {
4263
- case "matrix": return arg[i][j];
4264
- case "horizontal": return arg[i][0];
4265
- case "vertical": return arg[0][j];
4266
- case void 0: return arg;
4261
+ const argsBuffer = new Array(args.length);
4262
+ const argGetters = [];
4263
+ const vectorizedIndices = [];
4264
+ for (let k = 0; k < args.length; k++) {
4265
+ const arg = args[k];
4266
+ switch (vectorArgsType?.[k]) {
4267
+ case "matrix":
4268
+ argGetters.push((i, j) => arg[i][j]);
4269
+ vectorizedIndices.push(k);
4270
+ break;
4271
+ case "horizontal":
4272
+ argGetters.push((i) => arg[i][0]);
4273
+ vectorizedIndices.push(k);
4274
+ break;
4275
+ case "vertical":
4276
+ argGetters.push((_i, j) => arg[0][j]);
4277
+ vectorizedIndices.push(k);
4278
+ break;
4279
+ case void 0:
4280
+ argsBuffer[k] = arg;
4281
+ break;
4267
4282
  }
4268
- });
4269
- return generateMatrix(countVectorizedCol, countVectorizedRow, (col, row) => {
4270
- if (col > vectorizedColLimit - 1 || row > vectorizedRowLimit - 1) return new NotAvailableError(_t("Array arguments to [[FUNCTION_NAME]] are of different size."));
4271
- const singleCellComputeResult = formula(...getArgOffset(col, row));
4272
- return isMatrix(singleCellComputeResult) ? singleCellComputeResult[0][0] : singleCellComputeResult;
4273
- });
4283
+ }
4284
+ const nbVectorized = vectorizedIndices.length;
4285
+ let callFormula;
4286
+ switch (argsBuffer.length) {
4287
+ case 1:
4288
+ callFormula = () => formula(argsBuffer[0]);
4289
+ break;
4290
+ case 2:
4291
+ callFormula = () => formula(argsBuffer[0], argsBuffer[1]);
4292
+ break;
4293
+ case 3:
4294
+ callFormula = () => formula(argsBuffer[0], argsBuffer[1], argsBuffer[2]);
4295
+ break;
4296
+ default: callFormula = () => formula(...argsBuffer);
4297
+ }
4298
+ const result = new Array(countVectorizedCol);
4299
+ for (let col = 0; col < countVectorizedCol; col++) {
4300
+ const column = new Array(countVectorizedRow);
4301
+ result[col] = column;
4302
+ for (let row = 0; row < countVectorizedRow; row++) {
4303
+ if (col > vectorizedColLimit - 1 || row > vectorizedRowLimit - 1) {
4304
+ column[row] = new NotAvailableError(_t("Array arguments to [[FUNCTION_NAME]] are of different size."));
4305
+ continue;
4306
+ }
4307
+ for (let k = 0; k < nbVectorized; k++) argsBuffer[vectorizedIndices[k]] = argGetters[k](col, row);
4308
+ const singleCellComputeResult = callFormula();
4309
+ column[row] = isMatrix(singleCellComputeResult) ? singleCellComputeResult[0][0] : singleCellComputeResult;
4310
+ }
4311
+ }
4312
+ return result;
4274
4313
  }
4275
4314
  /**
4276
4315
  * This function allows to visit arguments and stop the visit if necessary.
@@ -4617,12 +4656,15 @@ function emptyDataErrorMessage(argName) {
4617
4656
  //#endregion
4618
4657
  //#region src/functions/create_compute_function.ts
4619
4658
  function createComputeFunction(descr) {
4659
+ let currentArgDefinitions = [];
4620
4660
  function vectorizedCompute(...args) {
4621
4661
  const acceptToVectorize = [];
4662
+ currentArgDefinitions = new Array(args.length);
4622
4663
  const argsToFocus = argTargeting(descr, args.length);
4623
4664
  for (let i = 0; i < args.length; i++) {
4624
4665
  const argIndex = argsToFocus[i].index;
4625
4666
  const argDefinition = descr.args[argIndex];
4667
+ currentArgDefinitions[i] = argDefinition;
4626
4668
  const arg = args[i];
4627
4669
  if (!isMatrix(arg) && argDefinition.acceptMatrixOnly) throw new BadExpressionError(_t("Function %s expects the parameter '%s' to be reference to a cell or range.", descr.name, (i + 1).toString()));
4628
4670
  acceptToVectorize.push(!argDefinition.acceptMatrix);
@@ -4635,10 +4677,9 @@ function createComputeFunction(descr) {
4635
4677
  return result;
4636
4678
  }
4637
4679
  function errorHandlingCompute(...args) {
4638
- const argsToFocus = argTargeting(descr, args.length);
4639
4680
  for (let i = 0; i < args.length; i++) {
4640
4681
  const arg = args[i];
4641
- if (!descr.args[argsToFocus[i].index].acceptErrors && !isMatrix(arg) && isEvaluationError(arg?.value)) return arg;
4682
+ if (!currentArgDefinitions[i].acceptErrors && !isMatrix(arg) && isEvaluationError(arg?.value)) return arg;
4642
4683
  }
4643
4684
  try {
4644
4685
  return computeFunctionToObject.apply(this, args);
@@ -4702,6 +4743,9 @@ var FunctionRegistry = class extends Registry {
4702
4743
  }
4703
4744
  };
4704
4745
  const functionRegistry = new FunctionRegistry();
4746
+ /** Formulas using one of these functions are never squished: they are always exported
4747
+ * as a full formula string, and they don't become the base formula of the next cells. */
4748
+ const NonSquishableFunctionRegistry = new Registry();
4705
4749
 
4706
4750
  //#endregion
4707
4751
  //#region src/types/locale.ts
@@ -8967,6 +9011,25 @@ const funnelTooltipPositioner = function(elements) {
8967
9011
  };
8968
9012
  };
8969
9013
 
9014
+ //#endregion
9015
+ //#region src/components/figures/chart/chartJs/chartjs_geo_projection_plugin.ts
9016
+ /**
9017
+ * ChartJS plugin to apply custom changes to a d3 projection.
9018
+ *
9019
+ * We pass the projection as a string instead of a customized d3 object so
9020
+ * Chart.js creates a fresh projection instance. Custom changes (e.g. rotation)
9021
+ * are then applied in `beforeUpdate`.
9022
+ *
9023
+ * This is important because Chart.js mutates the projection instance at runtime,
9024
+ * and a customize d3 projection object would not be copied during deepCopy.
9025
+ */
9026
+ const geoProjectionPlugin = {
9027
+ id: "geoProjection",
9028
+ beforeUpdate(chart) {
9029
+ if (chart.options?.scales?.projection?.projection === "conicConformal") chart.scales?.projection?.projection?.rotate([100, 0]);
9030
+ }
9031
+ };
9032
+
8970
9033
  //#endregion
8971
9034
  //#region src/components/figures/chart/chartJs/chartjs_minor_grid_plugin.ts
8972
9035
  const chartMinorGridPlugin = {
@@ -10024,7 +10087,7 @@ function getGeoChartScales(definition, args) {
10024
10087
  const format = axisFormats?.y || axisFormats?.y1;
10025
10088
  return {
10026
10089
  projection: {
10027
- projection: getGeoChartProjection(region?.defaultProjection || "mercator"),
10090
+ projection: region?.defaultProjection || "mercator",
10028
10091
  axis: "x"
10029
10092
  },
10030
10093
  color: {
@@ -10076,10 +10139,6 @@ function getFunnelChartScales(definition, args) {
10076
10139
  }
10077
10140
  };
10078
10141
  }
10079
- function getGeoChartProjection(projection) {
10080
- if (projection === "conicConformal") return globalThis.ChartGeo.geoConicConformal().rotate([100, 0]);
10081
- return projection;
10082
- }
10083
10142
  function getChartAxisTitleRuntime(design) {
10084
10143
  if (design?.title?.text) {
10085
10144
  const { text, color, align, italic, bold } = design.title;
@@ -10964,6 +11023,10 @@ chartJsExtensionRegistry.add("chartBackgroundPlugin", {
10964
11023
  register: (Chart) => Chart.register(chartBackgroundPlugin),
10965
11024
  unregister: (Chart) => Chart.unregister(chartBackgroundPlugin)
10966
11025
  });
11026
+ chartJsExtensionRegistry.add("geoProjectionPlugin", {
11027
+ register: (Chart) => Chart.register(geoProjectionPlugin),
11028
+ unregister: (Chart) => Chart.unregister(geoProjectionPlugin)
11029
+ });
10967
11030
  var ChartJsComponent = class extends Component {
10968
11031
  static template = "o-spreadsheet-ChartJsComponent";
10969
11032
  static props = {
@@ -18291,7 +18354,7 @@ var FigureComponent = class extends Component {
18291
18354
  case "ArrowLeft":
18292
18355
  case "ArrowRight":
18293
18356
  case "ArrowUp":
18294
- const { col, row, offset } = this.postionInBoundary(this.props.figureUI, ev.key);
18357
+ const { col, row, offset } = this.postionInBoundary(this.env.model.getters.getActiveSheetId(), this.props.figureUI, ev.key);
18295
18358
  this.env.model.dispatch("UPDATE_FIGURE", {
18296
18359
  sheetId: this.env.model.getters.getActiveSheetId(),
18297
18360
  figureId: this.props.figureUI.id,
@@ -18315,34 +18378,52 @@ var FigureComponent = class extends Component {
18315
18378
  break;
18316
18379
  }
18317
18380
  }
18318
- postionInBoundary(position, key) {
18319
- const sheetId = this.env.model.getters.getActiveSheetId();
18320
- let { col, row, offset } = position;
18381
+ postionInBoundary(sheetId, figure, key) {
18382
+ let { col, row, offset } = figure;
18321
18383
  offset = { ...offset };
18384
+ const maxAnchor = this.env.model.getters.getMaxAnchorOffset(sheetId, figure.height, figure.width);
18322
18385
  switch (key) {
18323
18386
  case "ArrowUp":
18324
18387
  if (offset.y === 0) {
18325
18388
  row--;
18389
+ while (row > 0 && this.env.model.getters.isRowHiddenByUser(sheetId, row)) row--;
18326
18390
  offset.y = this.env.model.getters.getRowSize(sheetId, row) - 1;
18327
18391
  } else offset.y--;
18328
18392
  break;
18329
18393
  case "ArrowLeft":
18330
18394
  if (offset.x === 0) {
18331
18395
  col--;
18396
+ while (col > 0 && this.env.model.getters.isColHiddenByUser(sheetId, col)) col--;
18332
18397
  offset.x = this.env.model.getters.getColSize(sheetId, col) - 1;
18333
18398
  } else offset.x--;
18334
18399
  break;
18335
18400
  case "ArrowDown":
18336
18401
  if (offset.y === this.env.model.getters.getRowSize(sheetId, row)) {
18337
18402
  row++;
18403
+ while (row <= maxAnchor.row && this.env.model.getters.isRowHiddenByUser(sheetId, row)) row++;
18338
18404
  offset.y = 0;
18339
18405
  } else offset.y++;
18340
18406
  break;
18341
18407
  case "ArrowRight": if (offset.x === this.env.model.getters.getColSize(sheetId, row)) {
18342
18408
  col++;
18409
+ while (col <= maxAnchor.col && this.env.model.getters.isColHiddenByUser(sheetId, col)) col++;
18343
18410
  offset.x = 0;
18344
18411
  } else offset.x++;
18345
18412
  }
18413
+ if (col < 0) {
18414
+ col = 0;
18415
+ offset.x = 0;
18416
+ } else if (col > maxAnchor.col || col === maxAnchor.col && offset.x > maxAnchor.offset.x) {
18417
+ col = maxAnchor.col;
18418
+ offset.x = maxAnchor.offset.x;
18419
+ }
18420
+ if (row < 0) {
18421
+ row = 0;
18422
+ offset.y = 0;
18423
+ } else if (row > maxAnchor.row || row === maxAnchor.row && offset.y > maxAnchor.offset.y) {
18424
+ row = maxAnchor.row;
18425
+ offset.y = maxAnchor.offset.y;
18426
+ }
18346
18427
  return {
18347
18428
  col,
18348
18429
  row,
@@ -44682,7 +44763,13 @@ var CellComposerStore = class extends AbstractComposerStore {
44682
44763
  const cell = this.getters.getEvaluatedCell(position);
44683
44764
  if (cell.link && !isFormula(content)) content = markdownLink(content, cell.link.url);
44684
44765
  const currentFormat = this.getters.getCell(position)?.format;
44685
- const afterFormat = currentFormat === "@" || currentFormat && isDateTimeFormat(currentFormat) ? void 0 : detectDateFormat(content, this.getters.getLocale());
44766
+ const isCurrentFormatDate = currentFormat && isDateTimeFormat(currentFormat);
44767
+ const contentDateFormat = detectDateFormat(content, this.getters.getLocale());
44768
+ let afterFormat;
44769
+ if (currentFormat !== "@") {
44770
+ if (contentDateFormat && !isCurrentFormatDate) afterFormat = contentDateFormat;
44771
+ else if (isCurrentFormatDate && isNumber(content, this.getters.getLocale())) afterFormat = "";
44772
+ }
44686
44773
  this.addHeadersForSpreadingFormula(content);
44687
44774
  result = this.model.dispatch("UPDATE_CELL", {
44688
44775
  ...this.currentEditedCell,
@@ -49779,7 +49866,8 @@ var Squisher = class {
49779
49866
  * The result of this method is:
49780
49867
  * - if the cell is not a formula, returns the content as is and resets the base formula
49781
49868
  * - if the cell is a formula:
49782
- * - if there is no previous formula, or the normalized formula is different from the previous one, resets the base formula to this one and returns the full formula string
49869
+ * - if there is no previous formula, or the normalized formula is different from the previous one or it contains blacklisted functions (see NonSquishableFunctionRegistry),
49870
+ * resets the base formula to this one and returns the full formula string
49783
49871
  * - else, compares the literal values and range dependencies to the previous formula, and for each parameter:
49784
49872
  * - for numbers: returns a relative change (+N or -N) if possible, else the full number or "=" if unchanged
49785
49873
  * - for strings: returns the full string if changed, else "="
@@ -49787,6 +49875,10 @@ var Squisher = class {
49787
49875
  * */
49788
49876
  squish(cell, forSheetId) {
49789
49877
  if (cell.isFormula) {
49878
+ if (NonSquishableFunctionRegistry.getAll().some((functionName) => cell.compiledFormula.usesSymbol(functionName))) {
49879
+ this.resetBaseFormula();
49880
+ return cell.compiledFormula.toFormulaString(this.getters, this.rangeToStringOptions);
49881
+ }
49790
49882
  let numbers = [];
49791
49883
  let strings = [];
49792
49884
  let references = [];
@@ -60968,7 +61060,7 @@ var Session = class extends EventBus {
60968
61060
  }
60969
61061
  message = {
60970
61062
  ...message,
60971
- commands: this.commandSquisher.squish(revision.commands)
61063
+ commands: revision.commands
60972
61064
  };
60973
61065
  }
60974
61066
  if (this.isReplayingInitialRevisions) throw new Error(`Trying to send a new revision while replaying initial revision. This can lead to endless dispatches every time the spreadsheet is open.
@@ -66212,6 +66304,10 @@ function inverseCreateFigure(cmd) {
66212
66304
  }
66213
66305
  function inverseCreateChart(cmd) {
66214
66306
  return [{
66307
+ type: "DELETE_CHART",
66308
+ chartId: cmd.chartId,
66309
+ sheetId: cmd.sheetId
66310
+ }, {
66215
66311
  type: "DELETE_FIGURE",
66216
66312
  figureId: cmd.figureId,
66217
66313
  sheetId: cmd.sheetId
@@ -84423,7 +84519,8 @@ const registries = {
84423
84519
  supportedPivotPositionalFormulaRegistry,
84424
84520
  pivotToFunctionValueRegistry,
84425
84521
  migrationStepRegistry,
84426
- chartJsExtensionRegistry
84522
+ chartJsExtensionRegistry,
84523
+ NonSquishableFunctionRegistry
84427
84524
  };
84428
84525
  const helpers = {
84429
84526
  arg,
@@ -84618,6 +84715,6 @@ const chartHelpers = {
84618
84715
  //#endregion
84619
84716
  export { AbstractCellClipboardHandler, AbstractChart, AbstractFigureClipboardHandler, CellErrorType, ClientDisconnectedError, CommandResult, CompiledFormula, CorePlugin, CoreViewPlugin, DEFAULT_LOCALE, DEFAULT_LOCALES, DispatchResult, EvaluationError, LocalTransportService, Model, PivotRuntimeDefinition, Registry, Revision, SPREADSHEET_DIMENSIONS, Spreadsheet, SpreadsheetPivotTable, UIPlugin, __info__, addFunction, addRenderingLayer, astToFormula, canExecuteInReadonly, categories, chartHelpers, components, constants, convertAstNodes, coreTypes, createAutocompleteArgumentsProvider, findCellInNewZone, functionCache, getCaretDownSvg, getCaretUpSvg, helpers, hooks, invalidateCFEvaluationCommands, invalidateChartEvaluationCommands, invalidateDependenciesCommands, invalidateEvaluationCommands, isCoreCommand, isSheetDependent, iterateAstNodes, links, load, lockedSheetAllowedCommands, parse, parseTokens, readonlyAllowedCommands, registries, setDefaultSheetViewSize, setTranslationMethod, stores, tokenColors, tokenize };
84620
84717
 
84621
- __info__.version = "19.3.14";
84622
- __info__.date = "2026-07-30T06:55:43.749Z";
84623
- __info__.hash = "3dd5632";
84718
+ __info__.version = "19.3.16";
84719
+ __info__.date = "2026-08-12T09:15:31.627Z";
84720
+ __info__.hash = "464be42";