@odoo/o-spreadsheet 16.3.0-alpha.4 → 16.3.0-alpha.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 +543 -176
- package/dist/o-spreadsheet.d.ts +535 -515
- package/dist/o-spreadsheet.esm.js +544 -177
- package/dist/o-spreadsheet.iife.js +543 -176
- package/dist/o-spreadsheet.iife.min.js +242 -242
- package/package.json +1 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Component, useRef, onMounted, onPatched, onWillPatch, useState, onWillUpdateProps, useComponent, useEffect, useExternalListener, onWillUnmount, onWillStart, xml, useChildSubEnv, useSubEnv,
|
|
1
|
+
import { Component, useRef, onMounted, onPatched, onWillPatch, useState, onWillUpdateProps, useComponent, useEffect, useExternalListener, onWillUnmount, onWillStart, xml, useChildSubEnv, useSubEnv, markRaw } from '@odoo/owl';
|
|
2
2
|
|
|
3
3
|
/*
|
|
4
4
|
* usage: every string should be translated either with _lt if they are registered with a registry at
|
|
@@ -1720,12 +1720,77 @@ const numberRepresentation = [];
|
|
|
1720
1720
|
* - all digit stored in the decimal part of the number
|
|
1721
1721
|
*
|
|
1722
1722
|
* The 'maxDecimal' parameter allows to indicate the number of digits to not
|
|
1723
|
-
* exceed in the decimal part, in which case digits are rounded
|
|
1723
|
+
* exceed in the decimal part, in which case digits are rounded.
|
|
1724
1724
|
*
|
|
1725
|
-
* Intl.Numberformat is used to properly handle all the roundings.
|
|
1726
|
-
* e.g. 1234.7 with format ### (<> maxDecimals=0) should become 1235, not 1234
|
|
1727
1725
|
**/
|
|
1728
1726
|
function splitNumber(value, maxDecimals = MAX_DECIMAL_PLACES) {
|
|
1727
|
+
const asString = value.toString();
|
|
1728
|
+
if (asString.includes("e"))
|
|
1729
|
+
return splitNumberIntl(value, maxDecimals);
|
|
1730
|
+
if (Number.isInteger(value)) {
|
|
1731
|
+
return { integerDigits: asString, decimalDigits: undefined };
|
|
1732
|
+
}
|
|
1733
|
+
const indexOfDot = asString.indexOf(".");
|
|
1734
|
+
let integerDigits = asString.substring(0, indexOfDot);
|
|
1735
|
+
let decimalDigits = asString.substring(indexOfDot + 1);
|
|
1736
|
+
if (maxDecimals === 0) {
|
|
1737
|
+
if (Number(decimalDigits[0]) >= 5) {
|
|
1738
|
+
integerDigits = (Number(integerDigits) + 1).toString();
|
|
1739
|
+
}
|
|
1740
|
+
return { integerDigits, decimalDigits: undefined };
|
|
1741
|
+
}
|
|
1742
|
+
if (decimalDigits.length > maxDecimals) {
|
|
1743
|
+
const { integerDigits: roundedIntegerDigits, decimalDigits: roundedDecimalDigits } = limitDecimalDigits(decimalDigits, maxDecimals);
|
|
1744
|
+
decimalDigits = roundedDecimalDigits;
|
|
1745
|
+
if (roundedIntegerDigits !== "0") {
|
|
1746
|
+
integerDigits = (Number(integerDigits) + Number(roundedIntegerDigits)).toString();
|
|
1747
|
+
}
|
|
1748
|
+
}
|
|
1749
|
+
return { integerDigits, decimalDigits: removeTrailingZeroes(decimalDigits || "") };
|
|
1750
|
+
}
|
|
1751
|
+
/**
|
|
1752
|
+
* Return the given string minus the trailing "0" characters.
|
|
1753
|
+
*
|
|
1754
|
+
* @param numberString : a string of integers
|
|
1755
|
+
* @returns the numberString, minus the eventual zeroes at the end
|
|
1756
|
+
*/
|
|
1757
|
+
function removeTrailingZeroes(numberString) {
|
|
1758
|
+
let i = numberString.length - 1;
|
|
1759
|
+
while (i >= 0 && numberString[i] === "0") {
|
|
1760
|
+
i--;
|
|
1761
|
+
}
|
|
1762
|
+
return numberString.slice(0, i + 1) || undefined;
|
|
1763
|
+
}
|
|
1764
|
+
/**
|
|
1765
|
+
* Limit the size of the decimal part of a number to the given number of digits.
|
|
1766
|
+
*/
|
|
1767
|
+
function limitDecimalDigits(decimalDigits, maxDecimals) {
|
|
1768
|
+
let integerDigits = "0";
|
|
1769
|
+
let resultDecimalDigits = decimalDigits;
|
|
1770
|
+
// Note : we'd want to simply use number.toFixed() to handle the max digits & rounding,
|
|
1771
|
+
// but it has very strange behaviour. Ex: 12.345.toFixed(2) => "12.35", but 1.345.toFixed(2) => "1.34"
|
|
1772
|
+
let slicedDecimalDigits = decimalDigits.slice(0, maxDecimals);
|
|
1773
|
+
const i = maxDecimals;
|
|
1774
|
+
if (Number(Number(decimalDigits[i]) < 5)) {
|
|
1775
|
+
return { integerDigits, decimalDigits: slicedDecimalDigits };
|
|
1776
|
+
}
|
|
1777
|
+
// round up
|
|
1778
|
+
const slicedRoundedUp = (Number(slicedDecimalDigits) + 1).toString();
|
|
1779
|
+
if (slicedRoundedUp.length > slicedDecimalDigits.length) {
|
|
1780
|
+
integerDigits = (Number(integerDigits) + 1).toString();
|
|
1781
|
+
resultDecimalDigits = undefined;
|
|
1782
|
+
}
|
|
1783
|
+
else {
|
|
1784
|
+
resultDecimalDigits = slicedRoundedUp;
|
|
1785
|
+
}
|
|
1786
|
+
return { integerDigits, decimalDigits: resultDecimalDigits };
|
|
1787
|
+
}
|
|
1788
|
+
/**
|
|
1789
|
+
* Split numbers into decimal/integer digits using Intl.NumberFormat.
|
|
1790
|
+
* Supports numbers with a lot of digits that are transformed to scientific notation by
|
|
1791
|
+
* number.toString(), but is slow.
|
|
1792
|
+
*/
|
|
1793
|
+
function splitNumberIntl(value, maxDecimals = MAX_DECIMAL_PLACES) {
|
|
1729
1794
|
let formatter = numberRepresentation[maxDecimals];
|
|
1730
1795
|
if (!formatter) {
|
|
1731
1796
|
formatter = new Intl.NumberFormat("en-US", {
|
|
@@ -5760,12 +5825,12 @@ const hideSheet = {
|
|
|
5760
5825
|
function createActions(menuItems) {
|
|
5761
5826
|
return menuItems.map(createAction).sort((a, b) => a.sequence - b.sequence);
|
|
5762
5827
|
}
|
|
5763
|
-
const uuidGenerator$
|
|
5828
|
+
const uuidGenerator$2 = new UuidGenerator();
|
|
5764
5829
|
function createAction(item) {
|
|
5765
5830
|
const name = item.name;
|
|
5766
5831
|
const children = item.children;
|
|
5767
5832
|
return {
|
|
5768
|
-
id: item.id || uuidGenerator$
|
|
5833
|
+
id: item.id || uuidGenerator$2.uuidv4(),
|
|
5769
5834
|
name: typeof name === "function" ? name : () => name,
|
|
5770
5835
|
isVisible: item.isVisible ? item.isVisible : () => true,
|
|
5771
5836
|
isEnabled: item.isEnabled ? item.isEnabled : () => true,
|
|
@@ -17435,7 +17500,7 @@ function updateSelectionWithArrowKeys(ev, selection) {
|
|
|
17435
17500
|
}
|
|
17436
17501
|
}
|
|
17437
17502
|
|
|
17438
|
-
const uuidGenerator = new UuidGenerator();
|
|
17503
|
+
const uuidGenerator$1 = new UuidGenerator();
|
|
17439
17504
|
css /* scss */ `
|
|
17440
17505
|
.o-selection {
|
|
17441
17506
|
.o-selection-input {
|
|
@@ -17492,7 +17557,7 @@ css /* scss */ `
|
|
|
17492
17557
|
class SelectionInput extends Component {
|
|
17493
17558
|
constructor() {
|
|
17494
17559
|
super(...arguments);
|
|
17495
|
-
this.id = uuidGenerator.uuidv4();
|
|
17560
|
+
this.id = uuidGenerator$1.uuidv4();
|
|
17496
17561
|
this.previousRanges = this.props.ranges || [];
|
|
17497
17562
|
this.originSheet = this.env.model.getters.getActiveSheetId();
|
|
17498
17563
|
this.state = useState({
|
|
@@ -21185,13 +21250,26 @@ class FilterIconsOverlay extends Component {
|
|
|
21185
21250
|
const sheetId = this.env.model.getters.getActiveSheetId();
|
|
21186
21251
|
const rowDims = this.env.model.getters.getRowDimensionsInViewport(sheetId, position.row);
|
|
21187
21252
|
const colDims = this.env.model.getters.getColDimensionsInViewport(sheetId, position.col);
|
|
21188
|
-
|
|
21189
|
-
const
|
|
21253
|
+
const cell = this.env.model.getters.getCell({ sheetId, ...position });
|
|
21254
|
+
const verticalFilterIconPosition = this.getIconVerticalPosition(rowDims, cell);
|
|
21190
21255
|
return {
|
|
21191
21256
|
x: colDims.end - FILTER_ICON_EDGE_LENGTH + this.props.gridPosition.x - FILTER_ICON_MARGIN - 1,
|
|
21192
|
-
y:
|
|
21257
|
+
y: verticalFilterIconPosition + this.props.gridPosition.y,
|
|
21193
21258
|
};
|
|
21194
21259
|
}
|
|
21260
|
+
// Calculates the vertical position of the filter icon based on the row dimensions and cell styles.
|
|
21261
|
+
getIconVerticalPosition(rowDims, cell) {
|
|
21262
|
+
var _a;
|
|
21263
|
+
const centeringOffset = Math.floor((rowDims.size - FILTER_ICON_EDGE_LENGTH) / 2);
|
|
21264
|
+
switch ((_a = cell === null || cell === void 0 ? void 0 : cell.style) === null || _a === void 0 ? void 0 : _a.verticalAlign) {
|
|
21265
|
+
case "bottom":
|
|
21266
|
+
return rowDims.end - FILTER_ICON_MARGIN - FILTER_ICON_EDGE_LENGTH;
|
|
21267
|
+
case "top":
|
|
21268
|
+
return rowDims.start + FILTER_ICON_MARGIN;
|
|
21269
|
+
default:
|
|
21270
|
+
return rowDims.end - FILTER_ICON_EDGE_LENGTH - centeringOffset;
|
|
21271
|
+
}
|
|
21272
|
+
}
|
|
21195
21273
|
isFilterActive(position) {
|
|
21196
21274
|
const sheetId = this.env.model.getters.getActiveSheetId();
|
|
21197
21275
|
return this.env.model.getters.isFilterActive({ sheetId, ...position });
|
|
@@ -23107,6 +23185,7 @@ class Grid extends Component {
|
|
|
23107
23185
|
"CTRL+A": () => this.env.model.selection.loopSelection(),
|
|
23108
23186
|
"CTRL+Z": () => this.env.model.dispatch("REQUEST_UNDO"),
|
|
23109
23187
|
"CTRL+Y": () => this.env.model.dispatch("REQUEST_REDO"),
|
|
23188
|
+
F4: () => this.env.model.dispatch("REQUEST_REDO"),
|
|
23110
23189
|
"CTRL+B": () => this.env.model.dispatch("SET_FORMATTING", {
|
|
23111
23190
|
sheetId: this.env.model.getters.getActiveSheetId(),
|
|
23112
23191
|
target: this.env.model.getters.getSelectedZones(),
|
|
@@ -23150,8 +23229,8 @@ class Grid extends Component {
|
|
|
23150
23229
|
},
|
|
23151
23230
|
"CTRL+END": () => {
|
|
23152
23231
|
const sheetId = this.env.model.getters.getActiveSheetId();
|
|
23153
|
-
const col = this.env.model.getters.findVisibleHeader(sheetId, "COL",
|
|
23154
|
-
const row = this.env.model.getters.findVisibleHeader(sheetId, "ROW",
|
|
23232
|
+
const col = this.env.model.getters.findVisibleHeader(sheetId, "COL", this.env.model.getters.getNumberCols(sheetId) - 1, 0);
|
|
23233
|
+
const row = this.env.model.getters.findVisibleHeader(sheetId, "ROW", this.env.model.getters.getNumberRows(sheetId) - 1, 0);
|
|
23155
23234
|
this.env.model.selection.selectCell(col, row);
|
|
23156
23235
|
},
|
|
23157
23236
|
"SHIFT+ ": () => {
|
|
@@ -23231,9 +23310,10 @@ class Grid extends Component {
|
|
|
23231
23310
|
this.focus();
|
|
23232
23311
|
}
|
|
23233
23312
|
focus() {
|
|
23313
|
+
var _a;
|
|
23234
23314
|
if (!this.env.model.getters.getSelectedFigureId() &&
|
|
23235
23315
|
this.env.model.getters.getEditionMode() === "inactive") {
|
|
23236
|
-
this.hiddenInput.el.focus();
|
|
23316
|
+
(_a = this.hiddenInput.el) === null || _a === void 0 ? void 0 : _a.focus();
|
|
23237
23317
|
}
|
|
23238
23318
|
}
|
|
23239
23319
|
get gridEl() {
|
|
@@ -27153,7 +27233,7 @@ function load(data, verboseImport) {
|
|
|
27153
27233
|
}
|
|
27154
27234
|
}
|
|
27155
27235
|
}
|
|
27156
|
-
data =
|
|
27236
|
+
data = deepCopy(data);
|
|
27157
27237
|
// apply migrations, if needed
|
|
27158
27238
|
if ("version" in data) {
|
|
27159
27239
|
if (data.version < CURRENT_VERSION) {
|
|
@@ -32271,7 +32351,7 @@ class SheetPlugin extends CorePlugin {
|
|
|
32271
32351
|
duplicateSheet(fromId, toId) {
|
|
32272
32352
|
const sheet = this.getSheet(fromId);
|
|
32273
32353
|
const toName = this.getDuplicateSheetName(sheet.name);
|
|
32274
|
-
const newSheet =
|
|
32354
|
+
const newSheet = deepCopy(sheet);
|
|
32275
32355
|
newSheet.id = toId;
|
|
32276
32356
|
newSheet.name = toName;
|
|
32277
32357
|
for (let col = 0; col <= newSheet.numberOfCols; col++) {
|
|
@@ -32398,10 +32478,10 @@ class SheetPlugin extends CorePlugin {
|
|
|
32398
32478
|
}
|
|
32399
32479
|
}
|
|
32400
32480
|
moveCellOnColumnsDeletion(sheet, deletedColumn) {
|
|
32401
|
-
for (let
|
|
32402
|
-
const
|
|
32481
|
+
for (let rowIndex = 0; rowIndex < sheet.rows.length; rowIndex++) {
|
|
32482
|
+
const row = sheet.rows[rowIndex];
|
|
32403
32483
|
for (let i in row.cells) {
|
|
32404
|
-
const colIndex =
|
|
32484
|
+
const colIndex = Number(i);
|
|
32405
32485
|
const cellId = row.cells[i];
|
|
32406
32486
|
if (cellId) {
|
|
32407
32487
|
if (colIndex === deletedColumn) {
|
|
@@ -32428,11 +32508,11 @@ class SheetPlugin extends CorePlugin {
|
|
|
32428
32508
|
*/
|
|
32429
32509
|
moveCellsOnAddition(sheet, addedElement, quantity, dimension) {
|
|
32430
32510
|
const commands = [];
|
|
32431
|
-
for (
|
|
32432
|
-
const
|
|
32511
|
+
for (let rowIndex = 0; rowIndex < sheet.rows.length; rowIndex++) {
|
|
32512
|
+
const row = sheet.rows[rowIndex];
|
|
32433
32513
|
if (dimension !== "rows" || rowIndex >= addedElement) {
|
|
32434
32514
|
for (let i in row.cells) {
|
|
32435
|
-
const colIndex =
|
|
32515
|
+
const colIndex = Number(i);
|
|
32436
32516
|
const cellId = row.cells[i];
|
|
32437
32517
|
if (cellId) {
|
|
32438
32518
|
if (dimension === "rows" || colIndex >= addedElement) {
|
|
@@ -32462,11 +32542,11 @@ class SheetPlugin extends CorePlugin {
|
|
|
32462
32542
|
*/
|
|
32463
32543
|
moveCellOnRowsDeletion(sheet, deleteFromRow, deleteToRow) {
|
|
32464
32544
|
const numberRows = deleteToRow - deleteFromRow + 1;
|
|
32465
|
-
for (let
|
|
32466
|
-
const
|
|
32545
|
+
for (let rowIndex = 0; rowIndex < sheet.rows.length; rowIndex++) {
|
|
32546
|
+
const row = sheet.rows[rowIndex];
|
|
32467
32547
|
if (rowIndex >= deleteFromRow && rowIndex <= deleteToRow) {
|
|
32468
32548
|
for (let i in row.cells) {
|
|
32469
|
-
const colIndex =
|
|
32549
|
+
const colIndex = Number(i);
|
|
32470
32550
|
const cellId = row.cells[i];
|
|
32471
32551
|
if (cellId) {
|
|
32472
32552
|
this.dispatch("CLEAR_CELL", {
|
|
@@ -32479,7 +32559,7 @@ class SheetPlugin extends CorePlugin {
|
|
|
32479
32559
|
}
|
|
32480
32560
|
if (rowIndex > deleteToRow) {
|
|
32481
32561
|
for (let i in row.cells) {
|
|
32482
|
-
const colIndex =
|
|
32562
|
+
const colIndex = Number(i);
|
|
32483
32563
|
const cellId = row.cells[i];
|
|
32484
32564
|
if (cellId) {
|
|
32485
32565
|
this.dispatch("UPDATE_CELL_POSITION", {
|
|
@@ -32497,7 +32577,7 @@ class SheetPlugin extends CorePlugin {
|
|
|
32497
32577
|
const rows = [];
|
|
32498
32578
|
const cellsQueue = sheet.rows.map((row) => row.cells);
|
|
32499
32579
|
for (let i in sheet.rows) {
|
|
32500
|
-
if (
|
|
32580
|
+
if (Number(i) === index) {
|
|
32501
32581
|
continue;
|
|
32502
32582
|
}
|
|
32503
32583
|
rows.push({
|
|
@@ -33668,13 +33748,11 @@ class FilterEvaluationPlugin extends UIPlugin {
|
|
|
33668
33748
|
return Boolean(id && ((_b = (_a = this.filterValues[sheetId]) === null || _a === void 0 ? void 0 : _a[id]) === null || _b === void 0 ? void 0 : _b.length));
|
|
33669
33749
|
}
|
|
33670
33750
|
intersectZoneWithViewport(sheetId, zone) {
|
|
33671
|
-
const colsRange = range(zone.left, zone.right + 1);
|
|
33672
|
-
const rowsRange = range(zone.top, zone.bottom + 1);
|
|
33673
33751
|
return {
|
|
33674
|
-
left: this.getters.findVisibleHeader(sheetId, "COL",
|
|
33675
|
-
right: this.getters.findVisibleHeader(sheetId, "COL",
|
|
33676
|
-
top: this.getters.findVisibleHeader(sheetId, "ROW",
|
|
33677
|
-
bottom: this.getters.findVisibleHeader(sheetId, "ROW",
|
|
33752
|
+
left: this.getters.findVisibleHeader(sheetId, "COL", zone.left, zone.right),
|
|
33753
|
+
right: this.getters.findVisibleHeader(sheetId, "COL", zone.right, zone.left),
|
|
33754
|
+
top: this.getters.findVisibleHeader(sheetId, "ROW", zone.top, zone.bottom),
|
|
33755
|
+
bottom: this.getters.findVisibleHeader(sheetId, "ROW", zone.bottom, zone.top),
|
|
33678
33756
|
};
|
|
33679
33757
|
}
|
|
33680
33758
|
updateFilter({ col, row, values, sheetId }) {
|
|
@@ -35796,13 +35874,33 @@ class HeaderVisibilityUIPlugin extends UIPlugin {
|
|
|
35796
35874
|
getNextVisibleCellPosition({ sheetId, col, row }) {
|
|
35797
35875
|
return {
|
|
35798
35876
|
sheetId,
|
|
35799
|
-
col: this.findVisibleHeader(sheetId, "COL",
|
|
35800
|
-
row: this.findVisibleHeader(sheetId, "ROW",
|
|
35877
|
+
col: this.findVisibleHeader(sheetId, "COL", col, this.getters.getNumberCols(sheetId) - 1),
|
|
35878
|
+
row: this.findVisibleHeader(sheetId, "ROW", row, this.getters.getNumberRows(sheetId) - 1),
|
|
35801
35879
|
};
|
|
35802
35880
|
}
|
|
35803
|
-
|
|
35804
|
-
|
|
35805
|
-
|
|
35881
|
+
/**
|
|
35882
|
+
* Find the first visible header in the range [`from` => `to`].
|
|
35883
|
+
*
|
|
35884
|
+
* Both `from` and `to` are inclusive.
|
|
35885
|
+
*/
|
|
35886
|
+
findVisibleHeader(sheetId, dimension, from, to) {
|
|
35887
|
+
if (from <= to) {
|
|
35888
|
+
for (let i = from; i <= to; i++) {
|
|
35889
|
+
if (this.getters.doesHeaderExist(sheetId, dimension, i) &&
|
|
35890
|
+
!this.isHeaderHidden(sheetId, dimension, i)) {
|
|
35891
|
+
return i;
|
|
35892
|
+
}
|
|
35893
|
+
}
|
|
35894
|
+
}
|
|
35895
|
+
if (from > to) {
|
|
35896
|
+
for (let i = from; i >= to; i--) {
|
|
35897
|
+
if (this.getters.doesHeaderExist(sheetId, dimension, i) &&
|
|
35898
|
+
!this.isHeaderHidden(sheetId, dimension, i)) {
|
|
35899
|
+
return i;
|
|
35900
|
+
}
|
|
35901
|
+
}
|
|
35902
|
+
}
|
|
35903
|
+
return undefined;
|
|
35806
35904
|
}
|
|
35807
35905
|
findLastVisibleColRowIndex(sheetId, dimension, { last, first }) {
|
|
35808
35906
|
const lastVisibleIndex = range(last, first, -1).find((index) => !this.isHeaderHidden(sheetId, dimension, index));
|
|
@@ -37263,7 +37361,8 @@ class Revision {
|
|
|
37263
37361
|
* - clientId: Client who initiated the action
|
|
37264
37362
|
* - changes: List of changes applied on the state.
|
|
37265
37363
|
*/
|
|
37266
|
-
constructor(id, clientId, commands, changes) {
|
|
37364
|
+
constructor(id, clientId, commands, rootCommand, changes) {
|
|
37365
|
+
this.rootCommand = rootCommand;
|
|
37267
37366
|
this._commands = [];
|
|
37268
37367
|
this._changes = [];
|
|
37269
37368
|
this.id = id;
|
|
@@ -37327,10 +37426,10 @@ class Session extends EventBus {
|
|
|
37327
37426
|
* Add a new revision to the collaborative session.
|
|
37328
37427
|
* It will be transmitted to all other connected clients.
|
|
37329
37428
|
*/
|
|
37330
|
-
save(commands, changes) {
|
|
37429
|
+
save(rootCommand, commands, changes) {
|
|
37331
37430
|
if (!commands.length || !changes.length || !this.canApplyOptimisticUpdate())
|
|
37332
37431
|
return;
|
|
37333
|
-
const revision = new Revision(this.uuidGenerator.uuidv4(), this.clientId, commands, changes);
|
|
37432
|
+
const revision = new Revision(this.uuidGenerator.uuidv4(), this.clientId, commands, rootCommand, changes);
|
|
37334
37433
|
this.revisions.append(revision.id, revision);
|
|
37335
37434
|
this.trigger("new-local-state-update", { id: revision.id });
|
|
37336
37435
|
this.sendUpdateMessage({
|
|
@@ -37431,6 +37530,22 @@ class Session extends EventBus {
|
|
|
37431
37530
|
isFullySynchronized() {
|
|
37432
37531
|
return this.pendingMessages.length === 0;
|
|
37433
37532
|
}
|
|
37533
|
+
/**
|
|
37534
|
+
* Get the last local revision whose root command isn't in the given list of ignored commands
|
|
37535
|
+
* */
|
|
37536
|
+
getLastLocalNonEmptyRevision(ignoredRootCommands) {
|
|
37537
|
+
var _a;
|
|
37538
|
+
const revisions = this.revisions.getRevertedExecution();
|
|
37539
|
+
for (const rev of revisions) {
|
|
37540
|
+
if (rev.rootCommand === "SNAPSHOT")
|
|
37541
|
+
return undefined;
|
|
37542
|
+
if (!rev.rootCommand || rev.rootCommand === "REMOTE")
|
|
37543
|
+
continue;
|
|
37544
|
+
if (!ignoredRootCommands.includes((_a = rev.rootCommand) === null || _a === void 0 ? void 0 : _a.type) && rev.commands.length)
|
|
37545
|
+
return rev;
|
|
37546
|
+
}
|
|
37547
|
+
return undefined;
|
|
37548
|
+
}
|
|
37434
37549
|
_move(position) {
|
|
37435
37550
|
var _a;
|
|
37436
37551
|
// this method is debounced and might be called after the client
|
|
@@ -37490,7 +37605,7 @@ class Session extends EventBus {
|
|
|
37490
37605
|
return;
|
|
37491
37606
|
}
|
|
37492
37607
|
const { clientId, commands } = message;
|
|
37493
|
-
const revision = new Revision(message.nextRevisionId, clientId, commands);
|
|
37608
|
+
const revision = new Revision(message.nextRevisionId, clientId, commands, "REMOTE");
|
|
37494
37609
|
if (revision.clientId !== this.clientId) {
|
|
37495
37610
|
this.revisions.insert(revision.id, revision, message.serverRevisionId);
|
|
37496
37611
|
const pendingCommands = this.pendingMessages
|
|
@@ -37503,7 +37618,7 @@ class Session extends EventBus {
|
|
|
37503
37618
|
}
|
|
37504
37619
|
break;
|
|
37505
37620
|
case "SNAPSHOT_CREATED": {
|
|
37506
|
-
const revision = new Revision(message.nextRevisionId, "server", []);
|
|
37621
|
+
const revision = new Revision(message.nextRevisionId, "server", [], "SNAPSHOT");
|
|
37507
37622
|
this.revisions.insert(revision.id, revision, message.serverRevisionId);
|
|
37508
37623
|
this.dropPendingHistoryMessages();
|
|
37509
37624
|
this.trigger("snapshot");
|
|
@@ -38281,6 +38396,324 @@ SheetUIPlugin.getters = [
|
|
|
38281
38396
|
"getColRowOffset",
|
|
38282
38397
|
];
|
|
38283
38398
|
|
|
38399
|
+
const genericRepeatsTransforms = [
|
|
38400
|
+
repeatSheetDependantCommand,
|
|
38401
|
+
repeatTargetDependantCommand,
|
|
38402
|
+
repeatPositionDependantCommand,
|
|
38403
|
+
];
|
|
38404
|
+
function repeatSheetDependantCommand(getters, command) {
|
|
38405
|
+
if (!("sheetId" in command))
|
|
38406
|
+
return command;
|
|
38407
|
+
return { ...deepCopy(command), sheetId: getters.getActiveSheetId() };
|
|
38408
|
+
}
|
|
38409
|
+
function repeatTargetDependantCommand(getters, command) {
|
|
38410
|
+
if (!("target" in command) || !Array.isArray(command.target))
|
|
38411
|
+
return command;
|
|
38412
|
+
return {
|
|
38413
|
+
...deepCopy(command),
|
|
38414
|
+
target: getters.getSelectedZones(),
|
|
38415
|
+
};
|
|
38416
|
+
}
|
|
38417
|
+
function repeatPositionDependantCommand(getters, command) {
|
|
38418
|
+
if (!("row" in command) || !("col" in command))
|
|
38419
|
+
return command;
|
|
38420
|
+
const { col, row } = getters.getActivePosition();
|
|
38421
|
+
return { ...deepCopy(command), col, row };
|
|
38422
|
+
}
|
|
38423
|
+
|
|
38424
|
+
const uuidGenerator = new UuidGenerator();
|
|
38425
|
+
function repeatCreateChartCommand(getters, cmd) {
|
|
38426
|
+
return {
|
|
38427
|
+
...repeatSheetDependantCommand(getters, cmd),
|
|
38428
|
+
id: uuidGenerator.uuidv4(),
|
|
38429
|
+
};
|
|
38430
|
+
}
|
|
38431
|
+
function repeatCreateImageCommand(getters, cmd) {
|
|
38432
|
+
return {
|
|
38433
|
+
...repeatSheetDependantCommand(getters, cmd),
|
|
38434
|
+
figureId: uuidGenerator.uuidv4(),
|
|
38435
|
+
};
|
|
38436
|
+
}
|
|
38437
|
+
function repeatCreateFigureCommand(getters, cmd) {
|
|
38438
|
+
const newCmd = repeatSheetDependantCommand(getters, cmd);
|
|
38439
|
+
newCmd.figure.id = uuidGenerator.uuidv4();
|
|
38440
|
+
return newCmd;
|
|
38441
|
+
}
|
|
38442
|
+
function repeatCreateSheetCommand(getters, cmd) {
|
|
38443
|
+
var _a;
|
|
38444
|
+
const newCmd = deepCopy(cmd);
|
|
38445
|
+
newCmd.sheetId = uuidGenerator.uuidv4();
|
|
38446
|
+
const sheetName = cmd.name || getters.getSheet(getters.getActiveSheetId()).name;
|
|
38447
|
+
// Extract the prefix of the sheet name (everything before the number at the end of the name)
|
|
38448
|
+
const namePrefix = ((_a = sheetName.match(/(.+?)\d*$/)) === null || _a === void 0 ? void 0 : _a[1]) || sheetName;
|
|
38449
|
+
newCmd.name = getters.getNextSheetName(namePrefix);
|
|
38450
|
+
return newCmd;
|
|
38451
|
+
}
|
|
38452
|
+
function repeatAddColumnsRowsCommand(getters, cmd) {
|
|
38453
|
+
const currentPosition = getters.getActivePosition();
|
|
38454
|
+
return {
|
|
38455
|
+
...repeatSheetDependantCommand(getters, cmd),
|
|
38456
|
+
base: cmd.dimension === "COL" ? currentPosition.col : currentPosition.row,
|
|
38457
|
+
};
|
|
38458
|
+
}
|
|
38459
|
+
function repeatHeaderElementCommand(getters, cmd) {
|
|
38460
|
+
const currentSelection = getters.getSelectedZone();
|
|
38461
|
+
return {
|
|
38462
|
+
...repeatSheetDependantCommand(getters, cmd),
|
|
38463
|
+
elements: cmd.dimension === "COL"
|
|
38464
|
+
? range(currentSelection.left, currentSelection.right + 1)
|
|
38465
|
+
: range(currentSelection.top, currentSelection.bottom + 1),
|
|
38466
|
+
};
|
|
38467
|
+
}
|
|
38468
|
+
function repeatInsertOrDeleteCellCommand(getters, cmd) {
|
|
38469
|
+
const currentSelection = getters.getSelectedZone();
|
|
38470
|
+
return {
|
|
38471
|
+
...deepCopy(cmd),
|
|
38472
|
+
zone: currentSelection,
|
|
38473
|
+
};
|
|
38474
|
+
}
|
|
38475
|
+
function repeatAutoResizeCommand(getters, cmd) {
|
|
38476
|
+
const newCmd = deepCopy(cmd);
|
|
38477
|
+
const currentSelection = getters.getSelectedZone();
|
|
38478
|
+
const { top, bottom, left, right } = currentSelection;
|
|
38479
|
+
if ("cols" in newCmd) {
|
|
38480
|
+
newCmd.cols = range(left, right + 1);
|
|
38481
|
+
}
|
|
38482
|
+
else if ("rows" in newCmd) {
|
|
38483
|
+
newCmd.rows = range(top, bottom + 1);
|
|
38484
|
+
}
|
|
38485
|
+
return newCmd;
|
|
38486
|
+
}
|
|
38487
|
+
function repeatSortCellsCommand(getters, cmd) {
|
|
38488
|
+
const currentSelection = getters.getSelectedZone();
|
|
38489
|
+
return {
|
|
38490
|
+
...repeatSheetDependantCommand(getters, cmd),
|
|
38491
|
+
col: currentSelection.left,
|
|
38492
|
+
row: currentSelection.top,
|
|
38493
|
+
zone: currentSelection,
|
|
38494
|
+
};
|
|
38495
|
+
}
|
|
38496
|
+
function repeatPasteCommand(getters, cmd) {
|
|
38497
|
+
/**
|
|
38498
|
+
* Note : we have to store the state of the clipboard in the clipboard plugin, and introduce a
|
|
38499
|
+
* new command REPEAT_PASTE to be able to repeat the paste command.
|
|
38500
|
+
*
|
|
38501
|
+
* We cannot re-dispatch a paste, because the content of the clipboard may have changed in between.
|
|
38502
|
+
*
|
|
38503
|
+
* And we cannot adapt the sub-commands of the paste command, because they are dependant on the state of the sheet,
|
|
38504
|
+
* and may change based on the paste location. A simple example is that paste create new col/rows for the clipboard
|
|
38505
|
+
* content to fit the sheet. So there will be ADD_COL_ROW_COMMANDS in the sub-commands in the history, but repeating
|
|
38506
|
+
* paste might not need them. Or they could only needed for the repeated paste, not for the original.
|
|
38507
|
+
*/
|
|
38508
|
+
return {
|
|
38509
|
+
type: "REPEAT_PASTE",
|
|
38510
|
+
pasteOption: deepCopy(cmd.pasteOption),
|
|
38511
|
+
target: getters.getSelectedZones(),
|
|
38512
|
+
};
|
|
38513
|
+
}
|
|
38514
|
+
|
|
38515
|
+
/**
|
|
38516
|
+
* Registry containing all the command that can be repeated on redo, and function to transform them
|
|
38517
|
+
* to the current state of the model.
|
|
38518
|
+
*
|
|
38519
|
+
* If the transform function is undefined, the command will be transformed using generic transformations.
|
|
38520
|
+
* (change the sheetId, the row, the col, the target, the ranges, to the current active sheet & selection)
|
|
38521
|
+
*
|
|
38522
|
+
*/
|
|
38523
|
+
const repeatCommandTransformRegistry = new Registry();
|
|
38524
|
+
repeatCommandTransformRegistry.add("UPDATE_CELL", genericRepeat);
|
|
38525
|
+
repeatCommandTransformRegistry.add("CLEAR_CELL", genericRepeat);
|
|
38526
|
+
repeatCommandTransformRegistry.add("DELETE_CONTENT", genericRepeat);
|
|
38527
|
+
repeatCommandTransformRegistry.add("ADD_MERGE", genericRepeat);
|
|
38528
|
+
repeatCommandTransformRegistry.add("REMOVE_MERGE", genericRepeat);
|
|
38529
|
+
repeatCommandTransformRegistry.add("SET_FORMATTING", genericRepeat);
|
|
38530
|
+
repeatCommandTransformRegistry.add("CLEAR_FORMATTING", genericRepeat);
|
|
38531
|
+
repeatCommandTransformRegistry.add("SET_BORDER", genericRepeat);
|
|
38532
|
+
repeatCommandTransformRegistry.add("CREATE_FILTER_TABLE", genericRepeat);
|
|
38533
|
+
repeatCommandTransformRegistry.add("REMOVE_FILTER_TABLE", genericRepeat);
|
|
38534
|
+
repeatCommandTransformRegistry.add("HIDE_SHEET", genericRepeat);
|
|
38535
|
+
repeatCommandTransformRegistry.add("ADD_COLUMNS_ROWS", repeatAddColumnsRowsCommand);
|
|
38536
|
+
repeatCommandTransformRegistry.add("REMOVE_COLUMNS_ROWS", repeatHeaderElementCommand);
|
|
38537
|
+
repeatCommandTransformRegistry.add("HIDE_COLUMNS_ROWS", repeatHeaderElementCommand);
|
|
38538
|
+
repeatCommandTransformRegistry.add("RESIZE_COLUMNS_ROWS", repeatHeaderElementCommand);
|
|
38539
|
+
repeatCommandTransformRegistry.add("CREATE_SHEET", repeatCreateSheetCommand);
|
|
38540
|
+
repeatCommandTransformRegistry.add("CREATE_FIGURE", repeatCreateFigureCommand);
|
|
38541
|
+
repeatCommandTransformRegistry.add("CREATE_CHART", repeatCreateChartCommand);
|
|
38542
|
+
repeatCommandTransformRegistry.add("CREATE_IMAGE", repeatCreateImageCommand);
|
|
38543
|
+
const repeatLocalCommandTransformRegistry = new Registry();
|
|
38544
|
+
repeatLocalCommandTransformRegistry.add("STOP_EDITION", repeatLocalCommandChildren);
|
|
38545
|
+
repeatLocalCommandTransformRegistry.add("PASTE", repeatPasteCommand);
|
|
38546
|
+
repeatLocalCommandTransformRegistry.add("INSERT_CELL", repeatInsertOrDeleteCellCommand);
|
|
38547
|
+
repeatLocalCommandTransformRegistry.add("DELETE_CELL", repeatInsertOrDeleteCellCommand);
|
|
38548
|
+
repeatLocalCommandTransformRegistry.add("AUTORESIZE_COLUMNS", repeatAutoResizeCommand);
|
|
38549
|
+
repeatLocalCommandTransformRegistry.add("AUTORESIZE_ROWS", repeatAutoResizeCommand);
|
|
38550
|
+
repeatLocalCommandTransformRegistry.add("SORT_CELLS", repeatSortCellsCommand);
|
|
38551
|
+
repeatLocalCommandTransformRegistry.add("SUM_SELECTION", genericRepeat);
|
|
38552
|
+
repeatLocalCommandTransformRegistry.add("SET_DECIMAL", genericRepeat);
|
|
38553
|
+
function genericRepeat(getters, command) {
|
|
38554
|
+
let transformedCommand = deepCopy(command);
|
|
38555
|
+
for (const repeatTransform of genericRepeatsTransforms) {
|
|
38556
|
+
transformedCommand = repeatTransform(getters, transformedCommand);
|
|
38557
|
+
}
|
|
38558
|
+
return transformedCommand;
|
|
38559
|
+
}
|
|
38560
|
+
function repeatCoreCommand(getters, command) {
|
|
38561
|
+
if (!command) {
|
|
38562
|
+
return undefined;
|
|
38563
|
+
}
|
|
38564
|
+
const isRepeatable = repeatCommandTransformRegistry.contains(command.type);
|
|
38565
|
+
if (!isRepeatable) {
|
|
38566
|
+
return undefined;
|
|
38567
|
+
}
|
|
38568
|
+
const transform = repeatCommandTransformRegistry.get(command.type);
|
|
38569
|
+
return transform(getters, command);
|
|
38570
|
+
}
|
|
38571
|
+
function repeatLocalCommand(getters, command, childCommands) {
|
|
38572
|
+
const isRepeatable = repeatLocalCommandTransformRegistry.contains(command.type);
|
|
38573
|
+
if (!isRepeatable) {
|
|
38574
|
+
return undefined;
|
|
38575
|
+
}
|
|
38576
|
+
const repeatTransform = repeatLocalCommandTransformRegistry.get(command.type);
|
|
38577
|
+
return repeatTransform(getters, command, childCommands);
|
|
38578
|
+
}
|
|
38579
|
+
function repeatLocalCommandChildren(getters, cmd, childCommands) {
|
|
38580
|
+
return childCommands
|
|
38581
|
+
.map((childCommand) => repeatCoreCommand(getters, childCommand))
|
|
38582
|
+
.filter(isDefined$1);
|
|
38583
|
+
}
|
|
38584
|
+
|
|
38585
|
+
function canRepeatRevision(revision) {
|
|
38586
|
+
if (!revision || !revision.rootCommand || typeof revision.rootCommand !== "object") {
|
|
38587
|
+
return false;
|
|
38588
|
+
}
|
|
38589
|
+
if (isCoreCommand(revision.rootCommand)) {
|
|
38590
|
+
return repeatCommandTransformRegistry.contains(revision.rootCommand.type);
|
|
38591
|
+
}
|
|
38592
|
+
return repeatLocalCommandTransformRegistry.contains(revision.rootCommand.type);
|
|
38593
|
+
}
|
|
38594
|
+
function repeatRevision(revision, getters) {
|
|
38595
|
+
if (!revision.rootCommand || typeof revision.rootCommand !== "object") {
|
|
38596
|
+
return undefined;
|
|
38597
|
+
}
|
|
38598
|
+
if (isCoreCommand(revision.rootCommand)) {
|
|
38599
|
+
return repeatCoreCommand(getters, revision.rootCommand);
|
|
38600
|
+
}
|
|
38601
|
+
return repeatLocalCommand(getters, revision.rootCommand, revision.commands);
|
|
38602
|
+
}
|
|
38603
|
+
|
|
38604
|
+
/**
|
|
38605
|
+
* Local History
|
|
38606
|
+
*
|
|
38607
|
+
* The local history is responsible of tracking the locally state updates
|
|
38608
|
+
* It maintains the local undo and redo stack to allow to undo/redo only local
|
|
38609
|
+
* changes
|
|
38610
|
+
*/
|
|
38611
|
+
class HistoryPlugin extends UIPlugin {
|
|
38612
|
+
constructor(config) {
|
|
38613
|
+
super(config);
|
|
38614
|
+
/**
|
|
38615
|
+
* Ids of the revisions which can be undone
|
|
38616
|
+
*/
|
|
38617
|
+
this.undoStack = [];
|
|
38618
|
+
/**
|
|
38619
|
+
* Ids of the revisions which can be redone
|
|
38620
|
+
*/
|
|
38621
|
+
this.redoStack = [];
|
|
38622
|
+
this.session = config.session;
|
|
38623
|
+
this.session.on("new-local-state-update", this, this.onNewLocalStateUpdate);
|
|
38624
|
+
this.session.on("pending-revisions-dropped", this, ({ revisionIds }) => this.drop(revisionIds));
|
|
38625
|
+
this.session.on("snapshot", this, () => {
|
|
38626
|
+
this.undoStack = [];
|
|
38627
|
+
this.redoStack = [];
|
|
38628
|
+
});
|
|
38629
|
+
}
|
|
38630
|
+
allowDispatch(cmd) {
|
|
38631
|
+
switch (cmd.type) {
|
|
38632
|
+
case "REQUEST_UNDO":
|
|
38633
|
+
if (!this.canUndo()) {
|
|
38634
|
+
return 6 /* CommandResult.EmptyUndoStack */;
|
|
38635
|
+
}
|
|
38636
|
+
break;
|
|
38637
|
+
case "REQUEST_REDO":
|
|
38638
|
+
if (!this.canRedo()) {
|
|
38639
|
+
return 7 /* CommandResult.EmptyRedoStack */;
|
|
38640
|
+
}
|
|
38641
|
+
break;
|
|
38642
|
+
}
|
|
38643
|
+
return 0 /* CommandResult.Success */;
|
|
38644
|
+
}
|
|
38645
|
+
handle(cmd) {
|
|
38646
|
+
switch (cmd.type) {
|
|
38647
|
+
case "REQUEST_UNDO":
|
|
38648
|
+
case "REQUEST_REDO":
|
|
38649
|
+
// History changes (undo & redo) are *not* applied optimistically on the local state.
|
|
38650
|
+
// We wait a global confirmation from the server. The goal is to avoid handling concurrent
|
|
38651
|
+
// history changes on multiple clients which are very hard to manage correctly.
|
|
38652
|
+
this.requestHistoryChange(cmd.type === "REQUEST_UNDO" ? "UNDO" : "REDO");
|
|
38653
|
+
}
|
|
38654
|
+
}
|
|
38655
|
+
finalize() { }
|
|
38656
|
+
requestHistoryChange(type) {
|
|
38657
|
+
const id = type === "UNDO" ? this.undoStack.pop() : this.redoStack.pop();
|
|
38658
|
+
if (!id) {
|
|
38659
|
+
const lastNonRedoRevision = this.getPossibleRevisionToRepeat();
|
|
38660
|
+
if (!lastNonRedoRevision) {
|
|
38661
|
+
return;
|
|
38662
|
+
}
|
|
38663
|
+
const repeatedCommands = repeatRevision(lastNonRedoRevision, this.getters);
|
|
38664
|
+
if (!repeatedCommands) {
|
|
38665
|
+
return;
|
|
38666
|
+
}
|
|
38667
|
+
if (!Array.isArray(repeatedCommands)) {
|
|
38668
|
+
this.dispatch(repeatedCommands.type, repeatedCommands);
|
|
38669
|
+
return;
|
|
38670
|
+
}
|
|
38671
|
+
for (const command of repeatedCommands) {
|
|
38672
|
+
this.dispatch(command.type, command);
|
|
38673
|
+
}
|
|
38674
|
+
return;
|
|
38675
|
+
}
|
|
38676
|
+
if (type === "UNDO") {
|
|
38677
|
+
this.session.undo(id);
|
|
38678
|
+
this.redoStack.push(id);
|
|
38679
|
+
}
|
|
38680
|
+
else {
|
|
38681
|
+
this.session.redo(id);
|
|
38682
|
+
this.undoStack.push(id);
|
|
38683
|
+
}
|
|
38684
|
+
}
|
|
38685
|
+
canUndo() {
|
|
38686
|
+
return this.undoStack.length > 0;
|
|
38687
|
+
}
|
|
38688
|
+
canRedo() {
|
|
38689
|
+
if (this.redoStack.length > 0)
|
|
38690
|
+
return true;
|
|
38691
|
+
const lastNonRedoRevision = this.getPossibleRevisionToRepeat();
|
|
38692
|
+
return canRepeatRevision(lastNonRedoRevision);
|
|
38693
|
+
}
|
|
38694
|
+
drop(revisionIds) {
|
|
38695
|
+
this.undoStack = this.undoStack.filter((id) => !revisionIds.includes(id));
|
|
38696
|
+
this.redoStack = [];
|
|
38697
|
+
}
|
|
38698
|
+
onNewLocalStateUpdate({ id }) {
|
|
38699
|
+
this.undoStack.push(id);
|
|
38700
|
+
this.redoStack = [];
|
|
38701
|
+
if (this.undoStack.length > MAX_HISTORY_STEPS) {
|
|
38702
|
+
this.undoStack.shift();
|
|
38703
|
+
}
|
|
38704
|
+
}
|
|
38705
|
+
/**
|
|
38706
|
+
* Fetch the last revision which is not empty and not a repeated command
|
|
38707
|
+
*
|
|
38708
|
+
* Ignore repeated commands (REQUEST_REDO command as root command)
|
|
38709
|
+
* Ignore standard undo/redo revisions (that are empty)
|
|
38710
|
+
*/
|
|
38711
|
+
getPossibleRevisionToRepeat() {
|
|
38712
|
+
return this.session.getLastLocalNonEmptyRevision(["REQUEST_REDO"]);
|
|
38713
|
+
}
|
|
38714
|
+
}
|
|
38715
|
+
HistoryPlugin.getters = ["canUndo", "canRedo"];
|
|
38716
|
+
|
|
38284
38717
|
/** Abstract state of the clipboard when copying/cutting content that is pasted in cells of the sheet */
|
|
38285
38718
|
class ClipboardCellsAbstractState {
|
|
38286
38719
|
constructor(operation, getters, dispatch, selection) {
|
|
@@ -38990,7 +39423,7 @@ class ClipboardPlugin extends UIPlugin {
|
|
|
38990
39423
|
return 0 /* CommandResult.Success */;
|
|
38991
39424
|
}
|
|
38992
39425
|
handle(cmd) {
|
|
38993
|
-
var _a, _b;
|
|
39426
|
+
var _a, _b, _c;
|
|
38994
39427
|
switch (cmd.type) {
|
|
38995
39428
|
case "COPY":
|
|
38996
39429
|
case "CUT":
|
|
@@ -39008,6 +39441,7 @@ class ClipboardPlugin extends UIPlugin {
|
|
|
39008
39441
|
if (this.state.operation === "CUT") {
|
|
39009
39442
|
this.state = undefined;
|
|
39010
39443
|
}
|
|
39444
|
+
this.lastPasteState = this.state;
|
|
39011
39445
|
this.status = "invisible";
|
|
39012
39446
|
break;
|
|
39013
39447
|
case "CLEAN_CLIPBOARD_HIGHLIGHT":
|
|
@@ -39060,10 +39494,19 @@ class ClipboardPlugin extends UIPlugin {
|
|
|
39060
39494
|
break;
|
|
39061
39495
|
}
|
|
39062
39496
|
case "PASTE_FROM_OS_CLIPBOARD":
|
|
39063
|
-
|
|
39064
|
-
state.paste(cmd.target);
|
|
39497
|
+
this.state = new ClipboardOsState(cmd.text, this.getters, this.dispatch, this.selection);
|
|
39498
|
+
this.state.paste(cmd.target);
|
|
39499
|
+
this.lastPasteState = this.state;
|
|
39065
39500
|
this.status = "invisible";
|
|
39066
39501
|
break;
|
|
39502
|
+
case "REPEAT_PASTE": {
|
|
39503
|
+
(_c = this.lastPasteState) === null || _c === void 0 ? void 0 : _c.paste(cmd.target, {
|
|
39504
|
+
pasteOption: cmd.pasteOption,
|
|
39505
|
+
shouldPasteCF: true,
|
|
39506
|
+
selectTarget: true,
|
|
39507
|
+
});
|
|
39508
|
+
break;
|
|
39509
|
+
}
|
|
39067
39510
|
case "ACTIVATE_PAINT_FORMAT": {
|
|
39068
39511
|
const zones = this.getters.getSelectedZones();
|
|
39069
39512
|
this.state = this.getClipboardStateForCopyCells(zones, "COPY");
|
|
@@ -40544,7 +40987,8 @@ const featurePluginRegistry = new Registry()
|
|
|
40544
40987
|
.add("automatic_sum", AutomaticSumPlugin)
|
|
40545
40988
|
.add("format", FormatPlugin)
|
|
40546
40989
|
.add("cell_popovers", CellPopoverPlugin)
|
|
40547
|
-
.add("selection_multiuser", SelectionMultiUserPlugin)
|
|
40990
|
+
.add("selection_multiuser", SelectionMultiUserPlugin)
|
|
40991
|
+
.add("history", HistoryPlugin);
|
|
40548
40992
|
// Plugins which have a state, but which should not be shared in collaborative
|
|
40549
40993
|
const statefulUIPluginRegistry = new Registry()
|
|
40550
40994
|
.add("selection", GridSelectionPlugin)
|
|
@@ -43231,6 +43675,14 @@ class SelectiveHistory {
|
|
|
43231
43675
|
this.revertBefore(operationId);
|
|
43232
43676
|
this.tree.drop(operationId);
|
|
43233
43677
|
}
|
|
43678
|
+
getRevertedExecution() {
|
|
43679
|
+
const data = [];
|
|
43680
|
+
const operations = this.tree.revertedExecution(this.HEAD_BRANCH);
|
|
43681
|
+
for (const { operation } of operations) {
|
|
43682
|
+
data.push(operation.data);
|
|
43683
|
+
}
|
|
43684
|
+
return data;
|
|
43685
|
+
}
|
|
43234
43686
|
/**
|
|
43235
43687
|
* Revert the state as it was *before* the given operation was executed.
|
|
43236
43688
|
*/
|
|
@@ -43287,12 +43739,12 @@ function buildRevisionLog(initialRevisionId, recordChanges, dispatch) {
|
|
|
43287
43739
|
}
|
|
43288
43740
|
});
|
|
43289
43741
|
revision.setChanges(changes);
|
|
43290
|
-
}, (revision) => revertChanges([revision]), (id) => new Revision(id, "empty", []
|
|
43742
|
+
}, (revision) => revertChanges([revision]), (id) => new Revision(id, "empty", []), {
|
|
43291
43743
|
with: (revision) => (toTransform) => {
|
|
43292
|
-
return new Revision(toTransform.id, toTransform.clientId, transformAll(toTransform.commands, revision.commands));
|
|
43744
|
+
return new Revision(toTransform.id, toTransform.clientId, transformAll(toTransform.commands, revision.commands), toTransform.rootCommand);
|
|
43293
43745
|
},
|
|
43294
43746
|
without: (revision) => (toTransform) => {
|
|
43295
|
-
return new Revision(toTransform.id, toTransform.clientId, transformAll(toTransform.commands, revision.commands.map(inverseCommand).flat()));
|
|
43747
|
+
return new Revision(toTransform.id, toTransform.clientId, transformAll(toTransform.commands, revision.commands.map(inverseCommand).flat()), toTransform.rootCommand);
|
|
43296
43748
|
},
|
|
43297
43749
|
});
|
|
43298
43750
|
}
|
|
@@ -43329,101 +43781,6 @@ function applyChange(change, target) {
|
|
|
43329
43781
|
}
|
|
43330
43782
|
}
|
|
43331
43783
|
|
|
43332
|
-
/**
|
|
43333
|
-
* Local History
|
|
43334
|
-
*
|
|
43335
|
-
* The local history is responsible of tracking the locally state updates
|
|
43336
|
-
* It maintains the local undo and redo stack to allow to undo/redo only local
|
|
43337
|
-
* changes
|
|
43338
|
-
*/
|
|
43339
|
-
class LocalHistory extends EventBus$1 {
|
|
43340
|
-
constructor(dispatch, session) {
|
|
43341
|
-
super();
|
|
43342
|
-
this.dispatch = dispatch;
|
|
43343
|
-
this.session = session;
|
|
43344
|
-
/**
|
|
43345
|
-
* Ids of the revisions which can be undone
|
|
43346
|
-
*/
|
|
43347
|
-
this.undoStack = [];
|
|
43348
|
-
/**
|
|
43349
|
-
* Ids of the revisions which can be redone
|
|
43350
|
-
*/
|
|
43351
|
-
this.redoStack = [];
|
|
43352
|
-
this.session.on("new-local-state-update", this, this.onNewLocalStateUpdate);
|
|
43353
|
-
this.session.on("revision-undone", this, ({ commands }) => this.selectiveUndo(commands));
|
|
43354
|
-
this.session.on("revision-redone", this, ({ commands }) => this.selectiveRedo(commands));
|
|
43355
|
-
this.session.on("pending-revisions-dropped", this, ({ revisionIds }) => this.drop(revisionIds));
|
|
43356
|
-
this.session.on("snapshot", this, () => {
|
|
43357
|
-
this.undoStack = [];
|
|
43358
|
-
this.redoStack = [];
|
|
43359
|
-
});
|
|
43360
|
-
}
|
|
43361
|
-
allowDispatch(cmd) {
|
|
43362
|
-
switch (cmd.type) {
|
|
43363
|
-
case "REQUEST_UNDO":
|
|
43364
|
-
if (!this.canUndo()) {
|
|
43365
|
-
return 6 /* CommandResult.EmptyUndoStack */;
|
|
43366
|
-
}
|
|
43367
|
-
break;
|
|
43368
|
-
case "REQUEST_REDO":
|
|
43369
|
-
if (!this.canRedo()) {
|
|
43370
|
-
return 7 /* CommandResult.EmptyRedoStack */;
|
|
43371
|
-
}
|
|
43372
|
-
break;
|
|
43373
|
-
}
|
|
43374
|
-
return 0 /* CommandResult.Success */;
|
|
43375
|
-
}
|
|
43376
|
-
beforeHandle(cmd) { }
|
|
43377
|
-
handle(cmd) {
|
|
43378
|
-
switch (cmd.type) {
|
|
43379
|
-
case "REQUEST_UNDO":
|
|
43380
|
-
case "REQUEST_REDO":
|
|
43381
|
-
// History changes (undo & redo) are *not* applied optimistically on the local state.
|
|
43382
|
-
// We wait a global confirmation from the server. The goal is to avoid handling concurrent
|
|
43383
|
-
// history changes on multiple clients which are very hard to manage correctly.
|
|
43384
|
-
this.requestHistoryChange(cmd.type === "REQUEST_UNDO" ? "UNDO" : "REDO");
|
|
43385
|
-
}
|
|
43386
|
-
}
|
|
43387
|
-
finalize() { }
|
|
43388
|
-
requestHistoryChange(type) {
|
|
43389
|
-
const id = type === "UNDO" ? this.undoStack.pop() : this.redoStack.pop();
|
|
43390
|
-
if (!id) {
|
|
43391
|
-
return;
|
|
43392
|
-
}
|
|
43393
|
-
if (type === "UNDO") {
|
|
43394
|
-
this.session.undo(id);
|
|
43395
|
-
this.redoStack.push(id);
|
|
43396
|
-
}
|
|
43397
|
-
else {
|
|
43398
|
-
this.session.redo(id);
|
|
43399
|
-
this.undoStack.push(id);
|
|
43400
|
-
}
|
|
43401
|
-
}
|
|
43402
|
-
canUndo() {
|
|
43403
|
-
return this.undoStack.length > 0;
|
|
43404
|
-
}
|
|
43405
|
-
canRedo() {
|
|
43406
|
-
return this.redoStack.length > 0;
|
|
43407
|
-
}
|
|
43408
|
-
drop(revisionIds) {
|
|
43409
|
-
this.undoStack = this.undoStack.filter((id) => !revisionIds.includes(id));
|
|
43410
|
-
this.redoStack = [];
|
|
43411
|
-
}
|
|
43412
|
-
onNewLocalStateUpdate({ id }) {
|
|
43413
|
-
this.undoStack.push(id);
|
|
43414
|
-
this.redoStack = [];
|
|
43415
|
-
if (this.undoStack.length > MAX_HISTORY_STEPS) {
|
|
43416
|
-
this.undoStack.shift();
|
|
43417
|
-
}
|
|
43418
|
-
}
|
|
43419
|
-
selectiveUndo(commands) {
|
|
43420
|
-
this.dispatch("UNDO", { commands });
|
|
43421
|
-
}
|
|
43422
|
-
selectiveRedo(commands) {
|
|
43423
|
-
this.dispatch("REDO", { commands });
|
|
43424
|
-
}
|
|
43425
|
-
}
|
|
43426
|
-
|
|
43427
43784
|
/**
|
|
43428
43785
|
* Stateless sequence of events that can be processed by consumers.
|
|
43429
43786
|
*
|
|
@@ -43824,8 +44181,8 @@ class SelectionStreamProcessorImpl {
|
|
|
43824
44181
|
}
|
|
43825
44182
|
const { left, right, top, bottom } = zone;
|
|
43826
44183
|
const sheetId = this.getters.getActiveSheetId();
|
|
43827
|
-
const refCol = this.getters.findVisibleHeader(sheetId, "COL",
|
|
43828
|
-
const refRow = this.getters.findVisibleHeader(sheetId, "ROW",
|
|
44184
|
+
const refCol = this.getters.findVisibleHeader(sheetId, "COL", left, right);
|
|
44185
|
+
const refRow = this.getters.findVisibleHeader(sheetId, "ROW", top, bottom);
|
|
43829
44186
|
if (refRow === undefined || refCol === undefined) {
|
|
43830
44187
|
return 17 /* CommandResult.SelectionOutOfBound */;
|
|
43831
44188
|
}
|
|
@@ -43900,10 +44257,10 @@ class SelectionStreamProcessorImpl {
|
|
|
43900
44257
|
const { col: anchorCol, row: anchorRow } = anchor.cell;
|
|
43901
44258
|
return {
|
|
43902
44259
|
col: this.getters.isColHidden(sheetId, anchorCol)
|
|
43903
|
-
? this.getters.findVisibleHeader(sheetId, "COL",
|
|
44260
|
+
? this.getters.findVisibleHeader(sheetId, "COL", left, right) || anchorCol
|
|
43904
44261
|
: anchorCol,
|
|
43905
44262
|
row: this.getters.isRowHidden(sheetId, anchorRow)
|
|
43906
|
-
? this.getters.findVisibleHeader(sheetId, "ROW",
|
|
44263
|
+
? this.getters.findVisibleHeader(sheetId, "ROW", top, bottom) || anchorRow
|
|
43907
44264
|
: anchorRow,
|
|
43908
44265
|
};
|
|
43909
44266
|
}
|
|
@@ -45653,6 +46010,8 @@ class Model extends EventBus {
|
|
|
45653
46010
|
* Internal status of the model. Important for command handling coordination
|
|
45654
46011
|
*/
|
|
45655
46012
|
this.status = 0 /* Status.Ready */;
|
|
46013
|
+
this.handlers = [];
|
|
46014
|
+
this.coreHandlers = [];
|
|
45656
46015
|
/**
|
|
45657
46016
|
* Check if a command can be dispatched, and returns a DispatchResult object with the possible
|
|
45658
46017
|
* reasons the dispatch failed.
|
|
@@ -45697,7 +46056,7 @@ class Model extends EventBus {
|
|
|
45697
46056
|
this.dispatchToHandlers(this.handlers, command);
|
|
45698
46057
|
this.finalize();
|
|
45699
46058
|
});
|
|
45700
|
-
this.session.save(commands, changes);
|
|
46059
|
+
this.session.save(command, commands, changes);
|
|
45701
46060
|
this.status = 0 /* Status.Ready */;
|
|
45702
46061
|
this.trigger("update");
|
|
45703
46062
|
break;
|
|
@@ -45729,9 +46088,7 @@ class Model extends EventBus {
|
|
|
45729
46088
|
const command = { ...payload, type };
|
|
45730
46089
|
const previousStatus = this.status;
|
|
45731
46090
|
this.status = 2 /* Status.RunningCore */;
|
|
45732
|
-
const handlers = this.isReplayingCommand
|
|
45733
|
-
? [this.range, ...this.corePlugins, ...this.coreViewsPlugins]
|
|
45734
|
-
: this.handlers;
|
|
46091
|
+
const handlers = this.isReplayingCommand ? this.coreHandlers : this.handlers;
|
|
45735
46092
|
this.dispatchToHandlers(handlers, command);
|
|
45736
46093
|
this.status = previousStatus;
|
|
45737
46094
|
return DispatchResult.Success;
|
|
@@ -45742,7 +46099,6 @@ class Model extends EventBus {
|
|
|
45742
46099
|
this.uuidGenerator = uuidGenerator;
|
|
45743
46100
|
this.config = this.setupConfig(config);
|
|
45744
46101
|
this.session = this.setupSession(workbookData.revisionId);
|
|
45745
|
-
this.history = new LocalHistory(this.dispatchFromCorePlugin, this.session);
|
|
45746
46102
|
this.coreGetters = {};
|
|
45747
46103
|
this.range = new RangeAdapter(this.coreGetters);
|
|
45748
46104
|
this.coreGetters.getRangeString = this.range.getRangeString.bind(this.range);
|
|
@@ -45755,8 +46111,6 @@ class Model extends EventBus {
|
|
|
45755
46111
|
this.getters = {
|
|
45756
46112
|
isReadonly: () => this.config.mode === "readonly" || this.config.mode === "dashboard",
|
|
45757
46113
|
isDashboard: () => this.config.mode === "dashboard",
|
|
45758
|
-
canUndo: this.history.canUndo.bind(this.history),
|
|
45759
|
-
canRedo: this.history.canRedo.bind(this.history),
|
|
45760
46114
|
getClient: this.session.getClient.bind(this.session),
|
|
45761
46115
|
getConnectedClients: this.session.getConnectedClients.bind(this.session),
|
|
45762
46116
|
isFullySynchronized: this.session.isFullySynchronized.bind(this.session),
|
|
@@ -45764,6 +46118,8 @@ class Model extends EventBus {
|
|
|
45764
46118
|
this.uuidGenerator.setIsFastStrategy(true);
|
|
45765
46119
|
// Initiate stream processor
|
|
45766
46120
|
this.selection = new SelectionStreamProcessorImpl(this.getters);
|
|
46121
|
+
this.coreHandlers.push(this.range);
|
|
46122
|
+
this.handlers.push(this.range);
|
|
45767
46123
|
this.corePluginConfig = this.setupCorePluginConfig();
|
|
45768
46124
|
this.uiPluginConfig = this.setupUiPluginConfig();
|
|
45769
46125
|
// registering plugins
|
|
@@ -45772,13 +46128,20 @@ class Model extends EventBus {
|
|
|
45772
46128
|
}
|
|
45773
46129
|
Object.assign(this.getters, this.coreGetters);
|
|
45774
46130
|
for (let Plugin of statefulUIPluginRegistry.getAll()) {
|
|
45775
|
-
this.
|
|
46131
|
+
const plugin = this.setupUiPlugin(Plugin);
|
|
46132
|
+
this.statefulUIPlugins.push(plugin);
|
|
46133
|
+
this.handlers.push(plugin);
|
|
45776
46134
|
}
|
|
45777
46135
|
for (let Plugin of coreViewsPluginRegistry.getAll()) {
|
|
45778
|
-
this.
|
|
46136
|
+
const plugin = this.setupUiPlugin(Plugin);
|
|
46137
|
+
this.coreViewsPlugins.push(plugin);
|
|
46138
|
+
this.handlers.push(plugin);
|
|
46139
|
+
this.coreHandlers.push(plugin);
|
|
45779
46140
|
}
|
|
45780
46141
|
for (let Plugin of featurePluginRegistry.getAll()) {
|
|
45781
|
-
this.
|
|
46142
|
+
const plugin = this.setupUiPlugin(Plugin);
|
|
46143
|
+
this.featurePlugins.push(plugin);
|
|
46144
|
+
this.handlers.push(plugin);
|
|
45782
46145
|
}
|
|
45783
46146
|
this.uuidGenerator.setIsFastStrategy(false);
|
|
45784
46147
|
// starting plugins
|
|
@@ -45802,15 +46165,6 @@ class Model extends EventBus {
|
|
|
45802
46165
|
// by owl, since we do not rely on reactivity
|
|
45803
46166
|
markRaw(this);
|
|
45804
46167
|
}
|
|
45805
|
-
get handlers() {
|
|
45806
|
-
return [...this.coreHandlers, ...this.allUIPlugins, this.history];
|
|
45807
|
-
}
|
|
45808
|
-
get coreHandlers() {
|
|
45809
|
-
return [this.range, ...this.corePlugins];
|
|
45810
|
-
}
|
|
45811
|
-
get allUIPlugins() {
|
|
45812
|
-
return [...this.statefulUIPlugins, ...this.coreViewsPlugins, ...this.featurePlugins];
|
|
45813
|
-
}
|
|
45814
46168
|
joinSession() {
|
|
45815
46169
|
this.session.join(this.config.client);
|
|
45816
46170
|
}
|
|
@@ -45852,6 +46206,8 @@ class Model extends EventBus {
|
|
|
45852
46206
|
}
|
|
45853
46207
|
plugin.import(data);
|
|
45854
46208
|
this.corePlugins.push(plugin);
|
|
46209
|
+
this.coreHandlers.push(plugin);
|
|
46210
|
+
this.handlers.push(plugin);
|
|
45855
46211
|
}
|
|
45856
46212
|
onRemoteRevisionReceived({ commands }) {
|
|
45857
46213
|
for (let command of commands) {
|
|
@@ -45869,15 +46225,21 @@ class Model extends EventBus {
|
|
|
45869
46225
|
return;
|
|
45870
46226
|
}
|
|
45871
46227
|
this.isReplayingCommand = true;
|
|
45872
|
-
this.dispatchToHandlers(
|
|
46228
|
+
this.dispatchToHandlers(this.coreHandlers, command);
|
|
45873
46229
|
this.isReplayingCommand = false;
|
|
45874
46230
|
}), this.config.transportService, revisionId);
|
|
45875
46231
|
return session;
|
|
45876
46232
|
}
|
|
45877
46233
|
setupSessionEvents() {
|
|
45878
46234
|
this.session.on("remote-revision-received", this, this.onRemoteRevisionReceived);
|
|
45879
|
-
this.session.on("revision-
|
|
45880
|
-
|
|
46235
|
+
this.session.on("revision-undone", this, ({ commands }) => {
|
|
46236
|
+
this.dispatchFromCorePlugin("UNDO", { commands });
|
|
46237
|
+
this.finalize();
|
|
46238
|
+
});
|
|
46239
|
+
this.session.on("revision-redone", this, ({ commands }) => {
|
|
46240
|
+
this.dispatchFromCorePlugin("REDO", { commands });
|
|
46241
|
+
this.finalize();
|
|
46242
|
+
});
|
|
45881
46243
|
// How could we improve communication between the session and UI?
|
|
45882
46244
|
// It feels weird to have the model piping specific session events to its own bus.
|
|
45883
46245
|
this.session.on("unexpected-revision-id", this, () => this.trigger("unexpected-revision-id"));
|
|
@@ -45925,6 +46287,7 @@ class Model extends EventBus {
|
|
|
45925
46287
|
custom: this.config.custom,
|
|
45926
46288
|
uiActions: this.config,
|
|
45927
46289
|
lazyEvaluation: this.config.lazyEvaluation,
|
|
46290
|
+
session: this.session,
|
|
45928
46291
|
};
|
|
45929
46292
|
}
|
|
45930
46293
|
// ---------------------------------------------------------------------------
|
|
@@ -45940,7 +46303,8 @@ class Model extends EventBus {
|
|
|
45940
46303
|
return this.checkDispatchAllowedLocalCommand(command);
|
|
45941
46304
|
}
|
|
45942
46305
|
checkDispatchAllowedCoreCommand(command) {
|
|
45943
|
-
const results = this.
|
|
46306
|
+
const results = this.corePlugins.map((handler) => handler.allowDispatch(command));
|
|
46307
|
+
results.push(this.range.allowDispatch(command));
|
|
45944
46308
|
return new DispatchResult(results.flat());
|
|
45945
46309
|
}
|
|
45946
46310
|
checkDispatchAllowedLocalCommand(command) {
|
|
@@ -46004,7 +46368,7 @@ class Model extends EventBus {
|
|
|
46004
46368
|
}
|
|
46005
46369
|
}
|
|
46006
46370
|
data.revisionId = this.session.getRevisionId() || DEFAULT_REVISION_ID;
|
|
46007
|
-
data =
|
|
46371
|
+
data = deepCopy(data);
|
|
46008
46372
|
return data;
|
|
46009
46373
|
}
|
|
46010
46374
|
updateMode(mode) {
|
|
@@ -46032,7 +46396,7 @@ class Model extends EventBus {
|
|
|
46032
46396
|
handler.exportForExcel(data);
|
|
46033
46397
|
}
|
|
46034
46398
|
}
|
|
46035
|
-
data =
|
|
46399
|
+
data = deepCopy(data);
|
|
46036
46400
|
return getXLSX(data);
|
|
46037
46401
|
}
|
|
46038
46402
|
garbageCollectExternalResources() {
|
|
@@ -46085,6 +46449,8 @@ const registries = {
|
|
|
46085
46449
|
urlRegistry,
|
|
46086
46450
|
cellPopoverRegistry,
|
|
46087
46451
|
numberFormatMenuRegistry,
|
|
46452
|
+
repeatLocalCommandTransformRegistry,
|
|
46453
|
+
repeatCommandTransformRegistry,
|
|
46088
46454
|
};
|
|
46089
46455
|
const helpers = {
|
|
46090
46456
|
arg,
|
|
@@ -46113,6 +46479,7 @@ const helpers = {
|
|
|
46113
46479
|
positionToZone,
|
|
46114
46480
|
isDefined: isDefined$1,
|
|
46115
46481
|
lazy,
|
|
46482
|
+
genericRepeat,
|
|
46116
46483
|
};
|
|
46117
46484
|
const links = {
|
|
46118
46485
|
isMarkdownLink,
|
|
@@ -46145,6 +46512,6 @@ function addFunction(functionName, functionDescription) {
|
|
|
46145
46512
|
export { AbstractChart, CommandResult, CorePlugin, DATETIME_FORMAT, 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 };
|
|
46146
46513
|
|
|
46147
46514
|
|
|
46148
|
-
__info__.version = '16.3.0-alpha.
|
|
46149
|
-
__info__.date = '2023-04-
|
|
46150
|
-
__info__.hash = '
|
|
46515
|
+
__info__.version = '16.3.0-alpha.5';
|
|
46516
|
+
__info__.date = '2023-04-21T07:28:52.982Z';
|
|
46517
|
+
__info__.hash = '6304f41';
|