@odoo/o-spreadsheet 17.0.10 → 17.0.13
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 +348 -206
- package/dist/o-spreadsheet.d.ts +31 -15
- package/dist/o-spreadsheet.esm.js +348 -206
- package/dist/o-spreadsheet.iife.js +348 -206
- package/dist/o-spreadsheet.iife.min.js +261 -260
- 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.0.
|
|
6
|
-
* @date 2024-02-
|
|
7
|
-
* @hash
|
|
5
|
+
* @version 17.0.13
|
|
6
|
+
* @date 2024-02-28T12:28:44.914Z
|
|
7
|
+
* @hash ed67be7
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
import { Component, useRef, onMounted, useEffect, onWillPatch, useState, onWillUpdateProps, onPatched, useComponent, useExternalListener, onWillUnmount, onWillStart, xml, useChildSubEnv, useSubEnv, markRaw } from '@odoo/owl';
|
|
@@ -550,8 +550,8 @@ function isObjectEmptyRecursive(argument) {
|
|
|
550
550
|
* If the given item does not exist in the dictionary, it creates one with a new id.
|
|
551
551
|
*/
|
|
552
552
|
function getItemId(item, itemsDic) {
|
|
553
|
-
for (
|
|
554
|
-
if (deepEquals(
|
|
553
|
+
for (const key in itemsDic) {
|
|
554
|
+
if (deepEquals(itemsDic[key], item)) {
|
|
555
555
|
return parseInt(key, 10);
|
|
556
556
|
}
|
|
557
557
|
}
|
|
@@ -650,11 +650,13 @@ function deepEquals(o1, o2) {
|
|
|
650
650
|
if (typeof o1 !== "object")
|
|
651
651
|
return o1 === o2;
|
|
652
652
|
// Objects can have different keys if the values are undefined
|
|
653
|
-
const
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
653
|
+
for (const key in o2) {
|
|
654
|
+
if (!(key in o1) && o2[key] !== undefined) {
|
|
655
|
+
return false;
|
|
656
|
+
}
|
|
657
|
+
}
|
|
658
|
+
for (const key in o1) {
|
|
659
|
+
if (typeof o1[key] !== typeof o2[key])
|
|
658
660
|
return false;
|
|
659
661
|
if (typeof o1[key] === "object") {
|
|
660
662
|
if (!deepEquals(o1[key], o2[key]))
|
|
@@ -724,18 +726,18 @@ function isConsecutive(iterable) {
|
|
|
724
726
|
return true;
|
|
725
727
|
}
|
|
726
728
|
class JetSet extends Set {
|
|
727
|
-
|
|
729
|
+
addMany(iterable) {
|
|
728
730
|
for (const element of iterable) {
|
|
729
731
|
super.add(element);
|
|
730
732
|
}
|
|
731
733
|
return this;
|
|
732
734
|
}
|
|
733
|
-
|
|
734
|
-
let
|
|
735
|
+
deleteMany(iterable) {
|
|
736
|
+
let wasDeleted = false;
|
|
735
737
|
for (const element of iterable) {
|
|
736
|
-
|
|
738
|
+
wasDeleted ||= super.delete(element);
|
|
737
739
|
}
|
|
738
|
-
return
|
|
740
|
+
return wasDeleted;
|
|
739
741
|
}
|
|
740
742
|
}
|
|
741
743
|
/**
|
|
@@ -1994,7 +1996,7 @@ class DispatchResult {
|
|
|
1994
1996
|
* Static helper which returns a successful DispatchResult
|
|
1995
1997
|
*/
|
|
1996
1998
|
static get Success() {
|
|
1997
|
-
return
|
|
1999
|
+
return SUCCESS;
|
|
1998
2000
|
}
|
|
1999
2001
|
get isSuccessful() {
|
|
2000
2002
|
return this.reasons.length === 0;
|
|
@@ -2007,6 +2009,7 @@ class DispatchResult {
|
|
|
2007
2009
|
return this.reasons.includes(reason);
|
|
2008
2010
|
}
|
|
2009
2011
|
}
|
|
2012
|
+
const SUCCESS = new DispatchResult();
|
|
2010
2013
|
var CommandResult;
|
|
2011
2014
|
(function (CommandResult) {
|
|
2012
2015
|
CommandResult["Success"] = "Success";
|
|
@@ -2888,6 +2891,9 @@ function parseFormat(formatString) {
|
|
|
2888
2891
|
function formatValue(value, { format, locale }) {
|
|
2889
2892
|
switch (typeof value) {
|
|
2890
2893
|
case "string":
|
|
2894
|
+
if (value.includes('\\"')) {
|
|
2895
|
+
return value.replace(/\\"/g, '"');
|
|
2896
|
+
}
|
|
2891
2897
|
return value;
|
|
2892
2898
|
case "boolean":
|
|
2893
2899
|
return value ? "TRUE" : "FALSE";
|
|
@@ -6276,11 +6282,14 @@ function cellTextStyleToCss(style) {
|
|
|
6276
6282
|
* Transform CSS properties into a CSS string.
|
|
6277
6283
|
*/
|
|
6278
6284
|
function cssPropertiesToCss(attributes) {
|
|
6279
|
-
|
|
6280
|
-
|
|
6281
|
-
|
|
6282
|
-
|
|
6283
|
-
|
|
6285
|
+
let styleStr = "";
|
|
6286
|
+
for (const attName in attributes) {
|
|
6287
|
+
if (!attributes[attName]) {
|
|
6288
|
+
continue;
|
|
6289
|
+
}
|
|
6290
|
+
styleStr += `${attName}:${attributes[attName]}; `;
|
|
6291
|
+
}
|
|
6292
|
+
return styleStr;
|
|
6284
6293
|
}
|
|
6285
6294
|
function getElementMargins(el) {
|
|
6286
6295
|
const style = window.getComputedStyle(el);
|
|
@@ -7379,7 +7388,7 @@ function tokenize(str, locale = DEFAULT_LOCALE) {
|
|
|
7379
7388
|
while (!chars.isOver()) {
|
|
7380
7389
|
let token = tokenizeSpace(chars) ||
|
|
7381
7390
|
tokenizeArgsSeparator(chars, locale) ||
|
|
7382
|
-
|
|
7391
|
+
tokenizeParenthesis(chars) ||
|
|
7383
7392
|
tokenizeOperator(chars) ||
|
|
7384
7393
|
tokenizeString(chars) ||
|
|
7385
7394
|
tokenizeDebugger(chars) ||
|
|
@@ -7400,15 +7409,14 @@ function tokenizeDebugger(chars) {
|
|
|
7400
7409
|
}
|
|
7401
7410
|
return null;
|
|
7402
7411
|
}
|
|
7403
|
-
const
|
|
7404
|
-
"(": "LEFT_PAREN",
|
|
7405
|
-
")": "RIGHT_PAREN",
|
|
7412
|
+
const parenthesis = {
|
|
7413
|
+
"(": { type: "LEFT_PAREN", value: "(" },
|
|
7414
|
+
")": { type: "RIGHT_PAREN", value: ")" },
|
|
7406
7415
|
};
|
|
7407
|
-
function
|
|
7408
|
-
if (chars.current
|
|
7416
|
+
function tokenizeParenthesis(chars) {
|
|
7417
|
+
if (chars.current === "(" || chars.current === ")") {
|
|
7409
7418
|
const value = chars.shift();
|
|
7410
|
-
|
|
7411
|
-
return { type, value };
|
|
7419
|
+
return parenthesis[value];
|
|
7412
7420
|
}
|
|
7413
7421
|
return null;
|
|
7414
7422
|
}
|
|
@@ -7429,7 +7437,12 @@ function tokenizeOperator(chars) {
|
|
|
7429
7437
|
}
|
|
7430
7438
|
return null;
|
|
7431
7439
|
}
|
|
7440
|
+
const FIRST_POSSIBLE_NUMBER_CHARS = new Set("0123456789");
|
|
7432
7441
|
function tokenizeNumber(chars, locale) {
|
|
7442
|
+
if (!FIRST_POSSIBLE_NUMBER_CHARS.has(chars.current) &&
|
|
7443
|
+
chars.current !== locale.decimalSeparator) {
|
|
7444
|
+
return null;
|
|
7445
|
+
}
|
|
7433
7446
|
const match = chars.remaining().match(getFormulaNumberRegex(locale.decimalSeparator));
|
|
7434
7447
|
if (match) {
|
|
7435
7448
|
chars.advanceBy(match[0].length);
|
|
@@ -7454,7 +7467,7 @@ function tokenizeString(chars) {
|
|
|
7454
7467
|
}
|
|
7455
7468
|
return null;
|
|
7456
7469
|
}
|
|
7457
|
-
const separatorRegexp =
|
|
7470
|
+
const separatorRegexp = /^[\w\.!\$]+/;
|
|
7458
7471
|
/**
|
|
7459
7472
|
* A "Symbol" is just basically any word-like element that can appear in a
|
|
7460
7473
|
* formula, which is not a string. So:
|
|
@@ -7494,8 +7507,11 @@ function tokenizeSymbol(chars) {
|
|
|
7494
7507
|
};
|
|
7495
7508
|
}
|
|
7496
7509
|
}
|
|
7497
|
-
|
|
7498
|
-
|
|
7510
|
+
const match = chars.remaining().match(separatorRegexp);
|
|
7511
|
+
if (match) {
|
|
7512
|
+
const value = match[0];
|
|
7513
|
+
result += value;
|
|
7514
|
+
chars.advanceBy(value.length);
|
|
7499
7515
|
}
|
|
7500
7516
|
if (result.length) {
|
|
7501
7517
|
const value = result;
|
|
@@ -7557,7 +7573,10 @@ class TokenizingChars {
|
|
|
7557
7573
|
return this.text.substring(this.currentIndex);
|
|
7558
7574
|
}
|
|
7559
7575
|
currentStartsWith(str) {
|
|
7560
|
-
|
|
7576
|
+
if (this.current !== str[0]) {
|
|
7577
|
+
return false;
|
|
7578
|
+
}
|
|
7579
|
+
for (let j = 1; j < str.length; j++) {
|
|
7561
7580
|
if (this.text[this.currentIndex + j] !== str[j]) {
|
|
7562
7581
|
return false;
|
|
7563
7582
|
}
|
|
@@ -7584,6 +7603,9 @@ function isValidLocale(locale) {
|
|
|
7584
7603
|
if (locale.formulaArgSeparator === locale.decimalSeparator) {
|
|
7585
7604
|
return false;
|
|
7586
7605
|
}
|
|
7606
|
+
if (locale.thousandsSeparator === locale.decimalSeparator) {
|
|
7607
|
+
return false;
|
|
7608
|
+
}
|
|
7587
7609
|
try {
|
|
7588
7610
|
formatValue(1, { locale, format: "#,##0.00" });
|
|
7589
7611
|
formatValue(1, { locale, format: locale.dateFormat });
|
|
@@ -7674,7 +7696,7 @@ function canonicalizeNumberLiteral(content, locale) {
|
|
|
7674
7696
|
if (locale.decimalSeparator === "." || !isNumber(content, locale)) {
|
|
7675
7697
|
return content;
|
|
7676
7698
|
}
|
|
7677
|
-
return content.replace(locale.decimalSeparator, ".");
|
|
7699
|
+
return content.replace(locale.thousandsSeparator, "").replace(locale.decimalSeparator, ".");
|
|
7678
7700
|
}
|
|
7679
7701
|
/**
|
|
7680
7702
|
* Change a content string from the given locale to its canonical form (en_US locale). Also convert date string.
|
|
@@ -10746,6 +10768,12 @@ const INSERT_LINK = (env) => {
|
|
|
10746
10768
|
let { col, row } = env.model.getters.getActivePosition();
|
|
10747
10769
|
env.model.dispatch("OPEN_CELL_POPOVER", { col, row, popoverType: "LinkEditor" });
|
|
10748
10770
|
};
|
|
10771
|
+
const INSERT_LINK_NAME = (env) => {
|
|
10772
|
+
const sheetId = env.model.getters.getActiveSheetId();
|
|
10773
|
+
const { col, row } = env.model.getters.getActivePosition();
|
|
10774
|
+
const cell = env.model.getters.getEvaluatedCell({ sheetId, col, row });
|
|
10775
|
+
return cell && cell.link ? _t("Edit link") : _t("Insert link");
|
|
10776
|
+
};
|
|
10749
10777
|
//------------------------------------------------------------------------------
|
|
10750
10778
|
// Filters action
|
|
10751
10779
|
//------------------------------------------------------------------------------
|
|
@@ -12898,6 +12926,18 @@ const TRUNC = {
|
|
|
12898
12926
|
},
|
|
12899
12927
|
isExported: true,
|
|
12900
12928
|
};
|
|
12929
|
+
// -----------------------------------------------------------------------------
|
|
12930
|
+
// INT
|
|
12931
|
+
// -----------------------------------------------------------------------------
|
|
12932
|
+
const INT = {
|
|
12933
|
+
description: _t("Rounds a number down to the nearest integer that is less than or equal to it."),
|
|
12934
|
+
args: [arg("value (number)", _t("The number to round down to the nearest integer."))],
|
|
12935
|
+
returns: ["NUMBER"],
|
|
12936
|
+
compute: function (value) {
|
|
12937
|
+
return Math.floor(toNumber(value, this.locale));
|
|
12938
|
+
},
|
|
12939
|
+
isExported: true,
|
|
12940
|
+
};
|
|
12901
12941
|
|
|
12902
12942
|
var math = /*#__PURE__*/Object.freeze({
|
|
12903
12943
|
__proto__: null,
|
|
@@ -12931,6 +12971,7 @@ var math = /*#__PURE__*/Object.freeze({
|
|
|
12931
12971
|
FLOOR: FLOOR,
|
|
12932
12972
|
FLOOR_MATH: FLOOR_MATH,
|
|
12933
12973
|
FLOOR_PRECISE: FLOOR_PRECISE,
|
|
12974
|
+
INT: INT,
|
|
12934
12975
|
ISEVEN: ISEVEN,
|
|
12935
12976
|
ISODD: ISODD,
|
|
12936
12977
|
ISO_CEILING: ISO_CEILING,
|
|
@@ -19777,7 +19818,7 @@ cellMenuRegistry
|
|
|
19777
19818
|
})
|
|
19778
19819
|
.add("insert_link", {
|
|
19779
19820
|
...insertLink,
|
|
19780
|
-
name:
|
|
19821
|
+
name: INSERT_LINK_NAME,
|
|
19781
19822
|
sequence: 150,
|
|
19782
19823
|
separator: true,
|
|
19783
19824
|
});
|
|
@@ -21369,7 +21410,7 @@ class SelectionInput extends Component {
|
|
|
21369
21410
|
? existingSelectionRanges
|
|
21370
21411
|
: this.props.ranges().map((xc, id) => ({
|
|
21371
21412
|
xc,
|
|
21372
|
-
id,
|
|
21413
|
+
id: id + 1,
|
|
21373
21414
|
isFocused: false,
|
|
21374
21415
|
}));
|
|
21375
21416
|
return ranges.map((range) => ({
|
|
@@ -26030,6 +26071,17 @@ css /* scss */ `
|
|
|
26030
26071
|
`;
|
|
26031
26072
|
class TextValueProvider extends Component {
|
|
26032
26073
|
static template = "o-spreadsheet-TextValueProvider";
|
|
26074
|
+
autoCompleteListRef = useRef("autoCompleteList");
|
|
26075
|
+
setup() {
|
|
26076
|
+
useEffect(() => {
|
|
26077
|
+
const selectedIndex = this.props.selectedIndex;
|
|
26078
|
+
if (selectedIndex === undefined) {
|
|
26079
|
+
return;
|
|
26080
|
+
}
|
|
26081
|
+
const selectedElement = this.autoCompleteListRef.el?.children[selectedIndex];
|
|
26082
|
+
selectedElement?.scrollIntoView?.({ block: "nearest" });
|
|
26083
|
+
}, () => [this.props.selectedIndex, this.autoCompleteListRef.el]);
|
|
26084
|
+
}
|
|
26033
26085
|
}
|
|
26034
26086
|
TextValueProvider.props = {
|
|
26035
26087
|
values: Array,
|
|
@@ -26481,6 +26533,7 @@ css /* scss */ `
|
|
|
26481
26533
|
position: absolute;
|
|
26482
26534
|
margin: 1px 4px;
|
|
26483
26535
|
pointer-events: none;
|
|
26536
|
+
overflow: auto;
|
|
26484
26537
|
|
|
26485
26538
|
.o-semi-bold {
|
|
26486
26539
|
/** FIXME: to remove in favor of Bootstrap
|
|
@@ -26496,6 +26549,7 @@ class Composer extends Component {
|
|
|
26496
26549
|
static components = { TextValueProvider, FunctionDescriptionProvider };
|
|
26497
26550
|
static defaultProps = {
|
|
26498
26551
|
inputStyle: "",
|
|
26552
|
+
isDefaultFocus: false,
|
|
26499
26553
|
};
|
|
26500
26554
|
composerRef = useRef("o_composer");
|
|
26501
26555
|
contentHelper = new ContentEditableHelper(this.composerRef.el);
|
|
@@ -26526,7 +26580,10 @@ class Composer extends Component {
|
|
|
26526
26580
|
if (this.props.delimitation && this.props.rect) {
|
|
26527
26581
|
const { x: cellX, y: cellY, height: cellHeight } = this.props.rect;
|
|
26528
26582
|
const remainingHeight = this.props.delimitation.height - (cellY + cellHeight);
|
|
26583
|
+
assistantStyle["max-height"] = `${remainingHeight}px`;
|
|
26529
26584
|
if (cellY > remainingHeight) {
|
|
26585
|
+
const availableSpaceAbove = cellY;
|
|
26586
|
+
assistantStyle["max-height"] = `${availableSpaceAbove}px`;
|
|
26530
26587
|
// render top
|
|
26531
26588
|
// We compensate 2 px of margin on the assistant style + 1px for design reasons
|
|
26532
26589
|
assistantStyle.top = `-3px`;
|
|
@@ -26552,7 +26609,6 @@ class Composer extends Component {
|
|
|
26552
26609
|
F2: () => console.warn("Not implemented"),
|
|
26553
26610
|
F4: this.processF4Key,
|
|
26554
26611
|
Tab: (ev) => this.processTabKey(ev),
|
|
26555
|
-
" ": (ev) => this.processSpaceKey(ev),
|
|
26556
26612
|
};
|
|
26557
26613
|
keyCodeMapping = {
|
|
26558
26614
|
NumpadDecimal: this.processNumpadDecimal,
|
|
@@ -26560,12 +26616,12 @@ class Composer extends Component {
|
|
|
26560
26616
|
setup() {
|
|
26561
26617
|
onMounted(() => {
|
|
26562
26618
|
const el = this.composerRef.el;
|
|
26619
|
+
if (this.props.isDefaultFocus) {
|
|
26620
|
+
this.env.focusableElement.setFocusableElement(el);
|
|
26621
|
+
}
|
|
26563
26622
|
this.contentHelper.updateEl(el);
|
|
26564
26623
|
this.processTokenAtCursor();
|
|
26565
26624
|
});
|
|
26566
|
-
onWillUnmount(() => {
|
|
26567
|
-
this.props.onComposerUnmounted?.();
|
|
26568
|
-
});
|
|
26569
26625
|
useEffect(() => {
|
|
26570
26626
|
this.processContent();
|
|
26571
26627
|
});
|
|
@@ -26574,7 +26630,8 @@ class Composer extends Component {
|
|
|
26574
26630
|
// Handlers
|
|
26575
26631
|
// ---------------------------------------------------------------------------
|
|
26576
26632
|
processArrowKeys(ev) {
|
|
26577
|
-
if (this.env.model.getters.isSelectingForComposer()
|
|
26633
|
+
if (this.env.model.getters.isSelectingForComposer() ||
|
|
26634
|
+
this.env.model.getters.getEditionMode() === "inactive") {
|
|
26578
26635
|
this.functionDescriptionState.showDescription = false;
|
|
26579
26636
|
// Prevent the default content editable behavior which moves the cursor
|
|
26580
26637
|
ev.preventDefault();
|
|
@@ -26617,26 +26674,20 @@ class Composer extends Component {
|
|
|
26617
26674
|
processTabKey(ev) {
|
|
26618
26675
|
ev.preventDefault();
|
|
26619
26676
|
ev.stopPropagation();
|
|
26620
|
-
|
|
26621
|
-
|
|
26622
|
-
|
|
26623
|
-
|
|
26624
|
-
|
|
26625
|
-
|
|
26677
|
+
if (this.env.model.getters.getEditionMode() !== "inactive") {
|
|
26678
|
+
const state = this.autoCompleteState;
|
|
26679
|
+
if (state.showProvider && state.selectedIndex !== undefined) {
|
|
26680
|
+
const autoCompleteValue = this.autoCompleteState.values[state.selectedIndex]?.text;
|
|
26681
|
+
if (autoCompleteValue) {
|
|
26682
|
+
this.autoComplete(autoCompleteValue);
|
|
26683
|
+
return;
|
|
26684
|
+
}
|
|
26626
26685
|
}
|
|
26686
|
+
interactiveStopEdition(this.env);
|
|
26627
26687
|
}
|
|
26628
26688
|
const direction = ev.shiftKey ? "left" : "right";
|
|
26629
|
-
interactiveStopEdition(this.env);
|
|
26630
26689
|
this.env.model.selection.moveAnchorCell(direction, 1);
|
|
26631
26690
|
}
|
|
26632
|
-
processSpaceKey(ev) {
|
|
26633
|
-
if (ev.ctrlKey) {
|
|
26634
|
-
ev.preventDefault();
|
|
26635
|
-
ev.stopPropagation();
|
|
26636
|
-
this.showFunctionAutocomplete("");
|
|
26637
|
-
this.env.model.dispatch("STOP_COMPOSER_RANGE_SELECTION");
|
|
26638
|
-
}
|
|
26639
|
-
}
|
|
26640
26691
|
processEnterKey(ev) {
|
|
26641
26692
|
ev.preventDefault();
|
|
26642
26693
|
ev.stopPropagation();
|
|
@@ -26689,6 +26740,9 @@ class Composer extends Component {
|
|
|
26689
26740
|
this.compositionActive = false;
|
|
26690
26741
|
}
|
|
26691
26742
|
onKeydown(ev) {
|
|
26743
|
+
if (this.env.model.getters.getEditionMode() === "inactive") {
|
|
26744
|
+
return;
|
|
26745
|
+
}
|
|
26692
26746
|
let handler = this.keyMapping[ev.key] || this.keyCodeMapping[ev.code];
|
|
26693
26747
|
if (handler) {
|
|
26694
26748
|
handler.call(this, ev);
|
|
@@ -26697,14 +26751,33 @@ class Composer extends Component {
|
|
|
26697
26751
|
ev.stopPropagation();
|
|
26698
26752
|
}
|
|
26699
26753
|
}
|
|
26754
|
+
onPaste(ev) {
|
|
26755
|
+
if (this.env.model.getters.getEditionMode() !== "inactive") {
|
|
26756
|
+
ev.stopPropagation();
|
|
26757
|
+
}
|
|
26758
|
+
}
|
|
26700
26759
|
/*
|
|
26701
26760
|
* Triggered automatically by the content-editable between the keydown and key up
|
|
26702
26761
|
* */
|
|
26703
26762
|
onInput(ev) {
|
|
26704
|
-
if (
|
|
26763
|
+
if (!this.shouldProcessInputEvents) {
|
|
26764
|
+
return;
|
|
26765
|
+
}
|
|
26766
|
+
if (ev.inputType === "insertFromPaste" &&
|
|
26767
|
+
this.env.model.getters.getEditionMode() === "inactive") {
|
|
26705
26768
|
return;
|
|
26706
26769
|
}
|
|
26707
|
-
|
|
26770
|
+
ev.stopPropagation();
|
|
26771
|
+
let content;
|
|
26772
|
+
if (this.env.model.getters.getEditionMode() === "inactive") {
|
|
26773
|
+
content = ev.data || "";
|
|
26774
|
+
}
|
|
26775
|
+
else {
|
|
26776
|
+
content = this.contentHelper.getText();
|
|
26777
|
+
}
|
|
26778
|
+
if (this.props.focus === "inactive") {
|
|
26779
|
+
return this.props.onComposerCellFocused?.(content);
|
|
26780
|
+
}
|
|
26708
26781
|
let selection = this.contentHelper.getCurrentSelection();
|
|
26709
26782
|
if (ev.inputType === "insertParagraph") {
|
|
26710
26783
|
const start = Math.min(selection.start, selection.end);
|
|
@@ -26786,9 +26859,8 @@ class Composer extends Component {
|
|
|
26786
26859
|
}
|
|
26787
26860
|
const newSelection = this.contentHelper.getCurrentSelection();
|
|
26788
26861
|
this.env.model.dispatch("STOP_COMPOSER_RANGE_SELECTION");
|
|
26789
|
-
|
|
26790
|
-
|
|
26791
|
-
}
|
|
26862
|
+
this.props.onComposerContentFocused();
|
|
26863
|
+
if (this.props.focus === "inactive") ;
|
|
26792
26864
|
this.env.model.dispatch("CHANGE_COMPOSER_CURSOR_SELECTION", newSelection);
|
|
26793
26865
|
this.processTokenAtCursor();
|
|
26794
26866
|
}
|
|
@@ -26955,7 +27027,7 @@ class Composer extends Component {
|
|
|
26955
27027
|
const highlights = this.env.model.getters.getHighlights();
|
|
26956
27028
|
const refSheet = sheetName
|
|
26957
27029
|
? this.env.model.getters.getSheetIdByName(sheetName)
|
|
26958
|
-
: this.env.model.getters.getCurrentEditedCell()
|
|
27030
|
+
: this.env.model.getters.getCurrentEditedCell()?.sheetId;
|
|
26959
27031
|
const highlight = highlights.find((highlight) => {
|
|
26960
27032
|
if (highlight.sheetId !== refSheet)
|
|
26961
27033
|
return false;
|
|
@@ -27048,12 +27120,13 @@ class Composer extends Component {
|
|
|
27048
27120
|
}
|
|
27049
27121
|
}
|
|
27050
27122
|
Composer.props = {
|
|
27051
|
-
focus: { validate: (value) => ["inactive", "cellFocus", "contentFocus"].includes(value) },
|
|
27052
|
-
onComposerContentFocused: Function,
|
|
27053
27123
|
inputStyle: { type: String, optional: true },
|
|
27054
27124
|
rect: { type: Object, optional: true },
|
|
27055
27125
|
delimitation: { type: Object, optional: true },
|
|
27056
|
-
|
|
27126
|
+
focus: { validate: (value) => ["inactive", "cellFocus", "contentFocus"].includes(value) },
|
|
27127
|
+
onComposerCellFocused: { type: Function, optional: true },
|
|
27128
|
+
onComposerContentFocused: Function,
|
|
27129
|
+
isDefaultFocus: { type: Boolean, optional: true },
|
|
27057
27130
|
};
|
|
27058
27131
|
|
|
27059
27132
|
const COMPOSER_BORDER_WIDTH = 3 * 0.4 * window.devicePixelRatio || 1;
|
|
@@ -27087,51 +27160,25 @@ css /* scss */ `
|
|
|
27087
27160
|
class GridComposer extends Component {
|
|
27088
27161
|
static template = "o-spreadsheet-GridComposer";
|
|
27089
27162
|
static components = { Composer };
|
|
27090
|
-
|
|
27091
|
-
|
|
27092
|
-
rect;
|
|
27163
|
+
rect = this.defaultRect;
|
|
27164
|
+
isEditing = false;
|
|
27093
27165
|
isCellReferenceVisible;
|
|
27094
|
-
|
|
27166
|
+
get defaultRect() {
|
|
27167
|
+
return { x: 0, y: 0, width: 0, height: 0 };
|
|
27168
|
+
}
|
|
27095
27169
|
setup() {
|
|
27096
|
-
this.gridComposerRef = useRef("gridComposer");
|
|
27097
|
-
this.composerState = useState({
|
|
27098
|
-
rect: undefined,
|
|
27099
|
-
delimitation: undefined,
|
|
27100
|
-
});
|
|
27101
|
-
const { sheetId, col, row } = this.env.model.getters.getActivePosition();
|
|
27102
|
-
this.zone = this.env.model.getters.expandZone(sheetId, positionToZone({ col, row }));
|
|
27103
|
-
this.rect = this.env.model.getters.getVisibleRect(this.zone);
|
|
27104
|
-
this.isCellReferenceVisible = false;
|
|
27105
|
-
onMounted(() => {
|
|
27106
|
-
const el = this.gridComposerRef.el;
|
|
27107
|
-
this.composerState.rect = {
|
|
27108
|
-
x: this.rect.x,
|
|
27109
|
-
y: this.rect.y,
|
|
27110
|
-
width: el.clientWidth,
|
|
27111
|
-
height: el.clientHeight,
|
|
27112
|
-
};
|
|
27113
|
-
this.composerState.delimitation = {
|
|
27114
|
-
width: this.props.gridDims.width,
|
|
27115
|
-
height: this.props.gridDims.height,
|
|
27116
|
-
};
|
|
27117
|
-
});
|
|
27118
27170
|
onWillUpdateProps(() => {
|
|
27119
|
-
|
|
27120
|
-
|
|
27121
|
-
}
|
|
27122
|
-
const sheetId = this.env.model.getters.getActiveSheetId();
|
|
27123
|
-
const zone = this.env.model.getters.getSelectedZone();
|
|
27124
|
-
const rect = this.env.model.getters.getVisibleRect(zone);
|
|
27125
|
-
if (!deepEquals(rect, this.rect) ||
|
|
27126
|
-
sheetId !== this.env.model.getters.getCurrentEditedCell().sheetId) {
|
|
27127
|
-
this.isCellReferenceVisible = true;
|
|
27128
|
-
}
|
|
27171
|
+
this.updateComponentPosition();
|
|
27172
|
+
this.updateCellReferenceVisibility();
|
|
27129
27173
|
});
|
|
27130
27174
|
}
|
|
27131
27175
|
get shouldDisplayCellReference() {
|
|
27132
27176
|
return this.isCellReferenceVisible;
|
|
27133
27177
|
}
|
|
27134
27178
|
get cellReference() {
|
|
27179
|
+
if (!this.env.model.getters.getCurrentEditedCell()) {
|
|
27180
|
+
return "";
|
|
27181
|
+
}
|
|
27135
27182
|
const { col, row, sheetId } = this.env.model.getters.getCurrentEditedCell();
|
|
27136
27183
|
const prefixSheet = sheetId !== this.env.model.getters.getActiveSheetId();
|
|
27137
27184
|
return `${prefixSheet ? getCanonicalSheetName(this.env.model.getters.getSheetName(sheetId)) + "!" : ""}${toXC(col, row)}`;
|
|
@@ -27143,7 +27190,27 @@ class GridComposer extends Component {
|
|
|
27143
27190
|
top: `${top - GRID_CELL_REFERENCE_TOP_OFFSET}px`,
|
|
27144
27191
|
});
|
|
27145
27192
|
}
|
|
27193
|
+
get composerProps() {
|
|
27194
|
+
const { width, height } = this.env.model.getters.getSheetViewDimensionWithHeaders();
|
|
27195
|
+
return {
|
|
27196
|
+
rect: { ...this.rect },
|
|
27197
|
+
delimitation: {
|
|
27198
|
+
width,
|
|
27199
|
+
height,
|
|
27200
|
+
},
|
|
27201
|
+
focus: this.props.focus,
|
|
27202
|
+
isDefaultFocus: true,
|
|
27203
|
+
onComposerContentFocused: this.props.onComposerContentFocused,
|
|
27204
|
+
onComposerCellFocused: this.props.onComposerCellFocused,
|
|
27205
|
+
};
|
|
27206
|
+
}
|
|
27146
27207
|
get containerStyle() {
|
|
27208
|
+
if (this.env.model.getters.getEditionMode() === "inactive" || !this.rect) {
|
|
27209
|
+
return `
|
|
27210
|
+
position: absolute;
|
|
27211
|
+
z-index: -1000;
|
|
27212
|
+
`;
|
|
27213
|
+
}
|
|
27147
27214
|
const isFormula = this.env.model.getters.getCurrentContent().startsWith("=");
|
|
27148
27215
|
const cell = this.env.model.getters.getActiveCell();
|
|
27149
27216
|
const position = this.env.model.getters.getActivePosition();
|
|
@@ -27163,6 +27230,8 @@ class GridComposer extends Component {
|
|
|
27163
27230
|
if (!isFormula) {
|
|
27164
27231
|
textAlign = style.align || cell.defaultAlign;
|
|
27165
27232
|
}
|
|
27233
|
+
const maxHeight = this.props.gridDims.height - this.rect.y;
|
|
27234
|
+
const maxWidth = this.props.gridDims.width - this.rect.x;
|
|
27166
27235
|
/**
|
|
27167
27236
|
* min-size is on the container, not the composer element, because we want to have the same size as the cell by default,
|
|
27168
27237
|
* including all the paddings/margins of the composer
|
|
@@ -27174,6 +27243,8 @@ class GridComposer extends Component {
|
|
|
27174
27243
|
top: `${top}px`,
|
|
27175
27244
|
"min-width": `${width + 1}px`,
|
|
27176
27245
|
"min-height": `${height + 1}px`,
|
|
27246
|
+
"max-width": `${maxWidth}px`,
|
|
27247
|
+
"max-height": `${maxHeight}px`,
|
|
27177
27248
|
background,
|
|
27178
27249
|
color,
|
|
27179
27250
|
"font-size": `${fontSizeInPixels(fontSize)}px`,
|
|
@@ -27183,20 +27254,38 @@ class GridComposer extends Component {
|
|
|
27183
27254
|
"text-align": textAlign,
|
|
27184
27255
|
});
|
|
27185
27256
|
}
|
|
27186
|
-
|
|
27187
|
-
const
|
|
27188
|
-
|
|
27189
|
-
|
|
27190
|
-
|
|
27191
|
-
|
|
27192
|
-
|
|
27257
|
+
updateComponentPosition() {
|
|
27258
|
+
const isEditing = this.env.model.getters.getEditionMode() !== "inactive";
|
|
27259
|
+
if (this.isEditing !== isEditing) {
|
|
27260
|
+
this.isEditing = isEditing;
|
|
27261
|
+
if (!isEditing) {
|
|
27262
|
+
this.rect = this.defaultRect;
|
|
27263
|
+
this.env.focusableElement.focus();
|
|
27264
|
+
return;
|
|
27265
|
+
}
|
|
27266
|
+
const position = this.env.model.getters.getActivePosition();
|
|
27267
|
+
const zone = this.env.model.getters.expandZone(position.sheetId, positionToZone(position));
|
|
27268
|
+
this.rect = this.env.model.getters.getVisibleRect(zone);
|
|
27269
|
+
}
|
|
27270
|
+
}
|
|
27271
|
+
updateCellReferenceVisibility() {
|
|
27272
|
+
if (this.isCellReferenceVisible || this.env.model.getters.getEditionMode() === "inactive") {
|
|
27273
|
+
return;
|
|
27274
|
+
}
|
|
27275
|
+
const sheetId = this.env.model.getters.getActiveSheetId();
|
|
27276
|
+
const zone = this.env.model.getters.getSelectedZone();
|
|
27277
|
+
const rect = this.env.model.getters.getVisibleRect(zone);
|
|
27278
|
+
if (!deepEquals(rect, this.rect) ||
|
|
27279
|
+
sheetId !== this.env.model.getters.getCurrentEditedCell().sheetId) {
|
|
27280
|
+
this.isCellReferenceVisible = true;
|
|
27281
|
+
}
|
|
27193
27282
|
}
|
|
27194
27283
|
}
|
|
27195
27284
|
GridComposer.props = {
|
|
27196
27285
|
focus: { validate: (value) => ["inactive", "cellFocus", "contentFocus"].includes(value) },
|
|
27197
|
-
onComposerUnmounted: Function,
|
|
27198
27286
|
onComposerContentFocused: Function,
|
|
27199
27287
|
gridDims: Object,
|
|
27288
|
+
onComposerCellFocused: Function,
|
|
27200
27289
|
};
|
|
27201
27290
|
|
|
27202
27291
|
const CSS$1 = css /* scss */ `
|
|
@@ -28287,7 +28376,6 @@ class GridOverlay extends Component {
|
|
|
28287
28376
|
this.props.onCellDoubleClicked(col, row);
|
|
28288
28377
|
}
|
|
28289
28378
|
onContextMenu(ev) {
|
|
28290
|
-
ev.preventDefault();
|
|
28291
28379
|
const [col, row] = this.getCartesianCoordinates(ev);
|
|
28292
28380
|
this.props.onCellRightClicked(col, row, { x: ev.clientX, y: ev.clientY });
|
|
28293
28381
|
}
|
|
@@ -29372,7 +29460,6 @@ class Grid extends Component {
|
|
|
29372
29460
|
HEADER_WIDTH = HEADER_WIDTH;
|
|
29373
29461
|
menuState;
|
|
29374
29462
|
gridRef;
|
|
29375
|
-
hiddenInput;
|
|
29376
29463
|
onMouseWheel;
|
|
29377
29464
|
canvasPosition;
|
|
29378
29465
|
hoveredCell;
|
|
@@ -29383,17 +29470,16 @@ class Grid extends Component {
|
|
|
29383
29470
|
menuItems: [],
|
|
29384
29471
|
});
|
|
29385
29472
|
this.gridRef = useRef("grid");
|
|
29386
|
-
this.hiddenInput = useRef("hiddenInput");
|
|
29387
29473
|
this.canvasPosition = useAbsoluteBoundingRect(this.gridRef);
|
|
29388
29474
|
this.hoveredCell = useState({ col: undefined, row: undefined });
|
|
29389
29475
|
useChildSubEnv({ getPopoverContainerRect: () => this.getGridRect() });
|
|
29390
29476
|
useExternalListener(document.body, "cut", this.copy.bind(this, true));
|
|
29391
29477
|
useExternalListener(document.body, "copy", this.copy.bind(this, false));
|
|
29392
29478
|
useExternalListener(document.body, "paste", this.paste);
|
|
29393
|
-
onMounted(() => this.
|
|
29394
|
-
this.props.exposeFocus(() => this.
|
|
29479
|
+
onMounted(() => this.focusDefaultElement());
|
|
29480
|
+
this.props.exposeFocus(() => this.focusDefaultElement());
|
|
29395
29481
|
useGridDrawing("canvas", this.env.model, () => this.env.model.getters.getSheetViewDimensionWithHeaders());
|
|
29396
|
-
useEffect(() => this.
|
|
29482
|
+
useEffect(() => this.focusDefaultElement(), () => [this.env.model.getters.getActiveSheetId()]);
|
|
29397
29483
|
this.onMouseWheel = useWheelHandler((deltaX, deltaY) => {
|
|
29398
29484
|
this.moveCanvas(deltaX, deltaY);
|
|
29399
29485
|
this.hoveredCell.col = undefined;
|
|
@@ -29416,7 +29502,7 @@ class Grid extends Component {
|
|
|
29416
29502
|
if (this.env.model.getters.hasOpenedPopover()) {
|
|
29417
29503
|
this.closeOpenedPopover();
|
|
29418
29504
|
}
|
|
29419
|
-
this.
|
|
29505
|
+
this.focusDefaultElement();
|
|
29420
29506
|
}
|
|
29421
29507
|
// this map will handle most of the actions that should happen on key down. The arrow keys are managed in the key
|
|
29422
29508
|
// down itself
|
|
@@ -29595,10 +29681,10 @@ class Grid extends Component {
|
|
|
29595
29681
|
"ALT+SHIFT+ARROWUP": () => this.processHeaderGroupingKey("up"),
|
|
29596
29682
|
"ALT+SHIFT+ARROWDOWN": () => this.processHeaderGroupingKey("down"),
|
|
29597
29683
|
};
|
|
29598
|
-
|
|
29684
|
+
focusDefaultElement() {
|
|
29599
29685
|
if (!this.env.model.getters.getSelectedFigureId() &&
|
|
29600
29686
|
this.env.model.getters.getEditionMode() === "inactive") {
|
|
29601
|
-
this.
|
|
29687
|
+
this.env.focusableElement.focus();
|
|
29602
29688
|
}
|
|
29603
29689
|
}
|
|
29604
29690
|
get gridEl() {
|
|
@@ -29743,19 +29829,6 @@ class Grid extends Component {
|
|
|
29743
29829
|
return;
|
|
29744
29830
|
}
|
|
29745
29831
|
}
|
|
29746
|
-
onInput(ev) {
|
|
29747
|
-
// the user meant to paste in the sheet, not open the composer with the pasted content
|
|
29748
|
-
if (!ev.isComposing && ev.inputType === "insertFromPaste") {
|
|
29749
|
-
return;
|
|
29750
|
-
}
|
|
29751
|
-
if (ev.data) {
|
|
29752
|
-
// if the user types a character on the grid, it means he wants to start composing the selected cell with that
|
|
29753
|
-
// character
|
|
29754
|
-
ev.preventDefault();
|
|
29755
|
-
ev.stopPropagation();
|
|
29756
|
-
this.props.onGridComposerCellFocused(ev.data);
|
|
29757
|
-
}
|
|
29758
|
-
}
|
|
29759
29832
|
// ---------------------------------------------------------------------------
|
|
29760
29833
|
// Context Menu
|
|
29761
29834
|
// ---------------------------------------------------------------------------
|
|
@@ -29870,7 +29943,7 @@ class Grid extends Component {
|
|
|
29870
29943
|
}
|
|
29871
29944
|
closeMenu() {
|
|
29872
29945
|
this.menuState.isOpen = false;
|
|
29873
|
-
this.
|
|
29946
|
+
this.focusDefaultElement();
|
|
29874
29947
|
}
|
|
29875
29948
|
processHeaderGroupingKey(direction) {
|
|
29876
29949
|
if (this.env.model.getters.getSelectedZones().length !== 1) {
|
|
@@ -33766,8 +33839,16 @@ const MIGRATIONS = [
|
|
|
33766
33839
|
},
|
|
33767
33840
|
},
|
|
33768
33841
|
{
|
|
33769
|
-
description: "
|
|
33842
|
+
description: "Fix datafilter duplication",
|
|
33770
33843
|
from: 12,
|
|
33844
|
+
to: 12.5,
|
|
33845
|
+
applyMigration(data) {
|
|
33846
|
+
return fixOverlappingFilters(data);
|
|
33847
|
+
},
|
|
33848
|
+
},
|
|
33849
|
+
{
|
|
33850
|
+
description: "Change Border description structure",
|
|
33851
|
+
from: 12.5,
|
|
33771
33852
|
to: 13,
|
|
33772
33853
|
applyMigration(data) {
|
|
33773
33854
|
for (const borderId in data.borders) {
|
|
@@ -33798,6 +33879,14 @@ const MIGRATIONS = [
|
|
|
33798
33879
|
return data;
|
|
33799
33880
|
},
|
|
33800
33881
|
},
|
|
33882
|
+
{
|
|
33883
|
+
description: "Fix datafilter duplication (post saas-16.4)",
|
|
33884
|
+
from: 14,
|
|
33885
|
+
to: 14.5,
|
|
33886
|
+
applyMigration(data) {
|
|
33887
|
+
return fixOverlappingFilters(data);
|
|
33888
|
+
},
|
|
33889
|
+
},
|
|
33801
33890
|
];
|
|
33802
33891
|
/**
|
|
33803
33892
|
* This function is used to repair faulty data independently of the migration.
|
|
@@ -33955,6 +34044,26 @@ function fixChartDefinitions(data, initialMessages) {
|
|
|
33955
34044
|
}
|
|
33956
34045
|
return messages;
|
|
33957
34046
|
}
|
|
34047
|
+
function fixOverlappingFilters(data) {
|
|
34048
|
+
for (let sheet of data.sheets || []) {
|
|
34049
|
+
let knownDataFilterZones = [];
|
|
34050
|
+
for (let filterTable of sheet.filterTables || []) {
|
|
34051
|
+
const zone = toZone(filterTable.range);
|
|
34052
|
+
// See commit message of https://github.com/odoo/o-spreadsheet/pull/3632 of more details
|
|
34053
|
+
const intersectZoneIndex = knownDataFilterZones.findIndex((knownZone) => overlap(knownZone, zone));
|
|
34054
|
+
if (intersectZoneIndex !== -1) {
|
|
34055
|
+
knownDataFilterZones[intersectZoneIndex] = zone;
|
|
34056
|
+
}
|
|
34057
|
+
else {
|
|
34058
|
+
knownDataFilterZones.push(zone);
|
|
34059
|
+
}
|
|
34060
|
+
}
|
|
34061
|
+
sheet.filterTables = knownDataFilterZones.map((zone) => ({
|
|
34062
|
+
range: zoneToXc(zone),
|
|
34063
|
+
}));
|
|
34064
|
+
}
|
|
34065
|
+
return data;
|
|
34066
|
+
}
|
|
33958
34067
|
// -----------------------------------------------------------------------------
|
|
33959
34068
|
// Helpers
|
|
33960
34069
|
// -----------------------------------------------------------------------------
|
|
@@ -34604,22 +34713,7 @@ class BordersPlugin extends CorePlugin {
|
|
|
34604
34713
|
}
|
|
34605
34714
|
}
|
|
34606
34715
|
export(data) {
|
|
34607
|
-
// Borders
|
|
34608
|
-
let borderId = 0;
|
|
34609
34716
|
const borders = {};
|
|
34610
|
-
/**
|
|
34611
|
-
* Get the id of the given border. If the border does not exist, it creates
|
|
34612
|
-
* one.
|
|
34613
|
-
*/
|
|
34614
|
-
function getBorderId(border) {
|
|
34615
|
-
for (let [key, value] of Object.entries(borders)) {
|
|
34616
|
-
if (deepEquals(value, border)) {
|
|
34617
|
-
return parseInt(key, 10);
|
|
34618
|
-
}
|
|
34619
|
-
}
|
|
34620
|
-
borders[++borderId] = border;
|
|
34621
|
-
return borderId;
|
|
34622
|
-
}
|
|
34623
34717
|
for (let sheet of data.sheets) {
|
|
34624
34718
|
for (let col = 0; col < sheet.colNumber; col++) {
|
|
34625
34719
|
for (let row = 0; row < sheet.rowNumber; row++) {
|
|
@@ -34627,7 +34721,7 @@ class BordersPlugin extends CorePlugin {
|
|
|
34627
34721
|
if (border) {
|
|
34628
34722
|
const xc = toXC(col, row);
|
|
34629
34723
|
const cell = sheet.cells[xc];
|
|
34630
|
-
const borderId =
|
|
34724
|
+
const borderId = getItemId(border, borders);
|
|
34631
34725
|
if (cell) {
|
|
34632
34726
|
cell.border = borderId;
|
|
34633
34727
|
}
|
|
@@ -35432,9 +35526,9 @@ class CellPlugin extends CorePlugin {
|
|
|
35432
35526
|
allowDispatch(cmd) {
|
|
35433
35527
|
switch (cmd.type) {
|
|
35434
35528
|
case "UPDATE_CELL":
|
|
35435
|
-
return this.
|
|
35529
|
+
return this.checkValidations(cmd, this.checkCellOutOfSheet, this.checkUselessUpdateCell);
|
|
35436
35530
|
case "CLEAR_CELL":
|
|
35437
|
-
return this.checkValidations(cmd, this.
|
|
35531
|
+
return this.checkValidations(cmd, this.checkCellOutOfSheet, this.checkUselessClearCell);
|
|
35438
35532
|
default:
|
|
35439
35533
|
return "Success" /* CommandResult.Success */;
|
|
35440
35534
|
}
|
|
@@ -35567,9 +35661,6 @@ class CellPlugin extends CorePlugin {
|
|
|
35567
35661
|
format: cell.format ? getItemId(cell.format, formats) : undefined,
|
|
35568
35662
|
content: cell.content || undefined,
|
|
35569
35663
|
};
|
|
35570
|
-
if (cell instanceof FormulaCellWithDependencies) {
|
|
35571
|
-
cells[xc].content = cell.contentWithFixedReferences || undefined;
|
|
35572
|
-
}
|
|
35573
35664
|
}
|
|
35574
35665
|
_sheet.cells = cells;
|
|
35575
35666
|
}
|
|
@@ -35587,8 +35678,8 @@ class CellPlugin extends CorePlugin {
|
|
|
35587
35678
|
}
|
|
35588
35679
|
removeDefaultStyleValues(style) {
|
|
35589
35680
|
const cleanedStyle = { ...style };
|
|
35590
|
-
for (const
|
|
35591
|
-
if (cleanedStyle[property] ===
|
|
35681
|
+
for (const property in DEFAULT_STYLE) {
|
|
35682
|
+
if (cleanedStyle[property] === DEFAULT_STYLE[property]) {
|
|
35592
35683
|
delete cleanedStyle[property];
|
|
35593
35684
|
}
|
|
35594
35685
|
}
|
|
@@ -35745,10 +35836,7 @@ class CellPlugin extends CorePlugin {
|
|
|
35745
35836
|
else {
|
|
35746
35837
|
style = before ? before.style : undefined;
|
|
35747
35838
|
}
|
|
35748
|
-
const
|
|
35749
|
-
let format = ("format" in after ? after.format : before && before.format) ||
|
|
35750
|
-
detectDateFormat(afterContent, locale) ||
|
|
35751
|
-
detectNumberFormat(afterContent);
|
|
35839
|
+
const format = "format" in after ? after.format : before && before.format;
|
|
35752
35840
|
/* Read the following IF as:
|
|
35753
35841
|
* we need to remove the cell if it is completely empty, but we can know if it completely empty if:
|
|
35754
35842
|
* - the command says the new content is empty and has no border/format/style
|
|
@@ -35789,12 +35877,11 @@ class CellPlugin extends CorePlugin {
|
|
|
35789
35877
|
}
|
|
35790
35878
|
createLiteralCell(id, content, format, style) {
|
|
35791
35879
|
const locale = this.getters.getLocale();
|
|
35792
|
-
content = parseLiteral(content, locale).toString();
|
|
35793
35880
|
return {
|
|
35794
35881
|
id,
|
|
35795
|
-
content,
|
|
35882
|
+
content: parseLiteral(content, locale).toString(),
|
|
35796
35883
|
style,
|
|
35797
|
-
format,
|
|
35884
|
+
format: format || detectDateFormat(content, locale) || detectNumberFormat(content),
|
|
35798
35885
|
isFormula: false,
|
|
35799
35886
|
};
|
|
35800
35887
|
}
|
|
@@ -35820,8 +35907,11 @@ class CellPlugin extends CorePlugin {
|
|
|
35820
35907
|
* being a computed property to rebuild the dependencies XC.
|
|
35821
35908
|
*/
|
|
35822
35909
|
createFormulaCellWithDependencies(id, compiledFormula, format, style, sheetId) {
|
|
35823
|
-
const dependencies =
|
|
35824
|
-
|
|
35910
|
+
const dependencies = [];
|
|
35911
|
+
for (const xc of compiledFormula.dependencies) {
|
|
35912
|
+
dependencies.push(this.getters.getRangeFromSheetXC(sheetId, xc));
|
|
35913
|
+
}
|
|
35914
|
+
return new FormulaCellWithDependencies(id, compiledFormula, format, style, dependencies, sheetId, this.getters.getRangeString);
|
|
35825
35915
|
}
|
|
35826
35916
|
createErrorFormula(id, content, format, style, error) {
|
|
35827
35917
|
return {
|
|
@@ -35856,6 +35946,18 @@ class CellPlugin extends CorePlugin {
|
|
|
35856
35946
|
}
|
|
35857
35947
|
return "Success" /* CommandResult.Success */;
|
|
35858
35948
|
}
|
|
35949
|
+
checkUselessUpdateCell(cmd) {
|
|
35950
|
+
const cell = this.getters.getCell(cmd);
|
|
35951
|
+
const hasContent = "content" in cmd || "formula" in cmd;
|
|
35952
|
+
const hasStyle = "style" in cmd;
|
|
35953
|
+
const hasFormat = "format" in cmd;
|
|
35954
|
+
if ((!hasContent || cell?.content === cmd.content) &&
|
|
35955
|
+
(!hasStyle || deepEquals(cell?.style, cmd.style)) &&
|
|
35956
|
+
(!hasFormat || cell?.format === cmd.format)) {
|
|
35957
|
+
return "NoChanges" /* CommandResult.NoChanges */;
|
|
35958
|
+
}
|
|
35959
|
+
return "Success" /* CommandResult.Success */;
|
|
35960
|
+
}
|
|
35859
35961
|
}
|
|
35860
35962
|
class FormulaCellWithDependencies {
|
|
35861
35963
|
id;
|
|
@@ -35875,7 +35977,7 @@ class FormulaCellWithDependencies {
|
|
|
35875
35977
|
const tokens = compiledFormula.tokens.map((token) => {
|
|
35876
35978
|
if (token.type === "REFERENCE") {
|
|
35877
35979
|
const index = rangeIndex++;
|
|
35878
|
-
return new RangeReferenceToken(
|
|
35980
|
+
return new RangeReferenceToken(dependencies, index, this.sheetId, this.getRangeString);
|
|
35879
35981
|
}
|
|
35880
35982
|
return token;
|
|
35881
35983
|
});
|
|
@@ -35902,13 +36004,20 @@ class FormulaCellWithDependencies {
|
|
|
35902
36004
|
}
|
|
35903
36005
|
}
|
|
35904
36006
|
class RangeReferenceToken {
|
|
35905
|
-
|
|
36007
|
+
ranges;
|
|
36008
|
+
rangeIndex;
|
|
36009
|
+
sheetId;
|
|
36010
|
+
getRangeString;
|
|
35906
36011
|
type = "REFERENCE";
|
|
35907
|
-
constructor(
|
|
35908
|
-
this.
|
|
36012
|
+
constructor(ranges, rangeIndex, sheetId, getRangeString) {
|
|
36013
|
+
this.ranges = ranges;
|
|
36014
|
+
this.rangeIndex = rangeIndex;
|
|
36015
|
+
this.sheetId = sheetId;
|
|
36016
|
+
this.getRangeString = getRangeString;
|
|
35909
36017
|
}
|
|
35910
36018
|
get value() {
|
|
35911
|
-
|
|
36019
|
+
const range = this.ranges[this.rangeIndex];
|
|
36020
|
+
return this.getRangeString(range, this.sheetId);
|
|
35912
36021
|
}
|
|
35913
36022
|
}
|
|
35914
36023
|
|
|
@@ -40644,7 +40753,15 @@ class SpreadsheetRTree {
|
|
|
40644
40753
|
if (!this.rTrees[sheetId]) {
|
|
40645
40754
|
return;
|
|
40646
40755
|
}
|
|
40647
|
-
this.rTrees[sheetId].remove(item,
|
|
40756
|
+
this.rTrees[sheetId].remove(item, this.rtreeItemComparer);
|
|
40757
|
+
}
|
|
40758
|
+
rtreeItemComparer(left, right) {
|
|
40759
|
+
return (left.data == right.data &&
|
|
40760
|
+
left.boundingBox.sheetId === right.boundingBox.sheetId &&
|
|
40761
|
+
left.boundingBox?.zone.left === right.boundingBox.zone.left &&
|
|
40762
|
+
left.boundingBox?.zone.top === right.boundingBox.zone.top &&
|
|
40763
|
+
left.boundingBox?.zone.right === right.boundingBox.zone.right &&
|
|
40764
|
+
left.boundingBox?.zone.bottom === right.boundingBox.zone.bottom);
|
|
40648
40765
|
}
|
|
40649
40766
|
}
|
|
40650
40767
|
/**
|
|
@@ -40722,7 +40839,7 @@ class FormulaDependencyGraph {
|
|
|
40722
40839
|
const queue = Array.from(ranges).reverse();
|
|
40723
40840
|
while (queue.length > 0) {
|
|
40724
40841
|
const range = queue.pop();
|
|
40725
|
-
visited.
|
|
40842
|
+
visited.addMany(this.encoder.encodeBoundingBox(range));
|
|
40726
40843
|
const impactedPositionIds = this.rTree.search(range).map((dep) => dep.data);
|
|
40727
40844
|
for (const positionId of impactedPositionIds) {
|
|
40728
40845
|
if (!visited.has(positionId)) {
|
|
@@ -40730,7 +40847,7 @@ class FormulaDependencyGraph {
|
|
|
40730
40847
|
}
|
|
40731
40848
|
}
|
|
40732
40849
|
}
|
|
40733
|
-
visited.
|
|
40850
|
+
visited.deleteMany(ranges.flatMap((r) => this.encoder.encodeBoundingBox(r)));
|
|
40734
40851
|
return visited;
|
|
40735
40852
|
}
|
|
40736
40853
|
}
|
|
@@ -40868,9 +40985,9 @@ class Evaluator {
|
|
|
40868
40985
|
const cells = positions.map((p) => this.encoder.encode(p));
|
|
40869
40986
|
const cellsToCompute = new JetSet(cells);
|
|
40870
40987
|
const arrayFormulasPositionIds = this.getArrayFormulasImpactedByChangesOf(cells);
|
|
40871
|
-
cellsToCompute.
|
|
40872
|
-
cellsToCompute.
|
|
40873
|
-
cellsToCompute.
|
|
40988
|
+
cellsToCompute.addMany(this.getCellsDependingOn(cells));
|
|
40989
|
+
cellsToCompute.addMany(arrayFormulasPositionIds);
|
|
40990
|
+
cellsToCompute.addMany(this.getCellsDependingOn(arrayFormulasPositionIds));
|
|
40874
40991
|
this.evaluate(cellsToCompute);
|
|
40875
40992
|
}
|
|
40876
40993
|
getArrayFormulasImpactedByChangesOf(positionIds) {
|
|
@@ -40884,7 +41001,7 @@ class Evaluator {
|
|
|
40884
41001
|
}
|
|
40885
41002
|
if (!content) {
|
|
40886
41003
|
// The previous content could have blocked some array formulas
|
|
40887
|
-
impactedPositionIds.
|
|
41004
|
+
impactedPositionIds.addMany(this.getArrayFormulasBlockedByOrSpreadingOn(positionId));
|
|
40888
41005
|
}
|
|
40889
41006
|
}
|
|
40890
41007
|
return impactedPositionIds;
|
|
@@ -40933,7 +41050,7 @@ class Evaluator {
|
|
|
40933
41050
|
}
|
|
40934
41051
|
const arrayFormulas = this.spreadingRelations.getFormulaPositionsSpreadingOn(positionId);
|
|
40935
41052
|
const cells = new JetSet(arrayFormulas);
|
|
40936
|
-
cells.
|
|
41053
|
+
cells.addMany(this.getCellsDependingOn(arrayFormulas));
|
|
40937
41054
|
return cells;
|
|
40938
41055
|
}
|
|
40939
41056
|
nextPositionsToUpdate = new JetSet();
|
|
@@ -41082,7 +41199,7 @@ class Evaluator {
|
|
|
41082
41199
|
this.setEvaluatedCell(positionId, evaluatedCell);
|
|
41083
41200
|
// check if formula dependencies present in the spread zone
|
|
41084
41201
|
// if so, they need to be recomputed
|
|
41085
|
-
this.nextPositionsToUpdate.
|
|
41202
|
+
this.nextPositionsToUpdate.addMany(this.getCellsDependingOn([positionId]));
|
|
41086
41203
|
};
|
|
41087
41204
|
}
|
|
41088
41205
|
invalidateSpreading(positionId) {
|
|
@@ -41097,8 +41214,8 @@ class Evaluator {
|
|
|
41097
41214
|
continue;
|
|
41098
41215
|
}
|
|
41099
41216
|
this.evaluatedCells.delete(child);
|
|
41100
|
-
this.nextPositionsToUpdate.
|
|
41101
|
-
this.nextPositionsToUpdate.
|
|
41217
|
+
this.nextPositionsToUpdate.addMany(this.getCellsDependingOn([child]));
|
|
41218
|
+
this.nextPositionsToUpdate.addMany(this.getArrayFormulasBlockedByOrSpreadingOn(child));
|
|
41102
41219
|
}
|
|
41103
41220
|
this.spreadingRelations.removeNode(positionId);
|
|
41104
41221
|
}
|
|
@@ -41447,7 +41564,13 @@ class EvaluationPlugin extends UIPlugin {
|
|
|
41447
41564
|
const format = newFormat
|
|
41448
41565
|
? getItemId(newFormat, data.formats)
|
|
41449
41566
|
: exportedCellData.format;
|
|
41450
|
-
|
|
41567
|
+
let content;
|
|
41568
|
+
if (formulaCell instanceof FormulaCellWithDependencies) {
|
|
41569
|
+
content = this.getters.getFormulaCellContent(exportedSheetData.id, formulaCell.compiledFormula, formulaCell.compiledFormula.dependencies, true);
|
|
41570
|
+
}
|
|
41571
|
+
else {
|
|
41572
|
+
content = !isExported ? newContent : exportedCellData.content;
|
|
41573
|
+
}
|
|
41451
41574
|
exportedSheetData.cells[xc] = { ...exportedCellData, value, isFormula, content, format };
|
|
41452
41575
|
}
|
|
41453
41576
|
}
|
|
@@ -46331,19 +46454,17 @@ class SelectionInputsManagerPlugin extends UIPlugin {
|
|
|
46331
46454
|
break;
|
|
46332
46455
|
case "FOCUS_RANGE":
|
|
46333
46456
|
case "CHANGE_RANGE":
|
|
46334
|
-
|
|
46335
|
-
|
|
46336
|
-
|
|
46337
|
-
|
|
46338
|
-
|
|
46339
|
-
|
|
46340
|
-
|
|
46341
|
-
|
|
46342
|
-
|
|
46343
|
-
});
|
|
46344
|
-
}
|
|
46345
|
-
this.focusedInputId = cmd.id;
|
|
46457
|
+
const input = this.inputs[cmd.id];
|
|
46458
|
+
const range = input.ranges.find((range) => range.id === cmd.rangeId);
|
|
46459
|
+
if (range) {
|
|
46460
|
+
const sheetId = this.getters.getActiveSheetId();
|
|
46461
|
+
const zone = this.getters.getRangeFromSheetXC(sheetId, range?.xc || "A1").zone;
|
|
46462
|
+
this.selection.capture(input, { cell: { col: zone.left, row: zone.top }, zone }, {
|
|
46463
|
+
handleEvent: input.handleEvent.bind(input),
|
|
46464
|
+
release: () => (this.focusedInputId = null),
|
|
46465
|
+
});
|
|
46346
46466
|
}
|
|
46467
|
+
this.focusedInputId = cmd.id;
|
|
46347
46468
|
break;
|
|
46348
46469
|
}
|
|
46349
46470
|
this.currentInput?.handle(cmd);
|
|
@@ -50763,6 +50884,16 @@ class ImageProvider {
|
|
|
50763
50884
|
}
|
|
50764
50885
|
}
|
|
50765
50886
|
|
|
50887
|
+
class FocusableElement {
|
|
50888
|
+
focusableElement = undefined;
|
|
50889
|
+
setFocusableElement(element) {
|
|
50890
|
+
this.focusableElement = element;
|
|
50891
|
+
}
|
|
50892
|
+
focus() {
|
|
50893
|
+
this.focusableElement?.focus();
|
|
50894
|
+
}
|
|
50895
|
+
}
|
|
50896
|
+
|
|
50766
50897
|
const RIPPLE_KEY_FRAMES = [
|
|
50767
50898
|
{ transform: "scale(0)" },
|
|
50768
50899
|
{ transform: "scale(0.8)", offset: 0.33 },
|
|
@@ -52919,6 +53050,7 @@ class Spreadsheet extends Component {
|
|
|
52919
53050
|
toggleSidePanel: this.toggleSidePanel.bind(this),
|
|
52920
53051
|
clipboard: this.env.clipboard || instantiateClipboard(),
|
|
52921
53052
|
startCellEdition: (content) => this.onGridComposerCellFocused(content),
|
|
53053
|
+
focusableElement: new FocusableElement(),
|
|
52922
53054
|
});
|
|
52923
53055
|
useExternalListener(window, "resize", () => this.render(true));
|
|
52924
53056
|
useExternalListener(window, "beforeunload", this.unbindModelEvents.bind(this));
|
|
@@ -53025,16 +53157,17 @@ class Spreadsheet extends Component {
|
|
|
53025
53157
|
}
|
|
53026
53158
|
this.composer.topBarFocus = "contentFocus";
|
|
53027
53159
|
this.composer.gridFocusMode = "inactive";
|
|
53028
|
-
this.setComposerContent({ selection }
|
|
53160
|
+
this.setComposerContent({ selection });
|
|
53029
53161
|
}
|
|
53030
|
-
onGridComposerContentFocused() {
|
|
53162
|
+
onGridComposerContentFocused(selection) {
|
|
53031
53163
|
if (this.model.getters.isReadonly()) {
|
|
53032
53164
|
return;
|
|
53033
53165
|
}
|
|
53034
53166
|
this.composer.topBarFocus = "inactive";
|
|
53035
53167
|
this.composer.gridFocusMode = "contentFocus";
|
|
53036
|
-
this.setComposerContent({});
|
|
53168
|
+
this.setComposerContent({ selection });
|
|
53037
53169
|
}
|
|
53170
|
+
// TODO: either both are defined or none of them. change those args to an object
|
|
53038
53171
|
onGridComposerCellFocused(content, selection) {
|
|
53039
53172
|
if (this.model.getters.isReadonly()) {
|
|
53040
53173
|
return;
|
|
@@ -54460,11 +54593,10 @@ class SelectionStreamProcessorImpl {
|
|
|
54460
54593
|
* next cluster if the given cell is outside a cluster or at the border of a cluster in the given direction.
|
|
54461
54594
|
*/
|
|
54462
54595
|
getEndOfCluster(startPosition, dim, dir) {
|
|
54463
|
-
const sheet = this.getters.getActiveSheet();
|
|
54464
54596
|
let currentPosition = startPosition;
|
|
54465
54597
|
// If both the current cell and the next cell are not empty, we want to go to the end of the cluster
|
|
54466
54598
|
const nextCellPosition = this.getNextCellPosition(startPosition, dim, dir);
|
|
54467
|
-
let mode = !this.
|
|
54599
|
+
let mode = !this.isEvaluatedCellEmpty(currentPosition) && !this.isEvaluatedCellEmpty(nextCellPosition)
|
|
54468
54600
|
? "endOfCluster"
|
|
54469
54601
|
: "nextCluster";
|
|
54470
54602
|
while (true) {
|
|
@@ -54474,7 +54606,7 @@ class SelectionStreamProcessorImpl {
|
|
|
54474
54606
|
currentPosition.row === nextCellPosition.row) {
|
|
54475
54607
|
break;
|
|
54476
54608
|
}
|
|
54477
|
-
const isNextCellEmpty = this.
|
|
54609
|
+
const isNextCellEmpty = this.isEvaluatedCellEmpty(nextCellPosition);
|
|
54478
54610
|
if (mode === "endOfCluster" && isNextCellEmpty) {
|
|
54479
54611
|
break;
|
|
54480
54612
|
}
|
|
@@ -54488,10 +54620,20 @@ class SelectionStreamProcessorImpl {
|
|
|
54488
54620
|
return dim === "cols" ? currentPosition.col : currentPosition.row;
|
|
54489
54621
|
}
|
|
54490
54622
|
/**
|
|
54491
|
-
*
|
|
54492
|
-
* check
|
|
54623
|
+
* Checks if a cell is empty (i.e. does not have a content). If the cell is part of a merge,
|
|
54624
|
+
* the check applies to the main cell of the merge.
|
|
54493
54625
|
*/
|
|
54494
|
-
isCellEmpty({ col, row }
|
|
54626
|
+
isCellEmpty({ col, row }) {
|
|
54627
|
+
const sheetId = this.getters.getActiveSheetId();
|
|
54628
|
+
const position = this.getters.getMainCellPosition({ sheetId, col, row });
|
|
54629
|
+
return !(this.getters.getCorrespondingFormulaCell(position) || this.getters.getCell(position)?.content);
|
|
54630
|
+
}
|
|
54631
|
+
/**
|
|
54632
|
+
* Checks if a cell evaluated value is empty. If the cell is part of a merge,
|
|
54633
|
+
* the check applies to the main cell of the merge.
|
|
54634
|
+
*/
|
|
54635
|
+
isEvaluatedCellEmpty({ col, row }) {
|
|
54636
|
+
const sheetId = this.getters.getActiveSheetId();
|
|
54495
54637
|
const position = this.getters.getMainCellPosition({ sheetId, col, row });
|
|
54496
54638
|
const cell = this.getters.getEvaluatedCell(position);
|
|
54497
54639
|
return cell.type === CellValueType.empty;
|
|
@@ -56762,6 +56904,6 @@ const constants = {
|
|
|
56762
56904
|
export { AbstractChart, CommandResult, CorePlugin, DispatchResult, EvaluationError, Model, Registry, Revision, SPREADSHEET_DIMENSIONS, Spreadsheet, UIPlugin, __info__, addFunction, astToFormula, compile, compileTokens, components, constants, convertAstNodes, coreTypes, findCellInNewZone, functionCache, helpers, hooks, invalidateCFEvaluationCommands, invalidateDependenciesCommands, invalidateEvaluationCommands, iterateAstNodes, links, load, parse, parseTokens, readonlyAllowedCommands, registries, setDefaultSheetViewSize, setTranslationMethod, tokenize };
|
|
56763
56905
|
|
|
56764
56906
|
|
|
56765
|
-
__info__.version = "17.0.
|
|
56766
|
-
__info__.date = "2024-02-
|
|
56767
|
-
__info__.hash = "
|
|
56907
|
+
__info__.version = "17.0.13";
|
|
56908
|
+
__info__.date = "2024-02-28T12:28:44.914Z";
|
|
56909
|
+
__info__.hash = "ed67be7";
|