@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
|
@@ -1724,12 +1724,77 @@ const numberRepresentation = [];
|
|
|
1724
1724
|
* - all digit stored in the decimal part of the number
|
|
1725
1725
|
*
|
|
1726
1726
|
* The 'maxDecimal' parameter allows to indicate the number of digits to not
|
|
1727
|
-
* exceed in the decimal part, in which case digits are rounded
|
|
1727
|
+
* exceed in the decimal part, in which case digits are rounded.
|
|
1728
1728
|
*
|
|
1729
|
-
* Intl.Numberformat is used to properly handle all the roundings.
|
|
1730
|
-
* e.g. 1234.7 with format ### (<> maxDecimals=0) should become 1235, not 1234
|
|
1731
1729
|
**/
|
|
1732
1730
|
function splitNumber(value, maxDecimals = MAX_DECIMAL_PLACES) {
|
|
1731
|
+
const asString = value.toString();
|
|
1732
|
+
if (asString.includes("e"))
|
|
1733
|
+
return splitNumberIntl(value, maxDecimals);
|
|
1734
|
+
if (Number.isInteger(value)) {
|
|
1735
|
+
return { integerDigits: asString, decimalDigits: undefined };
|
|
1736
|
+
}
|
|
1737
|
+
const indexOfDot = asString.indexOf(".");
|
|
1738
|
+
let integerDigits = asString.substring(0, indexOfDot);
|
|
1739
|
+
let decimalDigits = asString.substring(indexOfDot + 1);
|
|
1740
|
+
if (maxDecimals === 0) {
|
|
1741
|
+
if (Number(decimalDigits[0]) >= 5) {
|
|
1742
|
+
integerDigits = (Number(integerDigits) + 1).toString();
|
|
1743
|
+
}
|
|
1744
|
+
return { integerDigits, decimalDigits: undefined };
|
|
1745
|
+
}
|
|
1746
|
+
if (decimalDigits.length > maxDecimals) {
|
|
1747
|
+
const { integerDigits: roundedIntegerDigits, decimalDigits: roundedDecimalDigits } = limitDecimalDigits(decimalDigits, maxDecimals);
|
|
1748
|
+
decimalDigits = roundedDecimalDigits;
|
|
1749
|
+
if (roundedIntegerDigits !== "0") {
|
|
1750
|
+
integerDigits = (Number(integerDigits) + Number(roundedIntegerDigits)).toString();
|
|
1751
|
+
}
|
|
1752
|
+
}
|
|
1753
|
+
return { integerDigits, decimalDigits: removeTrailingZeroes(decimalDigits || "") };
|
|
1754
|
+
}
|
|
1755
|
+
/**
|
|
1756
|
+
* Return the given string minus the trailing "0" characters.
|
|
1757
|
+
*
|
|
1758
|
+
* @param numberString : a string of integers
|
|
1759
|
+
* @returns the numberString, minus the eventual zeroes at the end
|
|
1760
|
+
*/
|
|
1761
|
+
function removeTrailingZeroes(numberString) {
|
|
1762
|
+
let i = numberString.length - 1;
|
|
1763
|
+
while (i >= 0 && numberString[i] === "0") {
|
|
1764
|
+
i--;
|
|
1765
|
+
}
|
|
1766
|
+
return numberString.slice(0, i + 1) || undefined;
|
|
1767
|
+
}
|
|
1768
|
+
/**
|
|
1769
|
+
* Limit the size of the decimal part of a number to the given number of digits.
|
|
1770
|
+
*/
|
|
1771
|
+
function limitDecimalDigits(decimalDigits, maxDecimals) {
|
|
1772
|
+
let integerDigits = "0";
|
|
1773
|
+
let resultDecimalDigits = decimalDigits;
|
|
1774
|
+
// Note : we'd want to simply use number.toFixed() to handle the max digits & rounding,
|
|
1775
|
+
// but it has very strange behaviour. Ex: 12.345.toFixed(2) => "12.35", but 1.345.toFixed(2) => "1.34"
|
|
1776
|
+
let slicedDecimalDigits = decimalDigits.slice(0, maxDecimals);
|
|
1777
|
+
const i = maxDecimals;
|
|
1778
|
+
if (Number(Number(decimalDigits[i]) < 5)) {
|
|
1779
|
+
return { integerDigits, decimalDigits: slicedDecimalDigits };
|
|
1780
|
+
}
|
|
1781
|
+
// round up
|
|
1782
|
+
const slicedRoundedUp = (Number(slicedDecimalDigits) + 1).toString();
|
|
1783
|
+
if (slicedRoundedUp.length > slicedDecimalDigits.length) {
|
|
1784
|
+
integerDigits = (Number(integerDigits) + 1).toString();
|
|
1785
|
+
resultDecimalDigits = undefined;
|
|
1786
|
+
}
|
|
1787
|
+
else {
|
|
1788
|
+
resultDecimalDigits = slicedRoundedUp;
|
|
1789
|
+
}
|
|
1790
|
+
return { integerDigits, decimalDigits: resultDecimalDigits };
|
|
1791
|
+
}
|
|
1792
|
+
/**
|
|
1793
|
+
* Split numbers into decimal/integer digits using Intl.NumberFormat.
|
|
1794
|
+
* Supports numbers with a lot of digits that are transformed to scientific notation by
|
|
1795
|
+
* number.toString(), but is slow.
|
|
1796
|
+
*/
|
|
1797
|
+
function splitNumberIntl(value, maxDecimals = MAX_DECIMAL_PLACES) {
|
|
1733
1798
|
let formatter = numberRepresentation[maxDecimals];
|
|
1734
1799
|
if (!formatter) {
|
|
1735
1800
|
formatter = new Intl.NumberFormat("en-US", {
|
|
@@ -5764,12 +5829,12 @@ const hideSheet = {
|
|
|
5764
5829
|
function createActions(menuItems) {
|
|
5765
5830
|
return menuItems.map(createAction).sort((a, b) => a.sequence - b.sequence);
|
|
5766
5831
|
}
|
|
5767
|
-
const uuidGenerator$
|
|
5832
|
+
const uuidGenerator$2 = new UuidGenerator();
|
|
5768
5833
|
function createAction(item) {
|
|
5769
5834
|
const name = item.name;
|
|
5770
5835
|
const children = item.children;
|
|
5771
5836
|
return {
|
|
5772
|
-
id: item.id || uuidGenerator$
|
|
5837
|
+
id: item.id || uuidGenerator$2.uuidv4(),
|
|
5773
5838
|
name: typeof name === "function" ? name : () => name,
|
|
5774
5839
|
isVisible: item.isVisible ? item.isVisible : () => true,
|
|
5775
5840
|
isEnabled: item.isEnabled ? item.isEnabled : () => true,
|
|
@@ -17439,7 +17504,7 @@ function updateSelectionWithArrowKeys(ev, selection) {
|
|
|
17439
17504
|
}
|
|
17440
17505
|
}
|
|
17441
17506
|
|
|
17442
|
-
const uuidGenerator = new UuidGenerator();
|
|
17507
|
+
const uuidGenerator$1 = new UuidGenerator();
|
|
17443
17508
|
css /* scss */ `
|
|
17444
17509
|
.o-selection {
|
|
17445
17510
|
.o-selection-input {
|
|
@@ -17496,7 +17561,7 @@ css /* scss */ `
|
|
|
17496
17561
|
class SelectionInput extends owl.Component {
|
|
17497
17562
|
constructor() {
|
|
17498
17563
|
super(...arguments);
|
|
17499
|
-
this.id = uuidGenerator.uuidv4();
|
|
17564
|
+
this.id = uuidGenerator$1.uuidv4();
|
|
17500
17565
|
this.previousRanges = this.props.ranges || [];
|
|
17501
17566
|
this.originSheet = this.env.model.getters.getActiveSheetId();
|
|
17502
17567
|
this.state = owl.useState({
|
|
@@ -21189,13 +21254,26 @@ class FilterIconsOverlay extends owl.Component {
|
|
|
21189
21254
|
const sheetId = this.env.model.getters.getActiveSheetId();
|
|
21190
21255
|
const rowDims = this.env.model.getters.getRowDimensionsInViewport(sheetId, position.row);
|
|
21191
21256
|
const colDims = this.env.model.getters.getColDimensionsInViewport(sheetId, position.col);
|
|
21192
|
-
|
|
21193
|
-
const
|
|
21257
|
+
const cell = this.env.model.getters.getCell({ sheetId, ...position });
|
|
21258
|
+
const verticalFilterIconPosition = this.getIconVerticalPosition(rowDims, cell);
|
|
21194
21259
|
return {
|
|
21195
21260
|
x: colDims.end - FILTER_ICON_EDGE_LENGTH + this.props.gridPosition.x - FILTER_ICON_MARGIN - 1,
|
|
21196
|
-
y:
|
|
21261
|
+
y: verticalFilterIconPosition + this.props.gridPosition.y,
|
|
21197
21262
|
};
|
|
21198
21263
|
}
|
|
21264
|
+
// Calculates the vertical position of the filter icon based on the row dimensions and cell styles.
|
|
21265
|
+
getIconVerticalPosition(rowDims, cell) {
|
|
21266
|
+
var _a;
|
|
21267
|
+
const centeringOffset = Math.floor((rowDims.size - FILTER_ICON_EDGE_LENGTH) / 2);
|
|
21268
|
+
switch ((_a = cell === null || cell === void 0 ? void 0 : cell.style) === null || _a === void 0 ? void 0 : _a.verticalAlign) {
|
|
21269
|
+
case "bottom":
|
|
21270
|
+
return rowDims.end - FILTER_ICON_MARGIN - FILTER_ICON_EDGE_LENGTH;
|
|
21271
|
+
case "top":
|
|
21272
|
+
return rowDims.start + FILTER_ICON_MARGIN;
|
|
21273
|
+
default:
|
|
21274
|
+
return rowDims.end - FILTER_ICON_EDGE_LENGTH - centeringOffset;
|
|
21275
|
+
}
|
|
21276
|
+
}
|
|
21199
21277
|
isFilterActive(position) {
|
|
21200
21278
|
const sheetId = this.env.model.getters.getActiveSheetId();
|
|
21201
21279
|
return this.env.model.getters.isFilterActive({ sheetId, ...position });
|
|
@@ -23111,6 +23189,7 @@ class Grid extends owl.Component {
|
|
|
23111
23189
|
"CTRL+A": () => this.env.model.selection.loopSelection(),
|
|
23112
23190
|
"CTRL+Z": () => this.env.model.dispatch("REQUEST_UNDO"),
|
|
23113
23191
|
"CTRL+Y": () => this.env.model.dispatch("REQUEST_REDO"),
|
|
23192
|
+
F4: () => this.env.model.dispatch("REQUEST_REDO"),
|
|
23114
23193
|
"CTRL+B": () => this.env.model.dispatch("SET_FORMATTING", {
|
|
23115
23194
|
sheetId: this.env.model.getters.getActiveSheetId(),
|
|
23116
23195
|
target: this.env.model.getters.getSelectedZones(),
|
|
@@ -23154,8 +23233,8 @@ class Grid extends owl.Component {
|
|
|
23154
23233
|
},
|
|
23155
23234
|
"CTRL+END": () => {
|
|
23156
23235
|
const sheetId = this.env.model.getters.getActiveSheetId();
|
|
23157
|
-
const col = this.env.model.getters.findVisibleHeader(sheetId, "COL",
|
|
23158
|
-
const row = this.env.model.getters.findVisibleHeader(sheetId, "ROW",
|
|
23236
|
+
const col = this.env.model.getters.findVisibleHeader(sheetId, "COL", this.env.model.getters.getNumberCols(sheetId) - 1, 0);
|
|
23237
|
+
const row = this.env.model.getters.findVisibleHeader(sheetId, "ROW", this.env.model.getters.getNumberRows(sheetId) - 1, 0);
|
|
23159
23238
|
this.env.model.selection.selectCell(col, row);
|
|
23160
23239
|
},
|
|
23161
23240
|
"SHIFT+ ": () => {
|
|
@@ -23235,9 +23314,10 @@ class Grid extends owl.Component {
|
|
|
23235
23314
|
this.focus();
|
|
23236
23315
|
}
|
|
23237
23316
|
focus() {
|
|
23317
|
+
var _a;
|
|
23238
23318
|
if (!this.env.model.getters.getSelectedFigureId() &&
|
|
23239
23319
|
this.env.model.getters.getEditionMode() === "inactive") {
|
|
23240
|
-
this.hiddenInput.el.focus();
|
|
23320
|
+
(_a = this.hiddenInput.el) === null || _a === void 0 ? void 0 : _a.focus();
|
|
23241
23321
|
}
|
|
23242
23322
|
}
|
|
23243
23323
|
get gridEl() {
|
|
@@ -27157,7 +27237,7 @@ function load(data, verboseImport) {
|
|
|
27157
27237
|
}
|
|
27158
27238
|
}
|
|
27159
27239
|
}
|
|
27160
|
-
data =
|
|
27240
|
+
data = deepCopy(data);
|
|
27161
27241
|
// apply migrations, if needed
|
|
27162
27242
|
if ("version" in data) {
|
|
27163
27243
|
if (data.version < CURRENT_VERSION) {
|
|
@@ -32275,7 +32355,7 @@ class SheetPlugin extends CorePlugin {
|
|
|
32275
32355
|
duplicateSheet(fromId, toId) {
|
|
32276
32356
|
const sheet = this.getSheet(fromId);
|
|
32277
32357
|
const toName = this.getDuplicateSheetName(sheet.name);
|
|
32278
|
-
const newSheet =
|
|
32358
|
+
const newSheet = deepCopy(sheet);
|
|
32279
32359
|
newSheet.id = toId;
|
|
32280
32360
|
newSheet.name = toName;
|
|
32281
32361
|
for (let col = 0; col <= newSheet.numberOfCols; col++) {
|
|
@@ -32402,10 +32482,10 @@ class SheetPlugin extends CorePlugin {
|
|
|
32402
32482
|
}
|
|
32403
32483
|
}
|
|
32404
32484
|
moveCellOnColumnsDeletion(sheet, deletedColumn) {
|
|
32405
|
-
for (let
|
|
32406
|
-
const
|
|
32485
|
+
for (let rowIndex = 0; rowIndex < sheet.rows.length; rowIndex++) {
|
|
32486
|
+
const row = sheet.rows[rowIndex];
|
|
32407
32487
|
for (let i in row.cells) {
|
|
32408
|
-
const colIndex =
|
|
32488
|
+
const colIndex = Number(i);
|
|
32409
32489
|
const cellId = row.cells[i];
|
|
32410
32490
|
if (cellId) {
|
|
32411
32491
|
if (colIndex === deletedColumn) {
|
|
@@ -32432,11 +32512,11 @@ class SheetPlugin extends CorePlugin {
|
|
|
32432
32512
|
*/
|
|
32433
32513
|
moveCellsOnAddition(sheet, addedElement, quantity, dimension) {
|
|
32434
32514
|
const commands = [];
|
|
32435
|
-
for (
|
|
32436
|
-
const
|
|
32515
|
+
for (let rowIndex = 0; rowIndex < sheet.rows.length; rowIndex++) {
|
|
32516
|
+
const row = sheet.rows[rowIndex];
|
|
32437
32517
|
if (dimension !== "rows" || rowIndex >= addedElement) {
|
|
32438
32518
|
for (let i in row.cells) {
|
|
32439
|
-
const colIndex =
|
|
32519
|
+
const colIndex = Number(i);
|
|
32440
32520
|
const cellId = row.cells[i];
|
|
32441
32521
|
if (cellId) {
|
|
32442
32522
|
if (dimension === "rows" || colIndex >= addedElement) {
|
|
@@ -32466,11 +32546,11 @@ class SheetPlugin extends CorePlugin {
|
|
|
32466
32546
|
*/
|
|
32467
32547
|
moveCellOnRowsDeletion(sheet, deleteFromRow, deleteToRow) {
|
|
32468
32548
|
const numberRows = deleteToRow - deleteFromRow + 1;
|
|
32469
|
-
for (let
|
|
32470
|
-
const
|
|
32549
|
+
for (let rowIndex = 0; rowIndex < sheet.rows.length; rowIndex++) {
|
|
32550
|
+
const row = sheet.rows[rowIndex];
|
|
32471
32551
|
if (rowIndex >= deleteFromRow && rowIndex <= deleteToRow) {
|
|
32472
32552
|
for (let i in row.cells) {
|
|
32473
|
-
const colIndex =
|
|
32553
|
+
const colIndex = Number(i);
|
|
32474
32554
|
const cellId = row.cells[i];
|
|
32475
32555
|
if (cellId) {
|
|
32476
32556
|
this.dispatch("CLEAR_CELL", {
|
|
@@ -32483,7 +32563,7 @@ class SheetPlugin extends CorePlugin {
|
|
|
32483
32563
|
}
|
|
32484
32564
|
if (rowIndex > deleteToRow) {
|
|
32485
32565
|
for (let i in row.cells) {
|
|
32486
|
-
const colIndex =
|
|
32566
|
+
const colIndex = Number(i);
|
|
32487
32567
|
const cellId = row.cells[i];
|
|
32488
32568
|
if (cellId) {
|
|
32489
32569
|
this.dispatch("UPDATE_CELL_POSITION", {
|
|
@@ -32501,7 +32581,7 @@ class SheetPlugin extends CorePlugin {
|
|
|
32501
32581
|
const rows = [];
|
|
32502
32582
|
const cellsQueue = sheet.rows.map((row) => row.cells);
|
|
32503
32583
|
for (let i in sheet.rows) {
|
|
32504
|
-
if (
|
|
32584
|
+
if (Number(i) === index) {
|
|
32505
32585
|
continue;
|
|
32506
32586
|
}
|
|
32507
32587
|
rows.push({
|
|
@@ -33672,13 +33752,11 @@ class FilterEvaluationPlugin extends UIPlugin {
|
|
|
33672
33752
|
return Boolean(id && ((_b = (_a = this.filterValues[sheetId]) === null || _a === void 0 ? void 0 : _a[id]) === null || _b === void 0 ? void 0 : _b.length));
|
|
33673
33753
|
}
|
|
33674
33754
|
intersectZoneWithViewport(sheetId, zone) {
|
|
33675
|
-
const colsRange = range(zone.left, zone.right + 1);
|
|
33676
|
-
const rowsRange = range(zone.top, zone.bottom + 1);
|
|
33677
33755
|
return {
|
|
33678
|
-
left: this.getters.findVisibleHeader(sheetId, "COL",
|
|
33679
|
-
right: this.getters.findVisibleHeader(sheetId, "COL",
|
|
33680
|
-
top: this.getters.findVisibleHeader(sheetId, "ROW",
|
|
33681
|
-
bottom: this.getters.findVisibleHeader(sheetId, "ROW",
|
|
33756
|
+
left: this.getters.findVisibleHeader(sheetId, "COL", zone.left, zone.right),
|
|
33757
|
+
right: this.getters.findVisibleHeader(sheetId, "COL", zone.right, zone.left),
|
|
33758
|
+
top: this.getters.findVisibleHeader(sheetId, "ROW", zone.top, zone.bottom),
|
|
33759
|
+
bottom: this.getters.findVisibleHeader(sheetId, "ROW", zone.bottom, zone.top),
|
|
33682
33760
|
};
|
|
33683
33761
|
}
|
|
33684
33762
|
updateFilter({ col, row, values, sheetId }) {
|
|
@@ -35800,13 +35878,33 @@ class HeaderVisibilityUIPlugin extends UIPlugin {
|
|
|
35800
35878
|
getNextVisibleCellPosition({ sheetId, col, row }) {
|
|
35801
35879
|
return {
|
|
35802
35880
|
sheetId,
|
|
35803
|
-
col: this.findVisibleHeader(sheetId, "COL",
|
|
35804
|
-
row: this.findVisibleHeader(sheetId, "ROW",
|
|
35881
|
+
col: this.findVisibleHeader(sheetId, "COL", col, this.getters.getNumberCols(sheetId) - 1),
|
|
35882
|
+
row: this.findVisibleHeader(sheetId, "ROW", row, this.getters.getNumberRows(sheetId) - 1),
|
|
35805
35883
|
};
|
|
35806
35884
|
}
|
|
35807
|
-
|
|
35808
|
-
|
|
35809
|
-
|
|
35885
|
+
/**
|
|
35886
|
+
* Find the first visible header in the range [`from` => `to`].
|
|
35887
|
+
*
|
|
35888
|
+
* Both `from` and `to` are inclusive.
|
|
35889
|
+
*/
|
|
35890
|
+
findVisibleHeader(sheetId, dimension, from, to) {
|
|
35891
|
+
if (from <= to) {
|
|
35892
|
+
for (let i = from; i <= to; i++) {
|
|
35893
|
+
if (this.getters.doesHeaderExist(sheetId, dimension, i) &&
|
|
35894
|
+
!this.isHeaderHidden(sheetId, dimension, i)) {
|
|
35895
|
+
return i;
|
|
35896
|
+
}
|
|
35897
|
+
}
|
|
35898
|
+
}
|
|
35899
|
+
if (from > to) {
|
|
35900
|
+
for (let i = from; i >= to; i--) {
|
|
35901
|
+
if (this.getters.doesHeaderExist(sheetId, dimension, i) &&
|
|
35902
|
+
!this.isHeaderHidden(sheetId, dimension, i)) {
|
|
35903
|
+
return i;
|
|
35904
|
+
}
|
|
35905
|
+
}
|
|
35906
|
+
}
|
|
35907
|
+
return undefined;
|
|
35810
35908
|
}
|
|
35811
35909
|
findLastVisibleColRowIndex(sheetId, dimension, { last, first }) {
|
|
35812
35910
|
const lastVisibleIndex = range(last, first, -1).find((index) => !this.isHeaderHidden(sheetId, dimension, index));
|
|
@@ -37267,7 +37365,8 @@ class Revision {
|
|
|
37267
37365
|
* - clientId: Client who initiated the action
|
|
37268
37366
|
* - changes: List of changes applied on the state.
|
|
37269
37367
|
*/
|
|
37270
|
-
constructor(id, clientId, commands, changes) {
|
|
37368
|
+
constructor(id, clientId, commands, rootCommand, changes) {
|
|
37369
|
+
this.rootCommand = rootCommand;
|
|
37271
37370
|
this._commands = [];
|
|
37272
37371
|
this._changes = [];
|
|
37273
37372
|
this.id = id;
|
|
@@ -37331,10 +37430,10 @@ class Session extends EventBus {
|
|
|
37331
37430
|
* Add a new revision to the collaborative session.
|
|
37332
37431
|
* It will be transmitted to all other connected clients.
|
|
37333
37432
|
*/
|
|
37334
|
-
save(commands, changes) {
|
|
37433
|
+
save(rootCommand, commands, changes) {
|
|
37335
37434
|
if (!commands.length || !changes.length || !this.canApplyOptimisticUpdate())
|
|
37336
37435
|
return;
|
|
37337
|
-
const revision = new Revision(this.uuidGenerator.uuidv4(), this.clientId, commands, changes);
|
|
37436
|
+
const revision = new Revision(this.uuidGenerator.uuidv4(), this.clientId, commands, rootCommand, changes);
|
|
37338
37437
|
this.revisions.append(revision.id, revision);
|
|
37339
37438
|
this.trigger("new-local-state-update", { id: revision.id });
|
|
37340
37439
|
this.sendUpdateMessage({
|
|
@@ -37435,6 +37534,22 @@ class Session extends EventBus {
|
|
|
37435
37534
|
isFullySynchronized() {
|
|
37436
37535
|
return this.pendingMessages.length === 0;
|
|
37437
37536
|
}
|
|
37537
|
+
/**
|
|
37538
|
+
* Get the last local revision whose root command isn't in the given list of ignored commands
|
|
37539
|
+
* */
|
|
37540
|
+
getLastLocalNonEmptyRevision(ignoredRootCommands) {
|
|
37541
|
+
var _a;
|
|
37542
|
+
const revisions = this.revisions.getRevertedExecution();
|
|
37543
|
+
for (const rev of revisions) {
|
|
37544
|
+
if (rev.rootCommand === "SNAPSHOT")
|
|
37545
|
+
return undefined;
|
|
37546
|
+
if (!rev.rootCommand || rev.rootCommand === "REMOTE")
|
|
37547
|
+
continue;
|
|
37548
|
+
if (!ignoredRootCommands.includes((_a = rev.rootCommand) === null || _a === void 0 ? void 0 : _a.type) && rev.commands.length)
|
|
37549
|
+
return rev;
|
|
37550
|
+
}
|
|
37551
|
+
return undefined;
|
|
37552
|
+
}
|
|
37438
37553
|
_move(position) {
|
|
37439
37554
|
var _a;
|
|
37440
37555
|
// this method is debounced and might be called after the client
|
|
@@ -37494,7 +37609,7 @@ class Session extends EventBus {
|
|
|
37494
37609
|
return;
|
|
37495
37610
|
}
|
|
37496
37611
|
const { clientId, commands } = message;
|
|
37497
|
-
const revision = new Revision(message.nextRevisionId, clientId, commands);
|
|
37612
|
+
const revision = new Revision(message.nextRevisionId, clientId, commands, "REMOTE");
|
|
37498
37613
|
if (revision.clientId !== this.clientId) {
|
|
37499
37614
|
this.revisions.insert(revision.id, revision, message.serverRevisionId);
|
|
37500
37615
|
const pendingCommands = this.pendingMessages
|
|
@@ -37507,7 +37622,7 @@ class Session extends EventBus {
|
|
|
37507
37622
|
}
|
|
37508
37623
|
break;
|
|
37509
37624
|
case "SNAPSHOT_CREATED": {
|
|
37510
|
-
const revision = new Revision(message.nextRevisionId, "server", []);
|
|
37625
|
+
const revision = new Revision(message.nextRevisionId, "server", [], "SNAPSHOT");
|
|
37511
37626
|
this.revisions.insert(revision.id, revision, message.serverRevisionId);
|
|
37512
37627
|
this.dropPendingHistoryMessages();
|
|
37513
37628
|
this.trigger("snapshot");
|
|
@@ -38285,6 +38400,324 @@ SheetUIPlugin.getters = [
|
|
|
38285
38400
|
"getColRowOffset",
|
|
38286
38401
|
];
|
|
38287
38402
|
|
|
38403
|
+
const genericRepeatsTransforms = [
|
|
38404
|
+
repeatSheetDependantCommand,
|
|
38405
|
+
repeatTargetDependantCommand,
|
|
38406
|
+
repeatPositionDependantCommand,
|
|
38407
|
+
];
|
|
38408
|
+
function repeatSheetDependantCommand(getters, command) {
|
|
38409
|
+
if (!("sheetId" in command))
|
|
38410
|
+
return command;
|
|
38411
|
+
return { ...deepCopy(command), sheetId: getters.getActiveSheetId() };
|
|
38412
|
+
}
|
|
38413
|
+
function repeatTargetDependantCommand(getters, command) {
|
|
38414
|
+
if (!("target" in command) || !Array.isArray(command.target))
|
|
38415
|
+
return command;
|
|
38416
|
+
return {
|
|
38417
|
+
...deepCopy(command),
|
|
38418
|
+
target: getters.getSelectedZones(),
|
|
38419
|
+
};
|
|
38420
|
+
}
|
|
38421
|
+
function repeatPositionDependantCommand(getters, command) {
|
|
38422
|
+
if (!("row" in command) || !("col" in command))
|
|
38423
|
+
return command;
|
|
38424
|
+
const { col, row } = getters.getActivePosition();
|
|
38425
|
+
return { ...deepCopy(command), col, row };
|
|
38426
|
+
}
|
|
38427
|
+
|
|
38428
|
+
const uuidGenerator = new UuidGenerator();
|
|
38429
|
+
function repeatCreateChartCommand(getters, cmd) {
|
|
38430
|
+
return {
|
|
38431
|
+
...repeatSheetDependantCommand(getters, cmd),
|
|
38432
|
+
id: uuidGenerator.uuidv4(),
|
|
38433
|
+
};
|
|
38434
|
+
}
|
|
38435
|
+
function repeatCreateImageCommand(getters, cmd) {
|
|
38436
|
+
return {
|
|
38437
|
+
...repeatSheetDependantCommand(getters, cmd),
|
|
38438
|
+
figureId: uuidGenerator.uuidv4(),
|
|
38439
|
+
};
|
|
38440
|
+
}
|
|
38441
|
+
function repeatCreateFigureCommand(getters, cmd) {
|
|
38442
|
+
const newCmd = repeatSheetDependantCommand(getters, cmd);
|
|
38443
|
+
newCmd.figure.id = uuidGenerator.uuidv4();
|
|
38444
|
+
return newCmd;
|
|
38445
|
+
}
|
|
38446
|
+
function repeatCreateSheetCommand(getters, cmd) {
|
|
38447
|
+
var _a;
|
|
38448
|
+
const newCmd = deepCopy(cmd);
|
|
38449
|
+
newCmd.sheetId = uuidGenerator.uuidv4();
|
|
38450
|
+
const sheetName = cmd.name || getters.getSheet(getters.getActiveSheetId()).name;
|
|
38451
|
+
// Extract the prefix of the sheet name (everything before the number at the end of the name)
|
|
38452
|
+
const namePrefix = ((_a = sheetName.match(/(.+?)\d*$/)) === null || _a === void 0 ? void 0 : _a[1]) || sheetName;
|
|
38453
|
+
newCmd.name = getters.getNextSheetName(namePrefix);
|
|
38454
|
+
return newCmd;
|
|
38455
|
+
}
|
|
38456
|
+
function repeatAddColumnsRowsCommand(getters, cmd) {
|
|
38457
|
+
const currentPosition = getters.getActivePosition();
|
|
38458
|
+
return {
|
|
38459
|
+
...repeatSheetDependantCommand(getters, cmd),
|
|
38460
|
+
base: cmd.dimension === "COL" ? currentPosition.col : currentPosition.row,
|
|
38461
|
+
};
|
|
38462
|
+
}
|
|
38463
|
+
function repeatHeaderElementCommand(getters, cmd) {
|
|
38464
|
+
const currentSelection = getters.getSelectedZone();
|
|
38465
|
+
return {
|
|
38466
|
+
...repeatSheetDependantCommand(getters, cmd),
|
|
38467
|
+
elements: cmd.dimension === "COL"
|
|
38468
|
+
? range(currentSelection.left, currentSelection.right + 1)
|
|
38469
|
+
: range(currentSelection.top, currentSelection.bottom + 1),
|
|
38470
|
+
};
|
|
38471
|
+
}
|
|
38472
|
+
function repeatInsertOrDeleteCellCommand(getters, cmd) {
|
|
38473
|
+
const currentSelection = getters.getSelectedZone();
|
|
38474
|
+
return {
|
|
38475
|
+
...deepCopy(cmd),
|
|
38476
|
+
zone: currentSelection,
|
|
38477
|
+
};
|
|
38478
|
+
}
|
|
38479
|
+
function repeatAutoResizeCommand(getters, cmd) {
|
|
38480
|
+
const newCmd = deepCopy(cmd);
|
|
38481
|
+
const currentSelection = getters.getSelectedZone();
|
|
38482
|
+
const { top, bottom, left, right } = currentSelection;
|
|
38483
|
+
if ("cols" in newCmd) {
|
|
38484
|
+
newCmd.cols = range(left, right + 1);
|
|
38485
|
+
}
|
|
38486
|
+
else if ("rows" in newCmd) {
|
|
38487
|
+
newCmd.rows = range(top, bottom + 1);
|
|
38488
|
+
}
|
|
38489
|
+
return newCmd;
|
|
38490
|
+
}
|
|
38491
|
+
function repeatSortCellsCommand(getters, cmd) {
|
|
38492
|
+
const currentSelection = getters.getSelectedZone();
|
|
38493
|
+
return {
|
|
38494
|
+
...repeatSheetDependantCommand(getters, cmd),
|
|
38495
|
+
col: currentSelection.left,
|
|
38496
|
+
row: currentSelection.top,
|
|
38497
|
+
zone: currentSelection,
|
|
38498
|
+
};
|
|
38499
|
+
}
|
|
38500
|
+
function repeatPasteCommand(getters, cmd) {
|
|
38501
|
+
/**
|
|
38502
|
+
* Note : we have to store the state of the clipboard in the clipboard plugin, and introduce a
|
|
38503
|
+
* new command REPEAT_PASTE to be able to repeat the paste command.
|
|
38504
|
+
*
|
|
38505
|
+
* We cannot re-dispatch a paste, because the content of the clipboard may have changed in between.
|
|
38506
|
+
*
|
|
38507
|
+
* And we cannot adapt the sub-commands of the paste command, because they are dependant on the state of the sheet,
|
|
38508
|
+
* and may change based on the paste location. A simple example is that paste create new col/rows for the clipboard
|
|
38509
|
+
* content to fit the sheet. So there will be ADD_COL_ROW_COMMANDS in the sub-commands in the history, but repeating
|
|
38510
|
+
* paste might not need them. Or they could only needed for the repeated paste, not for the original.
|
|
38511
|
+
*/
|
|
38512
|
+
return {
|
|
38513
|
+
type: "REPEAT_PASTE",
|
|
38514
|
+
pasteOption: deepCopy(cmd.pasteOption),
|
|
38515
|
+
target: getters.getSelectedZones(),
|
|
38516
|
+
};
|
|
38517
|
+
}
|
|
38518
|
+
|
|
38519
|
+
/**
|
|
38520
|
+
* Registry containing all the command that can be repeated on redo, and function to transform them
|
|
38521
|
+
* to the current state of the model.
|
|
38522
|
+
*
|
|
38523
|
+
* If the transform function is undefined, the command will be transformed using generic transformations.
|
|
38524
|
+
* (change the sheetId, the row, the col, the target, the ranges, to the current active sheet & selection)
|
|
38525
|
+
*
|
|
38526
|
+
*/
|
|
38527
|
+
const repeatCommandTransformRegistry = new Registry();
|
|
38528
|
+
repeatCommandTransformRegistry.add("UPDATE_CELL", genericRepeat);
|
|
38529
|
+
repeatCommandTransformRegistry.add("CLEAR_CELL", genericRepeat);
|
|
38530
|
+
repeatCommandTransformRegistry.add("DELETE_CONTENT", genericRepeat);
|
|
38531
|
+
repeatCommandTransformRegistry.add("ADD_MERGE", genericRepeat);
|
|
38532
|
+
repeatCommandTransformRegistry.add("REMOVE_MERGE", genericRepeat);
|
|
38533
|
+
repeatCommandTransformRegistry.add("SET_FORMATTING", genericRepeat);
|
|
38534
|
+
repeatCommandTransformRegistry.add("CLEAR_FORMATTING", genericRepeat);
|
|
38535
|
+
repeatCommandTransformRegistry.add("SET_BORDER", genericRepeat);
|
|
38536
|
+
repeatCommandTransformRegistry.add("CREATE_FILTER_TABLE", genericRepeat);
|
|
38537
|
+
repeatCommandTransformRegistry.add("REMOVE_FILTER_TABLE", genericRepeat);
|
|
38538
|
+
repeatCommandTransformRegistry.add("HIDE_SHEET", genericRepeat);
|
|
38539
|
+
repeatCommandTransformRegistry.add("ADD_COLUMNS_ROWS", repeatAddColumnsRowsCommand);
|
|
38540
|
+
repeatCommandTransformRegistry.add("REMOVE_COLUMNS_ROWS", repeatHeaderElementCommand);
|
|
38541
|
+
repeatCommandTransformRegistry.add("HIDE_COLUMNS_ROWS", repeatHeaderElementCommand);
|
|
38542
|
+
repeatCommandTransformRegistry.add("RESIZE_COLUMNS_ROWS", repeatHeaderElementCommand);
|
|
38543
|
+
repeatCommandTransformRegistry.add("CREATE_SHEET", repeatCreateSheetCommand);
|
|
38544
|
+
repeatCommandTransformRegistry.add("CREATE_FIGURE", repeatCreateFigureCommand);
|
|
38545
|
+
repeatCommandTransformRegistry.add("CREATE_CHART", repeatCreateChartCommand);
|
|
38546
|
+
repeatCommandTransformRegistry.add("CREATE_IMAGE", repeatCreateImageCommand);
|
|
38547
|
+
const repeatLocalCommandTransformRegistry = new Registry();
|
|
38548
|
+
repeatLocalCommandTransformRegistry.add("STOP_EDITION", repeatLocalCommandChildren);
|
|
38549
|
+
repeatLocalCommandTransformRegistry.add("PASTE", repeatPasteCommand);
|
|
38550
|
+
repeatLocalCommandTransformRegistry.add("INSERT_CELL", repeatInsertOrDeleteCellCommand);
|
|
38551
|
+
repeatLocalCommandTransformRegistry.add("DELETE_CELL", repeatInsertOrDeleteCellCommand);
|
|
38552
|
+
repeatLocalCommandTransformRegistry.add("AUTORESIZE_COLUMNS", repeatAutoResizeCommand);
|
|
38553
|
+
repeatLocalCommandTransformRegistry.add("AUTORESIZE_ROWS", repeatAutoResizeCommand);
|
|
38554
|
+
repeatLocalCommandTransformRegistry.add("SORT_CELLS", repeatSortCellsCommand);
|
|
38555
|
+
repeatLocalCommandTransformRegistry.add("SUM_SELECTION", genericRepeat);
|
|
38556
|
+
repeatLocalCommandTransformRegistry.add("SET_DECIMAL", genericRepeat);
|
|
38557
|
+
function genericRepeat(getters, command) {
|
|
38558
|
+
let transformedCommand = deepCopy(command);
|
|
38559
|
+
for (const repeatTransform of genericRepeatsTransforms) {
|
|
38560
|
+
transformedCommand = repeatTransform(getters, transformedCommand);
|
|
38561
|
+
}
|
|
38562
|
+
return transformedCommand;
|
|
38563
|
+
}
|
|
38564
|
+
function repeatCoreCommand(getters, command) {
|
|
38565
|
+
if (!command) {
|
|
38566
|
+
return undefined;
|
|
38567
|
+
}
|
|
38568
|
+
const isRepeatable = repeatCommandTransformRegistry.contains(command.type);
|
|
38569
|
+
if (!isRepeatable) {
|
|
38570
|
+
return undefined;
|
|
38571
|
+
}
|
|
38572
|
+
const transform = repeatCommandTransformRegistry.get(command.type);
|
|
38573
|
+
return transform(getters, command);
|
|
38574
|
+
}
|
|
38575
|
+
function repeatLocalCommand(getters, command, childCommands) {
|
|
38576
|
+
const isRepeatable = repeatLocalCommandTransformRegistry.contains(command.type);
|
|
38577
|
+
if (!isRepeatable) {
|
|
38578
|
+
return undefined;
|
|
38579
|
+
}
|
|
38580
|
+
const repeatTransform = repeatLocalCommandTransformRegistry.get(command.type);
|
|
38581
|
+
return repeatTransform(getters, command, childCommands);
|
|
38582
|
+
}
|
|
38583
|
+
function repeatLocalCommandChildren(getters, cmd, childCommands) {
|
|
38584
|
+
return childCommands
|
|
38585
|
+
.map((childCommand) => repeatCoreCommand(getters, childCommand))
|
|
38586
|
+
.filter(isDefined$1);
|
|
38587
|
+
}
|
|
38588
|
+
|
|
38589
|
+
function canRepeatRevision(revision) {
|
|
38590
|
+
if (!revision || !revision.rootCommand || typeof revision.rootCommand !== "object") {
|
|
38591
|
+
return false;
|
|
38592
|
+
}
|
|
38593
|
+
if (isCoreCommand(revision.rootCommand)) {
|
|
38594
|
+
return repeatCommandTransformRegistry.contains(revision.rootCommand.type);
|
|
38595
|
+
}
|
|
38596
|
+
return repeatLocalCommandTransformRegistry.contains(revision.rootCommand.type);
|
|
38597
|
+
}
|
|
38598
|
+
function repeatRevision(revision, getters) {
|
|
38599
|
+
if (!revision.rootCommand || typeof revision.rootCommand !== "object") {
|
|
38600
|
+
return undefined;
|
|
38601
|
+
}
|
|
38602
|
+
if (isCoreCommand(revision.rootCommand)) {
|
|
38603
|
+
return repeatCoreCommand(getters, revision.rootCommand);
|
|
38604
|
+
}
|
|
38605
|
+
return repeatLocalCommand(getters, revision.rootCommand, revision.commands);
|
|
38606
|
+
}
|
|
38607
|
+
|
|
38608
|
+
/**
|
|
38609
|
+
* Local History
|
|
38610
|
+
*
|
|
38611
|
+
* The local history is responsible of tracking the locally state updates
|
|
38612
|
+
* It maintains the local undo and redo stack to allow to undo/redo only local
|
|
38613
|
+
* changes
|
|
38614
|
+
*/
|
|
38615
|
+
class HistoryPlugin extends UIPlugin {
|
|
38616
|
+
constructor(config) {
|
|
38617
|
+
super(config);
|
|
38618
|
+
/**
|
|
38619
|
+
* Ids of the revisions which can be undone
|
|
38620
|
+
*/
|
|
38621
|
+
this.undoStack = [];
|
|
38622
|
+
/**
|
|
38623
|
+
* Ids of the revisions which can be redone
|
|
38624
|
+
*/
|
|
38625
|
+
this.redoStack = [];
|
|
38626
|
+
this.session = config.session;
|
|
38627
|
+
this.session.on("new-local-state-update", this, this.onNewLocalStateUpdate);
|
|
38628
|
+
this.session.on("pending-revisions-dropped", this, ({ revisionIds }) => this.drop(revisionIds));
|
|
38629
|
+
this.session.on("snapshot", this, () => {
|
|
38630
|
+
this.undoStack = [];
|
|
38631
|
+
this.redoStack = [];
|
|
38632
|
+
});
|
|
38633
|
+
}
|
|
38634
|
+
allowDispatch(cmd) {
|
|
38635
|
+
switch (cmd.type) {
|
|
38636
|
+
case "REQUEST_UNDO":
|
|
38637
|
+
if (!this.canUndo()) {
|
|
38638
|
+
return 6 /* CommandResult.EmptyUndoStack */;
|
|
38639
|
+
}
|
|
38640
|
+
break;
|
|
38641
|
+
case "REQUEST_REDO":
|
|
38642
|
+
if (!this.canRedo()) {
|
|
38643
|
+
return 7 /* CommandResult.EmptyRedoStack */;
|
|
38644
|
+
}
|
|
38645
|
+
break;
|
|
38646
|
+
}
|
|
38647
|
+
return 0 /* CommandResult.Success */;
|
|
38648
|
+
}
|
|
38649
|
+
handle(cmd) {
|
|
38650
|
+
switch (cmd.type) {
|
|
38651
|
+
case "REQUEST_UNDO":
|
|
38652
|
+
case "REQUEST_REDO":
|
|
38653
|
+
// History changes (undo & redo) are *not* applied optimistically on the local state.
|
|
38654
|
+
// We wait a global confirmation from the server. The goal is to avoid handling concurrent
|
|
38655
|
+
// history changes on multiple clients which are very hard to manage correctly.
|
|
38656
|
+
this.requestHistoryChange(cmd.type === "REQUEST_UNDO" ? "UNDO" : "REDO");
|
|
38657
|
+
}
|
|
38658
|
+
}
|
|
38659
|
+
finalize() { }
|
|
38660
|
+
requestHistoryChange(type) {
|
|
38661
|
+
const id = type === "UNDO" ? this.undoStack.pop() : this.redoStack.pop();
|
|
38662
|
+
if (!id) {
|
|
38663
|
+
const lastNonRedoRevision = this.getPossibleRevisionToRepeat();
|
|
38664
|
+
if (!lastNonRedoRevision) {
|
|
38665
|
+
return;
|
|
38666
|
+
}
|
|
38667
|
+
const repeatedCommands = repeatRevision(lastNonRedoRevision, this.getters);
|
|
38668
|
+
if (!repeatedCommands) {
|
|
38669
|
+
return;
|
|
38670
|
+
}
|
|
38671
|
+
if (!Array.isArray(repeatedCommands)) {
|
|
38672
|
+
this.dispatch(repeatedCommands.type, repeatedCommands);
|
|
38673
|
+
return;
|
|
38674
|
+
}
|
|
38675
|
+
for (const command of repeatedCommands) {
|
|
38676
|
+
this.dispatch(command.type, command);
|
|
38677
|
+
}
|
|
38678
|
+
return;
|
|
38679
|
+
}
|
|
38680
|
+
if (type === "UNDO") {
|
|
38681
|
+
this.session.undo(id);
|
|
38682
|
+
this.redoStack.push(id);
|
|
38683
|
+
}
|
|
38684
|
+
else {
|
|
38685
|
+
this.session.redo(id);
|
|
38686
|
+
this.undoStack.push(id);
|
|
38687
|
+
}
|
|
38688
|
+
}
|
|
38689
|
+
canUndo() {
|
|
38690
|
+
return this.undoStack.length > 0;
|
|
38691
|
+
}
|
|
38692
|
+
canRedo() {
|
|
38693
|
+
if (this.redoStack.length > 0)
|
|
38694
|
+
return true;
|
|
38695
|
+
const lastNonRedoRevision = this.getPossibleRevisionToRepeat();
|
|
38696
|
+
return canRepeatRevision(lastNonRedoRevision);
|
|
38697
|
+
}
|
|
38698
|
+
drop(revisionIds) {
|
|
38699
|
+
this.undoStack = this.undoStack.filter((id) => !revisionIds.includes(id));
|
|
38700
|
+
this.redoStack = [];
|
|
38701
|
+
}
|
|
38702
|
+
onNewLocalStateUpdate({ id }) {
|
|
38703
|
+
this.undoStack.push(id);
|
|
38704
|
+
this.redoStack = [];
|
|
38705
|
+
if (this.undoStack.length > MAX_HISTORY_STEPS) {
|
|
38706
|
+
this.undoStack.shift();
|
|
38707
|
+
}
|
|
38708
|
+
}
|
|
38709
|
+
/**
|
|
38710
|
+
* Fetch the last revision which is not empty and not a repeated command
|
|
38711
|
+
*
|
|
38712
|
+
* Ignore repeated commands (REQUEST_REDO command as root command)
|
|
38713
|
+
* Ignore standard undo/redo revisions (that are empty)
|
|
38714
|
+
*/
|
|
38715
|
+
getPossibleRevisionToRepeat() {
|
|
38716
|
+
return this.session.getLastLocalNonEmptyRevision(["REQUEST_REDO"]);
|
|
38717
|
+
}
|
|
38718
|
+
}
|
|
38719
|
+
HistoryPlugin.getters = ["canUndo", "canRedo"];
|
|
38720
|
+
|
|
38288
38721
|
/** Abstract state of the clipboard when copying/cutting content that is pasted in cells of the sheet */
|
|
38289
38722
|
class ClipboardCellsAbstractState {
|
|
38290
38723
|
constructor(operation, getters, dispatch, selection) {
|
|
@@ -38994,7 +39427,7 @@ class ClipboardPlugin extends UIPlugin {
|
|
|
38994
39427
|
return 0 /* CommandResult.Success */;
|
|
38995
39428
|
}
|
|
38996
39429
|
handle(cmd) {
|
|
38997
|
-
var _a, _b;
|
|
39430
|
+
var _a, _b, _c;
|
|
38998
39431
|
switch (cmd.type) {
|
|
38999
39432
|
case "COPY":
|
|
39000
39433
|
case "CUT":
|
|
@@ -39012,6 +39445,7 @@ class ClipboardPlugin extends UIPlugin {
|
|
|
39012
39445
|
if (this.state.operation === "CUT") {
|
|
39013
39446
|
this.state = undefined;
|
|
39014
39447
|
}
|
|
39448
|
+
this.lastPasteState = this.state;
|
|
39015
39449
|
this.status = "invisible";
|
|
39016
39450
|
break;
|
|
39017
39451
|
case "CLEAN_CLIPBOARD_HIGHLIGHT":
|
|
@@ -39064,10 +39498,19 @@ class ClipboardPlugin extends UIPlugin {
|
|
|
39064
39498
|
break;
|
|
39065
39499
|
}
|
|
39066
39500
|
case "PASTE_FROM_OS_CLIPBOARD":
|
|
39067
|
-
|
|
39068
|
-
state.paste(cmd.target);
|
|
39501
|
+
this.state = new ClipboardOsState(cmd.text, this.getters, this.dispatch, this.selection);
|
|
39502
|
+
this.state.paste(cmd.target);
|
|
39503
|
+
this.lastPasteState = this.state;
|
|
39069
39504
|
this.status = "invisible";
|
|
39070
39505
|
break;
|
|
39506
|
+
case "REPEAT_PASTE": {
|
|
39507
|
+
(_c = this.lastPasteState) === null || _c === void 0 ? void 0 : _c.paste(cmd.target, {
|
|
39508
|
+
pasteOption: cmd.pasteOption,
|
|
39509
|
+
shouldPasteCF: true,
|
|
39510
|
+
selectTarget: true,
|
|
39511
|
+
});
|
|
39512
|
+
break;
|
|
39513
|
+
}
|
|
39071
39514
|
case "ACTIVATE_PAINT_FORMAT": {
|
|
39072
39515
|
const zones = this.getters.getSelectedZones();
|
|
39073
39516
|
this.state = this.getClipboardStateForCopyCells(zones, "COPY");
|
|
@@ -40548,7 +40991,8 @@ const featurePluginRegistry = new Registry()
|
|
|
40548
40991
|
.add("automatic_sum", AutomaticSumPlugin)
|
|
40549
40992
|
.add("format", FormatPlugin)
|
|
40550
40993
|
.add("cell_popovers", CellPopoverPlugin)
|
|
40551
|
-
.add("selection_multiuser", SelectionMultiUserPlugin)
|
|
40994
|
+
.add("selection_multiuser", SelectionMultiUserPlugin)
|
|
40995
|
+
.add("history", HistoryPlugin);
|
|
40552
40996
|
// Plugins which have a state, but which should not be shared in collaborative
|
|
40553
40997
|
const statefulUIPluginRegistry = new Registry()
|
|
40554
40998
|
.add("selection", GridSelectionPlugin)
|
|
@@ -43235,6 +43679,14 @@ class SelectiveHistory {
|
|
|
43235
43679
|
this.revertBefore(operationId);
|
|
43236
43680
|
this.tree.drop(operationId);
|
|
43237
43681
|
}
|
|
43682
|
+
getRevertedExecution() {
|
|
43683
|
+
const data = [];
|
|
43684
|
+
const operations = this.tree.revertedExecution(this.HEAD_BRANCH);
|
|
43685
|
+
for (const { operation } of operations) {
|
|
43686
|
+
data.push(operation.data);
|
|
43687
|
+
}
|
|
43688
|
+
return data;
|
|
43689
|
+
}
|
|
43238
43690
|
/**
|
|
43239
43691
|
* Revert the state as it was *before* the given operation was executed.
|
|
43240
43692
|
*/
|
|
@@ -43291,12 +43743,12 @@ function buildRevisionLog(initialRevisionId, recordChanges, dispatch) {
|
|
|
43291
43743
|
}
|
|
43292
43744
|
});
|
|
43293
43745
|
revision.setChanges(changes);
|
|
43294
|
-
}, (revision) => revertChanges([revision]), (id) => new Revision(id, "empty", []
|
|
43746
|
+
}, (revision) => revertChanges([revision]), (id) => new Revision(id, "empty", []), {
|
|
43295
43747
|
with: (revision) => (toTransform) => {
|
|
43296
|
-
return new Revision(toTransform.id, toTransform.clientId, transformAll(toTransform.commands, revision.commands));
|
|
43748
|
+
return new Revision(toTransform.id, toTransform.clientId, transformAll(toTransform.commands, revision.commands), toTransform.rootCommand);
|
|
43297
43749
|
},
|
|
43298
43750
|
without: (revision) => (toTransform) => {
|
|
43299
|
-
return new Revision(toTransform.id, toTransform.clientId, transformAll(toTransform.commands, revision.commands.map(inverseCommand).flat()));
|
|
43751
|
+
return new Revision(toTransform.id, toTransform.clientId, transformAll(toTransform.commands, revision.commands.map(inverseCommand).flat()), toTransform.rootCommand);
|
|
43300
43752
|
},
|
|
43301
43753
|
});
|
|
43302
43754
|
}
|
|
@@ -43333,101 +43785,6 @@ function applyChange(change, target) {
|
|
|
43333
43785
|
}
|
|
43334
43786
|
}
|
|
43335
43787
|
|
|
43336
|
-
/**
|
|
43337
|
-
* Local History
|
|
43338
|
-
*
|
|
43339
|
-
* The local history is responsible of tracking the locally state updates
|
|
43340
|
-
* It maintains the local undo and redo stack to allow to undo/redo only local
|
|
43341
|
-
* changes
|
|
43342
|
-
*/
|
|
43343
|
-
class LocalHistory extends owl.EventBus {
|
|
43344
|
-
constructor(dispatch, session) {
|
|
43345
|
-
super();
|
|
43346
|
-
this.dispatch = dispatch;
|
|
43347
|
-
this.session = session;
|
|
43348
|
-
/**
|
|
43349
|
-
* Ids of the revisions which can be undone
|
|
43350
|
-
*/
|
|
43351
|
-
this.undoStack = [];
|
|
43352
|
-
/**
|
|
43353
|
-
* Ids of the revisions which can be redone
|
|
43354
|
-
*/
|
|
43355
|
-
this.redoStack = [];
|
|
43356
|
-
this.session.on("new-local-state-update", this, this.onNewLocalStateUpdate);
|
|
43357
|
-
this.session.on("revision-undone", this, ({ commands }) => this.selectiveUndo(commands));
|
|
43358
|
-
this.session.on("revision-redone", this, ({ commands }) => this.selectiveRedo(commands));
|
|
43359
|
-
this.session.on("pending-revisions-dropped", this, ({ revisionIds }) => this.drop(revisionIds));
|
|
43360
|
-
this.session.on("snapshot", this, () => {
|
|
43361
|
-
this.undoStack = [];
|
|
43362
|
-
this.redoStack = [];
|
|
43363
|
-
});
|
|
43364
|
-
}
|
|
43365
|
-
allowDispatch(cmd) {
|
|
43366
|
-
switch (cmd.type) {
|
|
43367
|
-
case "REQUEST_UNDO":
|
|
43368
|
-
if (!this.canUndo()) {
|
|
43369
|
-
return 6 /* CommandResult.EmptyUndoStack */;
|
|
43370
|
-
}
|
|
43371
|
-
break;
|
|
43372
|
-
case "REQUEST_REDO":
|
|
43373
|
-
if (!this.canRedo()) {
|
|
43374
|
-
return 7 /* CommandResult.EmptyRedoStack */;
|
|
43375
|
-
}
|
|
43376
|
-
break;
|
|
43377
|
-
}
|
|
43378
|
-
return 0 /* CommandResult.Success */;
|
|
43379
|
-
}
|
|
43380
|
-
beforeHandle(cmd) { }
|
|
43381
|
-
handle(cmd) {
|
|
43382
|
-
switch (cmd.type) {
|
|
43383
|
-
case "REQUEST_UNDO":
|
|
43384
|
-
case "REQUEST_REDO":
|
|
43385
|
-
// History changes (undo & redo) are *not* applied optimistically on the local state.
|
|
43386
|
-
// We wait a global confirmation from the server. The goal is to avoid handling concurrent
|
|
43387
|
-
// history changes on multiple clients which are very hard to manage correctly.
|
|
43388
|
-
this.requestHistoryChange(cmd.type === "REQUEST_UNDO" ? "UNDO" : "REDO");
|
|
43389
|
-
}
|
|
43390
|
-
}
|
|
43391
|
-
finalize() { }
|
|
43392
|
-
requestHistoryChange(type) {
|
|
43393
|
-
const id = type === "UNDO" ? this.undoStack.pop() : this.redoStack.pop();
|
|
43394
|
-
if (!id) {
|
|
43395
|
-
return;
|
|
43396
|
-
}
|
|
43397
|
-
if (type === "UNDO") {
|
|
43398
|
-
this.session.undo(id);
|
|
43399
|
-
this.redoStack.push(id);
|
|
43400
|
-
}
|
|
43401
|
-
else {
|
|
43402
|
-
this.session.redo(id);
|
|
43403
|
-
this.undoStack.push(id);
|
|
43404
|
-
}
|
|
43405
|
-
}
|
|
43406
|
-
canUndo() {
|
|
43407
|
-
return this.undoStack.length > 0;
|
|
43408
|
-
}
|
|
43409
|
-
canRedo() {
|
|
43410
|
-
return this.redoStack.length > 0;
|
|
43411
|
-
}
|
|
43412
|
-
drop(revisionIds) {
|
|
43413
|
-
this.undoStack = this.undoStack.filter((id) => !revisionIds.includes(id));
|
|
43414
|
-
this.redoStack = [];
|
|
43415
|
-
}
|
|
43416
|
-
onNewLocalStateUpdate({ id }) {
|
|
43417
|
-
this.undoStack.push(id);
|
|
43418
|
-
this.redoStack = [];
|
|
43419
|
-
if (this.undoStack.length > MAX_HISTORY_STEPS) {
|
|
43420
|
-
this.undoStack.shift();
|
|
43421
|
-
}
|
|
43422
|
-
}
|
|
43423
|
-
selectiveUndo(commands) {
|
|
43424
|
-
this.dispatch("UNDO", { commands });
|
|
43425
|
-
}
|
|
43426
|
-
selectiveRedo(commands) {
|
|
43427
|
-
this.dispatch("REDO", { commands });
|
|
43428
|
-
}
|
|
43429
|
-
}
|
|
43430
|
-
|
|
43431
43788
|
/**
|
|
43432
43789
|
* Stateless sequence of events that can be processed by consumers.
|
|
43433
43790
|
*
|
|
@@ -43828,8 +44185,8 @@ class SelectionStreamProcessorImpl {
|
|
|
43828
44185
|
}
|
|
43829
44186
|
const { left, right, top, bottom } = zone;
|
|
43830
44187
|
const sheetId = this.getters.getActiveSheetId();
|
|
43831
|
-
const refCol = this.getters.findVisibleHeader(sheetId, "COL",
|
|
43832
|
-
const refRow = this.getters.findVisibleHeader(sheetId, "ROW",
|
|
44188
|
+
const refCol = this.getters.findVisibleHeader(sheetId, "COL", left, right);
|
|
44189
|
+
const refRow = this.getters.findVisibleHeader(sheetId, "ROW", top, bottom);
|
|
43833
44190
|
if (refRow === undefined || refCol === undefined) {
|
|
43834
44191
|
return 17 /* CommandResult.SelectionOutOfBound */;
|
|
43835
44192
|
}
|
|
@@ -43904,10 +44261,10 @@ class SelectionStreamProcessorImpl {
|
|
|
43904
44261
|
const { col: anchorCol, row: anchorRow } = anchor.cell;
|
|
43905
44262
|
return {
|
|
43906
44263
|
col: this.getters.isColHidden(sheetId, anchorCol)
|
|
43907
|
-
? this.getters.findVisibleHeader(sheetId, "COL",
|
|
44264
|
+
? this.getters.findVisibleHeader(sheetId, "COL", left, right) || anchorCol
|
|
43908
44265
|
: anchorCol,
|
|
43909
44266
|
row: this.getters.isRowHidden(sheetId, anchorRow)
|
|
43910
|
-
? this.getters.findVisibleHeader(sheetId, "ROW",
|
|
44267
|
+
? this.getters.findVisibleHeader(sheetId, "ROW", top, bottom) || anchorRow
|
|
43911
44268
|
: anchorRow,
|
|
43912
44269
|
};
|
|
43913
44270
|
}
|
|
@@ -45657,6 +46014,8 @@ class Model extends EventBus {
|
|
|
45657
46014
|
* Internal status of the model. Important for command handling coordination
|
|
45658
46015
|
*/
|
|
45659
46016
|
this.status = 0 /* Status.Ready */;
|
|
46017
|
+
this.handlers = [];
|
|
46018
|
+
this.coreHandlers = [];
|
|
45660
46019
|
/**
|
|
45661
46020
|
* Check if a command can be dispatched, and returns a DispatchResult object with the possible
|
|
45662
46021
|
* reasons the dispatch failed.
|
|
@@ -45701,7 +46060,7 @@ class Model extends EventBus {
|
|
|
45701
46060
|
this.dispatchToHandlers(this.handlers, command);
|
|
45702
46061
|
this.finalize();
|
|
45703
46062
|
});
|
|
45704
|
-
this.session.save(commands, changes);
|
|
46063
|
+
this.session.save(command, commands, changes);
|
|
45705
46064
|
this.status = 0 /* Status.Ready */;
|
|
45706
46065
|
this.trigger("update");
|
|
45707
46066
|
break;
|
|
@@ -45733,9 +46092,7 @@ class Model extends EventBus {
|
|
|
45733
46092
|
const command = { ...payload, type };
|
|
45734
46093
|
const previousStatus = this.status;
|
|
45735
46094
|
this.status = 2 /* Status.RunningCore */;
|
|
45736
|
-
const handlers = this.isReplayingCommand
|
|
45737
|
-
? [this.range, ...this.corePlugins, ...this.coreViewsPlugins]
|
|
45738
|
-
: this.handlers;
|
|
46095
|
+
const handlers = this.isReplayingCommand ? this.coreHandlers : this.handlers;
|
|
45739
46096
|
this.dispatchToHandlers(handlers, command);
|
|
45740
46097
|
this.status = previousStatus;
|
|
45741
46098
|
return DispatchResult.Success;
|
|
@@ -45746,7 +46103,6 @@ class Model extends EventBus {
|
|
|
45746
46103
|
this.uuidGenerator = uuidGenerator;
|
|
45747
46104
|
this.config = this.setupConfig(config);
|
|
45748
46105
|
this.session = this.setupSession(workbookData.revisionId);
|
|
45749
|
-
this.history = new LocalHistory(this.dispatchFromCorePlugin, this.session);
|
|
45750
46106
|
this.coreGetters = {};
|
|
45751
46107
|
this.range = new RangeAdapter(this.coreGetters);
|
|
45752
46108
|
this.coreGetters.getRangeString = this.range.getRangeString.bind(this.range);
|
|
@@ -45759,8 +46115,6 @@ class Model extends EventBus {
|
|
|
45759
46115
|
this.getters = {
|
|
45760
46116
|
isReadonly: () => this.config.mode === "readonly" || this.config.mode === "dashboard",
|
|
45761
46117
|
isDashboard: () => this.config.mode === "dashboard",
|
|
45762
|
-
canUndo: this.history.canUndo.bind(this.history),
|
|
45763
|
-
canRedo: this.history.canRedo.bind(this.history),
|
|
45764
46118
|
getClient: this.session.getClient.bind(this.session),
|
|
45765
46119
|
getConnectedClients: this.session.getConnectedClients.bind(this.session),
|
|
45766
46120
|
isFullySynchronized: this.session.isFullySynchronized.bind(this.session),
|
|
@@ -45768,6 +46122,8 @@ class Model extends EventBus {
|
|
|
45768
46122
|
this.uuidGenerator.setIsFastStrategy(true);
|
|
45769
46123
|
// Initiate stream processor
|
|
45770
46124
|
this.selection = new SelectionStreamProcessorImpl(this.getters);
|
|
46125
|
+
this.coreHandlers.push(this.range);
|
|
46126
|
+
this.handlers.push(this.range);
|
|
45771
46127
|
this.corePluginConfig = this.setupCorePluginConfig();
|
|
45772
46128
|
this.uiPluginConfig = this.setupUiPluginConfig();
|
|
45773
46129
|
// registering plugins
|
|
@@ -45776,13 +46132,20 @@ class Model extends EventBus {
|
|
|
45776
46132
|
}
|
|
45777
46133
|
Object.assign(this.getters, this.coreGetters);
|
|
45778
46134
|
for (let Plugin of statefulUIPluginRegistry.getAll()) {
|
|
45779
|
-
this.
|
|
46135
|
+
const plugin = this.setupUiPlugin(Plugin);
|
|
46136
|
+
this.statefulUIPlugins.push(plugin);
|
|
46137
|
+
this.handlers.push(plugin);
|
|
45780
46138
|
}
|
|
45781
46139
|
for (let Plugin of coreViewsPluginRegistry.getAll()) {
|
|
45782
|
-
this.
|
|
46140
|
+
const plugin = this.setupUiPlugin(Plugin);
|
|
46141
|
+
this.coreViewsPlugins.push(plugin);
|
|
46142
|
+
this.handlers.push(plugin);
|
|
46143
|
+
this.coreHandlers.push(plugin);
|
|
45783
46144
|
}
|
|
45784
46145
|
for (let Plugin of featurePluginRegistry.getAll()) {
|
|
45785
|
-
this.
|
|
46146
|
+
const plugin = this.setupUiPlugin(Plugin);
|
|
46147
|
+
this.featurePlugins.push(plugin);
|
|
46148
|
+
this.handlers.push(plugin);
|
|
45786
46149
|
}
|
|
45787
46150
|
this.uuidGenerator.setIsFastStrategy(false);
|
|
45788
46151
|
// starting plugins
|
|
@@ -45806,15 +46169,6 @@ class Model extends EventBus {
|
|
|
45806
46169
|
// by owl, since we do not rely on reactivity
|
|
45807
46170
|
owl.markRaw(this);
|
|
45808
46171
|
}
|
|
45809
|
-
get handlers() {
|
|
45810
|
-
return [...this.coreHandlers, ...this.allUIPlugins, this.history];
|
|
45811
|
-
}
|
|
45812
|
-
get coreHandlers() {
|
|
45813
|
-
return [this.range, ...this.corePlugins];
|
|
45814
|
-
}
|
|
45815
|
-
get allUIPlugins() {
|
|
45816
|
-
return [...this.statefulUIPlugins, ...this.coreViewsPlugins, ...this.featurePlugins];
|
|
45817
|
-
}
|
|
45818
46172
|
joinSession() {
|
|
45819
46173
|
this.session.join(this.config.client);
|
|
45820
46174
|
}
|
|
@@ -45856,6 +46210,8 @@ class Model extends EventBus {
|
|
|
45856
46210
|
}
|
|
45857
46211
|
plugin.import(data);
|
|
45858
46212
|
this.corePlugins.push(plugin);
|
|
46213
|
+
this.coreHandlers.push(plugin);
|
|
46214
|
+
this.handlers.push(plugin);
|
|
45859
46215
|
}
|
|
45860
46216
|
onRemoteRevisionReceived({ commands }) {
|
|
45861
46217
|
for (let command of commands) {
|
|
@@ -45873,15 +46229,21 @@ class Model extends EventBus {
|
|
|
45873
46229
|
return;
|
|
45874
46230
|
}
|
|
45875
46231
|
this.isReplayingCommand = true;
|
|
45876
|
-
this.dispatchToHandlers(
|
|
46232
|
+
this.dispatchToHandlers(this.coreHandlers, command);
|
|
45877
46233
|
this.isReplayingCommand = false;
|
|
45878
46234
|
}), this.config.transportService, revisionId);
|
|
45879
46235
|
return session;
|
|
45880
46236
|
}
|
|
45881
46237
|
setupSessionEvents() {
|
|
45882
46238
|
this.session.on("remote-revision-received", this, this.onRemoteRevisionReceived);
|
|
45883
|
-
this.session.on("revision-
|
|
45884
|
-
|
|
46239
|
+
this.session.on("revision-undone", this, ({ commands }) => {
|
|
46240
|
+
this.dispatchFromCorePlugin("UNDO", { commands });
|
|
46241
|
+
this.finalize();
|
|
46242
|
+
});
|
|
46243
|
+
this.session.on("revision-redone", this, ({ commands }) => {
|
|
46244
|
+
this.dispatchFromCorePlugin("REDO", { commands });
|
|
46245
|
+
this.finalize();
|
|
46246
|
+
});
|
|
45885
46247
|
// How could we improve communication between the session and UI?
|
|
45886
46248
|
// It feels weird to have the model piping specific session events to its own bus.
|
|
45887
46249
|
this.session.on("unexpected-revision-id", this, () => this.trigger("unexpected-revision-id"));
|
|
@@ -45929,6 +46291,7 @@ class Model extends EventBus {
|
|
|
45929
46291
|
custom: this.config.custom,
|
|
45930
46292
|
uiActions: this.config,
|
|
45931
46293
|
lazyEvaluation: this.config.lazyEvaluation,
|
|
46294
|
+
session: this.session,
|
|
45932
46295
|
};
|
|
45933
46296
|
}
|
|
45934
46297
|
// ---------------------------------------------------------------------------
|
|
@@ -45944,7 +46307,8 @@ class Model extends EventBus {
|
|
|
45944
46307
|
return this.checkDispatchAllowedLocalCommand(command);
|
|
45945
46308
|
}
|
|
45946
46309
|
checkDispatchAllowedCoreCommand(command) {
|
|
45947
|
-
const results = this.
|
|
46310
|
+
const results = this.corePlugins.map((handler) => handler.allowDispatch(command));
|
|
46311
|
+
results.push(this.range.allowDispatch(command));
|
|
45948
46312
|
return new DispatchResult(results.flat());
|
|
45949
46313
|
}
|
|
45950
46314
|
checkDispatchAllowedLocalCommand(command) {
|
|
@@ -46008,7 +46372,7 @@ class Model extends EventBus {
|
|
|
46008
46372
|
}
|
|
46009
46373
|
}
|
|
46010
46374
|
data.revisionId = this.session.getRevisionId() || DEFAULT_REVISION_ID;
|
|
46011
|
-
data =
|
|
46375
|
+
data = deepCopy(data);
|
|
46012
46376
|
return data;
|
|
46013
46377
|
}
|
|
46014
46378
|
updateMode(mode) {
|
|
@@ -46036,7 +46400,7 @@ class Model extends EventBus {
|
|
|
46036
46400
|
handler.exportForExcel(data);
|
|
46037
46401
|
}
|
|
46038
46402
|
}
|
|
46039
|
-
data =
|
|
46403
|
+
data = deepCopy(data);
|
|
46040
46404
|
return getXLSX(data);
|
|
46041
46405
|
}
|
|
46042
46406
|
garbageCollectExternalResources() {
|
|
@@ -46089,6 +46453,8 @@ const registries = {
|
|
|
46089
46453
|
urlRegistry,
|
|
46090
46454
|
cellPopoverRegistry,
|
|
46091
46455
|
numberFormatMenuRegistry,
|
|
46456
|
+
repeatLocalCommandTransformRegistry,
|
|
46457
|
+
repeatCommandTransformRegistry,
|
|
46092
46458
|
};
|
|
46093
46459
|
const helpers = {
|
|
46094
46460
|
arg,
|
|
@@ -46117,6 +46483,7 @@ const helpers = {
|
|
|
46117
46483
|
positionToZone,
|
|
46118
46484
|
isDefined: isDefined$1,
|
|
46119
46485
|
lazy,
|
|
46486
|
+
genericRepeat,
|
|
46120
46487
|
};
|
|
46121
46488
|
const links = {
|
|
46122
46489
|
isMarkdownLink,
|
|
@@ -46178,6 +46545,6 @@ exports.setTranslationMethod = setTranslationMethod;
|
|
|
46178
46545
|
exports.tokenize = tokenize;
|
|
46179
46546
|
|
|
46180
46547
|
|
|
46181
|
-
__info__.version = '16.3.0-alpha.
|
|
46182
|
-
__info__.date = '2023-04-
|
|
46183
|
-
__info__.hash = '
|
|
46548
|
+
__info__.version = '16.3.0-alpha.5';
|
|
46549
|
+
__info__.date = '2023-04-21T07:28:52.982Z';
|
|
46550
|
+
__info__.hash = '6304f41';
|