@odoo/o-spreadsheet 18.3.7 → 18.3.9
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.
- package/dist/o-spreadsheet.cjs.js +125 -56
- package/dist/o-spreadsheet.esm.js +125 -56
- package/dist/o-spreadsheet.iife.js +125 -56
- package/dist/o-spreadsheet.iife.min.js +165 -165
- package/dist/o_spreadsheet.xml +3 -3
- package/package.json +1 -1
|
@@ -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.
|
|
6
|
-
* @date 2025-06-
|
|
7
|
-
* @hash
|
|
5
|
+
* @version 18.3.9
|
|
6
|
+
* @date 2025-06-19T18:24:02.754Z
|
|
7
|
+
* @hash a820230
|
|
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)
|
|
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);
|
|
@@ -20951,6 +20953,7 @@ function drawLineOrBarOrRadarChartValues(chart, options, ctx) {
|
|
|
20951
20953
|
if (isTrendLineAxis(dataset.xAxisID) || dataset.hidden) {
|
|
20952
20954
|
continue;
|
|
20953
20955
|
}
|
|
20956
|
+
const yAxisScale = chart.scales[dataset.yAxisID];
|
|
20954
20957
|
for (let i = 0; i < dataset._parsed.length; i++) {
|
|
20955
20958
|
const parsedValue = dataset._parsed[i];
|
|
20956
20959
|
const value = Number(chart.config.type === "radar" ? parsedValue.r : parsedValue.y);
|
|
@@ -20961,10 +20964,18 @@ function drawLineOrBarOrRadarChartValues(chart, options, ctx) {
|
|
|
20961
20964
|
const xPosition = point.x;
|
|
20962
20965
|
let yPosition = 0;
|
|
20963
20966
|
if (chart.config.type === "line" || chart.config.type === "radar") {
|
|
20964
|
-
yPosition = point.y - 10;
|
|
20967
|
+
yPosition = value < 0 ? point.y + 10 : point.y - 10;
|
|
20965
20968
|
}
|
|
20966
20969
|
else {
|
|
20967
|
-
|
|
20970
|
+
const yZeroLine = yAxisScale.getPixelForValue(0);
|
|
20971
|
+
const distanceFromAxisOrigin = Math.abs(yZeroLine - point.y);
|
|
20972
|
+
const textHeight = 12; // ChartJS default text height
|
|
20973
|
+
if (distanceFromAxisOrigin < textHeight) {
|
|
20974
|
+
yPosition = value < 0 ? yZeroLine + textHeight / 2 : yZeroLine - textHeight / 2;
|
|
20975
|
+
}
|
|
20976
|
+
else {
|
|
20977
|
+
yPosition = value < 0 ? point.y - point.height / 2 : point.y + point.height / 2;
|
|
20978
|
+
}
|
|
20968
20979
|
}
|
|
20969
20980
|
yPosition = Math.min(yPosition, yMax);
|
|
20970
20981
|
yPosition = Math.max(yPosition, yMin);
|
|
@@ -20974,7 +20985,7 @@ function drawLineOrBarOrRadarChartValues(chart, options, ctx) {
|
|
|
20974
20985
|
}
|
|
20975
20986
|
for (const otherPosition of textsPositions[xPosition] || []) {
|
|
20976
20987
|
if (Math.abs(otherPosition - yPosition) < 13) {
|
|
20977
|
-
yPosition = otherPosition - 13;
|
|
20988
|
+
yPosition = value < 0 ? otherPosition + 13 : otherPosition - 13;
|
|
20978
20989
|
}
|
|
20979
20990
|
}
|
|
20980
20991
|
textsPositions[xPosition].push(yPosition);
|
|
@@ -20993,6 +21004,8 @@ function drawHorizontalBarChartValues(chart, options, ctx) {
|
|
|
20993
21004
|
if (isTrendLineAxis(dataset.xAxisID)) {
|
|
20994
21005
|
return; // ignore trend lines
|
|
20995
21006
|
}
|
|
21007
|
+
const xAxisScale = chart.scales[dataset.xAxisID];
|
|
21008
|
+
const xZeroLine = xAxisScale.getPixelForValue(0);
|
|
20996
21009
|
for (let i = 0; i < dataset._parsed.length; i++) {
|
|
20997
21010
|
const value = Number(dataset._parsed[i].x);
|
|
20998
21011
|
if (isNaN(value)) {
|
|
@@ -21001,17 +21014,27 @@ function drawHorizontalBarChartValues(chart, options, ctx) {
|
|
|
21001
21014
|
const displayValue = options.callback(value, dataset, i);
|
|
21002
21015
|
const point = dataset.data[i];
|
|
21003
21016
|
const yPosition = point.y;
|
|
21004
|
-
|
|
21005
|
-
|
|
21006
|
-
|
|
21017
|
+
const textWidth = computeTextWidth(ctx, displayValue, { fontSize: 12 }, "px");
|
|
21018
|
+
const distanceFromAxisOrigin = Math.abs(point.x - xZeroLine);
|
|
21019
|
+
const PADDING = 3;
|
|
21020
|
+
let xPosition;
|
|
21021
|
+
if (distanceFromAxisOrigin < textWidth) {
|
|
21022
|
+
xPosition =
|
|
21023
|
+
value < 0 ? xZeroLine - textWidth / 2 - PADDING : xZeroLine + textWidth / 2 + PADDING;
|
|
21024
|
+
}
|
|
21025
|
+
else {
|
|
21026
|
+
xPosition = value < 0 ? point.x + point.width / 2 : point.x - point.width / 2;
|
|
21027
|
+
xPosition = Math.min(xPosition, xMax);
|
|
21028
|
+
xPosition = Math.max(xPosition, xMin);
|
|
21029
|
+
}
|
|
21007
21030
|
// Avoid overlapping texts with same Y
|
|
21008
21031
|
if (!textsPositions[yPosition]) {
|
|
21009
21032
|
textsPositions[yPosition] = [];
|
|
21010
21033
|
}
|
|
21011
|
-
const textWidth = computeTextWidth(ctx, displayValue, { fontSize: 12 }, "px");
|
|
21012
21034
|
for (const otherPosition of textsPositions[yPosition]) {
|
|
21013
21035
|
if (Math.abs(otherPosition - xPosition) < textWidth) {
|
|
21014
|
-
xPosition =
|
|
21036
|
+
xPosition =
|
|
21037
|
+
value < 0 ? otherPosition - textWidth - PADDING : otherPosition + textWidth + PADDING;
|
|
21015
21038
|
}
|
|
21016
21039
|
}
|
|
21017
21040
|
textsPositions[yPosition].push(xPosition);
|
|
@@ -21033,10 +21056,22 @@ function drawPieChartValues(chart, options, ctx) {
|
|
|
21033
21056
|
const midAngle = (startAngle + endAngle) / 2;
|
|
21034
21057
|
const midRadius = (innerRadius + outerRadius) / 2;
|
|
21035
21058
|
const x = bar.x + midRadius * Math.cos(midAngle);
|
|
21036
|
-
const y = bar.y + midRadius * Math.sin(midAngle)
|
|
21059
|
+
const y = bar.y + midRadius * Math.sin(midAngle);
|
|
21060
|
+
const displayValue = options.callback(value, dataset, i);
|
|
21061
|
+
const textHeight = 12; // ChartJS default
|
|
21062
|
+
const textWidth = computeTextWidth(ctx, displayValue, { fontSize: textHeight }, "px");
|
|
21063
|
+
const radius = outerRadius - innerRadius;
|
|
21064
|
+
// Check if the text fits in the slice. Not perfect, but good enough heuristic.
|
|
21065
|
+
if (textWidth >= radius || radius < textHeight) {
|
|
21066
|
+
continue;
|
|
21067
|
+
}
|
|
21068
|
+
const sliceAngle = endAngle - startAngle;
|
|
21069
|
+
const midWidth = 2 * midRadius * Math.tan(sliceAngle / 2);
|
|
21070
|
+
if (sliceAngle < Math.PI / 2 && (textWidth >= midWidth || midWidth < textHeight)) {
|
|
21071
|
+
continue;
|
|
21072
|
+
}
|
|
21037
21073
|
ctx.fillStyle = chartFontColor(options.background);
|
|
21038
21074
|
ctx.strokeStyle = options.background || "#ffffff";
|
|
21039
|
-
const displayValue = options.callback(value, dataset, i);
|
|
21040
21075
|
drawTextWithBackground(displayValue, x, y, ctx);
|
|
21041
21076
|
}
|
|
21042
21077
|
}
|
|
@@ -26386,7 +26421,9 @@ function getPyramidChartShowValues(definition, args) {
|
|
|
26386
26421
|
background: definition.background,
|
|
26387
26422
|
callback: (value, dataset) => {
|
|
26388
26423
|
value = Math.abs(Number(value));
|
|
26389
|
-
return
|
|
26424
|
+
return value === 0
|
|
26425
|
+
? ""
|
|
26426
|
+
: formatChartDatasetValue(axisFormats, locale)(value, dataset.xAxisID || "x");
|
|
26390
26427
|
},
|
|
26391
26428
|
};
|
|
26392
26429
|
}
|
|
@@ -33172,7 +33209,7 @@ class XlsxBaseExtractor {
|
|
|
33172
33209
|
*/
|
|
33173
33210
|
handleMissingValue(parentElement, missingElementName, optionalArgs) {
|
|
33174
33211
|
if (optionalArgs?.required) {
|
|
33175
|
-
if (optionalArgs?.default) {
|
|
33212
|
+
if (optionalArgs?.default !== undefined) {
|
|
33176
33213
|
this.warningManager.addParsingWarning(`Missing required ${missingElementName} in element <${parentElement.tagName}> of ${this.currentFile}, replacing it by the default value ${optionalArgs.default}`);
|
|
33177
33214
|
}
|
|
33178
33215
|
else {
|
|
@@ -36125,19 +36162,26 @@ class FilterMenu extends owl.Component {
|
|
|
36125
36162
|
.filter(({ row }) => !this.env.model.getters.isRowHidden(sheetId, row))
|
|
36126
36163
|
.map(({ col, row }) => this.env.model.getters.getEvaluatedCell({ sheetId, col, row }).formattedValue);
|
|
36127
36164
|
const filterValues = this.env.model.getters.getFilterHiddenValues({ sheetId, ...position });
|
|
36128
|
-
const
|
|
36129
|
-
const
|
|
36130
|
-
|
|
36131
|
-
const
|
|
36132
|
-
|
|
36133
|
-
|
|
36134
|
-
|
|
36135
|
-
|
|
36136
|
-
|
|
36137
|
-
|
|
36138
|
-
|
|
36139
|
-
|
|
36140
|
-
|
|
36165
|
+
const normalizedFilteredValues = new Set(filterValues.map(toLowerCase));
|
|
36166
|
+
const set = new Set();
|
|
36167
|
+
const values = [];
|
|
36168
|
+
const addValue = (value) => {
|
|
36169
|
+
const normalizedValue = toLowerCase(value);
|
|
36170
|
+
if (!set.has(normalizedValue)) {
|
|
36171
|
+
values.push({
|
|
36172
|
+
string: value || "",
|
|
36173
|
+
checked: !normalizedFilteredValues.has(normalizedValue),
|
|
36174
|
+
normalizedValue,
|
|
36175
|
+
});
|
|
36176
|
+
set.add(normalizedValue);
|
|
36177
|
+
}
|
|
36178
|
+
};
|
|
36179
|
+
cellValues.forEach(addValue);
|
|
36180
|
+
filterValues.forEach(addValue);
|
|
36181
|
+
return values.sort((val1, val2) => val1.normalizedValue.localeCompare(val2.normalizedValue, undefined, {
|
|
36182
|
+
numeric: true,
|
|
36183
|
+
sensitivity: "base",
|
|
36184
|
+
}));
|
|
36141
36185
|
}
|
|
36142
36186
|
checkValue(value) {
|
|
36143
36187
|
this.state.selectedValue = value.string;
|
|
@@ -37112,6 +37156,10 @@ const REMOVE_ROWS_ACTION = (env) => {
|
|
|
37112
37156
|
});
|
|
37113
37157
|
};
|
|
37114
37158
|
const CAN_REMOVE_COLUMNS_ROWS = (dimension, env) => {
|
|
37159
|
+
if ((dimension === "COL" && env.model.getters.getActiveRows().size > 0) ||
|
|
37160
|
+
(dimension === "ROW" && env.model.getters.getActiveCols().size > 0)) {
|
|
37161
|
+
return false;
|
|
37162
|
+
}
|
|
37115
37163
|
const sheetId = env.model.getters.getActiveSheetId();
|
|
37116
37164
|
const selectedElements = env.model.getters.getElementsFromSelection(dimension);
|
|
37117
37165
|
const includesAllVisibleHeaders = env.model.getters.checkElementsIncludeAllVisibleHeaders(sheetId, dimension, selectedElements);
|
|
@@ -40009,11 +40057,11 @@ class OTRegistry extends Registry {
|
|
|
40009
40057
|
* transformation function given
|
|
40010
40058
|
*/
|
|
40011
40059
|
addTransformation(executed, toTransforms, fn) {
|
|
40012
|
-
|
|
40013
|
-
|
|
40014
|
-
|
|
40015
|
-
|
|
40016
|
-
this.content[
|
|
40060
|
+
if (!this.content[executed]) {
|
|
40061
|
+
this.content[executed] = new Map();
|
|
40062
|
+
}
|
|
40063
|
+
for (const toTransform of toTransforms) {
|
|
40064
|
+
this.content[executed].set(toTransform, fn);
|
|
40017
40065
|
}
|
|
40018
40066
|
return this;
|
|
40019
40067
|
}
|
|
@@ -40022,7 +40070,7 @@ class OTRegistry extends Registry {
|
|
|
40022
40070
|
* that the executed command happened.
|
|
40023
40071
|
*/
|
|
40024
40072
|
getTransformation(toTransform, executed) {
|
|
40025
|
-
return this.content[
|
|
40073
|
+
return this.content[executed] && this.content[executed].get(toTransform);
|
|
40026
40074
|
}
|
|
40027
40075
|
}
|
|
40028
40076
|
const otRegistry = new OTRegistry();
|
|
@@ -43312,6 +43360,12 @@ class Composer extends owl.Component {
|
|
|
43312
43360
|
owl.useEffect(() => {
|
|
43313
43361
|
this.processTokenAtCursor();
|
|
43314
43362
|
}, () => [this.props.composerStore.editionMode !== "inactive"]);
|
|
43363
|
+
owl.useEffect(() => {
|
|
43364
|
+
this.contentHelper.scrollSelectionIntoView();
|
|
43365
|
+
}, () => [
|
|
43366
|
+
this.props.composerStore.composerSelection.start,
|
|
43367
|
+
this.props.composerStore.composerSelection.end,
|
|
43368
|
+
]);
|
|
43315
43369
|
}
|
|
43316
43370
|
// ---------------------------------------------------------------------------
|
|
43317
43371
|
// Handlers
|
|
@@ -43534,6 +43588,7 @@ class Composer extends owl.Component {
|
|
|
43534
43588
|
// not main button, probably a context menu
|
|
43535
43589
|
return;
|
|
43536
43590
|
}
|
|
43591
|
+
this.debouncedHover.stopDebounce();
|
|
43537
43592
|
this.contentHelper.removeSelection();
|
|
43538
43593
|
}
|
|
43539
43594
|
onMouseup() {
|
|
@@ -43612,7 +43667,6 @@ class Composer extends owl.Component {
|
|
|
43612
43667
|
const { start, end } = this.props.composerStore.composerSelection;
|
|
43613
43668
|
this.contentHelper.selectRange(start, end);
|
|
43614
43669
|
}
|
|
43615
|
-
this.contentHelper.scrollSelectionIntoView();
|
|
43616
43670
|
}
|
|
43617
43671
|
this.shouldProcessInputEvents = true;
|
|
43618
43672
|
}
|
|
@@ -61478,7 +61532,9 @@ class TablePlugin extends CorePlugin {
|
|
|
61478
61532
|
const ranges = cmd.ranges.map((rangeData) => this.getters.getRangeFromRangeData(rangeData));
|
|
61479
61533
|
const union = this.getters.getRangesUnion(ranges);
|
|
61480
61534
|
const mergesInTarget = this.getters.getMergesInZone(cmd.sheetId, union.zone);
|
|
61481
|
-
|
|
61535
|
+
if (mergesInTarget.length) {
|
|
61536
|
+
this.dispatch("REMOVE_MERGE", { sheetId: cmd.sheetId, target: mergesInTarget });
|
|
61537
|
+
}
|
|
61482
61538
|
const id = this.consumeNextId();
|
|
61483
61539
|
const config = cmd.config || DEFAULT_TABLE_CONFIG;
|
|
61484
61540
|
const newTable = cmd.tableType === "dynamic"
|
|
@@ -61577,14 +61633,16 @@ class TablePlugin extends CorePlugin {
|
|
|
61577
61633
|
const zoneToCheckIfEmpty = direction === "down"
|
|
61578
61634
|
? { ...zone, bottom: zone.bottom + 1, top: zone.bottom + 1 }
|
|
61579
61635
|
: { ...zone, right: zone.right + 1, left: zone.right + 1 };
|
|
61580
|
-
for (
|
|
61581
|
-
|
|
61582
|
-
|
|
61583
|
-
|
|
61584
|
-
|
|
61585
|
-
|
|
61586
|
-
|
|
61587
|
-
|
|
61636
|
+
for (let row = zoneToCheckIfEmpty.top; row <= zoneToCheckIfEmpty.bottom; row++) {
|
|
61637
|
+
for (let col = zoneToCheckIfEmpty.left; col <= zoneToCheckIfEmpty.right; col++) {
|
|
61638
|
+
const cellPosition = { sheetId, col, row };
|
|
61639
|
+
// Since this plugin is loaded before CellPlugin, the getters still give us the old cell content
|
|
61640
|
+
const cellContent = this.getters.getCell(cellPosition)?.content;
|
|
61641
|
+
if (cellContent ||
|
|
61642
|
+
this.getters.isInMerge(cellPosition) ||
|
|
61643
|
+
this.getTablesOverlappingZones(sheetId, [positionToZone(cellPosition)]).length) {
|
|
61644
|
+
return "none";
|
|
61645
|
+
}
|
|
61588
61646
|
}
|
|
61589
61647
|
}
|
|
61590
61648
|
return direction;
|
|
@@ -67753,10 +67811,20 @@ function adaptTransform(toTransform, executed) {
|
|
|
67753
67811
|
*/
|
|
67754
67812
|
function transformAll(toTransform, executed) {
|
|
67755
67813
|
let transformedCommands = [...toTransform];
|
|
67814
|
+
const possibleTransformations = new Set(otRegistry.getKeys());
|
|
67756
67815
|
for (const executedCommand of executed) {
|
|
67757
|
-
|
|
67758
|
-
|
|
67759
|
-
|
|
67816
|
+
// If the executed command is not in the registry, we skip it
|
|
67817
|
+
// because we know there won't be any transformation impacting the
|
|
67818
|
+
// commands to transform.
|
|
67819
|
+
if (possibleTransformations.has(executedCommand.type)) {
|
|
67820
|
+
transformedCommands = transformedCommands.reduce((acc, cmd) => {
|
|
67821
|
+
const transformed = transform(cmd, executedCommand);
|
|
67822
|
+
if (transformed) {
|
|
67823
|
+
acc.push(transformed);
|
|
67824
|
+
}
|
|
67825
|
+
return acc;
|
|
67826
|
+
}, []);
|
|
67827
|
+
}
|
|
67760
67828
|
}
|
|
67761
67829
|
return transformedCommands;
|
|
67762
67830
|
}
|
|
@@ -69458,7 +69526,7 @@ class SheetUIPlugin extends UIPlugin {
|
|
|
69458
69526
|
}
|
|
69459
69527
|
const position = this.getters.getCellPosition(cell.id);
|
|
69460
69528
|
const colSize = this.getters.getColSize(sheetId, position.col);
|
|
69461
|
-
if (cell.isFormula) {
|
|
69529
|
+
if (cell.isFormula || this.getters.getArrayFormulaSpreadingOn(position)) {
|
|
69462
69530
|
const content = this.getters.getEvaluatedCell(position).formattedValue;
|
|
69463
69531
|
const evaluatedSize = getCellContentHeight(this.ctx, content, cell?.style, colSize);
|
|
69464
69532
|
if (evaluatedSize > evaluatedRowSize && evaluatedSize > DEFAULT_CELL_HEIGHT) {
|
|
@@ -71210,9 +71278,10 @@ class FilterEvaluationPlugin extends UIPlugin {
|
|
|
71210
71278
|
const filteredZone = filter.filteredRange?.zone;
|
|
71211
71279
|
if (!filteredValues || !filteredZone)
|
|
71212
71280
|
continue;
|
|
71281
|
+
const filteredValuesSet = new Set(filteredValues);
|
|
71213
71282
|
for (let row = filteredZone.top; row <= filteredZone.bottom; row++) {
|
|
71214
71283
|
const value = this.getCellValueAsString(sheetId, filter.col, row);
|
|
71215
|
-
if (
|
|
71284
|
+
if (filteredValuesSet.has(value)) {
|
|
71216
71285
|
hiddenRows.add(row);
|
|
71217
71286
|
}
|
|
71218
71287
|
}
|
|
@@ -80642,6 +80711,6 @@ exports.tokenColors = tokenColors;
|
|
|
80642
80711
|
exports.tokenize = tokenize;
|
|
80643
80712
|
|
|
80644
80713
|
|
|
80645
|
-
__info__.version = "18.3.
|
|
80646
|
-
__info__.date = "2025-06-
|
|
80647
|
-
__info__.hash = "
|
|
80714
|
+
__info__.version = "18.3.9";
|
|
80715
|
+
__info__.date = "2025-06-19T18:24:02.754Z";
|
|
80716
|
+
__info__.hash = "a820230";
|