@odoo/o-spreadsheet 19.2.24 → 19.2.26
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/o_spreadsheet.css +5 -3
- package/dist/o_spreadsheet.esm.js +61 -39
- package/dist/o_spreadsheet.iife.js +61 -39
- package/dist/o_spreadsheet.iife.min.js +244 -244
- package/dist/o_spreadsheet.xml +3 -3
- package/package.json +1 -1
package/dist/o_spreadsheet.css
CHANGED
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
/*
|
|
3
3
|
* This file is generated by o-spreadsheet build tools. Do not edit it.
|
|
4
4
|
* @see https://github.com/odoo/o-spreadsheet
|
|
5
|
-
* @version 19.2.
|
|
6
|
-
* @date 2026-08-
|
|
7
|
-
* @hash
|
|
5
|
+
* @version 19.2.26
|
|
6
|
+
* @date 2026-08-21T15:30:16.234Z
|
|
7
|
+
* @hash b9d68c1
|
|
8
8
|
*/
|
|
9
9
|
:root {
|
|
10
10
|
--os-gray-100: light-dark(#f9fafb, #1b1d26);
|
|
@@ -291,6 +291,7 @@
|
|
|
291
291
|
.os-input {
|
|
292
292
|
border-width: 0 0 1px 0;
|
|
293
293
|
border-color: transparent;
|
|
294
|
+
border-style: solid;
|
|
294
295
|
outline: none;
|
|
295
296
|
text-overflow: ellipsis;
|
|
296
297
|
color: var(--os-text-body);
|
|
@@ -571,6 +572,7 @@
|
|
|
571
572
|
width: 100%;
|
|
572
573
|
outline: none;
|
|
573
574
|
border-color: var(--os-border-color);
|
|
575
|
+
border-style: solid;
|
|
574
576
|
color: var(--os-gray-900);
|
|
575
577
|
|
|
576
578
|
&::placeholder {
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* This file is generated by o-spreadsheet build tools. Do not edit it.
|
|
4
4
|
* @see https://github.com/odoo/o-spreadsheet
|
|
5
|
-
* @version 19.2.
|
|
6
|
-
* @date 2026-08-
|
|
7
|
-
* @hash
|
|
5
|
+
* @version 19.2.26
|
|
6
|
+
* @date 2026-08-21T15:30:14.701Z
|
|
7
|
+
* @hash b9d68c1
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
import { App, Component, blockDom, markRaw, onMounted, onPatched, onWillPatch, onWillStart, onWillUnmount, onWillUpdateProps, status, toRaw, useChildSubEnv, useComponent, useEffect, useEnv, useExternalListener, useRef, useState, useSubEnv, whenReady, xml } from "@odoo/owl";
|
|
@@ -3751,6 +3751,18 @@ function toString(data) {
|
|
|
3751
3751
|
default: return "";
|
|
3752
3752
|
}
|
|
3753
3753
|
}
|
|
3754
|
+
/**
|
|
3755
|
+
* Only converts the decimal separator, ignore number format such as % or dates
|
|
3756
|
+
*/
|
|
3757
|
+
function toLocaleString(data, locale) {
|
|
3758
|
+
const value = toValue(data);
|
|
3759
|
+
switch (typeof value) {
|
|
3760
|
+
case "string": return value;
|
|
3761
|
+
case "number": return value.toString().replace(".", locale.decimalSeparator);
|
|
3762
|
+
case "boolean": return value ? "TRUE" : "FALSE";
|
|
3763
|
+
default: return "";
|
|
3764
|
+
}
|
|
3765
|
+
}
|
|
3754
3766
|
/** Normalize string by setting it to lowercase and replacing accent letters with plain letters */
|
|
3755
3767
|
const normalizeString = memoize(function normalizeString(str) {
|
|
3756
3768
|
return str.toLowerCase().normalize("NFD").replace(/[\u0300-\u036f]/g, "");
|
|
@@ -18291,7 +18303,7 @@ const ARRAYTOTEXT = {
|
|
|
18291
18303
|
else if (_format === 0) {
|
|
18292
18304
|
const rowSeparator = this.locale.decimalSeparator === "," ? "/" : ",";
|
|
18293
18305
|
return transposeMatrix(_array).flatMap((row) => row.map((value) => {
|
|
18294
|
-
return isEvaluationError(value.value) ? value.value :
|
|
18306
|
+
return isEvaluationError(value.value) ? value.value : toLocaleString(value, this.locale);
|
|
18295
18307
|
})).join(rowSeparator);
|
|
18296
18308
|
} else return new EvaluationError(_t("Format must be 0 or 1"));
|
|
18297
18309
|
},
|
|
@@ -24334,7 +24346,7 @@ const CONCAT = {
|
|
|
24334
24346
|
description: _t("Concatenation of two values."),
|
|
24335
24347
|
args: [arg("value1 (string)", _t("The value to which value2 will be appended.")), arg("value2 (string)", _t("The value to append to value1."))],
|
|
24336
24348
|
compute: function(value1, value2) {
|
|
24337
|
-
return
|
|
24349
|
+
return toLocaleString(value1, this.locale) + toLocaleString(value2, this.locale);
|
|
24338
24350
|
},
|
|
24339
24351
|
isExported: true
|
|
24340
24352
|
};
|
|
@@ -25203,7 +25215,7 @@ const CLEAN = {
|
|
|
25203
25215
|
description: _t("Remove non-printable characters from a piece of text."),
|
|
25204
25216
|
args: [arg("text (string)", _t("The text whose non-printable characters are to be removed."))],
|
|
25205
25217
|
compute: function(text) {
|
|
25206
|
-
const _text =
|
|
25218
|
+
const _text = toLocaleString(text, this.locale);
|
|
25207
25219
|
let cleanedStr = "";
|
|
25208
25220
|
for (const char of _text) if (char && char.charCodeAt(0) > 31) cleanedStr += char;
|
|
25209
25221
|
return cleanedStr;
|
|
@@ -25214,7 +25226,7 @@ const CONCATENATE = {
|
|
|
25214
25226
|
description: _t("Appends strings to one another."),
|
|
25215
25227
|
args: [arg("string (string, range<string>, repeating)", _t("String to append in sequence."))],
|
|
25216
25228
|
compute: function(...datas) {
|
|
25217
|
-
return reduceAny(datas, (acc, a) => acc +
|
|
25229
|
+
return reduceAny(datas, (acc, a) => acc + toLocaleString(a, this.locale), "");
|
|
25218
25230
|
},
|
|
25219
25231
|
isExported: true
|
|
25220
25232
|
};
|
|
@@ -25222,7 +25234,7 @@ const EXACT = {
|
|
|
25222
25234
|
description: _t("Tests whether two strings are identical."),
|
|
25223
25235
|
args: [arg("string1 (string)", _t("The first string to compare.")), arg("string2 (string)", _t("The second string to compare."))],
|
|
25224
25236
|
compute: function(string1, string2) {
|
|
25225
|
-
return
|
|
25237
|
+
return toLocaleString(string1, this.locale) === toLocaleString(string2, this.locale);
|
|
25226
25238
|
},
|
|
25227
25239
|
isExported: true
|
|
25228
25240
|
};
|
|
@@ -25234,8 +25246,8 @@ const FIND = {
|
|
|
25234
25246
|
arg(`starting_at (number, default=${DEFAULT_STARTING_AT})`, _t("The character within text_to_search at which to start the search."))
|
|
25235
25247
|
],
|
|
25236
25248
|
compute: function(searchFor, textToSearch, startingAt = { value: DEFAULT_STARTING_AT }) {
|
|
25237
|
-
const _searchFor =
|
|
25238
|
-
const _textToSearch =
|
|
25249
|
+
const _searchFor = toLocaleString(searchFor, this.locale);
|
|
25250
|
+
const _textToSearch = toLocaleString(textToSearch, this.locale);
|
|
25239
25251
|
const _startingAt = toNumber(startingAt, this.locale);
|
|
25240
25252
|
if (_textToSearch === "") return new EvaluationError(_t("The text_to_search must be non-empty."));
|
|
25241
25253
|
if (_startingAt < 1) return new EvaluationError(_t("The starting_at (%s) must be greater than or equal to 1.", _startingAt));
|
|
@@ -25272,8 +25284,8 @@ const JOIN = {
|
|
|
25272
25284
|
description: _t("Concatenates elements of arrays with delimiter."),
|
|
25273
25285
|
args: [arg("delimiter (string)", _t("The character or string to place between each concatenated value.")), arg("value_or_array (string, range<string>, repeating)", _t("Value to be appended using delimiter."))],
|
|
25274
25286
|
compute: function(delimiter, ...valuesOrArrays) {
|
|
25275
|
-
const _delimiter =
|
|
25276
|
-
return reduceAny(valuesOrArrays, (acc, a) => (acc ? acc + _delimiter : "") +
|
|
25287
|
+
const _delimiter = toLocaleString(delimiter, this.locale);
|
|
25288
|
+
return reduceAny(valuesOrArrays, (acc, a) => (acc ? acc + _delimiter : "") + toLocaleString(a, this.locale), "");
|
|
25277
25289
|
}
|
|
25278
25290
|
};
|
|
25279
25291
|
const LEFT = {
|
|
@@ -25282,7 +25294,7 @@ const LEFT = {
|
|
|
25282
25294
|
compute: function(text, ...args) {
|
|
25283
25295
|
const _numberOfCharacters = args.length ? toNumber(args[0], this.locale) : 1;
|
|
25284
25296
|
if (_numberOfCharacters < 0) return new EvaluationError(_t("The number_of_characters (%s) must be positive or null.", _numberOfCharacters));
|
|
25285
|
-
return
|
|
25297
|
+
return toLocaleString(text, this.locale).substring(0, _numberOfCharacters);
|
|
25286
25298
|
},
|
|
25287
25299
|
isExported: true
|
|
25288
25300
|
};
|
|
@@ -25290,7 +25302,7 @@ const LEN = {
|
|
|
25290
25302
|
description: _t("Length of a string."),
|
|
25291
25303
|
args: [arg("text (string)", _t("The string whose length will be returned."))],
|
|
25292
25304
|
compute: function(text) {
|
|
25293
|
-
return
|
|
25305
|
+
return toLocaleString(text, this.locale).length;
|
|
25294
25306
|
},
|
|
25295
25307
|
isExported: true
|
|
25296
25308
|
};
|
|
@@ -25298,7 +25310,7 @@ const LOWER = {
|
|
|
25298
25310
|
description: _t("Converts a specified string to lowercase."),
|
|
25299
25311
|
args: [arg("text (string)", _t("The string to convert to lowercase."))],
|
|
25300
25312
|
compute: function(text) {
|
|
25301
|
-
return
|
|
25313
|
+
return toLocaleString(text, this.locale).toLowerCase();
|
|
25302
25314
|
},
|
|
25303
25315
|
isExported: true
|
|
25304
25316
|
};
|
|
@@ -25310,7 +25322,7 @@ const MID = {
|
|
|
25310
25322
|
arg("extract_length (number)", _t("The length of the segment to extract."))
|
|
25311
25323
|
],
|
|
25312
25324
|
compute: function(text, starting_at, extract_length) {
|
|
25313
|
-
const _text =
|
|
25325
|
+
const _text = toLocaleString(text, this.locale);
|
|
25314
25326
|
const _starting_at = toNumber(starting_at, this.locale);
|
|
25315
25327
|
const _extract_length = toNumber(extract_length, this.locale);
|
|
25316
25328
|
if (_starting_at < 1) return new EvaluationError(_t("The starting_at argument (%s) must be positive greater than one.", _starting_at.toString()));
|
|
@@ -25323,7 +25335,7 @@ const PROPER = {
|
|
|
25323
25335
|
description: _t("Capitalizes each word in a specified string."),
|
|
25324
25336
|
args: [arg("text_to_capitalize (string)", _t("The text which will be returned with the first letter of each word in uppercase and all other letters in lowercase."))],
|
|
25325
25337
|
compute: function(text) {
|
|
25326
|
-
return
|
|
25338
|
+
return toLocaleString(text, this.locale).replace(wordRegex, (word) => {
|
|
25327
25339
|
return word.charAt(0).toUpperCase() + word.slice(1).toLowerCase();
|
|
25328
25340
|
});
|
|
25329
25341
|
},
|
|
@@ -25471,9 +25483,9 @@ const REPLACE = {
|
|
|
25471
25483
|
compute: function(text, position, length, newText) {
|
|
25472
25484
|
const _position = toNumber(position, this.locale);
|
|
25473
25485
|
if (_position < 1) return new EvaluationError(_t("The position (%s) must be greater than or equal to 1.", _position));
|
|
25474
|
-
const _text =
|
|
25486
|
+
const _text = toLocaleString(text, this.locale);
|
|
25475
25487
|
const _length = toNumber(length, this.locale);
|
|
25476
|
-
const _newText =
|
|
25488
|
+
const _newText = toLocaleString(newText, this.locale);
|
|
25477
25489
|
return _text.substring(0, _position - 1) + _newText + _text.substring(_position - 1 + _length);
|
|
25478
25490
|
},
|
|
25479
25491
|
isExported: true
|
|
@@ -25484,7 +25496,7 @@ const RIGHT = {
|
|
|
25484
25496
|
compute: function(text, ...args) {
|
|
25485
25497
|
const _numberOfCharacters = args.length ? toNumber(args[0], this.locale) : 1;
|
|
25486
25498
|
if (_numberOfCharacters < 0) return new EvaluationError(_t("The number_of_characters (%s) must be positive or null.", _numberOfCharacters));
|
|
25487
|
-
const _text =
|
|
25499
|
+
const _text = toLocaleString(text, this.locale);
|
|
25488
25500
|
const stringLength = _text.length;
|
|
25489
25501
|
return _text.substring(stringLength - _numberOfCharacters, stringLength);
|
|
25490
25502
|
},
|
|
@@ -25498,8 +25510,8 @@ const SEARCH = {
|
|
|
25498
25510
|
arg(`starting_at (number, default=${DEFAULT_STARTING_AT})`, _t("The character within text_to_search at which to start the search."))
|
|
25499
25511
|
],
|
|
25500
25512
|
compute: function(searchFor, textToSearch, startingAt = { value: DEFAULT_STARTING_AT }) {
|
|
25501
|
-
const _searchFor =
|
|
25502
|
-
const _textToSearch =
|
|
25513
|
+
const _searchFor = toLocaleString(searchFor, this.locale).toLowerCase();
|
|
25514
|
+
const _textToSearch = toLocaleString(textToSearch, this.locale).toLowerCase();
|
|
25503
25515
|
const _startingAt = toNumber(startingAt, this.locale);
|
|
25504
25516
|
if (_textToSearch === "") return {
|
|
25505
25517
|
value: CellErrorType.GenericError,
|
|
@@ -25529,8 +25541,8 @@ const SPLIT = {
|
|
|
25529
25541
|
arg(`remove_empty_text (boolean, default=${SPLIT_DEFAULT_REMOVE_EMPTY_TEXT})`, _t("Whether or not to remove empty text messages from the split results. The default behavior is to treat consecutive delimiters as one (if TRUE). If FALSE, empty cells values are added between consecutive delimiters."))
|
|
25530
25542
|
],
|
|
25531
25543
|
compute: function(text, delimiter, splitByEach = { value: SPLIT_DEFAULT_SPLIT_BY_EACH }, removeEmptyText = { value: SPLIT_DEFAULT_REMOVE_EMPTY_TEXT }) {
|
|
25532
|
-
const _text =
|
|
25533
|
-
const _delimiter = escapeRegExp(
|
|
25544
|
+
const _text = toLocaleString(text, this.locale);
|
|
25545
|
+
const _delimiter = escapeRegExp(toLocaleString(delimiter, this.locale));
|
|
25534
25546
|
const _splitByEach = toBoolean(splitByEach);
|
|
25535
25547
|
const _removeEmptyText = toBoolean(removeEmptyText);
|
|
25536
25548
|
if (_delimiter.length <= 0) return new EvaluationError(_t("The delimiter (%s) must be not be empty.", _delimiter));
|
|
@@ -25552,10 +25564,10 @@ const SUBSTITUTE = {
|
|
|
25552
25564
|
compute: function(textToSearch, searchFor, replaceWith, occurrenceNumber) {
|
|
25553
25565
|
const _occurrenceNumber = toNumber(occurrenceNumber, this.locale);
|
|
25554
25566
|
if (_occurrenceNumber < 0) return new EvaluationError(_t("The occurrenceNumber (%s) must be positive or null.", _occurrenceNumber));
|
|
25555
|
-
const _textToSearch =
|
|
25556
|
-
const _searchFor =
|
|
25567
|
+
const _textToSearch = toLocaleString(textToSearch, this.locale);
|
|
25568
|
+
const _searchFor = toLocaleString(searchFor, this.locale);
|
|
25557
25569
|
if (_searchFor === "") return _textToSearch;
|
|
25558
|
-
const _replaceWith =
|
|
25570
|
+
const _replaceWith = toLocaleString(replaceWith, this.locale);
|
|
25559
25571
|
const reg = new RegExp(escapeRegExp(_searchFor), "g");
|
|
25560
25572
|
if (_occurrenceNumber === 0) return _textToSearch.replace(reg, _replaceWith);
|
|
25561
25573
|
let n = 0;
|
|
@@ -25578,10 +25590,10 @@ const TEXTJOIN = {
|
|
|
25578
25590
|
arg("texts (string, range<string>, repeating)", _t("Text item to join."))
|
|
25579
25591
|
],
|
|
25580
25592
|
compute: function(delimiter, ignoreEmpty = { value: TEXTJOIN_DEFAULT_IGNORE_EMPTY }, ...textsOrArrays) {
|
|
25581
|
-
const _delimiter =
|
|
25593
|
+
const _delimiter = toLocaleString(delimiter, this.locale);
|
|
25582
25594
|
const _ignoreEmpty = toBoolean(ignoreEmpty);
|
|
25583
25595
|
let n = 0;
|
|
25584
|
-
return reduceAny(textsOrArrays, (acc, a) => !(_ignoreEmpty &&
|
|
25596
|
+
return reduceAny(textsOrArrays, (acc, a) => !(_ignoreEmpty && toLocaleString(a, this.locale) === "") ? (n++ ? acc + _delimiter : "") + toLocaleString(a, this.locale) : acc, "");
|
|
25585
25597
|
},
|
|
25586
25598
|
isExported: true
|
|
25587
25599
|
};
|
|
@@ -25634,7 +25646,7 @@ const TRIM = {
|
|
|
25634
25646
|
description: _t("Removes space characters."),
|
|
25635
25647
|
args: [arg("text (string)", _t("The text or reference to a cell containing text to be trimmed."))],
|
|
25636
25648
|
compute: function(text) {
|
|
25637
|
-
return trimContent(
|
|
25649
|
+
return trimContent(toLocaleString(text, this.locale));
|
|
25638
25650
|
},
|
|
25639
25651
|
isExported: true
|
|
25640
25652
|
};
|
|
@@ -25642,7 +25654,7 @@ const UPPER = {
|
|
|
25642
25654
|
description: _t("Converts a specified string to uppercase."),
|
|
25643
25655
|
args: [arg("text (string)", _t("The string to convert to uppercase."))],
|
|
25644
25656
|
compute: function(text) {
|
|
25645
|
-
return
|
|
25657
|
+
return toLocaleString(text, this.locale).toUpperCase();
|
|
25646
25658
|
},
|
|
25647
25659
|
isExported: true
|
|
25648
25660
|
};
|
|
@@ -25739,7 +25751,7 @@ const HYPERLINK = {
|
|
|
25739
25751
|
args: [arg("url (string)", _t("The full URL of the link enclosed in quotation marks.")), arg("link_label (string, optional)", _t("The text to display in the cell, enclosed in quotation marks."))],
|
|
25740
25752
|
compute: function(url, linkLabel) {
|
|
25741
25753
|
const processedUrl = toString(url).trim();
|
|
25742
|
-
const processedLabel =
|
|
25754
|
+
const processedLabel = toLocaleString(linkLabel, this.locale) || processedUrl;
|
|
25743
25755
|
if (processedUrl === "") return processedLabel;
|
|
25744
25756
|
return markdownLink(processedLabel, processedUrl);
|
|
25745
25757
|
},
|
|
@@ -25841,6 +25853,9 @@ for (const category of categories) {
|
|
|
25841
25853
|
});
|
|
25842
25854
|
}
|
|
25843
25855
|
}
|
|
25856
|
+
/** Formulas using one of these functions are never squished: they are always exported
|
|
25857
|
+
* as a full formula string, and they don't become the base formula of the next cells. */
|
|
25858
|
+
const NonSquishableFunctionRegistry = new Registry();
|
|
25844
25859
|
|
|
25845
25860
|
//#endregion
|
|
25846
25861
|
//#region src/types/xlsx.ts
|
|
@@ -36226,12 +36241,13 @@ var FilterMenu = class extends Component {
|
|
|
36226
36241
|
}
|
|
36227
36242
|
sortFilterZone(sortDirection) {
|
|
36228
36243
|
const filterPosition = this.props.filterPosition;
|
|
36229
|
-
const
|
|
36244
|
+
const table = this.table;
|
|
36245
|
+
const tableZone = table?.range.zone;
|
|
36230
36246
|
if (!filterPosition || !tableZone || tableZone.top === tableZone.bottom) return;
|
|
36231
36247
|
const sheetId = this.env.model.getters.getActiveSheetId();
|
|
36232
36248
|
const contentZone = {
|
|
36233
36249
|
...tableZone,
|
|
36234
|
-
top: tableZone.top +
|
|
36250
|
+
top: tableZone.top + table.config.numberOfHeaders
|
|
36235
36251
|
};
|
|
36236
36252
|
const sortAnchor = {
|
|
36237
36253
|
col: filterPosition.col,
|
|
@@ -55899,7 +55915,8 @@ var Squisher = class {
|
|
|
55899
55915
|
* The result of this method is:
|
|
55900
55916
|
* - if the cell is not a formula, returns the content as is and resets the base formula
|
|
55901
55917
|
* - if the cell is a formula:
|
|
55902
|
-
* - if there is no previous formula, or the normalized formula is different from the previous one
|
|
55918
|
+
* - if there is no previous formula, or the normalized formula is different from the previous one or it contains blacklisted functions (see NonSquishableFunctionRegistry),
|
|
55919
|
+
* resets the base formula to this one and returns the full formula string
|
|
55903
55920
|
* - else, compares the literal values and range dependencies to the previous formula, and for each parameter:
|
|
55904
55921
|
* - for numbers: returns a relative change (+N or -N) if possible, else the full number or "=" if unchanged
|
|
55905
55922
|
* - for strings: returns the full string if changed, else "="
|
|
@@ -55910,6 +55927,10 @@ var Squisher = class {
|
|
|
55910
55927
|
this.resetBase();
|
|
55911
55928
|
return cell.content;
|
|
55912
55929
|
}
|
|
55930
|
+
if (NonSquishableFunctionRegistry.getAll().some((functionName) => cell.compiledFormula.usesSymbol(functionName))) {
|
|
55931
|
+
this.resetBase();
|
|
55932
|
+
return cell.compiledFormula.toFormulaString(this.getters, this.rangeToStringOptions);
|
|
55933
|
+
}
|
|
55913
55934
|
let numbers = [];
|
|
55914
55935
|
let strings = [];
|
|
55915
55936
|
let references = [];
|
|
@@ -81610,7 +81631,8 @@ const registries = {
|
|
|
81610
81631
|
supportedPivotPositionalFormulaRegistry,
|
|
81611
81632
|
pivotToFunctionValueRegistry,
|
|
81612
81633
|
migrationStepRegistry,
|
|
81613
|
-
chartJsExtensionRegistry
|
|
81634
|
+
chartJsExtensionRegistry,
|
|
81635
|
+
NonSquishableFunctionRegistry
|
|
81614
81636
|
};
|
|
81615
81637
|
const helpers = {
|
|
81616
81638
|
arg,
|
|
@@ -81802,6 +81824,6 @@ const chartHelpers = {
|
|
|
81802
81824
|
//#endregion
|
|
81803
81825
|
export { AbstractCellClipboardHandler, AbstractChart, AbstractFigureClipboardHandler, CellErrorType, ClientDisconnectedError, CommandResult, CompiledFormula, CorePlugin, CoreViewPlugin, DEFAULT_LOCALE, DEFAULT_LOCALES, DispatchResult, EvaluationError, LocalTransportService, Model, PivotRuntimeDefinition, Registry, Revision, SPREADSHEET_DIMENSIONS, Spreadsheet, SpreadsheetPivotTable, UIPlugin, __info__, addFunction, addRenderingLayer, astToFormula, canExecuteInReadonly, categories, chartHelpers, components, constants, convertAstNodes, coreTypes, createAutocompleteArgumentsProvider, findCellInNewZone, functionCache, getCaretDownSvg, getCaretUpSvg, helpers, hooks, invalidateCFEvaluationCommands, invalidateChartEvaluationCommands, invalidateDependenciesCommands, invalidateEvaluationCommands, isCoreCommand, isSheetDependent, iterateAstNodes, links, load, lockedSheetAllowedCommands, parse, parseTokens, readonlyAllowedCommands, registries, setDefaultSheetViewSize, setTranslationMethod, stores, tokenColors, tokenize };
|
|
81804
81826
|
|
|
81805
|
-
__info__.version = "19.2.
|
|
81806
|
-
__info__.date = "2026-08-
|
|
81807
|
-
__info__.hash = "
|
|
81827
|
+
__info__.version = "19.2.26";
|
|
81828
|
+
__info__.date = "2026-08-21T15:30:14.701Z";
|
|
81829
|
+
__info__.hash = "b9d68c1";
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* This file is generated by o-spreadsheet build tools. Do not edit it.
|
|
4
4
|
* @see https://github.com/odoo/o-spreadsheet
|
|
5
|
-
* @version 19.2.
|
|
6
|
-
* @date 2026-08-
|
|
7
|
-
* @hash
|
|
5
|
+
* @version 19.2.26
|
|
6
|
+
* @date 2026-08-21T15:30:14.701Z
|
|
7
|
+
* @hash b9d68c1
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
(function(exports, _odoo_owl) {
|
|
@@ -3753,6 +3753,18 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
3753
3753
|
default: return "";
|
|
3754
3754
|
}
|
|
3755
3755
|
}
|
|
3756
|
+
/**
|
|
3757
|
+
* Only converts the decimal separator, ignore number format such as % or dates
|
|
3758
|
+
*/
|
|
3759
|
+
function toLocaleString(data, locale) {
|
|
3760
|
+
const value = toValue(data);
|
|
3761
|
+
switch (typeof value) {
|
|
3762
|
+
case "string": return value;
|
|
3763
|
+
case "number": return value.toString().replace(".", locale.decimalSeparator);
|
|
3764
|
+
case "boolean": return value ? "TRUE" : "FALSE";
|
|
3765
|
+
default: return "";
|
|
3766
|
+
}
|
|
3767
|
+
}
|
|
3756
3768
|
/** Normalize string by setting it to lowercase and replacing accent letters with plain letters */
|
|
3757
3769
|
const normalizeString = memoize(function normalizeString(str) {
|
|
3758
3770
|
return str.toLowerCase().normalize("NFD").replace(/[\u0300-\u036f]/g, "");
|
|
@@ -18293,7 +18305,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
18293
18305
|
else if (_format === 0) {
|
|
18294
18306
|
const rowSeparator = this.locale.decimalSeparator === "," ? "/" : ",";
|
|
18295
18307
|
return transposeMatrix(_array).flatMap((row) => row.map((value) => {
|
|
18296
|
-
return isEvaluationError(value.value) ? value.value :
|
|
18308
|
+
return isEvaluationError(value.value) ? value.value : toLocaleString(value, this.locale);
|
|
18297
18309
|
})).join(rowSeparator);
|
|
18298
18310
|
} else return new EvaluationError(_t("Format must be 0 or 1"));
|
|
18299
18311
|
},
|
|
@@ -24336,7 +24348,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
24336
24348
|
description: _t("Concatenation of two values."),
|
|
24337
24349
|
args: [arg("value1 (string)", _t("The value to which value2 will be appended.")), arg("value2 (string)", _t("The value to append to value1."))],
|
|
24338
24350
|
compute: function(value1, value2) {
|
|
24339
|
-
return
|
|
24351
|
+
return toLocaleString(value1, this.locale) + toLocaleString(value2, this.locale);
|
|
24340
24352
|
},
|
|
24341
24353
|
isExported: true
|
|
24342
24354
|
};
|
|
@@ -25205,7 +25217,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
25205
25217
|
description: _t("Remove non-printable characters from a piece of text."),
|
|
25206
25218
|
args: [arg("text (string)", _t("The text whose non-printable characters are to be removed."))],
|
|
25207
25219
|
compute: function(text) {
|
|
25208
|
-
const _text =
|
|
25220
|
+
const _text = toLocaleString(text, this.locale);
|
|
25209
25221
|
let cleanedStr = "";
|
|
25210
25222
|
for (const char of _text) if (char && char.charCodeAt(0) > 31) cleanedStr += char;
|
|
25211
25223
|
return cleanedStr;
|
|
@@ -25216,7 +25228,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
25216
25228
|
description: _t("Appends strings to one another."),
|
|
25217
25229
|
args: [arg("string (string, range<string>, repeating)", _t("String to append in sequence."))],
|
|
25218
25230
|
compute: function(...datas) {
|
|
25219
|
-
return reduceAny(datas, (acc, a) => acc +
|
|
25231
|
+
return reduceAny(datas, (acc, a) => acc + toLocaleString(a, this.locale), "");
|
|
25220
25232
|
},
|
|
25221
25233
|
isExported: true
|
|
25222
25234
|
};
|
|
@@ -25224,7 +25236,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
25224
25236
|
description: _t("Tests whether two strings are identical."),
|
|
25225
25237
|
args: [arg("string1 (string)", _t("The first string to compare.")), arg("string2 (string)", _t("The second string to compare."))],
|
|
25226
25238
|
compute: function(string1, string2) {
|
|
25227
|
-
return
|
|
25239
|
+
return toLocaleString(string1, this.locale) === toLocaleString(string2, this.locale);
|
|
25228
25240
|
},
|
|
25229
25241
|
isExported: true
|
|
25230
25242
|
};
|
|
@@ -25236,8 +25248,8 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
25236
25248
|
arg(`starting_at (number, default=${DEFAULT_STARTING_AT})`, _t("The character within text_to_search at which to start the search."))
|
|
25237
25249
|
],
|
|
25238
25250
|
compute: function(searchFor, textToSearch, startingAt = { value: DEFAULT_STARTING_AT }) {
|
|
25239
|
-
const _searchFor =
|
|
25240
|
-
const _textToSearch =
|
|
25251
|
+
const _searchFor = toLocaleString(searchFor, this.locale);
|
|
25252
|
+
const _textToSearch = toLocaleString(textToSearch, this.locale);
|
|
25241
25253
|
const _startingAt = toNumber(startingAt, this.locale);
|
|
25242
25254
|
if (_textToSearch === "") return new EvaluationError(_t("The text_to_search must be non-empty."));
|
|
25243
25255
|
if (_startingAt < 1) return new EvaluationError(_t("The starting_at (%s) must be greater than or equal to 1.", _startingAt));
|
|
@@ -25274,8 +25286,8 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
25274
25286
|
description: _t("Concatenates elements of arrays with delimiter."),
|
|
25275
25287
|
args: [arg("delimiter (string)", _t("The character or string to place between each concatenated value.")), arg("value_or_array (string, range<string>, repeating)", _t("Value to be appended using delimiter."))],
|
|
25276
25288
|
compute: function(delimiter, ...valuesOrArrays) {
|
|
25277
|
-
const _delimiter =
|
|
25278
|
-
return reduceAny(valuesOrArrays, (acc, a) => (acc ? acc + _delimiter : "") +
|
|
25289
|
+
const _delimiter = toLocaleString(delimiter, this.locale);
|
|
25290
|
+
return reduceAny(valuesOrArrays, (acc, a) => (acc ? acc + _delimiter : "") + toLocaleString(a, this.locale), "");
|
|
25279
25291
|
}
|
|
25280
25292
|
};
|
|
25281
25293
|
const LEFT = {
|
|
@@ -25284,7 +25296,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
25284
25296
|
compute: function(text, ...args) {
|
|
25285
25297
|
const _numberOfCharacters = args.length ? toNumber(args[0], this.locale) : 1;
|
|
25286
25298
|
if (_numberOfCharacters < 0) return new EvaluationError(_t("The number_of_characters (%s) must be positive or null.", _numberOfCharacters));
|
|
25287
|
-
return
|
|
25299
|
+
return toLocaleString(text, this.locale).substring(0, _numberOfCharacters);
|
|
25288
25300
|
},
|
|
25289
25301
|
isExported: true
|
|
25290
25302
|
};
|
|
@@ -25292,7 +25304,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
25292
25304
|
description: _t("Length of a string."),
|
|
25293
25305
|
args: [arg("text (string)", _t("The string whose length will be returned."))],
|
|
25294
25306
|
compute: function(text) {
|
|
25295
|
-
return
|
|
25307
|
+
return toLocaleString(text, this.locale).length;
|
|
25296
25308
|
},
|
|
25297
25309
|
isExported: true
|
|
25298
25310
|
};
|
|
@@ -25300,7 +25312,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
25300
25312
|
description: _t("Converts a specified string to lowercase."),
|
|
25301
25313
|
args: [arg("text (string)", _t("The string to convert to lowercase."))],
|
|
25302
25314
|
compute: function(text) {
|
|
25303
|
-
return
|
|
25315
|
+
return toLocaleString(text, this.locale).toLowerCase();
|
|
25304
25316
|
},
|
|
25305
25317
|
isExported: true
|
|
25306
25318
|
};
|
|
@@ -25312,7 +25324,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
25312
25324
|
arg("extract_length (number)", _t("The length of the segment to extract."))
|
|
25313
25325
|
],
|
|
25314
25326
|
compute: function(text, starting_at, extract_length) {
|
|
25315
|
-
const _text =
|
|
25327
|
+
const _text = toLocaleString(text, this.locale);
|
|
25316
25328
|
const _starting_at = toNumber(starting_at, this.locale);
|
|
25317
25329
|
const _extract_length = toNumber(extract_length, this.locale);
|
|
25318
25330
|
if (_starting_at < 1) return new EvaluationError(_t("The starting_at argument (%s) must be positive greater than one.", _starting_at.toString()));
|
|
@@ -25325,7 +25337,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
25325
25337
|
description: _t("Capitalizes each word in a specified string."),
|
|
25326
25338
|
args: [arg("text_to_capitalize (string)", _t("The text which will be returned with the first letter of each word in uppercase and all other letters in lowercase."))],
|
|
25327
25339
|
compute: function(text) {
|
|
25328
|
-
return
|
|
25340
|
+
return toLocaleString(text, this.locale).replace(wordRegex, (word) => {
|
|
25329
25341
|
return word.charAt(0).toUpperCase() + word.slice(1).toLowerCase();
|
|
25330
25342
|
});
|
|
25331
25343
|
},
|
|
@@ -25473,9 +25485,9 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
25473
25485
|
compute: function(text, position, length, newText) {
|
|
25474
25486
|
const _position = toNumber(position, this.locale);
|
|
25475
25487
|
if (_position < 1) return new EvaluationError(_t("The position (%s) must be greater than or equal to 1.", _position));
|
|
25476
|
-
const _text =
|
|
25488
|
+
const _text = toLocaleString(text, this.locale);
|
|
25477
25489
|
const _length = toNumber(length, this.locale);
|
|
25478
|
-
const _newText =
|
|
25490
|
+
const _newText = toLocaleString(newText, this.locale);
|
|
25479
25491
|
return _text.substring(0, _position - 1) + _newText + _text.substring(_position - 1 + _length);
|
|
25480
25492
|
},
|
|
25481
25493
|
isExported: true
|
|
@@ -25486,7 +25498,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
25486
25498
|
compute: function(text, ...args) {
|
|
25487
25499
|
const _numberOfCharacters = args.length ? toNumber(args[0], this.locale) : 1;
|
|
25488
25500
|
if (_numberOfCharacters < 0) return new EvaluationError(_t("The number_of_characters (%s) must be positive or null.", _numberOfCharacters));
|
|
25489
|
-
const _text =
|
|
25501
|
+
const _text = toLocaleString(text, this.locale);
|
|
25490
25502
|
const stringLength = _text.length;
|
|
25491
25503
|
return _text.substring(stringLength - _numberOfCharacters, stringLength);
|
|
25492
25504
|
},
|
|
@@ -25500,8 +25512,8 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
25500
25512
|
arg(`starting_at (number, default=${DEFAULT_STARTING_AT})`, _t("The character within text_to_search at which to start the search."))
|
|
25501
25513
|
],
|
|
25502
25514
|
compute: function(searchFor, textToSearch, startingAt = { value: DEFAULT_STARTING_AT }) {
|
|
25503
|
-
const _searchFor =
|
|
25504
|
-
const _textToSearch =
|
|
25515
|
+
const _searchFor = toLocaleString(searchFor, this.locale).toLowerCase();
|
|
25516
|
+
const _textToSearch = toLocaleString(textToSearch, this.locale).toLowerCase();
|
|
25505
25517
|
const _startingAt = toNumber(startingAt, this.locale);
|
|
25506
25518
|
if (_textToSearch === "") return {
|
|
25507
25519
|
value: CellErrorType.GenericError,
|
|
@@ -25531,8 +25543,8 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
25531
25543
|
arg(`remove_empty_text (boolean, default=${SPLIT_DEFAULT_REMOVE_EMPTY_TEXT})`, _t("Whether or not to remove empty text messages from the split results. The default behavior is to treat consecutive delimiters as one (if TRUE). If FALSE, empty cells values are added between consecutive delimiters."))
|
|
25532
25544
|
],
|
|
25533
25545
|
compute: function(text, delimiter, splitByEach = { value: SPLIT_DEFAULT_SPLIT_BY_EACH }, removeEmptyText = { value: SPLIT_DEFAULT_REMOVE_EMPTY_TEXT }) {
|
|
25534
|
-
const _text =
|
|
25535
|
-
const _delimiter = escapeRegExp(
|
|
25546
|
+
const _text = toLocaleString(text, this.locale);
|
|
25547
|
+
const _delimiter = escapeRegExp(toLocaleString(delimiter, this.locale));
|
|
25536
25548
|
const _splitByEach = toBoolean(splitByEach);
|
|
25537
25549
|
const _removeEmptyText = toBoolean(removeEmptyText);
|
|
25538
25550
|
if (_delimiter.length <= 0) return new EvaluationError(_t("The delimiter (%s) must be not be empty.", _delimiter));
|
|
@@ -25554,10 +25566,10 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
25554
25566
|
compute: function(textToSearch, searchFor, replaceWith, occurrenceNumber) {
|
|
25555
25567
|
const _occurrenceNumber = toNumber(occurrenceNumber, this.locale);
|
|
25556
25568
|
if (_occurrenceNumber < 0) return new EvaluationError(_t("The occurrenceNumber (%s) must be positive or null.", _occurrenceNumber));
|
|
25557
|
-
const _textToSearch =
|
|
25558
|
-
const _searchFor =
|
|
25569
|
+
const _textToSearch = toLocaleString(textToSearch, this.locale);
|
|
25570
|
+
const _searchFor = toLocaleString(searchFor, this.locale);
|
|
25559
25571
|
if (_searchFor === "") return _textToSearch;
|
|
25560
|
-
const _replaceWith =
|
|
25572
|
+
const _replaceWith = toLocaleString(replaceWith, this.locale);
|
|
25561
25573
|
const reg = new RegExp(escapeRegExp(_searchFor), "g");
|
|
25562
25574
|
if (_occurrenceNumber === 0) return _textToSearch.replace(reg, _replaceWith);
|
|
25563
25575
|
let n = 0;
|
|
@@ -25580,10 +25592,10 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
25580
25592
|
arg("texts (string, range<string>, repeating)", _t("Text item to join."))
|
|
25581
25593
|
],
|
|
25582
25594
|
compute: function(delimiter, ignoreEmpty = { value: TEXTJOIN_DEFAULT_IGNORE_EMPTY }, ...textsOrArrays) {
|
|
25583
|
-
const _delimiter =
|
|
25595
|
+
const _delimiter = toLocaleString(delimiter, this.locale);
|
|
25584
25596
|
const _ignoreEmpty = toBoolean(ignoreEmpty);
|
|
25585
25597
|
let n = 0;
|
|
25586
|
-
return reduceAny(textsOrArrays, (acc, a) => !(_ignoreEmpty &&
|
|
25598
|
+
return reduceAny(textsOrArrays, (acc, a) => !(_ignoreEmpty && toLocaleString(a, this.locale) === "") ? (n++ ? acc + _delimiter : "") + toLocaleString(a, this.locale) : acc, "");
|
|
25587
25599
|
},
|
|
25588
25600
|
isExported: true
|
|
25589
25601
|
};
|
|
@@ -25636,7 +25648,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
25636
25648
|
description: _t("Removes space characters."),
|
|
25637
25649
|
args: [arg("text (string)", _t("The text or reference to a cell containing text to be trimmed."))],
|
|
25638
25650
|
compute: function(text) {
|
|
25639
|
-
return trimContent(
|
|
25651
|
+
return trimContent(toLocaleString(text, this.locale));
|
|
25640
25652
|
},
|
|
25641
25653
|
isExported: true
|
|
25642
25654
|
};
|
|
@@ -25644,7 +25656,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
25644
25656
|
description: _t("Converts a specified string to uppercase."),
|
|
25645
25657
|
args: [arg("text (string)", _t("The string to convert to uppercase."))],
|
|
25646
25658
|
compute: function(text) {
|
|
25647
|
-
return
|
|
25659
|
+
return toLocaleString(text, this.locale).toUpperCase();
|
|
25648
25660
|
},
|
|
25649
25661
|
isExported: true
|
|
25650
25662
|
};
|
|
@@ -25741,7 +25753,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
25741
25753
|
args: [arg("url (string)", _t("The full URL of the link enclosed in quotation marks.")), arg("link_label (string, optional)", _t("The text to display in the cell, enclosed in quotation marks."))],
|
|
25742
25754
|
compute: function(url, linkLabel) {
|
|
25743
25755
|
const processedUrl = toString(url).trim();
|
|
25744
|
-
const processedLabel =
|
|
25756
|
+
const processedLabel = toLocaleString(linkLabel, this.locale) || processedUrl;
|
|
25745
25757
|
if (processedUrl === "") return processedLabel;
|
|
25746
25758
|
return markdownLink(processedLabel, processedUrl);
|
|
25747
25759
|
},
|
|
@@ -25843,6 +25855,9 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
25843
25855
|
});
|
|
25844
25856
|
}
|
|
25845
25857
|
}
|
|
25858
|
+
/** Formulas using one of these functions are never squished: they are always exported
|
|
25859
|
+
* as a full formula string, and they don't become the base formula of the next cells. */
|
|
25860
|
+
const NonSquishableFunctionRegistry = new Registry();
|
|
25846
25861
|
|
|
25847
25862
|
//#endregion
|
|
25848
25863
|
//#region src/types/xlsx.ts
|
|
@@ -36228,12 +36243,13 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
36228
36243
|
}
|
|
36229
36244
|
sortFilterZone(sortDirection) {
|
|
36230
36245
|
const filterPosition = this.props.filterPosition;
|
|
36231
|
-
const
|
|
36246
|
+
const table = this.table;
|
|
36247
|
+
const tableZone = table?.range.zone;
|
|
36232
36248
|
if (!filterPosition || !tableZone || tableZone.top === tableZone.bottom) return;
|
|
36233
36249
|
const sheetId = this.env.model.getters.getActiveSheetId();
|
|
36234
36250
|
const contentZone = {
|
|
36235
36251
|
...tableZone,
|
|
36236
|
-
top: tableZone.top +
|
|
36252
|
+
top: tableZone.top + table.config.numberOfHeaders
|
|
36237
36253
|
};
|
|
36238
36254
|
const sortAnchor = {
|
|
36239
36255
|
col: filterPosition.col,
|
|
@@ -55901,7 +55917,8 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
55901
55917
|
* The result of this method is:
|
|
55902
55918
|
* - if the cell is not a formula, returns the content as is and resets the base formula
|
|
55903
55919
|
* - if the cell is a formula:
|
|
55904
|
-
* - if there is no previous formula, or the normalized formula is different from the previous one
|
|
55920
|
+
* - if there is no previous formula, or the normalized formula is different from the previous one or it contains blacklisted functions (see NonSquishableFunctionRegistry),
|
|
55921
|
+
* resets the base formula to this one and returns the full formula string
|
|
55905
55922
|
* - else, compares the literal values and range dependencies to the previous formula, and for each parameter:
|
|
55906
55923
|
* - for numbers: returns a relative change (+N or -N) if possible, else the full number or "=" if unchanged
|
|
55907
55924
|
* - for strings: returns the full string if changed, else "="
|
|
@@ -55912,6 +55929,10 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
55912
55929
|
this.resetBase();
|
|
55913
55930
|
return cell.content;
|
|
55914
55931
|
}
|
|
55932
|
+
if (NonSquishableFunctionRegistry.getAll().some((functionName) => cell.compiledFormula.usesSymbol(functionName))) {
|
|
55933
|
+
this.resetBase();
|
|
55934
|
+
return cell.compiledFormula.toFormulaString(this.getters, this.rangeToStringOptions);
|
|
55935
|
+
}
|
|
55915
55936
|
let numbers = [];
|
|
55916
55937
|
let strings = [];
|
|
55917
55938
|
let references = [];
|
|
@@ -81612,7 +81633,8 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
81612
81633
|
supportedPivotPositionalFormulaRegistry,
|
|
81613
81634
|
pivotToFunctionValueRegistry,
|
|
81614
81635
|
migrationStepRegistry,
|
|
81615
|
-
chartJsExtensionRegistry
|
|
81636
|
+
chartJsExtensionRegistry,
|
|
81637
|
+
NonSquishableFunctionRegistry
|
|
81616
81638
|
};
|
|
81617
81639
|
const helpers = {
|
|
81618
81640
|
arg,
|
|
@@ -81862,8 +81884,8 @@ exports.stores = stores;
|
|
|
81862
81884
|
exports.tokenColors = tokenColors;
|
|
81863
81885
|
exports.tokenize = tokenize;
|
|
81864
81886
|
|
|
81865
|
-
__info__.version = "19.2.
|
|
81866
|
-
__info__.date = "2026-08-
|
|
81867
|
-
__info__.hash = "
|
|
81887
|
+
__info__.version = "19.2.26";
|
|
81888
|
+
__info__.date = "2026-08-21T15:30:14.701Z";
|
|
81889
|
+
__info__.hash = "b9d68c1";
|
|
81868
81890
|
|
|
81869
81891
|
})(this.o_spreadsheet = this.o_spreadsheet || {}, owl);
|