@odoo/o-spreadsheet 18.3.7 → 18.3.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.3.7
6
- * @date 2025-06-06T09:31:27.123Z
7
- * @hash 05333f1
5
+ * @version 18.3.8
6
+ * @date 2025-06-12T09:51:55.929Z
7
+ * @hash 32dedd1
8
8
  */
9
9
 
10
10
  'use strict';
@@ -5882,7 +5882,9 @@ function isTextFormat(format) {
5882
5882
  }
5883
5883
 
5884
5884
  function evaluateLiteral(literalCell, localeFormat) {
5885
- const value = isTextFormat(localeFormat.format) ? literalCell.content : literalCell.parsedValue;
5885
+ const value = isTextFormat(localeFormat.format) && literalCell.parsedValue !== null
5886
+ ? literalCell.content
5887
+ : literalCell.parsedValue;
5886
5888
  const functionResult = { value, format: localeFormat.format };
5887
5889
  return createEvaluatedCell(functionResult, localeFormat.locale);
5888
5890
  }
@@ -5931,6 +5933,9 @@ function _createEvaluatedCell(functionResult, locale, cell) {
5931
5933
  if (isEvaluationError(value)) {
5932
5934
  return errorCell(value, message);
5933
5935
  }
5936
+ if (value === null) {
5937
+ return emptyCell(format);
5938
+ }
5934
5939
  if (isTextFormat(format)) {
5935
5940
  // TO DO:
5936
5941
  // with the next line, the value of the cell is transformed depending on the format.
@@ -5938,9 +5943,6 @@ function _createEvaluatedCell(functionResult, locale, cell) {
5938
5943
  // to interpret the value as a number.
5939
5944
  return textCell(toString(value), format, formattedValue);
5940
5945
  }
5941
- if (value === null) {
5942
- return emptyCell(format);
5943
- }
5944
5946
  if (typeof value === "number") {
5945
5947
  if (isDateTimeFormat(format || "")) {
5946
5948
  return dateTimeCell(value, format, formattedValue);
@@ -21033,10 +21035,22 @@ function drawPieChartValues(chart, options, ctx) {
21033
21035
  const midAngle = (startAngle + endAngle) / 2;
21034
21036
  const midRadius = (innerRadius + outerRadius) / 2;
21035
21037
  const x = bar.x + midRadius * Math.cos(midAngle);
21036
- const y = bar.y + midRadius * Math.sin(midAngle) + 7;
21038
+ const y = bar.y + midRadius * Math.sin(midAngle);
21039
+ const displayValue = options.callback(value, dataset, i);
21040
+ const textHeight = 12; // ChartJS default
21041
+ const textWidth = computeTextWidth(ctx, displayValue, { fontSize: textHeight }, "px");
21042
+ const radius = outerRadius - innerRadius;
21043
+ // Check if the text fits in the slice. Not perfect, but good enough heuristic.
21044
+ if (textWidth >= radius || radius < textHeight) {
21045
+ continue;
21046
+ }
21047
+ const sliceAngle = endAngle - startAngle;
21048
+ const midWidth = 2 * midRadius * Math.tan(sliceAngle / 2);
21049
+ if (sliceAngle < Math.PI / 2 && (textWidth >= midWidth || midWidth < textHeight)) {
21050
+ continue;
21051
+ }
21037
21052
  ctx.fillStyle = chartFontColor(options.background);
21038
21053
  ctx.strokeStyle = options.background || "#ffffff";
21039
- const displayValue = options.callback(value, dataset, i);
21040
21054
  drawTextWithBackground(displayValue, x, y, ctx);
21041
21055
  }
21042
21056
  }
@@ -33172,7 +33186,7 @@ class XlsxBaseExtractor {
33172
33186
  */
33173
33187
  handleMissingValue(parentElement, missingElementName, optionalArgs) {
33174
33188
  if (optionalArgs?.required) {
33175
- if (optionalArgs?.default) {
33189
+ if (optionalArgs?.default !== undefined) {
33176
33190
  this.warningManager.addParsingWarning(`Missing required ${missingElementName} in element <${parentElement.tagName}> of ${this.currentFile}, replacing it by the default value ${optionalArgs.default}`);
33177
33191
  }
33178
33192
  else {
@@ -36125,19 +36139,26 @@ class FilterMenu extends owl.Component {
36125
36139
  .filter(({ row }) => !this.env.model.getters.isRowHidden(sheetId, row))
36126
36140
  .map(({ col, row }) => this.env.model.getters.getEvaluatedCell({ sheetId, col, row }).formattedValue);
36127
36141
  const filterValues = this.env.model.getters.getFilterHiddenValues({ sheetId, ...position });
36128
- const strValues = [...cellValues, ...filterValues];
36129
- const normalizedFilteredValues = filterValues.map(toLowerCase);
36130
- // Set with lowercase values to avoid duplicates
36131
- const normalizedValues = [...new Set(strValues.map(toLowerCase))];
36132
- const sortedValues = normalizedValues.sort((val1, val2) => val1.localeCompare(val2, undefined, { numeric: true, sensitivity: "base" }));
36133
- return sortedValues.map((normalizedValue) => {
36134
- const checked = normalizedFilteredValues.findIndex((filteredValue) => filteredValue === normalizedValue) ===
36135
- -1;
36136
- return {
36137
- checked,
36138
- string: strValues.find((val) => toLowerCase(val) === normalizedValue) || "",
36139
- };
36140
- });
36142
+ const normalizedFilteredValues = new Set(filterValues.map(toLowerCase));
36143
+ const set = new Set();
36144
+ const values = [];
36145
+ const addValue = (value) => {
36146
+ const normalizedValue = toLowerCase(value);
36147
+ if (!set.has(normalizedValue)) {
36148
+ values.push({
36149
+ string: value || "",
36150
+ checked: !normalizedFilteredValues.has(normalizedValue),
36151
+ normalizedValue,
36152
+ });
36153
+ set.add(normalizedValue);
36154
+ }
36155
+ };
36156
+ cellValues.forEach(addValue);
36157
+ filterValues.forEach(addValue);
36158
+ return values.sort((val1, val2) => val1.normalizedValue.localeCompare(val2.normalizedValue, undefined, {
36159
+ numeric: true,
36160
+ sensitivity: "base",
36161
+ }));
36141
36162
  }
36142
36163
  checkValue(value) {
36143
36164
  this.state.selectedValue = value.string;
@@ -61478,7 +61499,9 @@ class TablePlugin extends CorePlugin {
61478
61499
  const ranges = cmd.ranges.map((rangeData) => this.getters.getRangeFromRangeData(rangeData));
61479
61500
  const union = this.getters.getRangesUnion(ranges);
61480
61501
  const mergesInTarget = this.getters.getMergesInZone(cmd.sheetId, union.zone);
61481
- this.dispatch("REMOVE_MERGE", { sheetId: cmd.sheetId, target: mergesInTarget });
61502
+ if (mergesInTarget.length) {
61503
+ this.dispatch("REMOVE_MERGE", { sheetId: cmd.sheetId, target: mergesInTarget });
61504
+ }
61482
61505
  const id = this.consumeNextId();
61483
61506
  const config = cmd.config || DEFAULT_TABLE_CONFIG;
61484
61507
  const newTable = cmd.tableType === "dynamic"
@@ -61577,14 +61600,16 @@ class TablePlugin extends CorePlugin {
61577
61600
  const zoneToCheckIfEmpty = direction === "down"
61578
61601
  ? { ...zone, bottom: zone.bottom + 1, top: zone.bottom + 1 }
61579
61602
  : { ...zone, right: zone.right + 1, left: zone.right + 1 };
61580
- for (const position of positions(zoneToCheckIfEmpty)) {
61581
- const cellPosition = { sheetId, ...position };
61582
- // Since this plugin is loaded before CellPlugin, the getters still give us the old cell content
61583
- const cellContent = this.getters.getCell(cellPosition)?.content;
61584
- if (cellContent ||
61585
- this.getters.isInMerge(cellPosition) ||
61586
- this.getTablesOverlappingZones(sheetId, [positionToZone(position)]).length) {
61587
- return "none";
61603
+ for (let row = zoneToCheckIfEmpty.top; row <= zoneToCheckIfEmpty.bottom; row++) {
61604
+ for (let col = zoneToCheckIfEmpty.left; col <= zoneToCheckIfEmpty.right; col++) {
61605
+ const cellPosition = { sheetId, col, row };
61606
+ // Since this plugin is loaded before CellPlugin, the getters still give us the old cell content
61607
+ const cellContent = this.getters.getCell(cellPosition)?.content;
61608
+ if (cellContent ||
61609
+ this.getters.isInMerge(cellPosition) ||
61610
+ this.getTablesOverlappingZones(sheetId, [positionToZone(cellPosition)]).length) {
61611
+ return "none";
61612
+ }
61588
61613
  }
61589
61614
  }
61590
61615
  return direction;
@@ -71210,9 +71235,10 @@ class FilterEvaluationPlugin extends UIPlugin {
71210
71235
  const filteredZone = filter.filteredRange?.zone;
71211
71236
  if (!filteredValues || !filteredZone)
71212
71237
  continue;
71238
+ const filteredValuesSet = new Set(filteredValues);
71213
71239
  for (let row = filteredZone.top; row <= filteredZone.bottom; row++) {
71214
71240
  const value = this.getCellValueAsString(sheetId, filter.col, row);
71215
- if (filteredValues.includes(value)) {
71241
+ if (filteredValuesSet.has(value)) {
71216
71242
  hiddenRows.add(row);
71217
71243
  }
71218
71244
  }
@@ -80642,6 +80668,6 @@ exports.tokenColors = tokenColors;
80642
80668
  exports.tokenize = tokenize;
80643
80669
 
80644
80670
 
80645
- __info__.version = "18.3.7";
80646
- __info__.date = "2025-06-06T09:31:27.123Z";
80647
- __info__.hash = "05333f1";
80671
+ __info__.version = "18.3.8";
80672
+ __info__.date = "2025-06-12T09:51:55.929Z";
80673
+ __info__.hash = "32dedd1";
@@ -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.7
6
- * @date 2025-06-06T09:31:27.123Z
7
- * @hash 05333f1
5
+ * @version 18.3.8
6
+ * @date 2025-06-12T09:51:55.929Z
7
+ * @hash 32dedd1
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';
@@ -5880,7 +5880,9 @@ function isTextFormat(format) {
5880
5880
  }
5881
5881
 
5882
5882
  function evaluateLiteral(literalCell, localeFormat) {
5883
- const value = isTextFormat(localeFormat.format) ? literalCell.content : literalCell.parsedValue;
5883
+ const value = isTextFormat(localeFormat.format) && literalCell.parsedValue !== null
5884
+ ? literalCell.content
5885
+ : literalCell.parsedValue;
5884
5886
  const functionResult = { value, format: localeFormat.format };
5885
5887
  return createEvaluatedCell(functionResult, localeFormat.locale);
5886
5888
  }
@@ -5929,6 +5931,9 @@ function _createEvaluatedCell(functionResult, locale, cell) {
5929
5931
  if (isEvaluationError(value)) {
5930
5932
  return errorCell(value, message);
5931
5933
  }
5934
+ if (value === null) {
5935
+ return emptyCell(format);
5936
+ }
5932
5937
  if (isTextFormat(format)) {
5933
5938
  // TO DO:
5934
5939
  // with the next line, the value of the cell is transformed depending on the format.
@@ -5936,9 +5941,6 @@ function _createEvaluatedCell(functionResult, locale, cell) {
5936
5941
  // to interpret the value as a number.
5937
5942
  return textCell(toString(value), format, formattedValue);
5938
5943
  }
5939
- if (value === null) {
5940
- return emptyCell(format);
5941
- }
5942
5944
  if (typeof value === "number") {
5943
5945
  if (isDateTimeFormat(format || "")) {
5944
5946
  return dateTimeCell(value, format, formattedValue);
@@ -21031,10 +21033,22 @@ function drawPieChartValues(chart, options, ctx) {
21031
21033
  const midAngle = (startAngle + endAngle) / 2;
21032
21034
  const midRadius = (innerRadius + outerRadius) / 2;
21033
21035
  const x = bar.x + midRadius * Math.cos(midAngle);
21034
- const y = bar.y + midRadius * Math.sin(midAngle) + 7;
21036
+ const y = bar.y + midRadius * Math.sin(midAngle);
21037
+ const displayValue = options.callback(value, dataset, i);
21038
+ const textHeight = 12; // ChartJS default
21039
+ const textWidth = computeTextWidth(ctx, displayValue, { fontSize: textHeight }, "px");
21040
+ const radius = outerRadius - innerRadius;
21041
+ // Check if the text fits in the slice. Not perfect, but good enough heuristic.
21042
+ if (textWidth >= radius || radius < textHeight) {
21043
+ continue;
21044
+ }
21045
+ const sliceAngle = endAngle - startAngle;
21046
+ const midWidth = 2 * midRadius * Math.tan(sliceAngle / 2);
21047
+ if (sliceAngle < Math.PI / 2 && (textWidth >= midWidth || midWidth < textHeight)) {
21048
+ continue;
21049
+ }
21035
21050
  ctx.fillStyle = chartFontColor(options.background);
21036
21051
  ctx.strokeStyle = options.background || "#ffffff";
21037
- const displayValue = options.callback(value, dataset, i);
21038
21052
  drawTextWithBackground(displayValue, x, y, ctx);
21039
21053
  }
21040
21054
  }
@@ -33170,7 +33184,7 @@ class XlsxBaseExtractor {
33170
33184
  */
33171
33185
  handleMissingValue(parentElement, missingElementName, optionalArgs) {
33172
33186
  if (optionalArgs?.required) {
33173
- if (optionalArgs?.default) {
33187
+ if (optionalArgs?.default !== undefined) {
33174
33188
  this.warningManager.addParsingWarning(`Missing required ${missingElementName} in element <${parentElement.tagName}> of ${this.currentFile}, replacing it by the default value ${optionalArgs.default}`);
33175
33189
  }
33176
33190
  else {
@@ -36123,19 +36137,26 @@ class FilterMenu extends Component {
36123
36137
  .filter(({ row }) => !this.env.model.getters.isRowHidden(sheetId, row))
36124
36138
  .map(({ col, row }) => this.env.model.getters.getEvaluatedCell({ sheetId, col, row }).formattedValue);
36125
36139
  const filterValues = this.env.model.getters.getFilterHiddenValues({ sheetId, ...position });
36126
- const strValues = [...cellValues, ...filterValues];
36127
- const normalizedFilteredValues = filterValues.map(toLowerCase);
36128
- // Set with lowercase values to avoid duplicates
36129
- const normalizedValues = [...new Set(strValues.map(toLowerCase))];
36130
- const sortedValues = normalizedValues.sort((val1, val2) => val1.localeCompare(val2, undefined, { numeric: true, sensitivity: "base" }));
36131
- return sortedValues.map((normalizedValue) => {
36132
- const checked = normalizedFilteredValues.findIndex((filteredValue) => filteredValue === normalizedValue) ===
36133
- -1;
36134
- return {
36135
- checked,
36136
- string: strValues.find((val) => toLowerCase(val) === normalizedValue) || "",
36137
- };
36138
- });
36140
+ const normalizedFilteredValues = new Set(filterValues.map(toLowerCase));
36141
+ const set = new Set();
36142
+ const values = [];
36143
+ const addValue = (value) => {
36144
+ const normalizedValue = toLowerCase(value);
36145
+ if (!set.has(normalizedValue)) {
36146
+ values.push({
36147
+ string: value || "",
36148
+ checked: !normalizedFilteredValues.has(normalizedValue),
36149
+ normalizedValue,
36150
+ });
36151
+ set.add(normalizedValue);
36152
+ }
36153
+ };
36154
+ cellValues.forEach(addValue);
36155
+ filterValues.forEach(addValue);
36156
+ return values.sort((val1, val2) => val1.normalizedValue.localeCompare(val2.normalizedValue, undefined, {
36157
+ numeric: true,
36158
+ sensitivity: "base",
36159
+ }));
36139
36160
  }
36140
36161
  checkValue(value) {
36141
36162
  this.state.selectedValue = value.string;
@@ -61476,7 +61497,9 @@ class TablePlugin extends CorePlugin {
61476
61497
  const ranges = cmd.ranges.map((rangeData) => this.getters.getRangeFromRangeData(rangeData));
61477
61498
  const union = this.getters.getRangesUnion(ranges);
61478
61499
  const mergesInTarget = this.getters.getMergesInZone(cmd.sheetId, union.zone);
61479
- this.dispatch("REMOVE_MERGE", { sheetId: cmd.sheetId, target: mergesInTarget });
61500
+ if (mergesInTarget.length) {
61501
+ this.dispatch("REMOVE_MERGE", { sheetId: cmd.sheetId, target: mergesInTarget });
61502
+ }
61480
61503
  const id = this.consumeNextId();
61481
61504
  const config = cmd.config || DEFAULT_TABLE_CONFIG;
61482
61505
  const newTable = cmd.tableType === "dynamic"
@@ -61575,14 +61598,16 @@ class TablePlugin extends CorePlugin {
61575
61598
  const zoneToCheckIfEmpty = direction === "down"
61576
61599
  ? { ...zone, bottom: zone.bottom + 1, top: zone.bottom + 1 }
61577
61600
  : { ...zone, right: zone.right + 1, left: zone.right + 1 };
61578
- for (const position of positions(zoneToCheckIfEmpty)) {
61579
- const cellPosition = { sheetId, ...position };
61580
- // Since this plugin is loaded before CellPlugin, the getters still give us the old cell content
61581
- const cellContent = this.getters.getCell(cellPosition)?.content;
61582
- if (cellContent ||
61583
- this.getters.isInMerge(cellPosition) ||
61584
- this.getTablesOverlappingZones(sheetId, [positionToZone(position)]).length) {
61585
- return "none";
61601
+ for (let row = zoneToCheckIfEmpty.top; row <= zoneToCheckIfEmpty.bottom; row++) {
61602
+ for (let col = zoneToCheckIfEmpty.left; col <= zoneToCheckIfEmpty.right; col++) {
61603
+ const cellPosition = { sheetId, col, row };
61604
+ // Since this plugin is loaded before CellPlugin, the getters still give us the old cell content
61605
+ const cellContent = this.getters.getCell(cellPosition)?.content;
61606
+ if (cellContent ||
61607
+ this.getters.isInMerge(cellPosition) ||
61608
+ this.getTablesOverlappingZones(sheetId, [positionToZone(cellPosition)]).length) {
61609
+ return "none";
61610
+ }
61586
61611
  }
61587
61612
  }
61588
61613
  return direction;
@@ -71208,9 +71233,10 @@ class FilterEvaluationPlugin extends UIPlugin {
71208
71233
  const filteredZone = filter.filteredRange?.zone;
71209
71234
  if (!filteredValues || !filteredZone)
71210
71235
  continue;
71236
+ const filteredValuesSet = new Set(filteredValues);
71211
71237
  for (let row = filteredZone.top; row <= filteredZone.bottom; row++) {
71212
71238
  const value = this.getCellValueAsString(sheetId, filter.col, row);
71213
- if (filteredValues.includes(value)) {
71239
+ if (filteredValuesSet.has(value)) {
71214
71240
  hiddenRows.add(row);
71215
71241
  }
71216
71242
  }
@@ -80594,6 +80620,6 @@ const chartHelpers = { ...CHART_HELPERS, ...CHART_RUNTIME_HELPERS };
80594
80620
  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, invalidateChartEvaluationCommands, invalidateDependenciesCommands, invalidateEvaluationCommands, iterateAstNodes, links, load, parse, parseTokens, readonlyAllowedCommands, registries, setDefaultSheetViewSize, setTranslationMethod, stores, tokenColors, tokenize };
80595
80621
 
80596
80622
 
80597
- __info__.version = "18.3.7";
80598
- __info__.date = "2025-06-06T09:31:27.123Z";
80599
- __info__.hash = "05333f1";
80623
+ __info__.version = "18.3.8";
80624
+ __info__.date = "2025-06-12T09:51:55.929Z";
80625
+ __info__.hash = "32dedd1";
@@ -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.7
6
- * @date 2025-06-06T09:31:27.123Z
7
- * @hash 05333f1
5
+ * @version 18.3.8
6
+ * @date 2025-06-12T09:51:55.929Z
7
+ * @hash 32dedd1
8
8
  */
9
9
 
10
10
  (function (exports, owl) {
@@ -5881,7 +5881,9 @@
5881
5881
  }
5882
5882
 
5883
5883
  function evaluateLiteral(literalCell, localeFormat) {
5884
- const value = isTextFormat(localeFormat.format) ? literalCell.content : literalCell.parsedValue;
5884
+ const value = isTextFormat(localeFormat.format) && literalCell.parsedValue !== null
5885
+ ? literalCell.content
5886
+ : literalCell.parsedValue;
5885
5887
  const functionResult = { value, format: localeFormat.format };
5886
5888
  return createEvaluatedCell(functionResult, localeFormat.locale);
5887
5889
  }
@@ -5930,6 +5932,9 @@
5930
5932
  if (isEvaluationError(value)) {
5931
5933
  return errorCell(value, message);
5932
5934
  }
5935
+ if (value === null) {
5936
+ return emptyCell(format);
5937
+ }
5933
5938
  if (isTextFormat(format)) {
5934
5939
  // TO DO:
5935
5940
  // with the next line, the value of the cell is transformed depending on the format.
@@ -5937,9 +5942,6 @@
5937
5942
  // to interpret the value as a number.
5938
5943
  return textCell(toString(value), format, formattedValue);
5939
5944
  }
5940
- if (value === null) {
5941
- return emptyCell(format);
5942
- }
5943
5945
  if (typeof value === "number") {
5944
5946
  if (isDateTimeFormat(format || "")) {
5945
5947
  return dateTimeCell(value, format, formattedValue);
@@ -21032,10 +21034,22 @@ stores.inject(MyMetaStore, storeInstance);
21032
21034
  const midAngle = (startAngle + endAngle) / 2;
21033
21035
  const midRadius = (innerRadius + outerRadius) / 2;
21034
21036
  const x = bar.x + midRadius * Math.cos(midAngle);
21035
- const y = bar.y + midRadius * Math.sin(midAngle) + 7;
21037
+ const y = bar.y + midRadius * Math.sin(midAngle);
21038
+ const displayValue = options.callback(value, dataset, i);
21039
+ const textHeight = 12; // ChartJS default
21040
+ const textWidth = computeTextWidth(ctx, displayValue, { fontSize: textHeight }, "px");
21041
+ const radius = outerRadius - innerRadius;
21042
+ // Check if the text fits in the slice. Not perfect, but good enough heuristic.
21043
+ if (textWidth >= radius || radius < textHeight) {
21044
+ continue;
21045
+ }
21046
+ const sliceAngle = endAngle - startAngle;
21047
+ const midWidth = 2 * midRadius * Math.tan(sliceAngle / 2);
21048
+ if (sliceAngle < Math.PI / 2 && (textWidth >= midWidth || midWidth < textHeight)) {
21049
+ continue;
21050
+ }
21036
21051
  ctx.fillStyle = chartFontColor(options.background);
21037
21052
  ctx.strokeStyle = options.background || "#ffffff";
21038
- const displayValue = options.callback(value, dataset, i);
21039
21053
  drawTextWithBackground(displayValue, x, y, ctx);
21040
21054
  }
21041
21055
  }
@@ -33171,7 +33185,7 @@ stores.inject(MyMetaStore, storeInstance);
33171
33185
  */
33172
33186
  handleMissingValue(parentElement, missingElementName, optionalArgs) {
33173
33187
  if (optionalArgs?.required) {
33174
- if (optionalArgs?.default) {
33188
+ if (optionalArgs?.default !== undefined) {
33175
33189
  this.warningManager.addParsingWarning(`Missing required ${missingElementName} in element <${parentElement.tagName}> of ${this.currentFile}, replacing it by the default value ${optionalArgs.default}`);
33176
33190
  }
33177
33191
  else {
@@ -36124,19 +36138,26 @@ stores.inject(MyMetaStore, storeInstance);
36124
36138
  .filter(({ row }) => !this.env.model.getters.isRowHidden(sheetId, row))
36125
36139
  .map(({ col, row }) => this.env.model.getters.getEvaluatedCell({ sheetId, col, row }).formattedValue);
36126
36140
  const filterValues = this.env.model.getters.getFilterHiddenValues({ sheetId, ...position });
36127
- const strValues = [...cellValues, ...filterValues];
36128
- const normalizedFilteredValues = filterValues.map(toLowerCase);
36129
- // Set with lowercase values to avoid duplicates
36130
- const normalizedValues = [...new Set(strValues.map(toLowerCase))];
36131
- const sortedValues = normalizedValues.sort((val1, val2) => val1.localeCompare(val2, undefined, { numeric: true, sensitivity: "base" }));
36132
- return sortedValues.map((normalizedValue) => {
36133
- const checked = normalizedFilteredValues.findIndex((filteredValue) => filteredValue === normalizedValue) ===
36134
- -1;
36135
- return {
36136
- checked,
36137
- string: strValues.find((val) => toLowerCase(val) === normalizedValue) || "",
36138
- };
36139
- });
36141
+ const normalizedFilteredValues = new Set(filterValues.map(toLowerCase));
36142
+ const set = new Set();
36143
+ const values = [];
36144
+ const addValue = (value) => {
36145
+ const normalizedValue = toLowerCase(value);
36146
+ if (!set.has(normalizedValue)) {
36147
+ values.push({
36148
+ string: value || "",
36149
+ checked: !normalizedFilteredValues.has(normalizedValue),
36150
+ normalizedValue,
36151
+ });
36152
+ set.add(normalizedValue);
36153
+ }
36154
+ };
36155
+ cellValues.forEach(addValue);
36156
+ filterValues.forEach(addValue);
36157
+ return values.sort((val1, val2) => val1.normalizedValue.localeCompare(val2.normalizedValue, undefined, {
36158
+ numeric: true,
36159
+ sensitivity: "base",
36160
+ }));
36140
36161
  }
36141
36162
  checkValue(value) {
36142
36163
  this.state.selectedValue = value.string;
@@ -61477,7 +61498,9 @@ stores.inject(MyMetaStore, storeInstance);
61477
61498
  const ranges = cmd.ranges.map((rangeData) => this.getters.getRangeFromRangeData(rangeData));
61478
61499
  const union = this.getters.getRangesUnion(ranges);
61479
61500
  const mergesInTarget = this.getters.getMergesInZone(cmd.sheetId, union.zone);
61480
- this.dispatch("REMOVE_MERGE", { sheetId: cmd.sheetId, target: mergesInTarget });
61501
+ if (mergesInTarget.length) {
61502
+ this.dispatch("REMOVE_MERGE", { sheetId: cmd.sheetId, target: mergesInTarget });
61503
+ }
61481
61504
  const id = this.consumeNextId();
61482
61505
  const config = cmd.config || DEFAULT_TABLE_CONFIG;
61483
61506
  const newTable = cmd.tableType === "dynamic"
@@ -61576,14 +61599,16 @@ stores.inject(MyMetaStore, storeInstance);
61576
61599
  const zoneToCheckIfEmpty = direction === "down"
61577
61600
  ? { ...zone, bottom: zone.bottom + 1, top: zone.bottom + 1 }
61578
61601
  : { ...zone, right: zone.right + 1, left: zone.right + 1 };
61579
- for (const position of positions(zoneToCheckIfEmpty)) {
61580
- const cellPosition = { sheetId, ...position };
61581
- // Since this plugin is loaded before CellPlugin, the getters still give us the old cell content
61582
- const cellContent = this.getters.getCell(cellPosition)?.content;
61583
- if (cellContent ||
61584
- this.getters.isInMerge(cellPosition) ||
61585
- this.getTablesOverlappingZones(sheetId, [positionToZone(position)]).length) {
61586
- return "none";
61602
+ for (let row = zoneToCheckIfEmpty.top; row <= zoneToCheckIfEmpty.bottom; row++) {
61603
+ for (let col = zoneToCheckIfEmpty.left; col <= zoneToCheckIfEmpty.right; col++) {
61604
+ const cellPosition = { sheetId, col, row };
61605
+ // Since this plugin is loaded before CellPlugin, the getters still give us the old cell content
61606
+ const cellContent = this.getters.getCell(cellPosition)?.content;
61607
+ if (cellContent ||
61608
+ this.getters.isInMerge(cellPosition) ||
61609
+ this.getTablesOverlappingZones(sheetId, [positionToZone(cellPosition)]).length) {
61610
+ return "none";
61611
+ }
61587
61612
  }
61588
61613
  }
61589
61614
  return direction;
@@ -71209,9 +71234,10 @@ stores.inject(MyMetaStore, storeInstance);
71209
71234
  const filteredZone = filter.filteredRange?.zone;
71210
71235
  if (!filteredValues || !filteredZone)
71211
71236
  continue;
71237
+ const filteredValuesSet = new Set(filteredValues);
71212
71238
  for (let row = filteredZone.top; row <= filteredZone.bottom; row++) {
71213
71239
  const value = this.getCellValueAsString(sheetId, filter.col, row);
71214
- if (filteredValues.includes(value)) {
71240
+ if (filteredValuesSet.has(value)) {
71215
71241
  hiddenRows.add(row);
71216
71242
  }
71217
71243
  }
@@ -80641,9 +80667,9 @@ stores.inject(MyMetaStore, storeInstance);
80641
80667
  exports.tokenize = tokenize;
80642
80668
 
80643
80669
 
80644
- __info__.version = "18.3.7";
80645
- __info__.date = "2025-06-06T09:31:27.123Z";
80646
- __info__.hash = "05333f1";
80670
+ __info__.version = "18.3.8";
80671
+ __info__.date = "2025-06-12T09:51:55.929Z";
80672
+ __info__.hash = "32dedd1";
80647
80673
 
80648
80674
 
80649
80675
  })(this.o_spreadsheet = this.o_spreadsheet || {}, owl);