@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
  import { useEnv, useSubEnv, onWillUnmount, useComponent, status, Component, useRef, onMounted, useEffect, useState, onPatched, onWillPatch, onWillUpdateProps, useExternalListener, onWillStart, xml, useChildSubEnv, markRaw, toRaw } from '@odoo/owl';
@@ -6816,6 +6816,63 @@ function parseOSClipboardContent(content) {
6816
6816
  data: spreadsheetContent,
6817
6817
  };
6818
6818
  }
6819
+ /**
6820
+ * Applies each clipboard handler to paste its corresponding data into the target.
6821
+ */
6822
+ const applyClipboardHandlersPaste = (handlers, copiedData, target, options) => {
6823
+ handlers.forEach(({ handlerName, handler }) => {
6824
+ const data = copiedData[handlerName];
6825
+ if (data) {
6826
+ handler.paste(target, data, options);
6827
+ }
6828
+ });
6829
+ };
6830
+ /**
6831
+ * Returns the paste target based on clipboard handlers.
6832
+ * Also includes the full affected zone and the list of pasted zones for selection.
6833
+ */
6834
+ function getPasteTargetFromHandlers(sheetId, zones, copiedData, handlers, options) {
6835
+ let zone = undefined;
6836
+ let selectedZones = [];
6837
+ let target = {
6838
+ sheetId,
6839
+ zones,
6840
+ };
6841
+ for (const { handlerName, handler } of handlers) {
6842
+ const handlerData = copiedData[handlerName];
6843
+ if (!handlerData) {
6844
+ continue;
6845
+ }
6846
+ const currentTarget = handler.getPasteTarget(sheetId, zones, handlerData, options);
6847
+ if (currentTarget.figureId) {
6848
+ target.figureId = currentTarget.figureId;
6849
+ }
6850
+ for (const targetZone of currentTarget.zones) {
6851
+ selectedZones.push(targetZone);
6852
+ if (zone === undefined) {
6853
+ zone = targetZone;
6854
+ continue;
6855
+ }
6856
+ zone = union(zone, targetZone);
6857
+ }
6858
+ }
6859
+ return {
6860
+ target,
6861
+ zone,
6862
+ selectedZones,
6863
+ };
6864
+ }
6865
+ /**
6866
+ * Updates the selection after a paste operation.
6867
+ */
6868
+ const selectPastedZone = (selection, sourceZones, pastedZones) => {
6869
+ const anchorCell = {
6870
+ col: sourceZones[0].left,
6871
+ row: sourceZones[0].top,
6872
+ };
6873
+ selection.getBackToDefault();
6874
+ selection.selectZone({ cell: anchorCell, zone: union(...pastedZones) }, { scrollIntoView: false });
6875
+ };
6819
6876
 
6820
6877
  class ClipboardHandler {
6821
6878
  getters;
@@ -10202,6 +10259,7 @@ function drawLineOrBarOrRadarChartValues(chart, options, ctx) {
10202
10259
  if (isTrendLineAxis(dataset.xAxisID) || dataset.hidden) {
10203
10260
  continue;
10204
10261
  }
10262
+ const yAxisScale = chart.scales[dataset.yAxisID];
10205
10263
  for (let i = 0; i < dataset._parsed.length; i++) {
10206
10264
  const parsedValue = dataset._parsed[i];
10207
10265
  const value = Number(chart.config.type === "radar" ? parsedValue.r : parsedValue.y);
@@ -10212,10 +10270,18 @@ function drawLineOrBarOrRadarChartValues(chart, options, ctx) {
10212
10270
  const xPosition = point.x;
10213
10271
  let yPosition = 0;
10214
10272
  if (chart.config.type === "line" || chart.config.type === "radar") {
10215
- yPosition = point.y - 10;
10273
+ yPosition = value < 0 ? point.y + 10 : point.y - 10;
10216
10274
  }
10217
10275
  else {
10218
- yPosition = value < 0 ? point.y - point.height / 2 : point.y + point.height / 2;
10276
+ const yZeroLine = yAxisScale.getPixelForValue(0);
10277
+ const distanceFromAxisOrigin = Math.abs(yZeroLine - point.y);
10278
+ const textHeight = 12; // ChartJS default text height
10279
+ if (distanceFromAxisOrigin < textHeight) {
10280
+ yPosition = value < 0 ? yZeroLine + textHeight / 2 : yZeroLine - textHeight / 2;
10281
+ }
10282
+ else {
10283
+ yPosition = value < 0 ? point.y - point.height / 2 : point.y + point.height / 2;
10284
+ }
10219
10285
  }
10220
10286
  yPosition = Math.min(yPosition, yMax);
10221
10287
  yPosition = Math.max(yPosition, yMin);
@@ -10225,7 +10291,7 @@ function drawLineOrBarOrRadarChartValues(chart, options, ctx) {
10225
10291
  }
10226
10292
  for (const otherPosition of textsPositions[xPosition] || []) {
10227
10293
  if (Math.abs(otherPosition - yPosition) < 13) {
10228
- yPosition = otherPosition - 13;
10294
+ yPosition = value < 0 ? otherPosition + 13 : otherPosition - 13;
10229
10295
  }
10230
10296
  }
10231
10297
  textsPositions[xPosition].push(yPosition);
@@ -10244,6 +10310,8 @@ function drawHorizontalBarChartValues(chart, options, ctx) {
10244
10310
  if (isTrendLineAxis(dataset.xAxisID)) {
10245
10311
  return; // ignore trend lines
10246
10312
  }
10313
+ const xAxisScale = chart.scales[dataset.xAxisID];
10314
+ const xZeroLine = xAxisScale.getPixelForValue(0);
10247
10315
  for (let i = 0; i < dataset._parsed.length; i++) {
10248
10316
  const value = Number(dataset._parsed[i].x);
10249
10317
  if (isNaN(value)) {
@@ -10252,17 +10320,27 @@ function drawHorizontalBarChartValues(chart, options, ctx) {
10252
10320
  const displayValue = options.callback(value, dataset, i);
10253
10321
  const point = dataset.data[i];
10254
10322
  const yPosition = point.y;
10255
- let xPosition = value < 0 ? point.x + point.width / 2 : point.x - point.width / 2;
10256
- xPosition = Math.min(xPosition, xMax);
10257
- xPosition = Math.max(xPosition, xMin);
10323
+ const textWidth = computeTextWidth(ctx, displayValue, { fontSize: 12 }, "px");
10324
+ const distanceFromAxisOrigin = Math.abs(point.x - xZeroLine);
10325
+ const PADDING = 3;
10326
+ let xPosition;
10327
+ if (distanceFromAxisOrigin < textWidth) {
10328
+ xPosition =
10329
+ value < 0 ? xZeroLine - textWidth / 2 - PADDING : xZeroLine + textWidth / 2 + PADDING;
10330
+ }
10331
+ else {
10332
+ xPosition = value < 0 ? point.x + point.width / 2 : point.x - point.width / 2;
10333
+ xPosition = Math.min(xPosition, xMax);
10334
+ xPosition = Math.max(xPosition, xMin);
10335
+ }
10258
10336
  // Avoid overlapping texts with same Y
10259
10337
  if (!textsPositions[yPosition]) {
10260
10338
  textsPositions[yPosition] = [];
10261
10339
  }
10262
- const textWidth = computeTextWidth(ctx, displayValue, { fontSize: 12 }, "px");
10263
10340
  for (const otherPosition of textsPositions[yPosition]) {
10264
10341
  if (Math.abs(otherPosition - xPosition) < textWidth) {
10265
- xPosition = otherPosition + textWidth + 3;
10342
+ xPosition =
10343
+ value < 0 ? otherPosition - textWidth - PADDING : otherPosition + textWidth + PADDING;
10266
10344
  }
10267
10345
  }
10268
10346
  textsPositions[yPosition].push(xPosition);
@@ -30078,7 +30156,9 @@ function getPyramidChartShowValues(definition, args) {
30078
30156
  background: definition.background,
30079
30157
  callback: (value, dataset) => {
30080
30158
  value = Math.abs(Number(value));
30081
- return formatChartDatasetValue(axisFormats, locale)(value, dataset.xAxisID || "x");
30159
+ return value === 0
30160
+ ? ""
30161
+ : formatChartDatasetValue(axisFormats, locale)(value, dataset.xAxisID || "x");
30082
30162
  },
30083
30163
  };
30084
30164
  }
@@ -34578,6 +34658,10 @@ const REMOVE_ROWS_ACTION = (env) => {
34578
34658
  });
34579
34659
  };
34580
34660
  const CAN_REMOVE_COLUMNS_ROWS = (dimension, env) => {
34661
+ if ((dimension === "COL" && env.model.getters.getActiveRows().size > 0) ||
34662
+ (dimension === "ROW" && env.model.getters.getActiveCols().size > 0)) {
34663
+ return false;
34664
+ }
34581
34665
  const sheetId = env.model.getters.getActiveSheetId();
34582
34666
  const selectedElements = env.model.getters.getElementsFromSelection(dimension);
34583
34667
  const includesAllVisibleHeaders = env.model.getters.checkElementsIncludeAllVisibleHeaders(sheetId, dimension, selectedElements);
@@ -37557,11 +37641,11 @@ class OTRegistry extends Registry {
37557
37641
  * transformation function given
37558
37642
  */
37559
37643
  addTransformation(executed, toTransforms, fn) {
37560
- for (let toTransform of toTransforms) {
37561
- if (!this.content[toTransform]) {
37562
- this.content[toTransform] = new Map();
37563
- }
37564
- this.content[toTransform].set(executed, fn);
37644
+ if (!this.content[executed]) {
37645
+ this.content[executed] = new Map();
37646
+ }
37647
+ for (const toTransform of toTransforms) {
37648
+ this.content[executed].set(toTransform, fn);
37565
37649
  }
37566
37650
  return this;
37567
37651
  }
@@ -37570,7 +37654,7 @@ class OTRegistry extends Registry {
37570
37654
  * that the executed command happened.
37571
37655
  */
37572
37656
  getTransformation(toTransform, executed) {
37573
- return this.content[toTransform] && this.content[toTransform].get(executed);
37657
+ return this.content[executed] && this.content[executed].get(toTransform);
37574
37658
  }
37575
37659
  }
37576
37660
  const otRegistry = new OTRegistry();
@@ -46736,7 +46820,7 @@ class SpreadsheetPivot {
46736
46820
  }
46737
46821
  getTypeFromZone(sheetId, zone) {
46738
46822
  const cells = this.getters.getEvaluatedCellsInZone(sheetId, zone);
46739
- const nonEmptyCells = cells.filter((cell) => cell.type !== CellValueType.empty);
46823
+ const nonEmptyCells = cells.filter((cell) => !(cell.type === CellValueType.empty || cell.value === ""));
46740
46824
  if (nonEmptyCells.length === 0) {
46741
46825
  return "integer";
46742
46826
  }
@@ -50293,15 +50377,16 @@ class GridAddRowsFooter extends Component {
50293
50377
  }
50294
50378
  }
50295
50379
 
50380
+ const PAINT_FORMAT_HANDLER_KEYS = [
50381
+ "cell",
50382
+ "border",
50383
+ "table",
50384
+ "conditionalFormat",
50385
+ "merge",
50386
+ ];
50296
50387
  class PaintFormatStore extends SpreadsheetStore {
50297
50388
  mutators = ["activate", "cancel", "pasteFormat"];
50298
50389
  highlightStore = this.get(HighlightStore);
50299
- clipboardHandlers = [
50300
- new CellClipboardHandler(this.getters, this.model.dispatch),
50301
- new BorderClipboardHandler(this.getters, this.model.dispatch),
50302
- new TableClipboardHandler(this.getters, this.model.dispatch),
50303
- new ConditionalFormatClipboardHandler(this.getters, this.model.dispatch),
50304
- ];
50305
50390
  status = "inactive";
50306
50391
  copiedData;
50307
50392
  constructor(get) {
@@ -50332,24 +50417,38 @@ class PaintFormatStore extends SpreadsheetStore {
50332
50417
  get isActive() {
50333
50418
  return this.status !== "inactive";
50334
50419
  }
50420
+ get clipboardHandlers() {
50421
+ return PAINT_FORMAT_HANDLER_KEYS.map((handlerName) => {
50422
+ const HandlerClass = clipboardHandlersRegistries.cellHandlers.get(handlerName);
50423
+ return {
50424
+ handlerName,
50425
+ handler: new HandlerClass(this.getters, this.model.dispatch),
50426
+ };
50427
+ });
50428
+ }
50335
50429
  copyFormats() {
50336
50430
  const sheetId = this.getters.getActiveSheetId();
50337
50431
  const zones = this.getters.getSelectedZones();
50338
- const copiedData = {};
50339
- for (const handler of this.clipboardHandlers) {
50340
- Object.assign(copiedData, handler.copy(getClipboardDataPositions(sheetId, zones)));
50432
+ const copiedData = { zones, sheetId };
50433
+ for (const { handlerName, handler } of this.clipboardHandlers) {
50434
+ const handlerResult = handler.copy(getClipboardDataPositions(sheetId, zones));
50435
+ if (handlerResult !== undefined) {
50436
+ copiedData[handlerName] = handlerResult;
50437
+ }
50341
50438
  }
50342
50439
  return copiedData;
50343
50440
  }
50344
50441
  paintFormat(sheetId, target) {
50345
- if (this.copiedData) {
50346
- for (const handler of this.clipboardHandlers) {
50347
- handler.paste({ zones: target, sheetId }, this.copiedData, {
50348
- isCutOperation: false,
50349
- pasteOption: "onlyFormat",
50350
- });
50351
- }
50442
+ if (!this.copiedData) {
50443
+ return;
50352
50444
  }
50445
+ const options = {
50446
+ isCutOperation: false,
50447
+ pasteOption: "onlyFormat",
50448
+ };
50449
+ const { target: pasteTarget, selectedZones } = getPasteTargetFromHandlers(sheetId, target, this.copiedData, this.clipboardHandlers, options);
50450
+ applyClipboardHandlersPaste(this.clipboardHandlers, this.copiedData, pasteTarget, options);
50451
+ selectPastedZone(this.model.selection, target, selectedZones);
50353
50452
  if (this.status === "oneOff") {
50354
50453
  this.cancel();
50355
50454
  }
@@ -55515,7 +55614,7 @@ class DataValidationPlugin extends CorePlugin {
55515
55614
  else if (newRule.criterion.type === "isValueInList") {
55516
55615
  newRule.criterion.values = Array.from(new Set(newRule.criterion.values));
55517
55616
  }
55518
- const adaptedRules = this.removeRangesFromRules(sheetId, newRule.ranges, rules);
55617
+ const adaptedRules = this.removeRangesFromRules(sheetId, newRule.ranges, rules, newRule.id);
55519
55618
  const ruleIndex = adaptedRules.findIndex((rule) => rule.id === newRule.id);
55520
55619
  if (ruleIndex !== -1) {
55521
55620
  adaptedRules[ruleIndex] = newRule;
@@ -55525,9 +55624,12 @@ class DataValidationPlugin extends CorePlugin {
55525
55624
  this.history.update("rules", sheetId, [...adaptedRules, newRule]);
55526
55625
  }
55527
55626
  }
55528
- removeRangesFromRules(sheetId, ranges, rules) {
55627
+ removeRangesFromRules(sheetId, ranges, rules, editingRuleId) {
55529
55628
  rules = deepCopy(rules);
55530
55629
  for (const rule of rules) {
55630
+ if (rule.id === editingRuleId) {
55631
+ continue; // Skip the rule being edited to preserve its place in the list
55632
+ }
55531
55633
  rule.ranges = this.getters.recomputeRanges(rule.ranges, ranges);
55532
55634
  }
55533
55635
  return rules.filter((rule) => rule.ranges.length > 0);
@@ -64265,10 +64367,20 @@ function transform(toTransform, executed) {
64265
64367
  */
64266
64368
  function transformAll(toTransform, executed) {
64267
64369
  let transformedCommands = [...toTransform];
64370
+ const possibleTransformations = new Set(otRegistry.getKeys());
64268
64371
  for (const executedCommand of executed) {
64269
- transformedCommands = transformedCommands
64270
- .map((cmd) => transform(cmd, executedCommand))
64271
- .filter(isDefined);
64372
+ // If the executed command is not in the registry, we skip it
64373
+ // because we know there won't be any transformation impacting the
64374
+ // commands to transform.
64375
+ if (possibleTransformations.has(executedCommand.type)) {
64376
+ transformedCommands = transformedCommands.reduce((acc, cmd) => {
64377
+ const transformed = transform(cmd, executedCommand);
64378
+ if (transformed) {
64379
+ acc.push(transformed);
64380
+ }
64381
+ return acc;
64382
+ }, []);
64383
+ }
64272
64384
  }
64273
64385
  return transformedCommands;
64274
64386
  }
@@ -65963,7 +66075,7 @@ class SheetUIPlugin extends UIPlugin {
65963
66075
  }
65964
66076
  const position = this.getters.getCellPosition(cell.id);
65965
66077
  const colSize = this.getters.getColSize(sheetId, position.col);
65966
- if (cell.isFormula) {
66078
+ if (cell.isFormula || this.getters.getArrayFormulaSpreadingOn(position)) {
65967
66079
  const content = this.getters.getEvaluatedCell(position).formattedValue;
65968
66080
  const evaluatedSize = getCellContentHeight(this.ctx, content, cell?.style, colSize);
65969
66081
  if (evaluatedSize > evaluatedRowSize && evaluatedSize > DEFAULT_CELL_HEIGHT) {
@@ -67251,49 +67363,17 @@ class ClipboardPlugin extends UIPlugin {
67251
67363
  if (!copiedData) {
67252
67364
  return;
67253
67365
  }
67254
- let zone = undefined;
67255
- let selectedZones = [];
67256
67366
  const sheetId = this.getters.getActiveSheetId();
67257
- let target = {
67258
- sheetId,
67259
- zones,
67260
- };
67261
67367
  const handlers = this.selectClipboardHandlers(copiedData);
67262
- for (const { handlerName, handler } of handlers) {
67263
- const handlerData = copiedData[handlerName];
67264
- if (!handlerData) {
67265
- continue;
67266
- }
67267
- const currentTarget = handler.getPasteTarget(sheetId, zones, handlerData, options);
67268
- if (currentTarget.figureId) {
67269
- target.figureId = currentTarget.figureId;
67270
- }
67271
- for (const targetZone of currentTarget.zones) {
67272
- selectedZones.push(targetZone);
67273
- if (zone === undefined) {
67274
- zone = targetZone;
67275
- continue;
67276
- }
67277
- zone = union(zone, targetZone);
67278
- }
67279
- }
67368
+ const { target, zone, selectedZones } = getPasteTargetFromHandlers(sheetId, zones, copiedData, handlers, options);
67280
67369
  if (zone !== undefined) {
67281
- this.addMissingDimensions(this.getters.getActiveSheetId(), zone.right - zone.left + 1, zone.bottom - zone.top + 1, zone.left, zone.top);
67370
+ this.addMissingDimensions(sheetId, zone.right - zone.left + 1, zone.bottom - zone.top + 1, zone.left, zone.top);
67282
67371
  }
67283
- handlers.forEach(({ handlerName, handler }) => {
67284
- const handlerData = copiedData[handlerName];
67285
- if (handlerData) {
67286
- handler.paste(target, handlerData, options);
67287
- }
67288
- });
67372
+ applyClipboardHandlersPaste(handlers, copiedData, target, options);
67289
67373
  if (!options?.selectTarget) {
67290
67374
  return;
67291
67375
  }
67292
- const selection = zones[0];
67293
- const col = selection.left;
67294
- const row = selection.top;
67295
- this.selection.getBackToDefault();
67296
- this.selection.selectZone({ cell: { col, row }, zone: union(...selectedZones) }, { scrollIntoView: false });
67376
+ selectPastedZone(this.selection, zones, selectedZones);
67297
67377
  }
67298
67378
  /**
67299
67379
  * Add columns and/or rows to ensure that col + width and row + height are still
@@ -76491,6 +76571,6 @@ const chartHelpers = { ...CHART_HELPERS, ...CHART_RUNTIME_HELPERS };
76491
76571
  export { AbstractCellClipboardHandler, AbstractChart, AbstractFigureClipboardHandler, CellErrorType, CommandResult, CorePlugin, DispatchResult, EvaluationError, Model, PivotRuntimeDefinition, Registry, Revision, SPREADSHEET_DIMENSIONS, Spreadsheet, SpreadsheetPivotTable, UIPlugin, __info__, addFunction, addRenderingLayer, astToFormula, chartHelpers, compile, compileTokens, components, constants, convertAstNodes, coreTypes, findCellInNewZone, functionCache, helpers, hooks, invalidateCFEvaluationCommands, invalidateDependenciesCommands, invalidateEvaluationCommands, iterateAstNodes, links, load, parse, parseTokens, readonlyAllowedCommands, registries, setDefaultSheetViewSize, setTranslationMethod, stores, tokenColors, tokenize };
76492
76572
 
76493
76573
 
76494
- __info__.version = "18.1.25";
76495
- __info__.date = "2025-06-12T09:49:08.707Z";
76496
- __info__.hash = "a232524";
76574
+ __info__.version = "18.1.27";
76575
+ __info__.date = "2025-06-23T15:04:51.792Z";
76576
+ __info__.hash = "b25bcc7";