@odoo/o-spreadsheet 18.1.25 → 18.1.27

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -2,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 18.1.25
6
- * @date 2025-06-12T09:49:08.707Z
7
- * @hash a232524
5
+ * @version 18.1.27
6
+ * @date 2025-06-23T15:04:51.792Z
7
+ * @hash b25bcc7
8
8
  */
9
9
 
10
10
  (function (exports, owl) {
@@ -6817,6 +6817,63 @@
6817
6817
  data: spreadsheetContent,
6818
6818
  };
6819
6819
  }
6820
+ /**
6821
+ * Applies each clipboard handler to paste its corresponding data into the target.
6822
+ */
6823
+ const applyClipboardHandlersPaste = (handlers, copiedData, target, options) => {
6824
+ handlers.forEach(({ handlerName, handler }) => {
6825
+ const data = copiedData[handlerName];
6826
+ if (data) {
6827
+ handler.paste(target, data, options);
6828
+ }
6829
+ });
6830
+ };
6831
+ /**
6832
+ * Returns the paste target based on clipboard handlers.
6833
+ * Also includes the full affected zone and the list of pasted zones for selection.
6834
+ */
6835
+ function getPasteTargetFromHandlers(sheetId, zones, copiedData, handlers, options) {
6836
+ let zone = undefined;
6837
+ let selectedZones = [];
6838
+ let target = {
6839
+ sheetId,
6840
+ zones,
6841
+ };
6842
+ for (const { handlerName, handler } of handlers) {
6843
+ const handlerData = copiedData[handlerName];
6844
+ if (!handlerData) {
6845
+ continue;
6846
+ }
6847
+ const currentTarget = handler.getPasteTarget(sheetId, zones, handlerData, options);
6848
+ if (currentTarget.figureId) {
6849
+ target.figureId = currentTarget.figureId;
6850
+ }
6851
+ for (const targetZone of currentTarget.zones) {
6852
+ selectedZones.push(targetZone);
6853
+ if (zone === undefined) {
6854
+ zone = targetZone;
6855
+ continue;
6856
+ }
6857
+ zone = union(zone, targetZone);
6858
+ }
6859
+ }
6860
+ return {
6861
+ target,
6862
+ zone,
6863
+ selectedZones,
6864
+ };
6865
+ }
6866
+ /**
6867
+ * Updates the selection after a paste operation.
6868
+ */
6869
+ const selectPastedZone = (selection, sourceZones, pastedZones) => {
6870
+ const anchorCell = {
6871
+ col: sourceZones[0].left,
6872
+ row: sourceZones[0].top,
6873
+ };
6874
+ selection.getBackToDefault();
6875
+ selection.selectZone({ cell: anchorCell, zone: union(...pastedZones) }, { scrollIntoView: false });
6876
+ };
6820
6877
 
6821
6878
  class ClipboardHandler {
6822
6879
  getters;
@@ -10203,6 +10260,7 @@ stores.inject(MyMetaStore, storeInstance);
10203
10260
  if (isTrendLineAxis(dataset.xAxisID) || dataset.hidden) {
10204
10261
  continue;
10205
10262
  }
10263
+ const yAxisScale = chart.scales[dataset.yAxisID];
10206
10264
  for (let i = 0; i < dataset._parsed.length; i++) {
10207
10265
  const parsedValue = dataset._parsed[i];
10208
10266
  const value = Number(chart.config.type === "radar" ? parsedValue.r : parsedValue.y);
@@ -10213,10 +10271,18 @@ stores.inject(MyMetaStore, storeInstance);
10213
10271
  const xPosition = point.x;
10214
10272
  let yPosition = 0;
10215
10273
  if (chart.config.type === "line" || chart.config.type === "radar") {
10216
- yPosition = point.y - 10;
10274
+ yPosition = value < 0 ? point.y + 10 : point.y - 10;
10217
10275
  }
10218
10276
  else {
10219
- yPosition = value < 0 ? point.y - point.height / 2 : point.y + point.height / 2;
10277
+ const yZeroLine = yAxisScale.getPixelForValue(0);
10278
+ const distanceFromAxisOrigin = Math.abs(yZeroLine - point.y);
10279
+ const textHeight = 12; // ChartJS default text height
10280
+ if (distanceFromAxisOrigin < textHeight) {
10281
+ yPosition = value < 0 ? yZeroLine + textHeight / 2 : yZeroLine - textHeight / 2;
10282
+ }
10283
+ else {
10284
+ yPosition = value < 0 ? point.y - point.height / 2 : point.y + point.height / 2;
10285
+ }
10220
10286
  }
10221
10287
  yPosition = Math.min(yPosition, yMax);
10222
10288
  yPosition = Math.max(yPosition, yMin);
@@ -10226,7 +10292,7 @@ stores.inject(MyMetaStore, storeInstance);
10226
10292
  }
10227
10293
  for (const otherPosition of textsPositions[xPosition] || []) {
10228
10294
  if (Math.abs(otherPosition - yPosition) < 13) {
10229
- yPosition = otherPosition - 13;
10295
+ yPosition = value < 0 ? otherPosition + 13 : otherPosition - 13;
10230
10296
  }
10231
10297
  }
10232
10298
  textsPositions[xPosition].push(yPosition);
@@ -10245,6 +10311,8 @@ stores.inject(MyMetaStore, storeInstance);
10245
10311
  if (isTrendLineAxis(dataset.xAxisID)) {
10246
10312
  return; // ignore trend lines
10247
10313
  }
10314
+ const xAxisScale = chart.scales[dataset.xAxisID];
10315
+ const xZeroLine = xAxisScale.getPixelForValue(0);
10248
10316
  for (let i = 0; i < dataset._parsed.length; i++) {
10249
10317
  const value = Number(dataset._parsed[i].x);
10250
10318
  if (isNaN(value)) {
@@ -10253,17 +10321,27 @@ stores.inject(MyMetaStore, storeInstance);
10253
10321
  const displayValue = options.callback(value, dataset, i);
10254
10322
  const point = dataset.data[i];
10255
10323
  const yPosition = point.y;
10256
- let xPosition = value < 0 ? point.x + point.width / 2 : point.x - point.width / 2;
10257
- xPosition = Math.min(xPosition, xMax);
10258
- xPosition = Math.max(xPosition, xMin);
10324
+ const textWidth = computeTextWidth(ctx, displayValue, { fontSize: 12 }, "px");
10325
+ const distanceFromAxisOrigin = Math.abs(point.x - xZeroLine);
10326
+ const PADDING = 3;
10327
+ let xPosition;
10328
+ if (distanceFromAxisOrigin < textWidth) {
10329
+ xPosition =
10330
+ value < 0 ? xZeroLine - textWidth / 2 - PADDING : xZeroLine + textWidth / 2 + PADDING;
10331
+ }
10332
+ else {
10333
+ xPosition = value < 0 ? point.x + point.width / 2 : point.x - point.width / 2;
10334
+ xPosition = Math.min(xPosition, xMax);
10335
+ xPosition = Math.max(xPosition, xMin);
10336
+ }
10259
10337
  // Avoid overlapping texts with same Y
10260
10338
  if (!textsPositions[yPosition]) {
10261
10339
  textsPositions[yPosition] = [];
10262
10340
  }
10263
- const textWidth = computeTextWidth(ctx, displayValue, { fontSize: 12 }, "px");
10264
10341
  for (const otherPosition of textsPositions[yPosition]) {
10265
10342
  if (Math.abs(otherPosition - xPosition) < textWidth) {
10266
- xPosition = otherPosition + textWidth + 3;
10343
+ xPosition =
10344
+ value < 0 ? otherPosition - textWidth - PADDING : otherPosition + textWidth + PADDING;
10267
10345
  }
10268
10346
  }
10269
10347
  textsPositions[yPosition].push(xPosition);
@@ -30079,7 +30157,9 @@ stores.inject(MyMetaStore, storeInstance);
30079
30157
  background: definition.background,
30080
30158
  callback: (value, dataset) => {
30081
30159
  value = Math.abs(Number(value));
30082
- return formatChartDatasetValue(axisFormats, locale)(value, dataset.xAxisID || "x");
30160
+ return value === 0
30161
+ ? ""
30162
+ : formatChartDatasetValue(axisFormats, locale)(value, dataset.xAxisID || "x");
30083
30163
  },
30084
30164
  };
30085
30165
  }
@@ -34579,6 +34659,10 @@ stores.inject(MyMetaStore, storeInstance);
34579
34659
  });
34580
34660
  };
34581
34661
  const CAN_REMOVE_COLUMNS_ROWS = (dimension, env) => {
34662
+ if ((dimension === "COL" && env.model.getters.getActiveRows().size > 0) ||
34663
+ (dimension === "ROW" && env.model.getters.getActiveCols().size > 0)) {
34664
+ return false;
34665
+ }
34582
34666
  const sheetId = env.model.getters.getActiveSheetId();
34583
34667
  const selectedElements = env.model.getters.getElementsFromSelection(dimension);
34584
34668
  const includesAllVisibleHeaders = env.model.getters.checkElementsIncludeAllVisibleHeaders(sheetId, dimension, selectedElements);
@@ -37558,11 +37642,11 @@ stores.inject(MyMetaStore, storeInstance);
37558
37642
  * transformation function given
37559
37643
  */
37560
37644
  addTransformation(executed, toTransforms, fn) {
37561
- for (let toTransform of toTransforms) {
37562
- if (!this.content[toTransform]) {
37563
- this.content[toTransform] = new Map();
37564
- }
37565
- this.content[toTransform].set(executed, fn);
37645
+ if (!this.content[executed]) {
37646
+ this.content[executed] = new Map();
37647
+ }
37648
+ for (const toTransform of toTransforms) {
37649
+ this.content[executed].set(toTransform, fn);
37566
37650
  }
37567
37651
  return this;
37568
37652
  }
@@ -37571,7 +37655,7 @@ stores.inject(MyMetaStore, storeInstance);
37571
37655
  * that the executed command happened.
37572
37656
  */
37573
37657
  getTransformation(toTransform, executed) {
37574
- return this.content[toTransform] && this.content[toTransform].get(executed);
37658
+ return this.content[executed] && this.content[executed].get(toTransform);
37575
37659
  }
37576
37660
  }
37577
37661
  const otRegistry = new OTRegistry();
@@ -46737,7 +46821,7 @@ stores.inject(MyMetaStore, storeInstance);
46737
46821
  }
46738
46822
  getTypeFromZone(sheetId, zone) {
46739
46823
  const cells = this.getters.getEvaluatedCellsInZone(sheetId, zone);
46740
- const nonEmptyCells = cells.filter((cell) => cell.type !== CellValueType.empty);
46824
+ const nonEmptyCells = cells.filter((cell) => !(cell.type === CellValueType.empty || cell.value === ""));
46741
46825
  if (nonEmptyCells.length === 0) {
46742
46826
  return "integer";
46743
46827
  }
@@ -50294,15 +50378,16 @@ stores.inject(MyMetaStore, storeInstance);
50294
50378
  }
50295
50379
  }
50296
50380
 
50381
+ const PAINT_FORMAT_HANDLER_KEYS = [
50382
+ "cell",
50383
+ "border",
50384
+ "table",
50385
+ "conditionalFormat",
50386
+ "merge",
50387
+ ];
50297
50388
  class PaintFormatStore extends SpreadsheetStore {
50298
50389
  mutators = ["activate", "cancel", "pasteFormat"];
50299
50390
  highlightStore = this.get(HighlightStore);
50300
- clipboardHandlers = [
50301
- new CellClipboardHandler(this.getters, this.model.dispatch),
50302
- new BorderClipboardHandler(this.getters, this.model.dispatch),
50303
- new TableClipboardHandler(this.getters, this.model.dispatch),
50304
- new ConditionalFormatClipboardHandler(this.getters, this.model.dispatch),
50305
- ];
50306
50391
  status = "inactive";
50307
50392
  copiedData;
50308
50393
  constructor(get) {
@@ -50333,24 +50418,38 @@ stores.inject(MyMetaStore, storeInstance);
50333
50418
  get isActive() {
50334
50419
  return this.status !== "inactive";
50335
50420
  }
50421
+ get clipboardHandlers() {
50422
+ return PAINT_FORMAT_HANDLER_KEYS.map((handlerName) => {
50423
+ const HandlerClass = clipboardHandlersRegistries.cellHandlers.get(handlerName);
50424
+ return {
50425
+ handlerName,
50426
+ handler: new HandlerClass(this.getters, this.model.dispatch),
50427
+ };
50428
+ });
50429
+ }
50336
50430
  copyFormats() {
50337
50431
  const sheetId = this.getters.getActiveSheetId();
50338
50432
  const zones = this.getters.getSelectedZones();
50339
- const copiedData = {};
50340
- for (const handler of this.clipboardHandlers) {
50341
- Object.assign(copiedData, handler.copy(getClipboardDataPositions(sheetId, zones)));
50433
+ const copiedData = { zones, sheetId };
50434
+ for (const { handlerName, handler } of this.clipboardHandlers) {
50435
+ const handlerResult = handler.copy(getClipboardDataPositions(sheetId, zones));
50436
+ if (handlerResult !== undefined) {
50437
+ copiedData[handlerName] = handlerResult;
50438
+ }
50342
50439
  }
50343
50440
  return copiedData;
50344
50441
  }
50345
50442
  paintFormat(sheetId, target) {
50346
- if (this.copiedData) {
50347
- for (const handler of this.clipboardHandlers) {
50348
- handler.paste({ zones: target, sheetId }, this.copiedData, {
50349
- isCutOperation: false,
50350
- pasteOption: "onlyFormat",
50351
- });
50352
- }
50443
+ if (!this.copiedData) {
50444
+ return;
50353
50445
  }
50446
+ const options = {
50447
+ isCutOperation: false,
50448
+ pasteOption: "onlyFormat",
50449
+ };
50450
+ const { target: pasteTarget, selectedZones } = getPasteTargetFromHandlers(sheetId, target, this.copiedData, this.clipboardHandlers, options);
50451
+ applyClipboardHandlersPaste(this.clipboardHandlers, this.copiedData, pasteTarget, options);
50452
+ selectPastedZone(this.model.selection, target, selectedZones);
50354
50453
  if (this.status === "oneOff") {
50355
50454
  this.cancel();
50356
50455
  }
@@ -55516,7 +55615,7 @@ stores.inject(MyMetaStore, storeInstance);
55516
55615
  else if (newRule.criterion.type === "isValueInList") {
55517
55616
  newRule.criterion.values = Array.from(new Set(newRule.criterion.values));
55518
55617
  }
55519
- const adaptedRules = this.removeRangesFromRules(sheetId, newRule.ranges, rules);
55618
+ const adaptedRules = this.removeRangesFromRules(sheetId, newRule.ranges, rules, newRule.id);
55520
55619
  const ruleIndex = adaptedRules.findIndex((rule) => rule.id === newRule.id);
55521
55620
  if (ruleIndex !== -1) {
55522
55621
  adaptedRules[ruleIndex] = newRule;
@@ -55526,9 +55625,12 @@ stores.inject(MyMetaStore, storeInstance);
55526
55625
  this.history.update("rules", sheetId, [...adaptedRules, newRule]);
55527
55626
  }
55528
55627
  }
55529
- removeRangesFromRules(sheetId, ranges, rules) {
55628
+ removeRangesFromRules(sheetId, ranges, rules, editingRuleId) {
55530
55629
  rules = deepCopy(rules);
55531
55630
  for (const rule of rules) {
55631
+ if (rule.id === editingRuleId) {
55632
+ continue; // Skip the rule being edited to preserve its place in the list
55633
+ }
55532
55634
  rule.ranges = this.getters.recomputeRanges(rule.ranges, ranges);
55533
55635
  }
55534
55636
  return rules.filter((rule) => rule.ranges.length > 0);
@@ -64266,10 +64368,20 @@ stores.inject(MyMetaStore, storeInstance);
64266
64368
  */
64267
64369
  function transformAll(toTransform, executed) {
64268
64370
  let transformedCommands = [...toTransform];
64371
+ const possibleTransformations = new Set(otRegistry.getKeys());
64269
64372
  for (const executedCommand of executed) {
64270
- transformedCommands = transformedCommands
64271
- .map((cmd) => transform(cmd, executedCommand))
64272
- .filter(isDefined);
64373
+ // If the executed command is not in the registry, we skip it
64374
+ // because we know there won't be any transformation impacting the
64375
+ // commands to transform.
64376
+ if (possibleTransformations.has(executedCommand.type)) {
64377
+ transformedCommands = transformedCommands.reduce((acc, cmd) => {
64378
+ const transformed = transform(cmd, executedCommand);
64379
+ if (transformed) {
64380
+ acc.push(transformed);
64381
+ }
64382
+ return acc;
64383
+ }, []);
64384
+ }
64273
64385
  }
64274
64386
  return transformedCommands;
64275
64387
  }
@@ -65964,7 +66076,7 @@ stores.inject(MyMetaStore, storeInstance);
65964
66076
  }
65965
66077
  const position = this.getters.getCellPosition(cell.id);
65966
66078
  const colSize = this.getters.getColSize(sheetId, position.col);
65967
- if (cell.isFormula) {
66079
+ if (cell.isFormula || this.getters.getArrayFormulaSpreadingOn(position)) {
65968
66080
  const content = this.getters.getEvaluatedCell(position).formattedValue;
65969
66081
  const evaluatedSize = getCellContentHeight(this.ctx, content, cell?.style, colSize);
65970
66082
  if (evaluatedSize > evaluatedRowSize && evaluatedSize > DEFAULT_CELL_HEIGHT) {
@@ -67252,49 +67364,17 @@ stores.inject(MyMetaStore, storeInstance);
67252
67364
  if (!copiedData) {
67253
67365
  return;
67254
67366
  }
67255
- let zone = undefined;
67256
- let selectedZones = [];
67257
67367
  const sheetId = this.getters.getActiveSheetId();
67258
- let target = {
67259
- sheetId,
67260
- zones,
67261
- };
67262
67368
  const handlers = this.selectClipboardHandlers(copiedData);
67263
- for (const { handlerName, handler } of handlers) {
67264
- const handlerData = copiedData[handlerName];
67265
- if (!handlerData) {
67266
- continue;
67267
- }
67268
- const currentTarget = handler.getPasteTarget(sheetId, zones, handlerData, options);
67269
- if (currentTarget.figureId) {
67270
- target.figureId = currentTarget.figureId;
67271
- }
67272
- for (const targetZone of currentTarget.zones) {
67273
- selectedZones.push(targetZone);
67274
- if (zone === undefined) {
67275
- zone = targetZone;
67276
- continue;
67277
- }
67278
- zone = union(zone, targetZone);
67279
- }
67280
- }
67369
+ const { target, zone, selectedZones } = getPasteTargetFromHandlers(sheetId, zones, copiedData, handlers, options);
67281
67370
  if (zone !== undefined) {
67282
- this.addMissingDimensions(this.getters.getActiveSheetId(), zone.right - zone.left + 1, zone.bottom - zone.top + 1, zone.left, zone.top);
67371
+ this.addMissingDimensions(sheetId, zone.right - zone.left + 1, zone.bottom - zone.top + 1, zone.left, zone.top);
67283
67372
  }
67284
- handlers.forEach(({ handlerName, handler }) => {
67285
- const handlerData = copiedData[handlerName];
67286
- if (handlerData) {
67287
- handler.paste(target, handlerData, options);
67288
- }
67289
- });
67373
+ applyClipboardHandlersPaste(handlers, copiedData, target, options);
67290
67374
  if (!options?.selectTarget) {
67291
67375
  return;
67292
67376
  }
67293
- const selection = zones[0];
67294
- const col = selection.left;
67295
- const row = selection.top;
67296
- this.selection.getBackToDefault();
67297
- this.selection.selectZone({ cell: { col, row }, zone: union(...selectedZones) }, { scrollIntoView: false });
67377
+ selectPastedZone(this.selection, zones, selectedZones);
67298
67378
  }
67299
67379
  /**
67300
67380
  * Add columns and/or rows to ensure that col + width and row + height are still
@@ -76536,9 +76616,9 @@ stores.inject(MyMetaStore, storeInstance);
76536
76616
  exports.tokenize = tokenize;
76537
76617
 
76538
76618
 
76539
- __info__.version = "18.1.25";
76540
- __info__.date = "2025-06-12T09:49:08.707Z";
76541
- __info__.hash = "a232524";
76619
+ __info__.version = "18.1.27";
76620
+ __info__.date = "2025-06-23T15:04:51.792Z";
76621
+ __info__.hash = "b25bcc7";
76542
76622
 
76543
76623
 
76544
76624
  })(this.o_spreadsheet = this.o_spreadsheet || {}, owl);