@odoo/o-spreadsheet 16.3.24 → 16.3.26
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/o-spreadsheet.cjs.js +219 -124
- package/dist/o-spreadsheet.d.ts +29 -14
- package/dist/o-spreadsheet.esm.js +219 -124
- package/dist/o-spreadsheet.iife.js +219 -124
- package/dist/o-spreadsheet.iife.min.js +141 -141
- package/dist/o_spreadsheet.xml +15 -33
- package/package.json +1 -1
|
@@ -1742,6 +1742,9 @@ function parseFormat(formatString) {
|
|
|
1742
1742
|
function formatValue(value, format) {
|
|
1743
1743
|
switch (typeof value) {
|
|
1744
1744
|
case "string":
|
|
1745
|
+
if (value.includes('\\"')) {
|
|
1746
|
+
return value.replace(/\\"/g, '"');
|
|
1747
|
+
}
|
|
1745
1748
|
return value;
|
|
1746
1749
|
case "boolean":
|
|
1747
1750
|
return value ? "TRUE" : "FALSE";
|
|
@@ -9429,6 +9432,12 @@ const INSERT_LINK = (env) => {
|
|
|
9429
9432
|
let { col, row } = env.model.getters.getActivePosition();
|
|
9430
9433
|
env.model.dispatch("OPEN_CELL_POPOVER", { col, row, popoverType: "LinkEditor" });
|
|
9431
9434
|
};
|
|
9435
|
+
const INSERT_LINK_NAME = (env) => {
|
|
9436
|
+
const sheetId = env.model.getters.getActiveSheetId();
|
|
9437
|
+
const { col, row } = env.model.getters.getActivePosition();
|
|
9438
|
+
const cell = env.model.getters.getEvaluatedCell({ sheetId, col, row });
|
|
9439
|
+
return cell && cell.link ? _lt("Edit link") : _lt("Insert link");
|
|
9440
|
+
};
|
|
9432
9441
|
//------------------------------------------------------------------------------
|
|
9433
9442
|
// Filters action
|
|
9434
9443
|
//------------------------------------------------------------------------------
|
|
@@ -16653,7 +16662,7 @@ cellMenuRegistry
|
|
|
16653
16662
|
})
|
|
16654
16663
|
.add("insert_link", {
|
|
16655
16664
|
...insertLink,
|
|
16656
|
-
name:
|
|
16665
|
+
name: INSERT_LINK_NAME,
|
|
16657
16666
|
sequence: 150,
|
|
16658
16667
|
separator: true,
|
|
16659
16668
|
});
|
|
@@ -20980,6 +20989,7 @@ class Composer extends Component {
|
|
|
20980
20989
|
static components = { TextValueProvider, FunctionDescriptionProvider };
|
|
20981
20990
|
static defaultProps = {
|
|
20982
20991
|
inputStyle: "",
|
|
20992
|
+
isDefaultFocus: false,
|
|
20983
20993
|
};
|
|
20984
20994
|
composerRef = useRef("o_composer");
|
|
20985
20995
|
contentHelper = new ContentEditableHelper(this.composerRef.el);
|
|
@@ -21037,13 +21047,13 @@ class Composer extends Component {
|
|
|
21037
21047
|
setup() {
|
|
21038
21048
|
onMounted(() => {
|
|
21039
21049
|
const el = this.composerRef.el;
|
|
21050
|
+
if (this.props.isDefaultFocus) {
|
|
21051
|
+
this.env.focusableElement.setFocusableElement(el);
|
|
21052
|
+
}
|
|
21040
21053
|
this.contentHelper.updateEl(el);
|
|
21041
21054
|
this.processContent();
|
|
21042
21055
|
this.contentHelper.scrollSelectionIntoView();
|
|
21043
21056
|
});
|
|
21044
|
-
onWillUnmount(() => {
|
|
21045
|
-
this.props.onComposerUnmounted?.();
|
|
21046
|
-
});
|
|
21047
21057
|
onPatched(() => {
|
|
21048
21058
|
if (!this.isKeyStillDown) {
|
|
21049
21059
|
this.processContent();
|
|
@@ -21054,7 +21064,8 @@ class Composer extends Component {
|
|
|
21054
21064
|
// Handlers
|
|
21055
21065
|
// ---------------------------------------------------------------------------
|
|
21056
21066
|
processArrowKeys(ev) {
|
|
21057
|
-
if (this.env.model.getters.isSelectingForComposer()
|
|
21067
|
+
if (this.env.model.getters.isSelectingForComposer() ||
|
|
21068
|
+
this.env.model.getters.getEditionMode() === "inactive") {
|
|
21058
21069
|
this.functionDescriptionState.showDescription = false;
|
|
21059
21070
|
// Prevent the default content editable behavior which moves the cursor
|
|
21060
21071
|
ev.preventDefault();
|
|
@@ -21094,21 +21105,23 @@ class Composer extends Component {
|
|
|
21094
21105
|
processTabKey(ev) {
|
|
21095
21106
|
ev.preventDefault();
|
|
21096
21107
|
ev.stopPropagation();
|
|
21097
|
-
if (this.
|
|
21098
|
-
|
|
21099
|
-
|
|
21100
|
-
|
|
21101
|
-
|
|
21108
|
+
if (this.env.model.getters.getEditionMode() !== "inactive") {
|
|
21109
|
+
if (this.autoCompleteState.showProvider) {
|
|
21110
|
+
const autoCompleteValue = this.autoCompleteState.values[this.autoCompleteState.selectedIndex]?.text;
|
|
21111
|
+
if (autoCompleteValue) {
|
|
21112
|
+
this.autoComplete(autoCompleteValue);
|
|
21113
|
+
return;
|
|
21114
|
+
}
|
|
21102
21115
|
}
|
|
21103
|
-
|
|
21104
|
-
|
|
21105
|
-
|
|
21106
|
-
|
|
21107
|
-
|
|
21108
|
-
|
|
21116
|
+
else {
|
|
21117
|
+
// when completing with tab, if there is no value to complete, the active cell will be moved to the right.
|
|
21118
|
+
// we can't let the model think that it is for a ref selection.
|
|
21119
|
+
// todo: check if this can be removed someday
|
|
21120
|
+
this.env.model.dispatch("STOP_COMPOSER_RANGE_SELECTION");
|
|
21121
|
+
}
|
|
21122
|
+
this.env.model.dispatch("STOP_EDITION");
|
|
21109
21123
|
}
|
|
21110
21124
|
const direction = ev.shiftKey ? "left" : "right";
|
|
21111
|
-
this.env.model.dispatch("STOP_EDITION");
|
|
21112
21125
|
this.env.model.selection.moveAnchorCell(direction, 1);
|
|
21113
21126
|
}
|
|
21114
21127
|
processEnterKey(ev) {
|
|
@@ -21152,6 +21165,9 @@ class Composer extends Component {
|
|
|
21152
21165
|
this.compositionActive = false;
|
|
21153
21166
|
}
|
|
21154
21167
|
onKeydown(ev) {
|
|
21168
|
+
if (this.env.model.getters.getEditionMode() === "inactive") {
|
|
21169
|
+
return;
|
|
21170
|
+
}
|
|
21155
21171
|
let handler = this.keyMapping[ev.key];
|
|
21156
21172
|
if (handler) {
|
|
21157
21173
|
handler.call(this, ev);
|
|
@@ -21162,22 +21178,44 @@ class Composer extends Component {
|
|
|
21162
21178
|
}
|
|
21163
21179
|
}
|
|
21164
21180
|
updateCursorIfNeeded() {
|
|
21165
|
-
|
|
21181
|
+
const moveCursor = !this.env.model.getters.isSelectingForComposer() &&
|
|
21182
|
+
!(this.env.model.getters.getEditionMode() === "inactive");
|
|
21183
|
+
if (moveCursor) {
|
|
21166
21184
|
const { start, end } = this.contentHelper.getCurrentSelection();
|
|
21167
21185
|
this.env.model.dispatch("CHANGE_COMPOSER_CURSOR_SELECTION", { start, end });
|
|
21168
21186
|
this.isKeyStillDown = true;
|
|
21169
21187
|
}
|
|
21170
21188
|
}
|
|
21189
|
+
onPaste(ev) {
|
|
21190
|
+
if (this.env.model.getters.getEditionMode() !== "inactive") {
|
|
21191
|
+
ev.stopPropagation();
|
|
21192
|
+
}
|
|
21193
|
+
}
|
|
21171
21194
|
/*
|
|
21172
21195
|
* Triggered automatically by the content-editable between the keydown and key up
|
|
21173
21196
|
* */
|
|
21174
|
-
onInput() {
|
|
21175
|
-
if (
|
|
21197
|
+
onInput(ev) {
|
|
21198
|
+
if (!this.shouldProcessInputEvents) {
|
|
21176
21199
|
return;
|
|
21177
21200
|
}
|
|
21201
|
+
if (ev.inputType === "insertFromPaste" &&
|
|
21202
|
+
this.env.model.getters.getEditionMode() === "inactive") {
|
|
21203
|
+
return;
|
|
21204
|
+
}
|
|
21205
|
+
ev.stopPropagation();
|
|
21206
|
+
let content;
|
|
21207
|
+
if (this.env.model.getters.getEditionMode() === "inactive") {
|
|
21208
|
+
content = ev.data || "";
|
|
21209
|
+
}
|
|
21210
|
+
else {
|
|
21211
|
+
content = this.contentHelper.getText();
|
|
21212
|
+
}
|
|
21213
|
+
if (this.props.focus === "inactive") {
|
|
21214
|
+
return this.props.onComposerCellFocused?.(content);
|
|
21215
|
+
}
|
|
21178
21216
|
this.env.model.dispatch("STOP_COMPOSER_RANGE_SELECTION");
|
|
21179
21217
|
this.env.model.dispatch("SET_CURRENT_CONTENT", {
|
|
21180
|
-
content
|
|
21218
|
+
content,
|
|
21181
21219
|
selection: this.contentHelper.getCurrentSelection(),
|
|
21182
21220
|
});
|
|
21183
21221
|
}
|
|
@@ -21248,9 +21286,8 @@ class Composer extends Component {
|
|
|
21248
21286
|
}
|
|
21249
21287
|
const newSelection = this.contentHelper.getCurrentSelection();
|
|
21250
21288
|
this.env.model.dispatch("STOP_COMPOSER_RANGE_SELECTION");
|
|
21251
|
-
|
|
21252
|
-
|
|
21253
|
-
}
|
|
21289
|
+
this.props.onComposerContentFocused();
|
|
21290
|
+
if (this.props.focus === "inactive") ;
|
|
21254
21291
|
this.env.model.dispatch("CHANGE_COMPOSER_CURSOR_SELECTION", newSelection);
|
|
21255
21292
|
this.processTokenAtCursor();
|
|
21256
21293
|
}
|
|
@@ -21394,7 +21431,7 @@ class Composer extends Component {
|
|
|
21394
21431
|
const highlights = this.env.model.getters.getHighlights();
|
|
21395
21432
|
const refSheet = sheetName
|
|
21396
21433
|
? this.env.model.getters.getSheetIdByName(sheetName)
|
|
21397
|
-
: this.env.model.getters.getCurrentEditedCell()
|
|
21434
|
+
: this.env.model.getters.getCurrentEditedCell()?.sheetId;
|
|
21398
21435
|
const highlight = highlights.find((highlight) => {
|
|
21399
21436
|
if (highlight.sheetId !== refSheet)
|
|
21400
21437
|
return false;
|
|
@@ -21471,12 +21508,13 @@ class Composer extends Component {
|
|
|
21471
21508
|
}
|
|
21472
21509
|
}
|
|
21473
21510
|
Composer.props = {
|
|
21474
|
-
focus: { validate: (value) => ["inactive", "cellFocus", "contentFocus"].includes(value) },
|
|
21475
|
-
onComposerContentFocused: Function,
|
|
21476
21511
|
inputStyle: { type: String, optional: true },
|
|
21477
21512
|
rect: { type: Object, optional: true },
|
|
21478
21513
|
delimitation: { type: Object, optional: true },
|
|
21479
|
-
|
|
21514
|
+
focus: { validate: (value) => ["inactive", "cellFocus", "contentFocus"].includes(value) },
|
|
21515
|
+
onComposerCellFocused: { type: Function, optional: true },
|
|
21516
|
+
onComposerContentFocused: Function,
|
|
21517
|
+
isDefaultFocus: { type: Boolean, optional: true },
|
|
21480
21518
|
};
|
|
21481
21519
|
|
|
21482
21520
|
const COMPOSER_BORDER_WIDTH = 3 * 0.4 * window.devicePixelRatio || 1;
|
|
@@ -21510,51 +21548,26 @@ css /* scss */ `
|
|
|
21510
21548
|
class GridComposer extends Component {
|
|
21511
21549
|
static template = "o-spreadsheet-GridComposer";
|
|
21512
21550
|
static components = { Composer };
|
|
21513
|
-
|
|
21514
|
-
|
|
21515
|
-
|
|
21551
|
+
// TODORAR see if we can keep it undefined
|
|
21552
|
+
rect = this.defaultRect;
|
|
21553
|
+
isEditing = false;
|
|
21516
21554
|
isCellReferenceVisible;
|
|
21517
|
-
|
|
21555
|
+
get defaultRect() {
|
|
21556
|
+
return { x: 0, y: 0, width: 0, height: 0 };
|
|
21557
|
+
}
|
|
21518
21558
|
setup() {
|
|
21519
|
-
this.gridComposerRef = useRef("gridComposer");
|
|
21520
|
-
this.composerState = useState({
|
|
21521
|
-
rect: undefined,
|
|
21522
|
-
delimitation: undefined,
|
|
21523
|
-
});
|
|
21524
|
-
const { sheetId, col, row } = this.env.model.getters.getActivePosition();
|
|
21525
|
-
this.zone = this.env.model.getters.expandZone(sheetId, positionToZone({ col, row }));
|
|
21526
|
-
this.rect = this.env.model.getters.getVisibleRect(this.zone);
|
|
21527
|
-
this.isCellReferenceVisible = false;
|
|
21528
|
-
onMounted(() => {
|
|
21529
|
-
const el = this.gridComposerRef.el;
|
|
21530
|
-
this.composerState.rect = {
|
|
21531
|
-
x: this.rect.x,
|
|
21532
|
-
y: this.rect.y,
|
|
21533
|
-
width: el.clientWidth,
|
|
21534
|
-
height: el.clientHeight,
|
|
21535
|
-
};
|
|
21536
|
-
this.composerState.delimitation = {
|
|
21537
|
-
width: el.parentElement.clientWidth,
|
|
21538
|
-
height: el.parentElement.clientHeight,
|
|
21539
|
-
};
|
|
21540
|
-
});
|
|
21541
21559
|
onWillUpdateProps(() => {
|
|
21542
|
-
|
|
21543
|
-
|
|
21544
|
-
}
|
|
21545
|
-
const sheetId = this.env.model.getters.getActiveSheetId();
|
|
21546
|
-
const zone = this.env.model.getters.getSelectedZone();
|
|
21547
|
-
const rect = this.env.model.getters.getVisibleRect(zone);
|
|
21548
|
-
if (!deepEquals(rect, this.rect) ||
|
|
21549
|
-
sheetId !== this.env.model.getters.getCurrentEditedCell().sheetId) {
|
|
21550
|
-
this.isCellReferenceVisible = true;
|
|
21551
|
-
}
|
|
21560
|
+
this.updateComponentPosition();
|
|
21561
|
+
this.updateCellReferenceVisibility();
|
|
21552
21562
|
});
|
|
21553
21563
|
}
|
|
21554
21564
|
get shouldDisplayCellReference() {
|
|
21555
21565
|
return this.isCellReferenceVisible;
|
|
21556
21566
|
}
|
|
21557
21567
|
get cellReference() {
|
|
21568
|
+
if (!this.env.model.getters.getCurrentEditedCell()) {
|
|
21569
|
+
return "";
|
|
21570
|
+
}
|
|
21558
21571
|
const { col, row, sheetId } = this.env.model.getters.getCurrentEditedCell();
|
|
21559
21572
|
const prefixSheet = sheetId !== this.env.model.getters.getActiveSheetId();
|
|
21560
21573
|
return `${prefixSheet ? getCanonicalSheetName(this.env.model.getters.getSheetName(sheetId)) + "!" : ""}${toXC(col, row)}`;
|
|
@@ -21566,7 +21579,27 @@ class GridComposer extends Component {
|
|
|
21566
21579
|
top: `${top - GRID_CELL_REFERENCE_TOP_OFFSET}px`,
|
|
21567
21580
|
});
|
|
21568
21581
|
}
|
|
21582
|
+
get composerProps() {
|
|
21583
|
+
const { width, height } = this.env.model.getters.getSheetViewDimensionWithHeaders();
|
|
21584
|
+
return {
|
|
21585
|
+
rect: { ...this.rect },
|
|
21586
|
+
delimitation: {
|
|
21587
|
+
width,
|
|
21588
|
+
height,
|
|
21589
|
+
},
|
|
21590
|
+
focus: this.props.focus,
|
|
21591
|
+
isDefaultFocus: true,
|
|
21592
|
+
onComposerContentFocused: this.props.onComposerContentFocused,
|
|
21593
|
+
onComposerCellFocused: this.props.onComposerCellFocused,
|
|
21594
|
+
};
|
|
21595
|
+
}
|
|
21569
21596
|
get containerStyle() {
|
|
21597
|
+
if (this.env.model.getters.getEditionMode() === "inactive" || !this.rect) {
|
|
21598
|
+
return `
|
|
21599
|
+
position: absolute;
|
|
21600
|
+
z-index: -1000;
|
|
21601
|
+
`;
|
|
21602
|
+
}
|
|
21570
21603
|
const isFormula = this.env.model.getters.getCurrentContent().startsWith("=");
|
|
21571
21604
|
const cell = this.env.model.getters.getActiveCell();
|
|
21572
21605
|
const position = this.env.model.getters.getActivePosition();
|
|
@@ -21586,6 +21619,8 @@ class GridComposer extends Component {
|
|
|
21586
21619
|
if (!isFormula) {
|
|
21587
21620
|
textAlign = style.align || cell.defaultAlign;
|
|
21588
21621
|
}
|
|
21622
|
+
const maxHeight = this.props.gridDims.height - this.rect.y;
|
|
21623
|
+
const maxWidth = this.props.gridDims.width - this.rect.x;
|
|
21589
21624
|
/**
|
|
21590
21625
|
* min-size is on the container, not the composer element, because we want to have the same size as the cell by default,
|
|
21591
21626
|
* including all the paddings/margins of the composer
|
|
@@ -21597,6 +21632,8 @@ class GridComposer extends Component {
|
|
|
21597
21632
|
top: `${top}px`,
|
|
21598
21633
|
"min-width": `${width + 1}px`,
|
|
21599
21634
|
"min-height": `${height + 1}px`,
|
|
21635
|
+
"max-width": `${maxWidth}px`,
|
|
21636
|
+
"max-height": `${maxHeight}px`,
|
|
21600
21637
|
background,
|
|
21601
21638
|
color,
|
|
21602
21639
|
"font-size": `${fontSizeInPixels(fontSize)}px`,
|
|
@@ -21606,20 +21643,38 @@ class GridComposer extends Component {
|
|
|
21606
21643
|
"text-align": textAlign,
|
|
21607
21644
|
});
|
|
21608
21645
|
}
|
|
21609
|
-
|
|
21610
|
-
const
|
|
21611
|
-
|
|
21612
|
-
|
|
21613
|
-
|
|
21614
|
-
|
|
21615
|
-
|
|
21646
|
+
updateComponentPosition() {
|
|
21647
|
+
const isEditing = this.env.model.getters.getEditionMode() !== "inactive";
|
|
21648
|
+
if (this.isEditing !== isEditing) {
|
|
21649
|
+
this.isEditing = isEditing;
|
|
21650
|
+
if (!isEditing) {
|
|
21651
|
+
this.rect = this.defaultRect;
|
|
21652
|
+
this.env.focusableElement.focus();
|
|
21653
|
+
return;
|
|
21654
|
+
}
|
|
21655
|
+
const position = this.env.model.getters.getActivePosition();
|
|
21656
|
+
const zone = this.env.model.getters.expandZone(position.sheetId, positionToZone(position));
|
|
21657
|
+
this.rect = this.env.model.getters.getVisibleRect(zone);
|
|
21658
|
+
}
|
|
21659
|
+
}
|
|
21660
|
+
updateCellReferenceVisibility() {
|
|
21661
|
+
if (this.isCellReferenceVisible || this.env.model.getters.getEditionMode() === "inactive") {
|
|
21662
|
+
return;
|
|
21663
|
+
}
|
|
21664
|
+
const sheetId = this.env.model.getters.getActiveSheetId();
|
|
21665
|
+
const zone = this.env.model.getters.getSelectedZone();
|
|
21666
|
+
const rect = this.env.model.getters.getVisibleRect(zone);
|
|
21667
|
+
if (!deepEquals(rect, this.rect) ||
|
|
21668
|
+
sheetId !== this.env.model.getters.getCurrentEditedCell().sheetId) {
|
|
21669
|
+
this.isCellReferenceVisible = true;
|
|
21670
|
+
}
|
|
21616
21671
|
}
|
|
21617
21672
|
}
|
|
21618
21673
|
GridComposer.props = {
|
|
21619
21674
|
focus: { validate: (value) => ["inactive", "cellFocus", "contentFocus"].includes(value) },
|
|
21620
|
-
onComposerUnmounted: Function,
|
|
21621
21675
|
onComposerContentFocused: Function,
|
|
21622
21676
|
gridDims: Object,
|
|
21677
|
+
onComposerCellFocused: Function,
|
|
21623
21678
|
};
|
|
21624
21679
|
|
|
21625
21680
|
const CSS$1 = css /* scss */ `
|
|
@@ -22488,7 +22543,6 @@ class GridOverlay extends Component {
|
|
|
22488
22543
|
this.props.onCellDoubleClicked(col, row);
|
|
22489
22544
|
}
|
|
22490
22545
|
onContextMenu(ev) {
|
|
22491
|
-
ev.preventDefault();
|
|
22492
22546
|
const [col, row] = this.getCartesianCoordinates(ev);
|
|
22493
22547
|
this.props.onCellRightClicked(col, row, { x: ev.clientX, y: ev.clientY });
|
|
22494
22548
|
}
|
|
@@ -23582,7 +23636,6 @@ class Grid extends Component {
|
|
|
23582
23636
|
HEADER_WIDTH = HEADER_WIDTH;
|
|
23583
23637
|
menuState;
|
|
23584
23638
|
gridRef;
|
|
23585
|
-
hiddenInput;
|
|
23586
23639
|
onMouseWheel;
|
|
23587
23640
|
canvasPosition;
|
|
23588
23641
|
hoveredCell;
|
|
@@ -23593,17 +23646,16 @@ class Grid extends Component {
|
|
|
23593
23646
|
menuItems: [],
|
|
23594
23647
|
});
|
|
23595
23648
|
this.gridRef = useRef("grid");
|
|
23596
|
-
this.hiddenInput = useRef("hiddenInput");
|
|
23597
23649
|
this.canvasPosition = useAbsoluteBoundingRect(this.gridRef);
|
|
23598
23650
|
this.hoveredCell = useState({ col: undefined, row: undefined });
|
|
23599
23651
|
useChildSubEnv({ getPopoverContainerRect: () => this.getGridRect() });
|
|
23600
23652
|
useExternalListener(document.body, "cut", this.copy.bind(this, true));
|
|
23601
23653
|
useExternalListener(document.body, "copy", this.copy.bind(this, false));
|
|
23602
23654
|
useExternalListener(document.body, "paste", this.paste);
|
|
23603
|
-
onMounted(() => this.
|
|
23604
|
-
this.props.exposeFocus(() => this.
|
|
23655
|
+
onMounted(() => this.focusDefaultElement());
|
|
23656
|
+
this.props.exposeFocus(() => this.focusDefaultElement());
|
|
23605
23657
|
useGridDrawing("canvas", this.env.model, () => this.env.model.getters.getSheetViewDimensionWithHeaders());
|
|
23606
|
-
useEffect(() => this.
|
|
23658
|
+
useEffect(() => this.focusDefaultElement(), () => [this.env.model.getters.getActiveSheetId()]);
|
|
23607
23659
|
this.onMouseWheel = useWheelHandler((deltaX, deltaY) => {
|
|
23608
23660
|
this.moveCanvas(deltaX, deltaY);
|
|
23609
23661
|
this.hoveredCell.col = undefined;
|
|
@@ -23626,7 +23678,7 @@ class Grid extends Component {
|
|
|
23626
23678
|
if (this.env.model.getters.hasOpenedPopover()) {
|
|
23627
23679
|
this.closeOpenedPopover();
|
|
23628
23680
|
}
|
|
23629
|
-
this.
|
|
23681
|
+
this.focusDefaultElement();
|
|
23630
23682
|
}
|
|
23631
23683
|
// this map will handle most of the actions that should happen on key down. The arrow keys are managed in the key
|
|
23632
23684
|
// down itself
|
|
@@ -23796,10 +23848,10 @@ class Grid extends Component {
|
|
|
23796
23848
|
PAGEUP: () => this.env.model.dispatch("SHIFT_VIEWPORT_UP"),
|
|
23797
23849
|
"CTRL+K": () => INSERT_LINK(this.env),
|
|
23798
23850
|
};
|
|
23799
|
-
|
|
23851
|
+
focusDefaultElement() {
|
|
23800
23852
|
if (!this.env.model.getters.getSelectedFigureId() &&
|
|
23801
23853
|
this.env.model.getters.getEditionMode() === "inactive") {
|
|
23802
|
-
this.
|
|
23854
|
+
this.env.focusableElement.focus();
|
|
23803
23855
|
}
|
|
23804
23856
|
}
|
|
23805
23857
|
get gridEl() {
|
|
@@ -23943,19 +23995,6 @@ class Grid extends Component {
|
|
|
23943
23995
|
return;
|
|
23944
23996
|
}
|
|
23945
23997
|
}
|
|
23946
|
-
onInput(ev) {
|
|
23947
|
-
// the user meant to paste in the sheet, not open the composer with the pasted content
|
|
23948
|
-
if (!ev.isComposing && ev.inputType === "insertFromPaste") {
|
|
23949
|
-
return;
|
|
23950
|
-
}
|
|
23951
|
-
if (ev.data) {
|
|
23952
|
-
// if the user types a character on the grid, it means he wants to start composing the selected cell with that
|
|
23953
|
-
// character
|
|
23954
|
-
ev.preventDefault();
|
|
23955
|
-
ev.stopPropagation();
|
|
23956
|
-
this.props.onGridComposerCellFocused(ev.data);
|
|
23957
|
-
}
|
|
23958
|
-
}
|
|
23959
23998
|
// ---------------------------------------------------------------------------
|
|
23960
23999
|
// Context Menu
|
|
23961
24000
|
// ---------------------------------------------------------------------------
|
|
@@ -24065,7 +24104,7 @@ class Grid extends Component {
|
|
|
24065
24104
|
}
|
|
24066
24105
|
closeMenu() {
|
|
24067
24106
|
this.menuState.isOpen = false;
|
|
24068
|
-
this.
|
|
24107
|
+
this.focusDefaultElement();
|
|
24069
24108
|
}
|
|
24070
24109
|
}
|
|
24071
24110
|
Grid.props = {
|
|
@@ -28725,6 +28764,31 @@ const MIGRATIONS = [
|
|
|
28725
28764
|
return data;
|
|
28726
28765
|
},
|
|
28727
28766
|
},
|
|
28767
|
+
{
|
|
28768
|
+
description: "Fix datafilter duplication",
|
|
28769
|
+
from: 12,
|
|
28770
|
+
to: 12.5,
|
|
28771
|
+
applyMigration(data) {
|
|
28772
|
+
for (let sheet of data.sheets || []) {
|
|
28773
|
+
let knownDataFilterZones = [];
|
|
28774
|
+
for (let filterTable of sheet.filterTables || []) {
|
|
28775
|
+
const zone = toZone(filterTable.range);
|
|
28776
|
+
// See commit message for the details
|
|
28777
|
+
const intersectZoneIndex = knownDataFilterZones.findIndex((knownZone) => overlap(knownZone, zone));
|
|
28778
|
+
if (intersectZoneIndex !== -1) {
|
|
28779
|
+
knownDataFilterZones[intersectZoneIndex] = zone;
|
|
28780
|
+
}
|
|
28781
|
+
else {
|
|
28782
|
+
knownDataFilterZones.push(zone);
|
|
28783
|
+
}
|
|
28784
|
+
}
|
|
28785
|
+
sheet.filterTables = knownDataFilterZones.map((zone) => ({
|
|
28786
|
+
range: zoneToXc(zone),
|
|
28787
|
+
}));
|
|
28788
|
+
}
|
|
28789
|
+
return data;
|
|
28790
|
+
},
|
|
28791
|
+
},
|
|
28728
28792
|
{
|
|
28729
28793
|
description: "Change Border description structure",
|
|
28730
28794
|
from: 12,
|
|
@@ -29626,9 +29690,9 @@ class CellPlugin extends CorePlugin {
|
|
|
29626
29690
|
allowDispatch(cmd) {
|
|
29627
29691
|
switch (cmd.type) {
|
|
29628
29692
|
case "UPDATE_CELL":
|
|
29629
|
-
return this.
|
|
29693
|
+
return this.checkValidations(cmd, this.checkCellOutOfSheet, this.checkUselessUpdateCell);
|
|
29630
29694
|
case "CLEAR_CELL":
|
|
29631
|
-
return this.checkValidations(cmd, this.
|
|
29695
|
+
return this.checkValidations(cmd, this.checkCellOutOfSheet, this.checkUselessClearCell);
|
|
29632
29696
|
default:
|
|
29633
29697
|
return 0 /* CommandResult.Success */;
|
|
29634
29698
|
}
|
|
@@ -29760,10 +29824,6 @@ class CellPlugin extends CorePlugin {
|
|
|
29760
29824
|
format: cell.format ? getItemId(cell.format, formats) : undefined,
|
|
29761
29825
|
content: cell.content || undefined,
|
|
29762
29826
|
};
|
|
29763
|
-
// if there is a formula but no dependencies (maybe because the cell is in error), no need to recompute the formula text
|
|
29764
|
-
if (cell.isFormula && cell.dependencies.length) {
|
|
29765
|
-
cells[xc].content = this.buildFormulaContent(_sheet.id, cell, cell.dependencies, true);
|
|
29766
|
-
}
|
|
29767
29827
|
}
|
|
29768
29828
|
_sheet.cells = cells;
|
|
29769
29829
|
}
|
|
@@ -29926,7 +29986,7 @@ class CellPlugin extends CorePlugin {
|
|
|
29926
29986
|
else {
|
|
29927
29987
|
style = before ? before.style : undefined;
|
|
29928
29988
|
}
|
|
29929
|
-
|
|
29989
|
+
const format = "format" in after ? after.format : before && before.format;
|
|
29930
29990
|
/* Read the following IF as:
|
|
29931
29991
|
* we need to remove the cell if it is completely empty, but we can know if it completely empty if:
|
|
29932
29992
|
* - the command says the new content is empty and has no border/format/style
|
|
@@ -29970,7 +30030,7 @@ class CellPlugin extends CorePlugin {
|
|
|
29970
30030
|
id,
|
|
29971
30031
|
content,
|
|
29972
30032
|
style,
|
|
29973
|
-
format,
|
|
30033
|
+
format: format || detectFormat(content),
|
|
29974
30034
|
isFormula: false,
|
|
29975
30035
|
};
|
|
29976
30036
|
}
|
|
@@ -30047,6 +30107,18 @@ class CellPlugin extends CorePlugin {
|
|
|
30047
30107
|
}
|
|
30048
30108
|
return 0 /* CommandResult.Success */;
|
|
30049
30109
|
}
|
|
30110
|
+
checkUselessUpdateCell(cmd) {
|
|
30111
|
+
const cell = this.getters.getCell(cmd);
|
|
30112
|
+
const hasContent = "content" in cmd || "formula" in cmd;
|
|
30113
|
+
const hasStyle = "style" in cmd;
|
|
30114
|
+
const hasFormat = "format" in cmd;
|
|
30115
|
+
if ((!hasContent || cell?.content === cmd.content) &&
|
|
30116
|
+
(!hasStyle || deepEquals(cell?.style, cmd.style)) &&
|
|
30117
|
+
(!hasFormat || cell?.format === cmd.format)) {
|
|
30118
|
+
return 94 /* CommandResult.NoChanges */;
|
|
30119
|
+
}
|
|
30120
|
+
return 0 /* CommandResult.Success */;
|
|
30121
|
+
}
|
|
30050
30122
|
}
|
|
30051
30123
|
|
|
30052
30124
|
class ChartPlugin extends CorePlugin {
|
|
@@ -33838,6 +33910,10 @@ class EvaluationPlugin extends UIPlugin {
|
|
|
33838
33910
|
if (cell.format !== evaluatedCell.format) {
|
|
33839
33911
|
exportedCellData.computedFormat = evaluatedCell.format;
|
|
33840
33912
|
}
|
|
33913
|
+
// if there is a formula but no dependencies (maybe because the cell is in error), no need to recompute the formula text
|
|
33914
|
+
if (cell.isFormula && cell.dependencies.length) {
|
|
33915
|
+
exportedCellData.content = this.getters.buildFormulaContent(sheet.id, cell, cell.dependencies, true);
|
|
33916
|
+
}
|
|
33841
33917
|
}
|
|
33842
33918
|
}
|
|
33843
33919
|
}
|
|
@@ -35706,8 +35782,7 @@ class AutofillPlugin extends UIPlugin {
|
|
|
35706
35782
|
let row = zone.bottom;
|
|
35707
35783
|
if (col > 0) {
|
|
35708
35784
|
let leftPosition = { sheetId, col: col - 1, row };
|
|
35709
|
-
while (this.getters.
|
|
35710
|
-
this.getters.getCell(leftPosition)?.content) {
|
|
35785
|
+
while (this.getters.getCell(leftPosition)?.content) {
|
|
35711
35786
|
row += 1;
|
|
35712
35787
|
leftPosition = { sheetId, col: col - 1, row };
|
|
35713
35788
|
}
|
|
@@ -35716,8 +35791,7 @@ class AutofillPlugin extends UIPlugin {
|
|
|
35716
35791
|
col = zone.right;
|
|
35717
35792
|
if (col <= this.getters.getNumberCols(sheetId)) {
|
|
35718
35793
|
let rightPosition = { sheetId, col: col + 1, row };
|
|
35719
|
-
while (this.getters.
|
|
35720
|
-
this.getters.getCell(rightPosition)?.content) {
|
|
35794
|
+
while (this.getters.getCell(rightPosition)?.content) {
|
|
35721
35795
|
row += 1;
|
|
35722
35796
|
rightPosition = { sheetId, col: col + 1, row };
|
|
35723
35797
|
}
|
|
@@ -41182,7 +41256,7 @@ class EditionPlugin extends UIPlugin {
|
|
|
41182
41256
|
getZoneReference(zone, fixedParts = [{ colFixed: false, rowFixed: false }]) {
|
|
41183
41257
|
const sheetId = this.getters.getActiveSheetId();
|
|
41184
41258
|
let selectedXc = this.getters.zoneToXC(sheetId, zone, fixedParts);
|
|
41185
|
-
if (this.getters.getCurrentEditedCell()
|
|
41259
|
+
if (this.getters.getCurrentEditedCell()?.sheetId !== this.getters.getActiveSheetId()) {
|
|
41186
41260
|
const sheetName = getCanonicalSheetName(this.getters.getSheetName(this.getters.getActiveSheetId()));
|
|
41187
41261
|
selectedXc = `${sheetName}!${selectedXc}`;
|
|
41188
41262
|
}
|
|
@@ -41197,7 +41271,7 @@ class EditionPlugin extends UIPlugin {
|
|
|
41197
41271
|
_fixedParts.pop();
|
|
41198
41272
|
}
|
|
41199
41273
|
const newRange = range.clone({ parts: _fixedParts });
|
|
41200
|
-
return this.getters.getSelectionRangeString(newRange, this.getters.getCurrentEditedCell().
|
|
41274
|
+
return this.getters.getSelectionRangeString(newRange, this.getters.getCurrentEditedCell()?.sheetId || this.getters.getActiveSheetId());
|
|
41201
41275
|
}
|
|
41202
41276
|
/**
|
|
41203
41277
|
* Replace the current selection by a new text.
|
|
@@ -41230,7 +41304,7 @@ class EditionPlugin extends UIPlugin {
|
|
|
41230
41304
|
if (!this.currentContent.startsWith("=") || this.mode === "inactive") {
|
|
41231
41305
|
return;
|
|
41232
41306
|
}
|
|
41233
|
-
const editionSheetId = this.getters.getCurrentEditedCell().
|
|
41307
|
+
const editionSheetId = this.getters.getCurrentEditedCell()?.sheetId || this.getters.getActiveSheetId();
|
|
41234
41308
|
const XCs = this.getReferencedRanges().map((range) => this.getters.getRangeString(range, editionSheetId));
|
|
41235
41309
|
const colorsToKeep = {};
|
|
41236
41310
|
for (const xc of XCs) {
|
|
@@ -41259,7 +41333,7 @@ class EditionPlugin extends UIPlugin {
|
|
|
41259
41333
|
if (!this.currentContent.startsWith("=") || this.mode === "inactive") {
|
|
41260
41334
|
return [];
|
|
41261
41335
|
}
|
|
41262
|
-
const editionSheetId = this.getters.getCurrentEditedCell().
|
|
41336
|
+
const editionSheetId = this.getters.getCurrentEditedCell()?.sheetId || this.getters.getActiveSheetId();
|
|
41263
41337
|
const rangeColor = (rangeString) => {
|
|
41264
41338
|
const colorIndex = this.colorIndexByRange[rangeString];
|
|
41265
41339
|
return colors$1[colorIndex % colors$1.length];
|
|
@@ -41277,7 +41351,7 @@ class EditionPlugin extends UIPlugin {
|
|
|
41277
41351
|
* Return ranges currently referenced in the composer
|
|
41278
41352
|
*/
|
|
41279
41353
|
getReferencedRanges() {
|
|
41280
|
-
const editionSheetId = this.getters.getCurrentEditedCell().
|
|
41354
|
+
const editionSheetId = this.getters.getCurrentEditedCell()?.sheetId || this.getters.getActiveSheetId();
|
|
41281
41355
|
const referenceRanges = this.currentTokens
|
|
41282
41356
|
.filter((token) => token.type === "REFERENCE")
|
|
41283
41357
|
.map((token) => this.getters.getRangeFromSheetXC(editionSheetId, token.value));
|
|
@@ -42088,6 +42162,16 @@ class ImageProvider {
|
|
|
42088
42162
|
}
|
|
42089
42163
|
}
|
|
42090
42164
|
|
|
42165
|
+
class FocusableElement {
|
|
42166
|
+
focusableElement = undefined;
|
|
42167
|
+
setFocusableElement(element) {
|
|
42168
|
+
this.focusableElement = element;
|
|
42169
|
+
}
|
|
42170
|
+
focus() {
|
|
42171
|
+
this.focusableElement?.focus();
|
|
42172
|
+
}
|
|
42173
|
+
}
|
|
42174
|
+
|
|
42091
42175
|
const RIPPLE_KEY_FRAMES = [
|
|
42092
42176
|
{ transform: "scale(0)" },
|
|
42093
42177
|
{ transform: "scale(0.8)", offset: 0.33 },
|
|
@@ -44116,6 +44200,7 @@ class Spreadsheet extends Component {
|
|
|
44116
44200
|
_t: Spreadsheet._t,
|
|
44117
44201
|
clipboard: this.env.clipboard || instantiateClipboard(),
|
|
44118
44202
|
startCellEdition: (content) => this.onGridComposerCellFocused(content),
|
|
44203
|
+
focusableElement: new FocusableElement(),
|
|
44119
44204
|
});
|
|
44120
44205
|
useExternalListener(window, "resize", () => this.render(true));
|
|
44121
44206
|
useExternalListener(window, "beforeunload", this.unbindModelEvents.bind(this));
|
|
@@ -44222,17 +44307,18 @@ class Spreadsheet extends Component {
|
|
|
44222
44307
|
this.model.dispatch("UNFOCUS_SELECTION_INPUT");
|
|
44223
44308
|
this.composer.topBarFocus = "contentFocus";
|
|
44224
44309
|
this.composer.gridFocusMode = "inactive";
|
|
44225
|
-
this.setComposerContent({ selection }
|
|
44310
|
+
this.setComposerContent({ selection });
|
|
44226
44311
|
}
|
|
44227
|
-
onGridComposerContentFocused() {
|
|
44312
|
+
onGridComposerContentFocused(selection) {
|
|
44228
44313
|
if (this.model.getters.isReadonly()) {
|
|
44229
44314
|
return;
|
|
44230
44315
|
}
|
|
44231
44316
|
this.model.dispatch("UNFOCUS_SELECTION_INPUT");
|
|
44232
44317
|
this.composer.topBarFocus = "inactive";
|
|
44233
44318
|
this.composer.gridFocusMode = "contentFocus";
|
|
44234
|
-
this.setComposerContent({});
|
|
44319
|
+
this.setComposerContent({ selection });
|
|
44235
44320
|
}
|
|
44321
|
+
// TODO: either both are defined or none of them. change those args to an object
|
|
44236
44322
|
onGridComposerCellFocused(content, selection) {
|
|
44237
44323
|
if (this.model.getters.isReadonly()) {
|
|
44238
44324
|
return;
|
|
@@ -45630,11 +45716,10 @@ class SelectionStreamProcessorImpl {
|
|
|
45630
45716
|
* next cluster if the given cell is outside a cluster or at the border of a cluster in the given direction.
|
|
45631
45717
|
*/
|
|
45632
45718
|
getEndOfCluster(startPosition, dim, dir) {
|
|
45633
|
-
const sheet = this.getters.getActiveSheet();
|
|
45634
45719
|
let currentPosition = startPosition;
|
|
45635
45720
|
// If both the current cell and the next cell are not empty, we want to go to the end of the cluster
|
|
45636
45721
|
const nextCellPosition = this.getNextCellPosition(startPosition, dim, dir);
|
|
45637
|
-
let mode = !this.
|
|
45722
|
+
let mode = !this.isEvaluatedCellEmpty(currentPosition) && !this.isEvaluatedCellEmpty(nextCellPosition)
|
|
45638
45723
|
? "endOfCluster"
|
|
45639
45724
|
: "nextCluster";
|
|
45640
45725
|
while (true) {
|
|
@@ -45644,7 +45729,7 @@ class SelectionStreamProcessorImpl {
|
|
|
45644
45729
|
currentPosition.row === nextCellPosition.row) {
|
|
45645
45730
|
break;
|
|
45646
45731
|
}
|
|
45647
|
-
const isNextCellEmpty = this.
|
|
45732
|
+
const isNextCellEmpty = this.isEvaluatedCellEmpty(nextCellPosition);
|
|
45648
45733
|
if (mode === "endOfCluster" && isNextCellEmpty) {
|
|
45649
45734
|
break;
|
|
45650
45735
|
}
|
|
@@ -45658,14 +45743,24 @@ class SelectionStreamProcessorImpl {
|
|
|
45658
45743
|
return dim === "cols" ? currentPosition.col : currentPosition.row;
|
|
45659
45744
|
}
|
|
45660
45745
|
/**
|
|
45661
|
-
* Check if a cell is empty
|
|
45662
|
-
* check
|
|
45746
|
+
* Check if a cell evaluated value is empty. If the cell is part of a merge,
|
|
45747
|
+
* the check applies to the main cell of the merge.
|
|
45663
45748
|
*/
|
|
45664
|
-
|
|
45749
|
+
isEvaluatedCellEmpty({ col, row }) {
|
|
45750
|
+
const sheetId = this.getters.getActiveSheetId();
|
|
45665
45751
|
const position = this.getters.getMainCellPosition({ sheetId, col, row });
|
|
45666
45752
|
const cell = this.getters.getEvaluatedCell(position);
|
|
45667
45753
|
return cell.type === CellValueType.empty;
|
|
45668
45754
|
}
|
|
45755
|
+
/**
|
|
45756
|
+
* Checks if a cell is empty (i.e. does not have a content). If the cell is part of a merge,
|
|
45757
|
+
* the check applies to the main cell of the merge.
|
|
45758
|
+
*/
|
|
45759
|
+
isCellEmpty({ col, row }) {
|
|
45760
|
+
const sheetId = this.getters.getActiveSheetId();
|
|
45761
|
+
const position = this.getters.getMainCellPosition({ sheetId, col, row });
|
|
45762
|
+
return !this.getters.getCell(position)?.content;
|
|
45763
|
+
}
|
|
45669
45764
|
/** Computes the next cell position in the given direction by crossing through merges and skipping hidden cells.
|
|
45670
45765
|
*
|
|
45671
45766
|
* This has the same behaviour as getNextAvailablePosition() for certain arguments, but use this method instead
|
|
@@ -47906,6 +48001,6 @@ function addFunction(functionName, functionDescription) {
|
|
|
47906
48001
|
export { AbstractChart, CommandResult, CorePlugin, DispatchResult, EvaluationError, Model, Registry, Revision, SPREADSHEET_DIMENSIONS, Spreadsheet, UIPlugin, __info__, addFunction, astToFormula, compile, components, convertAstNodes, coreTypes, findCellInNewZone, functionCache, helpers, invalidateEvaluationCommands, links, load, parse, readonlyAllowedCommands, registries, setDefaultSheetViewSize, setTranslationMethod, tokenize };
|
|
47907
48002
|
|
|
47908
48003
|
|
|
47909
|
-
__info__.version = '16.3.
|
|
47910
|
-
__info__.date = '2024-02-
|
|
47911
|
-
__info__.hash = '
|
|
48004
|
+
__info__.version = '16.3.26';
|
|
48005
|
+
__info__.date = '2024-02-16T14:22:11.270Z';
|
|
48006
|
+
__info__.hash = 'ab9abb6';
|