@odoo/o-spreadsheet 17.1.3 → 17.1.5
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.cjs.js +320 -196
- package/dist/o-spreadsheet.d.ts +47 -22
- package/dist/o-spreadsheet.esm.js +320 -196
- package/dist/o-spreadsheet.iife.js +320 -196
- package/dist/o-spreadsheet.iife.min.js +262 -261
- package/dist/o_spreadsheet.xml +19 -36
- package/package.json +1 -1
|
@@ -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 17.1.
|
|
6
|
-
* @date 2024-02-
|
|
7
|
-
* @hash
|
|
5
|
+
* @version 17.1.5
|
|
6
|
+
* @date 2024-02-16T14:23:54.651Z
|
|
7
|
+
* @hash 29d8ad6
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
'use strict';
|
|
@@ -453,8 +453,8 @@ function isObjectEmptyRecursive(argument) {
|
|
|
453
453
|
* If the given item does not exist in the dictionary, it creates one with a new id.
|
|
454
454
|
*/
|
|
455
455
|
function getItemId(item, itemsDic) {
|
|
456
|
-
for (
|
|
457
|
-
if (deepEquals(
|
|
456
|
+
for (const key in itemsDic) {
|
|
457
|
+
if (deepEquals(itemsDic[key], item)) {
|
|
458
458
|
return parseInt(key, 10);
|
|
459
459
|
}
|
|
460
460
|
}
|
|
@@ -553,11 +553,13 @@ function deepEquals(o1, o2) {
|
|
|
553
553
|
if (typeof o1 !== "object")
|
|
554
554
|
return o1 === o2;
|
|
555
555
|
// Objects can have different keys if the values are undefined
|
|
556
|
-
const
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
556
|
+
for (const key in o2) {
|
|
557
|
+
if (!(key in o1) && o2[key] !== undefined) {
|
|
558
|
+
return false;
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
for (const key in o1) {
|
|
562
|
+
if (typeof o1[key] !== typeof o2[key])
|
|
561
563
|
return false;
|
|
562
564
|
if (typeof o1[key] === "object") {
|
|
563
565
|
if (!deepEquals(o1[key], o2[key]))
|
|
@@ -627,18 +629,18 @@ function isConsecutive(iterable) {
|
|
|
627
629
|
return true;
|
|
628
630
|
}
|
|
629
631
|
class JetSet extends Set {
|
|
630
|
-
|
|
632
|
+
addMany(iterable) {
|
|
631
633
|
for (const element of iterable) {
|
|
632
634
|
super.add(element);
|
|
633
635
|
}
|
|
634
636
|
return this;
|
|
635
637
|
}
|
|
636
|
-
|
|
637
|
-
let
|
|
638
|
+
deleteMany(iterable) {
|
|
639
|
+
let wasDeleted = false;
|
|
638
640
|
for (const element of iterable) {
|
|
639
|
-
|
|
641
|
+
wasDeleted ||= super.delete(element);
|
|
640
642
|
}
|
|
641
|
-
return
|
|
643
|
+
return wasDeleted;
|
|
642
644
|
}
|
|
643
645
|
}
|
|
644
646
|
/**
|
|
@@ -1936,7 +1938,7 @@ class DispatchResult {
|
|
|
1936
1938
|
* Static helper which returns a successful DispatchResult
|
|
1937
1939
|
*/
|
|
1938
1940
|
static get Success() {
|
|
1939
|
-
return
|
|
1941
|
+
return SUCCESS;
|
|
1940
1942
|
}
|
|
1941
1943
|
get isSuccessful() {
|
|
1942
1944
|
return this.reasons.length === 0;
|
|
@@ -1949,6 +1951,7 @@ class DispatchResult {
|
|
|
1949
1951
|
return this.reasons.includes(reason);
|
|
1950
1952
|
}
|
|
1951
1953
|
}
|
|
1954
|
+
const SUCCESS = new DispatchResult();
|
|
1952
1955
|
exports.CommandResult = void 0;
|
|
1953
1956
|
(function (CommandResult) {
|
|
1954
1957
|
CommandResult["Success"] = "Success";
|
|
@@ -2940,6 +2943,9 @@ function formatValue(value, { format, locale }) {
|
|
|
2940
2943
|
}
|
|
2941
2944
|
switch (typeof value) {
|
|
2942
2945
|
case "string":
|
|
2946
|
+
if (value.includes('\\"')) {
|
|
2947
|
+
return value.replace(/\\"/g, '"');
|
|
2948
|
+
}
|
|
2943
2949
|
return value;
|
|
2944
2950
|
case "boolean":
|
|
2945
2951
|
return value ? "TRUE" : "FALSE";
|
|
@@ -4993,14 +4999,8 @@ function createDataSets(getters, dataSetsString, sheetId, dataSetsHaveTitle) {
|
|
|
4993
4999
|
: undefined));
|
|
4994
5000
|
}
|
|
4995
5001
|
}
|
|
4996
|
-
else if (zone.left === zone.right && zone.top === zone.bottom) {
|
|
4997
|
-
// A single cell. If it's only the title, the dataset is not added.
|
|
4998
|
-
if (!dataSetsHaveTitle) {
|
|
4999
|
-
dataSets.push(createDataSet(getters, dataSetSheetId, zone, undefined));
|
|
5000
|
-
}
|
|
5001
|
-
}
|
|
5002
5002
|
else {
|
|
5003
|
-
/* 1 row or 1 column */
|
|
5003
|
+
/* 1 cell, 1 row or 1 column */
|
|
5004
5004
|
dataSets.push(createDataSet(getters, dataSetSheetId, zone, dataSetsHaveTitle
|
|
5005
5005
|
? {
|
|
5006
5006
|
top: zone.top,
|
|
@@ -7443,7 +7443,7 @@ function tokenize(str, locale = DEFAULT_LOCALE) {
|
|
|
7443
7443
|
while (!chars.isOver()) {
|
|
7444
7444
|
let token = tokenizeSpace(chars) ||
|
|
7445
7445
|
tokenizeArgsSeparator(chars, locale) ||
|
|
7446
|
-
|
|
7446
|
+
tokenizeParenthesis(chars) ||
|
|
7447
7447
|
tokenizeOperator(chars) ||
|
|
7448
7448
|
tokenizeString(chars) ||
|
|
7449
7449
|
tokenizeDebugger(chars) ||
|
|
@@ -7464,15 +7464,14 @@ function tokenizeDebugger(chars) {
|
|
|
7464
7464
|
}
|
|
7465
7465
|
return null;
|
|
7466
7466
|
}
|
|
7467
|
-
const
|
|
7468
|
-
"(": "LEFT_PAREN",
|
|
7469
|
-
")": "RIGHT_PAREN",
|
|
7467
|
+
const parenthesis = {
|
|
7468
|
+
"(": { type: "LEFT_PAREN", value: "(" },
|
|
7469
|
+
")": { type: "RIGHT_PAREN", value: ")" },
|
|
7470
7470
|
};
|
|
7471
|
-
function
|
|
7472
|
-
if (chars.current
|
|
7471
|
+
function tokenizeParenthesis(chars) {
|
|
7472
|
+
if (chars.current === "(" || chars.current === ")") {
|
|
7473
7473
|
const value = chars.shift();
|
|
7474
|
-
|
|
7475
|
-
return { type, value };
|
|
7474
|
+
return parenthesis[value];
|
|
7476
7475
|
}
|
|
7477
7476
|
return null;
|
|
7478
7477
|
}
|
|
@@ -7493,7 +7492,12 @@ function tokenizeOperator(chars) {
|
|
|
7493
7492
|
}
|
|
7494
7493
|
return null;
|
|
7495
7494
|
}
|
|
7495
|
+
const FIRST_POSSIBLE_NUMBER_CHARS = new Set("0123456789");
|
|
7496
7496
|
function tokenizeNumber(chars, locale) {
|
|
7497
|
+
if (!FIRST_POSSIBLE_NUMBER_CHARS.has(chars.current) &&
|
|
7498
|
+
chars.current !== locale.decimalSeparator) {
|
|
7499
|
+
return null;
|
|
7500
|
+
}
|
|
7497
7501
|
const match = chars.remaining().match(getFormulaNumberRegex(locale.decimalSeparator));
|
|
7498
7502
|
if (match) {
|
|
7499
7503
|
chars.advanceBy(match[0].length);
|
|
@@ -7518,7 +7522,7 @@ function tokenizeString(chars) {
|
|
|
7518
7522
|
}
|
|
7519
7523
|
return null;
|
|
7520
7524
|
}
|
|
7521
|
-
const separatorRegexp =
|
|
7525
|
+
const separatorRegexp = /^[\w\.!\$]+/;
|
|
7522
7526
|
/**
|
|
7523
7527
|
* A "Symbol" is just basically any word-like element that can appear in a
|
|
7524
7528
|
* formula, which is not a string. So:
|
|
@@ -7558,8 +7562,11 @@ function tokenizeSymbol(chars) {
|
|
|
7558
7562
|
};
|
|
7559
7563
|
}
|
|
7560
7564
|
}
|
|
7561
|
-
|
|
7562
|
-
|
|
7565
|
+
const match = chars.remaining().match(separatorRegexp);
|
|
7566
|
+
if (match) {
|
|
7567
|
+
const value = match[0];
|
|
7568
|
+
result += value;
|
|
7569
|
+
chars.advanceBy(value.length);
|
|
7563
7570
|
}
|
|
7564
7571
|
if (result.length) {
|
|
7565
7572
|
const value = result;
|
|
@@ -7621,7 +7628,10 @@ class TokenizingChars {
|
|
|
7621
7628
|
return this.text.substring(this.currentIndex);
|
|
7622
7629
|
}
|
|
7623
7630
|
currentStartsWith(str) {
|
|
7624
|
-
|
|
7631
|
+
if (this.current !== str[0]) {
|
|
7632
|
+
return false;
|
|
7633
|
+
}
|
|
7634
|
+
for (let j = 1; j < str.length; j++) {
|
|
7625
7635
|
if (this.text[this.currentIndex + j] !== str[j]) {
|
|
7626
7636
|
return false;
|
|
7627
7637
|
}
|
|
@@ -7648,6 +7658,9 @@ function isValidLocale(locale) {
|
|
|
7648
7658
|
if (locale.formulaArgSeparator === locale.decimalSeparator) {
|
|
7649
7659
|
return false;
|
|
7650
7660
|
}
|
|
7661
|
+
if (locale.thousandsSeparator === locale.decimalSeparator) {
|
|
7662
|
+
return false;
|
|
7663
|
+
}
|
|
7651
7664
|
try {
|
|
7652
7665
|
formatValue(1, { locale, format: "#,##0.00" });
|
|
7653
7666
|
formatValue(1, { locale, format: locale.dateFormat });
|
|
@@ -7738,7 +7751,7 @@ function canonicalizeNumberLiteral(content, locale) {
|
|
|
7738
7751
|
if (locale.decimalSeparator === "." || !isNumber(content, locale)) {
|
|
7739
7752
|
return content;
|
|
7740
7753
|
}
|
|
7741
|
-
return content.replace(locale.decimalSeparator, ".");
|
|
7754
|
+
return content.replace(locale.thousandsSeparator, "").replace(locale.decimalSeparator, ".");
|
|
7742
7755
|
}
|
|
7743
7756
|
/**
|
|
7744
7757
|
* Change a content string from the given locale to its canonical form (en_US locale). Also convert date string.
|
|
@@ -8653,7 +8666,7 @@ class BarChart extends AbstractChart {
|
|
|
8653
8666
|
return undefined;
|
|
8654
8667
|
const dataSets = this.dataSets
|
|
8655
8668
|
.map((ds) => toExcelDataset(this.getters, ds))
|
|
8656
|
-
.filter((ds) => ds.range !== ""
|
|
8669
|
+
.filter((ds) => ds.range !== "" && ds.range !== CellErrorType.InvalidReference);
|
|
8657
8670
|
const labelRange = toExcelLabelRange(this.getters, this.labelRange, shouldRemoveFirstLabel(this.labelRange, this.dataSets[0], this.dataSetsHaveTitle));
|
|
8658
8671
|
return {
|
|
8659
8672
|
...this.getDefinition(),
|
|
@@ -9231,7 +9244,7 @@ class LineChart extends AbstractChart {
|
|
|
9231
9244
|
return undefined;
|
|
9232
9245
|
const dataSets = this.dataSets
|
|
9233
9246
|
.map((ds) => toExcelDataset(this.getters, ds))
|
|
9234
|
-
.filter((ds) => ds.range !== ""
|
|
9247
|
+
.filter((ds) => ds.range !== "" && ds.range !== CellErrorType.InvalidReference);
|
|
9235
9248
|
const labelRange = toExcelLabelRange(this.getters, this.labelRange, shouldRemoveFirstLabel(this.labelRange, this.dataSets[0], this.dataSetsHaveTitle));
|
|
9236
9249
|
return {
|
|
9237
9250
|
...this.getDefinition(),
|
|
@@ -9537,7 +9550,7 @@ class PieChart extends AbstractChart {
|
|
|
9537
9550
|
return undefined;
|
|
9538
9551
|
const dataSets = this.dataSets
|
|
9539
9552
|
.map((ds) => toExcelDataset(this.getters, ds))
|
|
9540
|
-
.filter((ds) => ds.range !== ""
|
|
9553
|
+
.filter((ds) => ds.range !== "" && ds.range !== CellErrorType.InvalidReference);
|
|
9541
9554
|
const labelRange = toExcelLabelRange(this.getters, this.labelRange, shouldRemoveFirstLabel(this.labelRange, this.dataSets[0], this.dataSetsHaveTitle));
|
|
9542
9555
|
return {
|
|
9543
9556
|
...this.getDefinition(),
|
|
@@ -10833,6 +10846,12 @@ const INSERT_LINK = (env) => {
|
|
|
10833
10846
|
let { col, row } = env.model.getters.getActivePosition();
|
|
10834
10847
|
env.model.dispatch("OPEN_CELL_POPOVER", { col, row, popoverType: "LinkEditor" });
|
|
10835
10848
|
};
|
|
10849
|
+
const INSERT_LINK_NAME = (env) => {
|
|
10850
|
+
const sheetId = env.model.getters.getActiveSheetId();
|
|
10851
|
+
const { col, row } = env.model.getters.getActivePosition();
|
|
10852
|
+
const cell = env.model.getters.getEvaluatedCell({ sheetId, col, row });
|
|
10853
|
+
return cell && cell.link ? _t("Edit link") : _t("Insert link");
|
|
10854
|
+
};
|
|
10836
10855
|
//------------------------------------------------------------------------------
|
|
10837
10856
|
// Filters action
|
|
10838
10857
|
//------------------------------------------------------------------------------
|
|
@@ -18684,6 +18703,9 @@ const HLOOKUP = {
|
|
|
18684
18703
|
compute: function (searchKey, range, index, isSorted = { value: DEFAULT_IS_SORTED }) {
|
|
18685
18704
|
const _index = Math.trunc(toNumber(index?.value, this.locale));
|
|
18686
18705
|
assert(() => 1 <= _index && _index <= range[0].length, _t("[[FUNCTION_NAME]] evaluates to an out of bounds range."));
|
|
18706
|
+
if (searchKey && isEvaluationError(searchKey.value)) {
|
|
18707
|
+
return searchKey;
|
|
18708
|
+
}
|
|
18687
18709
|
const getValueFromRange = (range, index) => range[index][0].value;
|
|
18688
18710
|
const _isSorted = toBoolean(isSorted.value);
|
|
18689
18711
|
const colIndex = _isSorted
|
|
@@ -18851,6 +18873,9 @@ const VLOOKUP = {
|
|
|
18851
18873
|
compute: function (searchKey, range, index, isSorted = { value: DEFAULT_IS_SORTED }) {
|
|
18852
18874
|
const _index = Math.trunc(toNumber(index?.value, this.locale));
|
|
18853
18875
|
assert(() => 1 <= _index && _index <= range.length, _t("[[FUNCTION_NAME]] evaluates to an out of bounds range."));
|
|
18876
|
+
if (searchKey && isEvaluationError(searchKey.value)) {
|
|
18877
|
+
return searchKey;
|
|
18878
|
+
}
|
|
18854
18879
|
const getValueFromRange = (range, index) => range[0][index].value;
|
|
18855
18880
|
const _isSorted = toBoolean(isSorted.value);
|
|
18856
18881
|
const rowIndex = _isSorted
|
|
@@ -18890,6 +18915,9 @@ const XLOOKUP = {
|
|
|
18890
18915
|
assert(() => lookupDirection === "col"
|
|
18891
18916
|
? returnRange[0].length === lookupRange[0].length
|
|
18892
18917
|
: returnRange.length === lookupRange.length, _t("return_range should have the same dimensions as lookup_range."));
|
|
18918
|
+
if (searchKey && isEvaluationError(searchKey.value)) {
|
|
18919
|
+
return [[searchKey]];
|
|
18920
|
+
}
|
|
18893
18921
|
const getElement = lookupDirection === "col"
|
|
18894
18922
|
? (range, index) => range[0][index].value
|
|
18895
18923
|
: (range, index) => range[index][0].value;
|
|
@@ -20068,7 +20096,7 @@ cellMenuRegistry
|
|
|
20068
20096
|
})
|
|
20069
20097
|
.add("insert_link", {
|
|
20070
20098
|
...insertLink,
|
|
20071
|
-
name:
|
|
20099
|
+
name: INSERT_LINK_NAME,
|
|
20072
20100
|
sequence: 150,
|
|
20073
20101
|
separator: true,
|
|
20074
20102
|
});
|
|
@@ -26511,6 +26539,17 @@ class TextValueProvider extends owl.Component {
|
|
|
26511
26539
|
onValueSelected: Function,
|
|
26512
26540
|
onValueHovered: Function,
|
|
26513
26541
|
};
|
|
26542
|
+
autoCompleteListRef = owl.useRef("autoCompleteList");
|
|
26543
|
+
setup() {
|
|
26544
|
+
owl.useEffect(() => {
|
|
26545
|
+
const selectedIndex = this.props.selectedIndex;
|
|
26546
|
+
if (selectedIndex === undefined) {
|
|
26547
|
+
return;
|
|
26548
|
+
}
|
|
26549
|
+
const selectedElement = this.autoCompleteListRef.el?.children[selectedIndex];
|
|
26550
|
+
selectedElement?.scrollIntoView?.({ block: "nearest" });
|
|
26551
|
+
}, () => [this.props.selectedIndex, this.autoCompleteListRef.el]);
|
|
26552
|
+
}
|
|
26514
26553
|
}
|
|
26515
26554
|
|
|
26516
26555
|
class ContentEditableHelper {
|
|
@@ -26955,6 +26994,7 @@ css /* scss */ `
|
|
|
26955
26994
|
position: absolute;
|
|
26956
26995
|
margin: 1px 4px;
|
|
26957
26996
|
pointer-events: none;
|
|
26997
|
+
overflow: auto;
|
|
26958
26998
|
|
|
26959
26999
|
.o-semi-bold {
|
|
26960
27000
|
/** FIXME: to remove in favor of Bootstrap
|
|
@@ -26971,15 +27011,17 @@ class Composer extends owl.Component {
|
|
|
26971
27011
|
focus: {
|
|
26972
27012
|
validate: (value) => ["inactive", "cellFocus", "contentFocus"].includes(value),
|
|
26973
27013
|
},
|
|
26974
|
-
onComposerContentFocused: Function,
|
|
26975
27014
|
inputStyle: { type: String, optional: true },
|
|
26976
27015
|
rect: { type: Object, optional: true },
|
|
26977
27016
|
delimitation: { type: Object, optional: true },
|
|
26978
|
-
|
|
27017
|
+
onComposerCellFocused: { type: Function, optional: true },
|
|
27018
|
+
onComposerContentFocused: Function,
|
|
27019
|
+
isDefaultFocus: { type: Boolean, optional: true },
|
|
26979
27020
|
};
|
|
26980
27021
|
static components = { TextValueProvider, FunctionDescriptionProvider };
|
|
26981
27022
|
static defaultProps = {
|
|
26982
27023
|
inputStyle: "",
|
|
27024
|
+
isDefaultFocus: false,
|
|
26983
27025
|
};
|
|
26984
27026
|
composerRef = owl.useRef("o_composer");
|
|
26985
27027
|
contentHelper = new ContentEditableHelper(this.composerRef.el);
|
|
@@ -27010,7 +27052,10 @@ class Composer extends owl.Component {
|
|
|
27010
27052
|
if (this.props.delimitation && this.props.rect) {
|
|
27011
27053
|
const { x: cellX, y: cellY, height: cellHeight } = this.props.rect;
|
|
27012
27054
|
const remainingHeight = this.props.delimitation.height - (cellY + cellHeight);
|
|
27055
|
+
assistantStyle["max-height"] = `${remainingHeight}px`;
|
|
27013
27056
|
if (cellY > remainingHeight) {
|
|
27057
|
+
const availableSpaceAbove = cellY;
|
|
27058
|
+
assistantStyle["max-height"] = `${availableSpaceAbove}px`;
|
|
27014
27059
|
// render top
|
|
27015
27060
|
// We compensate 2 px of margin on the assistant style + 1px for design reasons
|
|
27016
27061
|
assistantStyle.top = `-3px`;
|
|
@@ -27036,7 +27081,6 @@ class Composer extends owl.Component {
|
|
|
27036
27081
|
F4: this.processF4Key,
|
|
27037
27082
|
Tab: (ev) => this.processTabKey(ev, "right"),
|
|
27038
27083
|
"Shift+Tab": (ev) => this.processTabKey(ev, "left"),
|
|
27039
|
-
"Ctrl+ ": this.processSpaceKey,
|
|
27040
27084
|
};
|
|
27041
27085
|
keyCodeMapping = {
|
|
27042
27086
|
NumpadDecimal: this.processNumpadDecimal,
|
|
@@ -27044,12 +27088,12 @@ class Composer extends owl.Component {
|
|
|
27044
27088
|
setup() {
|
|
27045
27089
|
owl.onMounted(() => {
|
|
27046
27090
|
const el = this.composerRef.el;
|
|
27091
|
+
if (this.props.isDefaultFocus) {
|
|
27092
|
+
this.env.focusableElement.setFocusableElement(el);
|
|
27093
|
+
}
|
|
27047
27094
|
this.contentHelper.updateEl(el);
|
|
27048
27095
|
this.processTokenAtCursor();
|
|
27049
27096
|
});
|
|
27050
|
-
owl.onWillUnmount(() => {
|
|
27051
|
-
this.props.onComposerUnmounted?.();
|
|
27052
|
-
});
|
|
27053
27097
|
owl.useEffect(() => {
|
|
27054
27098
|
this.processContent();
|
|
27055
27099
|
});
|
|
@@ -27058,7 +27102,8 @@ class Composer extends owl.Component {
|
|
|
27058
27102
|
// Handlers
|
|
27059
27103
|
// ---------------------------------------------------------------------------
|
|
27060
27104
|
processArrowKeys(ev) {
|
|
27061
|
-
if (this.env.model.getters.isSelectingForComposer()
|
|
27105
|
+
if (this.env.model.getters.isSelectingForComposer() ||
|
|
27106
|
+
this.env.model.getters.getEditionMode() === "inactive") {
|
|
27062
27107
|
this.functionDescriptionState.showDescription = false;
|
|
27063
27108
|
// Prevent the default content editable behavior which moves the cursor
|
|
27064
27109
|
ev.preventDefault();
|
|
@@ -27101,23 +27146,19 @@ class Composer extends owl.Component {
|
|
|
27101
27146
|
processTabKey(ev, direction) {
|
|
27102
27147
|
ev.preventDefault();
|
|
27103
27148
|
ev.stopPropagation();
|
|
27104
|
-
|
|
27105
|
-
|
|
27106
|
-
|
|
27107
|
-
|
|
27108
|
-
|
|
27109
|
-
|
|
27149
|
+
if (this.env.model.getters.getEditionMode() !== "inactive") {
|
|
27150
|
+
const state = this.autoCompleteState;
|
|
27151
|
+
if (state.showProvider && state.selectedIndex !== undefined) {
|
|
27152
|
+
const autoCompleteValue = this.autoCompleteState.values[state.selectedIndex]?.text;
|
|
27153
|
+
if (autoCompleteValue) {
|
|
27154
|
+
this.autoComplete(autoCompleteValue);
|
|
27155
|
+
return;
|
|
27156
|
+
}
|
|
27110
27157
|
}
|
|
27158
|
+
interactiveStopEdition(this.env);
|
|
27111
27159
|
}
|
|
27112
|
-
interactiveStopEdition(this.env);
|
|
27113
27160
|
this.env.model.selection.moveAnchorCell(direction, 1);
|
|
27114
27161
|
}
|
|
27115
|
-
processSpaceKey(ev) {
|
|
27116
|
-
ev.preventDefault();
|
|
27117
|
-
ev.stopPropagation();
|
|
27118
|
-
this.showFunctionAutocomplete("");
|
|
27119
|
-
this.env.model.dispatch("STOP_COMPOSER_RANGE_SELECTION");
|
|
27120
|
-
}
|
|
27121
27162
|
processEnterKey(ev, direction) {
|
|
27122
27163
|
ev.preventDefault();
|
|
27123
27164
|
ev.stopPropagation();
|
|
@@ -27179,6 +27220,9 @@ class Composer extends owl.Component {
|
|
|
27179
27220
|
this.compositionActive = false;
|
|
27180
27221
|
}
|
|
27181
27222
|
onKeydown(ev) {
|
|
27223
|
+
if (this.env.model.getters.getEditionMode() === "inactive") {
|
|
27224
|
+
return;
|
|
27225
|
+
}
|
|
27182
27226
|
if (ev.key.startsWith("Arrow")) {
|
|
27183
27227
|
this.processArrowKeys(ev);
|
|
27184
27228
|
return;
|
|
@@ -27192,14 +27236,33 @@ class Composer extends owl.Component {
|
|
|
27192
27236
|
ev.stopPropagation();
|
|
27193
27237
|
}
|
|
27194
27238
|
}
|
|
27239
|
+
onPaste(ev) {
|
|
27240
|
+
if (this.env.model.getters.getEditionMode() !== "inactive") {
|
|
27241
|
+
ev.stopPropagation();
|
|
27242
|
+
}
|
|
27243
|
+
}
|
|
27195
27244
|
/*
|
|
27196
27245
|
* Triggered automatically by the content-editable between the keydown and key up
|
|
27197
27246
|
* */
|
|
27198
27247
|
onInput(ev) {
|
|
27199
|
-
if (
|
|
27248
|
+
if (!this.shouldProcessInputEvents) {
|
|
27200
27249
|
return;
|
|
27201
27250
|
}
|
|
27202
|
-
|
|
27251
|
+
if (ev.inputType === "insertFromPaste" &&
|
|
27252
|
+
this.env.model.getters.getEditionMode() === "inactive") {
|
|
27253
|
+
return;
|
|
27254
|
+
}
|
|
27255
|
+
ev.stopPropagation();
|
|
27256
|
+
let content;
|
|
27257
|
+
if (this.env.model.getters.getEditionMode() === "inactive") {
|
|
27258
|
+
content = ev.data || "";
|
|
27259
|
+
}
|
|
27260
|
+
else {
|
|
27261
|
+
content = this.contentHelper.getText();
|
|
27262
|
+
}
|
|
27263
|
+
if (this.props.focus === "inactive") {
|
|
27264
|
+
return this.props.onComposerCellFocused?.(content);
|
|
27265
|
+
}
|
|
27203
27266
|
let selection = this.contentHelper.getCurrentSelection();
|
|
27204
27267
|
this.env.model.dispatch("STOP_COMPOSER_RANGE_SELECTION");
|
|
27205
27268
|
this.env.model.dispatch("SET_CURRENT_CONTENT", {
|
|
@@ -27275,9 +27338,8 @@ class Composer extends owl.Component {
|
|
|
27275
27338
|
}
|
|
27276
27339
|
const newSelection = this.contentHelper.getCurrentSelection();
|
|
27277
27340
|
this.env.model.dispatch("STOP_COMPOSER_RANGE_SELECTION");
|
|
27278
|
-
|
|
27279
|
-
|
|
27280
|
-
}
|
|
27341
|
+
this.props.onComposerContentFocused();
|
|
27342
|
+
if (this.props.focus === "inactive") ;
|
|
27281
27343
|
this.env.model.dispatch("CHANGE_COMPOSER_CURSOR_SELECTION", newSelection);
|
|
27282
27344
|
this.processTokenAtCursor();
|
|
27283
27345
|
}
|
|
@@ -27444,7 +27506,7 @@ class Composer extends owl.Component {
|
|
|
27444
27506
|
const highlights = this.env.model.getters.getHighlights();
|
|
27445
27507
|
const refSheet = sheetName
|
|
27446
27508
|
? this.env.model.getters.getSheetIdByName(sheetName)
|
|
27447
|
-
: this.env.model.getters.getCurrentEditedCell()
|
|
27509
|
+
: this.env.model.getters.getCurrentEditedCell()?.sheetId;
|
|
27448
27510
|
const highlight = highlights.find((highlight) => {
|
|
27449
27511
|
if (highlight.sheetId !== refSheet)
|
|
27450
27512
|
return false;
|
|
@@ -27571,56 +27633,30 @@ class GridComposer extends owl.Component {
|
|
|
27571
27633
|
focus: {
|
|
27572
27634
|
validate: (value) => ["inactive", "cellFocus", "contentFocus"].includes(value),
|
|
27573
27635
|
},
|
|
27574
|
-
|
|
27636
|
+
onComposerCellFocused: Function,
|
|
27575
27637
|
onComposerContentFocused: Function,
|
|
27576
27638
|
gridDims: Object,
|
|
27577
27639
|
};
|
|
27578
27640
|
static components = { Composer };
|
|
27579
|
-
|
|
27580
|
-
|
|
27581
|
-
rect;
|
|
27641
|
+
rect = this.defaultRect;
|
|
27642
|
+
isEditing = false;
|
|
27582
27643
|
isCellReferenceVisible;
|
|
27583
|
-
|
|
27644
|
+
get defaultRect() {
|
|
27645
|
+
return { x: 0, y: 0, width: 0, height: 0 };
|
|
27646
|
+
}
|
|
27584
27647
|
setup() {
|
|
27585
|
-
this.gridComposerRef = owl.useRef("gridComposer");
|
|
27586
|
-
this.composerState = owl.useState({
|
|
27587
|
-
rect: undefined,
|
|
27588
|
-
delimitation: undefined,
|
|
27589
|
-
});
|
|
27590
|
-
const { sheetId, col, row } = this.env.model.getters.getActivePosition();
|
|
27591
|
-
this.zone = this.env.model.getters.expandZone(sheetId, positionToZone({ col, row }));
|
|
27592
|
-
this.rect = this.env.model.getters.getVisibleRect(this.zone);
|
|
27593
|
-
this.isCellReferenceVisible = false;
|
|
27594
|
-
owl.onMounted(() => {
|
|
27595
|
-
const el = this.gridComposerRef.el;
|
|
27596
|
-
this.composerState.rect = {
|
|
27597
|
-
x: this.rect.x,
|
|
27598
|
-
y: this.rect.y,
|
|
27599
|
-
width: el.clientWidth,
|
|
27600
|
-
height: el.clientHeight,
|
|
27601
|
-
};
|
|
27602
|
-
this.composerState.delimitation = {
|
|
27603
|
-
width: this.props.gridDims.width,
|
|
27604
|
-
height: this.props.gridDims.height,
|
|
27605
|
-
};
|
|
27606
|
-
});
|
|
27607
27648
|
owl.onWillUpdateProps(() => {
|
|
27608
|
-
|
|
27609
|
-
|
|
27610
|
-
}
|
|
27611
|
-
const sheetId = this.env.model.getters.getActiveSheetId();
|
|
27612
|
-
const zone = this.env.model.getters.getSelectedZone();
|
|
27613
|
-
const rect = this.env.model.getters.getVisibleRect(zone);
|
|
27614
|
-
if (!deepEquals(rect, this.rect) ||
|
|
27615
|
-
sheetId !== this.env.model.getters.getCurrentEditedCell().sheetId) {
|
|
27616
|
-
this.isCellReferenceVisible = true;
|
|
27617
|
-
}
|
|
27649
|
+
this.updateComponentPosition();
|
|
27650
|
+
this.updateCellReferenceVisibility();
|
|
27618
27651
|
});
|
|
27619
27652
|
}
|
|
27620
27653
|
get shouldDisplayCellReference() {
|
|
27621
27654
|
return this.isCellReferenceVisible;
|
|
27622
27655
|
}
|
|
27623
27656
|
get cellReference() {
|
|
27657
|
+
if (!this.env.model.getters.getCurrentEditedCell()) {
|
|
27658
|
+
return "";
|
|
27659
|
+
}
|
|
27624
27660
|
const { col, row, sheetId } = this.env.model.getters.getCurrentEditedCell();
|
|
27625
27661
|
const prefixSheet = sheetId !== this.env.model.getters.getActiveSheetId();
|
|
27626
27662
|
return getFullReference(prefixSheet ? this.env.model.getters.getSheetName(sheetId) : undefined, toXC(col, row));
|
|
@@ -27632,7 +27668,27 @@ class GridComposer extends owl.Component {
|
|
|
27632
27668
|
top: `${top - GRID_CELL_REFERENCE_TOP_OFFSET}px`,
|
|
27633
27669
|
});
|
|
27634
27670
|
}
|
|
27671
|
+
get composerProps() {
|
|
27672
|
+
const { width, height } = this.env.model.getters.getSheetViewDimensionWithHeaders();
|
|
27673
|
+
return {
|
|
27674
|
+
rect: { ...this.rect },
|
|
27675
|
+
delimitation: {
|
|
27676
|
+
width,
|
|
27677
|
+
height,
|
|
27678
|
+
},
|
|
27679
|
+
focus: this.props.focus,
|
|
27680
|
+
isDefaultFocus: true,
|
|
27681
|
+
onComposerContentFocused: this.props.onComposerContentFocused,
|
|
27682
|
+
onComposerCellFocused: this.props.onComposerCellFocused,
|
|
27683
|
+
};
|
|
27684
|
+
}
|
|
27635
27685
|
get containerStyle() {
|
|
27686
|
+
if (this.env.model.getters.getEditionMode() === "inactive" || !this.rect) {
|
|
27687
|
+
return `
|
|
27688
|
+
position: absolute;
|
|
27689
|
+
z-index: -1000;
|
|
27690
|
+
`;
|
|
27691
|
+
}
|
|
27636
27692
|
const isFormula = this.env.model.getters.getCurrentContent().startsWith("=");
|
|
27637
27693
|
const cell = this.env.model.getters.getActiveCell();
|
|
27638
27694
|
const position = this.env.model.getters.getActivePosition();
|
|
@@ -27652,6 +27708,8 @@ class GridComposer extends owl.Component {
|
|
|
27652
27708
|
if (!isFormula) {
|
|
27653
27709
|
textAlign = style.align || cell.defaultAlign;
|
|
27654
27710
|
}
|
|
27711
|
+
const maxHeight = this.props.gridDims.height - this.rect.y;
|
|
27712
|
+
const maxWidth = this.props.gridDims.width - this.rect.x;
|
|
27655
27713
|
/**
|
|
27656
27714
|
* min-size is on the container, not the composer element, because we want to have the same size as the cell by default,
|
|
27657
27715
|
* including all the paddings/margins of the composer
|
|
@@ -27663,6 +27721,8 @@ class GridComposer extends owl.Component {
|
|
|
27663
27721
|
top: `${top}px`,
|
|
27664
27722
|
"min-width": `${width + 1}px`,
|
|
27665
27723
|
"min-height": `${height + 1}px`,
|
|
27724
|
+
"max-width": `${maxWidth}px`,
|
|
27725
|
+
"max-height": `${maxHeight}px`,
|
|
27666
27726
|
background,
|
|
27667
27727
|
color,
|
|
27668
27728
|
"font-size": `${fontSizeInPixels(fontSize)}px`,
|
|
@@ -27672,13 +27732,31 @@ class GridComposer extends owl.Component {
|
|
|
27672
27732
|
"text-align": textAlign,
|
|
27673
27733
|
});
|
|
27674
27734
|
}
|
|
27675
|
-
|
|
27676
|
-
const
|
|
27677
|
-
|
|
27678
|
-
|
|
27679
|
-
|
|
27680
|
-
|
|
27681
|
-
|
|
27735
|
+
updateComponentPosition() {
|
|
27736
|
+
const isEditing = this.env.model.getters.getEditionMode() !== "inactive";
|
|
27737
|
+
if (this.isEditing !== isEditing) {
|
|
27738
|
+
this.isEditing = isEditing;
|
|
27739
|
+
if (!isEditing) {
|
|
27740
|
+
this.rect = this.defaultRect;
|
|
27741
|
+
this.env.focusableElement.focus();
|
|
27742
|
+
return;
|
|
27743
|
+
}
|
|
27744
|
+
const position = this.env.model.getters.getActivePosition();
|
|
27745
|
+
const zone = this.env.model.getters.expandZone(position.sheetId, positionToZone(position));
|
|
27746
|
+
this.rect = this.env.model.getters.getVisibleRect(zone);
|
|
27747
|
+
}
|
|
27748
|
+
}
|
|
27749
|
+
updateCellReferenceVisibility() {
|
|
27750
|
+
if (this.isCellReferenceVisible || this.env.model.getters.getEditionMode() === "inactive") {
|
|
27751
|
+
return;
|
|
27752
|
+
}
|
|
27753
|
+
const sheetId = this.env.model.getters.getActiveSheetId();
|
|
27754
|
+
const zone = this.env.model.getters.getSelectedZone();
|
|
27755
|
+
const rect = this.env.model.getters.getVisibleRect(zone);
|
|
27756
|
+
if (!deepEquals(rect, this.rect) ||
|
|
27757
|
+
sheetId !== this.env.model.getters.getCurrentEditedCell().sheetId) {
|
|
27758
|
+
this.isCellReferenceVisible = true;
|
|
27759
|
+
}
|
|
27682
27760
|
}
|
|
27683
27761
|
}
|
|
27684
27762
|
|
|
@@ -28778,7 +28856,6 @@ class GridOverlay extends owl.Component {
|
|
|
28778
28856
|
this.props.onCellDoubleClicked(col, row);
|
|
28779
28857
|
}
|
|
28780
28858
|
onContextMenu(ev) {
|
|
28781
|
-
ev.preventDefault();
|
|
28782
28859
|
const [col, row] = this.getCartesianCoordinates(ev);
|
|
28783
28860
|
this.props.onCellRightClicked(col, row, { x: ev.clientX, y: ev.clientY });
|
|
28784
28861
|
}
|
|
@@ -29843,7 +29920,6 @@ class Grid extends owl.Component {
|
|
|
29843
29920
|
HEADER_WIDTH = HEADER_WIDTH;
|
|
29844
29921
|
menuState;
|
|
29845
29922
|
gridRef;
|
|
29846
|
-
hiddenInput;
|
|
29847
29923
|
onMouseWheel;
|
|
29848
29924
|
canvasPosition;
|
|
29849
29925
|
hoveredCell;
|
|
@@ -29854,15 +29930,14 @@ class Grid extends owl.Component {
|
|
|
29854
29930
|
menuItems: [],
|
|
29855
29931
|
});
|
|
29856
29932
|
this.gridRef = owl.useRef("grid");
|
|
29857
|
-
this.hiddenInput = owl.useRef("hiddenInput");
|
|
29858
29933
|
this.canvasPosition = useAbsoluteBoundingRect(this.gridRef);
|
|
29859
29934
|
this.hoveredCell = owl.useState({ col: undefined, row: undefined });
|
|
29860
29935
|
owl.useChildSubEnv({ getPopoverContainerRect: () => this.getGridRect() });
|
|
29861
29936
|
owl.useExternalListener(document.body, "cut", this.copy.bind(this, true));
|
|
29862
29937
|
owl.useExternalListener(document.body, "copy", this.copy.bind(this, false));
|
|
29863
29938
|
owl.useExternalListener(document.body, "paste", this.paste);
|
|
29864
|
-
owl.onMounted(() => this.
|
|
29865
|
-
this.props.exposeFocus(() => this.
|
|
29939
|
+
owl.onMounted(() => this.focusDefaultElement());
|
|
29940
|
+
this.props.exposeFocus(() => this.focusDefaultElement());
|
|
29866
29941
|
useGridDrawing("canvas", this.env.model, () => this.env.model.getters.getSheetViewDimensionWithHeaders());
|
|
29867
29942
|
this.onMouseWheel = useWheelHandler((deltaX, deltaY) => {
|
|
29868
29943
|
this.moveCanvas(deltaX, deltaY);
|
|
@@ -29886,7 +29961,7 @@ class Grid extends owl.Component {
|
|
|
29886
29961
|
if (this.env.model.getters.hasOpenedPopover()) {
|
|
29887
29962
|
this.closeOpenedPopover();
|
|
29888
29963
|
}
|
|
29889
|
-
this.
|
|
29964
|
+
this.focusDefaultElement();
|
|
29890
29965
|
}
|
|
29891
29966
|
// this map will handle most of the actions that should happen on key down. The arrow keys are managed in the key
|
|
29892
29967
|
// down itself
|
|
@@ -30065,10 +30140,10 @@ class Grid extends owl.Component {
|
|
|
30065
30140
|
"Alt+Shift+ArrowUp": () => this.processHeaderGroupingKey("up"),
|
|
30066
30141
|
"Alt+Shift+ArrowDown": () => this.processHeaderGroupingKey("down"),
|
|
30067
30142
|
};
|
|
30068
|
-
|
|
30143
|
+
focusDefaultElement() {
|
|
30069
30144
|
if (!this.env.model.getters.getSelectedFigureId() &&
|
|
30070
30145
|
this.env.model.getters.getEditionMode() === "inactive") {
|
|
30071
|
-
this.
|
|
30146
|
+
this.env.focusableElement.focus();
|
|
30072
30147
|
}
|
|
30073
30148
|
}
|
|
30074
30149
|
get gridEl() {
|
|
@@ -30204,19 +30279,6 @@ class Grid extends owl.Component {
|
|
|
30204
30279
|
return;
|
|
30205
30280
|
}
|
|
30206
30281
|
}
|
|
30207
|
-
onInput(ev) {
|
|
30208
|
-
// the user meant to paste in the sheet, not open the composer with the pasted content
|
|
30209
|
-
if (!ev.isComposing && ev.inputType === "insertFromPaste") {
|
|
30210
|
-
return;
|
|
30211
|
-
}
|
|
30212
|
-
if (ev.data) {
|
|
30213
|
-
// if the user types a character on the grid, it means he wants to start composing the selected cell with that
|
|
30214
|
-
// character
|
|
30215
|
-
ev.preventDefault();
|
|
30216
|
-
ev.stopPropagation();
|
|
30217
|
-
this.props.onGridComposerCellFocused(ev.data);
|
|
30218
|
-
}
|
|
30219
|
-
}
|
|
30220
30282
|
// ---------------------------------------------------------------------------
|
|
30221
30283
|
// Context Menu
|
|
30222
30284
|
// ---------------------------------------------------------------------------
|
|
@@ -30331,7 +30393,7 @@ class Grid extends owl.Component {
|
|
|
30331
30393
|
}
|
|
30332
30394
|
closeMenu() {
|
|
30333
30395
|
this.menuState.isOpen = false;
|
|
30334
|
-
this.
|
|
30396
|
+
this.focusDefaultElement();
|
|
30335
30397
|
}
|
|
30336
30398
|
processHeaderGroupingKey(direction) {
|
|
30337
30399
|
if (this.env.model.getters.getSelectedZones().length !== 1) {
|
|
@@ -34248,6 +34310,31 @@ const MIGRATIONS = [
|
|
|
34248
34310
|
return data;
|
|
34249
34311
|
},
|
|
34250
34312
|
},
|
|
34313
|
+
{
|
|
34314
|
+
description: "Fix datafilter duplication",
|
|
34315
|
+
from: 12,
|
|
34316
|
+
to: 12.5,
|
|
34317
|
+
applyMigration(data) {
|
|
34318
|
+
for (let sheet of data.sheets || []) {
|
|
34319
|
+
let knownDataFilterZones = [];
|
|
34320
|
+
for (let filterTable of sheet.filterTables || []) {
|
|
34321
|
+
const zone = toZone(filterTable.range);
|
|
34322
|
+
// See commit message for the details
|
|
34323
|
+
const intersectZoneIndex = knownDataFilterZones.findIndex((knownZone) => overlap(knownZone, zone));
|
|
34324
|
+
if (intersectZoneIndex !== -1) {
|
|
34325
|
+
knownDataFilterZones[intersectZoneIndex] = zone;
|
|
34326
|
+
}
|
|
34327
|
+
else {
|
|
34328
|
+
knownDataFilterZones.push(zone);
|
|
34329
|
+
}
|
|
34330
|
+
}
|
|
34331
|
+
sheet.filterTables = knownDataFilterZones.map((zone) => ({
|
|
34332
|
+
range: zoneToXc(zone),
|
|
34333
|
+
}));
|
|
34334
|
+
}
|
|
34335
|
+
return data;
|
|
34336
|
+
},
|
|
34337
|
+
},
|
|
34251
34338
|
{
|
|
34252
34339
|
description: "Change Border description structure",
|
|
34253
34340
|
from: 12,
|
|
@@ -35087,22 +35174,7 @@ class BordersPlugin extends CorePlugin {
|
|
|
35087
35174
|
}
|
|
35088
35175
|
}
|
|
35089
35176
|
export(data) {
|
|
35090
|
-
// Borders
|
|
35091
|
-
let borderId = 0;
|
|
35092
35177
|
const borders = {};
|
|
35093
|
-
/**
|
|
35094
|
-
* Get the id of the given border. If the border does not exist, it creates
|
|
35095
|
-
* one.
|
|
35096
|
-
*/
|
|
35097
|
-
function getBorderId(border) {
|
|
35098
|
-
for (let [key, value] of Object.entries(borders)) {
|
|
35099
|
-
if (deepEquals(value, border)) {
|
|
35100
|
-
return parseInt(key, 10);
|
|
35101
|
-
}
|
|
35102
|
-
}
|
|
35103
|
-
borders[++borderId] = border;
|
|
35104
|
-
return borderId;
|
|
35105
|
-
}
|
|
35106
35178
|
for (let sheet of data.sheets) {
|
|
35107
35179
|
for (let col = 0; col < sheet.colNumber; col++) {
|
|
35108
35180
|
for (let row = 0; row < sheet.rowNumber; row++) {
|
|
@@ -35110,7 +35182,7 @@ class BordersPlugin extends CorePlugin {
|
|
|
35110
35182
|
if (border) {
|
|
35111
35183
|
const xc = toXC(col, row);
|
|
35112
35184
|
const cell = sheet.cells[xc];
|
|
35113
|
-
const borderId =
|
|
35185
|
+
const borderId = getItemId(border, borders);
|
|
35114
35186
|
if (cell) {
|
|
35115
35187
|
cell.border = borderId;
|
|
35116
35188
|
}
|
|
@@ -35899,9 +35971,9 @@ class CellPlugin extends CorePlugin {
|
|
|
35899
35971
|
allowDispatch(cmd) {
|
|
35900
35972
|
switch (cmd.type) {
|
|
35901
35973
|
case "UPDATE_CELL":
|
|
35902
|
-
return this.
|
|
35974
|
+
return this.checkValidations(cmd, this.checkCellOutOfSheet, this.checkUselessUpdateCell);
|
|
35903
35975
|
case "CLEAR_CELL":
|
|
35904
|
-
return this.checkValidations(cmd, this.
|
|
35976
|
+
return this.checkValidations(cmd, this.checkCellOutOfSheet, this.checkUselessClearCell);
|
|
35905
35977
|
default:
|
|
35906
35978
|
return "Success" /* CommandResult.Success */;
|
|
35907
35979
|
}
|
|
@@ -36034,9 +36106,6 @@ class CellPlugin extends CorePlugin {
|
|
|
36034
36106
|
format: cell.format ? getItemId(cell.format, formats) : undefined,
|
|
36035
36107
|
content: cell.content || undefined,
|
|
36036
36108
|
};
|
|
36037
|
-
if (cell instanceof FormulaCellWithDependencies) {
|
|
36038
|
-
cells[xc].content = cell.contentWithFixedReferences || undefined;
|
|
36039
|
-
}
|
|
36040
36109
|
}
|
|
36041
36110
|
_sheet.cells = cells;
|
|
36042
36111
|
}
|
|
@@ -36054,8 +36123,8 @@ class CellPlugin extends CorePlugin {
|
|
|
36054
36123
|
}
|
|
36055
36124
|
removeDefaultStyleValues(style) {
|
|
36056
36125
|
const cleanedStyle = { ...style };
|
|
36057
|
-
for (const
|
|
36058
|
-
if (cleanedStyle[property] ===
|
|
36126
|
+
for (const property in DEFAULT_STYLE) {
|
|
36127
|
+
if (cleanedStyle[property] === DEFAULT_STYLE[property]) {
|
|
36059
36128
|
delete cleanedStyle[property];
|
|
36060
36129
|
}
|
|
36061
36130
|
}
|
|
@@ -36211,10 +36280,7 @@ class CellPlugin extends CorePlugin {
|
|
|
36211
36280
|
else {
|
|
36212
36281
|
style = before ? before.style : undefined;
|
|
36213
36282
|
}
|
|
36214
|
-
const
|
|
36215
|
-
let format = ("format" in after ? after.format : before && before.format) ||
|
|
36216
|
-
detectDateFormat(afterContent, locale) ||
|
|
36217
|
-
detectNumberFormat(afterContent);
|
|
36283
|
+
const format = "format" in after ? after.format : before && before.format;
|
|
36218
36284
|
/* Read the following IF as:
|
|
36219
36285
|
* we need to remove the cell if it is completely empty, but we can know if it completely empty if:
|
|
36220
36286
|
* - the command says the new content is empty and has no border/format/style
|
|
@@ -36255,6 +36321,7 @@ class CellPlugin extends CorePlugin {
|
|
|
36255
36321
|
}
|
|
36256
36322
|
createLiteralCell(id, content, format, style) {
|
|
36257
36323
|
const locale = this.getters.getLocale();
|
|
36324
|
+
format = format || detectDateFormat(content, locale) || detectNumberFormat(content);
|
|
36258
36325
|
if (format !== PLAIN_TEXT_FORMAT && !isEvaluationError(content)) {
|
|
36259
36326
|
content = toString(parseLiteral(content, locale));
|
|
36260
36327
|
}
|
|
@@ -36288,8 +36355,11 @@ class CellPlugin extends CorePlugin {
|
|
|
36288
36355
|
* being a computed property to rebuild the dependencies XC.
|
|
36289
36356
|
*/
|
|
36290
36357
|
createFormulaCellWithDependencies(id, compiledFormula, format, style, sheetId) {
|
|
36291
|
-
const dependencies =
|
|
36292
|
-
|
|
36358
|
+
const dependencies = [];
|
|
36359
|
+
for (const xc of compiledFormula.dependencies) {
|
|
36360
|
+
dependencies.push(this.getters.getRangeFromSheetXC(sheetId, xc));
|
|
36361
|
+
}
|
|
36362
|
+
return new FormulaCellWithDependencies(id, compiledFormula, format, style, dependencies, sheetId, this.getters.getRangeString);
|
|
36293
36363
|
}
|
|
36294
36364
|
createErrorFormula(id, content, format, style, error) {
|
|
36295
36365
|
return {
|
|
@@ -36324,6 +36394,18 @@ class CellPlugin extends CorePlugin {
|
|
|
36324
36394
|
}
|
|
36325
36395
|
return "Success" /* CommandResult.Success */;
|
|
36326
36396
|
}
|
|
36397
|
+
checkUselessUpdateCell(cmd) {
|
|
36398
|
+
const cell = this.getters.getCell(cmd);
|
|
36399
|
+
const hasContent = "content" in cmd || "formula" in cmd;
|
|
36400
|
+
const hasStyle = "style" in cmd;
|
|
36401
|
+
const hasFormat = "format" in cmd;
|
|
36402
|
+
if ((!hasContent || cell?.content === cmd.content) &&
|
|
36403
|
+
(!hasStyle || deepEquals(cell?.style, cmd.style)) &&
|
|
36404
|
+
(!hasFormat || cell?.format === cmd.format)) {
|
|
36405
|
+
return "NoChanges" /* CommandResult.NoChanges */;
|
|
36406
|
+
}
|
|
36407
|
+
return "Success" /* CommandResult.Success */;
|
|
36408
|
+
}
|
|
36327
36409
|
}
|
|
36328
36410
|
class FormulaCellWithDependencies {
|
|
36329
36411
|
id;
|
|
@@ -36343,7 +36425,7 @@ class FormulaCellWithDependencies {
|
|
|
36343
36425
|
const tokens = compiledFormula.tokens.map((token) => {
|
|
36344
36426
|
if (token.type === "REFERENCE") {
|
|
36345
36427
|
const index = rangeIndex++;
|
|
36346
|
-
return new RangeReferenceToken(
|
|
36428
|
+
return new RangeReferenceToken(dependencies, index, this.sheetId, this.getRangeString);
|
|
36347
36429
|
}
|
|
36348
36430
|
return token;
|
|
36349
36431
|
});
|
|
@@ -36370,13 +36452,20 @@ class FormulaCellWithDependencies {
|
|
|
36370
36452
|
}
|
|
36371
36453
|
}
|
|
36372
36454
|
class RangeReferenceToken {
|
|
36373
|
-
|
|
36455
|
+
ranges;
|
|
36456
|
+
rangeIndex;
|
|
36457
|
+
sheetId;
|
|
36458
|
+
getRangeString;
|
|
36374
36459
|
type = "REFERENCE";
|
|
36375
|
-
constructor(
|
|
36376
|
-
this.
|
|
36460
|
+
constructor(ranges, rangeIndex, sheetId, getRangeString) {
|
|
36461
|
+
this.ranges = ranges;
|
|
36462
|
+
this.rangeIndex = rangeIndex;
|
|
36463
|
+
this.sheetId = sheetId;
|
|
36464
|
+
this.getRangeString = getRangeString;
|
|
36377
36465
|
}
|
|
36378
36466
|
get value() {
|
|
36379
|
-
|
|
36467
|
+
const range = this.ranges[this.rangeIndex];
|
|
36468
|
+
return this.getRangeString(range, this.sheetId);
|
|
36380
36469
|
}
|
|
36381
36470
|
}
|
|
36382
36471
|
|
|
@@ -41101,7 +41190,15 @@ class SpreadsheetRTree {
|
|
|
41101
41190
|
if (!this.rTrees[sheetId]) {
|
|
41102
41191
|
return;
|
|
41103
41192
|
}
|
|
41104
|
-
this.rTrees[sheetId].remove(item,
|
|
41193
|
+
this.rTrees[sheetId].remove(item, this.rtreeItemComparer);
|
|
41194
|
+
}
|
|
41195
|
+
rtreeItemComparer(left, right) {
|
|
41196
|
+
return (left.data == right.data &&
|
|
41197
|
+
left.boundingBox.sheetId === right.boundingBox.sheetId &&
|
|
41198
|
+
left.boundingBox?.zone.left === right.boundingBox.zone.left &&
|
|
41199
|
+
left.boundingBox?.zone.top === right.boundingBox.zone.top &&
|
|
41200
|
+
left.boundingBox?.zone.right === right.boundingBox.zone.right &&
|
|
41201
|
+
left.boundingBox?.zone.bottom === right.boundingBox.zone.bottom);
|
|
41105
41202
|
}
|
|
41106
41203
|
}
|
|
41107
41204
|
/**
|
|
@@ -41179,7 +41276,7 @@ class FormulaDependencyGraph {
|
|
|
41179
41276
|
const queue = Array.from(ranges).reverse();
|
|
41180
41277
|
while (queue.length > 0) {
|
|
41181
41278
|
const range = queue.pop();
|
|
41182
|
-
visited.
|
|
41279
|
+
visited.addMany(this.encoder.encodeBoundingBox(range));
|
|
41183
41280
|
const impactedPositionIds = this.rTree.search(range).map((dep) => dep.data);
|
|
41184
41281
|
for (const positionId of impactedPositionIds) {
|
|
41185
41282
|
if (!visited.has(positionId)) {
|
|
@@ -41187,7 +41284,7 @@ class FormulaDependencyGraph {
|
|
|
41187
41284
|
}
|
|
41188
41285
|
}
|
|
41189
41286
|
}
|
|
41190
|
-
visited.
|
|
41287
|
+
visited.deleteMany(ranges.flatMap((r) => this.encoder.encodeBoundingBox(r)));
|
|
41191
41288
|
return visited;
|
|
41192
41289
|
}
|
|
41193
41290
|
}
|
|
@@ -41327,9 +41424,9 @@ class Evaluator {
|
|
|
41327
41424
|
const cells = positions.map((p) => this.encoder.encode(p));
|
|
41328
41425
|
const cellsToCompute = new JetSet(cells);
|
|
41329
41426
|
const arrayFormulasPositionIds = this.getArrayFormulasImpactedByChangesOf(cells);
|
|
41330
|
-
cellsToCompute.
|
|
41331
|
-
cellsToCompute.
|
|
41332
|
-
cellsToCompute.
|
|
41427
|
+
cellsToCompute.addMany(this.getCellsDependingOn(cells));
|
|
41428
|
+
cellsToCompute.addMany(arrayFormulasPositionIds);
|
|
41429
|
+
cellsToCompute.addMany(this.getCellsDependingOn(arrayFormulasPositionIds));
|
|
41333
41430
|
this.evaluate(cellsToCompute);
|
|
41334
41431
|
}
|
|
41335
41432
|
getArrayFormulasImpactedByChangesOf(positionIds) {
|
|
@@ -41343,7 +41440,7 @@ class Evaluator {
|
|
|
41343
41440
|
}
|
|
41344
41441
|
if (!content) {
|
|
41345
41442
|
// The previous content could have blocked some array formulas
|
|
41346
|
-
impactedPositionIds.
|
|
41443
|
+
impactedPositionIds.addMany(this.getArrayFormulasBlockedByOrSpreadingOn(positionId));
|
|
41347
41444
|
}
|
|
41348
41445
|
}
|
|
41349
41446
|
return impactedPositionIds;
|
|
@@ -41395,7 +41492,7 @@ class Evaluator {
|
|
|
41395
41492
|
}
|
|
41396
41493
|
const arrayFormulas = this.spreadingRelations.getFormulaPositionsSpreadingOn(positionId);
|
|
41397
41494
|
const cells = new JetSet(arrayFormulas);
|
|
41398
|
-
cells.
|
|
41495
|
+
cells.addMany(this.getCellsDependingOn(arrayFormulas));
|
|
41399
41496
|
return cells;
|
|
41400
41497
|
}
|
|
41401
41498
|
nextPositionsToUpdate = new JetSet();
|
|
@@ -41533,7 +41630,7 @@ class Evaluator {
|
|
|
41533
41630
|
this.setEvaluatedCell(positionId, evaluatedCell);
|
|
41534
41631
|
// check if formula dependencies present in the spread zone
|
|
41535
41632
|
// if so, they need to be recomputed
|
|
41536
|
-
this.nextPositionsToUpdate.
|
|
41633
|
+
this.nextPositionsToUpdate.addMany(this.getCellsDependingOn([positionId]));
|
|
41537
41634
|
};
|
|
41538
41635
|
}
|
|
41539
41636
|
invalidateSpreading(positionId) {
|
|
@@ -41548,8 +41645,8 @@ class Evaluator {
|
|
|
41548
41645
|
continue;
|
|
41549
41646
|
}
|
|
41550
41647
|
this.evaluatedCells.delete(child);
|
|
41551
|
-
this.nextPositionsToUpdate.
|
|
41552
|
-
this.nextPositionsToUpdate.
|
|
41648
|
+
this.nextPositionsToUpdate.addMany(this.getCellsDependingOn([child]));
|
|
41649
|
+
this.nextPositionsToUpdate.addMany(this.getArrayFormulasBlockedByOrSpreadingOn(child));
|
|
41553
41650
|
}
|
|
41554
41651
|
this.spreadingRelations.removeNode(positionId);
|
|
41555
41652
|
}
|
|
@@ -41911,7 +42008,13 @@ class EvaluationPlugin extends UIPlugin {
|
|
|
41911
42008
|
const format = newFormat
|
|
41912
42009
|
? getItemId(newFormat, data.formats)
|
|
41913
42010
|
: exportedCellData.format;
|
|
41914
|
-
|
|
42011
|
+
let content;
|
|
42012
|
+
if (formulaCell instanceof FormulaCellWithDependencies) {
|
|
42013
|
+
content = formulaCell.contentWithFixedReferences;
|
|
42014
|
+
}
|
|
42015
|
+
else {
|
|
42016
|
+
content = !isExported ? newContent : exportedCellData.content;
|
|
42017
|
+
}
|
|
41915
42018
|
exportedSheetData.cells[xc] = { ...exportedCellData, value, isFormula, content, format };
|
|
41916
42019
|
}
|
|
41917
42020
|
}
|
|
@@ -45412,7 +45515,7 @@ class FindAndReplacePlugin extends UIPlugin {
|
|
|
45412
45515
|
}
|
|
45413
45516
|
finalize() {
|
|
45414
45517
|
if (this.isSearchDirty) {
|
|
45415
|
-
this.refreshSearch();
|
|
45518
|
+
this.refreshSearch(false);
|
|
45416
45519
|
this.isSearchDirty = false;
|
|
45417
45520
|
}
|
|
45418
45521
|
}
|
|
@@ -45456,10 +45559,10 @@ class FindAndReplacePlugin extends UIPlugin {
|
|
|
45456
45559
|
/**
|
|
45457
45560
|
* refresh the matches according to the current search options
|
|
45458
45561
|
*/
|
|
45459
|
-
refreshSearch() {
|
|
45562
|
+
refreshSearch(jumpToMatchSheet = true) {
|
|
45460
45563
|
this.selectedMatchIndex = null;
|
|
45461
45564
|
this.findMatches();
|
|
45462
|
-
this.selectNextCell(Direction.current);
|
|
45565
|
+
this.selectNextCell(Direction.current, jumpToMatchSheet);
|
|
45463
45566
|
}
|
|
45464
45567
|
/**
|
|
45465
45568
|
* Updates the regex based on the current searchOptions and
|
|
@@ -45541,7 +45644,7 @@ class FindAndReplacePlugin extends UIPlugin {
|
|
|
45541
45644
|
* It is also used to keep coherence between the selected searchMatch
|
|
45542
45645
|
* and selectedMatchIndex.
|
|
45543
45646
|
*/
|
|
45544
|
-
selectNextCell(indexChange) {
|
|
45647
|
+
selectNextCell(indexChange, jumpToMatchSheet = true) {
|
|
45545
45648
|
const matches = this.searchMatches;
|
|
45546
45649
|
if (!matches.length) {
|
|
45547
45650
|
this.selectedMatchIndex = null;
|
|
@@ -45567,7 +45670,7 @@ class FindAndReplacePlugin extends UIPlugin {
|
|
|
45567
45670
|
this.selectedMatchIndex = nextIndex;
|
|
45568
45671
|
const selectedMatch = matches[nextIndex];
|
|
45569
45672
|
// Switch to the sheet where the match is located
|
|
45570
|
-
if (this.getters.getActiveSheetId() !== selectedMatch.sheetId) {
|
|
45673
|
+
if (jumpToMatchSheet && this.getters.getActiveSheetId() !== selectedMatch.sheetId) {
|
|
45571
45674
|
this.dispatch("ACTIVATE_SHEET", {
|
|
45572
45675
|
sheetIdFrom: this.getters.getActiveSheetId(),
|
|
45573
45676
|
sheetIdTo: selectedMatch.sheetId,
|
|
@@ -47139,7 +47242,7 @@ class SheetUIPlugin extends UIPlugin {
|
|
|
47139
47242
|
"getCellText",
|
|
47140
47243
|
"getCellMultiLineText",
|
|
47141
47244
|
"getContiguousZone",
|
|
47142
|
-
"
|
|
47245
|
+
"isEvaluatedCellEmpty",
|
|
47143
47246
|
];
|
|
47144
47247
|
ctx = document.createElement("canvas").getContext("2d");
|
|
47145
47248
|
// ---------------------------------------------------------------------------
|
|
@@ -47267,14 +47370,23 @@ class SheetUIPlugin extends UIPlugin {
|
|
|
47267
47370
|
return zone;
|
|
47268
47371
|
}
|
|
47269
47372
|
/**
|
|
47270
|
-
*
|
|
47271
|
-
* check
|
|
47373
|
+
* Checks if a cell evaluated value is empty. If the cell is part of a merge,
|
|
47374
|
+
* the check applies to the main cell of the merge.
|
|
47272
47375
|
*/
|
|
47273
|
-
|
|
47376
|
+
isEvaluatedCellEmpty(position) {
|
|
47274
47377
|
const mainPosition = this.getters.getMainCellPosition(position);
|
|
47275
47378
|
const cell = this.getters.getEvaluatedCell(mainPosition);
|
|
47276
47379
|
return cell.type === CellValueType.empty;
|
|
47277
47380
|
}
|
|
47381
|
+
/**
|
|
47382
|
+
* Checks if a cell is empty (i.e. does not have a content or a formula does not spread over it).
|
|
47383
|
+
* If the cell is part of a merge, the check applies to the main cell of the merge.
|
|
47384
|
+
*/
|
|
47385
|
+
isCellEmpty(position) {
|
|
47386
|
+
const mainPosition = this.getters.getMainCellPosition(position);
|
|
47387
|
+
return !(this.getters.getCorrespondingFormulaCell(mainPosition) ||
|
|
47388
|
+
this.getters.getCell(mainPosition)?.content);
|
|
47389
|
+
}
|
|
47278
47390
|
getColMaxWidth(sheetId, index) {
|
|
47279
47391
|
const cellsPositions = positions(this.getters.getColsZone(sheetId, index, index));
|
|
47280
47392
|
const sizes = cellsPositions.map((position) => this.getCellWidth({ sheetId, ...position }));
|
|
@@ -51095,6 +51207,16 @@ class ImageProvider {
|
|
|
51095
51207
|
}
|
|
51096
51208
|
}
|
|
51097
51209
|
|
|
51210
|
+
class FocusableElement {
|
|
51211
|
+
focusableElement = undefined;
|
|
51212
|
+
setFocusableElement(element) {
|
|
51213
|
+
this.focusableElement = element;
|
|
51214
|
+
}
|
|
51215
|
+
focus() {
|
|
51216
|
+
this.focusableElement?.focus();
|
|
51217
|
+
}
|
|
51218
|
+
}
|
|
51219
|
+
|
|
51098
51220
|
const RIPPLE_KEY_FRAMES = [
|
|
51099
51221
|
{ transform: "scale(0)" },
|
|
51100
51222
|
{ transform: "scale(0.8)", offset: 0.33 },
|
|
@@ -53249,6 +53371,7 @@ class Spreadsheet extends owl.Component {
|
|
|
53249
53371
|
toggleSidePanel: this.toggleSidePanel.bind(this),
|
|
53250
53372
|
clipboard: this.env.clipboard || instantiateClipboard(),
|
|
53251
53373
|
startCellEdition: (content) => this.onGridComposerCellFocused(content),
|
|
53374
|
+
focusableElement: new FocusableElement(),
|
|
53252
53375
|
});
|
|
53253
53376
|
owl.useEffect(() => {
|
|
53254
53377
|
/**
|
|
@@ -53367,16 +53490,17 @@ class Spreadsheet extends owl.Component {
|
|
|
53367
53490
|
}
|
|
53368
53491
|
this.composer.topBarFocus = "contentFocus";
|
|
53369
53492
|
this.composer.gridFocusMode = "inactive";
|
|
53370
|
-
this.setComposerContent({ selection }
|
|
53493
|
+
this.setComposerContent({ selection });
|
|
53371
53494
|
}
|
|
53372
|
-
onGridComposerContentFocused() {
|
|
53495
|
+
onGridComposerContentFocused(selection) {
|
|
53373
53496
|
if (this.model.getters.isReadonly()) {
|
|
53374
53497
|
return;
|
|
53375
53498
|
}
|
|
53376
53499
|
this.composer.topBarFocus = "inactive";
|
|
53377
53500
|
this.composer.gridFocusMode = "contentFocus";
|
|
53378
|
-
this.setComposerContent({});
|
|
53501
|
+
this.setComposerContent({ selection });
|
|
53379
53502
|
}
|
|
53503
|
+
// TODO: either both are defined or none of them. change those args to an object
|
|
53380
53504
|
onGridComposerCellFocused(content, selection) {
|
|
53381
53505
|
if (this.model.getters.isReadonly()) {
|
|
53382
53506
|
return;
|
|
@@ -54804,8 +54928,8 @@ class SelectionStreamProcessorImpl {
|
|
|
54804
54928
|
let currentPosition = startPosition;
|
|
54805
54929
|
// If both the current cell and the next cell are not empty, we want to go to the end of the cluster
|
|
54806
54930
|
const nextCellPosition = this.getNextCellPosition(startPosition, dim, dir);
|
|
54807
|
-
let mode = !this.getters.
|
|
54808
|
-
!this.getters.
|
|
54931
|
+
let mode = !this.getters.isEvaluatedCellEmpty({ ...currentPosition, sheetId }) &&
|
|
54932
|
+
!this.getters.isEvaluatedCellEmpty({ ...nextCellPosition, sheetId })
|
|
54809
54933
|
? "endOfCluster"
|
|
54810
54934
|
: "nextCluster";
|
|
54811
54935
|
while (true) {
|
|
@@ -54815,7 +54939,7 @@ class SelectionStreamProcessorImpl {
|
|
|
54815
54939
|
currentPosition.row === nextCellPosition.row) {
|
|
54816
54940
|
break;
|
|
54817
54941
|
}
|
|
54818
|
-
const isNextCellEmpty = this.getters.
|
|
54942
|
+
const isNextCellEmpty = this.getters.isEvaluatedCellEmpty({ ...nextCellPosition, sheetId });
|
|
54819
54943
|
if (mode === "endOfCluster" && isNextCellEmpty) {
|
|
54820
54944
|
break;
|
|
54821
54945
|
}
|
|
@@ -57100,6 +57224,6 @@ exports.setTranslationMethod = setTranslationMethod;
|
|
|
57100
57224
|
exports.tokenize = tokenize;
|
|
57101
57225
|
|
|
57102
57226
|
|
|
57103
|
-
__info__.version = "17.1.
|
|
57104
|
-
__info__.date = "2024-02-
|
|
57105
|
-
__info__.hash = "
|
|
57227
|
+
__info__.version = "17.1.5";
|
|
57228
|
+
__info__.date = "2024-02-16T14:23:54.651Z";
|
|
57229
|
+
__info__.hash = "29d8ad6";
|