From d27d9a475730a9a33b9db06e0f8f6b6c65beadba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lucas=20Lef=C3=A8vre=20=28lul=29?= Date: Fri, 24 Nov 2023 14:28:12 +0100 Subject: [PATCH] [FIX] spreadsheet: update o_spreadsheet to latest version ### Contains the following commits: https://github.com/odoo/o-spreadsheet/commit/7346d0af9 [REL] 17.0.4 https://github.com/odoo/o-spreadsheet/commit/661e290f4 [FIX] sheet: remove zombie code https://github.com/odoo/o-spreadsheet/commit/7362fc105 [FIX] sheet: remove rows faster https://github.com/odoo/o-spreadsheet/commit/dcaa1ab0c [FIX] tokenizer: faster current char lookup https://github.com/odoo/o-spreadsheet/commit/0448b26f5 [FIX] tokenize: replace special chars faster https://github.com/odoo/o-spreadsheet/commit/88a57c6be [FIX] icons: use correct comment syntax in xml files https://github.com/odoo/o-spreadsheet/commit/cf8194df2 [FIX] clipboard: fix array formula copy https://github.com/odoo/o-spreadsheet/commit/ca24d7e8c [FIX] evaluation: faster range matrix https://github.com/odoo/o-spreadsheet/commit/a13ff80b3 [FIX] clipboard: traceback on paste from copy after deleting that sheet https://github.com/odoo/o-spreadsheet/commit/eaa0dca12 [FIX] evaluation: cache range references values Task: 3588401 https://github.com/odoo/o-spreadsheet/commit/cdeb716c3 [REF] evaluation: group compilation parameters https://github.com/odoo/o-spreadsheet/commit/943a7e7f8 [FIX] lookup: cache string normalization Task: 3588401 https://github.com/odoo/o-spreadsheet/commit/0330db03c [FIX] cells,borders: cancel useless commands Task: 3603259 https://github.com/odoo/o-spreadsheet/commit/38d77fe89 [FIX] cells: faster getter to get cell Task: 3588401 https://github.com/odoo/o-spreadsheet/commit/15bdafc80 [FIX] model: don't flatten command result closes odoo/odoo#143587 Signed-off-by: Vincent Schippefilt (vsc) --- .../static/src/o_spreadsheet/o_spreadsheet.js | 333 ++++++++++-------- .../src/o_spreadsheet/o_spreadsheet.xml | 13 +- 2 files changed, 187 insertions(+), 159 deletions(-) diff --git a/addons/spreadsheet/static/src/o_spreadsheet/o_spreadsheet.js b/addons/spreadsheet/static/src/o_spreadsheet/o_spreadsheet.js index 00d35dad4cd..07f13329d0c 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.3 - * @date 2023-11-16T13:27:10.083Z - * @hash 8bd0fb2b4 + * @version 17.0.4 + * @date 2023-11-24T13:28:06.767Z + * @hash 7346d0af9 */ (function (exports, owl) { @@ -692,8 +692,7 @@ * * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions/Character_Classes */ - const whiteSpaceCharacters = [ - " ", + const whiteSpaceSpecialCharacters = [ "\t", "\f", "\v", @@ -708,8 +707,7 @@ String.fromCharCode(parseInt("3000", 16)), String.fromCharCode(parseInt("feff", 16)), ]; - const whiteSpaceRegexp = new RegExp(whiteSpaceCharacters.join("|"), "g"); - const newLineRegex = /\r\n|\r|\n/g; + const whiteSpaceRegexp = new RegExp(whiteSpaceSpecialCharacters.join("|") + "|(\r\n|\r|\n)", "g"); /** * Replace all the special spaces in a string (non-breaking, tabs, ...) by normal spaces, and all the * different newlines types by \n. @@ -717,9 +715,9 @@ function replaceSpecialSpaces(text) { if (!text) return ""; - text = text.replace(whiteSpaceRegexp, " "); - text = text.replace(newLineRegex, NEWLINE); - return text; + if (!whiteSpaceRegexp.test(text)) + return text; + return text.replace(whiteSpaceRegexp, (match, newLine) => (newLine ? NEWLINE : " ")); } /** * Determine if the numbers are consecutive. @@ -2061,6 +2059,7 @@ CommandResult["InvalidNumberOfCriterionValues"] = "InvalidNumberOfCriterionValues"; CommandResult["BlockingValidationRule"] = "BlockingValidationRule"; CommandResult["InvalidCopyPasteSelection"] = "InvalidCopyPasteSelection"; + CommandResult["NoChanges"] = "NoChanges"; })(exports.CommandResult || (exports.CommandResult = {})); const DEFAULT_LOCALES = [ @@ -2190,12 +2189,12 @@ } } /** Normalize string by setting it to lowercase and replacing accent letters with plain letters */ - function normalizeString(str) { + const normalizeString = memoize(function normalizeString(str) { return str .toLowerCase() .normalize("NFD") .replace(/[\u0300-\u036f]/g, ""); - } + }); const expectBooleanValueError = (value) => _t("The function [[FUNCTION_NAME]] expects a boolean value, but '%s' is a text, and cannot be coerced to a number.", value); function toBoolean(value) { switch (typeof value) { @@ -7330,7 +7329,7 @@ return result; } function tokenizeDebugger(chars) { - if (chars.current() === "?") { + if (chars.current === "?") { chars.shift(); return { type: "DEBUGGER", value: "?" }; } @@ -7341,7 +7340,7 @@ ")": "RIGHT_PAREN", }; function tokenizeMisc(chars) { - if (chars.current() in misc$1) { + if (chars.current in misc$1) { const value = chars.shift(); const type = misc$1[value]; return { type, value }; @@ -7349,7 +7348,7 @@ return null; } function tokenizeArgsSeparator(chars, locale) { - if (chars.current() === locale.formulaArgSeparator) { + if (chars.current === locale.formulaArgSeparator) { const value = chars.shift(); const type = "ARG_SEPARATOR"; return { type, value }; @@ -7374,14 +7373,13 @@ return null; } function tokenizeString(chars) { - if (chars.current() === '"') { + if (chars.current === '"') { const startChar = chars.shift(); let letters = startChar; - while (chars.current() && - (chars.current() !== startChar || letters[letters.length - 1] === "\\")) { + while (chars.current && (chars.current !== startChar || letters[letters.length - 1] === "\\")) { letters += chars.shift(); } - if (chars.current() === '"') { + if (chars.current === '"') { letters += chars.shift(); } return { @@ -7408,14 +7406,14 @@ let result = ""; // there are two main cases to manage: either something which starts with // a ', like 'Sheet 2'A2, or a word-like element. - if (chars.current() === "'") { + if (chars.current === "'") { let lastChar = chars.shift(); result += lastChar; - while (chars.current()) { + while (chars.current) { lastChar = chars.shift(); result += lastChar; if (lastChar === "'") { - if (chars.current() && chars.current() === "'") { + if (chars.current && chars.current === "'") { lastChar = chars.shift(); result += lastChar; } @@ -7431,7 +7429,7 @@ }; } } - while (chars.current() && separatorRegexp.test(chars.current())) { + while (chars.current && separatorRegexp.test(chars.current)) { result += chars.shift(); } if (result.length) { @@ -7446,14 +7444,14 @@ } function tokenizeSpace(chars) { let length = 0; - while (chars.current() === NEWLINE) { + while (chars.current === NEWLINE) { length++; chars.shift(); } if (length) { return { type: "SPACE", value: NEWLINE.repeat(length) }; } - while (chars.current() === " ") { + while (chars.current === " ") { length++; chars.shift(); } @@ -7472,17 +7470,20 @@ class TokenizingChars { text; currentIndex = 0; + current; constructor(text) { this.text = text; - } - current() { - return this.text[this.currentIndex]; + this.current = text[0]; } shift() { - return this.text[this.currentIndex++]; + const current = this.current; + const next = this.text[++this.currentIndex]; + this.current = next; + return current; } advanceBy(length) { this.currentIndex += length; + this.current = this.text[this.currentIndex]; } isOver() { return this.currentIndex >= this.text.length; @@ -29758,6 +29759,7 @@ const content = clipboardData.getData(ClipboardMIMEType.PlainText); const target = this.env.model.getters.getSelectedZones(); const clipboardString = this.env.model.getters.getClipboardTextContent(); + const isCutOperation = this.env.model.getters.isCutOperation(); if (clipboardString === content) { // the paste actually comes from o-spreadsheet itself interactivePaste(this.env, target); @@ -29765,7 +29767,7 @@ else { interactivePasteFromOS(this.env, target, content); } - if (this.env.model.getters.isCutOperation()) { + if (isCutOperation) { await this.env.clipboard.write({ [ClipboardMIMEType.PlainText]: "" }); } } @@ -34059,6 +34061,14 @@ // --------------------------------------------------------------------------- // Command Handling // --------------------------------------------------------------------------- + allowDispatch(cmd) { + switch (cmd.type) { + case "SET_BORDER": + return this.checkBordersUnchanged(cmd); + default: + return "Success" /* CommandResult.Success */; + } + } handle(cmd) { switch (cmd.type) { case "ADD_MERGE": @@ -34478,6 +34488,14 @@ this.setBorders(sheetId, [{ ...zone, left: right }], "right", bordersTopLeft.right); } } + checkBordersUnchanged(cmd) { + const currentBorder = this.getCellBorder(cmd); + const areAllNewBordersUndefined = !cmd.border?.bottom && !cmd.border?.left && !cmd.border?.right && !cmd.border?.top; + if ((!currentBorder && areAllNewBordersUndefined) || deepEquals(currentBorder, cmd.border)) { + return "NoChanges" /* CommandResult.NoChanges */; + } + return "Success" /* CommandResult.Success */; + } // --------------------------------------------------------------------------- // Import/Export // --------------------------------------------------------------------------- @@ -35333,8 +35351,9 @@ allowDispatch(cmd) { switch (cmd.type) { case "UPDATE_CELL": + return this.checkCellOutOfSheet(cmd); case "CLEAR_CELL": - return this.checkCellOutOfSheet(cmd.sheetId, cmd.col, cmd.row); + return this.checkValidations(cmd, this.chainValidations(this.checkCellOutOfSheet, this.checkUselessClearCell)); default: return "Success" /* CommandResult.Success */; } @@ -35503,14 +35522,9 @@ */ getCellById(cellId) { // this must be as fast as possible - for (const sheetId in this.cells) { - const sheet = this.cells[sheetId]; - const cell = sheet[cellId]; - if (cell) { - return cell; - } - } - return undefined; + const position = this.getters.getCellPosition(cellId); + const sheet = this.cells[position.sheetId]; + return sheet[cellId]; } /* * Reconstructs the original formula string based on a normalized form and its dependencies @@ -35741,13 +35755,23 @@ }, }; } - checkCellOutOfSheet(sheetId, col, row) { + checkCellOutOfSheet(cmd) { + const { sheetId, col, row } = cmd; const sheet = this.getters.tryGetSheet(sheetId); if (!sheet) return "InvalidSheetId" /* CommandResult.InvalidSheetId */; const sheetZone = this.getters.getSheetZone(sheetId); return isInside(col, row, sheetZone) ? "Success" /* CommandResult.Success */ : "TargetOutOfSheet" /* CommandResult.TargetOutOfSheet */; } + checkUselessClearCell(cmd) { + const cell = this.getters.getCell(cmd); + if (!cell) + return "NoChanges" /* CommandResult.NoChanges */; + if (!cell.content && !cell.style && !cell.format) { + return "NoChanges" /* CommandResult.NoChanges */; + } + return "Success" /* CommandResult.Success */; + } } class FormulaCellWithDependencies { id; @@ -36437,6 +36461,9 @@ return this.rules[sheetId].find((rule) => rule.id === id); } getValidationRuleForCell({ sheetId, col, row }) { + if (!this.rules[sheetId]) { + return undefined; + } for (const rule of this.rules[sheetId]) { for (const range of rule.ranges) { if (isInside(col, row, range.zone)) { @@ -39034,10 +39061,13 @@ // begin with the end. rows.sort((a, b) => b - a); for (let group of groupConsecutive(rows)) { + // indexes are sorted in the descending order + const from = group[group.length - 1]; + const to = group[0]; // Move the cells. - this.moveCellOnRowsDeletion(sheet, group[group.length - 1], group[0]); - // Effectively delete the element and recompute the left-right/top-bottom. - group.map((row) => this.updateRowsStructureOnDeletion(row, sheet)); + this.moveCellOnRowsDeletion(sheet, from, to); + // Effectively delete the rows + this.updateRowsStructureOnDeletion(sheet, from, to); } const count = rows.filter((row) => row < sheet.panes.ySplit).length; if (count) { @@ -39059,8 +39089,6 @@ this.addEmptyRows(sheet, quantity); // Move the cells. this.moveCellsOnAddition(sheet, index, quantity, "rows"); - // Recompute the left-right/top-bottom. - this.updateRowsStructureOnAddition(sheet, row, quantity); if (index < sheet.panes.ySplit) { this.setPaneDivisions(sheet.id, sheet.panes.ySplit + quantity, "ROW"); } @@ -39161,35 +39189,20 @@ } } } - updateRowsStructureOnDeletion(index, sheet) { + updateRowsStructureOnDeletion(sheet, deleteFromRow, deleteToRow) { const rows = []; - const cellsQueue = sheet.rows.map((row) => row.cells); + const cellsQueue = sheet.rows.map((row) => row.cells).reverse(); for (let i in sheet.rows) { - if (Number(i) === index) { + const row = Number(i); + if (row >= deleteFromRow && row <= deleteToRow) { continue; } rows.push({ - cells: cellsQueue.shift(), + cells: cellsQueue.pop(), }); } this.history.update("sheets", sheet.id, "rows", rows); } - /** - * Update the rows of the sheet after an addition: - * - Rename the rows - * - * @param sheet Sheet on which the deletion occurs - * @param addedRow Index of the added row - * @param rowsToAdd Number of the rows to add - */ - updateRowsStructureOnAddition(sheet, addedRow, rowsToAdd) { - const rows = []; - const cellsQueue = sheet.rows.map((row) => row.cells); - sheet.rows.forEach(() => rows.push({ - cells: cellsQueue.shift(), - })); - this.history.update("sheets", sheet.id, "rows", rows); - } /** * Add empty rows at the end of the rows * @@ -39716,6 +39729,7 @@ getters; computeCell; evalContext; + rangeCache = {}; constructor(context, getters, computeCell) { this.getters = getters; this.computeCell = computeCell; @@ -39801,17 +39815,24 @@ if (!_zone) { return [[]]; } + const { top, left, bottom, right } = zone; + const cacheKey = `${sheetId}-${top}-${left}-${bottom}-${right}`; + if (cacheKey in this.rangeCache) { + return this.rangeCache[cacheKey]; + } const height = _zone.bottom - _zone.top + 1; const width = _zone.right - _zone.left + 1; - const matrix = Array.from({ length: width }, () => Array.from({ length: height })); + const matrix = new Array(width); // Performance issue: nested loop is faster than a map here for (let col = _zone.left; col <= _zone.right; col++) { + const colIndex = col - _zone.left; + matrix[colIndex] = new Array(height); for (let row = _zone.top; row <= _zone.bottom; row++) { - const colIndex = col - _zone.left; const rowIndex = row - _zone.top; matrix[colIndex][rowIndex] = this.readCell({ sheetId, col, row }); } } + this.rangeCache[cacheKey] = matrix; return matrix; } } @@ -40017,6 +40038,7 @@ this.formulaDependencies().addDependencies(positionId, dependencies); } updateCompilationParameters() { + // rebuild the compilation parameters (with a clean cache) this.compilationParams = buildCompilationParameters(this.context, this.getters, this.computeAndSave.bind(this)); } evaluateCells(positions) { @@ -40060,6 +40082,16 @@ this.evaluatedCells = new Map(); this.evaluate(this.getAllCells()); } + evaluateFormula(sheetId, formulaString) { + const compiledFormula = compile(formulaString); + const ranges = compiledFormula.dependencies.map((xc) => this.getters.getRangeFromSheetXC(sheetId, xc)); + this.updateCompilationParameters(); + const array = compiledFormula.execute(ranges, ...this.compilationParams); + if (isMatrix(array)) { + return matrixMap(array, (cell) => cell.value); + } + return array.value; + } getAllCells() { const positionIds = new JetSet(); for (const sheetId of this.getters.getSheetIds()) { @@ -40086,6 +40118,7 @@ this.nextPositionsToUpdate = cells; let currentIteration = 0; while (this.nextPositionsToUpdate.size && currentIteration++ < MAX_ITERATION) { + this.updateCompilationParameters(); const positionIds = Array.from(this.nextPositionsToUpdate); this.nextPositionsToUpdate.clear(); for (let i = 0; i < positionIds.length; ++i) { @@ -40449,7 +40482,6 @@ // of other cells depending on it, at the next iteration. //#endregion class EvaluationPlugin extends UIPlugin { - config; static getters = [ "evaluateFormula", "getCorrespondingFormulaCell", @@ -40460,15 +40492,13 @@ "getEvaluatedCells", "getEvaluatedCellsInZone", "getSpreadPositionsOf", + "getArrayFormulaSpreadingOn", ]; shouldRebuildDependenciesGraph = true; evaluator; - compilationParams; positionsToUpdate = []; constructor(config) { super(config); - this.config = config; - this.compilationParams = this.getCompilationParameters(); this.evaluator = new Evaluator(config.custom, this.getters); } // --------------------------------------------------------------------------- @@ -40493,10 +40523,6 @@ case "EVALUATE_CELLS": this.evaluator.evaluateAllCells(); break; - case "UPDATE_LOCALE": - this.compilationParams = this.getCompilationParameters(); - this.evaluator.updateCompilationParameters(); - break; } } finalize() { @@ -40514,16 +40540,7 @@ // Getters // --------------------------------------------------------------------------- evaluateFormula(sheetId, formulaString) { - const compiledFormula = compile(formulaString); - const ranges = []; - for (let xc of compiledFormula.dependencies) { - ranges.push(this.getters.getRangeFromSheetXC(sheetId, xc)); - } - const array = compiledFormula.execute(ranges, ...this.compilationParams); - if (isMatrix(array)) { - return array.map((col) => col.map((row) => row.value)); - } - return array.value; + return this.evaluator.evaluateFormula(sheetId, formulaString); } /** * Return the value of each cell in the range as they are displayed in the grid. @@ -40572,6 +40589,9 @@ getSpreadPositionsOf(position) { return this.evaluator.getSpreadPositionsOf(position); } + getArrayFormulaSpreadingOn(position) { + return this.evaluator.getArrayFormulaSpreadingOn(position); + } // --------------------------------------------------------------------------- // Export // --------------------------------------------------------------------------- @@ -40615,7 +40635,7 @@ else if (cell && cell.content) { return undefined; } - const spreadingFormulaPosition = this.evaluator.getArrayFormulaSpreadingOn(position); + const spreadingFormulaPosition = this.getArrayFormulaSpreadingOn(position); if (spreadingFormulaPosition === undefined) { return undefined; } @@ -40625,9 +40645,6 @@ } return undefined; } - getCompilationParameters() { - return buildCompilationParameters(this.config.custom, this.getters, (position) => this.evaluator.getEvaluatedCell(position)); - } } function isBadExpression(tokens) { try { @@ -43326,11 +43343,26 @@ let cellsInRow = []; for (let col of columnsIndex) { const position = { col, row, sheetId }; + const spreader = getters.getArrayFormulaSpreadingOn(position); + let cell = getters.getCell(position); + const evaluatedCell = getters.getEvaluatedCell(position); + if (spreader) { + const isSpreaderCopied = rowsIndex.includes(spreader.row) && columnsIndex.includes(spreader.col); + const content = isSpreaderCopied + ? "" + : formatValue(evaluatedCell.value, { locale: getters.getLocale() }); + cell = { + id: cell?.id || "", + style: cell?.style, + format: evaluatedCell.format, + content, + isFormula: false, + }; + } cellsInRow.push({ - cell: getters.getCell(position), - style: getters.getCellComputedStyle(position), - evaluatedCell: getters.getEvaluatedCell(position), + cell, border: getters.getCellBorder(position) || undefined, + evaluatedCell, position, }); } @@ -43452,13 +43484,6 @@ }); } this.pasteCopiedTables(target); - this.cells.forEach((row) => { - row.forEach((c) => { - if (c.cell) { - c.cell = undefined; - } - }); - }); } /** * The clipped zone is copied as many times as it fits in the target. @@ -43530,7 +43555,7 @@ clearClippedZones() { for (const row of this.cells) { for (const cell of row) { - if (cell.cell) { + if (cell?.cell) { this.dispatch("CLEAR_CELL", cell.position); } } @@ -43556,6 +43581,9 @@ const rowCells = this.cells[r]; for (let c = 0; c < width; c++) { const origin = rowCells[c]; + if (!origin) { + continue; + } const position = { col: col + c, row: row + r, sheetId: sheetId }; // TODO: refactor this part. the "Paste merge" action is also executed with // MOVE_RANGES in pasteFromCut. Adding a condition on the operation type here @@ -43579,53 +43607,42 @@ pasteCell(origin, target, operation, clipboardOption) { const { sheetId, col, row } = target; const targetCell = this.getters.getEvaluatedCell(target); - if (clipboardOption?.pasteOption !== "onlyValue") { - const targetBorders = this.getters.getCellBorder(target); - const originBorders = origin.border; - const border = { - top: targetBorders?.top || originBorders?.top, - bottom: targetBorders?.bottom || originBorders?.bottom, - left: targetBorders?.left || originBorders?.left, - right: targetBorders?.right || originBorders?.right, - }; - this.dispatch("SET_BORDER", { sheetId, col, row, border }); + if (clipboardOption?.pasteOption === "onlyValue") { + const locale = this.getters.getLocale(); + const content = formatValue(origin.evaluatedCell.value, { locale }); + this.dispatch("UPDATE_CELL", { ...target, content }); + return; } - if (origin.cell) { - if (clipboardOption?.pasteOption === "onlyFormat") { - this.dispatch("UPDATE_CELL", { - ...target, - style: origin.cell.style, - format: origin.evaluatedCell.format, - }); - return; - } - if (clipboardOption?.pasteOption === "onlyValue") { - const locale = this.getters.getLocale(); - const content = formatValue(origin.evaluatedCell.value, { locale }); - this.dispatch("UPDATE_CELL", { ...target, content }); - return; - } - let content = origin.cell.content; - if (origin.cell.isFormula && operation === "COPY") { - content = this.getters.getTranslatedCellFormula(sheetId, col - origin.position.col, row - origin.position.row, origin.cell.compiledFormula); - } + const targetBorders = this.getters.getCellBorder(target); + const originBorders = origin.border; + const border = { + top: targetBorders?.top || originBorders?.top, + bottom: targetBorders?.bottom || originBorders?.bottom, + left: targetBorders?.left || originBorders?.left, + right: targetBorders?.right || originBorders?.right, + }; + this.dispatch("SET_BORDER", { sheetId, col, row, border }); + if (clipboardOption?.pasteOption === "onlyFormat") { + this.dispatch("UPDATE_CELL", { + ...target, + style: origin.cell?.style ?? null, + format: origin.cell?.format ?? origin.evaluatedCell.format ?? targetCell.format, + }); + return; + } + const content = origin.cell && origin.cell.isFormula && operation === "COPY" + ? this.getters.getTranslatedCellFormula(sheetId, col - origin.position.col, row - origin.position.row, origin.cell.compiledFormula) + : origin.cell?.content; + if (content !== "" || origin.cell?.format || origin.cell?.style) { this.dispatch("UPDATE_CELL", { ...target, content, - style: origin.cell.style || null, - format: origin.cell.format, + style: origin.cell?.style || null, + format: origin.cell?.format, }); } else if (targetCell) { - if (clipboardOption?.pasteOption === "onlyValue") { - this.dispatch("UPDATE_CELL", { ...target, content: "" }); - } - else if (clipboardOption?.pasteOption === "onlyFormat") { - this.dispatch("UPDATE_CELL", { ...target, style: null, format: "" }); - } - else { - this.dispatch("CLEAR_CELL", target); - } + this.dispatch("CLEAR_CELL", target); } } /** @@ -43687,22 +43704,28 @@ return (this.cells .map((cells) => { return cells - .map((c) => this.getters.shouldShowFormulas() && c.cell?.isFormula + .map((c) => this.getters.shouldShowFormulas() && c?.cell?.isFormula ? c.cell?.content || "" - : c.evaluatedCell?.formattedValue || "") + : c?.evaluatedCell?.formattedValue || "") .join("\t"); }) .join("\n") || "\t"); } getHTMLContent() { if (this.cells.length === 1 && this.cells[0].length === 1) { + if (!this.cells[0][0]) { + return ""; + } return this.getters.getCellText(this.cells[0][0].position); } let htmlTable = ''; for (const row of this.cells) { htmlTable += ""; for (const cell of row) { - const cssStyle = cssPropertiesToCss(cellStyleToCss(cell.style)); + if (!cell) { + continue; + } + const cssStyle = cssPropertiesToCss(cellStyleToCss(this.getters.getCellComputedStyle(cell.position))); const cellText = this.getters.getCellText(cell.position); htmlTable += `"; } @@ -46832,11 +46855,14 @@ } const pasteOption = cmd.pasteOption || (this.paintFormatStatus !== "inactive" ? "onlyFormat" : undefined); this.state.paste(cmd.target, { pasteOption, shouldPasteCF: true, selectTarget: true }); + if (this.state.operation === "CUT") { + this.state = undefined; + } this.lastPasteState = this.state; if (this.paintFormatStatus === "oneOff") { this.paintFormatStatus = "inactive"; - this.status = "invisible"; } + this.status = "invisible"; break; case "COPY_PASTE_CELLS_ABOVE": { @@ -55560,19 +55586,22 @@ * Check if the given command is allowed by all the plugins and the history. */ checkDispatchAllowed(command) { - if (isCoreCommand(command)) { - return this.checkDispatchAllowedCoreCommand(command); + const results = isCoreCommand(command) + ? this.checkDispatchAllowedCoreCommand(command) + : this.checkDispatchAllowedLocalCommand(command); + if (results.some((r) => r !== "Success" /* CommandResult.Success */)) { + return new DispatchResult(results.flat()); } - return this.checkDispatchAllowedLocalCommand(command); + return DispatchResult.Success; } checkDispatchAllowedCoreCommand(command) { const results = this.corePlugins.map((handler) => handler.allowDispatch(command)); results.push(this.range.allowDispatch(command)); - return new DispatchResult(results.flat()); + return results; } checkDispatchAllowedLocalCommand(command) { const results = this.uiHandlers.map((handler) => handler.allowDispatch(command)); - return new DispatchResult(results.flat()); + return results; } finalize() { this.status = 3 /* Status.Finalizing */; @@ -55919,9 +55948,9 @@ exports.tokenize = tokenize; - __info__.version = "17.0.3"; - __info__.date = "2023-11-16T13:27:10.083Z"; - __info__.hash = "8bd0fb2b4"; + __info__.version = "17.0.4"; + __info__.date = "2023-11-24T13:28:06.767Z"; + __info__.hash = "7346d0af9"; })(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 c79ca7a4257..1ba36b8fb25 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 @@ @@ -1963,10 +1963,9 @@ /> - /** Font Awesome by Dave Gandy - * http://fontawesome.io/ - * https://fontawesome.com/license - */ +
` + xmlEscape(cellText) + "