@odoo/o-spreadsheet 19.1.0-alpha.13 → 19.1.0-alpha.14
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-engine.d.ts +18 -6
- package/dist/o-spreadsheet-engine.esm.js +342 -60
- package/dist/o-spreadsheet-engine.iife.js +342 -60
- package/dist/o-spreadsheet-engine.min.iife.js +313 -313
- package/dist/o-spreadsheet.d.ts +35 -21
- package/dist/o_spreadsheet.esm.js +518 -143
- package/dist/o_spreadsheet.iife.js +518 -143
- package/dist/o_spreadsheet.min.iife.js +320 -320
- package/dist/o_spreadsheet.xml +31 -16
- package/package.json +1 -1
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
* This file is generated by o-spreadsheet build tools. Do not edit it.
|
|
4
4
|
* @see https://github.com/odoo/o-spreadsheet
|
|
5
5
|
* @version 19.1.0-alpha.3
|
|
6
|
-
* @date 2025-
|
|
7
|
-
* @hash
|
|
6
|
+
* @date 2025-12-02T05:34:07.213Z
|
|
7
|
+
* @hash 04cf666
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
class FunctionCodeBuilder {
|
|
@@ -3106,8 +3106,56 @@ function isMultipleElementMatrix(arg) {
|
|
|
3106
3106
|
return isMatrix(arg) && !isSingleElementMatrix(arg);
|
|
3107
3107
|
}
|
|
3108
3108
|
|
|
3109
|
+
function stackHorizontally(ranges, options) {
|
|
3110
|
+
const matrices = ranges.map(toMatrix);
|
|
3111
|
+
const nbRowsArr = matrices.map((m) => m?.[0]?.length ?? 0);
|
|
3112
|
+
const nbRows = Math.max(...nbRowsArr);
|
|
3113
|
+
if (options?.requireSameRowCount) {
|
|
3114
|
+
const firstLength = nbRowsArr[0];
|
|
3115
|
+
if (nbRowsArr.some((len) => len !== firstLength)) {
|
|
3116
|
+
return new EvaluationError(_t("All ranges in [[FUNCTION_NAME]] must have the same number of columns (got %s).", nbRowsArr.join(", ")));
|
|
3117
|
+
}
|
|
3118
|
+
}
|
|
3119
|
+
const result = [];
|
|
3120
|
+
for (const matrix of matrices) {
|
|
3121
|
+
for (let col = 0; col < matrix.length; col++) {
|
|
3122
|
+
// Fill with nulls if needed
|
|
3123
|
+
const array = Array(nbRows).fill({ value: null });
|
|
3124
|
+
for (let row = 0; row < matrix[col].length; row++) {
|
|
3125
|
+
array[row] = matrix[col][row];
|
|
3126
|
+
}
|
|
3127
|
+
result.push(array);
|
|
3128
|
+
}
|
|
3129
|
+
}
|
|
3130
|
+
return result;
|
|
3131
|
+
}
|
|
3132
|
+
function stackVertically(ranges, options) {
|
|
3133
|
+
const matrices = ranges.map(toMatrix);
|
|
3134
|
+
const nbColsArr = matrices.map((m) => m?.length ?? 0);
|
|
3135
|
+
const nbCols = Math.max(...nbColsArr);
|
|
3136
|
+
if (options?.requireSameColCount) {
|
|
3137
|
+
const firstLength = nbColsArr[0];
|
|
3138
|
+
if (nbColsArr.some((len) => len !== firstLength)) {
|
|
3139
|
+
return new EvaluationError(_t("All ranges in [[FUNCTION_NAME]] must have the same number of columns (got %s).", nbColsArr.join(", ")));
|
|
3140
|
+
}
|
|
3141
|
+
}
|
|
3142
|
+
const nbRows = matrices.reduce((acc, m) => acc + (m?.[0]?.length ?? 0), 0);
|
|
3143
|
+
const result = generateMatrix(nbCols, nbRows, () => ({
|
|
3144
|
+
value: null,
|
|
3145
|
+
}));
|
|
3146
|
+
let currentRow = 0;
|
|
3147
|
+
for (const matrix of matrices) {
|
|
3148
|
+
for (let col = 0; col < matrix.length; col++) {
|
|
3149
|
+
for (let row = 0; row < matrix[col].length; row++) {
|
|
3150
|
+
result[col][currentRow + row] = matrix[col][row];
|
|
3151
|
+
}
|
|
3152
|
+
}
|
|
3153
|
+
currentRow += matrix[0]?.length ?? 0;
|
|
3154
|
+
}
|
|
3155
|
+
return result;
|
|
3156
|
+
}
|
|
3109
3157
|
// -----------------------------------------------------------------------------
|
|
3110
|
-
//
|
|
3158
|
+
// ARRAY.CONSTRAIN
|
|
3111
3159
|
// -----------------------------------------------------------------------------
|
|
3112
3160
|
const ARRAY_CONSTRAIN = {
|
|
3113
3161
|
description: _t("Returns a result array constrained to a specific width and height."),
|
|
@@ -3133,6 +3181,30 @@ const ARRAY_CONSTRAIN = {
|
|
|
3133
3181
|
isExported: false,
|
|
3134
3182
|
};
|
|
3135
3183
|
// -----------------------------------------------------------------------------
|
|
3184
|
+
// ARRAY.LITERAL
|
|
3185
|
+
// -----------------------------------------------------------------------------
|
|
3186
|
+
const ARRAY_LITERAL = {
|
|
3187
|
+
description: _t("Appends ranges vertically and in sequence to return a larger array. All ranges must have the same number of columns."),
|
|
3188
|
+
args: [arg("range (any, range<any>, repeating)", _t("The range to be appended."))],
|
|
3189
|
+
compute: function (...ranges) {
|
|
3190
|
+
return stackVertically(ranges, { requireSameColCount: true });
|
|
3191
|
+
},
|
|
3192
|
+
isExported: false,
|
|
3193
|
+
hidden: true,
|
|
3194
|
+
};
|
|
3195
|
+
// -----------------------------------------------------------------------------
|
|
3196
|
+
// ARRAY.ROW
|
|
3197
|
+
// -----------------------------------------------------------------------------
|
|
3198
|
+
const ARRAY_ROW = {
|
|
3199
|
+
description: _t("Appends ranges horizontally and in sequence to return a larger array. All ranges must have the same number of rows."),
|
|
3200
|
+
args: [arg("range (any, range<any>, repeating)", _t("The range to be appended."))],
|
|
3201
|
+
compute: function (...ranges) {
|
|
3202
|
+
return stackHorizontally(ranges, { requireSameRowCount: true });
|
|
3203
|
+
},
|
|
3204
|
+
isExported: false,
|
|
3205
|
+
hidden: true,
|
|
3206
|
+
};
|
|
3207
|
+
// -----------------------------------------------------------------------------
|
|
3136
3208
|
// CHOOSECOLS
|
|
3137
3209
|
// -----------------------------------------------------------------------------
|
|
3138
3210
|
const CHOOSECOLS = {
|
|
@@ -3278,20 +3350,7 @@ const HSTACK = {
|
|
|
3278
3350
|
description: _t("Appends ranges horizontally and in sequence to return a larger array."),
|
|
3279
3351
|
args: [arg("range (any, range<any>, repeating)", _t("The range to be appended."))],
|
|
3280
3352
|
compute: function (...ranges) {
|
|
3281
|
-
|
|
3282
|
-
const result = [];
|
|
3283
|
-
for (const range of ranges) {
|
|
3284
|
-
const _range = toMatrix(range);
|
|
3285
|
-
for (let col = 0; col < _range.length; col++) {
|
|
3286
|
-
//TODO: fill with #N/A for unavailable values instead of zeroes
|
|
3287
|
-
const array = Array(nbRows).fill({ value: null });
|
|
3288
|
-
for (let row = 0; row < _range[col].length; row++) {
|
|
3289
|
-
array[row] = _range[col][row];
|
|
3290
|
-
}
|
|
3291
|
-
result.push(array);
|
|
3292
|
-
}
|
|
3293
|
-
}
|
|
3294
|
-
return result;
|
|
3353
|
+
return stackHorizontally(ranges);
|
|
3295
3354
|
},
|
|
3296
3355
|
isExported: true,
|
|
3297
3356
|
};
|
|
@@ -3550,22 +3609,7 @@ const VSTACK = {
|
|
|
3550
3609
|
description: _t("Appends ranges vertically and in sequence to return a larger array."),
|
|
3551
3610
|
args: [arg("range (any, range<any>, repeating)", _t("The range to be appended."))],
|
|
3552
3611
|
compute: function (...ranges) {
|
|
3553
|
-
|
|
3554
|
-
const nbRows = ranges.reduce((acc, range) => acc + toMatrix(range)[0].length, 0);
|
|
3555
|
-
const result = Array(nbColumns)
|
|
3556
|
-
.fill([])
|
|
3557
|
-
.map(() => Array(nbRows).fill({ value: 0 })); // TODO fill with #N/A
|
|
3558
|
-
let currentRow = 0;
|
|
3559
|
-
for (const range of ranges) {
|
|
3560
|
-
const _array = toMatrix(range);
|
|
3561
|
-
for (let col = 0; col < _array.length; col++) {
|
|
3562
|
-
for (let row = 0; row < _array[col].length; row++) {
|
|
3563
|
-
result[col][currentRow + row] = _array[col][row];
|
|
3564
|
-
}
|
|
3565
|
-
}
|
|
3566
|
-
currentRow += _array[0].length;
|
|
3567
|
-
}
|
|
3568
|
-
return result;
|
|
3612
|
+
return stackVertically(ranges);
|
|
3569
3613
|
},
|
|
3570
3614
|
isExported: true,
|
|
3571
3615
|
};
|
|
@@ -3625,6 +3669,8 @@ const WRAPROWS = {
|
|
|
3625
3669
|
var array = /*#__PURE__*/Object.freeze({
|
|
3626
3670
|
__proto__: null,
|
|
3627
3671
|
ARRAY_CONSTRAIN: ARRAY_CONSTRAIN,
|
|
3672
|
+
ARRAY_LITERAL: ARRAY_LITERAL,
|
|
3673
|
+
ARRAY_ROW: ARRAY_ROW,
|
|
3628
3674
|
CHOOSECOLS: CHOOSECOLS,
|
|
3629
3675
|
CHOOSEROWS: CHOOSEROWS,
|
|
3630
3676
|
EXPAND: EXPAND,
|
|
@@ -6080,6 +6126,47 @@ function splitIfAdjacent(zone, zoneToRemove) {
|
|
|
6080
6126
|
return newZones;
|
|
6081
6127
|
}
|
|
6082
6128
|
}
|
|
6129
|
+
/**
|
|
6130
|
+
* Splits zone z2 by removing the overlapping zone z1 (fully inside z2).
|
|
6131
|
+
* Returns the remaining parts of z2 that don't overlap with z1.
|
|
6132
|
+
*
|
|
6133
|
+
* Diagram:
|
|
6134
|
+
* ┌──────────── z2 ─────────────┐
|
|
6135
|
+
* │ 1 │
|
|
6136
|
+
* │--------─────────────--------│
|
|
6137
|
+
* │ 2 | z1 | 3 │
|
|
6138
|
+
* │--------─────────────--------│
|
|
6139
|
+
* │ 4 │
|
|
6140
|
+
* └─────────────────────────────┘
|
|
6141
|
+
*
|
|
6142
|
+
* Input:
|
|
6143
|
+
* z1 = { top: 2, bottom: 3, left: 2, right: 3 }
|
|
6144
|
+
* z2 = { top: 1, bottom: 4, left: 1, right: 4 }
|
|
6145
|
+
*
|
|
6146
|
+
* Output:
|
|
6147
|
+
* [
|
|
6148
|
+
* { top: 4, bottom: 4, left: 1, right: 4 }, // bottom
|
|
6149
|
+
* { top: 2, bottom: 3, left: 4, right: 4 }, // right
|
|
6150
|
+
* { top: 2, bottom: 3, left: 1, right: 1 }, // left
|
|
6151
|
+
* { top: 1, bottom: 1, left: 1, right: 4 } // top
|
|
6152
|
+
* ]
|
|
6153
|
+
*/
|
|
6154
|
+
function splitZone(z1, z2) {
|
|
6155
|
+
const zones = [];
|
|
6156
|
+
if (z1.bottom < z2.bottom) {
|
|
6157
|
+
zones.push({ ...z2, top: z1.bottom + 1 });
|
|
6158
|
+
}
|
|
6159
|
+
if (z1.right < z2.right) {
|
|
6160
|
+
zones.push({ ...z2, left: z1.right + 1, top: z1.top, bottom: z1.bottom });
|
|
6161
|
+
}
|
|
6162
|
+
if (z1.left > z2.left) {
|
|
6163
|
+
zones.push({ ...z2, right: z1.left - 1, top: z1.top, bottom: z1.bottom });
|
|
6164
|
+
}
|
|
6165
|
+
if (z1.top > z2.top) {
|
|
6166
|
+
zones.push({ ...z2, bottom: z1.top - 1 });
|
|
6167
|
+
}
|
|
6168
|
+
return zones;
|
|
6169
|
+
}
|
|
6083
6170
|
|
|
6084
6171
|
function isSheetDependent(cmd) {
|
|
6085
6172
|
return "sheetId" in cmd;
|
|
@@ -6110,7 +6197,6 @@ const invalidateEvaluationCommands = new Set([
|
|
|
6110
6197
|
"REDO",
|
|
6111
6198
|
"ADD_MERGE",
|
|
6112
6199
|
"REMOVE_MERGE",
|
|
6113
|
-
"DUPLICATE_SHEET",
|
|
6114
6200
|
"UPDATE_LOCALE",
|
|
6115
6201
|
"ADD_PIVOT",
|
|
6116
6202
|
"UPDATE_PIVOT",
|
|
@@ -9600,7 +9686,9 @@ function tokenize(str, locale = DEFAULT_LOCALE) {
|
|
|
9600
9686
|
while (!chars.isOver()) {
|
|
9601
9687
|
let token = tokenizeNewLine(chars) ||
|
|
9602
9688
|
tokenizeSpace(chars) ||
|
|
9689
|
+
tokenizeArrayRowSeparator(chars, locale) ||
|
|
9603
9690
|
tokenizeArgsSeparator(chars, locale) ||
|
|
9691
|
+
tokenizeBraces(chars) ||
|
|
9604
9692
|
tokenizeParenthesis(chars) ||
|
|
9605
9693
|
tokenizeOperator(chars) ||
|
|
9606
9694
|
tokenizeString(chars) ||
|
|
@@ -9633,6 +9721,17 @@ function tokenizeParenthesis(chars) {
|
|
|
9633
9721
|
}
|
|
9634
9722
|
return null;
|
|
9635
9723
|
}
|
|
9724
|
+
const braces = {
|
|
9725
|
+
"{": { type: "LEFT_BRACE", value: "{" },
|
|
9726
|
+
"}": { type: "RIGHT_BRACE", value: "}" },
|
|
9727
|
+
};
|
|
9728
|
+
function tokenizeBraces(chars) {
|
|
9729
|
+
if (chars.current === "{" || chars.current === "}") {
|
|
9730
|
+
const value = chars.shift();
|
|
9731
|
+
return braces[value];
|
|
9732
|
+
}
|
|
9733
|
+
return null;
|
|
9734
|
+
}
|
|
9636
9735
|
function tokenizeArgsSeparator(chars, locale) {
|
|
9637
9736
|
if (chars.current === locale.formulaArgSeparator) {
|
|
9638
9737
|
const value = chars.shift();
|
|
@@ -9641,6 +9740,20 @@ function tokenizeArgsSeparator(chars, locale) {
|
|
|
9641
9740
|
}
|
|
9642
9741
|
return null;
|
|
9643
9742
|
}
|
|
9743
|
+
function tokenizeArrayRowSeparator(chars, locale) {
|
|
9744
|
+
// The array row separator is used in array literals to separate rows.
|
|
9745
|
+
// It is not explicitly defined in locales, but depends on the formulaArgSeparator.
|
|
9746
|
+
// Example: {1,2,3;4,5,6} — here, ';' separates rows and ',' separates columns.
|
|
9747
|
+
const rowSeparator = locale.formulaArgSeparator === ";" ? "\\" : ";";
|
|
9748
|
+
if (!rowSeparator) {
|
|
9749
|
+
return null;
|
|
9750
|
+
}
|
|
9751
|
+
if (chars.current === rowSeparator) {
|
|
9752
|
+
chars.shift();
|
|
9753
|
+
return { type: "ARRAY_ROW_SEPARATOR", value: rowSeparator };
|
|
9754
|
+
}
|
|
9755
|
+
return null;
|
|
9756
|
+
}
|
|
9644
9757
|
function tokenizeOperator(chars) {
|
|
9645
9758
|
for (const op of OPERATORS) {
|
|
9646
9759
|
if (chars.currentStartsWith(op)) {
|
|
@@ -9861,6 +9974,9 @@ function _localizeFormula(formula, fromLocale, toLocale) {
|
|
|
9861
9974
|
else if (token.type === "ARG_SEPARATOR") {
|
|
9862
9975
|
localizedFormula += toLocale.formulaArgSeparator;
|
|
9863
9976
|
}
|
|
9977
|
+
else if (token.type === "ARRAY_ROW_SEPARATOR") {
|
|
9978
|
+
localizedFormula += toLocale.formulaArgSeparator === ";" ? "\\" : ";";
|
|
9979
|
+
}
|
|
9864
9980
|
else {
|
|
9865
9981
|
localizedFormula += token.value;
|
|
9866
9982
|
}
|
|
@@ -11024,7 +11140,6 @@ function sortMatrix(matrix, locale, ...criteria) {
|
|
|
11024
11140
|
// -----------------------------------------------------------------------------
|
|
11025
11141
|
const FILTER = {
|
|
11026
11142
|
description: _t("Returns a filtered version of the source range, returning only rows or columns that meet the specified conditions."),
|
|
11027
|
-
// TODO modify args description when vectorization on formulas is available
|
|
11028
11143
|
args: [
|
|
11029
11144
|
arg("range (any, range<any>)", _t("The data to be filtered.")),
|
|
11030
11145
|
arg("condition (boolean, range<boolean>, repeating)", _t("Column or row containing true or false values corresponding to the range.")),
|
|
@@ -17224,6 +17339,8 @@ function parseOperand(tokens) {
|
|
|
17224
17339
|
tokenStartIndex: current.tokenIndex,
|
|
17225
17340
|
tokenEndIndex: rightParen.tokenIndex,
|
|
17226
17341
|
};
|
|
17342
|
+
case "LEFT_BRACE":
|
|
17343
|
+
return parseArrayLiteral(tokens, current);
|
|
17227
17344
|
case "OPERATOR":
|
|
17228
17345
|
const operator = current.value;
|
|
17229
17346
|
if (UNARY_OPERATORS_PREFIX.includes(operator)) {
|
|
@@ -17277,6 +17394,34 @@ function consumeOrThrow(tokens, type, message) {
|
|
|
17277
17394
|
}
|
|
17278
17395
|
return token;
|
|
17279
17396
|
}
|
|
17397
|
+
function parseArrayLiteral(tokens, leftBrace) {
|
|
17398
|
+
const rows = [];
|
|
17399
|
+
let currentRow = [parseExpression(tokens)]; // there must be at least one element
|
|
17400
|
+
while (tokens.current?.type !== "RIGHT_BRACE") {
|
|
17401
|
+
const nextToken = tokens.shift();
|
|
17402
|
+
if (!nextToken) {
|
|
17403
|
+
throw new BadExpressionError(_t("Missing closing brace"));
|
|
17404
|
+
}
|
|
17405
|
+
else if (nextToken.type === "ARG_SEPARATOR") {
|
|
17406
|
+
currentRow.push(parseExpression(tokens));
|
|
17407
|
+
}
|
|
17408
|
+
else if (nextToken.type === "ARRAY_ROW_SEPARATOR") {
|
|
17409
|
+
rows.push(currentRow);
|
|
17410
|
+
currentRow = [parseExpression(tokens)];
|
|
17411
|
+
}
|
|
17412
|
+
else {
|
|
17413
|
+
throw new BadExpressionError(_t("Unexpected token: %s", nextToken.value));
|
|
17414
|
+
}
|
|
17415
|
+
}
|
|
17416
|
+
const rightBrace = consumeOrThrow(tokens, "RIGHT_BRACE", _t("Missing closing brace"));
|
|
17417
|
+
rows.push(currentRow);
|
|
17418
|
+
return {
|
|
17419
|
+
type: "ARRAY",
|
|
17420
|
+
value: rows,
|
|
17421
|
+
tokenStartIndex: leftBrace.tokenIndex,
|
|
17422
|
+
tokenEndIndex: rightBrace.tokenIndex,
|
|
17423
|
+
};
|
|
17424
|
+
}
|
|
17280
17425
|
function parseExpression(tokens, parent_priority = 0) {
|
|
17281
17426
|
if (tokens.length === 0) {
|
|
17282
17427
|
throw new BadExpressionError();
|
|
@@ -17367,6 +17512,13 @@ function* astIterator(ast) {
|
|
|
17367
17512
|
yield* astIterator(arg);
|
|
17368
17513
|
}
|
|
17369
17514
|
break;
|
|
17515
|
+
case "ARRAY":
|
|
17516
|
+
for (const row of ast.value) {
|
|
17517
|
+
for (const cell of row) {
|
|
17518
|
+
yield* astIterator(cell);
|
|
17519
|
+
}
|
|
17520
|
+
}
|
|
17521
|
+
break;
|
|
17370
17522
|
case "UNARY_OPERATION":
|
|
17371
17523
|
yield* astIterator(ast.operand);
|
|
17372
17524
|
break;
|
|
@@ -17384,6 +17536,11 @@ function mapAst(ast, fn) {
|
|
|
17384
17536
|
...ast,
|
|
17385
17537
|
args: ast.args.map((child) => mapAst(child, fn)),
|
|
17386
17538
|
};
|
|
17539
|
+
case "ARRAY":
|
|
17540
|
+
return {
|
|
17541
|
+
...ast,
|
|
17542
|
+
value: ast.value.map((row) => row.map((cell) => mapAst(cell, fn))),
|
|
17543
|
+
};
|
|
17387
17544
|
case "UNARY_OPERATION":
|
|
17388
17545
|
return {
|
|
17389
17546
|
...ast,
|
|
@@ -17542,6 +17699,23 @@ function compileTokensOrThrow(tokens) {
|
|
|
17542
17699
|
code.append(...args);
|
|
17543
17700
|
const fnName = ast.value.toUpperCase();
|
|
17544
17701
|
return code.return(`ctx['${fnName}'](${args.map((arg) => arg.returnExpression)})`);
|
|
17702
|
+
case "ARRAY": {
|
|
17703
|
+
// a literal array is compiled into function calls
|
|
17704
|
+
const arrayFunctionCall = {
|
|
17705
|
+
type: "FUNCALL",
|
|
17706
|
+
value: "ARRAY.LITERAL",
|
|
17707
|
+
args: ast.value.map((row) => ({
|
|
17708
|
+
type: "FUNCALL",
|
|
17709
|
+
value: "ARRAY.ROW",
|
|
17710
|
+
args: row,
|
|
17711
|
+
tokenStartIndex: 0,
|
|
17712
|
+
tokenEndIndex: 0,
|
|
17713
|
+
})),
|
|
17714
|
+
tokenStartIndex: 0,
|
|
17715
|
+
tokenEndIndex: 0,
|
|
17716
|
+
};
|
|
17717
|
+
return compileAST(arrayFunctionCall);
|
|
17718
|
+
}
|
|
17545
17719
|
case "UNARY_OPERATION": {
|
|
17546
17720
|
const fnName = UNARY_OPERATOR_MAP[ast.value];
|
|
17547
17721
|
const operand = compileAST(ast.operand, false, false).assignResultToVariable();
|
|
@@ -25884,6 +26058,17 @@ class XlsxReader {
|
|
|
25884
26058
|
}
|
|
25885
26059
|
}
|
|
25886
26060
|
|
|
26061
|
+
function schemeToColorScale(scheme) {
|
|
26062
|
+
const colors = COLORSCHEMES[scheme];
|
|
26063
|
+
return colors === undefined
|
|
26064
|
+
? undefined
|
|
26065
|
+
: {
|
|
26066
|
+
minColor: colors[0],
|
|
26067
|
+
midColor: colors.length === 3 ? colors[1] : undefined,
|
|
26068
|
+
maxColor: colors[colors.length - 1],
|
|
26069
|
+
};
|
|
26070
|
+
}
|
|
26071
|
+
|
|
25887
26072
|
/**
|
|
25888
26073
|
* parses a formula (as a string) into the same formula,
|
|
25889
26074
|
* but with the references to other cells extracted
|
|
@@ -26528,13 +26713,22 @@ migrationStepRegistry
|
|
|
26528
26713
|
return data;
|
|
26529
26714
|
},
|
|
26530
26715
|
})
|
|
26531
|
-
.add("19.
|
|
26716
|
+
.add("19.1.0", {
|
|
26532
26717
|
migrate(data) {
|
|
26533
26718
|
for (const sheet of data.sheets || []) {
|
|
26534
26719
|
for (const figure of sheet.figures || []) {
|
|
26535
26720
|
if (figure.tag === "chart" && figure.data.type === "geo") {
|
|
26536
26721
|
if ("colorScale" in figure.data && typeof figure.data.colorScale === "string") {
|
|
26537
|
-
figure.data.colorScale =
|
|
26722
|
+
figure.data.colorScale = schemeToColorScale(figure.data.colorScale);
|
|
26723
|
+
}
|
|
26724
|
+
}
|
|
26725
|
+
if (figure.tag === "carousel") {
|
|
26726
|
+
for (const definition of Object.values(figure.data.chartDefinitions) || []) {
|
|
26727
|
+
if (definition.type === "geo") {
|
|
26728
|
+
if ("colorScale" in definition && typeof definition.colorScale === "string") {
|
|
26729
|
+
definition.colorScale = schemeToColorScale(definition.colorScale);
|
|
26730
|
+
}
|
|
26731
|
+
}
|
|
26538
26732
|
}
|
|
26539
26733
|
}
|
|
26540
26734
|
}
|
|
@@ -26797,18 +26991,22 @@ function dropCommands(initialMessages, commandType) {
|
|
|
26797
26991
|
return messages;
|
|
26798
26992
|
}
|
|
26799
26993
|
function fixChartDefinitions(data, initialMessages) {
|
|
26994
|
+
/**
|
|
26995
|
+
* Revisions created after version 18.5.1 contain the full chart definition in the command
|
|
26996
|
+
* if the data was alreay updated to 18.5.1, then those older revision cannot (by definition) be reaplied
|
|
26997
|
+
* and should not be replayed.
|
|
26998
|
+
* FIXME: every command should be versionned when upgraded to allow finer tuning.
|
|
26999
|
+
*/
|
|
27000
|
+
if (!data.version || compareVersions(String(data.version), "18.5.1") >= 0) {
|
|
27001
|
+
return initialMessages;
|
|
27002
|
+
}
|
|
26800
27003
|
const messages = [];
|
|
26801
27004
|
const map = {};
|
|
26802
27005
|
for (const sheet of data.sheets || []) {
|
|
26803
27006
|
sheet.figures?.forEach((figure) => {
|
|
26804
27007
|
if (figure.tag === "chart") {
|
|
26805
27008
|
// chart definition
|
|
26806
|
-
|
|
26807
|
-
map[figure.data.chartId] = figure.data;
|
|
26808
|
-
}
|
|
26809
|
-
else {
|
|
26810
|
-
map[figure.id] = figure.data;
|
|
26811
|
-
}
|
|
27009
|
+
map[figure.id] = figure.data;
|
|
26812
27010
|
}
|
|
26813
27011
|
});
|
|
26814
27012
|
}
|
|
@@ -29848,7 +30046,8 @@ class FigurePlugin extends CorePlugin {
|
|
|
29848
30046
|
}
|
|
29849
30047
|
break;
|
|
29850
30048
|
case "DUPLICATE_SHEET": {
|
|
29851
|
-
for (const
|
|
30049
|
+
for (const figure of this.getFigures(cmd.sheetId)) {
|
|
30050
|
+
const figureId = figure.id;
|
|
29852
30051
|
const fig = this.figures[cmd.sheetId]?.[figureId];
|
|
29853
30052
|
if (!fig) {
|
|
29854
30053
|
continue;
|
|
@@ -32181,10 +32380,17 @@ class PivotCorePlugin extends CorePlugin {
|
|
|
32181
32380
|
if (!pivot) {
|
|
32182
32381
|
continue;
|
|
32183
32382
|
}
|
|
32184
|
-
|
|
32383
|
+
const def = deepCopy(pivot.definition);
|
|
32384
|
+
for (const measure of def.measures) {
|
|
32185
32385
|
if (measure.computedBy?.formula === formulaString) {
|
|
32186
|
-
const measureIndex =
|
|
32187
|
-
|
|
32386
|
+
const measureIndex = def.measures.indexOf(measure);
|
|
32387
|
+
if (measureIndex !== -1) {
|
|
32388
|
+
def.measures[measureIndex].computedBy = {
|
|
32389
|
+
formula: newFormulaString,
|
|
32390
|
+
sheetId,
|
|
32391
|
+
};
|
|
32392
|
+
}
|
|
32393
|
+
this.dispatch("UPDATE_PIVOT", { pivotId, pivot: def });
|
|
32188
32394
|
}
|
|
32189
32395
|
}
|
|
32190
32396
|
}
|
|
@@ -33216,6 +33422,9 @@ class SpreadsheetPivotCorePlugin extends CorePlugin {
|
|
|
33216
33422
|
const { sheetId, zone } = definition.dataSet;
|
|
33217
33423
|
const range = this.getters.getRangeFromZone(sheetId, zone);
|
|
33218
33424
|
const adaptedRange = adaptPivotRange(range, applyChange);
|
|
33425
|
+
if (adaptedRange === range) {
|
|
33426
|
+
return;
|
|
33427
|
+
}
|
|
33219
33428
|
const dataSet = adaptedRange && {
|
|
33220
33429
|
sheetId: adaptedRange.sheetId,
|
|
33221
33430
|
zone: adaptedRange.zone,
|
|
@@ -38947,6 +39156,10 @@ function astToFormula(ast) {
|
|
|
38947
39156
|
? `(${astToFormula(ast.operand)})`
|
|
38948
39157
|
: astToFormula(ast.operand);
|
|
38949
39158
|
return ast.value + rightOperand;
|
|
39159
|
+
case "ARRAY":
|
|
39160
|
+
return ("{" +
|
|
39161
|
+
ast.value.map((row) => row.map((cell) => astToFormula(cell)).join(",")).join(";") +
|
|
39162
|
+
"}");
|
|
38950
39163
|
case "BIN_OPERATION":
|
|
38951
39164
|
const leftOperation = leftOperandNeedsParenthesis(ast)
|
|
38952
39165
|
? `(${astToFormula(ast.left)})`
|
|
@@ -40511,7 +40724,6 @@ const dateGranularities = [
|
|
|
40511
40724
|
pivotRegistry.add("SPREADSHEET", {
|
|
40512
40725
|
ui: SpreadsheetPivot,
|
|
40513
40726
|
definition: SpreadsheetPivotRuntimeDefinition,
|
|
40514
|
-
externalData: false,
|
|
40515
40727
|
dateGranularities: [...dateGranularities],
|
|
40516
40728
|
datetimeGranularities: [...dateGranularities, "hour_number", "minute_number", "second_number"],
|
|
40517
40729
|
isMeasureCandidate: (field) => field.type !== "boolean",
|
|
@@ -40553,9 +40765,7 @@ class PivotUIPlugin extends CoreViewPlugin {
|
|
|
40553
40765
|
handle(cmd) {
|
|
40554
40766
|
if (invalidateEvaluationCommands.has(cmd.type)) {
|
|
40555
40767
|
for (const pivotId of this.getters.getPivotIds()) {
|
|
40556
|
-
|
|
40557
|
-
this.setupPivot(pivotId, { recreate: true });
|
|
40558
|
-
}
|
|
40768
|
+
this.setupPivot(pivotId, { recreate: true });
|
|
40559
40769
|
}
|
|
40560
40770
|
}
|
|
40561
40771
|
switch (cmd.type) {
|
|
@@ -40769,7 +40979,7 @@ class PivotUIPlugin extends CoreViewPlugin {
|
|
|
40769
40979
|
pivot.init({ reload: true });
|
|
40770
40980
|
}
|
|
40771
40981
|
setupPivot(pivotId, { recreate } = { recreate: false }) {
|
|
40772
|
-
const definition = this.getters.getPivotCoreDefinition(pivotId);
|
|
40982
|
+
const definition = deepCopy(this.getters.getPivotCoreDefinition(pivotId));
|
|
40773
40983
|
if (!(pivotId in this.pivots)) {
|
|
40774
40984
|
const Pivot = withPivotPresentationLayer(pivotRegistry.get(definition.type).ui);
|
|
40775
40985
|
this.pivots[pivotId] = new Pivot(this.custom, { definition, getters: this.getters });
|
|
@@ -43840,6 +44050,15 @@ class CarouselUIPlugin extends UIPlugin {
|
|
|
43840
44050
|
return "InvalidFigureId" /* CommandResult.InvalidFigureId */;
|
|
43841
44051
|
}
|
|
43842
44052
|
return "Success" /* CommandResult.Success */;
|
|
44053
|
+
case "DUPLICATE_CAROUSEL_CHART":
|
|
44054
|
+
if (!this.getters.doesCarouselExist(cmd.carouselId) ||
|
|
44055
|
+
!this.getters
|
|
44056
|
+
.getCarousel(cmd.carouselId)
|
|
44057
|
+
.items.some((item) => item.type === "chart" && item.chartId === cmd.chartId) ||
|
|
44058
|
+
this.getters.getChart(cmd.duplicatedChartId)) {
|
|
44059
|
+
return "InvalidFigureId" /* CommandResult.InvalidFigureId */;
|
|
44060
|
+
}
|
|
44061
|
+
return "Success" /* CommandResult.Success */;
|
|
43843
44062
|
case "ADD_NEW_CHART_TO_CAROUSEL":
|
|
43844
44063
|
if (!this.getters.doesCarouselExist(cmd.figureId)) {
|
|
43845
44064
|
return "InvalidFigureId" /* CommandResult.InvalidFigureId */;
|
|
@@ -43864,6 +44083,9 @@ class CarouselUIPlugin extends UIPlugin {
|
|
|
43864
44083
|
case "ADD_FIGURE_CHART_TO_CAROUSEL":
|
|
43865
44084
|
this.addFigureChartToCarousel(cmd.carouselFigureId, cmd.chartFigureId, cmd.sheetId);
|
|
43866
44085
|
break;
|
|
44086
|
+
case "DUPLICATE_CAROUSEL_CHART":
|
|
44087
|
+
this.duplicateCarouselChart(cmd);
|
|
44088
|
+
break;
|
|
43867
44089
|
case "UPDATE_CAROUSEL_ACTIVE_ITEM":
|
|
43868
44090
|
this.carouselStates[cmd.figureId] = this.getCarouselItemId(cmd.item);
|
|
43869
44091
|
break;
|
|
@@ -44002,6 +44224,29 @@ class CarouselUIPlugin extends UIPlugin {
|
|
|
44002
44224
|
});
|
|
44003
44225
|
this.dispatch("DELETE_FIGURE", { sheetId, figureId: chartFigureId });
|
|
44004
44226
|
}
|
|
44227
|
+
duplicateCarouselChart({ carouselId, chartId, sheetId, duplicatedChartId, }) {
|
|
44228
|
+
const chart = this.getters.getChart(chartId);
|
|
44229
|
+
if (!chart) {
|
|
44230
|
+
return;
|
|
44231
|
+
}
|
|
44232
|
+
const carousel = this.getters.getCarousel(carouselId);
|
|
44233
|
+
const duplicatedItemIndex = carousel.items.findIndex((item) => item.type === "chart" && item.chartId === chartId);
|
|
44234
|
+
if (duplicatedItemIndex === -1) {
|
|
44235
|
+
return;
|
|
44236
|
+
}
|
|
44237
|
+
this.dispatch("CREATE_CHART", {
|
|
44238
|
+
chartId: duplicatedChartId,
|
|
44239
|
+
figureId: carouselId,
|
|
44240
|
+
sheetId,
|
|
44241
|
+
definition: chart.getDefinition(),
|
|
44242
|
+
});
|
|
44243
|
+
const carouselItems = insertItemsAtIndex(carousel.items, [{ type: "chart", chartId: duplicatedChartId }], duplicatedItemIndex + 1);
|
|
44244
|
+
this.dispatch("UPDATE_CAROUSEL", {
|
|
44245
|
+
sheetId,
|
|
44246
|
+
figureId: carouselId,
|
|
44247
|
+
definition: { ...carousel, items: carouselItems },
|
|
44248
|
+
});
|
|
44249
|
+
}
|
|
44005
44250
|
getCarouselItemId(item) {
|
|
44006
44251
|
return item.type === "chart" ? item.chartId : "carouselDataView";
|
|
44007
44252
|
}
|
|
@@ -45217,25 +45462,43 @@ class GridSelectionPlugin extends UIPlugin {
|
|
|
45217
45462
|
return "Success" /* CommandResult.Success */;
|
|
45218
45463
|
}
|
|
45219
45464
|
handleEvent(event) {
|
|
45220
|
-
|
|
45221
|
-
let zones = [];
|
|
45465
|
+
let anchor = event.anchor;
|
|
45466
|
+
let zones = [...this.gridSelection.zones];
|
|
45222
45467
|
this.isUnbounded = event.options?.unbounded || false;
|
|
45223
45468
|
switch (event.mode) {
|
|
45224
45469
|
case "overrideSelection":
|
|
45225
45470
|
zones = [anchor.zone];
|
|
45226
45471
|
break;
|
|
45227
45472
|
case "updateAnchor":
|
|
45228
|
-
zones = [...this.gridSelection.zones];
|
|
45229
45473
|
const index = zones.findIndex((z) => isEqual(z, event.previousAnchor.zone));
|
|
45230
45474
|
if (index >= 0) {
|
|
45231
45475
|
zones[index] = anchor.zone;
|
|
45232
45476
|
}
|
|
45233
45477
|
break;
|
|
45234
45478
|
case "newAnchor":
|
|
45235
|
-
zones
|
|
45479
|
+
zones.push(anchor.zone);
|
|
45480
|
+
break;
|
|
45481
|
+
case "commitSelection":
|
|
45482
|
+
const zoneToSplit = zones.find((zone) => isZoneInside(anchor.zone, zone) && !isEqual(anchor.zone, zone));
|
|
45483
|
+
const removeFullAnchor = zones.filter((zone) => isEqual(anchor.zone, zone)).length > 1;
|
|
45484
|
+
if (removeFullAnchor && zones.length > 2) {
|
|
45485
|
+
zones = zones.filter((zone) => !isEqual(zone, anchor.zone));
|
|
45486
|
+
}
|
|
45487
|
+
else if (zoneToSplit) {
|
|
45488
|
+
const splittedZones = splitZone(anchor.zone, zoneToSplit);
|
|
45489
|
+
zones = zones
|
|
45490
|
+
.filter((z) => !isEqual(z, anchor.zone) && !isEqual(z, zoneToSplit))
|
|
45491
|
+
.concat(splittedZones);
|
|
45492
|
+
}
|
|
45493
|
+
zones = uniqueZones(zones);
|
|
45494
|
+
const lastZone = zones[zones.length - 1];
|
|
45495
|
+
anchor = {
|
|
45496
|
+
cell: { col: lastZone.left, row: lastZone.top },
|
|
45497
|
+
zone: lastZone,
|
|
45498
|
+
};
|
|
45236
45499
|
break;
|
|
45237
45500
|
}
|
|
45238
|
-
this.setSelectionMixin(
|
|
45501
|
+
this.setSelectionMixin(anchor, zones);
|
|
45239
45502
|
/** Any change to the selection has to be reflected in the selection processor. */
|
|
45240
45503
|
this.selection.resetDefaultAnchor(this, deepCopy(this.gridSelection.anchor));
|
|
45241
45504
|
const { col, row } = this.gridSelection.anchor.cell;
|
|
@@ -45515,7 +45778,7 @@ class GridSelectionPlugin extends UIPlugin {
|
|
|
45515
45778
|
setSelectionMixin(anchor, zones) {
|
|
45516
45779
|
const { anchor: clippedAnchor, zones: clippedZones } = this.clipSelection(this.getters.getActiveSheetId(), { anchor, zones });
|
|
45517
45780
|
this.gridSelection.anchor = clippedAnchor;
|
|
45518
|
-
this.gridSelection.zones =
|
|
45781
|
+
this.gridSelection.zones = clippedZones;
|
|
45519
45782
|
}
|
|
45520
45783
|
/**
|
|
45521
45784
|
* Change the anchor of the selection active cell to an absolute col and row index.
|
|
@@ -47487,6 +47750,9 @@ class SelectionStreamProcessorImpl {
|
|
|
47487
47750
|
bottom: Math.max(anchorRow, row),
|
|
47488
47751
|
};
|
|
47489
47752
|
const expandedZone = this.getters.expandZone(sheetId, zone);
|
|
47753
|
+
if (isEqual(this.anchor.zone, expandedZone)) {
|
|
47754
|
+
return new DispatchResult("NoChanges" /* CommandResult.NoChanges */);
|
|
47755
|
+
}
|
|
47490
47756
|
const anchor = { zone: expandedZone, cell: { col: anchorCol, row: anchorRow } };
|
|
47491
47757
|
return this.processEvent({
|
|
47492
47758
|
mode: "updateAnchor",
|
|
@@ -47507,6 +47773,22 @@ class SelectionStreamProcessorImpl {
|
|
|
47507
47773
|
mode: "newAnchor",
|
|
47508
47774
|
});
|
|
47509
47775
|
}
|
|
47776
|
+
/**
|
|
47777
|
+
* Triggered on mouse up to finalize the selection.
|
|
47778
|
+
* - If the current anchor zone already exists in the selection, it will be removed.
|
|
47779
|
+
* - If the anchor zone overlaps with existing zones, it will be split into
|
|
47780
|
+
* multiple non-overlapping parts.
|
|
47781
|
+
*/
|
|
47782
|
+
commitSelection() {
|
|
47783
|
+
return this.processEvent({
|
|
47784
|
+
options: {
|
|
47785
|
+
scrollIntoView: false,
|
|
47786
|
+
unbounded: true,
|
|
47787
|
+
},
|
|
47788
|
+
anchor: this.anchor,
|
|
47789
|
+
mode: "commitSelection",
|
|
47790
|
+
});
|
|
47791
|
+
}
|
|
47510
47792
|
/**
|
|
47511
47793
|
* Increase or decrease the size of the current anchor zone.
|
|
47512
47794
|
* The anchor cell remains where it is. It's the opposite side
|
|
@@ -50913,5 +51195,5 @@ export { BadExpressionError, BasePlugin, CellErrorType, CircularDependencyError,
|
|
|
50913
51195
|
|
|
50914
51196
|
|
|
50915
51197
|
__info__.version = "19.1.0-alpha.3";
|
|
50916
|
-
__info__.date = "2025-
|
|
50917
|
-
__info__.hash = "
|
|
51198
|
+
__info__.date = "2025-12-02T05:34:07.213Z";
|
|
51199
|
+
__info__.hash = "04cf666";
|