@odoo/o-spreadsheet 18.5.0-alpha.11 → 18.5.0-alpha.12

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.5.0-alpha.11
6
- * @date 2025-08-26T10:14:05.357Z
7
- * @hash b913e49
5
+ * @version 18.5.0-alpha.12
6
+ * @date 2025-08-29T08:05:09.767Z
7
+ * @hash 8ee7677
8
8
  */
9
9
 
10
10
  'use strict';
@@ -1347,6 +1347,18 @@ function hslaToHex(hsla) {
1347
1347
  function hexToHSLA(hex) {
1348
1348
  return rgbaToHSLA(colorToRGBA(hex));
1349
1349
  }
1350
+ /**
1351
+ * Blend color2 on top of color1, with alpha blending.
1352
+ */
1353
+ function blendColors(color1, color2) {
1354
+ const rgba2 = colorToRGBA(color2);
1355
+ const rgba1 = colorToRGBA(color1);
1356
+ const a = rgba2.a + rgba1.a * (1 - rgba2.a);
1357
+ const r = Math.round((rgba2.r * rgba2.a + rgba1.r * rgba1.a * (1 - rgba2.a)) / a);
1358
+ const g = Math.round((rgba2.g * rgba2.a + rgba1.g * rgba1.a * (1 - rgba2.a)) / a);
1359
+ const b = Math.round((rgba2.b * rgba2.a + rgba1.b * rgba1.a * (1 - rgba2.a)) / a);
1360
+ return rgbaToHex({ r, g, b, a });
1361
+ }
1350
1362
  function colorOrNumberToRGBA(color) {
1351
1363
  if (typeof color === "number") {
1352
1364
  return colorToRGBA(colorNumberToHex(color));
@@ -2661,6 +2673,7 @@ const invalidateBordersCommands = new Set([
2661
2673
  "AUTOFILL_CELL",
2662
2674
  "SET_BORDER",
2663
2675
  "SET_ZONE_BORDERS",
2676
+ "SET_BORDERS_ON_TARGET",
2664
2677
  ]);
2665
2678
  const invalidSubtotalFormulasCommands = new Set([
2666
2679
  "UNHIDE_COLUMNS_ROWS",
@@ -2688,6 +2701,7 @@ const readonlyAllowedCommands = new Set([
2688
2701
  "UPDATE_FILTER",
2689
2702
  "UPDATE_CHART",
2690
2703
  "UPDATE_CAROUSEL_ACTIVE_ITEM",
2704
+ "UPDATE_PIVOT",
2691
2705
  ]);
2692
2706
  const coreTypes = new Set([
2693
2707
  /** CELLS */
@@ -46926,18 +46940,18 @@ const pivotProperties = {
46926
46940
  };
46927
46941
  const pivotSortingAsc = {
46928
46942
  name: _t("Ascending"),
46929
- execute: (env) => sortPivot(env, "asc"),
46930
- isActive: (env) => isPivotSortMenuItemActive(env, "asc"),
46943
+ execute: (env) => sortPivot(env, env.model.getters.getActivePosition(), "asc"),
46944
+ isActive: (env) => env.model.getters.getPivotCellSortDirection(env.model.getters.getActivePosition()) === "asc",
46931
46945
  };
46932
46946
  const pivotSortingDesc = {
46933
46947
  name: _t("Descending"),
46934
- execute: (env) => sortPivot(env, "desc"),
46935
- isActive: (env) => isPivotSortMenuItemActive(env, "desc"),
46948
+ execute: (env) => sortPivot(env, env.model.getters.getActivePosition(), "desc"),
46949
+ isActive: (env) => env.model.getters.getPivotCellSortDirection(env.model.getters.getActivePosition()) === "desc",
46936
46950
  };
46937
46951
  const noPivotSorting = {
46938
46952
  name: _t("No sorting"),
46939
- execute: (env) => sortPivot(env, "none"),
46940
- isActive: (env) => isPivotSortMenuItemActive(env, "none"),
46953
+ execute: (env) => sortPivot(env, env.model.getters.getActivePosition(), "none"),
46954
+ isActive: (env) => env.model.getters.getPivotCellSortDirection(env.model.getters.getActivePosition()) === "none",
46941
46955
  };
46942
46956
  const FIX_FORMULAS = {
46943
46957
  name: _t("Convert to individual formulas"),
@@ -47073,23 +47087,19 @@ const ungroupPivotHeadersAction = {
47073
47087
  return areFieldValuesInGroups(definition, values, field, pivot.getFields());
47074
47088
  },
47075
47089
  };
47076
- function canSortPivot(env) {
47077
- const position = env.model.getters.getActivePosition();
47078
- const pivotId = env.model.getters.getPivotIdFromPosition(position);
47079
- if (!pivotId ||
47080
- !env.model.getters.isExistingPivot(pivotId) ||
47081
- !env.model.getters.isSpillPivotFormula(position)) {
47090
+ function canSortPivot(getters, position) {
47091
+ const pivotId = getters.getPivotIdFromPosition(position);
47092
+ if (!pivotId || !getters.isExistingPivot(pivotId) || !getters.isSpillPivotFormula(position)) {
47082
47093
  return false;
47083
47094
  }
47084
- const pivot = env.model.getters.getPivot(pivotId);
47095
+ const pivot = getters.getPivot(pivotId);
47085
47096
  if (!pivot.isValid()) {
47086
47097
  return false;
47087
47098
  }
47088
- const pivotCell = env.model.getters.getPivotCellFromPosition(position);
47099
+ const pivotCell = getters.getPivotCellFromPosition(position);
47089
47100
  return pivotCell.type === "VALUE" || pivotCell.type === "MEASURE_HEADER";
47090
47101
  }
47091
- function sortPivot(env, order) {
47092
- const position = env.model.getters.getActivePosition();
47102
+ function sortPivot(env, position, order) {
47093
47103
  const pivotId = env.model.getters.getPivotIdFromPosition(position);
47094
47104
  const pivotCell = env.model.getters.getPivotCellFromPosition(position);
47095
47105
  if (pivotCell.type === "EMPTY" || pivotCell.type === "HEADER" || !pivotId) {
@@ -47115,24 +47125,6 @@ function sortPivot(env, order) {
47115
47125
  },
47116
47126
  });
47117
47127
  }
47118
- function isPivotSortMenuItemActive(env, order) {
47119
- const position = env.model.getters.getActivePosition();
47120
- const pivotId = env.model.getters.getPivotIdFromPosition(position);
47121
- const pivotCell = env.model.getters.getPivotCellFromPosition(position);
47122
- if (pivotCell.type === "EMPTY" || pivotCell.type === "HEADER" || !pivotId) {
47123
- return false;
47124
- }
47125
- const pivot = env.model.getters.getPivot(pivotId);
47126
- const colDomain = domainToColRowDomain(pivot, pivotCell.domain).colDomain;
47127
- const sortedColumn = pivot.definition.sortedColumn;
47128
- if (order === "none") {
47129
- return !sortedColumn;
47130
- }
47131
- if (!sortedColumn || sortedColumn.order !== order) {
47132
- return false;
47133
- }
47134
- return sortedColumn.measure === pivotCell.measure && deepEquals(sortedColumn.domain, colDomain);
47135
- }
47136
47128
  /*
47137
47129
  * Get the values of the pivot headers in the current selection, if all the pivot headers on the selection belong
47138
47130
  * to the same pivot, the same field and that the pivot formula is a dynamic pivot. Otherwise return undefined.
@@ -47435,7 +47427,10 @@ cellMenuRegistry
47435
47427
  name: _t("Sort pivot"),
47436
47428
  sequence: 155,
47437
47429
  icon: "o-spreadsheet-Icon.SORT_RANGE",
47438
- isVisible: canSortPivot,
47430
+ isVisible: (env) => {
47431
+ const position = env.model.getters.getActivePosition();
47432
+ return canSortPivot(env.model.getters, position);
47433
+ },
47439
47434
  })
47440
47435
  .add("pivot_fix_formulas", {
47441
47436
  ...FIX_FORMULAS,
@@ -60701,6 +60696,42 @@ class Grid extends owl.Component {
60701
60696
  const supportedPivotPositionalFormulaRegistry = new Registry();
60702
60697
  supportedPivotPositionalFormulaRegistry.add("SPREADSHEET", false);
60703
60698
 
60699
+ class ClickableCellSortIcon extends owl.Component {
60700
+ static template = "o-spreadsheet-ClickableCellSortIcon";
60701
+ static props = {
60702
+ position: Object,
60703
+ sortDirection: String,
60704
+ };
60705
+ hoveredTableStore;
60706
+ setup() {
60707
+ this.hoveredTableStore = useStore(HoveredTableStore);
60708
+ }
60709
+ get style() {
60710
+ const cellStyle = this.env.model.getters.getCellComputedStyle(this.props.position);
60711
+ return cssPropertiesToCss({
60712
+ color: cellStyle.textColor || TEXT_BODY_MUTED,
60713
+ "background-color": this.getBackgroundColor(cellStyle),
60714
+ });
60715
+ }
60716
+ get icon() {
60717
+ switch (this.props.sortDirection) {
60718
+ case "asc":
60719
+ return "fa-sort-asc";
60720
+ case "desc":
60721
+ return "fa-sort-desc";
60722
+ default:
60723
+ return "fa-sort";
60724
+ }
60725
+ }
60726
+ getBackgroundColor(cellStyle) {
60727
+ const overlayColor = this.hoveredTableStore.overlayColors.get(this.props.position);
60728
+ if (overlayColor) {
60729
+ return blendColors(cellStyle.fillColor || "#FFFFFF", overlayColor);
60730
+ }
60731
+ return cellStyle.fillColor || "#FFFFFF";
60732
+ }
60733
+ }
60734
+
60704
60735
  class FullScreenChart extends owl.Component {
60705
60736
  static template = "o-spreadsheet-FullScreenChart";
60706
60737
  static props = {};
@@ -71057,6 +71088,7 @@ class PivotUIPlugin extends CoreViewPlugin {
71057
71088
  static getters = [
71058
71089
  "getPivot",
71059
71090
  "getFirstPivotFunction",
71091
+ "getPivotCellSortDirection",
71060
71092
  "getPivotIdFromPosition",
71061
71093
  "getPivotCellFromPosition",
71062
71094
  "generateNewCalculatedMeasureName",
@@ -71277,6 +71309,20 @@ class PivotUIPlugin extends CoreViewPlugin {
71277
71309
  isPivotUnused(pivotId) {
71278
71310
  return this._getUnusedPivots().includes(pivotId);
71279
71311
  }
71312
+ getPivotCellSortDirection(position) {
71313
+ const pivotId = this.getters.getPivotIdFromPosition(position);
71314
+ const pivotCell = this.getters.getPivotCellFromPosition(position);
71315
+ if (pivotCell.type === "EMPTY" || pivotCell.type === "HEADER" || !pivotId) {
71316
+ return undefined;
71317
+ }
71318
+ const pivot = this.getters.getPivot(pivotId);
71319
+ const colDomain = domainToColRowDomain(pivot, pivotCell.domain).colDomain;
71320
+ const sortedColumn = pivot.definition.sortedColumn;
71321
+ if (sortedColumn?.measure === pivotCell.measure && deepEquals(sortedColumn.domain, colDomain)) {
71322
+ return sortedColumn.order;
71323
+ }
71324
+ return "none";
71325
+ }
71280
71326
  // ---------------------------------------------------------------------
71281
71327
  // Private
71282
71328
  // ---------------------------------------------------------------------
@@ -78969,6 +79015,34 @@ clickableCellRegistry.add("link", {
78969
79015
  },
78970
79016
  sequence: 5,
78971
79017
  });
79018
+ clickableCellRegistry.add("dashboard_pivot_sorting", {
79019
+ condition: (position, getters) => {
79020
+ if (!getters.isDashboard()) {
79021
+ return false;
79022
+ }
79023
+ const pivotCell = getters.getPivotCellFromPosition(position);
79024
+ return canSortPivot(getters, position) && pivotCell.type === "MEASURE_HEADER";
79025
+ },
79026
+ execute: (position, env) => {
79027
+ sortPivot(env, position, getNextSortDirection(env.model.getters, position));
79028
+ },
79029
+ component: ClickableCellSortIcon,
79030
+ componentProps: (position, getters) => {
79031
+ return {
79032
+ position,
79033
+ sortDirection: getters.getPivotCellSortDirection(position),
79034
+ };
79035
+ },
79036
+ sequence: 2,
79037
+ });
79038
+ const NEXT_SORT_DIRECTION = {
79039
+ none: "asc",
79040
+ asc: "desc",
79041
+ desc: "none",
79042
+ };
79043
+ function getNextSortDirection(getters, position) {
79044
+ return NEXT_SORT_DIRECTION[getters.getPivotCellSortDirection(position) ?? "none"];
79045
+ }
78972
79046
 
78973
79047
  const inverseCommandRegistry = new Registry()
78974
79048
  .add("ADD_COLUMNS_ROWS", inverseAddColumnsRows)
@@ -80685,6 +80759,8 @@ class ClickableCellsStore extends SpreadsheetStore {
80685
80759
  position,
80686
80760
  action: item.execute,
80687
80761
  title: title || "",
80762
+ component: item.component,
80763
+ componentProps: item.componentProps?.(position, getters) ?? {},
80688
80764
  });
80689
80765
  }
80690
80766
  return cells;
@@ -83031,6 +83107,27 @@ class Spreadsheet extends owl.Component {
83031
83107
  }
83032
83108
  }
83033
83109
 
83110
+ class ReadonlyTransportFilter {
83111
+ transportService;
83112
+ constructor(transportService) {
83113
+ this.transportService = transportService;
83114
+ }
83115
+ async sendMessage(message) {
83116
+ if (message.type === "CLIENT_JOINED" ||
83117
+ message.type === "CLIENT_LEFT" ||
83118
+ message.type === "CLIENT_MOVED") {
83119
+ this.transportService.sendMessage(message);
83120
+ }
83121
+ // ignore all other messages
83122
+ }
83123
+ onNewMessage(id, callback) {
83124
+ this.transportService.onNewMessage(id, callback);
83125
+ }
83126
+ leave(id) {
83127
+ this.transportService.leave(id);
83128
+ }
83129
+ }
83130
+
83034
83131
  function inverseCommand(cmd) {
83035
83132
  return inverseCommandRegistry.get(cmd.type)(cmd);
83036
83133
  }
@@ -87123,12 +87220,15 @@ class Model extends EventBus {
87123
87220
  name: _t("Anonymous").toString(),
87124
87221
  };
87125
87222
  const transportService = config.transportService || new LocalTransportService();
87223
+ const isReadonly = config.mode === "readonly" || config.mode === "dashboard";
87126
87224
  return {
87127
87225
  ...config,
87128
87226
  mode: config.mode || "normal",
87129
87227
  custom: config.custom || {},
87130
87228
  external: this.setupExternalConfig(config.external || {}),
87131
- transportService,
87229
+ transportService: isReadonly
87230
+ ? new ReadonlyTransportFilter(transportService)
87231
+ : transportService,
87132
87232
  client,
87133
87233
  moveClient: () => { },
87134
87234
  snapshotRequested: false,
@@ -87541,6 +87641,7 @@ const components = {
87541
87641
  ChartPanel,
87542
87642
  ChartFigure,
87543
87643
  ChartJsComponent,
87644
+ ClickableCellSortIcon,
87544
87645
  ZoomableChartJsComponent,
87545
87646
  Grid,
87546
87647
  GridOverlay,
@@ -87626,6 +87727,8 @@ const constants = {
87626
87727
  PIVOT_TABLE_CONFIG,
87627
87728
  ChartTerms,
87628
87729
  FIGURE_ID_SPLITTER,
87730
+ GRID_ICON_EDGE_LENGTH,
87731
+ GRID_ICON_MARGIN,
87629
87732
  };
87630
87733
  const chartHelpers = { ...CHART_HELPERS, ...CHART_RUNTIME_HELPERS };
87631
87734
 
@@ -87680,6 +87783,6 @@ exports.tokenColors = tokenColors;
87680
87783
  exports.tokenize = tokenize;
87681
87784
 
87682
87785
 
87683
- __info__.version = "18.5.0-alpha.11";
87684
- __info__.date = "2025-08-26T10:14:05.357Z";
87685
- __info__.hash = "b913e49";
87786
+ __info__.version = "18.5.0-alpha.12";
87787
+ __info__.date = "2025-08-29T08:05:09.767Z";
87788
+ __info__.hash = "8ee7677";