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