@odoo/o-spreadsheet 19.1.0-alpha.12 → 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 +53 -25
- package/dist/o-spreadsheet-engine.esm.js +510 -131
- package/dist/o-spreadsheet-engine.iife.js +510 -131
- package/dist/o-spreadsheet-engine.min.iife.js +313 -313
- package/dist/o-spreadsheet.d.ts +371 -388
- package/dist/o_spreadsheet.esm.js +29566 -19200
- package/dist/o_spreadsheet.iife.js +29567 -19201
- package/dist/o_spreadsheet.min.iife.js +335 -317
- package/dist/o_spreadsheet.xml +434 -212
- 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 {
|
|
@@ -634,6 +634,7 @@ const DEFAULT_STYLE = {
|
|
|
634
634
|
fontSize: 10,
|
|
635
635
|
fillColor: "",
|
|
636
636
|
textColor: "",
|
|
637
|
+
rotation: 0,
|
|
637
638
|
};
|
|
638
639
|
const DEFAULT_VERTICAL_ALIGN = DEFAULT_STYLE.verticalAlign;
|
|
639
640
|
// Fonts
|
|
@@ -679,7 +680,7 @@ const PIVOT_TABLE_CONFIG = {
|
|
|
679
680
|
};
|
|
680
681
|
const PIVOT_INDENT = 15;
|
|
681
682
|
const PIVOT_COLLAPSE_ICON_SIZE = 12;
|
|
682
|
-
const PIVOT_MAX_NUMBER_OF_CELLS =
|
|
683
|
+
const PIVOT_MAX_NUMBER_OF_CELLS = 5e5;
|
|
683
684
|
|
|
684
685
|
//------------------------------------------------------------------------------
|
|
685
686
|
// Miscellaneous
|
|
@@ -3105,8 +3106,56 @@ function isMultipleElementMatrix(arg) {
|
|
|
3105
3106
|
return isMatrix(arg) && !isSingleElementMatrix(arg);
|
|
3106
3107
|
}
|
|
3107
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
|
+
}
|
|
3108
3157
|
// -----------------------------------------------------------------------------
|
|
3109
|
-
//
|
|
3158
|
+
// ARRAY.CONSTRAIN
|
|
3110
3159
|
// -----------------------------------------------------------------------------
|
|
3111
3160
|
const ARRAY_CONSTRAIN = {
|
|
3112
3161
|
description: _t("Returns a result array constrained to a specific width and height."),
|
|
@@ -3132,6 +3181,30 @@ const ARRAY_CONSTRAIN = {
|
|
|
3132
3181
|
isExported: false,
|
|
3133
3182
|
};
|
|
3134
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
|
+
// -----------------------------------------------------------------------------
|
|
3135
3208
|
// CHOOSECOLS
|
|
3136
3209
|
// -----------------------------------------------------------------------------
|
|
3137
3210
|
const CHOOSECOLS = {
|
|
@@ -3277,20 +3350,7 @@ const HSTACK = {
|
|
|
3277
3350
|
description: _t("Appends ranges horizontally and in sequence to return a larger array."),
|
|
3278
3351
|
args: [arg("range (any, range<any>, repeating)", _t("The range to be appended."))],
|
|
3279
3352
|
compute: function (...ranges) {
|
|
3280
|
-
|
|
3281
|
-
const result = [];
|
|
3282
|
-
for (const range of ranges) {
|
|
3283
|
-
const _range = toMatrix(range);
|
|
3284
|
-
for (let col = 0; col < _range.length; col++) {
|
|
3285
|
-
//TODO: fill with #N/A for unavailable values instead of zeroes
|
|
3286
|
-
const array = Array(nbRows).fill({ value: null });
|
|
3287
|
-
for (let row = 0; row < _range[col].length; row++) {
|
|
3288
|
-
array[row] = _range[col][row];
|
|
3289
|
-
}
|
|
3290
|
-
result.push(array);
|
|
3291
|
-
}
|
|
3292
|
-
}
|
|
3293
|
-
return result;
|
|
3353
|
+
return stackHorizontally(ranges);
|
|
3294
3354
|
},
|
|
3295
3355
|
isExported: true,
|
|
3296
3356
|
};
|
|
@@ -3549,22 +3609,7 @@ const VSTACK = {
|
|
|
3549
3609
|
description: _t("Appends ranges vertically and in sequence to return a larger array."),
|
|
3550
3610
|
args: [arg("range (any, range<any>, repeating)", _t("The range to be appended."))],
|
|
3551
3611
|
compute: function (...ranges) {
|
|
3552
|
-
|
|
3553
|
-
const nbRows = ranges.reduce((acc, range) => acc + toMatrix(range)[0].length, 0);
|
|
3554
|
-
const result = Array(nbColumns)
|
|
3555
|
-
.fill([])
|
|
3556
|
-
.map(() => Array(nbRows).fill({ value: 0 })); // TODO fill with #N/A
|
|
3557
|
-
let currentRow = 0;
|
|
3558
|
-
for (const range of ranges) {
|
|
3559
|
-
const _array = toMatrix(range);
|
|
3560
|
-
for (let col = 0; col < _array.length; col++) {
|
|
3561
|
-
for (let row = 0; row < _array[col].length; row++) {
|
|
3562
|
-
result[col][currentRow + row] = _array[col][row];
|
|
3563
|
-
}
|
|
3564
|
-
}
|
|
3565
|
-
currentRow += _array[0].length;
|
|
3566
|
-
}
|
|
3567
|
-
return result;
|
|
3612
|
+
return stackVertically(ranges);
|
|
3568
3613
|
},
|
|
3569
3614
|
isExported: true,
|
|
3570
3615
|
};
|
|
@@ -3624,6 +3669,8 @@ const WRAPROWS = {
|
|
|
3624
3669
|
var array = /*#__PURE__*/Object.freeze({
|
|
3625
3670
|
__proto__: null,
|
|
3626
3671
|
ARRAY_CONSTRAIN: ARRAY_CONSTRAIN,
|
|
3672
|
+
ARRAY_LITERAL: ARRAY_LITERAL,
|
|
3673
|
+
ARRAY_ROW: ARRAY_ROW,
|
|
3627
3674
|
CHOOSECOLS: CHOOSECOLS,
|
|
3628
3675
|
CHOOSEROWS: CHOOSEROWS,
|
|
3629
3676
|
EXPAND: EXPAND,
|
|
@@ -3645,6 +3692,30 @@ var array = /*#__PURE__*/Object.freeze({
|
|
|
3645
3692
|
WRAPROWS: WRAPROWS
|
|
3646
3693
|
});
|
|
3647
3694
|
|
|
3695
|
+
const DEFAULT_LOCALES = [
|
|
3696
|
+
{
|
|
3697
|
+
name: "English (US)",
|
|
3698
|
+
code: "en_US",
|
|
3699
|
+
thousandsSeparator: ",",
|
|
3700
|
+
decimalSeparator: ".",
|
|
3701
|
+
weekStart: 7, // Sunday
|
|
3702
|
+
dateFormat: "m/d/yyyy",
|
|
3703
|
+
timeFormat: "hh:mm:ss a",
|
|
3704
|
+
formulaArgSeparator: ",",
|
|
3705
|
+
},
|
|
3706
|
+
{
|
|
3707
|
+
name: "French",
|
|
3708
|
+
code: "fr_FR",
|
|
3709
|
+
thousandsSeparator: " ",
|
|
3710
|
+
decimalSeparator: ",",
|
|
3711
|
+
weekStart: 1, // Monday
|
|
3712
|
+
dateFormat: "dd/mm/yyyy",
|
|
3713
|
+
timeFormat: "hh:mm:ss",
|
|
3714
|
+
formulaArgSeparator: ";",
|
|
3715
|
+
},
|
|
3716
|
+
];
|
|
3717
|
+
const DEFAULT_LOCALE = DEFAULT_LOCALES[0];
|
|
3718
|
+
|
|
3648
3719
|
function tokenizeFormat(str) {
|
|
3649
3720
|
const chars = new TokenizingChars(str);
|
|
3650
3721
|
const result = [];
|
|
@@ -3929,7 +4000,7 @@ function parseNumberFormatTokens(tokens) {
|
|
|
3929
4000
|
}
|
|
3930
4001
|
function parseDateFormatTokens(tokens) {
|
|
3931
4002
|
const internalFormat = tokens && areValidDateFormatTokens(tokens) ? { type: "date", tokens } : undefined;
|
|
3932
|
-
if (!internalFormat) {
|
|
4003
|
+
if (!internalFormat || !tokens?.some((token) => token.type === "DATE_PART")) {
|
|
3933
4004
|
return undefined;
|
|
3934
4005
|
}
|
|
3935
4006
|
if (internalFormat.tokens.length &&
|
|
@@ -4166,6 +4237,9 @@ function repeatCharToFitWidth(formattedValue, formatWidth) {
|
|
|
4166
4237
|
return prefix + repeatedChar.repeat(timesToRepeat) + padding + suffix;
|
|
4167
4238
|
}
|
|
4168
4239
|
function applyInternalNumberFormat(value, format, locale) {
|
|
4240
|
+
if (!format.integerPart.length && !format.decimalPart?.length) {
|
|
4241
|
+
return "";
|
|
4242
|
+
}
|
|
4169
4243
|
if (value === Infinity) {
|
|
4170
4244
|
return "∞" + (format.percentSymbols ? "%" : "");
|
|
4171
4245
|
}
|
|
@@ -4772,7 +4846,7 @@ function getTokenNextReferenceType(xc) {
|
|
|
4772
4846
|
function setXcToFixedReferenceType(xc, referenceType) {
|
|
4773
4847
|
let sheetName;
|
|
4774
4848
|
({ sheetName, xc } = splitReference(xc));
|
|
4775
|
-
sheetName = sheetName ? sheetName + "!" : "";
|
|
4849
|
+
sheetName = sheetName ? getCanonicalSymbolName(sheetName) + "!" : "";
|
|
4776
4850
|
xc = xc.replace(/\$/g, "");
|
|
4777
4851
|
const splitIndex = xc.indexOf(":");
|
|
4778
4852
|
if (splitIndex >= 0) {
|
|
@@ -6052,6 +6126,47 @@ function splitIfAdjacent(zone, zoneToRemove) {
|
|
|
6052
6126
|
return newZones;
|
|
6053
6127
|
}
|
|
6054
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
|
+
}
|
|
6055
6170
|
|
|
6056
6171
|
function isSheetDependent(cmd) {
|
|
6057
6172
|
return "sheetId" in cmd;
|
|
@@ -6082,7 +6197,6 @@ const invalidateEvaluationCommands = new Set([
|
|
|
6082
6197
|
"REDO",
|
|
6083
6198
|
"ADD_MERGE",
|
|
6084
6199
|
"REMOVE_MERGE",
|
|
6085
|
-
"DUPLICATE_SHEET",
|
|
6086
6200
|
"UPDATE_LOCALE",
|
|
6087
6201
|
"ADD_PIVOT",
|
|
6088
6202
|
"UPDATE_PIVOT",
|
|
@@ -7649,7 +7763,7 @@ const SUBTOTAL = {
|
|
|
7649
7763
|
if (!acceptHiddenCells && this.getters.isRowHiddenByUser(sheetId, row))
|
|
7650
7764
|
continue;
|
|
7651
7765
|
for (let col = left; col <= right; col++) {
|
|
7652
|
-
const cell = this.getters.
|
|
7766
|
+
const cell = this.getters.getCorrespondingFormulaCell({ sheetId, col, row });
|
|
7653
7767
|
if (!cell || !isSubtotalCell(cell)) {
|
|
7654
7768
|
evaluatedCellToKeep.push(this.getters.getEvaluatedCell({ sheetId, col, row }));
|
|
7655
7769
|
}
|
|
@@ -9544,30 +9658,6 @@ var database = /*#__PURE__*/Object.freeze({
|
|
|
9544
9658
|
DVARP: DVARP
|
|
9545
9659
|
});
|
|
9546
9660
|
|
|
9547
|
-
const DEFAULT_LOCALES = [
|
|
9548
|
-
{
|
|
9549
|
-
name: "English (US)",
|
|
9550
|
-
code: "en_US",
|
|
9551
|
-
thousandsSeparator: ",",
|
|
9552
|
-
decimalSeparator: ".",
|
|
9553
|
-
weekStart: 7, // Sunday
|
|
9554
|
-
dateFormat: "m/d/yyyy",
|
|
9555
|
-
timeFormat: "hh:mm:ss a",
|
|
9556
|
-
formulaArgSeparator: ",",
|
|
9557
|
-
},
|
|
9558
|
-
{
|
|
9559
|
-
name: "French",
|
|
9560
|
-
code: "fr_FR",
|
|
9561
|
-
thousandsSeparator: " ",
|
|
9562
|
-
decimalSeparator: ",",
|
|
9563
|
-
weekStart: 1, // Monday
|
|
9564
|
-
dateFormat: "dd/mm/yyyy",
|
|
9565
|
-
timeFormat: "hh:mm:ss",
|
|
9566
|
-
formulaArgSeparator: ";",
|
|
9567
|
-
},
|
|
9568
|
-
];
|
|
9569
|
-
const DEFAULT_LOCALE = DEFAULT_LOCALES[0];
|
|
9570
|
-
|
|
9571
9661
|
/**
|
|
9572
9662
|
* Tokenizer
|
|
9573
9663
|
*
|
|
@@ -9596,7 +9686,9 @@ function tokenize(str, locale = DEFAULT_LOCALE) {
|
|
|
9596
9686
|
while (!chars.isOver()) {
|
|
9597
9687
|
let token = tokenizeNewLine(chars) ||
|
|
9598
9688
|
tokenizeSpace(chars) ||
|
|
9689
|
+
tokenizeArrayRowSeparator(chars, locale) ||
|
|
9599
9690
|
tokenizeArgsSeparator(chars, locale) ||
|
|
9691
|
+
tokenizeBraces(chars) ||
|
|
9600
9692
|
tokenizeParenthesis(chars) ||
|
|
9601
9693
|
tokenizeOperator(chars) ||
|
|
9602
9694
|
tokenizeString(chars) ||
|
|
@@ -9629,6 +9721,17 @@ function tokenizeParenthesis(chars) {
|
|
|
9629
9721
|
}
|
|
9630
9722
|
return null;
|
|
9631
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
|
+
}
|
|
9632
9735
|
function tokenizeArgsSeparator(chars, locale) {
|
|
9633
9736
|
if (chars.current === locale.formulaArgSeparator) {
|
|
9634
9737
|
const value = chars.shift();
|
|
@@ -9637,6 +9740,20 @@ function tokenizeArgsSeparator(chars, locale) {
|
|
|
9637
9740
|
}
|
|
9638
9741
|
return null;
|
|
9639
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
|
+
}
|
|
9640
9757
|
function tokenizeOperator(chars) {
|
|
9641
9758
|
for (const op of OPERATORS) {
|
|
9642
9759
|
if (chars.currentStartsWith(op)) {
|
|
@@ -9857,6 +9974,9 @@ function _localizeFormula(formula, fromLocale, toLocale) {
|
|
|
9857
9974
|
else if (token.type === "ARG_SEPARATOR") {
|
|
9858
9975
|
localizedFormula += toLocale.formulaArgSeparator;
|
|
9859
9976
|
}
|
|
9977
|
+
else if (token.type === "ARRAY_ROW_SEPARATOR") {
|
|
9978
|
+
localizedFormula += toLocale.formulaArgSeparator === ";" ? "\\" : ";";
|
|
9979
|
+
}
|
|
9860
9980
|
else {
|
|
9861
9981
|
localizedFormula += token.value;
|
|
9862
9982
|
}
|
|
@@ -11020,7 +11140,6 @@ function sortMatrix(matrix, locale, ...criteria) {
|
|
|
11020
11140
|
// -----------------------------------------------------------------------------
|
|
11021
11141
|
const FILTER = {
|
|
11022
11142
|
description: _t("Returns a filtered version of the source range, returning only rows or columns that meet the specified conditions."),
|
|
11023
|
-
// TODO modify args description when vectorization on formulas is available
|
|
11024
11143
|
args: [
|
|
11025
11144
|
arg("range (any, range<any>)", _t("The data to be filtered.")),
|
|
11026
11145
|
arg("condition (boolean, range<boolean>, repeating)", _t("Column or row containing true or false values corresponding to the range.")),
|
|
@@ -13972,18 +14091,17 @@ var logical = /*#__PURE__*/Object.freeze({
|
|
|
13972
14091
|
["GaugeLowerInflectionPointNaN" /* CommandResult.GaugeLowerInflectionPointNaN */]: _t("The lower inflection point value must be a number"),
|
|
13973
14092
|
["GaugeUpperInflectionPointNaN" /* CommandResult.GaugeUpperInflectionPointNaN */]: _t("The upper inflection point value must be a number"),
|
|
13974
14093
|
},
|
|
13975
|
-
|
|
13976
|
-
|
|
13977
|
-
|
|
13978
|
-
|
|
13979
|
-
|
|
13980
|
-
|
|
13981
|
-
|
|
13982
|
-
|
|
13983
|
-
|
|
13984
|
-
|
|
13985
|
-
|
|
13986
|
-
},
|
|
14094
|
+
ColorScales: {
|
|
14095
|
+
blues: _t("Blues"),
|
|
14096
|
+
cividis: _t("Cividis"),
|
|
14097
|
+
custom: _t("Custom"),
|
|
14098
|
+
greens: _t("Greens"),
|
|
14099
|
+
greys: _t("Greys"),
|
|
14100
|
+
oranges: _t("Oranges"),
|
|
14101
|
+
purples: _t("Purples"),
|
|
14102
|
+
rainbow: _t("Rainbow"),
|
|
14103
|
+
reds: _t("Reds"),
|
|
14104
|
+
viridis: _t("Viridis"),
|
|
13987
14105
|
},
|
|
13988
14106
|
});
|
|
13989
14107
|
({
|
|
@@ -17221,6 +17339,8 @@ function parseOperand(tokens) {
|
|
|
17221
17339
|
tokenStartIndex: current.tokenIndex,
|
|
17222
17340
|
tokenEndIndex: rightParen.tokenIndex,
|
|
17223
17341
|
};
|
|
17342
|
+
case "LEFT_BRACE":
|
|
17343
|
+
return parseArrayLiteral(tokens, current);
|
|
17224
17344
|
case "OPERATOR":
|
|
17225
17345
|
const operator = current.value;
|
|
17226
17346
|
if (UNARY_OPERATORS_PREFIX.includes(operator)) {
|
|
@@ -17274,6 +17394,34 @@ function consumeOrThrow(tokens, type, message) {
|
|
|
17274
17394
|
}
|
|
17275
17395
|
return token;
|
|
17276
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
|
+
}
|
|
17277
17425
|
function parseExpression(tokens, parent_priority = 0) {
|
|
17278
17426
|
if (tokens.length === 0) {
|
|
17279
17427
|
throw new BadExpressionError();
|
|
@@ -17364,6 +17512,13 @@ function* astIterator(ast) {
|
|
|
17364
17512
|
yield* astIterator(arg);
|
|
17365
17513
|
}
|
|
17366
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;
|
|
17367
17522
|
case "UNARY_OPERATION":
|
|
17368
17523
|
yield* astIterator(ast.operand);
|
|
17369
17524
|
break;
|
|
@@ -17381,6 +17536,11 @@ function mapAst(ast, fn) {
|
|
|
17381
17536
|
...ast,
|
|
17382
17537
|
args: ast.args.map((child) => mapAst(child, fn)),
|
|
17383
17538
|
};
|
|
17539
|
+
case "ARRAY":
|
|
17540
|
+
return {
|
|
17541
|
+
...ast,
|
|
17542
|
+
value: ast.value.map((row) => row.map((cell) => mapAst(cell, fn))),
|
|
17543
|
+
};
|
|
17384
17544
|
case "UNARY_OPERATION":
|
|
17385
17545
|
return {
|
|
17386
17546
|
...ast,
|
|
@@ -17539,6 +17699,23 @@ function compileTokensOrThrow(tokens) {
|
|
|
17539
17699
|
code.append(...args);
|
|
17540
17700
|
const fnName = ast.value.toUpperCase();
|
|
17541
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
|
+
}
|
|
17542
17719
|
case "UNARY_OPERATION": {
|
|
17543
17720
|
const fnName = UNARY_OPERATOR_MAP[ast.value];
|
|
17544
17721
|
const operand = compileAST(ast.operand, false, false).assignResultToVariable();
|
|
@@ -18818,6 +18995,17 @@ class AlternatingColorMap {
|
|
|
18818
18995
|
return this.colors[id];
|
|
18819
18996
|
}
|
|
18820
18997
|
}
|
|
18998
|
+
const COLORSCHEMES = {
|
|
18999
|
+
greys: ["#ffffff", "#808080", "#000000"],
|
|
19000
|
+
blues: ["#f7fbff", "#6aaed6", "#08306b"],
|
|
19001
|
+
reds: ["#fff5f0", "#fb694a", "#67000d"],
|
|
19002
|
+
greens: ["#f7fcf5", "#73c476", "#00441b"],
|
|
19003
|
+
oranges: ["#fff5eb", "#fd8c3b", "#7f2704"],
|
|
19004
|
+
purples: ["#fcfbfd", "#9e9ac8", "#3f007d"],
|
|
19005
|
+
viridis: ["#440154", "#21918c", "#fde725"],
|
|
19006
|
+
cividis: ["#00224e", "#7d7c78", "#fee838"],
|
|
19007
|
+
rainbow: ["#B41DB4", "#FFFF00", "#00FFFF"],
|
|
19008
|
+
};
|
|
18821
19009
|
/**
|
|
18822
19010
|
* Returns a function that maps a value to a color using a color scale defined by the given
|
|
18823
19011
|
* color/threshold values pairs.
|
|
@@ -25870,6 +26058,17 @@ class XlsxReader {
|
|
|
25870
26058
|
}
|
|
25871
26059
|
}
|
|
25872
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
|
+
|
|
25873
26072
|
/**
|
|
25874
26073
|
* parses a formula (as a string) into the same formula,
|
|
25875
26074
|
* but with the references to other cells extracted
|
|
@@ -26513,6 +26712,29 @@ migrationStepRegistry
|
|
|
26513
26712
|
}
|
|
26514
26713
|
return data;
|
|
26515
26714
|
},
|
|
26715
|
+
})
|
|
26716
|
+
.add("19.1.0", {
|
|
26717
|
+
migrate(data) {
|
|
26718
|
+
for (const sheet of data.sheets || []) {
|
|
26719
|
+
for (const figure of sheet.figures || []) {
|
|
26720
|
+
if (figure.tag === "chart" && figure.data.type === "geo") {
|
|
26721
|
+
if ("colorScale" in figure.data && typeof figure.data.colorScale === "string") {
|
|
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
|
+
}
|
|
26732
|
+
}
|
|
26733
|
+
}
|
|
26734
|
+
}
|
|
26735
|
+
}
|
|
26736
|
+
return data;
|
|
26737
|
+
},
|
|
26516
26738
|
});
|
|
26517
26739
|
function fixOverlappingFilters(data) {
|
|
26518
26740
|
for (const sheet of data.sheets || []) {
|
|
@@ -26769,18 +26991,22 @@ function dropCommands(initialMessages, commandType) {
|
|
|
26769
26991
|
return messages;
|
|
26770
26992
|
}
|
|
26771
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
|
+
}
|
|
26772
27003
|
const messages = [];
|
|
26773
27004
|
const map = {};
|
|
26774
27005
|
for (const sheet of data.sheets || []) {
|
|
26775
27006
|
sheet.figures?.forEach((figure) => {
|
|
26776
27007
|
if (figure.tag === "chart") {
|
|
26777
27008
|
// chart definition
|
|
26778
|
-
|
|
26779
|
-
map[figure.data.chartId] = figure.data;
|
|
26780
|
-
}
|
|
26781
|
-
else {
|
|
26782
|
-
map[figure.id] = figure.data;
|
|
26783
|
-
}
|
|
27009
|
+
map[figure.id] = figure.data;
|
|
26784
27010
|
}
|
|
26785
27011
|
});
|
|
26786
27012
|
}
|
|
@@ -27313,22 +27539,34 @@ class BordersPlugin extends CorePlugin {
|
|
|
27313
27539
|
addBorder(sheetId, zone, newBorder, force = false) {
|
|
27314
27540
|
const borders = [];
|
|
27315
27541
|
const plannedBorder = newBorder ? { zone, style: newBorder } : undefined;
|
|
27316
|
-
|
|
27317
|
-
|
|
27318
|
-
|
|
27319
|
-
|
|
27320
|
-
|
|
27542
|
+
// For each side, decide if we must clear the border on the *adjacent*
|
|
27543
|
+
// existing cell when we draw on the opposite side of the new zone.
|
|
27544
|
+
//
|
|
27545
|
+
// Example:
|
|
27546
|
+
// - newBorder.right is set → we draw border on the RIGHT side of `zone`
|
|
27547
|
+
// - the cell on the right may already have a LEFT border on that edge
|
|
27548
|
+
// In that case we clear that LEFT border, so only the new RIGHT border
|
|
27549
|
+
// remains on the shared edge.
|
|
27550
|
+
//
|
|
27551
|
+
// existingBorderSideToClear[side] = true means we should clear the border on that
|
|
27552
|
+
// side of the existing adjacent zone before adding the new border.
|
|
27553
|
+
const existingBorderSideToClear = {
|
|
27554
|
+
left: force || !!newBorder?.right,
|
|
27555
|
+
right: force || !!newBorder?.left,
|
|
27556
|
+
top: force || !!newBorder?.bottom,
|
|
27557
|
+
bottom: force || !!newBorder?.top,
|
|
27321
27558
|
};
|
|
27322
27559
|
let editingZone = [zone];
|
|
27323
27560
|
for (const existingBorder of this.borders[sheetId] ?? []) {
|
|
27324
27561
|
const inter = intersection(existingBorder.zone, zone);
|
|
27325
27562
|
if (!inter) {
|
|
27326
|
-
//
|
|
27563
|
+
// Check if the existing border is adjacent to the new zone
|
|
27327
27564
|
const adjacentEdge = adjacent(existingBorder.zone, zone);
|
|
27328
|
-
if (adjacentEdge &&
|
|
27565
|
+
if (adjacentEdge && existingBorderSideToClear[adjacentEdge.position]) {
|
|
27329
27566
|
for (const newZone of splitIfAdjacent(existingBorder.zone, zone)) {
|
|
27330
27567
|
const border = this.computeBorderFromZone(newZone, existingBorder);
|
|
27331
27568
|
const adjacentEdge = adjacent(newZone, zone);
|
|
27569
|
+
// Clear the existing border on the side that touches the new zone
|
|
27332
27570
|
switch (adjacentEdge?.position) {
|
|
27333
27571
|
case "left":
|
|
27334
27572
|
border.style.left = undefined;
|
|
@@ -29808,7 +30046,8 @@ class FigurePlugin extends CorePlugin {
|
|
|
29808
30046
|
}
|
|
29809
30047
|
break;
|
|
29810
30048
|
case "DUPLICATE_SHEET": {
|
|
29811
|
-
for (const
|
|
30049
|
+
for (const figure of this.getFigures(cmd.sheetId)) {
|
|
30050
|
+
const figureId = figure.id;
|
|
29812
30051
|
const fig = this.figures[cmd.sheetId]?.[figureId];
|
|
29813
30052
|
if (!fig) {
|
|
29814
30053
|
continue;
|
|
@@ -32141,10 +32380,17 @@ class PivotCorePlugin extends CorePlugin {
|
|
|
32141
32380
|
if (!pivot) {
|
|
32142
32381
|
continue;
|
|
32143
32382
|
}
|
|
32144
|
-
|
|
32383
|
+
const def = deepCopy(pivot.definition);
|
|
32384
|
+
for (const measure of def.measures) {
|
|
32145
32385
|
if (measure.computedBy?.formula === formulaString) {
|
|
32146
|
-
const measureIndex =
|
|
32147
|
-
|
|
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 });
|
|
32148
32394
|
}
|
|
32149
32395
|
}
|
|
32150
32396
|
}
|
|
@@ -33176,6 +33422,9 @@ class SpreadsheetPivotCorePlugin extends CorePlugin {
|
|
|
33176
33422
|
const { sheetId, zone } = definition.dataSet;
|
|
33177
33423
|
const range = this.getters.getRangeFromZone(sheetId, zone);
|
|
33178
33424
|
const adaptedRange = adaptPivotRange(range, applyChange);
|
|
33425
|
+
if (adaptedRange === range) {
|
|
33426
|
+
return;
|
|
33427
|
+
}
|
|
33179
33428
|
const dataSet = adaptedRange && {
|
|
33180
33429
|
sheetId: adaptedRange.sheetId,
|
|
33181
33430
|
zone: adaptedRange.zone,
|
|
@@ -33215,6 +33464,13 @@ class StylePlugin extends CorePlugin {
|
|
|
33215
33464
|
"getStyleColors",
|
|
33216
33465
|
];
|
|
33217
33466
|
styles = {};
|
|
33467
|
+
allowDispatch(cmd) {
|
|
33468
|
+
switch (cmd.type) {
|
|
33469
|
+
case "SET_FORMATTING":
|
|
33470
|
+
return this.checkUselessSetFormatting(cmd);
|
|
33471
|
+
}
|
|
33472
|
+
return "Success" /* CommandResult.Success */;
|
|
33473
|
+
}
|
|
33218
33474
|
handle(cmd) {
|
|
33219
33475
|
switch (cmd.type) {
|
|
33220
33476
|
case "ADD_MERGE":
|
|
@@ -33439,6 +33695,28 @@ class StylePlugin extends CorePlugin {
|
|
|
33439
33695
|
exportForExcel(data) {
|
|
33440
33696
|
this.export(data);
|
|
33441
33697
|
}
|
|
33698
|
+
checkUselessSetFormatting(cmd) {
|
|
33699
|
+
const { sheetId, target } = cmd;
|
|
33700
|
+
const hasStyle = "style" in cmd;
|
|
33701
|
+
const hasFormat = "format" in cmd;
|
|
33702
|
+
if (!hasStyle && !hasFormat) {
|
|
33703
|
+
return "NoChanges" /* CommandResult.NoChanges */;
|
|
33704
|
+
}
|
|
33705
|
+
for (const zone of recomputeZones(target)) {
|
|
33706
|
+
for (let col = zone.left; col <= zone.right; col++) {
|
|
33707
|
+
for (let row = zone.top; row <= zone.bottom; row++) {
|
|
33708
|
+
const position = { sheetId, col, row };
|
|
33709
|
+
const cell = this.getters.getCell(position);
|
|
33710
|
+
const style = this.getCellStyle(position);
|
|
33711
|
+
if ((hasStyle && !deepEquals(style, cmd.style)) ||
|
|
33712
|
+
(hasFormat && cell?.format !== cmd.format)) {
|
|
33713
|
+
return "Success" /* CommandResult.Success */;
|
|
33714
|
+
}
|
|
33715
|
+
}
|
|
33716
|
+
}
|
|
33717
|
+
}
|
|
33718
|
+
return "NoChanges" /* CommandResult.NoChanges */;
|
|
33719
|
+
}
|
|
33442
33720
|
}
|
|
33443
33721
|
|
|
33444
33722
|
class TableStylePlugin extends CorePlugin {
|
|
@@ -36466,48 +36744,64 @@ function getDefaultCellHeight(ctx, cell, style, colSize) {
|
|
|
36466
36744
|
}
|
|
36467
36745
|
function getCellContentHeight(ctx, content, style, colSize) {
|
|
36468
36746
|
const maxWidth = style?.wrapping === "wrap" ? colSize - 2 * MIN_CELL_TEXT_MARGIN : undefined;
|
|
36469
|
-
const
|
|
36470
|
-
|
|
36471
|
-
return computeTextLinesHeight(fontSize, numberOfLines) + 2 * PADDING_AUTORESIZE_VERTICAL;
|
|
36747
|
+
const lines = splitTextToWidth(ctx, content, style, maxWidth);
|
|
36748
|
+
return computeMultilineTextSize(ctx, lines, style).height + 2 * PADDING_AUTORESIZE_VERTICAL;
|
|
36472
36749
|
}
|
|
36473
36750
|
function getDefaultContextFont(fontSize, bold = false, italic = false) {
|
|
36474
36751
|
const italicStr = italic ? "italic" : "";
|
|
36475
36752
|
const weight = bold ? "bold" : "";
|
|
36476
36753
|
return `${italicStr} ${weight} ${fontSize}px ${DEFAULT_FONT}`;
|
|
36477
36754
|
}
|
|
36478
|
-
|
|
36479
|
-
|
|
36755
|
+
function computeMultilineTextSize(context, textLines, style = {}, fontUnit = "pt") {
|
|
36756
|
+
if (!textLines.length)
|
|
36757
|
+
return { width: 0, height: 0 };
|
|
36480
36758
|
const font = computeTextFont(style, fontUnit);
|
|
36481
|
-
|
|
36482
|
-
|
|
36483
|
-
|
|
36484
|
-
if (!
|
|
36485
|
-
|
|
36759
|
+
const sizes = textLines.map((line) => computeCachedTextDimension(context, line, font));
|
|
36760
|
+
const height = computeTextLinesHeight(sizes[0].height, textLines.length);
|
|
36761
|
+
const width = Math.max(...sizes.map((size) => size.width));
|
|
36762
|
+
if (!style.rotation) {
|
|
36763
|
+
return { height, width };
|
|
36486
36764
|
}
|
|
36487
|
-
|
|
36488
|
-
|
|
36489
|
-
|
|
36490
|
-
|
|
36491
|
-
|
|
36765
|
+
const cos = Math.abs(Math.cos(style.rotation));
|
|
36766
|
+
const sin = Math.abs(Math.sin(style.rotation));
|
|
36767
|
+
return { width: width * cos + height * sin, height: sin * width + cos * height };
|
|
36768
|
+
}
|
|
36769
|
+
function computeTextWidth(context, text, style = {}, fontUnit = "pt") {
|
|
36770
|
+
const font = computeTextFont(style, fontUnit);
|
|
36771
|
+
return computeCachedTextWidth(context, text, font, style.rotation);
|
|
36772
|
+
}
|
|
36773
|
+
function computeCachedTextWidth(context, text, font, rotation) {
|
|
36774
|
+
const size = computeCachedTextDimension(context, text, font);
|
|
36775
|
+
if (!rotation) {
|
|
36776
|
+
return size.width;
|
|
36492
36777
|
}
|
|
36493
|
-
|
|
36778
|
+
const cos = Math.abs(Math.cos(rotation));
|
|
36779
|
+
const sin = Math.abs(Math.sin(rotation));
|
|
36780
|
+
return size.width * cos + size.height * sin;
|
|
36494
36781
|
}
|
|
36495
36782
|
const textDimensionsCache = {};
|
|
36496
36783
|
function computeTextDimension(context, text, style, fontUnit = "pt") {
|
|
36497
36784
|
const font = computeTextFont(style, fontUnit);
|
|
36498
|
-
context
|
|
36499
|
-
|
|
36500
|
-
|
|
36501
|
-
|
|
36502
|
-
|
|
36503
|
-
|
|
36504
|
-
|
|
36505
|
-
|
|
36785
|
+
const size = computeCachedTextDimension(context, text, font);
|
|
36786
|
+
if (!style.rotation) {
|
|
36787
|
+
return size;
|
|
36788
|
+
}
|
|
36789
|
+
const cos = Math.abs(Math.cos(style.rotation));
|
|
36790
|
+
const sin = Math.abs(Math.sin(style.rotation));
|
|
36791
|
+
return {
|
|
36792
|
+
width: size.width * cos + size.height * sin,
|
|
36793
|
+
height: size.height * cos + size.width * sin,
|
|
36794
|
+
};
|
|
36795
|
+
}
|
|
36796
|
+
function computeCachedTextDimension(context, text, font) {
|
|
36506
36797
|
if (!textDimensionsCache[font]) {
|
|
36507
36798
|
textDimensionsCache[font] = {};
|
|
36508
36799
|
}
|
|
36509
36800
|
if (textDimensionsCache[font][text] === undefined) {
|
|
36801
|
+
context.save();
|
|
36802
|
+
context.font = font;
|
|
36510
36803
|
const measure = context.measureText(text);
|
|
36804
|
+
context.restore();
|
|
36511
36805
|
const width = measure.width;
|
|
36512
36806
|
const height = measure.fontBoundingBoxAscent + measure.fontBoundingBoxDescent;
|
|
36513
36807
|
textDimensionsCache[font][text] = { width, height };
|
|
@@ -38704,7 +38998,8 @@ class HeaderSizeUIPlugin extends CoreViewPlugin {
|
|
|
38704
38998
|
}
|
|
38705
38999
|
break;
|
|
38706
39000
|
case "SET_FORMATTING":
|
|
38707
|
-
if (cmd.style &&
|
|
39001
|
+
if (cmd.style &&
|
|
39002
|
+
("fontSize" in cmd.style || "wrapping" in cmd.style || "rotation" in cmd.style)) {
|
|
38708
39003
|
for (const zone of cmd.target) {
|
|
38709
39004
|
// TODO FLDA use rangeSet
|
|
38710
39005
|
this.updateRowSizeForZoneChange(cmd.sheetId, zone);
|
|
@@ -38861,6 +39156,10 @@ function astToFormula(ast) {
|
|
|
38861
39156
|
? `(${astToFormula(ast.operand)})`
|
|
38862
39157
|
: astToFormula(ast.operand);
|
|
38863
39158
|
return ast.value + rightOperand;
|
|
39159
|
+
case "ARRAY":
|
|
39160
|
+
return ("{" +
|
|
39161
|
+
ast.value.map((row) => row.map((cell) => astToFormula(cell)).join(",")).join(";") +
|
|
39162
|
+
"}");
|
|
38864
39163
|
case "BIN_OPERATION":
|
|
38865
39164
|
const leftOperation = leftOperandNeedsParenthesis(ast)
|
|
38866
39165
|
? `(${astToFormula(ast.left)})`
|
|
@@ -40425,7 +40724,6 @@ const dateGranularities = [
|
|
|
40425
40724
|
pivotRegistry.add("SPREADSHEET", {
|
|
40426
40725
|
ui: SpreadsheetPivot,
|
|
40427
40726
|
definition: SpreadsheetPivotRuntimeDefinition,
|
|
40428
|
-
externalData: false,
|
|
40429
40727
|
dateGranularities: [...dateGranularities],
|
|
40430
40728
|
datetimeGranularities: [...dateGranularities, "hour_number", "minute_number", "second_number"],
|
|
40431
40729
|
isMeasureCandidate: (field) => field.type !== "boolean",
|
|
@@ -40467,9 +40765,7 @@ class PivotUIPlugin extends CoreViewPlugin {
|
|
|
40467
40765
|
handle(cmd) {
|
|
40468
40766
|
if (invalidateEvaluationCommands.has(cmd.type)) {
|
|
40469
40767
|
for (const pivotId of this.getters.getPivotIds()) {
|
|
40470
|
-
|
|
40471
|
-
this.setupPivot(pivotId, { recreate: true });
|
|
40472
|
-
}
|
|
40768
|
+
this.setupPivot(pivotId, { recreate: true });
|
|
40473
40769
|
}
|
|
40474
40770
|
}
|
|
40475
40771
|
switch (cmd.type) {
|
|
@@ -40683,7 +40979,7 @@ class PivotUIPlugin extends CoreViewPlugin {
|
|
|
40683
40979
|
pivot.init({ reload: true });
|
|
40684
40980
|
}
|
|
40685
40981
|
setupPivot(pivotId, { recreate } = { recreate: false }) {
|
|
40686
|
-
const definition = this.getters.getPivotCoreDefinition(pivotId);
|
|
40982
|
+
const definition = deepCopy(this.getters.getPivotCoreDefinition(pivotId));
|
|
40687
40983
|
if (!(pivotId in this.pivots)) {
|
|
40688
40984
|
const Pivot = withPivotPresentationLayer(pivotRegistry.get(definition.type).ui);
|
|
40689
40985
|
this.pivots[pivotId] = new Pivot(this.custom, { definition, getters: this.getters });
|
|
@@ -43481,6 +43777,7 @@ class SheetUIPlugin extends UIPlugin {
|
|
|
43481
43777
|
"getTextWidth",
|
|
43482
43778
|
"getCellText",
|
|
43483
43779
|
"getCellMultiLineText",
|
|
43780
|
+
"getMultilineTextSize",
|
|
43484
43781
|
"getContiguousZone",
|
|
43485
43782
|
"computeTextYCoordinate",
|
|
43486
43783
|
];
|
|
@@ -43531,7 +43828,7 @@ class SheetUIPlugin extends UIPlugin {
|
|
|
43531
43828
|
const content = this.getters.getEvaluatedCell(position).formattedValue;
|
|
43532
43829
|
if (content) {
|
|
43533
43830
|
const multiLineText = splitTextToWidth(this.ctx, content, style, undefined);
|
|
43534
|
-
contentWidth +=
|
|
43831
|
+
contentWidth += computeMultilineTextSize(this.ctx, multiLineText, style).width;
|
|
43535
43832
|
}
|
|
43536
43833
|
for (const icon of this.getters.getCellIcons(position)) {
|
|
43537
43834
|
contentWidth += icon.margin + icon.size;
|
|
@@ -43552,6 +43849,9 @@ class SheetUIPlugin extends UIPlugin {
|
|
|
43552
43849
|
getTextWidth(text, style) {
|
|
43553
43850
|
return computeTextWidth(this.ctx, text, style);
|
|
43554
43851
|
}
|
|
43852
|
+
getMultilineTextSize(text, style) {
|
|
43853
|
+
return computeMultilineTextSize(this.ctx, text, style);
|
|
43854
|
+
}
|
|
43555
43855
|
getCellText(position, args) {
|
|
43556
43856
|
const cell = this.getters.getCell(position);
|
|
43557
43857
|
const locale = this.getters.getLocale();
|
|
@@ -43750,6 +44050,15 @@ class CarouselUIPlugin extends UIPlugin {
|
|
|
43750
44050
|
return "InvalidFigureId" /* CommandResult.InvalidFigureId */;
|
|
43751
44051
|
}
|
|
43752
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 */;
|
|
43753
44062
|
case "ADD_NEW_CHART_TO_CAROUSEL":
|
|
43754
44063
|
if (!this.getters.doesCarouselExist(cmd.figureId)) {
|
|
43755
44064
|
return "InvalidFigureId" /* CommandResult.InvalidFigureId */;
|
|
@@ -43774,6 +44083,9 @@ class CarouselUIPlugin extends UIPlugin {
|
|
|
43774
44083
|
case "ADD_FIGURE_CHART_TO_CAROUSEL":
|
|
43775
44084
|
this.addFigureChartToCarousel(cmd.carouselFigureId, cmd.chartFigureId, cmd.sheetId);
|
|
43776
44085
|
break;
|
|
44086
|
+
case "DUPLICATE_CAROUSEL_CHART":
|
|
44087
|
+
this.duplicateCarouselChart(cmd);
|
|
44088
|
+
break;
|
|
43777
44089
|
case "UPDATE_CAROUSEL_ACTIVE_ITEM":
|
|
43778
44090
|
this.carouselStates[cmd.figureId] = this.getCarouselItemId(cmd.item);
|
|
43779
44091
|
break;
|
|
@@ -43912,6 +44224,29 @@ class CarouselUIPlugin extends UIPlugin {
|
|
|
43912
44224
|
});
|
|
43913
44225
|
this.dispatch("DELETE_FIGURE", { sheetId, figureId: chartFigureId });
|
|
43914
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
|
+
}
|
|
43915
44250
|
getCarouselItemId(item) {
|
|
43916
44251
|
return item.type === "chart" ? item.chartId : "carouselDataView";
|
|
43917
44252
|
}
|
|
@@ -45127,25 +45462,43 @@ class GridSelectionPlugin extends UIPlugin {
|
|
|
45127
45462
|
return "Success" /* CommandResult.Success */;
|
|
45128
45463
|
}
|
|
45129
45464
|
handleEvent(event) {
|
|
45130
|
-
|
|
45131
|
-
let zones = [];
|
|
45465
|
+
let anchor = event.anchor;
|
|
45466
|
+
let zones = [...this.gridSelection.zones];
|
|
45132
45467
|
this.isUnbounded = event.options?.unbounded || false;
|
|
45133
45468
|
switch (event.mode) {
|
|
45134
45469
|
case "overrideSelection":
|
|
45135
45470
|
zones = [anchor.zone];
|
|
45136
45471
|
break;
|
|
45137
45472
|
case "updateAnchor":
|
|
45138
|
-
zones = [...this.gridSelection.zones];
|
|
45139
45473
|
const index = zones.findIndex((z) => isEqual(z, event.previousAnchor.zone));
|
|
45140
45474
|
if (index >= 0) {
|
|
45141
45475
|
zones[index] = anchor.zone;
|
|
45142
45476
|
}
|
|
45143
45477
|
break;
|
|
45144
45478
|
case "newAnchor":
|
|
45145
|
-
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
|
+
};
|
|
45146
45499
|
break;
|
|
45147
45500
|
}
|
|
45148
|
-
this.setSelectionMixin(
|
|
45501
|
+
this.setSelectionMixin(anchor, zones);
|
|
45149
45502
|
/** Any change to the selection has to be reflected in the selection processor. */
|
|
45150
45503
|
this.selection.resetDefaultAnchor(this, deepCopy(this.gridSelection.anchor));
|
|
45151
45504
|
const { col, row } = this.gridSelection.anchor.cell;
|
|
@@ -45425,7 +45778,7 @@ class GridSelectionPlugin extends UIPlugin {
|
|
|
45425
45778
|
setSelectionMixin(anchor, zones) {
|
|
45426
45779
|
const { anchor: clippedAnchor, zones: clippedZones } = this.clipSelection(this.getters.getActiveSheetId(), { anchor, zones });
|
|
45427
45780
|
this.gridSelection.anchor = clippedAnchor;
|
|
45428
|
-
this.gridSelection.zones =
|
|
45781
|
+
this.gridSelection.zones = clippedZones;
|
|
45429
45782
|
}
|
|
45430
45783
|
/**
|
|
45431
45784
|
* Change the anchor of the selection active cell to an absolute col and row index.
|
|
@@ -46135,6 +46488,7 @@ class SheetViewPlugin extends UIPlugin {
|
|
|
46135
46488
|
"getGridOffset",
|
|
46136
46489
|
"getViewportZoomLevel",
|
|
46137
46490
|
"getScrollBarWidth",
|
|
46491
|
+
"getMaximumSheetOffset",
|
|
46138
46492
|
];
|
|
46139
46493
|
viewports = {};
|
|
46140
46494
|
/**
|
|
@@ -47240,6 +47594,9 @@ class EventStream {
|
|
|
47240
47594
|
observe(owner, callbacks) {
|
|
47241
47595
|
this.observers.set(owner, { owner, callbacks });
|
|
47242
47596
|
}
|
|
47597
|
+
unobserve(owner) {
|
|
47598
|
+
this.observers.delete(owner);
|
|
47599
|
+
}
|
|
47243
47600
|
/**
|
|
47244
47601
|
* Capture the stream for yourself
|
|
47245
47602
|
*/
|
|
@@ -47332,6 +47689,9 @@ class SelectionStreamProcessorImpl {
|
|
|
47332
47689
|
observe(owner, callbacks) {
|
|
47333
47690
|
this.stream.observe(owner, callbacks);
|
|
47334
47691
|
}
|
|
47692
|
+
unobserve(owner) {
|
|
47693
|
+
this.stream.unobserve(owner);
|
|
47694
|
+
}
|
|
47335
47695
|
release(owner) {
|
|
47336
47696
|
if (this.stream.isListening(owner)) {
|
|
47337
47697
|
this.stream.release(owner);
|
|
@@ -47390,6 +47750,9 @@ class SelectionStreamProcessorImpl {
|
|
|
47390
47750
|
bottom: Math.max(anchorRow, row),
|
|
47391
47751
|
};
|
|
47392
47752
|
const expandedZone = this.getters.expandZone(sheetId, zone);
|
|
47753
|
+
if (isEqual(this.anchor.zone, expandedZone)) {
|
|
47754
|
+
return new DispatchResult("NoChanges" /* CommandResult.NoChanges */);
|
|
47755
|
+
}
|
|
47393
47756
|
const anchor = { zone: expandedZone, cell: { col: anchorCol, row: anchorRow } };
|
|
47394
47757
|
return this.processEvent({
|
|
47395
47758
|
mode: "updateAnchor",
|
|
@@ -47410,6 +47773,22 @@ class SelectionStreamProcessorImpl {
|
|
|
47410
47773
|
mode: "newAnchor",
|
|
47411
47774
|
});
|
|
47412
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
|
+
}
|
|
47413
47792
|
/**
|
|
47414
47793
|
* Increase or decrease the size of the current anchor zone.
|
|
47415
47794
|
* The anchor cell remains where it is. It's the opposite side
|
|
@@ -50816,5 +51195,5 @@ export { BadExpressionError, BasePlugin, CellErrorType, CircularDependencyError,
|
|
|
50816
51195
|
|
|
50817
51196
|
|
|
50818
51197
|
__info__.version = "19.1.0-alpha.3";
|
|
50819
|
-
__info__.date = "2025-
|
|
50820
|
-
__info__.hash = "
|
|
51198
|
+
__info__.date = "2025-12-02T05:34:07.213Z";
|
|
51199
|
+
__info__.hash = "04cf666";
|