[IMP] spreadsheet: update o_spreadsheet to latest version

### Contains the following commits:

https://github.com/odoo/o-spreadsheet/commit/4dc340fc [REL] 16.5.0-alpha.10
https://github.com/odoo/o-spreadsheet/commit/c6e5d58e [IMP] dates: cache date parsing
https://github.com/odoo/o-spreadsheet/commit/38e44577 [IMP] range: don't spend time building a zone
https://github.com/odoo/o-spreadsheet/commit/7e491948 [IMP] cell: faster formula string
https://github.com/odoo/o-spreadsheet/commit/d6a498b4 [FIX] evaluation: don't build formula content for errors
https://github.com/odoo/o-spreadsheet/commit/5e2a7640 [IMP] evaluation: don't create useless Errors
https://github.com/odoo/o-spreadsheet/commit/d5ff0843 [IMP] Composer: highlight fuzzy matched letters in autocomplete dropdown
https://github.com/odoo/o-spreadsheet/commit/3562883e [IMP] Composer: always sort autocomplete elements
https://github.com/odoo/o-spreadsheet/commit/e8d76dd3 [IMP] Composer: Select autocomplete results on mouseover Task: 3387413
https://github.com/odoo/o-spreadsheet/commit/b6dc8877 [IMP] evaluation: don't spend time building formula string
https://github.com/odoo/o-spreadsheet/commit/078e888d [FIX] evaluation: compute cell only once

closes odoo/odoo#138145

Signed-off-by: Rémi Rahir (rar) <rar@odoo.com>
This commit is contained in:
Lucas Lefèvre (lul)
2023-10-11 17:02:10 +00:00
parent 13b3f8af4b
commit 8600d5d363
2 changed files with 103 additions and 41 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 16.5.0-alpha.9
* @date 2023-10-03T13:36:38.605Z
* @hash c46de914
* @version 16.5.0-alpha.10
* @date 2023-10-10T08:16:10.236Z
* @hash 4dc340fc
*/
(function (exports, owl) {
@@ -131,6 +131,7 @@
const GRID_BORDER_COLOR = "#E2E3E3";
const FROZEN_PANE_HEADER_BORDER_COLOR = "#BCBCBC";
const FROZEN_PANE_BORDER_COLOR = "#DADFE8";
const COMPOSER_ASSISTANT_COLOR = "#9B359B";
// Color picker defaults as upper case HEX to match `toHex`helper
const COLOR_PICKER_DEFAULTS = [
"#000000",
@@ -1242,7 +1243,17 @@
function isDateTime(str, locale) {
return parseDateTime(str, locale) !== null;
}
const CACHE = new Map();
function parseDateTime(str, locale) {
if (!CACHE.has(locale)) {
CACHE.set(locale, new Map());
}
if (!CACHE.get(locale).has(str)) {
CACHE.get(locale).set(str, _parseDateTime(str, locale));
}
return CACHE.get(locale).get(str);
}
function _parseDateTime(str, locale) {
str = str.trim();
let time = null;
const timeMatch = str.match(timeRegexp);
@@ -2100,7 +2111,7 @@
const SORT_TYPES_ORDER = ["number", "string", "boolean", "undefined"];
function assert(condition, message) {
if (!condition()) {
throw new Error(message);
throw new EvaluationError(CellErrorType.GenericError, message);
}
}
// -----------------------------------------------------------------------------
@@ -4062,8 +4073,9 @@
}
get zone() {
const { left, top, bottom, right } = this._zone;
if (right !== undefined && bottom !== undefined)
return { left, top, right, bottom };
if (right !== undefined && bottom !== undefined) {
return this._zone;
}
else if (bottom === undefined && right !== undefined) {
return { right, top, left, bottom: this.getSheetSize(this.sheetId).numberOfRows - 1 };
}
@@ -5169,7 +5181,7 @@
return textCell((value || "").toString(), localeFormat);
}
catch (error) {
return errorCell((value || "").toString(), new EvaluationError(CellErrorType.GenericError, error.message || DEFAULT_ERROR_MESSAGE));
return errorCell(new EvaluationError(CellErrorType.GenericError, error.message || DEFAULT_ERROR_MESSAGE));
}
}
function textCell(value, localeFormat) {
@@ -5236,7 +5248,7 @@
formattedValue,
};
}
function errorCell(content, error) {
function errorCell(error) {
return {
type: CellValueType.error,
value: error.errorType,
@@ -26307,16 +26319,31 @@
row: Number,
};
function getHtmlContentFromPattern(pattern, value, highlightColor, className) {
const pendingHtmlContent = [];
pattern = pattern.toLowerCase();
for (const patternChar of pattern) {
const index = value.toLocaleLowerCase().indexOf(patternChar);
if (index === -1) {
continue;
}
pendingHtmlContent.push({ value: value.slice(0, index), color: "" }, { value: value[index], color: highlightColor, class: className });
value = value.slice(index + 1);
}
pendingHtmlContent.push({ value });
const htmlContent = pendingHtmlContent.filter((content) => content.value);
return htmlContent;
}
css /* scss */ `
.o-autocomplete-dropdown {
pointer-events: auto;
cursor: pointer;
background-color: #fff;
max-width: 400px;
& > div:hover {
background-color: #f2f2f2;
}
.o-autocomplete-value-focus {
background-color: rgba(0, 0, 0, 0.08);
background-color: #f2f2f2;
}
& > div {
@@ -26334,7 +26361,9 @@
TextValueProvider.props = {
values: Array,
selectedIndex: { type: Number, optional: true },
getHtmlContent: Function,
onValueSelected: Function,
onValueHovered: Function,
};
class ContentEditableHelper {
@@ -26683,8 +26712,8 @@
.o-formula-assistant-focus {
div:first-child,
span {
color: purple;
text-shadow: 0px 0px 1px purple;
color: ${COMPOSER_ASSISTANT_COLOR};
text-shadow: 0px 0px 1px ${COMPOSER_ASSISTANT_COLOR};
}
div:last-child {
color: black;
@@ -26729,6 +26758,7 @@
const functions = functionRegistry.content;
const ASSISTANT_WIDTH = 300;
const AUTOCOMPLETE_ENTRIES = 10;
const selectionIndicatorClass = "selector-flag";
const selectionIndicatorColor = "#a9a9a9";
const selectionIndicator = "␣";
@@ -26778,6 +26808,13 @@
position: absolute;
margin: 1px 4px;
pointer-events: none;
.o-semi-bold {
/** FIXME: to remove in favor of Bootstrap
* 'fw-semibold' when we upgrade to Bootstrap 5.2
*/
font-weight: 600 !important;
}
}
}
`;
@@ -26798,6 +26835,7 @@
values: [],
selectedIndex: undefined,
type: "function",
getHtmlContent: () => [],
});
functionDescriptionState = owl.useState({
showDescription: false,
@@ -27040,24 +27078,27 @@
text,
description,
};
})
.sort((a, b) => {
return a.text.length - b.text.length || a.text.localeCompare(b.text);
});
if (searchTerm) {
values = fuzzyLookup(searchTerm, values, (t) => t.text);
values = fuzzyLookup(searchTerm, values, (t) => t.text).slice(0, AUTOCOMPLETE_ENTRIES);
}
else {
// alphabetical order
values = values.sort((a, b) => a.text.localeCompare(b.text));
}
this.autoCompleteState.values = values.slice(0, 10);
this.autoCompleteState.values = values.slice(0, AUTOCOMPLETE_ENTRIES);
this.autoCompleteState.getHtmlContent = (value) => getHtmlContentFromPattern(searchTerm, value, COMPOSER_ASSISTANT_COLOR, "o-semi-bold");
this.autoCompleteState.selectedIndex = 0;
}
updateAutoCompleteIndex(index) {
this.autoCompleteState.selectedIndex = clip(0, index, 10);
}
/**
* This is required to ensure the content helper selection is
* properly updated on "onclick" events. Depending on the browser,
* the callback onClick from the composer will be executed before
* the selection was updated in the dom, which means we capture an
* wrong selection which is then forced upon the content helper on
* patchContent.
* processContent.
*/
onMousedown(ev) {
if (ev.button > 0) {
@@ -27327,6 +27368,7 @@
this.autoCompleteState.type = "dataValidation";
this.autoCompleteState.selectedIndex = undefined;
this.autoCompleteState.values = values.map((value) => ({ text: value, description: "" }));
this.autoCompleteState.getHtmlContent = (value) => [{ value }];
}
}
Composer.props = {
@@ -34971,10 +35013,11 @@
* Reconstructs the original formula string based on a normalized form and its dependencies
*/
getFormulaCellContent(sheetId, cell, dependencies) {
const ranges = dependencies || [...cell.dependencies];
const ranges = dependencies || cell.dependencies;
let rangeIndex = 0;
return concat(cell.compiledFormula.tokens.map((token) => {
if (token.type === "REFERENCE") {
const range = ranges.shift();
const range = ranges[rangeIndex++];
return this.getters.getRangeString(range, sheetId);
}
return token.value;
@@ -39188,7 +39231,7 @@
const evaluatedCell = this.getEvaluatedCell(position);
if (evaluatedCell.type === CellValueType.empty) {
const cell = this.getters.getCell(position);
if (!cell || cell.content === "") {
if (!cell || (!cell.isFormula && cell.content === "")) {
return undefined;
}
}
@@ -39401,7 +39444,7 @@
constructor(context, getters) {
this.context = context;
this.getters = getters;
this.compilationParams = buildCompilationParameters(context, getters, (position) => this.computeCell(this.encodePosition(position)));
this.compilationParams = buildCompilationParameters(this.context, this.getters, this.computeAndSave.bind(this));
}
getEvaluatedCell(position) {
return (this.evaluatedCells.get(this.encodePosition(position)) ||
@@ -39429,7 +39472,7 @@
this.formulaDependencies().addDependencies(positionId, dependencies);
}
updateCompilationParameters() {
this.compilationParams = buildCompilationParameters(this.context, this.getters, (position) => this.computeCell(this.encodePosition(position)));
this.compilationParams = buildCompilationParameters(this.context, this.getters, this.computeAndSave.bind(this));
}
evaluateCells(positions) {
const cells = positions.map(this.encodePosition.bind(this));
@@ -39545,15 +39588,21 @@
this.cellsBeingComputed.delete(cellId);
}
}
handleError(e, cell) {
if (!(e instanceof Error)) {
e = new Error(e);
computeAndSave(position) {
const positionId = this.encodePosition(position);
const evaluatedCell = this.computeCell(positionId);
if (!this.evaluatedCells.has(positionId)) {
this.setEvaluatedCell(positionId, evaluatedCell);
}
return evaluatedCell;
}
handleError(e, cell) {
if (!(e instanceof EvaluationError)) {
e = new EvaluationError(CellErrorType.GenericError, e.message);
}
const msg = e?.errorType || CellErrorType.GenericError;
// apply function name
const __lastFnCalled = this.compilationParams[2].__lastFnCalled || "";
const error = new EvaluationError(msg, e.message.replace("[[FUNCTION_NAME]]", __lastFnCalled), e.logLevel !== undefined ? e.logLevel : CellErrorLevel.error);
return errorCell(cell.content, error);
e.message = e.message.replace("[[FUNCTION_NAME]]", __lastFnCalled);
return errorCell(e);
}
computeFormulaCell(cellData) {
const cellId = cellData.id;
@@ -55110,9 +55159,9 @@
exports.tokenize = tokenize;
__info__.version = "16.5.0-alpha.9";
__info__.date = "2023-10-03T13:36:38.605Z";
__info__.hash = "c46de914";
__info__.version = "16.5.0-alpha.10";
__info__.date = "2023-10-10T08:16:10.236Z";
__info__.hash = "4dc340fc";
})(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 16.5.0-alpha.9
@date 2023-10-03T13:36:42.361Z
@hash c46de914
@version 16.5.0-alpha.10
@date 2023-10-10T08:16:13.959Z
@hash 4dc340fc
-->
<odoo>
<t t-name="o-spreadsheet-ActionButton">
@@ -430,8 +430,19 @@
<div
class="d-flex flex-column text-start"
t-att-class="{'o-autocomplete-value-focus': props.selectedIndex === v_index}"
t-on-click="() => this.props.onValueSelected(v.text)">
<div class="o-autocomplete-value text-truncate" t-esc="v.text"/>
t-on-click="() => this.props.onValueSelected(v.text)"
t-on-mousemove="() => this.props.onValueHovered(v_index)">
<div class="o-autocomplete-value text-truncate">
<t t-set="htmlContent" t-value="props.getHtmlContent(v.text)"/>
<span
t-foreach="htmlContent"
t-as="content"
t-key="content_index"
t-att-class="content.class"
t-attf-style="color: {{content.color || 'inherit'}};"
t-esc="content.value"
/>
</div>
<div
class="o-autocomplete-description text-truncate"
t-esc="v.description"
@@ -476,6 +487,8 @@
values="autoCompleteState.values"
selectedIndex="autoCompleteState.selectedIndex"
onValueSelected.bind="this.autoComplete"
onValueHovered.bind="this.updateAutoCompleteIndex"
getHtmlContent="autoCompleteState.getHtmlContent"
/>
<FunctionDescriptionProvider
t-if="functionDescriptionState.showDescription"