diff --git a/addons/spreadsheet/static/src/o_spreadsheet/o_spreadsheet.js b/addons/spreadsheet/static/src/o_spreadsheet/o_spreadsheet.js index 8a34d916dfb..c12278f5ca8 100644 --- a/addons/spreadsheet/static/src/o_spreadsheet/o_spreadsheet.js +++ b/addons/spreadsheet/static/src/o_spreadsheet/o_spreadsheet.js @@ -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); diff --git a/addons/spreadsheet/static/src/o_spreadsheet/o_spreadsheet.xml b/addons/spreadsheet/static/src/o_spreadsheet/o_spreadsheet.xml index 1d5d3040ce1..520de34302d 100644 --- a/addons/spreadsheet/static/src/o_spreadsheet/o_spreadsheet.xml +++ b/addons/spreadsheet/static/src/o_spreadsheet/o_spreadsheet.xml @@ -1,9 +1,9 @@ @@ -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'" /> @@ -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)">
-
+