@odoo/o-spreadsheet 18.3.8 → 18.3.10

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.3.8
6
- * @date 2025-06-12T09:51:55.929Z
7
- * @hash 32dedd1
5
+ * @version 18.3.10
6
+ * @date 2025-06-23T15:05:03.747Z
7
+ * @hash 748e300
8
8
  */
9
9
 
10
10
  (function (exports, owl) {
@@ -142,6 +142,7 @@
142
142
  const FROZEN_PANE_BORDER_COLOR = "#DADFE8";
143
143
  const COMPOSER_ASSISTANT_COLOR = "#9B359B";
144
144
  const COLOR_TRANSPARENT = "#00000000";
145
+ const TABLE_HOVER_BACKGROUND_COLOR = "#017E8414";
145
146
  const CHART_WATERFALL_POSITIVE_COLOR = "#4EA7F2";
146
147
  const CHART_WATERFALL_NEGATIVE_COLOR = "#EA6175";
147
148
  const CHART_WATERFALL_SUBTOTAL_COLOR = "#AAAAAA";
@@ -7121,6 +7122,63 @@
7121
7122
  };
7122
7123
  return osClipboardContent;
7123
7124
  }
7125
+ /**
7126
+ * Applies each clipboard handler to paste its corresponding data into the target.
7127
+ */
7128
+ const applyClipboardHandlersPaste = (handlers, copiedData, target, options) => {
7129
+ handlers.forEach(({ handlerName, handler }) => {
7130
+ const data = copiedData[handlerName];
7131
+ if (data) {
7132
+ handler.paste(target, data, options);
7133
+ }
7134
+ });
7135
+ };
7136
+ /**
7137
+ * Returns the paste target based on clipboard handlers.
7138
+ * Also includes the full affected zone and the list of pasted zones for selection.
7139
+ */
7140
+ function getPasteTargetFromHandlers(sheetId, zones, copiedData, handlers, options) {
7141
+ let zone = undefined;
7142
+ let selectedZones = [];
7143
+ let target = {
7144
+ sheetId,
7145
+ zones,
7146
+ };
7147
+ for (const { handlerName, handler } of handlers) {
7148
+ const handlerData = copiedData[handlerName];
7149
+ if (!handlerData) {
7150
+ continue;
7151
+ }
7152
+ const currentTarget = handler.getPasteTarget(sheetId, zones, handlerData, options);
7153
+ if (currentTarget.figureId) {
7154
+ target.figureId = currentTarget.figureId;
7155
+ }
7156
+ for (const targetZone of currentTarget.zones) {
7157
+ selectedZones.push(targetZone);
7158
+ if (zone === undefined) {
7159
+ zone = targetZone;
7160
+ continue;
7161
+ }
7162
+ zone = union(zone, targetZone);
7163
+ }
7164
+ }
7165
+ return {
7166
+ target,
7167
+ zone,
7168
+ selectedZones,
7169
+ };
7170
+ }
7171
+ /**
7172
+ * Updates the selection after a paste operation.
7173
+ */
7174
+ const selectPastedZone = (selection, sourceZones, pastedZones) => {
7175
+ const anchorCell = {
7176
+ col: sourceZones[0].left,
7177
+ row: sourceZones[0].top,
7178
+ };
7179
+ selection.getBackToDefault();
7180
+ selection.selectZone({ cell: anchorCell, zone: union(...pastedZones) }, { scrollIntoView: false });
7181
+ };
7124
7182
 
7125
7183
  class ClipboardHandler {
7126
7184
  getters;
@@ -20952,6 +21010,7 @@ stores.inject(MyMetaStore, storeInstance);
20952
21010
  if (isTrendLineAxis(dataset.xAxisID) || dataset.hidden) {
20953
21011
  continue;
20954
21012
  }
21013
+ const yAxisScale = chart.scales[dataset.yAxisID];
20955
21014
  for (let i = 0; i < dataset._parsed.length; i++) {
20956
21015
  const parsedValue = dataset._parsed[i];
20957
21016
  const value = Number(chart.config.type === "radar" ? parsedValue.r : parsedValue.y);
@@ -20962,10 +21021,18 @@ stores.inject(MyMetaStore, storeInstance);
20962
21021
  const xPosition = point.x;
20963
21022
  let yPosition = 0;
20964
21023
  if (chart.config.type === "line" || chart.config.type === "radar") {
20965
- yPosition = point.y - 10;
21024
+ yPosition = value < 0 ? point.y + 10 : point.y - 10;
20966
21025
  }
20967
21026
  else {
20968
- yPosition = value < 0 ? point.y - point.height / 2 : point.y + point.height / 2;
21027
+ const yZeroLine = yAxisScale.getPixelForValue(0);
21028
+ const distanceFromAxisOrigin = Math.abs(yZeroLine - point.y);
21029
+ const textHeight = 12; // ChartJS default text height
21030
+ if (distanceFromAxisOrigin < textHeight) {
21031
+ yPosition = value < 0 ? yZeroLine + textHeight / 2 : yZeroLine - textHeight / 2;
21032
+ }
21033
+ else {
21034
+ yPosition = value < 0 ? point.y - point.height / 2 : point.y + point.height / 2;
21035
+ }
20969
21036
  }
20970
21037
  yPosition = Math.min(yPosition, yMax);
20971
21038
  yPosition = Math.max(yPosition, yMin);
@@ -20975,7 +21042,7 @@ stores.inject(MyMetaStore, storeInstance);
20975
21042
  }
20976
21043
  for (const otherPosition of textsPositions[xPosition] || []) {
20977
21044
  if (Math.abs(otherPosition - yPosition) < 13) {
20978
- yPosition = otherPosition - 13;
21045
+ yPosition = value < 0 ? otherPosition + 13 : otherPosition - 13;
20979
21046
  }
20980
21047
  }
20981
21048
  textsPositions[xPosition].push(yPosition);
@@ -20994,6 +21061,8 @@ stores.inject(MyMetaStore, storeInstance);
20994
21061
  if (isTrendLineAxis(dataset.xAxisID)) {
20995
21062
  return; // ignore trend lines
20996
21063
  }
21064
+ const xAxisScale = chart.scales[dataset.xAxisID];
21065
+ const xZeroLine = xAxisScale.getPixelForValue(0);
20997
21066
  for (let i = 0; i < dataset._parsed.length; i++) {
20998
21067
  const value = Number(dataset._parsed[i].x);
20999
21068
  if (isNaN(value)) {
@@ -21002,17 +21071,27 @@ stores.inject(MyMetaStore, storeInstance);
21002
21071
  const displayValue = options.callback(value, dataset, i);
21003
21072
  const point = dataset.data[i];
21004
21073
  const yPosition = point.y;
21005
- let xPosition = value < 0 ? point.x + point.width / 2 : point.x - point.width / 2;
21006
- xPosition = Math.min(xPosition, xMax);
21007
- xPosition = Math.max(xPosition, xMin);
21074
+ const textWidth = computeTextWidth(ctx, displayValue, { fontSize: 12 }, "px");
21075
+ const distanceFromAxisOrigin = Math.abs(point.x - xZeroLine);
21076
+ const PADDING = 3;
21077
+ let xPosition;
21078
+ if (distanceFromAxisOrigin < textWidth) {
21079
+ xPosition =
21080
+ value < 0 ? xZeroLine - textWidth / 2 - PADDING : xZeroLine + textWidth / 2 + PADDING;
21081
+ }
21082
+ else {
21083
+ xPosition = value < 0 ? point.x + point.width / 2 : point.x - point.width / 2;
21084
+ xPosition = Math.min(xPosition, xMax);
21085
+ xPosition = Math.max(xPosition, xMin);
21086
+ }
21008
21087
  // Avoid overlapping texts with same Y
21009
21088
  if (!textsPositions[yPosition]) {
21010
21089
  textsPositions[yPosition] = [];
21011
21090
  }
21012
- const textWidth = computeTextWidth(ctx, displayValue, { fontSize: 12 }, "px");
21013
21091
  for (const otherPosition of textsPositions[yPosition]) {
21014
21092
  if (Math.abs(otherPosition - xPosition) < textWidth) {
21015
- xPosition = otherPosition + textWidth + 3;
21093
+ xPosition =
21094
+ value < 0 ? otherPosition - textWidth - PADDING : otherPosition + textWidth + PADDING;
21016
21095
  }
21017
21096
  }
21018
21097
  textsPositions[yPosition].push(xPosition);
@@ -26399,7 +26478,9 @@ stores.inject(MyMetaStore, storeInstance);
26399
26478
  background: definition.background,
26400
26479
  callback: (value, dataset) => {
26401
26480
  value = Math.abs(Number(value));
26402
- return formatChartDatasetValue(axisFormats, locale)(value, dataset.xAxisID || "x");
26481
+ return value === 0
26482
+ ? ""
26483
+ : formatChartDatasetValue(axisFormats, locale)(value, dataset.xAxisID || "x");
26403
26484
  },
26404
26485
  };
26405
26486
  }
@@ -37132,6 +37213,10 @@ stores.inject(MyMetaStore, storeInstance);
37132
37213
  });
37133
37214
  };
37134
37215
  const CAN_REMOVE_COLUMNS_ROWS = (dimension, env) => {
37216
+ if ((dimension === "COL" && env.model.getters.getActiveRows().size > 0) ||
37217
+ (dimension === "ROW" && env.model.getters.getActiveCols().size > 0)) {
37218
+ return false;
37219
+ }
37135
37220
  const sheetId = env.model.getters.getActiveSheetId();
37136
37221
  const selectedElements = env.model.getters.getElementsFromSelection(dimension);
37137
37222
  const includesAllVisibleHeaders = env.model.getters.checkElementsIncludeAllVisibleHeaders(sheetId, dimension, selectedElements);
@@ -40029,11 +40114,11 @@ stores.inject(MyMetaStore, storeInstance);
40029
40114
  * transformation function given
40030
40115
  */
40031
40116
  addTransformation(executed, toTransforms, fn) {
40032
- for (let toTransform of toTransforms) {
40033
- if (!this.content[toTransform]) {
40034
- this.content[toTransform] = new Map();
40035
- }
40036
- this.content[toTransform].set(executed, fn);
40117
+ if (!this.content[executed]) {
40118
+ this.content[executed] = new Map();
40119
+ }
40120
+ for (const toTransform of toTransforms) {
40121
+ this.content[executed].set(toTransform, fn);
40037
40122
  }
40038
40123
  return this;
40039
40124
  }
@@ -40042,7 +40127,7 @@ stores.inject(MyMetaStore, storeInstance);
40042
40127
  * that the executed command happened.
40043
40128
  */
40044
40129
  getTransformation(toTransform, executed) {
40045
- return this.content[toTransform] && this.content[toTransform].get(executed);
40130
+ return this.content[executed] && this.content[executed].get(toTransform);
40046
40131
  }
40047
40132
  }
40048
40133
  const otRegistry = new OTRegistry();
@@ -43332,6 +43417,12 @@ stores.inject(MyMetaStore, storeInstance);
43332
43417
  owl.useEffect(() => {
43333
43418
  this.processTokenAtCursor();
43334
43419
  }, () => [this.props.composerStore.editionMode !== "inactive"]);
43420
+ owl.useEffect(() => {
43421
+ this.contentHelper.scrollSelectionIntoView();
43422
+ }, () => [
43423
+ this.props.composerStore.composerSelection.start,
43424
+ this.props.composerStore.composerSelection.end,
43425
+ ]);
43335
43426
  }
43336
43427
  // ---------------------------------------------------------------------------
43337
43428
  // Handlers
@@ -43554,6 +43645,7 @@ stores.inject(MyMetaStore, storeInstance);
43554
43645
  // not main button, probably a context menu
43555
43646
  return;
43556
43647
  }
43648
+ this.debouncedHover.stopDebounce();
43557
43649
  this.contentHelper.removeSelection();
43558
43650
  }
43559
43651
  onMouseup() {
@@ -43632,7 +43724,6 @@ stores.inject(MyMetaStore, storeInstance);
43632
43724
  const { start, end } = this.props.composerStore.composerSelection;
43633
43725
  this.contentHelper.selectRange(start, end);
43634
43726
  }
43635
- this.contentHelper.scrollSelectionIntoView();
43636
43727
  }
43637
43728
  this.shouldProcessInputEvents = true;
43638
43729
  }
@@ -49789,7 +49880,7 @@ stores.inject(MyMetaStore, storeInstance);
49789
49880
  }
49790
49881
  getTypeFromZone(sheetId, zone) {
49791
49882
  const cells = this.getters.getEvaluatedCellsInZone(sheetId, zone);
49792
- const nonEmptyCells = cells.filter((cell) => cell.type !== CellValueType.empty);
49883
+ const nonEmptyCells = cells.filter((cell) => !(cell.type === CellValueType.empty || cell.value === ""));
49793
49884
  if (nonEmptyCells.length === 0) {
49794
49885
  return "integer";
49795
49886
  }
@@ -53506,15 +53597,16 @@ stores.inject(MyMetaStore, storeInstance);
53506
53597
  }
53507
53598
  }
53508
53599
 
53600
+ const PAINT_FORMAT_HANDLER_KEYS = [
53601
+ "cell",
53602
+ "border",
53603
+ "table",
53604
+ "conditionalFormat",
53605
+ "merge",
53606
+ ];
53509
53607
  class PaintFormatStore extends SpreadsheetStore {
53510
53608
  mutators = ["activate", "cancel", "pasteFormat"];
53511
53609
  highlightStore = this.get(HighlightStore);
53512
- clipboardHandlers = [
53513
- new CellClipboardHandler(this.getters, this.model.dispatch),
53514
- new BorderClipboardHandler(this.getters, this.model.dispatch),
53515
- new TableClipboardHandler(this.getters, this.model.dispatch),
53516
- new ConditionalFormatClipboardHandler(this.getters, this.model.dispatch),
53517
- ];
53518
53610
  status = "inactive";
53519
53611
  copiedData;
53520
53612
  constructor(get) {
@@ -53545,24 +53637,38 @@ stores.inject(MyMetaStore, storeInstance);
53545
53637
  get isActive() {
53546
53638
  return this.status !== "inactive";
53547
53639
  }
53640
+ get clipboardHandlers() {
53641
+ return PAINT_FORMAT_HANDLER_KEYS.map((handlerName) => {
53642
+ const HandlerClass = clipboardHandlersRegistries.cellHandlers.get(handlerName);
53643
+ return {
53644
+ handlerName,
53645
+ handler: new HandlerClass(this.getters, this.model.dispatch),
53646
+ };
53647
+ });
53648
+ }
53548
53649
  copyFormats() {
53549
53650
  const sheetId = this.getters.getActiveSheetId();
53550
53651
  const zones = this.getters.getSelectedZones();
53551
- const copiedData = {};
53552
- for (const handler of this.clipboardHandlers) {
53553
- Object.assign(copiedData, handler.copy(getClipboardDataPositions(sheetId, zones), false));
53652
+ const copiedData = { zones, sheetId };
53653
+ for (const { handlerName, handler } of this.clipboardHandlers) {
53654
+ const handlerResult = handler.copy(getClipboardDataPositions(sheetId, zones), false);
53655
+ if (handlerResult !== undefined) {
53656
+ copiedData[handlerName] = handlerResult;
53657
+ }
53554
53658
  }
53555
53659
  return copiedData;
53556
53660
  }
53557
53661
  paintFormat(sheetId, target) {
53558
- if (this.copiedData) {
53559
- for (const handler of this.clipboardHandlers) {
53560
- handler.paste({ zones: target, sheetId }, this.copiedData, {
53561
- isCutOperation: false,
53562
- pasteOption: "onlyFormat",
53563
- });
53564
- }
53662
+ if (!this.copiedData) {
53663
+ return;
53565
53664
  }
53665
+ const options = {
53666
+ isCutOperation: false,
53667
+ pasteOption: "onlyFormat",
53668
+ };
53669
+ const { target: pasteTarget, selectedZones } = getPasteTargetFromHandlers(sheetId, target, this.copiedData, this.clipboardHandlers, options);
53670
+ applyClipboardHandlersPaste(this.clipboardHandlers, this.copiedData, pasteTarget, options);
53671
+ selectPastedZone(this.model.selection, target, selectedZones);
53566
53672
  if (this.status === "oneOff") {
53567
53673
  this.cancel();
53568
53674
  }
@@ -53609,12 +53715,8 @@ stores.inject(MyMetaStore, storeInstance);
53609
53715
  this.row = undefined;
53610
53716
  }
53611
53717
  computeOverlay() {
53612
- if (!this.getters.isDashboard()) {
53613
- return;
53614
- }
53615
53718
  this.overlayColors = new PositionMap();
53616
- const col = this.col;
53617
- const row = this.row;
53719
+ const { col, row } = this;
53618
53720
  if (col === undefined || row === undefined) {
53619
53721
  return;
53620
53722
  }
@@ -53623,9 +53725,16 @@ stores.inject(MyMetaStore, storeInstance);
53623
53725
  if (!table) {
53624
53726
  return;
53625
53727
  }
53626
- const { left, right } = table.range.zone;
53627
- for (let c = left; c <= right; c++) {
53628
- this.overlayColors.set({ sheetId, col: c, row }, setColorAlpha("#017E84", 0.08));
53728
+ const { left, right, top } = table.range.zone;
53729
+ const isTableHeader = row < top + table.config.numberOfHeaders;
53730
+ const doesTableRowHaveContent = range(left, right + 1).some((col) => {
53731
+ return (!this.getters.isColHidden(sheetId, col) &&
53732
+ this.getters.getEvaluatedCell({ sheetId, col, row }).formattedValue);
53733
+ });
53734
+ if (!isTableHeader && doesTableRowHaveContent) {
53735
+ for (let col = left; col <= right; col++) {
53736
+ this.overlayColors.set({ sheetId, col, row }, TABLE_HOVER_BACKGROUND_COLOR);
53737
+ }
53629
53738
  }
53630
53739
  }
53631
53740
  }
@@ -58972,7 +59081,7 @@ stores.inject(MyMetaStore, storeInstance);
58972
59081
  else if (newRule.criterion.type === "isValueInList") {
58973
59082
  newRule.criterion.values = Array.from(new Set(newRule.criterion.values));
58974
59083
  }
58975
- const adaptedRules = this.removeRangesFromRules(sheetId, newRule.ranges, rules);
59084
+ const adaptedRules = this.removeRangesFromRules(sheetId, newRule.ranges, rules, newRule.id);
58976
59085
  const ruleIndex = adaptedRules.findIndex((rule) => rule.id === newRule.id);
58977
59086
  if (ruleIndex !== -1) {
58978
59087
  adaptedRules[ruleIndex] = newRule;
@@ -58982,9 +59091,12 @@ stores.inject(MyMetaStore, storeInstance);
58982
59091
  this.history.update("rules", sheetId, [...adaptedRules, newRule]);
58983
59092
  }
58984
59093
  }
58985
- removeRangesFromRules(sheetId, ranges, rules) {
59094
+ removeRangesFromRules(sheetId, ranges, rules, editingRuleId) {
58986
59095
  rules = deepCopy(rules);
58987
59096
  for (const rule of rules) {
59097
+ if (rule.id === editingRuleId) {
59098
+ continue; // Skip the rule being edited to preserve its place in the list
59099
+ }
58988
59100
  rule.ranges = this.getters.recomputeRanges(rule.ranges, ranges);
58989
59101
  }
58990
59102
  return rules.filter((rule) => rule.ranges.length > 0);
@@ -67777,10 +67889,20 @@ stores.inject(MyMetaStore, storeInstance);
67777
67889
  */
67778
67890
  function transformAll(toTransform, executed) {
67779
67891
  let transformedCommands = [...toTransform];
67892
+ const possibleTransformations = new Set(otRegistry.getKeys());
67780
67893
  for (const executedCommand of executed) {
67781
- transformedCommands = transformedCommands
67782
- .map((cmd) => transform(cmd, executedCommand))
67783
- .filter(isDefined);
67894
+ // If the executed command is not in the registry, we skip it
67895
+ // because we know there won't be any transformation impacting the
67896
+ // commands to transform.
67897
+ if (possibleTransformations.has(executedCommand.type)) {
67898
+ transformedCommands = transformedCommands.reduce((acc, cmd) => {
67899
+ const transformed = transform(cmd, executedCommand);
67900
+ if (transformed) {
67901
+ acc.push(transformed);
67902
+ }
67903
+ return acc;
67904
+ }, []);
67905
+ }
67784
67906
  }
67785
67907
  return transformedCommands;
67786
67908
  }
@@ -69482,7 +69604,7 @@ stores.inject(MyMetaStore, storeInstance);
69482
69604
  }
69483
69605
  const position = this.getters.getCellPosition(cell.id);
69484
69606
  const colSize = this.getters.getColSize(sheetId, position.col);
69485
- if (cell.isFormula) {
69607
+ if (cell.isFormula || this.getters.getArrayFormulaSpreadingOn(position)) {
69486
69608
  const content = this.getters.getEvaluatedCell(position).formattedValue;
69487
69609
  const evaluatedSize = getCellContentHeight(this.ctx, content, cell?.style, colSize);
69488
69610
  if (evaluatedSize > evaluatedRowSize && evaluatedSize > DEFAULT_CELL_HEIGHT) {
@@ -70802,49 +70924,17 @@ stores.inject(MyMetaStore, storeInstance);
70802
70924
  if (!copiedData) {
70803
70925
  return;
70804
70926
  }
70805
- let zone = undefined;
70806
- let selectedZones = [];
70807
70927
  const sheetId = this.getters.getActiveSheetId();
70808
- let target = {
70809
- sheetId,
70810
- zones,
70811
- };
70812
70928
  const handlers = this.selectClipboardHandlers(copiedData);
70813
- for (const { handlerName, handler } of handlers) {
70814
- const handlerData = copiedData[handlerName];
70815
- if (!handlerData) {
70816
- continue;
70817
- }
70818
- const currentTarget = handler.getPasteTarget(sheetId, zones, handlerData, options);
70819
- if (currentTarget.figureId) {
70820
- target.figureId = currentTarget.figureId;
70821
- }
70822
- for (const targetZone of currentTarget.zones) {
70823
- selectedZones.push(targetZone);
70824
- if (zone === undefined) {
70825
- zone = targetZone;
70826
- continue;
70827
- }
70828
- zone = union(zone, targetZone);
70829
- }
70830
- }
70929
+ const { target, zone, selectedZones } = getPasteTargetFromHandlers(sheetId, zones, copiedData, handlers, options);
70831
70930
  if (zone !== undefined) {
70832
- this.addMissingDimensions(this.getters.getActiveSheetId(), zone.right - zone.left + 1, zone.bottom - zone.top + 1, zone.left, zone.top);
70931
+ this.addMissingDimensions(sheetId, zone.right - zone.left + 1, zone.bottom - zone.top + 1, zone.left, zone.top);
70833
70932
  }
70834
- handlers.forEach(({ handlerName, handler }) => {
70835
- const handlerData = copiedData[handlerName];
70836
- if (handlerData) {
70837
- handler.paste(target, handlerData, options);
70838
- }
70839
- });
70933
+ applyClipboardHandlersPaste(handlers, copiedData, target, options);
70840
70934
  if (!options?.selectTarget) {
70841
70935
  return;
70842
70936
  }
70843
- const selection = zones[0];
70844
- const col = selection.left;
70845
- const row = selection.top;
70846
- this.selection.getBackToDefault();
70847
- this.selection.selectZone({ cell: { col, row }, zone: union(...selectedZones) }, { scrollIntoView: false });
70937
+ selectPastedZone(this.selection, zones, selectedZones);
70848
70938
  }
70849
70939
  /**
70850
70940
  * Add columns and/or rows to ensure that col + width and row + height are still
@@ -80667,9 +80757,9 @@ stores.inject(MyMetaStore, storeInstance);
80667
80757
  exports.tokenize = tokenize;
80668
80758
 
80669
80759
 
80670
- __info__.version = "18.3.8";
80671
- __info__.date = "2025-06-12T09:51:55.929Z";
80672
- __info__.hash = "32dedd1";
80760
+ __info__.version = "18.3.10";
80761
+ __info__.date = "2025-06-23T15:05:03.747Z";
80762
+ __info__.hash = "748e300";
80673
80763
 
80674
80764
 
80675
80765
  })(this.o_spreadsheet = this.o_spreadsheet || {}, owl);