@odoo/o-spreadsheet 18.2.16 → 18.2.17

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.2.16
6
- * @date 2025-06-06T09:32:04.909Z
7
- * @hash 7ee118c
5
+ * @version 18.2.17
6
+ * @date 2025-06-12T09:52:15.050Z
7
+ * @hash ea64209
8
8
  */
9
9
 
10
10
  'use strict';
@@ -5856,7 +5856,9 @@ function isTextFormat(format) {
5856
5856
  }
5857
5857
 
5858
5858
  function evaluateLiteral(literalCell, localeFormat) {
5859
- const value = isTextFormat(localeFormat.format) ? literalCell.content : literalCell.parsedValue;
5859
+ const value = isTextFormat(localeFormat.format) && literalCell.parsedValue !== null
5860
+ ? literalCell.content
5861
+ : literalCell.parsedValue;
5860
5862
  const functionResult = { value, format: localeFormat.format };
5861
5863
  return createEvaluatedCell(functionResult, localeFormat.locale);
5862
5864
  }
@@ -5905,6 +5907,9 @@ function _createEvaluatedCell(functionResult, locale, cell) {
5905
5907
  if (isEvaluationError(value)) {
5906
5908
  return errorCell(value, message);
5907
5909
  }
5910
+ if (value === null) {
5911
+ return emptyCell(format);
5912
+ }
5908
5913
  if (isTextFormat(format)) {
5909
5914
  // TO DO:
5910
5915
  // with the next line, the value of the cell is transformed depending on the format.
@@ -5912,9 +5917,6 @@ function _createEvaluatedCell(functionResult, locale, cell) {
5912
5917
  // to interpret the value as a number.
5913
5918
  return textCell(toString(value), format, formattedValue);
5914
5919
  }
5915
- if (value === null) {
5916
- return emptyCell(format);
5917
- }
5918
5920
  if (typeof value === "number") {
5919
5921
  if (isDateTimeFormat(format || "")) {
5920
5922
  return dateTimeCell(value, format, formattedValue);
@@ -10448,10 +10450,22 @@ function drawPieChartValues(chart, options, ctx) {
10448
10450
  const midAngle = (startAngle + endAngle) / 2;
10449
10451
  const midRadius = (innerRadius + outerRadius) / 2;
10450
10452
  const x = bar.x + midRadius * Math.cos(midAngle);
10451
- const y = bar.y + midRadius * Math.sin(midAngle) + 7;
10453
+ const y = bar.y + midRadius * Math.sin(midAngle);
10454
+ const displayValue = options.callback(value, dataset, i);
10455
+ const textHeight = 12; // ChartJS default
10456
+ const textWidth = computeTextWidth(ctx, displayValue, { fontSize: textHeight }, "px");
10457
+ const radius = outerRadius - innerRadius;
10458
+ // Check if the text fits in the slice. Not perfect, but good enough heuristic.
10459
+ if (textWidth >= radius || radius < textHeight) {
10460
+ continue;
10461
+ }
10462
+ const sliceAngle = endAngle - startAngle;
10463
+ const midWidth = 2 * midRadius * Math.tan(sliceAngle / 2);
10464
+ if (sliceAngle < Math.PI / 2 && (textWidth >= midWidth || midWidth < textHeight)) {
10465
+ continue;
10466
+ }
10452
10467
  ctx.fillStyle = chartFontColor(options.background);
10453
10468
  ctx.strokeStyle = options.background || "#ffffff";
10454
- const displayValue = options.callback(value, dataset, i);
10455
10469
  drawTextWithBackground(displayValue, x, y, ctx);
10456
10470
  }
10457
10471
  }
@@ -26217,7 +26231,7 @@ class XlsxBaseExtractor {
26217
26231
  */
26218
26232
  handleMissingValue(parentElement, missingElementName, optionalArgs) {
26219
26233
  if (optionalArgs?.required) {
26220
- if (optionalArgs?.default) {
26234
+ if (optionalArgs?.default !== undefined) {
26221
26235
  this.warningManager.addParsingWarning(`Missing required ${missingElementName} in element <${parentElement.tagName}> of ${this.currentFile}, replacing it by the default value ${optionalArgs.default}`);
26222
26236
  }
26223
26237
  else {
@@ -33414,19 +33428,26 @@ class FilterMenu extends owl.Component {
33414
33428
  .filter(({ row }) => !this.env.model.getters.isRowHidden(sheetId, row))
33415
33429
  .map(({ col, row }) => this.env.model.getters.getEvaluatedCell({ sheetId, col, row }).formattedValue);
33416
33430
  const filterValues = this.env.model.getters.getFilterHiddenValues({ sheetId, ...position });
33417
- const strValues = [...cellValues, ...filterValues];
33418
- const normalizedFilteredValues = filterValues.map(toLowerCase);
33419
- // Set with lowercase values to avoid duplicates
33420
- const normalizedValues = [...new Set(strValues.map(toLowerCase))];
33421
- const sortedValues = normalizedValues.sort((val1, val2) => val1.localeCompare(val2, undefined, { numeric: true, sensitivity: "base" }));
33422
- return sortedValues.map((normalizedValue) => {
33423
- const checked = normalizedFilteredValues.findIndex((filteredValue) => filteredValue === normalizedValue) ===
33424
- -1;
33425
- return {
33426
- checked,
33427
- string: strValues.find((val) => toLowerCase(val) === normalizedValue) || "",
33428
- };
33429
- });
33431
+ const normalizedFilteredValues = new Set(filterValues.map(toLowerCase));
33432
+ const set = new Set();
33433
+ const values = [];
33434
+ const addValue = (value) => {
33435
+ const normalizedValue = toLowerCase(value);
33436
+ if (!set.has(normalizedValue)) {
33437
+ values.push({
33438
+ string: value || "",
33439
+ checked: !normalizedFilteredValues.has(normalizedValue),
33440
+ normalizedValue,
33441
+ });
33442
+ set.add(normalizedValue);
33443
+ }
33444
+ };
33445
+ cellValues.forEach(addValue);
33446
+ filterValues.forEach(addValue);
33447
+ return values.sort((val1, val2) => val1.normalizedValue.localeCompare(val2.normalizedValue, undefined, {
33448
+ numeric: true,
33449
+ sensitivity: "base",
33450
+ }));
33430
33451
  }
33431
33452
  checkValue(value) {
33432
33453
  this.state.selectedValue = value.string;
@@ -58593,7 +58614,9 @@ class TablePlugin extends CorePlugin {
58593
58614
  const ranges = cmd.ranges.map((rangeData) => this.getters.getRangeFromRangeData(rangeData));
58594
58615
  const union = this.getters.getRangesUnion(ranges);
58595
58616
  const mergesInTarget = this.getters.getMergesInZone(cmd.sheetId, union.zone);
58596
- this.dispatch("REMOVE_MERGE", { sheetId: cmd.sheetId, target: mergesInTarget });
58617
+ if (mergesInTarget.length) {
58618
+ this.dispatch("REMOVE_MERGE", { sheetId: cmd.sheetId, target: mergesInTarget });
58619
+ }
58597
58620
  const id = this.consumeNextId();
58598
58621
  const config = cmd.config || DEFAULT_TABLE_CONFIG;
58599
58622
  const newTable = cmd.tableType === "dynamic"
@@ -58692,14 +58715,16 @@ class TablePlugin extends CorePlugin {
58692
58715
  const zoneToCheckIfEmpty = direction === "down"
58693
58716
  ? { ...zone, bottom: zone.bottom + 1, top: zone.bottom + 1 }
58694
58717
  : { ...zone, right: zone.right + 1, left: zone.right + 1 };
58695
- for (const position of positions(zoneToCheckIfEmpty)) {
58696
- const cellPosition = { sheetId, ...position };
58697
- // Since this plugin is loaded before CellPlugin, the getters still give us the old cell content
58698
- const cellContent = this.getters.getCell(cellPosition)?.content;
58699
- if (cellContent ||
58700
- this.getters.isInMerge(cellPosition) ||
58701
- this.getTablesOverlappingZones(sheetId, [positionToZone(position)]).length) {
58702
- return "none";
58718
+ for (let row = zoneToCheckIfEmpty.top; row <= zoneToCheckIfEmpty.bottom; row++) {
58719
+ for (let col = zoneToCheckIfEmpty.left; col <= zoneToCheckIfEmpty.right; col++) {
58720
+ const cellPosition = { sheetId, col, row };
58721
+ // Since this plugin is loaded before CellPlugin, the getters still give us the old cell content
58722
+ const cellContent = this.getters.getCell(cellPosition)?.content;
58723
+ if (cellContent ||
58724
+ this.getters.isInMerge(cellPosition) ||
58725
+ this.getTablesOverlappingZones(sheetId, [positionToZone(cellPosition)]).length) {
58726
+ return "none";
58727
+ }
58703
58728
  }
58704
58729
  }
58705
58730
  return direction;
@@ -68073,9 +68098,10 @@ class FilterEvaluationPlugin extends UIPlugin {
68073
68098
  const filteredZone = filter.filteredRange?.zone;
68074
68099
  if (!filteredValues || !filteredZone)
68075
68100
  continue;
68101
+ const filteredValuesSet = new Set(filteredValues);
68076
68102
  for (let row = filteredZone.top; row <= filteredZone.bottom; row++) {
68077
68103
  const value = this.getCellValueAsString(sheetId, filter.col, row);
68078
- if (filteredValues.includes(value)) {
68104
+ if (filteredValuesSet.has(value)) {
68079
68105
  hiddenRows.add(row);
68080
68106
  }
68081
68107
  }
@@ -76989,6 +77015,6 @@ exports.tokenColors = tokenColors;
76989
77015
  exports.tokenize = tokenize;
76990
77016
 
76991
77017
 
76992
- __info__.version = "18.2.16";
76993
- __info__.date = "2025-06-06T09:32:04.909Z";
76994
- __info__.hash = "7ee118c";
77018
+ __info__.version = "18.2.17";
77019
+ __info__.date = "2025-06-12T09:52:15.050Z";
77020
+ __info__.hash = "ea64209";
@@ -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.2.16
6
- * @date 2025-06-06T09:32:04.909Z
7
- * @hash 7ee118c
5
+ * @version 18.2.17
6
+ * @date 2025-06-12T09:52:15.050Z
7
+ * @hash ea64209
8
8
  */
9
9
 
10
10
  import { useEnv, useSubEnv, onWillUnmount, useComponent, status, Component, useRef, onMounted, useEffect, App, blockDom, useState, onPatched, onWillPatch, onWillUpdateProps, useExternalListener, onWillStart, xml, useChildSubEnv, markRaw, toRaw } from '@odoo/owl';
@@ -5854,7 +5854,9 @@ function isTextFormat(format) {
5854
5854
  }
5855
5855
 
5856
5856
  function evaluateLiteral(literalCell, localeFormat) {
5857
- const value = isTextFormat(localeFormat.format) ? literalCell.content : literalCell.parsedValue;
5857
+ const value = isTextFormat(localeFormat.format) && literalCell.parsedValue !== null
5858
+ ? literalCell.content
5859
+ : literalCell.parsedValue;
5858
5860
  const functionResult = { value, format: localeFormat.format };
5859
5861
  return createEvaluatedCell(functionResult, localeFormat.locale);
5860
5862
  }
@@ -5903,6 +5905,9 @@ function _createEvaluatedCell(functionResult, locale, cell) {
5903
5905
  if (isEvaluationError(value)) {
5904
5906
  return errorCell(value, message);
5905
5907
  }
5908
+ if (value === null) {
5909
+ return emptyCell(format);
5910
+ }
5906
5911
  if (isTextFormat(format)) {
5907
5912
  // TO DO:
5908
5913
  // with the next line, the value of the cell is transformed depending on the format.
@@ -5910,9 +5915,6 @@ function _createEvaluatedCell(functionResult, locale, cell) {
5910
5915
  // to interpret the value as a number.
5911
5916
  return textCell(toString(value), format, formattedValue);
5912
5917
  }
5913
- if (value === null) {
5914
- return emptyCell(format);
5915
- }
5916
5918
  if (typeof value === "number") {
5917
5919
  if (isDateTimeFormat(format || "")) {
5918
5920
  return dateTimeCell(value, format, formattedValue);
@@ -10446,10 +10448,22 @@ function drawPieChartValues(chart, options, ctx) {
10446
10448
  const midAngle = (startAngle + endAngle) / 2;
10447
10449
  const midRadius = (innerRadius + outerRadius) / 2;
10448
10450
  const x = bar.x + midRadius * Math.cos(midAngle);
10449
- const y = bar.y + midRadius * Math.sin(midAngle) + 7;
10451
+ const y = bar.y + midRadius * Math.sin(midAngle);
10452
+ const displayValue = options.callback(value, dataset, i);
10453
+ const textHeight = 12; // ChartJS default
10454
+ const textWidth = computeTextWidth(ctx, displayValue, { fontSize: textHeight }, "px");
10455
+ const radius = outerRadius - innerRadius;
10456
+ // Check if the text fits in the slice. Not perfect, but good enough heuristic.
10457
+ if (textWidth >= radius || radius < textHeight) {
10458
+ continue;
10459
+ }
10460
+ const sliceAngle = endAngle - startAngle;
10461
+ const midWidth = 2 * midRadius * Math.tan(sliceAngle / 2);
10462
+ if (sliceAngle < Math.PI / 2 && (textWidth >= midWidth || midWidth < textHeight)) {
10463
+ continue;
10464
+ }
10450
10465
  ctx.fillStyle = chartFontColor(options.background);
10451
10466
  ctx.strokeStyle = options.background || "#ffffff";
10452
- const displayValue = options.callback(value, dataset, i);
10453
10467
  drawTextWithBackground(displayValue, x, y, ctx);
10454
10468
  }
10455
10469
  }
@@ -26215,7 +26229,7 @@ class XlsxBaseExtractor {
26215
26229
  */
26216
26230
  handleMissingValue(parentElement, missingElementName, optionalArgs) {
26217
26231
  if (optionalArgs?.required) {
26218
- if (optionalArgs?.default) {
26232
+ if (optionalArgs?.default !== undefined) {
26219
26233
  this.warningManager.addParsingWarning(`Missing required ${missingElementName} in element <${parentElement.tagName}> of ${this.currentFile}, replacing it by the default value ${optionalArgs.default}`);
26220
26234
  }
26221
26235
  else {
@@ -33412,19 +33426,26 @@ class FilterMenu extends Component {
33412
33426
  .filter(({ row }) => !this.env.model.getters.isRowHidden(sheetId, row))
33413
33427
  .map(({ col, row }) => this.env.model.getters.getEvaluatedCell({ sheetId, col, row }).formattedValue);
33414
33428
  const filterValues = this.env.model.getters.getFilterHiddenValues({ sheetId, ...position });
33415
- const strValues = [...cellValues, ...filterValues];
33416
- const normalizedFilteredValues = filterValues.map(toLowerCase);
33417
- // Set with lowercase values to avoid duplicates
33418
- const normalizedValues = [...new Set(strValues.map(toLowerCase))];
33419
- const sortedValues = normalizedValues.sort((val1, val2) => val1.localeCompare(val2, undefined, { numeric: true, sensitivity: "base" }));
33420
- return sortedValues.map((normalizedValue) => {
33421
- const checked = normalizedFilteredValues.findIndex((filteredValue) => filteredValue === normalizedValue) ===
33422
- -1;
33423
- return {
33424
- checked,
33425
- string: strValues.find((val) => toLowerCase(val) === normalizedValue) || "",
33426
- };
33427
- });
33429
+ const normalizedFilteredValues = new Set(filterValues.map(toLowerCase));
33430
+ const set = new Set();
33431
+ const values = [];
33432
+ const addValue = (value) => {
33433
+ const normalizedValue = toLowerCase(value);
33434
+ if (!set.has(normalizedValue)) {
33435
+ values.push({
33436
+ string: value || "",
33437
+ checked: !normalizedFilteredValues.has(normalizedValue),
33438
+ normalizedValue,
33439
+ });
33440
+ set.add(normalizedValue);
33441
+ }
33442
+ };
33443
+ cellValues.forEach(addValue);
33444
+ filterValues.forEach(addValue);
33445
+ return values.sort((val1, val2) => val1.normalizedValue.localeCompare(val2.normalizedValue, undefined, {
33446
+ numeric: true,
33447
+ sensitivity: "base",
33448
+ }));
33428
33449
  }
33429
33450
  checkValue(value) {
33430
33451
  this.state.selectedValue = value.string;
@@ -58591,7 +58612,9 @@ class TablePlugin extends CorePlugin {
58591
58612
  const ranges = cmd.ranges.map((rangeData) => this.getters.getRangeFromRangeData(rangeData));
58592
58613
  const union = this.getters.getRangesUnion(ranges);
58593
58614
  const mergesInTarget = this.getters.getMergesInZone(cmd.sheetId, union.zone);
58594
- this.dispatch("REMOVE_MERGE", { sheetId: cmd.sheetId, target: mergesInTarget });
58615
+ if (mergesInTarget.length) {
58616
+ this.dispatch("REMOVE_MERGE", { sheetId: cmd.sheetId, target: mergesInTarget });
58617
+ }
58595
58618
  const id = this.consumeNextId();
58596
58619
  const config = cmd.config || DEFAULT_TABLE_CONFIG;
58597
58620
  const newTable = cmd.tableType === "dynamic"
@@ -58690,14 +58713,16 @@ class TablePlugin extends CorePlugin {
58690
58713
  const zoneToCheckIfEmpty = direction === "down"
58691
58714
  ? { ...zone, bottom: zone.bottom + 1, top: zone.bottom + 1 }
58692
58715
  : { ...zone, right: zone.right + 1, left: zone.right + 1 };
58693
- for (const position of positions(zoneToCheckIfEmpty)) {
58694
- const cellPosition = { sheetId, ...position };
58695
- // Since this plugin is loaded before CellPlugin, the getters still give us the old cell content
58696
- const cellContent = this.getters.getCell(cellPosition)?.content;
58697
- if (cellContent ||
58698
- this.getters.isInMerge(cellPosition) ||
58699
- this.getTablesOverlappingZones(sheetId, [positionToZone(position)]).length) {
58700
- return "none";
58716
+ for (let row = zoneToCheckIfEmpty.top; row <= zoneToCheckIfEmpty.bottom; row++) {
58717
+ for (let col = zoneToCheckIfEmpty.left; col <= zoneToCheckIfEmpty.right; col++) {
58718
+ const cellPosition = { sheetId, col, row };
58719
+ // Since this plugin is loaded before CellPlugin, the getters still give us the old cell content
58720
+ const cellContent = this.getters.getCell(cellPosition)?.content;
58721
+ if (cellContent ||
58722
+ this.getters.isInMerge(cellPosition) ||
58723
+ this.getTablesOverlappingZones(sheetId, [positionToZone(cellPosition)]).length) {
58724
+ return "none";
58725
+ }
58701
58726
  }
58702
58727
  }
58703
58728
  return direction;
@@ -68071,9 +68096,10 @@ class FilterEvaluationPlugin extends UIPlugin {
68071
68096
  const filteredZone = filter.filteredRange?.zone;
68072
68097
  if (!filteredValues || !filteredZone)
68073
68098
  continue;
68099
+ const filteredValuesSet = new Set(filteredValues);
68074
68100
  for (let row = filteredZone.top; row <= filteredZone.bottom; row++) {
68075
68101
  const value = this.getCellValueAsString(sheetId, filter.col, row);
68076
- if (filteredValues.includes(value)) {
68102
+ if (filteredValuesSet.has(value)) {
68077
68103
  hiddenRows.add(row);
68078
68104
  }
68079
68105
  }
@@ -76942,6 +76968,6 @@ const chartHelpers = { ...CHART_HELPERS, ...CHART_RUNTIME_HELPERS };
76942
76968
  export { AbstractCellClipboardHandler, AbstractChart, AbstractFigureClipboardHandler, CellErrorType, CommandResult, CorePlugin, CoreViewPlugin, 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 };
76943
76969
 
76944
76970
 
76945
- __info__.version = "18.2.16";
76946
- __info__.date = "2025-06-06T09:32:04.909Z";
76947
- __info__.hash = "7ee118c";
76971
+ __info__.version = "18.2.17";
76972
+ __info__.date = "2025-06-12T09:52:15.050Z";
76973
+ __info__.hash = "ea64209";
@@ -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.2.16
6
- * @date 2025-06-06T09:32:04.909Z
7
- * @hash 7ee118c
5
+ * @version 18.2.17
6
+ * @date 2025-06-12T09:52:15.050Z
7
+ * @hash ea64209
8
8
  */
9
9
 
10
10
  (function (exports, owl) {
@@ -5855,7 +5855,9 @@
5855
5855
  }
5856
5856
 
5857
5857
  function evaluateLiteral(literalCell, localeFormat) {
5858
- const value = isTextFormat(localeFormat.format) ? literalCell.content : literalCell.parsedValue;
5858
+ const value = isTextFormat(localeFormat.format) && literalCell.parsedValue !== null
5859
+ ? literalCell.content
5860
+ : literalCell.parsedValue;
5859
5861
  const functionResult = { value, format: localeFormat.format };
5860
5862
  return createEvaluatedCell(functionResult, localeFormat.locale);
5861
5863
  }
@@ -5904,6 +5906,9 @@
5904
5906
  if (isEvaluationError(value)) {
5905
5907
  return errorCell(value, message);
5906
5908
  }
5909
+ if (value === null) {
5910
+ return emptyCell(format);
5911
+ }
5907
5912
  if (isTextFormat(format)) {
5908
5913
  // TO DO:
5909
5914
  // with the next line, the value of the cell is transformed depending on the format.
@@ -5911,9 +5916,6 @@
5911
5916
  // to interpret the value as a number.
5912
5917
  return textCell(toString(value), format, formattedValue);
5913
5918
  }
5914
- if (value === null) {
5915
- return emptyCell(format);
5916
- }
5917
5919
  if (typeof value === "number") {
5918
5920
  if (isDateTimeFormat(format || "")) {
5919
5921
  return dateTimeCell(value, format, formattedValue);
@@ -10447,10 +10449,22 @@ stores.inject(MyMetaStore, storeInstance);
10447
10449
  const midAngle = (startAngle + endAngle) / 2;
10448
10450
  const midRadius = (innerRadius + outerRadius) / 2;
10449
10451
  const x = bar.x + midRadius * Math.cos(midAngle);
10450
- const y = bar.y + midRadius * Math.sin(midAngle) + 7;
10452
+ const y = bar.y + midRadius * Math.sin(midAngle);
10453
+ const displayValue = options.callback(value, dataset, i);
10454
+ const textHeight = 12; // ChartJS default
10455
+ const textWidth = computeTextWidth(ctx, displayValue, { fontSize: textHeight }, "px");
10456
+ const radius = outerRadius - innerRadius;
10457
+ // Check if the text fits in the slice. Not perfect, but good enough heuristic.
10458
+ if (textWidth >= radius || radius < textHeight) {
10459
+ continue;
10460
+ }
10461
+ const sliceAngle = endAngle - startAngle;
10462
+ const midWidth = 2 * midRadius * Math.tan(sliceAngle / 2);
10463
+ if (sliceAngle < Math.PI / 2 && (textWidth >= midWidth || midWidth < textHeight)) {
10464
+ continue;
10465
+ }
10451
10466
  ctx.fillStyle = chartFontColor(options.background);
10452
10467
  ctx.strokeStyle = options.background || "#ffffff";
10453
- const displayValue = options.callback(value, dataset, i);
10454
10468
  drawTextWithBackground(displayValue, x, y, ctx);
10455
10469
  }
10456
10470
  }
@@ -26216,7 +26230,7 @@ stores.inject(MyMetaStore, storeInstance);
26216
26230
  */
26217
26231
  handleMissingValue(parentElement, missingElementName, optionalArgs) {
26218
26232
  if (optionalArgs?.required) {
26219
- if (optionalArgs?.default) {
26233
+ if (optionalArgs?.default !== undefined) {
26220
26234
  this.warningManager.addParsingWarning(`Missing required ${missingElementName} in element <${parentElement.tagName}> of ${this.currentFile}, replacing it by the default value ${optionalArgs.default}`);
26221
26235
  }
26222
26236
  else {
@@ -33413,19 +33427,26 @@ stores.inject(MyMetaStore, storeInstance);
33413
33427
  .filter(({ row }) => !this.env.model.getters.isRowHidden(sheetId, row))
33414
33428
  .map(({ col, row }) => this.env.model.getters.getEvaluatedCell({ sheetId, col, row }).formattedValue);
33415
33429
  const filterValues = this.env.model.getters.getFilterHiddenValues({ sheetId, ...position });
33416
- const strValues = [...cellValues, ...filterValues];
33417
- const normalizedFilteredValues = filterValues.map(toLowerCase);
33418
- // Set with lowercase values to avoid duplicates
33419
- const normalizedValues = [...new Set(strValues.map(toLowerCase))];
33420
- const sortedValues = normalizedValues.sort((val1, val2) => val1.localeCompare(val2, undefined, { numeric: true, sensitivity: "base" }));
33421
- return sortedValues.map((normalizedValue) => {
33422
- const checked = normalizedFilteredValues.findIndex((filteredValue) => filteredValue === normalizedValue) ===
33423
- -1;
33424
- return {
33425
- checked,
33426
- string: strValues.find((val) => toLowerCase(val) === normalizedValue) || "",
33427
- };
33428
- });
33430
+ const normalizedFilteredValues = new Set(filterValues.map(toLowerCase));
33431
+ const set = new Set();
33432
+ const values = [];
33433
+ const addValue = (value) => {
33434
+ const normalizedValue = toLowerCase(value);
33435
+ if (!set.has(normalizedValue)) {
33436
+ values.push({
33437
+ string: value || "",
33438
+ checked: !normalizedFilteredValues.has(normalizedValue),
33439
+ normalizedValue,
33440
+ });
33441
+ set.add(normalizedValue);
33442
+ }
33443
+ };
33444
+ cellValues.forEach(addValue);
33445
+ filterValues.forEach(addValue);
33446
+ return values.sort((val1, val2) => val1.normalizedValue.localeCompare(val2.normalizedValue, undefined, {
33447
+ numeric: true,
33448
+ sensitivity: "base",
33449
+ }));
33429
33450
  }
33430
33451
  checkValue(value) {
33431
33452
  this.state.selectedValue = value.string;
@@ -58592,7 +58613,9 @@ stores.inject(MyMetaStore, storeInstance);
58592
58613
  const ranges = cmd.ranges.map((rangeData) => this.getters.getRangeFromRangeData(rangeData));
58593
58614
  const union = this.getters.getRangesUnion(ranges);
58594
58615
  const mergesInTarget = this.getters.getMergesInZone(cmd.sheetId, union.zone);
58595
- this.dispatch("REMOVE_MERGE", { sheetId: cmd.sheetId, target: mergesInTarget });
58616
+ if (mergesInTarget.length) {
58617
+ this.dispatch("REMOVE_MERGE", { sheetId: cmd.sheetId, target: mergesInTarget });
58618
+ }
58596
58619
  const id = this.consumeNextId();
58597
58620
  const config = cmd.config || DEFAULT_TABLE_CONFIG;
58598
58621
  const newTable = cmd.tableType === "dynamic"
@@ -58691,14 +58714,16 @@ stores.inject(MyMetaStore, storeInstance);
58691
58714
  const zoneToCheckIfEmpty = direction === "down"
58692
58715
  ? { ...zone, bottom: zone.bottom + 1, top: zone.bottom + 1 }
58693
58716
  : { ...zone, right: zone.right + 1, left: zone.right + 1 };
58694
- for (const position of positions(zoneToCheckIfEmpty)) {
58695
- const cellPosition = { sheetId, ...position };
58696
- // Since this plugin is loaded before CellPlugin, the getters still give us the old cell content
58697
- const cellContent = this.getters.getCell(cellPosition)?.content;
58698
- if (cellContent ||
58699
- this.getters.isInMerge(cellPosition) ||
58700
- this.getTablesOverlappingZones(sheetId, [positionToZone(position)]).length) {
58701
- return "none";
58717
+ for (let row = zoneToCheckIfEmpty.top; row <= zoneToCheckIfEmpty.bottom; row++) {
58718
+ for (let col = zoneToCheckIfEmpty.left; col <= zoneToCheckIfEmpty.right; col++) {
58719
+ const cellPosition = { sheetId, col, row };
58720
+ // Since this plugin is loaded before CellPlugin, the getters still give us the old cell content
58721
+ const cellContent = this.getters.getCell(cellPosition)?.content;
58722
+ if (cellContent ||
58723
+ this.getters.isInMerge(cellPosition) ||
58724
+ this.getTablesOverlappingZones(sheetId, [positionToZone(cellPosition)]).length) {
58725
+ return "none";
58726
+ }
58702
58727
  }
58703
58728
  }
58704
58729
  return direction;
@@ -68072,9 +68097,10 @@ stores.inject(MyMetaStore, storeInstance);
68072
68097
  const filteredZone = filter.filteredRange?.zone;
68073
68098
  if (!filteredValues || !filteredZone)
68074
68099
  continue;
68100
+ const filteredValuesSet = new Set(filteredValues);
68075
68101
  for (let row = filteredZone.top; row <= filteredZone.bottom; row++) {
68076
68102
  const value = this.getCellValueAsString(sheetId, filter.col, row);
68077
- if (filteredValues.includes(value)) {
68103
+ if (filteredValuesSet.has(value)) {
68078
68104
  hiddenRows.add(row);
68079
68105
  }
68080
68106
  }
@@ -76988,9 +77014,9 @@ stores.inject(MyMetaStore, storeInstance);
76988
77014
  exports.tokenize = tokenize;
76989
77015
 
76990
77016
 
76991
- __info__.version = "18.2.16";
76992
- __info__.date = "2025-06-06T09:32:04.909Z";
76993
- __info__.hash = "7ee118c";
77017
+ __info__.version = "18.2.17";
77018
+ __info__.date = "2025-06-12T09:52:15.050Z";
77019
+ __info__.hash = "ea64209";
76994
77020
 
76995
77021
 
76996
77022
  })(this.o_spreadsheet = this.o_spreadsheet || {}, owl);