@odoo/o-spreadsheet 18.4.0-alpha.7 → 18.4.0-alpha.8

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.4.0-alpha.7
6
- * @date 2025-06-06T09:32:44.285Z
7
- * @hash 2bfbe64
5
+ * @version 18.4.0-alpha.8
6
+ * @date 2025-06-12T09:53:48.133Z
7
+ * @hash 9b7a8d0
8
8
  */
9
9
 
10
10
  import { useEnv, useSubEnv, onWillUnmount, useComponent, status, Component, useRef, onMounted, useEffect, App, blockDom, useState, onPatched, useExternalListener, onWillUpdateProps, onWillStart, onWillPatch, xml, useChildSubEnv, markRaw, toRaw } from '@odoo/owl';
@@ -130,7 +130,7 @@ class Registry {
130
130
 
131
131
  const CANVAS_SHIFT = 0.5;
132
132
  // Colors
133
- const HIGHLIGHT_COLOR = "#37A850";
133
+ const HIGHLIGHT_COLOR = "#017E84";
134
134
  const BACKGROUND_GRAY_COLOR = "#f5f5f5";
135
135
  const BACKGROUND_HEADER_COLOR = "#F8F9FA";
136
136
  const BACKGROUND_HEADER_SELECTED_COLOR = "#E8EAED";
@@ -143,7 +143,7 @@ const CELL_BORDER_COLOR = "#E2E3E3";
143
143
  const BACKGROUND_CHART_COLOR = "#FFFFFF";
144
144
  const DISABLED_TEXT_COLOR = "#CACACA";
145
145
  const DEFAULT_COLOR_SCALE_MIDPOINT_COLOR = 0xb6d7a8;
146
- const LINK_COLOR = "#017E84";
146
+ const LINK_COLOR = HIGHLIGHT_COLOR;
147
147
  const FILTERS_COLOR = "#188038";
148
148
  const SEPARATOR_COLOR = "#E0E2E4";
149
149
  const ICONS_COLOR = "#4A4F59";
@@ -172,7 +172,7 @@ const BUTTON_HOVER_BG = GRAY_300;
172
172
  const BUTTON_HOVER_TEXT_COLOR = "#111827";
173
173
  const BUTTON_ACTIVE_BG = "#e6f2f3";
174
174
  const BUTTON_ACTIVE_TEXT_COLOR = "#111827";
175
- const ACTION_COLOR = "#017E84";
175
+ const ACTION_COLOR = HIGHLIGHT_COLOR;
176
176
  const ACTION_COLOR_HOVER = "#01585c";
177
177
  const ALERT_WARNING_BG = "#FBEBCC";
178
178
  const ALERT_WARNING_BORDER = "#F8E2B3";
@@ -1566,6 +1566,19 @@ class AlternatingColorGenerator extends ColorGenerator {
1566
1566
  this.palette = getAlternatingColorsPalette(paletteSize).filter((c) => !preferredColors.includes(c));
1567
1567
  }
1568
1568
  }
1569
+ class AlternatingColorMap {
1570
+ availableColors;
1571
+ colors = {};
1572
+ constructor(paletteSize = 12) {
1573
+ this.availableColors = new AlternatingColorGenerator(paletteSize);
1574
+ }
1575
+ get(id) {
1576
+ if (!this.colors[id]) {
1577
+ this.colors[id] = this.availableColors.next();
1578
+ }
1579
+ return this.colors[id];
1580
+ }
1581
+ }
1569
1582
  /**
1570
1583
  * Returns a function that maps a value to a color using a color scale defined by the given
1571
1584
  * color/threshold values pairs.
@@ -5005,7 +5018,9 @@ function isTextFormat(format) {
5005
5018
  }
5006
5019
 
5007
5020
  function evaluateLiteral(literalCell, localeFormat) {
5008
- const value = isTextFormat(localeFormat.format) ? literalCell.content : literalCell.parsedValue;
5021
+ const value = isTextFormat(localeFormat.format) && literalCell.parsedValue !== null
5022
+ ? literalCell.content
5023
+ : literalCell.parsedValue;
5009
5024
  const functionResult = { value, format: localeFormat.format };
5010
5025
  return createEvaluatedCell(functionResult, localeFormat.locale);
5011
5026
  }
@@ -5054,6 +5069,9 @@ function _createEvaluatedCell(functionResult, locale, cell) {
5054
5069
  if (isEvaluationError(value)) {
5055
5070
  return errorCell(value, message);
5056
5071
  }
5072
+ if (value === null) {
5073
+ return emptyCell(format);
5074
+ }
5057
5075
  if (isTextFormat(format)) {
5058
5076
  // TO DO:
5059
5077
  // with the next line, the value of the cell is transformed depending on the format.
@@ -5061,9 +5079,6 @@ function _createEvaluatedCell(functionResult, locale, cell) {
5061
5079
  // to interpret the value as a number.
5062
5080
  return textCell(toString(value), format, formattedValue);
5063
5081
  }
5064
- if (value === null) {
5065
- return emptyCell(format);
5066
- }
5067
5082
  if (typeof value === "number") {
5068
5083
  if (isDateTimeFormat(format || "")) {
5069
5084
  return dateTimeCell(value, format, formattedValue);
@@ -19351,8 +19366,9 @@ const PIVOT = {
19351
19366
  arg("include_total (boolean, default=TRUE)", _t("Whether to include total/sub-totals or not.")),
19352
19367
  arg("include_column_titles (boolean, default=TRUE)", _t("Whether to include the column titles or not.")),
19353
19368
  arg("column_count (number, optional)", _t("number of columns")),
19369
+ arg("include_measure_titles (boolean, default=TRUE)", _t("Whether to include the measure titles row or not.")),
19354
19370
  ],
19355
- compute: function (pivotFormulaId, rowCount = { value: 10000 }, includeTotal = { value: true }, includeColumnHeaders = { value: true }, columnCount = { value: Number.MAX_VALUE }) {
19371
+ compute: function (pivotFormulaId, rowCount = { value: 10000 }, includeTotal = { value: true }, includeColumnHeaders = { value: true }, columnCount = { value: Number.MAX_VALUE }, includeMeasureTitles = { value: true }) {
19356
19372
  const _pivotFormulaId = toString(pivotFormulaId);
19357
19373
  const _rowCount = toNumber(rowCount, this.locale);
19358
19374
  if (_rowCount < 0) {
@@ -19362,8 +19378,11 @@ const PIVOT = {
19362
19378
  if (_columnCount < 0) {
19363
19379
  return new EvaluationError(_t("The number of columns must be positive."));
19364
19380
  }
19365
- const _includeColumnHeaders = toBoolean(includeColumnHeaders);
19366
- const _includedTotal = toBoolean(includeTotal);
19381
+ const visibilityOptions = {
19382
+ displayColumnHeaders: toBoolean(includeColumnHeaders),
19383
+ displayTotals: toBoolean(includeTotal),
19384
+ displayMeasuresRow: toBoolean(includeMeasureTitles),
19385
+ };
19367
19386
  const pivotId = getPivotId(_pivotFormulaId, this.getters);
19368
19387
  const pivot = this.getters.getPivot(pivotId);
19369
19388
  const coreDefinition = this.getters.getPivotCoreDefinition(pivotId);
@@ -19374,9 +19393,15 @@ const PIVOT = {
19374
19393
  return error;
19375
19394
  }
19376
19395
  const table = pivot.getCollapsedTableStructure();
19377
- const cells = table.getPivotCells(_includedTotal, _includeColumnHeaders);
19378
- const headerRows = _includeColumnHeaders ? table.columns.length : 0;
19379
- const pivotTitle = this.getters.getPivotDisplayName(pivotId);
19396
+ const cells = table.getPivotCells(visibilityOptions);
19397
+ let headerRows = 0;
19398
+ if (visibilityOptions.displayColumnHeaders) {
19399
+ headerRows = table.columns.length - 1;
19400
+ }
19401
+ if (visibilityOptions.displayMeasuresRow) {
19402
+ headerRows++;
19403
+ }
19404
+ const pivotTitle = this.getters.getPivotName(pivotId);
19380
19405
  const tableHeight = Math.min(headerRows + _rowCount, cells[0].length);
19381
19406
  if (tableHeight === 0) {
19382
19407
  return [[{ value: pivotTitle }]];
@@ -19404,7 +19429,7 @@ const PIVOT = {
19404
19429
  }
19405
19430
  }
19406
19431
  }
19407
- if (_includeColumnHeaders) {
19432
+ if (visibilityOptions.displayColumnHeaders || visibilityOptions.displayMeasuresRow) {
19408
19433
  result[0][0] = { value: pivotTitle };
19409
19434
  }
19410
19435
  return result;
@@ -21743,10 +21768,22 @@ function drawPieChartValues(chart, options, ctx) {
21743
21768
  const midAngle = (startAngle + endAngle) / 2;
21744
21769
  const midRadius = (innerRadius + outerRadius) / 2;
21745
21770
  const x = bar.x + midRadius * Math.cos(midAngle);
21746
- const y = bar.y + midRadius * Math.sin(midAngle) + 7;
21771
+ const y = bar.y + midRadius * Math.sin(midAngle);
21772
+ const displayValue = options.callback(value, dataset, i);
21773
+ const textHeight = 12; // ChartJS default
21774
+ const textWidth = computeTextWidth(ctx, displayValue, { fontSize: textHeight }, "px");
21775
+ const radius = outerRadius - innerRadius;
21776
+ // Check if the text fits in the slice. Not perfect, but good enough heuristic.
21777
+ if (textWidth >= radius || radius < textHeight) {
21778
+ continue;
21779
+ }
21780
+ const sliceAngle = endAngle - startAngle;
21781
+ const midWidth = 2 * midRadius * Math.tan(sliceAngle / 2);
21782
+ if (sliceAngle < Math.PI / 2 && (textWidth >= midWidth || midWidth < textHeight)) {
21783
+ continue;
21784
+ }
21747
21785
  ctx.fillStyle = chartFontColor(options.background);
21748
21786
  ctx.strokeStyle = options.background || "#ffffff";
21749
- const displayValue = options.callback(value, dataset, i);
21750
21787
  drawTextWithBackground(displayValue, x, y, ctx);
21751
21788
  }
21752
21789
  }
@@ -34939,17 +34976,22 @@ class FilterMenuValueList extends Component {
34939
34976
  static components = { FilterMenuValueItem };
34940
34977
  state = useState({
34941
34978
  values: [],
34979
+ displayedValues: [],
34942
34980
  textFilter: "",
34943
34981
  selectedValue: undefined,
34982
+ numberOfDisplayedValues: 50,
34983
+ hasMoreValues: false,
34944
34984
  });
34945
34985
  searchBar = useRef("filterMenuSearchBar");
34946
34986
  setup() {
34947
34987
  onWillUpdateProps((nextProps) => {
34948
34988
  if (!deepEquals(nextProps.filterPosition, this.props.filterPosition)) {
34949
34989
  this.state.values = this.getFilterHiddenValues(nextProps.filterPosition);
34990
+ this.computeDisplayedValues();
34950
34991
  }
34951
34992
  });
34952
34993
  this.state.values = this.getFilterHiddenValues(this.props.filterPosition);
34994
+ this.computeDisplayedValues();
34953
34995
  }
34954
34996
  getFilterHiddenValues(position) {
34955
34997
  const sheetId = this.env.model.getters.getActiveSheetId();
@@ -34967,21 +35009,28 @@ class FilterMenuValueList extends Component {
34967
35009
  }
34968
35010
  const cellValues = cells.map((val) => val.cellValue);
34969
35011
  const filterValues = filterValue?.filterType === "values" ? filterValue.hiddenValues : [];
34970
- const strValues = [...cellValues, ...filterValues];
34971
- const normalizedFilteredValues = filterValues.map(toLowerCase);
34972
- // Set with lowercase values to avoid duplicates
34973
- const normalizedValues = [...new Set(strValues.map(toLowerCase))];
34974
- const sortedValues = normalizedValues.sort((val1, val2) => val1.localeCompare(val2, undefined, { numeric: true, sensitivity: "base" }));
34975
- return sortedValues.map((normalizedValue) => {
34976
- let checked = false;
34977
- if (filterValue?.filterType !== "criterion") {
34978
- checked = normalizedFilteredValues.findIndex((val) => val === normalizedValue) === -1;
35012
+ const normalizedFilteredValues = new Set(filterValues.map(toLowerCase));
35013
+ const set = new Set();
35014
+ const values = [];
35015
+ const addValue = (value) => {
35016
+ const normalizedValue = toLowerCase(value);
35017
+ if (!set.has(normalizedValue)) {
35018
+ values.push({
35019
+ string: value || "",
35020
+ checked: filterValue?.filterType !== "criterion"
35021
+ ? !normalizedFilteredValues.has(normalizedValue)
35022
+ : false,
35023
+ normalizedValue,
35024
+ });
35025
+ set.add(normalizedValue);
34979
35026
  }
34980
- return {
34981
- checked,
34982
- string: strValues.find((val) => toLowerCase(val) === normalizedValue) || "",
34983
- };
34984
- });
35027
+ };
35028
+ cellValues.forEach(addValue);
35029
+ filterValues.forEach(addValue);
35030
+ return values.sort((val1, val2) => val1.normalizedValue.localeCompare(val2.normalizedValue, undefined, {
35031
+ numeric: true,
35032
+ sensitivity: "base",
35033
+ }));
34985
35034
  }
34986
35035
  checkValue(value) {
34987
35036
  this.state.selectedValue = value.string;
@@ -34993,25 +35042,37 @@ class FilterMenuValueList extends Component {
34993
35042
  this.state.selectedValue = value.string;
34994
35043
  }
34995
35044
  selectAll() {
34996
- this.displayedValues.forEach((value) => (value.checked = true));
34997
- this.updateHiddenValues();
35045
+ this.state.displayedValues.forEach((value) => (value.checked = true));
35046
+ this.props.onUpdateHiddenValues([]);
34998
35047
  }
34999
35048
  clearAll() {
35000
- this.displayedValues.forEach((value) => (value.checked = false));
35001
- this.updateHiddenValues();
35049
+ this.state.displayedValues.forEach((value) => (value.checked = false));
35050
+ const hiddenValues = this.state.values.map((val) => val.string);
35051
+ this.props.onUpdateHiddenValues(hiddenValues);
35002
35052
  }
35003
35053
  updateHiddenValues() {
35004
35054
  const hiddenValues = this.state.values.filter((val) => !val.checked).map((val) => val.string);
35005
35055
  this.props.onUpdateHiddenValues(hiddenValues);
35006
35056
  }
35007
- get displayedValues() {
35008
- if (!this.state.textFilter) {
35009
- return this.state.values;
35010
- }
35011
- return fuzzyLookup(this.state.textFilter, this.state.values, (val) => val.string);
35057
+ updateSearch(ev) {
35058
+ const target = ev.target;
35059
+ this.state.textFilter = target.value;
35060
+ this.state.selectedValue = undefined;
35061
+ this.computeDisplayedValues();
35062
+ }
35063
+ computeDisplayedValues() {
35064
+ const values = !this.state.textFilter
35065
+ ? this.state.values
35066
+ : fuzzyLookup(this.state.textFilter, this.state.values, (val) => val.string);
35067
+ this.state.displayedValues = values.slice(0, this.state.numberOfDisplayedValues);
35068
+ this.state.hasMoreValues = values.length > this.state.numberOfDisplayedValues;
35069
+ }
35070
+ loadMoreValues() {
35071
+ this.state.numberOfDisplayedValues += 100;
35072
+ this.computeDisplayedValues();
35012
35073
  }
35013
35074
  onKeyDown(ev) {
35014
- const displayedValues = this.displayedValues;
35075
+ const displayedValues = this.state.displayedValues;
35015
35076
  if (displayedValues.length === 0)
35016
35077
  return;
35017
35078
  let selectedIndex = undefined;
@@ -38484,7 +38545,7 @@ class XlsxBaseExtractor {
38484
38545
  */
38485
38546
  handleMissingValue(parentElement, missingElementName, optionalArgs) {
38486
38547
  if (optionalArgs?.required) {
38487
- if (optionalArgs?.default) {
38548
+ if (optionalArgs?.default !== undefined) {
38488
38549
  this.warningManager.addParsingWarning(`Missing required ${missingElementName} in element <${parentElement.tagName}> of ${this.currentFile}, replacing it by the default value ${optionalArgs.default}`);
38489
38550
  }
38490
38551
  else {
@@ -48707,7 +48768,8 @@ css /* scss */ `
48707
48768
  input.o-font-size {
48708
48769
  outline: none;
48709
48770
  height: 20px;
48710
- width: 23px;
48771
+ width: 31px;
48772
+ text-align: center;
48711
48773
  }
48712
48774
  }
48713
48775
  .o-text-options > div {
@@ -53785,28 +53847,45 @@ class SpreadsheetPivotTable {
53785
53847
  getNumberOfDataColumns() {
53786
53848
  return this.columns.at(-1)?.length || 0;
53787
53849
  }
53788
- getPivotCells(includeTotal = true, includeColumnHeaders = true) {
53789
- const key = JSON.stringify({ includeTotal, includeColumnHeaders });
53850
+ getSkippedRows(visibilityOptions) {
53851
+ const skippedRows = new Set();
53852
+ if (!visibilityOptions.displayColumnHeaders) {
53853
+ for (let i = 0; i < this.columns.length - 1; i++) {
53854
+ skippedRows.add(i);
53855
+ }
53856
+ }
53857
+ if (!visibilityOptions.displayMeasuresRow) {
53858
+ skippedRows.add(this.columns.length - 1);
53859
+ }
53860
+ return skippedRows;
53861
+ }
53862
+ getPivotCells(visibilityOptions = {
53863
+ displayColumnHeaders: true,
53864
+ displayTotals: true,
53865
+ displayMeasuresRow: true,
53866
+ }) {
53867
+ const key = JSON.stringify(visibilityOptions);
53790
53868
  if (!this.pivotCells[key]) {
53869
+ const { displayTotals } = visibilityOptions;
53791
53870
  const numberOfDataRows = this.rows.length;
53792
53871
  const numberOfDataColumns = this.getNumberOfDataColumns();
53793
53872
  let pivotHeight = this.columns.length + numberOfDataRows;
53794
53873
  let pivotWidth = 1 /*(row headers)*/ + numberOfDataColumns;
53795
- if (!includeTotal && numberOfDataRows !== 1) {
53874
+ if (!displayTotals && numberOfDataRows !== 1) {
53796
53875
  pivotHeight -= 1;
53797
53876
  }
53798
- if (!includeTotal && numberOfDataColumns !== this.measures.length) {
53877
+ if (!displayTotals && numberOfDataColumns !== this.measures.length) {
53799
53878
  pivotWidth -= this.measures.length;
53800
53879
  }
53801
53880
  const domainArray = [];
53802
- const startRow = includeColumnHeaders ? 0 : this.columns.length;
53881
+ const skippedRows = this.getSkippedRows(visibilityOptions);
53803
53882
  for (let col = 0; col < pivotWidth; col++) {
53804
53883
  domainArray.push([]);
53805
- for (let row = startRow; row < pivotHeight; row++) {
53806
- if (!includeTotal && row === pivotHeight) {
53884
+ for (let row = 0; row < pivotHeight; row++) {
53885
+ if (skippedRows.has(row)) {
53807
53886
  continue;
53808
53887
  }
53809
- domainArray[col].push(this.getPivotCell(col, row, includeTotal));
53888
+ domainArray[col].push(this.getPivotCell(col, row, displayTotals));
53810
53889
  }
53811
53890
  }
53812
53891
  this.pivotCells[key] = domainArray;
@@ -61997,7 +62076,9 @@ class TablePlugin extends CorePlugin {
61997
62076
  const ranges = cmd.ranges.map((rangeData) => this.getters.getRangeFromRangeData(rangeData));
61998
62077
  const union = this.getters.getRangesUnion(ranges);
61999
62078
  const mergesInTarget = this.getters.getMergesInZone(cmd.sheetId, union.zone);
62000
- this.dispatch("REMOVE_MERGE", { sheetId: cmd.sheetId, target: mergesInTarget });
62079
+ if (mergesInTarget.length) {
62080
+ this.dispatch("REMOVE_MERGE", { sheetId: cmd.sheetId, target: mergesInTarget });
62081
+ }
62001
62082
  const id = this.consumeNextId();
62002
62083
  const config = cmd.config || DEFAULT_TABLE_CONFIG;
62003
62084
  const newTable = cmd.tableType === "dynamic"
@@ -62096,14 +62177,16 @@ class TablePlugin extends CorePlugin {
62096
62177
  const zoneToCheckIfEmpty = direction === "down"
62097
62178
  ? { ...zone, bottom: zone.bottom + 1, top: zone.bottom + 1 }
62098
62179
  : { ...zone, right: zone.right + 1, left: zone.right + 1 };
62099
- for (const position of positions(zoneToCheckIfEmpty)) {
62100
- const cellPosition = { sheetId, ...position };
62101
- // Since this plugin is loaded before CellPlugin, the getters still give us the old cell content
62102
- const cellContent = this.getters.getCell(cellPosition)?.content;
62103
- if (cellContent ||
62104
- this.getters.isInMerge(cellPosition) ||
62105
- this.getTablesOverlappingZones(sheetId, [positionToZone(position)]).length) {
62106
- return "none";
62180
+ for (let row = zoneToCheckIfEmpty.top; row <= zoneToCheckIfEmpty.bottom; row++) {
62181
+ for (let col = zoneToCheckIfEmpty.left; col <= zoneToCheckIfEmpty.right; col++) {
62182
+ const cellPosition = { sheetId, col, row };
62183
+ // Since this plugin is loaded before CellPlugin, the getters still give us the old cell content
62184
+ const cellContent = this.getters.getCell(cellPosition)?.content;
62185
+ if (cellContent ||
62186
+ this.getters.isInMerge(cellPosition) ||
62187
+ this.getTablesOverlappingZones(sheetId, [positionToZone(cellPosition)]).length) {
62188
+ return "none";
62189
+ }
62107
62190
  }
62108
62191
  }
62109
62192
  return direction;
@@ -67256,10 +67339,15 @@ class PivotUIPlugin extends CoreViewPlugin {
67256
67339
  const includeTotal = toScalar(args[2]);
67257
67340
  const shouldIncludeTotal = includeTotal === undefined ? true : toBoolean(includeTotal);
67258
67341
  const includeColumnHeaders = toScalar(args[3]);
67342
+ const includeMeasures = toScalar(args[5]);
67343
+ const shouldIncludeMeasures = includeMeasures === undefined ? true : toBoolean(includeMeasures);
67259
67344
  const shouldIncludeColumnHeaders = includeColumnHeaders === undefined ? true : toBoolean(includeColumnHeaders);
67260
- const pivotCells = pivot
67261
- .getCollapsedTableStructure()
67262
- .getPivotCells(shouldIncludeTotal, shouldIncludeColumnHeaders);
67345
+ const visibilityOptions = {
67346
+ displayColumnHeaders: shouldIncludeColumnHeaders,
67347
+ displayTotals: shouldIncludeTotal,
67348
+ displayMeasuresRow: shouldIncludeMeasures,
67349
+ };
67350
+ const pivotCells = pivot.getCollapsedTableStructure().getPivotCells(visibilityOptions);
67263
67351
  const pivotCol = position.col - mainPosition.col;
67264
67352
  const pivotRow = position.row - mainPosition.row;
67265
67353
  return pivotCells[pivotCol][pivotRow];
@@ -69481,8 +69569,7 @@ class CollaborativePlugin extends UIPlugin {
69481
69569
  "isFullySynchronized",
69482
69570
  ];
69483
69571
  static layers = ["Selection"];
69484
- availableColors = new AlternatingColorGenerator(12);
69485
- colors = {};
69572
+ colors = new AlternatingColorMap(12);
69486
69573
  session;
69487
69574
  constructor(config) {
69488
69575
  super(config);
@@ -69500,7 +69587,7 @@ class CollaborativePlugin extends UIPlugin {
69500
69587
  }
69501
69588
  getConnectedClients() {
69502
69589
  return [...this.session.getConnectedClients()].map((client) => {
69503
- return { ...client, color: this.colors[client.id] };
69590
+ return { ...client, color: this.colors.get(client.id) };
69504
69591
  });
69505
69592
  }
69506
69593
  isFullySynchronized() {
@@ -69529,10 +69616,7 @@ class CollaborativePlugin extends UIPlugin {
69529
69616
  client.position &&
69530
69617
  client.position.sheetId === sheetId &&
69531
69618
  this.isPositionValid(client.position)) {
69532
- if (!this.colors[client.id]) {
69533
- this.colors[client.id] = this.availableColors.next();
69534
- }
69535
- clients.push({ ...client, color: this.colors[client.id], position: client.position });
69619
+ clients.push({ ...client, position: client.position });
69536
69620
  }
69537
69621
  }
69538
69622
  return clients;
@@ -72406,9 +72490,10 @@ class FilterEvaluationPlugin extends UIPlugin {
72406
72490
  const filteredValues = filterValue.hiddenValues?.map(toLowerCase);
72407
72491
  if (!filteredValues)
72408
72492
  continue;
72493
+ const filteredValuesSet = new Set(filteredValues);
72409
72494
  for (let row = filteredZone.top; row <= filteredZone.bottom; row++) {
72410
72495
  const value = this.getCellValueAsString(sheetId, filter.col, row);
72411
- if (filteredValues.includes(value)) {
72496
+ if (filteredValuesSet.has(value)) {
72412
72497
  hiddenRows.add(row);
72413
72498
  }
72414
72499
  }
@@ -75090,7 +75175,7 @@ topbarMenuRegistry
75090
75175
  })
75091
75176
  .addChild("settings", ["file"], {
75092
75177
  name: _t("Settings"),
75093
- sequence: 100,
75178
+ sequence: 200,
75094
75179
  execute: (env) => env.openSidePanel("Settings"),
75095
75180
  isEnabled: (env) => !env.isSmall,
75096
75181
  icon: "o-spreadsheet-Icon.COG",
@@ -77999,7 +78084,7 @@ css /* scss */ `
77999
78084
 
78000
78085
  .o-spreadsheet-topbar {
78001
78086
  line-height: 1.2;
78002
- font-size: 13px;
78087
+ font-size: 14px;
78003
78088
  font-weight: 500;
78004
78089
  background-color: #fff;
78005
78090
 
@@ -83254,9 +83339,9 @@ const constants = {
83254
83339
  };
83255
83340
  const chartHelpers = { ...CHART_HELPERS, ...CHART_RUNTIME_HELPERS };
83256
83341
 
83257
- export { AbstractCellClipboardHandler, AbstractChart, AbstractFigureClipboardHandler, CellErrorType, CommandResult, CorePlugin, CoreViewPlugin, DispatchResult, EvaluationError, LocalTransportService, 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, invalidateChartEvaluationCommands, invalidateDependenciesCommands, invalidateEvaluationCommands, iterateAstNodes, links, load, parse, parseTokens, readonlyAllowedCommands, registries, setDefaultSheetViewSize, setTranslationMethod, stores, tokenColors, tokenize };
83342
+ export { AbstractCellClipboardHandler, AbstractChart, AbstractFigureClipboardHandler, CellErrorType, ClientDisconnectedError, CommandResult, CorePlugin, CoreViewPlugin, DispatchResult, EvaluationError, LocalTransportService, 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, invalidateChartEvaluationCommands, invalidateDependenciesCommands, invalidateEvaluationCommands, iterateAstNodes, links, load, parse, parseTokens, readonlyAllowedCommands, registries, setDefaultSheetViewSize, setTranslationMethod, stores, tokenColors, tokenize };
83258
83343
 
83259
83344
 
83260
- __info__.version = "18.4.0-alpha.7";
83261
- __info__.date = "2025-06-06T09:32:44.285Z";
83262
- __info__.hash = "2bfbe64";
83345
+ __info__.version = "18.4.0-alpha.8";
83346
+ __info__.date = "2025-06-12T09:53:48.133Z";
83347
+ __info__.hash = "9b7a8d0";