diff options
| author | Pierre Rousseau <pro@odoo.com> | 2025-02-05 08:24:16 +0100 |
|---|---|---|
| committer | Pierre Rousseau <pro@odoo.com> | 2025-02-05 12:54:35 +0000 |
| commit | ca5139046d46c858bca6066566bdb8eda9c766d8 (patch) | |
| tree | 636a98e681e38e865c3193dd7f18366529914df6 | |
| parent | 2917b4cf37fe8d5d75d550912d0760a89a4b5096 (diff) | |
[FIX] spreadsheet: update o_spreadsheet to latest version
### Contains the following commits:
https://github.com/odoo/o-spreadsheet/commit/90f2af4ff [REL] 18.0.14 Task: 0
https://github.com/odoo/o-spreadsheet/commit/3590a49ee [FIX] formulas: accept simple values on search range for lookup formulas Task: 4543381
https://github.com/odoo/o-spreadsheet/commit/48e368c47 [FIX] Rendering: Skip hidden headers Task: 4535794
https://github.com/odoo/o-spreadsheet/commit/fc0aa2a66 [FIX] SheetView: avoid some array allocation
https://github.com/odoo/o-spreadsheet/commit/4bcac65b0 [FIX] SheetView: remove useless/costy calls to Object.x
https://github.com/odoo/o-spreadsheet/commit/a1ab57a4e [FIX] Renderer: Do not draw if no canvas Task: 4535794
https://github.com/odoo/o-spreadsheet/commit/95266d1ab [FIX] SheetView: Fix viewports with negative dimensions Task: 4535794
https://github.com/odoo/o-spreadsheet/commit/8c4ee5d8f [FIX] Borders: Fix border continuity Task: 4523890
https://github.com/odoo/o-spreadsheet/commit/e1ffbf678 [FIX] renderer: Re-fix box rendering Task: 4526742
closes odoo/odoo#196557
Signed-off-by: Vincent Schippefilt (vsc) <vsc@odoo.com>
Co-authored-by: Anthony Hendrickx (anhe) <anhe@odoo.com>
Co-authored-by: Alexis Lacroix (laa) <laa@odoo.com>
Co-authored-by: Lucas Lefèvre (lul) <lul@odoo.com>
Co-authored-by: Dhrutik Patel (dhrp) <dhrp@odoo.com>
Co-authored-by: Adrien Minne (adrm) <adrm@odoo.com>
Co-authored-by: Mehdi Rachico (mera) <mera@odoo.com>
Co-authored-by: Florian Damhaut (flda) <flda@odoo.com>
Co-authored-by: Rémi Rahir (rar) <rar@odoo.com>
Co-authored-by: Pierre Rousseau (pro) <pro@odoo.com>
Co-authored-by: Vincent Schippefilt (vsc) <vsc@odoo.com>
| -rw-r--r-- | addons/spreadsheet/static/src/o_spreadsheet/o_spreadsheet.js | 283 | ||||
| -rw-r--r-- | addons/spreadsheet/static/src/o_spreadsheet/o_spreadsheet.xml | 6 |
2 files changed, 163 insertions, 126 deletions
diff --git a/addons/spreadsheet/static/src/o_spreadsheet/o_spreadsheet.js b/addons/spreadsheet/static/src/o_spreadsheet/o_spreadsheet.js index 0149c8fb53f..1c8f3e339d7 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 18.0.13 - * @date 2025-01-31T08:06:35.677Z - * @hash f505971d4 + * @version 18.0.14 + * @date 2025-02-05T07:24:11.049Z + * @hash 90f2af4ff */ import { useEnv, useSubEnv, onWillUnmount, useComponent, status, Component, useRef, onMounted, useEffect, useState, onPatched, onWillPatch, onWillUpdateProps, useExternalListener, onWillStart, xml, useChildSubEnv, markRaw, toRaw } from '@odoo/owl'; @@ -18382,19 +18382,20 @@ const HLOOKUP = { description: _t("Horizontal lookup"), args: [ arg("search_key (string, number, boolean)", _t("The value to search for. For example, 42, 'Cats', or I24.")), - arg("range (range)", _t("The range to consider for the search. The first row in the range is searched for the key specified in search_key.")), + arg("range (any, range)", _t("The range to consider for the search. The first row in the range is searched for the key specified in search_key.")), arg("index (number)", _t("The row index of the value to be returned, where the first row in range is numbered 1.")), arg(`is_sorted (boolean, default=${DEFAULT_IS_SORTED})`, _t("Indicates whether the row to be searched (the first row of the specified range) is sorted, in which case the closest match for search_key will be returned.")), ], compute: function (searchKey, range, index, isSorted = { value: DEFAULT_IS_SORTED }) { const _index = Math.trunc(toNumber(index?.value, this.locale)); - assert(() => 1 <= _index && _index <= range[0].length, _t("[[FUNCTION_NAME]] evaluates to an out of bounds range.")); + const _range = toMatrix(range); + assert(() => 1 <= _index && _index <= _range[0].length, _t("[[FUNCTION_NAME]] evaluates to an out of bounds range.")); const getValueFromRange = (range, index) => range[index][0].value; const _isSorted = toBoolean(isSorted.value); const colIndex = _isSorted - ? dichotomicSearch(range, searchKey, "nextSmaller", "asc", range.length, getValueFromRange) - : linearSearch(range, searchKey, "wildcard", range.length, getValueFromRange, this.lookupCaches); - const col = range[colIndex]; + ? dichotomicSearch(_range, searchKey, "nextSmaller", "asc", _range.length, getValueFromRange) + : linearSearch(range, searchKey, "wildcard", _range.length, getValueFromRange, this.lookupCaches); + const col = _range[colIndex]; if (col === undefined) { return valueNotAvailable(searchKey); } @@ -18486,35 +18487,37 @@ const LOOKUP = { description: _t("Look up a value."), args: [ arg("search_key (string, number, boolean)", _t("The value to search for. For example, 42, 'Cats', or I24.")), - arg("search_array (range)", _t("One method of using this function is to provide a single sorted row or column search_array to look through for the search_key with a second argument result_range. The other way is to combine these two arguments into one search_array where the first row or column is searched and a value is returned from the last row or column in the array. If search_key is not found, a non-exact match may be returned.")), - arg("result_range (range, optional)", _t("The range from which to return a result. The value returned corresponds to the location where search_key is found in search_range. This range must be only a single row or column and should not be used if using the search_result_array method.")), + arg("search_array (any, range)", _t("One method of using this function is to provide a single sorted row or column search_array to look through for the search_key with a second argument result_range. The other way is to combine these two arguments into one search_array where the first row or column is searched and a value is returned from the last row or column in the array. If search_key is not found, a non-exact match may be returned.")), + arg("result_range (any, range, optional)", _t("The range from which to return a result. The value returned corresponds to the location where search_key is found in search_range. This range must be only a single row or column and should not be used if using the search_result_array method.")), ], compute: function (searchKey, searchArray, resultRange) { - let nbCol = searchArray.length; - let nbRow = searchArray[0].length; + const _searchArray = toMatrix(searchArray); + const _resultRange = toMatrix(resultRange); + let nbCol = _searchArray.length; + let nbRow = _searchArray[0].length; const verticalSearch = nbRow >= nbCol; const getElement = verticalSearch ? (range, index) => range[0][index].value : (range, index) => range[index][0].value; const rangeLength = verticalSearch ? nbRow : nbCol; - const index = dichotomicSearch(searchArray, searchKey, "nextSmaller", "asc", rangeLength, getElement); + const index = dichotomicSearch(_searchArray, searchKey, "nextSmaller", "asc", rangeLength, getElement); if (index === -1 || - (verticalSearch && searchArray[0][index] === undefined) || - (!verticalSearch && searchArray[index][nbRow - 1] === undefined)) { + (verticalSearch && _searchArray[0][index] === undefined) || + (!verticalSearch && _searchArray[index][nbRow - 1] === undefined)) { return valueNotAvailable(searchKey); } - if (resultRange === undefined) { - return verticalSearch ? searchArray[nbCol - 1][index] : searchArray[index][nbRow - 1]; + if (_resultRange[0].length === 0) { + return verticalSearch ? _searchArray[nbCol - 1][index] : _searchArray[index][nbRow - 1]; } - nbCol = resultRange.length; - nbRow = resultRange[0].length; + nbCol = _resultRange.length; + nbRow = _resultRange[0].length; assert(() => nbCol === 1 || nbRow === 1, _t("The result_range must be a single row or a single column.")); if (nbCol > 1) { assert(() => index <= nbCol - 1, _t("[[FUNCTION_NAME]] evaluates to an out of range row value %s.", (index + 1).toString())); - return resultRange[index][0]; + return _resultRange[index][0]; } assert(() => index <= nbRow - 1, _t("[[FUNCTION_NAME]] evaluates to an out of range column value %s.", (index + 1).toString())); - return resultRange[0][index]; + return _resultRange[0][index]; }, isExported: true, }; @@ -18531,28 +18534,29 @@ const MATCH = { ], compute: function (searchKey, range, searchType = { value: DEFAULT_SEARCH_TYPE }) { let _searchType = toNumber(searchType, this.locale); - const nbCol = range.length; - const nbRow = range[0].length; + const _range = toMatrix(range); + const nbCol = _range.length; + const nbRow = _range[0].length; assert(() => nbCol === 1 || nbRow === 1, _t("The range must be a single row or a single column.")); let index = -1; const getElement = nbCol === 1 - ? (range, index) => range[0][index].value - : (range, index) => range[index][0].value; - const rangeLen = nbCol === 1 ? range[0].length : range.length; + ? (_range, index) => _range[0][index].value + : (_range, index) => _range[index][0].value; + const rangeLen = nbCol === 1 ? _range[0].length : _range.length; _searchType = Math.sign(_searchType); switch (_searchType) { case 1: - index = dichotomicSearch(range, searchKey, "nextSmaller", "asc", rangeLen, getElement); + index = dichotomicSearch(_range, searchKey, "nextSmaller", "asc", rangeLen, getElement); break; case 0: - index = linearSearch(range, searchKey, "wildcard", rangeLen, getElement, this.lookupCaches); + index = linearSearch(_range, searchKey, "wildcard", rangeLen, getElement, this.lookupCaches); break; case -1: - index = dichotomicSearch(range, searchKey, "nextGreater", "desc", rangeLen, getElement); + index = dichotomicSearch(_range, searchKey, "nextGreater", "desc", rangeLen, getElement); break; } - if ((nbCol === 1 && range[0][index] === undefined) || - (nbCol !== 1 && range[index] === undefined)) { + if ((nbCol === 1 && _range[0][index] === undefined) || + (nbCol !== 1 && _range[index] === undefined)) { return valueNotAvailable(searchKey); } return index + 1; @@ -18607,13 +18611,14 @@ const VLOOKUP = { ], compute: function (searchKey, range, index, isSorted = { value: DEFAULT_IS_SORTED }) { const _index = Math.trunc(toNumber(index?.value, this.locale)); - assert(() => 1 <= _index && _index <= range.length, _t("[[FUNCTION_NAME]] evaluates to an out of bounds range.")); + const _range = toMatrix(range); + assert(() => 1 <= _index && _index <= _range.length, _t("[[FUNCTION_NAME]] evaluates to an out of bounds range.")); const getValueFromRange = (range, index) => range[0][index].value; const _isSorted = toBoolean(isSorted.value); const rowIndex = _isSorted - ? dichotomicSearch(range, searchKey, "nextSmaller", "asc", range[0].length, getValueFromRange) - : linearSearch(range, searchKey, "wildcard", range[0].length, getValueFromRange, this.lookupCaches); - const value = range[_index - 1][rowIndex]; + ? dichotomicSearch(_range, searchKey, "nextSmaller", "asc", _range[0].length, getValueFromRange) + : linearSearch(_range, searchKey, "wildcard", _range[0].length, getValueFromRange, this.lookupCaches); + const value = _range[_index - 1][rowIndex]; if (value === undefined) { return valueNotAvailable(searchKey); } @@ -18650,27 +18655,29 @@ const XLOOKUP = { compute: function (searchKey, lookupRange, returnRange, defaultValue, matchMode = { value: DEFAULT_MATCH_MODE }, searchMode = { value: DEFAULT_SEARCH_MODE }) { const _matchMode = Math.trunc(toNumber(matchMode.value, this.locale)); const _searchMode = Math.trunc(toNumber(searchMode.value, this.locale)); - assert(() => lookupRange.length === 1 || lookupRange[0].length === 1, _t("lookup_range should be either a single row or single column.")); + const _lookupRange = toMatrix(lookupRange); + const _returnRange = toMatrix(returnRange); + assert(() => _lookupRange.length === 1 || _lookupRange[0].length === 1, _t("lookup_range should be either a single row or single column.")); assert(() => [-1, 1, -2, 2].includes(_searchMode), _t("search_mode should be a value in [-1, 1, -2, 2].")); assert(() => [-1, 0, 1, 2].includes(_matchMode), _t("match_mode should be a value in [-1, 0, 1, 2].")); - const lookupDirection = lookupRange.length === 1 ? "col" : "row"; + const lookupDirection = _lookupRange.length === 1 ? "col" : "row"; assert(() => !(_matchMode === 2 && [-2, 2].includes(_searchMode)), _t("the search and match mode combination is not supported for XLOOKUP evaluation.")); assert(() => lookupDirection === "col" - ? returnRange[0].length === lookupRange[0].length - : returnRange.length === lookupRange.length, _t("return_range should have the same dimensions as lookup_range.")); + ? _returnRange[0].length === _lookupRange[0].length + : _returnRange.length === _lookupRange.length, _t("return_range should have the same dimensions as lookup_range.")); const getElement = lookupDirection === "col" ? (range, index) => range[0][index].value : (range, index) => range[index][0].value; - const rangeLen = lookupDirection === "col" ? lookupRange[0].length : lookupRange.length; + const rangeLen = lookupDirection === "col" ? _lookupRange[0].length : _lookupRange.length; const mode = MATCH_MODE[_matchMode]; const reverseSearch = _searchMode === -1; const index = _searchMode === 2 || _searchMode === -2 - ? dichotomicSearch(lookupRange, searchKey, mode, _searchMode === 2 ? "asc" : "desc", rangeLen, getElement) - : linearSearch(lookupRange, searchKey, mode, rangeLen, getElement, this.lookupCaches, reverseSearch); + ? dichotomicSearch(_lookupRange, searchKey, mode, _searchMode === 2 ? "asc" : "desc", rangeLen, getElement) + : linearSearch(_lookupRange, searchKey, mode, rangeLen, getElement, this.lookupCaches, reverseSearch); if (index !== -1) { return lookupDirection === "col" - ? returnRange.map((col) => [col[index]]) - : [returnRange[index]]; + ? _returnRange.map((col) => [col[index]]) + : [_returnRange[index]]; } if (defaultValue === undefined) { return valueNotAvailable(searchKey); @@ -48476,11 +48483,10 @@ class GridRenderer { switch (layer) { case "Background": this.drawGlobalBackground(renderingContext); - for (const zone of this.getters.getAllActiveViewportsZones()) { + for (const { zone, rect } of this.getters.getAllActiveViewportsZonesAndRect()) { const { ctx } = renderingContext; ctx.save(); ctx.beginPath(); - const rect = this.getters.getVisibleRect(zone); ctx.rect(rect.x, rect.y, rect.width, rect.height); ctx.clip(); const boxes = this.getGridBoxes(zone); @@ -48756,10 +48762,8 @@ class GridRenderer { const { ctx, thinLineWidth } = renderingContext; const visibleCols = this.getters.getSheetViewVisibleCols(); const left = visibleCols[0]; - const right = visibleCols[visibleCols.length - 1]; const visibleRows = this.getters.getSheetViewVisibleRows(); const top = visibleRows[0]; - const bottom = visibleRows[visibleRows.length - 1]; const { width, height } = this.getters.getSheetViewDimensionWithHeaders(); const selection = this.getters.getSelectedZones(); const selectedCols = getZonesCols(selection); @@ -48775,7 +48779,7 @@ class GridRenderer { ctx.lineWidth = thinLineWidth; ctx.strokeStyle = "#333"; // Columns headers background - for (let col = left; col <= right; col++) { + for (const col of visibleCols) { const colZone = { left: col, right: col, top: 0, bottom: numberOfRows - 1 }; const { x, width } = this.getters.getVisibleRect(colZone); const isColActive = activeCols.has(col); @@ -48792,7 +48796,7 @@ class GridRenderer { ctx.fillRect(x, 0, width, HEADER_HEIGHT); } // Rows headers background - for (let row = top; row <= bottom; row++) { + for (const row of visibleRows) { const rowZone = { top: row, bottom: row, left: 0, right: numberOfCols - 1 }; const { y, height } = this.getters.getVisibleRect(rowZone); const isRowActive = activeRows.has(row); @@ -48818,21 +48822,21 @@ class GridRenderer { ctx.stroke(); ctx.beginPath(); // column text + separator - for (const i of visibleCols) { - const colSize = this.getters.getColSize(sheetId, i); - const colName = numberToLetters(i); - ctx.fillStyle = activeCols.has(i) ? "#fff" : TEXT_HEADER_COLOR; - let colStart = this.getHeaderOffset("COL", left, i); + for (const col of visibleCols) { + const colSize = this.getters.getColSize(sheetId, col); + const colName = numberToLetters(col); + ctx.fillStyle = activeCols.has(col) ? "#fff" : TEXT_HEADER_COLOR; + let colStart = this.getHeaderOffset("COL", left, col); ctx.fillText(colName, colStart + colSize / 2, HEADER_HEIGHT / 2); ctx.moveTo(colStart + colSize, 0); ctx.lineTo(colStart + colSize, HEADER_HEIGHT); } // row text + separator - for (const i of visibleRows) { - const rowSize = this.getters.getRowSize(sheetId, i); - ctx.fillStyle = activeRows.has(i) ? "#fff" : TEXT_HEADER_COLOR; - let rowStart = this.getHeaderOffset("ROW", top, i); - ctx.fillText(String(i + 1), HEADER_WIDTH / 2, rowStart + rowSize / 2); + for (const row of visibleRows) { + const rowSize = this.getters.getRowSize(sheetId, row); + ctx.fillStyle = activeRows.has(row) ? "#fff" : TEXT_HEADER_COLOR; + let rowStart = this.getHeaderOffset("ROW", top, row); + ctx.fillText(String(row + 1), HEADER_WIDTH / 2, rowStart + rowSize / 2); ctx.moveTo(0, rowStart + rowSize); ctx.lineTo(HEADER_WIDTH, rowStart + rowSize); } @@ -49132,6 +49136,9 @@ function useGridDrawing(refName, model, canvasSize) { canvas.width = width * dpr; canvas.height = height * dpr; canvas.setAttribute("style", `width:${width}px;height:${height}px;`); + if (width === 0 || height === 0) { + return; + } // Imagine each pixel as a large square. The whole-number coordinates (0, 1, 2…) // are the edges of the squares. If you draw a one-unit-wide line between whole-number // coordinates, it will overlap opposite sides of the pixel square, and the resulting @@ -50703,7 +50710,7 @@ class BordersPlugin extends CorePlugin { getCommonSides(border1, border2) { const commonBorder = {}; for (let side of ["top", "bottom", "left", "right"]) { - if (border1[side] && border1[side] === border2[side]) { + if (border1[side] && deepEquals(border1[side], border2[side])) { commonBorder[side] = border1[side]; } } @@ -64973,8 +64980,17 @@ class InternalViewport { this.getters = getters; this.sheetId = sheetId; this.boundaries = boundaries; - this.viewportWidth = sizeInGrid.width; - this.viewportHeight = sizeInGrid.height; + if (sizeInGrid.width < 0 || sizeInGrid.height < 0) { + throw new Error("Viewport size cannot be negative"); + } + this.viewportWidth = sizeInGrid.height && sizeInGrid.width; + this.viewportHeight = sizeInGrid.width && sizeInGrid.height; + this.top = boundaries.top; + this.bottom = boundaries.bottom; + this.left = boundaries.left; + this.right = boundaries.right; + this.offsetX = offsets.x; + this.offsetY = offsets.y; this.offsetScrollbarX = offsets.x; this.offsetScrollbarY = offsets.y; this.canScrollVertically = options.canScrollVertically; @@ -65017,9 +65033,9 @@ class InternalViewport { Math.min(topRowSize, this.viewportHeight - lastRowSize) // Add pixels that allows the snapping at maximum vertical scroll ); height = Math.max(height, this.viewportHeight); // if the viewport grid size is smaller than its client height, return client height - } - if (lastRowEnd + FOOTER_HEIGHT > height && !this.getters.isReadonly()) { - height += FOOTER_HEIGHT; + if (lastRowEnd + FOOTER_HEIGHT > height && !this.getters.isReadonly()) { + height += FOOTER_HEIGHT; + } } return { width, height }; } @@ -65160,6 +65176,9 @@ class InternalViewport { !this.getters.isRowHidden(this.sheetId, row)); } searchHeaderIndex(dimension, position, startIndex = 0) { + if (this.viewportWidth <= 0 || this.viewportHeight <= 0) { + return -1; + } const sheetId = this.sheetId; const headers = this.getters.getNumberHeaders(sheetId, dimension); // using a binary search: @@ -65196,7 +65215,7 @@ class InternalViewport { this.adjustViewportZoneY(); } /** Corrects the viewport's horizontal offset based on the current structure - * To make sure that at least on column is visible inside the viewport. + * To make sure that at least one column is visible inside the viewport. */ adjustViewportOffsetX() { if (this.canScrollHorizontally) { @@ -65208,7 +65227,7 @@ class InternalViewport { this.adjustViewportZoneX(); } /** Corrects the viewport's vertical offset based on the current structure - * To make sure that at least on row is visible inside the viewport. + * To make sure that at least one row is visible inside the viewport. */ adjustViewportOffsetY() { if (this.canScrollVertically) { @@ -65225,11 +65244,14 @@ class InternalViewport { const sheetId = this.sheetId; this.left = this.searchHeaderIndex("COL", this.offsetScrollbarX, this.boundaries.left); this.right = Math.min(this.boundaries.right, this.searchHeaderIndex("COL", this.viewportWidth, this.left)); + if (!this.viewportWidth) { + return; + } if (this.left === -1) { this.left = this.boundaries.left; } if (this.right === -1) { - this.right = this.getters.getNumberCols(sheetId) - 1; + this.right = this.boundaries.right; } this.offsetX = this.getters.getColDimensions(sheetId, this.left).start - @@ -65241,11 +65263,14 @@ class InternalViewport { const sheetId = this.sheetId; this.top = this.searchHeaderIndex("ROW", this.offsetScrollbarY, this.boundaries.top); this.bottom = Math.min(this.boundaries.bottom, this.searchHeaderIndex("ROW", this.viewportHeight, this.top)); + if (!this.viewportHeight) { + return; + } if (this.top === -1) { this.top = this.boundaries.top; } if (this.bottom === -1) { - this.bottom = this.getters.getNumberRows(sheetId) - 1; + this.bottom = this.boundaries.bottom; } this.offsetY = this.getters.getRowDimensions(sheetId, this.top).start - @@ -65319,7 +65344,7 @@ class SheetViewPlugin extends UIPlugin { "isPositionVisible", "getColDimensionsInViewport", "getRowDimensionsInViewport", - "getAllActiveViewportsZones", + "getAllActiveViewportsZonesAndRect", "getRect", ]; viewports = {}; @@ -65552,12 +65577,12 @@ class SheetViewPlugin extends UIPlugin { const sheetId = this.getters.getActiveSheetId(); const viewports = this.getSubViewports(sheetId); //TODO ake another commit to eimprove this - return [...new Set(viewports.map((v) => range(v.left, v.right + 1)).flat())].filter((col) => !this.getters.isHeaderHidden(sheetId, "COL", col)); + return [...new Set(viewports.map((v) => range(v.left, v.right + 1)).flat())].filter((col) => col >= 0 && !this.getters.isHeaderHidden(sheetId, "COL", col)); } getSheetViewVisibleRows() { const sheetId = this.getters.getActiveSheetId(); const viewports = this.getSubViewports(sheetId); - return [...new Set(viewports.map((v) => range(v.top, v.bottom + 1)).flat())].filter((row) => !this.getters.isHeaderHidden(sheetId, "ROW", row)); + return [...new Set(viewports.map((v) => range(v.top, v.bottom + 1)).flat())].filter((row) => row >= 0 && !this.getters.isHeaderHidden(sheetId, "ROW", row)); } /** * Get the positions of all the cells that are visible in the viewport, taking merges into account. @@ -65600,19 +65625,19 @@ class SheetViewPlugin extends UIPlugin { maxOffsetY: Math.max(0, height - viewport.viewportHeight + 1), }; } - getColRowOffsetInViewport(dimension, referenceIndex, index) { - const sheetId = this.getters.getActiveSheetId(); - const visibleCols = this.getters.getSheetViewVisibleCols(); - const visibleRows = this.getters.getSheetViewVisibleRows(); - if (index < referenceIndex) { - return -this.getColRowOffsetInViewport(dimension, index, referenceIndex); + getColRowOffsetInViewport(dimension, referenceHeaderIndex, targetHeaderIndex) { + if (targetHeaderIndex < referenceHeaderIndex) { + return -this.getColRowOffsetInViewport(dimension, targetHeaderIndex, referenceHeaderIndex); } + const sheetId = this.getters.getActiveSheetId(); + const visibleHeaders = dimension === "COL" + ? this.getters.getSheetViewVisibleCols() + : this.getters.getSheetViewVisibleRows(); + const startIndex = visibleHeaders.findIndex((header) => referenceHeaderIndex >= header); + const endIndex = visibleHeaders.findIndex((header) => targetHeaderIndex <= header); + const relevantIndexes = visibleHeaders.slice(startIndex, endIndex); let offset = 0; - const visibleIndexes = dimension === "COL" ? visibleCols : visibleRows; - for (let i = referenceIndex; i < index; i++) { - if (!visibleIndexes.includes(i)) { - continue; - } + for (const i of relevantIndexes) { offset += this.getters.getHeaderSize(sheetId, dimension, i); } return offset; @@ -65659,7 +65684,7 @@ class SheetViewPlugin extends UIPlugin { } return { canEdgeScroll, direction, delay }; } - getEdgeScrollRow(y, previousY, tartingY) { + getEdgeScrollRow(y, previousY, startingY) { let canEdgeScroll = false; let direction = 0; let delay = 0; @@ -65680,7 +65705,7 @@ class SheetViewPlugin extends UIPlugin { delay = scrollDelay(y - height); direction = 1; } - else if (y < offsetCorrectionY && tartingY >= offsetCorrectionY && currentOffsetY > 0) { + else if (y < offsetCorrectionY && startingY >= offsetCorrectionY && currentOffsetY > 0) { // 2 canEdgeScroll = true; delay = scrollDelay(offsetCorrectionY - y); @@ -65706,13 +65731,7 @@ class SheetViewPlugin extends UIPlugin { */ getVisibleRectWithoutHeaders(zone) { const sheetId = this.getters.getActiveSheetId(); - const viewportRects = this.getSubViewports(sheetId) - .map((viewport) => viewport.getVisibleRect(zone)) - .filter(isDefined); - if (viewportRects.length === 0) { - return { x: 0, y: 0, width: 0, height: 0 }; - } - return this.recomposeRect(viewportRects); + return this.mapViewportsToRect(sheetId, (viewport) => viewport.getVisibleRect(zone)); } /** * Computes the actual size and position (:Rect) of the zone on the canvas @@ -65720,13 +65739,7 @@ class SheetViewPlugin extends UIPlugin { */ getRect(zone) { const sheetId = this.getters.getActiveSheetId(); - const viewportRects = this.getSubViewports(sheetId) - .map((viewport) => viewport.getFullRect(zone)) - .filter(isDefined); - if (viewportRects.length === 0) { - return { x: 0, y: 0, width: 0, height: 0 }; - } - const rect = this.recomposeRect(viewportRects); + const rect = this.mapViewportsToRect(sheetId, (viewport) => viewport.getFullRect(zone)); return { ...rect, x: rect.x + this.gridOffsetX, y: rect.y + this.gridOffsetY }; } /** @@ -65771,9 +65784,18 @@ class SheetViewPlugin extends UIPlugin { end: start + (isRowHidden ? 0 : size), }; } - getAllActiveViewportsZones() { + getAllActiveViewportsZonesAndRect() { const sheetId = this.getters.getActiveSheetId(); - return this.getSubViewports(sheetId); + return this.getSubViewports(sheetId).map((viewport) => { + return { + zone: viewport, + rect: { + x: viewport.offsetCorrectionX + this.gridOffsetX, + y: viewport.offsetCorrectionY + this.gridOffsetY, + ...viewport.getMaxSize(), + }, + }; + }); } // --------------------------------------------------------------------------- // Private @@ -65832,12 +65854,11 @@ class SheetViewPlugin extends UIPlugin { } /** gets rid of deprecated sheetIds */ cleanViewports() { - const sheetIds = this.getters.getSheetIds(); - for (let sheetId of Object.keys(this.viewports)) { - if (!sheetIds.includes(sheetId)) { - delete this.viewports[sheetId]; - } + const newViewport = {}; + for (const sheetId of this.getters.getSheetIds()) { + newViewport[sheetId] = this.viewports[sheetId]; } + this.viewports = newViewport; } resizeSheetView(height, width, gridOffsetX = 0, gridOffsetY = 0) { this.sheetViewHeight = height; @@ -65847,7 +65868,7 @@ class SheetViewPlugin extends UIPlugin { this.recomputeViewports(); } recomputeViewports() { - for (let sheetId of Object.keys(this.viewports)) { + for (const sheetId of this.getters.getSheetIds()) { this.resetViewports(sheetId); } } @@ -65869,8 +65890,10 @@ class SheetViewPlugin extends UIPlugin { const { xSplit, ySplit } = this.getters.getPaneDivisions(sheetId); const nCols = this.getters.getNumberCols(sheetId); const nRows = this.getters.getNumberRows(sheetId); - const colOffset = this.getters.getColRowOffset("COL", 0, xSplit, sheetId); - const rowOffset = this.getters.getColRowOffset("ROW", 0, ySplit, sheetId); + const colOffset = Math.min(this.getters.getColRowOffset("COL", 0, xSplit, sheetId), this.sheetViewWidth); + const rowOffset = Math.min(this.getters.getColRowOffset("ROW", 0, ySplit, sheetId), this.sheetViewHeight); + const unfrozenWidth = Math.max(this.sheetViewWidth - colOffset, 0); + const unfrozenHeight = Math.max(this.sheetViewHeight - rowOffset, 0); const { xRatio, yRatio } = this.getFrozenSheetViewRatio(sheetId); const canScrollHorizontally = xRatio < 1.0; const canScrollVertically = yRatio < 1.0; @@ -65881,14 +65904,14 @@ class SheetViewPlugin extends UIPlugin { new InternalViewport(this.getters, sheetId, { left: 0, right: xSplit - 1, top: 0, bottom: ySplit - 1 }, { width: colOffset, height: rowOffset }, { canScrollHorizontally: false, canScrollVertically: false }, { x: 0, y: 0 })) || undefined, topRight: (ySplit && - new InternalViewport(this.getters, sheetId, { left: xSplit, right: nCols - 1, top: 0, bottom: ySplit - 1 }, { width: this.sheetViewWidth - colOffset, height: rowOffset }, { canScrollHorizontally, canScrollVertically: false }, { x: canScrollHorizontally ? previousOffset.x : 0, y: 0 })) || + new InternalViewport(this.getters, sheetId, { left: xSplit, right: nCols - 1, top: 0, bottom: ySplit - 1 }, { width: unfrozenWidth, height: rowOffset }, { canScrollHorizontally, canScrollVertically: false }, { x: canScrollHorizontally ? previousOffset.x : 0, y: 0 })) || undefined, bottomLeft: (xSplit && - new InternalViewport(this.getters, sheetId, { left: 0, right: xSplit - 1, top: ySplit, bottom: nRows - 1 }, { width: colOffset, height: this.sheetViewHeight - rowOffset }, { canScrollHorizontally: false, canScrollVertically }, { x: 0, y: canScrollVertically ? previousOffset.y : 0 })) || + new InternalViewport(this.getters, sheetId, { left: 0, right: xSplit - 1, top: ySplit, bottom: nRows - 1 }, { width: colOffset, height: unfrozenHeight }, { canScrollHorizontally: false, canScrollVertically }, { x: 0, y: canScrollVertically ? previousOffset.y : 0 })) || undefined, bottomRight: new InternalViewport(this.getters, sheetId, { left: xSplit, right: nCols - 1, top: ySplit, bottom: nRows - 1 }, { - width: this.sheetViewWidth - colOffset, - height: this.sheetViewHeight - rowOffset, + width: unfrozenWidth, + height: unfrozenHeight, }, { canScrollHorizontally, canScrollVertically }, { x: canScrollHorizontally ? previousOffset.x : 0, y: canScrollVertically ? previousOffset.y : 0, @@ -65965,12 +65988,26 @@ class SheetViewPlugin extends UIPlugin { const height = this.sheetViewHeight + this.gridOffsetY; return { xRatio: offsetCorrectionX / width, yRatio: offsetCorrectionY / height }; } - recomposeRect(viewportRects) { - const x = Math.min(...viewportRects.map((rect) => rect.x)); - const y = Math.min(...viewportRects.map((rect) => rect.y)); - const width = Math.max(...viewportRects.map((rect) => rect.x + rect.width)) - x; - const height = Math.max(...viewportRects.map((rect) => rect.y + rect.height)) - y; - return { x, y, width, height }; + mapViewportsToRect(sheetId, rectCallBack) { + let x = Infinity; + let y = Infinity; + let width = 0; + let height = 0; + let hasViewports = false; + for (const viewport of this.getSubViewports(sheetId)) { + const rect = rectCallBack(viewport); + if (rect) { + hasViewports = true; + x = Math.min(x, rect.x); + y = Math.min(y, rect.y); + width = Math.max(width, rect.x + rect.width); + height = Math.max(height, rect.y + rect.height); + } + } + if (!hasViewports) { + return { x: 0, y: 0, width: 0, height: 0 }; + } + return { x, y, width: width - x, height: height - y }; } } @@ -73088,7 +73125,7 @@ const constants = { export { AbstractCellClipboardHandler, AbstractChart, AbstractFigureClipboardHandler, CellErrorType, CommandResult, CorePlugin, DispatchResult, EvaluationError, Model, PivotRuntimeDefinition, Registry, Revision, SPREADSHEET_DIMENSIONS, Spreadsheet, SpreadsheetPivotTable, UIPlugin, __info__, addFunction, addRenderingLayer, astToFormula, 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 }; -__info__.version = "18.0.13"; -__info__.date = "2025-01-31T08:06:35.677Z"; -__info__.hash = "f505971d4"; +__info__.version = "18.0.14"; +__info__.date = "2025-02-05T07:24:11.049Z"; +__info__.hash = "90f2af4ff"; //# sourceMappingURL=o_spreadsheet.js.map diff --git a/addons/spreadsheet/static/src/o_spreadsheet/o_spreadsheet.xml b/addons/spreadsheet/static/src/o_spreadsheet/o_spreadsheet.xml index 5b8b107da3a..371fe1b376e 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 @@ <!-- This file is generated by o-spreadsheet build tools. Do not edit it. @see https://github.com/odoo/o-spreadsheet - @version 18.0.13 - @date 2025-01-31T08:06:39.800Z - @hash f505971d4 + @version 18.0.14 + @date 2025-02-05T07:24:15.390Z + @hash 90f2af4ff --> <odoo> <t t-name="o-spreadsheet-ActionButton"> |
