[FIX] spreadsheet: update o_spreadsheet to latest version

### Contains the following commits:

https://github.com/odoo/o-spreadsheet/commit/33f09d7c4 [REL] 17.0.17
https://github.com/odoo/o-spreadsheet/commit/1767b3728 [FIX] chart: preserve label order when aggregate
https://github.com/odoo/o-spreadsheet/commit/6c96ded0b [PERF] helpers: faster default cell height Task: 3845472
https://github.com/odoo/o-spreadsheet/commit/b48eb1cf7 [IMP] package: update owl to version 2.2.10
https://github.com/odoo/o-spreadsheet/commit/c2f6da219 [FIX] *: Compute max/min on huge arrays Task: 3802691
https://github.com/odoo/o-spreadsheet/commit/06db89656 [FIX] Charts: format of negative values
https://github.com/odoo/o-spreadsheet/commit/c2eed35cb [FIX] EditionPlugin: data validation duplicate values
https://github.com/odoo/o-spreadsheet/commit/ed7d3fa63 [FIX] BottomBarSheet: Rename a sheet with style content on Firefox Task: 3754944
https://github.com/odoo/o-spreadsheet/commit/33ed353a9 [FIX] bottom_bar: disable sheet drag & drop in readonly Task: 3820888
https://github.com/odoo/o-spreadsheet/commit/261616dfa [FIX] menu: non-aligned menu items Task: 3814222

closes odoo/odoo#160740

Signed-off-by: Vincent Schippefilt (vsc) <vsc@odoo.com>
This commit is contained in:
Lucas Lefèvre (lul)
2024-04-05 15:52:06 +00:00
parent 36a5a419bc
commit f5d6a8c4f8
2 changed files with 95 additions and 55 deletions
@@ -2,9 +2,9 @@
/**
* This file is generated by o-spreadsheet build tools. Do not edit it.
* @see https://github.com/odoo/o-spreadsheet
* @version 17.0.16
* @date 2024-03-25T11:04:46.936Z
* @hash 9b128f8c
* @version 17.0.17
* @date 2024-04-05T14:13:49.946Z
* @hash 33f09d7c4
*/
(function (exports, owl) {
@@ -558,7 +558,7 @@
}
// Generate new Id if the item didn't exist in the dictionary
const ids = Object.keys(itemsDic);
const maxId = ids.length === 0 ? 0 : Math.max(...ids.map((id) => parseInt(id, 10)));
const maxId = ids.length === 0 ? 0 : largeMax(ids.map((id) => parseInt(id, 10)));
itemsDic[maxId + 1] = item;
return maxId + 1;
}
@@ -775,6 +775,34 @@
}
return value >= min && value <= max;
}
/**
* Alternative to Math.max that works with large arrays.
* Typically useful for arrays bigger than 100k elements.
*/
function largeMax(array) {
let len = array.length;
if (len < 100000)
return Math.max(...array);
let max = -Infinity;
while (len--) {
max = array[len] > max ? array[len] : max;
}
return max;
}
/**
* Alternative to Math.min that works with large arrays.
* Typically useful for arrays bigger than 100k elements.
*/
function largeMin(array) {
let len = array.length;
if (len < 100000)
return Math.min(...array);
let min = +Infinity;
while (len--) {
min = array[len] < min ? array[len] : min;
}
return min;
}
const RBA_REGEX = /rgba?\(|\s+|\)/gi;
const HEX_MATCH = /^#([A-F\d]{2}){3,4}$/;
@@ -4421,8 +4449,9 @@
* Get the default height of the cell given its style.
*/
function getDefaultCellHeight(ctx, cell, colSize) {
if (!cell || !cell.content)
if (!cell || (!cell.isFormula && !cell.content)) {
return DEFAULT_CELL_HEIGHT;
}
const maxWidth = cell.style?.wrapping === "wrap" ? colSize - 2 * MIN_CELL_TEXT_MARGIN : undefined;
const numberOfLines = cell.isFormula
? 1
@@ -8325,10 +8354,10 @@
}
}
return {
labels: Object.keys(labelMap),
labels: Array.from(labelSet),
dataSetsValues: datasets.map((dataset, indexOfDataset) => ({
...dataset,
data: Object.values(labelMap).map((dataOfLabel) => dataOfLabel[indexOfDataset]),
data: Array.from(labelSet).map((label) => labelMap[label][indexOfDataset]),
})),
};
}
@@ -8379,7 +8408,7 @@
const xLabel = tooltipItem.dataset?.label || tooltipItem.label;
// tooltipItem.parsed.y can be an object or a number for pie charts
const yLabel = tooltipItem.parsed.y ?? tooltipItem.parsed;
const toolTipFormat = !format && yLabel > 1000 ? "#,##" : format;
const toolTipFormat = !format && Math.abs(yLabel) >= 1000 ? "#,##" : format;
const yLabelStr = formatValue(yLabel, { format: toolTipFormat, locale });
return xLabel ? `${xLabel}: ${yLabelStr}` : yLabelStr;
},
@@ -8665,7 +8694,10 @@
if (isNaN(value))
return value;
const { locale, format } = localeFormat;
return formatValue(value, { locale, format: !format && value > 1000 ? "#,##" : format });
return formatValue(value, {
locale,
format: !format && Math.abs(value) >= 1000 ? "#,##" : format,
});
},
},
},
@@ -9079,7 +9111,7 @@
return undefined;
}
const labelsTimestamps = labelDates.map((date) => date.getTime());
const period = Math.max(...labelsTimestamps) - Math.min(...labelsTimestamps);
const period = largeMax(labelsTimestamps) - largeMin(labelsTimestamps);
const minUnit = getFormatMinDisplayUnit(format);
if (UNIT_LENGTH.second >= UNIT_LENGTH[minUnit] && Milliseconds.inSeconds(period) < 180) {
return "second";
@@ -9316,7 +9348,10 @@
if (isNaN(value))
return value;
const { locale, format } = localeFormat;
return formatValue(value, { locale, format: !format && value > 1000 ? "#,##" : format });
return formatValue(value, {
locale,
format: !format && Math.abs(value) >= 1000 ? "#,##" : format,
});
},
},
},
@@ -9543,7 +9578,7 @@
const percentage = calculatePercentage(data, dataIndex);
const xLabel = tooltipItem.label || tooltipItem.dataset.label;
const yLabel = tooltipItem.parsed.y ?? tooltipItem.parsed;
const toolTipFormat = !format && yLabel > 1000 ? "#,##" : format;
const toolTipFormat = !format && Math.abs(yLabel) >= 1000 ? "#,##" : format;
const yLabelStr = formatValue(yLabel, { format: toolTipFormat, locale });
return xLabel ? `${xLabel}: ${yLabelStr} (${percentage}%)` : `${yLabelStr} (${percentage}%)`;
};
@@ -9551,7 +9586,7 @@
}
function getPieColors(colors, dataSetsValues) {
const pieColors = [];
const maxLength = Math.max(...dataSetsValues.map((ds) => ds.data.length));
const maxLength = largeMax(dataSetsValues.map((ds) => ds.data.length));
for (let i = 0; i <= maxLength; i++) {
pieColors.push(colors.next());
}
@@ -10453,8 +10488,8 @@
let last;
const activesRows = env.model.getters.getActiveRows();
if (activesRows.size !== 0) {
first = Math.min(...activesRows);
last = Math.max(...activesRows);
first = largeMin([...activesRows]);
last = largeMax([...activesRows]);
}
else {
const zone = env.model.getters.getSelectedZones()[0];
@@ -10482,8 +10517,8 @@
let last;
const activeCols = env.model.getters.getActiveCols();
if (activeCols.size !== 0) {
first = Math.min(...activeCols);
last = Math.max(...activeCols);
first = largeMin([...activeCols]);
last = largeMax([...activeCols]);
}
else {
const zone = env.model.getters.getSelectedZones()[0];
@@ -10511,8 +10546,8 @@
let last;
const activesRows = env.model.getters.getActiveRows();
if (activesRows.size !== 0) {
first = Math.min(...activesRows);
last = Math.max(...activesRows);
first = largeMin([...activesRows]);
last = largeMax([...activesRows]);
}
else {
const zone = env.model.getters.getSelectedZones()[0];
@@ -10553,8 +10588,8 @@
let last;
const activeCols = env.model.getters.getActiveCols();
if (activeCols.size !== 0) {
first = Math.min(...activeCols);
last = Math.max(...activeCols);
first = largeMin([...activeCols]);
last = largeMax([...activeCols]);
}
else {
const zone = env.model.getters.getSelectedZones()[0];
@@ -10595,7 +10630,7 @@
let row;
let quantity;
if (activeRows.size) {
row = Math.min(...activeRows);
row = largeMin([...activeRows]);
quantity = activeRows.size;
}
else {
@@ -10616,7 +10651,7 @@
let row;
let quantity;
if (activeRows.size) {
row = Math.max(...activeRows);
row = largeMax([...activeRows]);
quantity = activeRows.size;
}
else {
@@ -10637,7 +10672,7 @@
let column;
let quantity;
if (activeCols.size) {
column = Math.min(...activeCols);
column = largeMin([...activeCols]);
quantity = activeCols.size;
}
else {
@@ -10658,7 +10693,7 @@
let column;
let quantity;
if (activeCols.size) {
column = Math.max(...activeCols);
column = largeMax([...activeCols]);
quantity = activeCols.size;
}
else {
@@ -31855,7 +31890,7 @@
function getSheetDims(sheet) {
const dims = [0, 0];
for (let row of sheet.rows) {
dims[0] = Math.max(dims[0], ...row.cells.map((cell) => toCartesian(cell.xc).col));
dims[0] = Math.max(dims[0], largeMax(row.cells.map((cell) => toCartesian(cell.xc).col)));
dims[1] = Math.max(dims[1], row.index);
}
dims[0] = Math.max(dims[0], EXCEL_IMPORT_DEFAULT_NUMBER_OF_COLS);
@@ -36745,6 +36780,9 @@
if (newRule.criterion.type === "isBoolean") {
this.setCenterStyleToBooleanCells(newRule);
}
else if (newRule.criterion.type === "isValueInList") {
newRule.criterion.values = Array.from(new Set(newRule.criterion.values));
}
const adaptedRules = this.removeRangesFromRules(sheetId, newRule.ranges, rules);
const ruleIndex = adaptedRules.findIndex((rule) => rule.id === newRule.id);
if (ruleIndex !== -1) {
@@ -37471,7 +37509,7 @@
if (hiddenElements.size >= elements) {
return "TooManyHiddenElements" /* CommandResult.TooManyHiddenElements */;
}
else if (Math.min(...cmd.elements) < 0 || Math.max(...cmd.elements) > elements) {
else if (largeMin(cmd.elements) < 0 || largeMax(cmd.elements) > elements) {
return "InvalidHeaderIndex" /* CommandResult.InvalidHeaderIndex */;
}
else {
@@ -38286,8 +38324,8 @@
let newRange = range;
let changeType = "NONE";
for (let group of groups) {
const min = Math.min(...group);
const max = Math.max(...group);
const min = largeMin(group);
const max = largeMax(group);
if (range.zone[start] <= min && min <= range.zone[end]) {
const toRemove = Math.min(range.zone[end], max) - min + 1;
changeType = "RESIZE";
@@ -38710,8 +38748,8 @@
}
return "Success" /* CommandResult.Success */;
case "REMOVE_COLUMNS_ROWS": {
const min = Math.min(...cmd.elements);
const max = Math.max(...cmd.elements);
const min = largeMin(cmd.elements);
const max = largeMax(cmd.elements);
if (min < 0 || !this.doesHeaderExist(cmd.sheetId, cmd.dimension, max)) {
return "InvalidHeaderIndex" /* CommandResult.InvalidHeaderIndex */;
}
@@ -42043,13 +42081,13 @@
.map((cell) => cell.value);
switch (threshold.type) {
case "value":
const result = functionName === "max" ? Math.max(...rangeValues) : Math.min(...rangeValues);
const result = functionName === "max" ? largeMax(rangeValues) : largeMin(rangeValues);
return result;
case "number":
return Number(threshold.value);
case "percentage":
const min = Math.min(...rangeValues);
const max = Math.max(...rangeValues);
const min = largeMin(rangeValues);
const max = largeMax(rangeValues);
const delta = max - min;
return min + (delta * Number(threshold.value)) / 100;
case "percentile":
@@ -43101,13 +43139,13 @@
const cells = this.getters.getEvaluatedCellsInZone(sheet.id, zone);
const cellPositions = range(end, -1, -1);
const invalidCells = cellPositions.filter((position) => cells[position] && !cells[position].isAutoSummable);
const maxValidPosition = Math.max(...invalidCells);
const maxValidPosition = largeMax(invalidCells);
const numberSequences = groupConsecutive(cellPositions.filter((position) => this.isNumber(cells[position])));
const firstSequence = numberSequences[0] || [];
if (Math.max(...firstSequence) < maxValidPosition) {
if (largeMax(firstSequence) < maxValidPosition) {
return Infinity;
}
return Math.min(...firstSequence);
return largeMin(firstSequence);
}
shouldFindData(sheetId, zone) {
return this.getters.isEmpty(sheetId, zone) || this.getters.isSingleCellOrMerge(sheetId, zone);
@@ -45511,7 +45549,7 @@
* column of the current viewport
*/
getColDimensionsInViewport(sheetId, col) {
const left = Math.min(...this.getters.getSheetViewVisibleCols());
const left = largeMin(this.getters.getSheetViewVisibleCols());
const start = this.getters.getColRowOffsetInViewport("COL", left, col);
const size = this.getters.getColSize(sheetId, col);
const isColHidden = this.getters.isColHidden(sheetId, col);
@@ -45526,7 +45564,7 @@
* of the current viewport
*/
getRowDimensionsInViewport(sheetId, row) {
const top = Math.min(...this.getters.getSheetViewVisibleRows());
const top = largeMin(this.getters.getSheetViewVisibleRows());
const start = this.getters.getColRowOffsetInViewport("ROW", top, row);
const size = this.getters.getRowSize(sheetId, row);
const isRowHidden = this.getters.isRowHidden(sheetId, row);
@@ -46990,7 +47028,7 @@
getColMaxWidth(sheetId, index) {
const cellsPositions = positions(this.getters.getColsZone(sheetId, index, index));
const sizes = cellsPositions.map((position) => this.getCellWidth({ sheetId, ...position }));
return Math.max(0, ...sizes);
return Math.max(0, largeMax(sizes));
}
/**
* Check that any "sheetId" in the command matches an existing
@@ -47755,7 +47793,7 @@
}
getPasteZone(target) {
const height = this.values.length;
const width = Math.max(...this.values.map((a) => a.length));
const width = largeMax(this.values.map((a) => a.length));
const { left: activeCol, top: activeRow } = target[0];
return {
top: activeRow,
@@ -48792,11 +48830,11 @@
}
else {
const range = this.getters.getRangeFromSheetXC(this.sheetId, rule.criterion.values[0]);
values = this.getters
values = Array.from(new Set(this.getters
.getRangeValues(range)
.filter(isNotNull)
.map((value) => value.toString())
.filter((val) => val !== "");
.filter((val) => val !== "")));
}
const composerContent = this.getCurrentContent();
if (composerContent && composerContent !== this.getInitialComposerContent()) {
@@ -51253,12 +51291,14 @@
this.editionState = "initializing";
}
stopEdition() {
if (!this.state.isEditing)
const input = this.sheetNameRef.el;
if (!this.state.isEditing || !input)
return;
this.state.isEditing = false;
this.editionState = "initializing";
this.sheetNameRef.el?.blur();
input.blur();
const inputValue = this.getInputContent() || "";
input.innerText = inputValue;
interactiveRenameSheet(this.env, this.props.sheetId, inputValue, () => this.startEdition());
}
cancelEdition() {
@@ -51552,7 +51592,7 @@
this.sheetListRef.el.scrollTo({ top: 0, left: scroll, behavior: "smooth" });
}
onSheetMouseDown(sheetId, event) {
if (event.button !== 0)
if (event.button !== 0 || this.env.model.getters.isReadonly())
return;
this.closeMenu();
const visibleSheets = this.getVisibleSheets();
@@ -55057,7 +55097,7 @@
}
function addDoughnutChart(chart, chartSheetIndex, data, { holeSize } = { holeSize: 50 }) {
const colors = new ChartColors();
const maxLength = Math.max(...chart.dataSets.map((ds) => getRangeSize(ds.range, chartSheetIndex, data)));
const maxLength = largeMax(chart.dataSets.map((ds) => getRangeSize(ds.range, chartSheetIndex, data)));
const doughnutColors = range(0, maxLength).map(() => toXlsxHexColor(colors.next()));
const dataSetsNodes = [];
for (const [dsIndex, dataset] of Object.entries(chart.dataSets).reverse()) {
@@ -57011,9 +57051,9 @@
exports.tokenize = tokenize;
__info__.version = "17.0.16";
__info__.date = "2024-03-25T11:04:46.936Z";
__info__.hash = "9b128f8c";
__info__.version = "17.0.17";
__info__.date = "2024-04-05T14:13:49.946Z";
__info__.hash = "33f09d7c4";
})(this.o_spreadsheet = this.o_spreadsheet || {}, owl);
@@ -1,9 +1,9 @@
<!--
This file is generated by o-spreadsheet build tools. Do not edit it.
@see https://github.com/odoo/o-spreadsheet
@version 17.0.16
@date 2024-03-25T11:04:49.559Z
@hash 9b128f8c
@version 17.0.17
@date 2024-04-05T14:13:52.721Z
@hash 33f09d7c4
-->
<odoo>
<t t-name="o-spreadsheet-ActionButton">
@@ -276,7 +276,7 @@
t-on-dblclick="() => this.onDblClick()"
t-on-focusout="() => this.onFocusOut()"
t-on-keydown="(ev) => this.onKeyDown(ev)"
t-att-contenteditable="state.isEditing ? 'plaintext-only': 'false'"
t-att-contenteditable="state.isEditing ? 'true': 'false'"
/>
<span class="o-sheet-icon ms-1" t-on-click.stop="(ev) => this.onIconClick(ev)">
<t t-call="o-spreadsheet-Icon.TRIANGLE_DOWN"/>
@@ -2424,7 +2424,7 @@ https://fontawesome.com/license -->
t-att-class="{ 'o-menu-root': isMenuRoot, 'disabled': !isMenuEnabled, 'o-menu-item-active': isParentMenu(subMenu, menuItem)}"
t-att-style="getColor(menuItem)">
<div class="d-flex w-100">
<div t-if="childrenHaveIcon" class="o-menu-item-icon align-middle">
<div t-if="childrenHaveIcon" class="o-menu-item-icon align-middle flex-shrink-0">
<t t-if="getIconName(menuItem)" t-call="{{getIconName(menuItem)}}"/>
</div>
<div class="o-menu-item-name align-middle text-truncate" t-esc="getName(menuItem)"/>