@odoo/o-spreadsheet 16.4.0-alpha.6 → 16.4.0
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 +928 -567
- package/dist/o-spreadsheet.d.ts +167 -137
- package/dist/o-spreadsheet.esm.js +928 -568
- package/dist/o-spreadsheet.iife.js +928 -567
- package/dist/o-spreadsheet.iife.min.js +278 -271
- package/package.json +12 -12
|
@@ -236,6 +236,7 @@
|
|
|
236
236
|
const HEADER_FONT_SIZE = 11;
|
|
237
237
|
const DEFAULT_FONT = "'Roboto', arial";
|
|
238
238
|
const DEFAULT_VERTICAL_ALIGN = "bottom";
|
|
239
|
+
const DEFAULT_WRAPPING_MODE = "overflow";
|
|
239
240
|
// Borders
|
|
240
241
|
const DEFAULT_BORDER_DESC = { style: "thin", color: "#000000" };
|
|
241
242
|
const DEFAULT_FILTER_BORDER_DESC = { style: "thin", color: FILTERS_COLOR };
|
|
@@ -393,82 +394,6 @@
|
|
|
393
394
|
function clip(val, min, max) {
|
|
394
395
|
return val < min ? min : val > max ? max : val;
|
|
395
396
|
}
|
|
396
|
-
function computeTextLinesHeight(textLineHeight, numberOfLines = 1) {
|
|
397
|
-
return numberOfLines * (textLineHeight + MIN_CELL_TEXT_MARGIN) - MIN_CELL_TEXT_MARGIN;
|
|
398
|
-
}
|
|
399
|
-
/**
|
|
400
|
-
* Get the default height of the cell given its style.
|
|
401
|
-
*/
|
|
402
|
-
function getDefaultCellHeight(cell) {
|
|
403
|
-
if (!cell || !cell.content) {
|
|
404
|
-
return DEFAULT_CELL_HEIGHT;
|
|
405
|
-
}
|
|
406
|
-
const fontSize = computeTextFontSizeInPixels(cell.style);
|
|
407
|
-
// the number of lines should be computed from the formula result, but it's not evaluated at this point
|
|
408
|
-
const numberOfLines = cell.isFormula ? 1 : cell.content.split(NEWLINE).length;
|
|
409
|
-
return computeTextLinesHeight(fontSize, numberOfLines) + 2 * PADDING_AUTORESIZE_VERTICAL;
|
|
410
|
-
}
|
|
411
|
-
function computeTextWidth(context, text, style) {
|
|
412
|
-
const font = computeTextFont(style);
|
|
413
|
-
if (!textWidthCache[font]) {
|
|
414
|
-
textWidthCache[font] = {};
|
|
415
|
-
}
|
|
416
|
-
if (textWidthCache[font][text] === undefined) {
|
|
417
|
-
context.save();
|
|
418
|
-
context.font = font;
|
|
419
|
-
const textWidth = context.measureText(text).width;
|
|
420
|
-
context.restore();
|
|
421
|
-
textWidthCache[font][text] = textWidth;
|
|
422
|
-
}
|
|
423
|
-
return textWidthCache[font][text];
|
|
424
|
-
}
|
|
425
|
-
const textWidthCache = {};
|
|
426
|
-
function fontSizeInPixels(fontSize) {
|
|
427
|
-
return Math.round((fontSize * 96) / 72);
|
|
428
|
-
}
|
|
429
|
-
function computeTextFont(style) {
|
|
430
|
-
const italic = style.italic ? "italic " : "";
|
|
431
|
-
const weight = style.bold ? "bold" : DEFAULT_FONT_WEIGHT;
|
|
432
|
-
const size = computeTextFontSizeInPixels(style);
|
|
433
|
-
return `${italic}${weight} ${size}px ${DEFAULT_FONT}`;
|
|
434
|
-
}
|
|
435
|
-
function computeTextFontSizeInPixels(style) {
|
|
436
|
-
const sizeInPt = style?.fontSize || DEFAULT_FONT_SIZE;
|
|
437
|
-
return fontSizeInPixels(sizeInPt);
|
|
438
|
-
}
|
|
439
|
-
/**
|
|
440
|
-
* Return the font size that makes the width of a text match the given line width.
|
|
441
|
-
* Minimum font size is 1.
|
|
442
|
-
*
|
|
443
|
-
* @param getTextWidth function that takes a fontSize as argument, and return the width of the text with this font size.
|
|
444
|
-
*/
|
|
445
|
-
function getFontSizeMatchingWidth(lineWidth, maxFontSize, getTextWidth, precision = 0.25) {
|
|
446
|
-
let minFontSize = 1;
|
|
447
|
-
if (getTextWidth(minFontSize) > lineWidth)
|
|
448
|
-
return minFontSize;
|
|
449
|
-
if (getTextWidth(maxFontSize) < lineWidth)
|
|
450
|
-
return maxFontSize;
|
|
451
|
-
// Dichotomic search
|
|
452
|
-
let fontSize = (minFontSize + maxFontSize) / 2;
|
|
453
|
-
let currentTextWidth = getTextWidth(fontSize);
|
|
454
|
-
// Use a maximum number of iterations to be safe, because measuring text isn't 100% precise
|
|
455
|
-
let iterations = 0;
|
|
456
|
-
while (Math.abs(currentTextWidth - lineWidth) > precision && iterations < 20) {
|
|
457
|
-
if (currentTextWidth >= lineWidth) {
|
|
458
|
-
maxFontSize = (minFontSize + maxFontSize) / 2;
|
|
459
|
-
}
|
|
460
|
-
else {
|
|
461
|
-
minFontSize = (minFontSize + maxFontSize) / 2;
|
|
462
|
-
}
|
|
463
|
-
fontSize = (minFontSize + maxFontSize) / 2;
|
|
464
|
-
currentTextWidth = getTextWidth(fontSize);
|
|
465
|
-
iterations++;
|
|
466
|
-
}
|
|
467
|
-
return fontSize;
|
|
468
|
-
}
|
|
469
|
-
function computeIconWidth(style) {
|
|
470
|
-
return computeTextFontSizeInPixels(style) + 2 * MIN_CF_ICON_MARGIN;
|
|
471
|
-
}
|
|
472
397
|
/**
|
|
473
398
|
* Create a range from start (included) to end (excluded).
|
|
474
399
|
* range(10, 13) => [10, 11, 12]
|
|
@@ -723,10 +648,6 @@
|
|
|
723
648
|
Object.keys(cleanObject).forEach((key) => !cleanObject[key] && delete cleanObject[key]);
|
|
724
649
|
return cleanObject;
|
|
725
650
|
}
|
|
726
|
-
/** Transform a string to lower case. If the string is undefined, return an empty string */
|
|
727
|
-
function toLowerCase(str) {
|
|
728
|
-
return str ? str.toLowerCase() : "";
|
|
729
|
-
}
|
|
730
651
|
function transpose2dArray(matrix, callback = (val) => val) {
|
|
731
652
|
if (!matrix.length)
|
|
732
653
|
return [];
|
|
@@ -815,6 +736,14 @@
|
|
|
815
736
|
return cache.get(args[0]);
|
|
816
737
|
},
|
|
817
738
|
}[funcName];
|
|
739
|
+
}
|
|
740
|
+
function removeIndexesFromArray(array, indexes) {
|
|
741
|
+
return array.filter((_, index) => !indexes.includes(index));
|
|
742
|
+
}
|
|
743
|
+
function insertItemsAtIndex(array, items, index) {
|
|
744
|
+
const newArray = [...array];
|
|
745
|
+
newArray.splice(index, 0, ...items);
|
|
746
|
+
return newArray;
|
|
818
747
|
}
|
|
819
748
|
|
|
820
749
|
const RBA_REGEX = /rgba?\(|\s+|\)/gi;
|
|
@@ -958,13 +887,13 @@
|
|
|
958
887
|
let g = rgba.g.toString(16);
|
|
959
888
|
let b = rgba.b.toString(16);
|
|
960
889
|
let a = Math.round(rgba.a * 255).toString(16);
|
|
961
|
-
if (r.length
|
|
890
|
+
if (r.length === 1)
|
|
962
891
|
r = "0" + r;
|
|
963
|
-
if (g.length
|
|
892
|
+
if (g.length === 1)
|
|
964
893
|
g = "0" + g;
|
|
965
|
-
if (b.length
|
|
894
|
+
if (b.length === 1)
|
|
966
895
|
b = "0" + b;
|
|
967
|
-
if (a.length
|
|
896
|
+
if (a.length === 1)
|
|
968
897
|
a = "0" + a;
|
|
969
898
|
if (a === "ff")
|
|
970
899
|
a = "";
|
|
@@ -1067,13 +996,13 @@
|
|
|
1067
996
|
let l = 0;
|
|
1068
997
|
// Calculate hue
|
|
1069
998
|
// No difference
|
|
1070
|
-
if (delta
|
|
999
|
+
if (delta === 0)
|
|
1071
1000
|
h = 0;
|
|
1072
1001
|
// Red is max
|
|
1073
|
-
else if (cMax
|
|
1002
|
+
else if (cMax === r)
|
|
1074
1003
|
h = ((g - b) / delta) % 6;
|
|
1075
1004
|
// Green is max
|
|
1076
|
-
else if (cMax
|
|
1005
|
+
else if (cMax === g)
|
|
1077
1006
|
h = (b - r) / delta + 2;
|
|
1078
1007
|
// Blue is max
|
|
1079
1008
|
else
|
|
@@ -1084,7 +1013,7 @@
|
|
|
1084
1013
|
h += 360;
|
|
1085
1014
|
l = (cMax + cMin) / 2;
|
|
1086
1015
|
// Calculate saturation
|
|
1087
|
-
s = delta
|
|
1016
|
+
s = delta === 0 ? 0 : delta / (1 - Math.abs(2 * l - 1));
|
|
1088
1017
|
// Multiply l and s by 100
|
|
1089
1018
|
s = +(s * 100).toFixed(1);
|
|
1090
1019
|
l = +(l * 100).toFixed(1);
|
|
@@ -1521,7 +1450,7 @@
|
|
|
1521
1450
|
}
|
|
1522
1451
|
function isLeapYear(year) {
|
|
1523
1452
|
const _year = Math.trunc(year);
|
|
1524
|
-
return (_year % 4 === 0 && _year % 100 != 0) || _year % 400
|
|
1453
|
+
return (_year % 4 === 0 && _year % 100 != 0) || _year % 400 === 0;
|
|
1525
1454
|
}
|
|
1526
1455
|
function getYearFrac(startDate, endDate, _dayCountConvention) {
|
|
1527
1456
|
if (startDate === endDate) {
|
|
@@ -2182,6 +2111,39 @@
|
|
|
2182
2111
|
}
|
|
2183
2112
|
return undefined;
|
|
2184
2113
|
}
|
|
2114
|
+
function createCurrencyFormat(currency) {
|
|
2115
|
+
const decimalPlaces = currency.decimalPlaces ?? 2;
|
|
2116
|
+
const position = currency.position ?? "before";
|
|
2117
|
+
const code = currency.code ?? "";
|
|
2118
|
+
const symbol = currency.symbol ?? "";
|
|
2119
|
+
const decimalRepresentation = decimalPlaces ? "." + "0".repeat(decimalPlaces) : "";
|
|
2120
|
+
const numberFormat = "#,##0" + decimalRepresentation;
|
|
2121
|
+
let textExpression = `${code} ${symbol}`.trim();
|
|
2122
|
+
if (position === "after" && code) {
|
|
2123
|
+
textExpression = " " + textExpression;
|
|
2124
|
+
}
|
|
2125
|
+
return insertTextInFormat(textExpression, position, numberFormat);
|
|
2126
|
+
}
|
|
2127
|
+
function insertTextInFormat(text, position, format) {
|
|
2128
|
+
const textExpression = `[$${text}]`;
|
|
2129
|
+
return position === "before" ? textExpression + format : format + textExpression;
|
|
2130
|
+
}
|
|
2131
|
+
function roundFormat(format) {
|
|
2132
|
+
const internalFormat = parseFormat(format);
|
|
2133
|
+
const roundedFormat = internalFormat.map((formatPart) => {
|
|
2134
|
+
if (formatPart.type === "NUMBER") {
|
|
2135
|
+
return {
|
|
2136
|
+
type: formatPart.type,
|
|
2137
|
+
format: {
|
|
2138
|
+
...formatPart.format,
|
|
2139
|
+
decimalPart: undefined,
|
|
2140
|
+
},
|
|
2141
|
+
};
|
|
2142
|
+
}
|
|
2143
|
+
return formatPart;
|
|
2144
|
+
});
|
|
2145
|
+
return convertInternalFormatToFormat(roundedFormat);
|
|
2146
|
+
}
|
|
2185
2147
|
function createLargeNumberFormat(format, magnitude, postFix, locale) {
|
|
2186
2148
|
const internalFormat = parseFormat(format || "#,##0");
|
|
2187
2149
|
const largeNumberFormat = internalFormat
|
|
@@ -3275,6 +3237,169 @@
|
|
|
3275
3237
|
return rows;
|
|
3276
3238
|
}
|
|
3277
3239
|
|
|
3240
|
+
function computeTextLinesHeight(textLineHeight, numberOfLines = 1) {
|
|
3241
|
+
return numberOfLines * (textLineHeight + MIN_CELL_TEXT_MARGIN) - MIN_CELL_TEXT_MARGIN;
|
|
3242
|
+
}
|
|
3243
|
+
/**
|
|
3244
|
+
* Get the default height of the cell given its style.
|
|
3245
|
+
*/
|
|
3246
|
+
function getDefaultCellHeight(ctx, cell, colSize) {
|
|
3247
|
+
if (!cell || !cell.content)
|
|
3248
|
+
return DEFAULT_CELL_HEIGHT;
|
|
3249
|
+
const maxWidth = cell.style?.wrapping ? colSize - 2 * MIN_CELL_TEXT_MARGIN : undefined;
|
|
3250
|
+
const numberOfLines = cell.isFormula
|
|
3251
|
+
? 1
|
|
3252
|
+
: splitTextToWidth(ctx, cell.content, cell.style, maxWidth).length;
|
|
3253
|
+
const fontSize = computeTextFontSizeInPixels(cell.style);
|
|
3254
|
+
return computeTextLinesHeight(fontSize, numberOfLines) + 2 * PADDING_AUTORESIZE_VERTICAL;
|
|
3255
|
+
}
|
|
3256
|
+
const textWidthCache = {};
|
|
3257
|
+
function computeTextWidth(context, text, style) {
|
|
3258
|
+
const font = computeTextFont(style);
|
|
3259
|
+
if (!textWidthCache[font]) {
|
|
3260
|
+
textWidthCache[font] = {};
|
|
3261
|
+
}
|
|
3262
|
+
if (textWidthCache[font][text] === undefined) {
|
|
3263
|
+
context.save();
|
|
3264
|
+
context.font = font;
|
|
3265
|
+
const textWidth = context.measureText(text).width;
|
|
3266
|
+
context.restore();
|
|
3267
|
+
textWidthCache[font][text] = textWidth;
|
|
3268
|
+
}
|
|
3269
|
+
return textWidthCache[font][text];
|
|
3270
|
+
}
|
|
3271
|
+
function fontSizeInPixels(fontSize) {
|
|
3272
|
+
return Math.round((fontSize * 96) / 72);
|
|
3273
|
+
}
|
|
3274
|
+
function computeTextFont(style) {
|
|
3275
|
+
const italic = style.italic ? "italic " : "";
|
|
3276
|
+
const weight = style.bold ? "bold" : DEFAULT_FONT_WEIGHT;
|
|
3277
|
+
const size = computeTextFontSizeInPixels(style);
|
|
3278
|
+
return `${italic}${weight} ${size}px ${DEFAULT_FONT}`;
|
|
3279
|
+
}
|
|
3280
|
+
function computeTextFontSizeInPixels(style) {
|
|
3281
|
+
const sizeInPt = style?.fontSize || DEFAULT_FONT_SIZE;
|
|
3282
|
+
return fontSizeInPixels(sizeInPt);
|
|
3283
|
+
}
|
|
3284
|
+
function splitWordToSpecificWidth(ctx, word, width, style) {
|
|
3285
|
+
const wordWidth = computeTextWidth(ctx, word, style);
|
|
3286
|
+
if (wordWidth <= width) {
|
|
3287
|
+
return [word];
|
|
3288
|
+
}
|
|
3289
|
+
const splitWord = [];
|
|
3290
|
+
let wordPart = "";
|
|
3291
|
+
for (let l of word) {
|
|
3292
|
+
const wordPartWidth = computeTextWidth(ctx, wordPart + l, style);
|
|
3293
|
+
if (wordPartWidth > width) {
|
|
3294
|
+
splitWord.push(wordPart);
|
|
3295
|
+
wordPart = l;
|
|
3296
|
+
}
|
|
3297
|
+
else {
|
|
3298
|
+
wordPart += l;
|
|
3299
|
+
}
|
|
3300
|
+
}
|
|
3301
|
+
splitWord.push(wordPart);
|
|
3302
|
+
return splitWord;
|
|
3303
|
+
}
|
|
3304
|
+
/**
|
|
3305
|
+
* Return the given text, split in multiple lines if needed. The text will be split in multiple
|
|
3306
|
+
* line if it contains NEWLINE characters, or if it's longer than the given width.
|
|
3307
|
+
*/
|
|
3308
|
+
function splitTextToWidth(ctx, text, style, width) {
|
|
3309
|
+
if (!style)
|
|
3310
|
+
style = {};
|
|
3311
|
+
const brokenText = [];
|
|
3312
|
+
// Checking if text contains NEWLINE before split makes it very slightly slower if text contains it,
|
|
3313
|
+
// but 5-10x faster if it doesn't
|
|
3314
|
+
const lines = text.includes(NEWLINE) ? text.split(NEWLINE) : [text];
|
|
3315
|
+
for (const line of lines) {
|
|
3316
|
+
const words = line.includes(" ") ? line.split(" ") : [line];
|
|
3317
|
+
if (!width) {
|
|
3318
|
+
brokenText.push(line);
|
|
3319
|
+
continue;
|
|
3320
|
+
}
|
|
3321
|
+
let textLine = "";
|
|
3322
|
+
let availableWidth = width;
|
|
3323
|
+
for (let word of words) {
|
|
3324
|
+
const splitWord = splitWordToSpecificWidth(ctx, word, width, style);
|
|
3325
|
+
const lastPart = splitWord.pop();
|
|
3326
|
+
const lastPartWidth = computeTextWidth(ctx, lastPart, style);
|
|
3327
|
+
// At this step: "splitWord" is an array composed of parts of word whose
|
|
3328
|
+
// length is at most equal to "width".
|
|
3329
|
+
// Last part contains the end of the word.
|
|
3330
|
+
// Note that: When word length is less than width, then lastPart is equal
|
|
3331
|
+
// to word and splitWord is empty
|
|
3332
|
+
if (splitWord.length) {
|
|
3333
|
+
if (textLine !== "") {
|
|
3334
|
+
brokenText.push(textLine);
|
|
3335
|
+
textLine = "";
|
|
3336
|
+
availableWidth = width;
|
|
3337
|
+
}
|
|
3338
|
+
splitWord.forEach((wordPart) => {
|
|
3339
|
+
brokenText.push(wordPart);
|
|
3340
|
+
});
|
|
3341
|
+
textLine = lastPart;
|
|
3342
|
+
availableWidth = width - lastPartWidth;
|
|
3343
|
+
}
|
|
3344
|
+
else {
|
|
3345
|
+
// here "lastPart" is equal to "word" and the "word" size is smaller than "width"
|
|
3346
|
+
const _word = textLine === "" ? lastPart : " " + lastPart;
|
|
3347
|
+
const wordWidth = computeTextWidth(ctx, _word, style);
|
|
3348
|
+
if (wordWidth <= availableWidth) {
|
|
3349
|
+
textLine += _word;
|
|
3350
|
+
availableWidth -= wordWidth;
|
|
3351
|
+
}
|
|
3352
|
+
else {
|
|
3353
|
+
brokenText.push(textLine);
|
|
3354
|
+
textLine = lastPart;
|
|
3355
|
+
availableWidth = width - lastPartWidth;
|
|
3356
|
+
}
|
|
3357
|
+
}
|
|
3358
|
+
}
|
|
3359
|
+
if (textLine !== "") {
|
|
3360
|
+
brokenText.push(textLine);
|
|
3361
|
+
}
|
|
3362
|
+
}
|
|
3363
|
+
return brokenText;
|
|
3364
|
+
}
|
|
3365
|
+
/**
|
|
3366
|
+
* Return the font size that makes the width of a text match the given line width.
|
|
3367
|
+
* Minimum font size is 1.
|
|
3368
|
+
*
|
|
3369
|
+
* @param getTextWidth function that takes a fontSize as argument, and return the width of the text with this font size.
|
|
3370
|
+
*/
|
|
3371
|
+
function getFontSizeMatchingWidth(lineWidth, maxFontSize, getTextWidth, precision = 0.25) {
|
|
3372
|
+
let minFontSize = 1;
|
|
3373
|
+
if (getTextWidth(minFontSize) > lineWidth)
|
|
3374
|
+
return minFontSize;
|
|
3375
|
+
if (getTextWidth(maxFontSize) < lineWidth)
|
|
3376
|
+
return maxFontSize;
|
|
3377
|
+
// Dichotomic search
|
|
3378
|
+
let fontSize = (minFontSize + maxFontSize) / 2;
|
|
3379
|
+
let currentTextWidth = getTextWidth(fontSize);
|
|
3380
|
+
// Use a maximum number of iterations to be safe, because measuring text isn't 100% precise
|
|
3381
|
+
let iterations = 0;
|
|
3382
|
+
while (Math.abs(currentTextWidth - lineWidth) > precision && iterations < 20) {
|
|
3383
|
+
if (currentTextWidth >= lineWidth) {
|
|
3384
|
+
maxFontSize = (minFontSize + maxFontSize) / 2;
|
|
3385
|
+
}
|
|
3386
|
+
else {
|
|
3387
|
+
minFontSize = (minFontSize + maxFontSize) / 2;
|
|
3388
|
+
}
|
|
3389
|
+
fontSize = (minFontSize + maxFontSize) / 2;
|
|
3390
|
+
currentTextWidth = getTextWidth(fontSize);
|
|
3391
|
+
iterations++;
|
|
3392
|
+
}
|
|
3393
|
+
return fontSize;
|
|
3394
|
+
}
|
|
3395
|
+
function computeIconWidth(style) {
|
|
3396
|
+
return computeTextFontSizeInPixels(style) + 2 * MIN_CF_ICON_MARGIN;
|
|
3397
|
+
}
|
|
3398
|
+
/** Transform a string to lower case. If the string is undefined, return an empty string */
|
|
3399
|
+
function toLowerCase(str) {
|
|
3400
|
+
return str ? str.toLowerCase() : "";
|
|
3401
|
+
}
|
|
3402
|
+
|
|
3278
3403
|
/*
|
|
3279
3404
|
* https://stackoverflow.com/questions/105034/create-guid-uuid-in-javascript
|
|
3280
3405
|
* */
|
|
@@ -3297,7 +3422,7 @@
|
|
|
3297
3422
|
else {
|
|
3298
3423
|
// mainly for jest and other browsers that do not have the crypto functionality
|
|
3299
3424
|
return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function (c) {
|
|
3300
|
-
var r = (Math.random() * 16) | 0, v = c
|
|
3425
|
+
var r = (Math.random() * 16) | 0, v = c === "x" ? r : (r & 0x3) | 0x8;
|
|
3301
3426
|
return v.toString(16);
|
|
3302
3427
|
});
|
|
3303
3428
|
}
|
|
@@ -4125,6 +4250,7 @@
|
|
|
4125
4250
|
CommandResult[CommandResult["NoSplitSeparatorInSelection"] = 92] = "NoSplitSeparatorInSelection";
|
|
4126
4251
|
CommandResult[CommandResult["NoActiveSheet"] = 93] = "NoActiveSheet";
|
|
4127
4252
|
CommandResult[CommandResult["InvalidLocale"] = 94] = "InvalidLocale";
|
|
4253
|
+
CommandResult[CommandResult["AlreadyInPaintingFormatMode"] = 95] = "AlreadyInPaintingFormatMode";
|
|
4128
4254
|
})(exports.CommandResult || (exports.CommandResult = {}));
|
|
4129
4255
|
|
|
4130
4256
|
const borderStyles = ["thin", "medium", "thick", "dashed", "dotted"];
|
|
@@ -5940,8 +6066,8 @@
|
|
|
5940
6066
|
return this.props.menuItems.some((menuItem) => !!menuItem.icon || !!menuItem.isActive);
|
|
5941
6067
|
}
|
|
5942
6068
|
getIconName(menu) {
|
|
5943
|
-
if (menu.icon) {
|
|
5944
|
-
return menu.icon;
|
|
6069
|
+
if (menu.icon(this.env)) {
|
|
6070
|
+
return menu.icon(this.env);
|
|
5945
6071
|
}
|
|
5946
6072
|
if (menu.isActive?.(this.env)) {
|
|
5947
6073
|
return "o-spreadsheet-Icon.CHECK";
|
|
@@ -6375,7 +6501,7 @@
|
|
|
6375
6501
|
for (let pivot = 0; pivot < dim; pivot++) {
|
|
6376
6502
|
let diagonalElement = C[pivot][pivot];
|
|
6377
6503
|
// if we have a 0 on the diagonal we'll need to swap with a lower row
|
|
6378
|
-
if (diagonalElement
|
|
6504
|
+
if (diagonalElement === 0) {
|
|
6379
6505
|
//look through every row below the i'th row
|
|
6380
6506
|
for (let row = pivot + 1; row < dim; row++) {
|
|
6381
6507
|
//if the ii'th row has a non-0 in the i'th col, swap it with that row
|
|
@@ -6402,7 +6528,7 @@
|
|
|
6402
6528
|
// the other rows so that there will be 0's in this column in the
|
|
6403
6529
|
// rows above and below this one
|
|
6404
6530
|
for (let row = 0; row < dim; row++) {
|
|
6405
|
-
if (row
|
|
6531
|
+
if (row === pivot) {
|
|
6406
6532
|
continue;
|
|
6407
6533
|
}
|
|
6408
6534
|
// We want to change this element to 0
|
|
@@ -12467,7 +12593,7 @@
|
|
|
12467
12593
|
return false;
|
|
12468
12594
|
}
|
|
12469
12595
|
catch (e) {
|
|
12470
|
-
return e?.errorType
|
|
12596
|
+
return e?.errorType === CellErrorType.NotAvailable;
|
|
12471
12597
|
}
|
|
12472
12598
|
},
|
|
12473
12599
|
isExported: true,
|
|
@@ -12593,6 +12719,18 @@
|
|
|
12593
12719
|
isExported: true,
|
|
12594
12720
|
};
|
|
12595
12721
|
// -----------------------------------------------------------------------------
|
|
12722
|
+
// FALSE
|
|
12723
|
+
// -----------------------------------------------------------------------------
|
|
12724
|
+
const FALSE = {
|
|
12725
|
+
description: _lt("Logical value `false`."),
|
|
12726
|
+
args: [],
|
|
12727
|
+
returns: ["BOOLEAN"],
|
|
12728
|
+
compute: function () {
|
|
12729
|
+
return false;
|
|
12730
|
+
},
|
|
12731
|
+
isExported: true,
|
|
12732
|
+
};
|
|
12733
|
+
// -----------------------------------------------------------------------------
|
|
12596
12734
|
// IF
|
|
12597
12735
|
// -----------------------------------------------------------------------------
|
|
12598
12736
|
const IF = {
|
|
@@ -12728,6 +12866,18 @@
|
|
|
12728
12866
|
isExported: true,
|
|
12729
12867
|
};
|
|
12730
12868
|
// -----------------------------------------------------------------------------
|
|
12869
|
+
// TRUE
|
|
12870
|
+
// -----------------------------------------------------------------------------
|
|
12871
|
+
const TRUE = {
|
|
12872
|
+
description: _lt("Logical value `true`."),
|
|
12873
|
+
args: [],
|
|
12874
|
+
returns: ["BOOLEAN"],
|
|
12875
|
+
compute: function () {
|
|
12876
|
+
return true;
|
|
12877
|
+
},
|
|
12878
|
+
isExported: true,
|
|
12879
|
+
};
|
|
12880
|
+
// -----------------------------------------------------------------------------
|
|
12731
12881
|
// XOR
|
|
12732
12882
|
// -----------------------------------------------------------------------------
|
|
12733
12883
|
const XOR = {
|
|
@@ -12754,12 +12904,14 @@
|
|
|
12754
12904
|
var logical = /*#__PURE__*/Object.freeze({
|
|
12755
12905
|
__proto__: null,
|
|
12756
12906
|
AND: AND,
|
|
12907
|
+
FALSE: FALSE,
|
|
12757
12908
|
IF: IF,
|
|
12758
12909
|
IFERROR: IFERROR,
|
|
12759
12910
|
IFNA: IFNA,
|
|
12760
12911
|
IFS: IFS,
|
|
12761
12912
|
NOT: NOT,
|
|
12762
12913
|
OR: OR,
|
|
12914
|
+
TRUE: TRUE,
|
|
12763
12915
|
XOR: XOR
|
|
12764
12916
|
});
|
|
12765
12917
|
|
|
@@ -13946,7 +14098,6 @@
|
|
|
13946
14098
|
* is useful for the composer, which needs to be able to work with incomplete
|
|
13947
14099
|
* formulas.
|
|
13948
14100
|
*/
|
|
13949
|
-
const functions$3 = functionRegistry.content;
|
|
13950
14101
|
const POSTFIX_UNARY_OPERATORS = ["%"];
|
|
13951
14102
|
const OPERATORS = "+,-,*,/,:,=,<>,>=,>,<=,<,^,&".split(",").concat(POSTFIX_UNARY_OPERATORS);
|
|
13952
14103
|
function tokenize(str, locale = DEFAULT_LOCALE) {
|
|
@@ -14077,17 +14228,11 @@
|
|
|
14077
14228
|
}
|
|
14078
14229
|
if (result.length) {
|
|
14079
14230
|
const value = result;
|
|
14080
|
-
const isFunction = value.toUpperCase() in functions$3;
|
|
14081
|
-
if (isFunction) {
|
|
14082
|
-
return { type: "FUNCTION", value };
|
|
14083
|
-
}
|
|
14084
14231
|
const isReference = rangeReference.test(value);
|
|
14085
14232
|
if (isReference) {
|
|
14086
14233
|
return { type: "REFERENCE", value };
|
|
14087
14234
|
}
|
|
14088
|
-
|
|
14089
|
-
return { type: "SYMBOL", value };
|
|
14090
|
-
}
|
|
14235
|
+
return { type: "SYMBOL", value };
|
|
14091
14236
|
}
|
|
14092
14237
|
return null;
|
|
14093
14238
|
}
|
|
@@ -14187,9 +14332,6 @@
|
|
|
14187
14332
|
return { type: "NUMBER", value: parseNumber(current.value, DEFAULT_LOCALE) };
|
|
14188
14333
|
case "STRING":
|
|
14189
14334
|
return { type: "STRING", value: removeStringQuotes(current.value) };
|
|
14190
|
-
case "FUNCTION":
|
|
14191
|
-
const args = parseFunctionArgs(tokens);
|
|
14192
|
-
return { type: "FUNCALL", value: current.value, args };
|
|
14193
14335
|
case "INVALID_REFERENCE":
|
|
14194
14336
|
throw new InvalidReferenceError();
|
|
14195
14337
|
case "REFERENCE":
|
|
@@ -14206,13 +14348,15 @@
|
|
|
14206
14348
|
value: current.value,
|
|
14207
14349
|
};
|
|
14208
14350
|
case "SYMBOL":
|
|
14209
|
-
|
|
14210
|
-
|
|
14351
|
+
const value = current.value;
|
|
14352
|
+
const nextToken = tokens[0];
|
|
14353
|
+
if (nextToken?.type === "LEFT_PAREN" && functionRegex.test(current.value)) {
|
|
14354
|
+
const args = parseFunctionArgs(tokens);
|
|
14355
|
+
return { type: "FUNCALL", value: value, args };
|
|
14211
14356
|
}
|
|
14212
|
-
|
|
14213
|
-
|
|
14214
|
-
|
|
14215
|
-
}
|
|
14357
|
+
const upperCaseValue = value.toUpperCase();
|
|
14358
|
+
if (upperCaseValue === "TRUE" || upperCaseValue === "FALSE") {
|
|
14359
|
+
return { type: "BOOLEAN", value: upperCaseValue === "TRUE" };
|
|
14216
14360
|
}
|
|
14217
14361
|
throw new BadExpressionError(_lt("Invalid formula"));
|
|
14218
14362
|
case "LEFT_PAREN":
|
|
@@ -14314,36 +14458,60 @@
|
|
|
14314
14458
|
* to nodes of a specific type.
|
|
14315
14459
|
* Useful if you want to convert some part of a formula.
|
|
14316
14460
|
*
|
|
14317
|
-
*
|
|
14318
|
-
* ```ts
|
|
14461
|
+
* @example
|
|
14319
14462
|
* convertAstNodes(ast, "FUNCALL", convertFormulaToExcel)
|
|
14320
14463
|
*
|
|
14321
14464
|
* function convertFormulaToExcel(ast: ASTFuncall) {
|
|
14322
14465
|
* // ...
|
|
14323
14466
|
* return modifiedAst
|
|
14324
14467
|
* }
|
|
14325
|
-
* ```
|
|
14326
14468
|
*/
|
|
14327
14469
|
function convertAstNodes(ast, type, fn) {
|
|
14328
|
-
|
|
14329
|
-
ast
|
|
14470
|
+
return mapAst(ast, (ast) => {
|
|
14471
|
+
if (ast.type === type) {
|
|
14472
|
+
return fn(ast);
|
|
14473
|
+
}
|
|
14474
|
+
return ast;
|
|
14475
|
+
});
|
|
14476
|
+
}
|
|
14477
|
+
function iterateAstNodes(ast) {
|
|
14478
|
+
return Array.from(astIterator(ast));
|
|
14479
|
+
}
|
|
14480
|
+
function* astIterator(ast) {
|
|
14481
|
+
yield ast;
|
|
14482
|
+
switch (ast.type) {
|
|
14483
|
+
case "FUNCALL":
|
|
14484
|
+
for (const arg of ast.args) {
|
|
14485
|
+
yield* astIterator(arg);
|
|
14486
|
+
}
|
|
14487
|
+
break;
|
|
14488
|
+
case "UNARY_OPERATION":
|
|
14489
|
+
yield* astIterator(ast.operand);
|
|
14490
|
+
break;
|
|
14491
|
+
case "BIN_OPERATION":
|
|
14492
|
+
yield* astIterator(ast.left);
|
|
14493
|
+
yield* astIterator(ast.right);
|
|
14494
|
+
break;
|
|
14330
14495
|
}
|
|
14496
|
+
}
|
|
14497
|
+
function mapAst(ast, fn) {
|
|
14498
|
+
ast = fn(ast);
|
|
14331
14499
|
switch (ast.type) {
|
|
14332
14500
|
case "FUNCALL":
|
|
14333
14501
|
return {
|
|
14334
14502
|
...ast,
|
|
14335
|
-
args: ast.args.map((child) =>
|
|
14503
|
+
args: ast.args.map((child) => mapAst(child, fn)),
|
|
14336
14504
|
};
|
|
14337
14505
|
case "UNARY_OPERATION":
|
|
14338
14506
|
return {
|
|
14339
14507
|
...ast,
|
|
14340
|
-
operand:
|
|
14508
|
+
operand: mapAst(ast.operand, fn),
|
|
14341
14509
|
};
|
|
14342
14510
|
case "BIN_OPERATION":
|
|
14343
14511
|
return {
|
|
14344
14512
|
...ast,
|
|
14345
|
-
right:
|
|
14346
|
-
left:
|
|
14513
|
+
right: mapAst(ast.right, fn),
|
|
14514
|
+
left: mapAst(ast.left, fn),
|
|
14347
14515
|
};
|
|
14348
14516
|
default:
|
|
14349
14517
|
return ast;
|
|
@@ -14613,6 +14781,9 @@
|
|
|
14613
14781
|
const { args } = ast;
|
|
14614
14782
|
const functionName = ast.value.toUpperCase();
|
|
14615
14783
|
const functionDefinition = functions$2[functionName];
|
|
14784
|
+
if (!functionDefinition) {
|
|
14785
|
+
throw new UnknownFunctionError(ast.value);
|
|
14786
|
+
}
|
|
14616
14787
|
assertEnoughArgs(ast);
|
|
14617
14788
|
const compiledArgs = [];
|
|
14618
14789
|
for (let i = 0; i < args.length; i++) {
|
|
@@ -14871,7 +15042,7 @@
|
|
|
14871
15042
|
functionStarted = "";
|
|
14872
15043
|
}
|
|
14873
15044
|
switch (token.type) {
|
|
14874
|
-
case "
|
|
15045
|
+
case "SYMBOL":
|
|
14875
15046
|
functionStarted = token.value;
|
|
14876
15047
|
break;
|
|
14877
15048
|
case "LEFT_PAREN":
|
|
@@ -14908,6 +15079,17 @@
|
|
|
14908
15079
|
return mapParentFunction(mapParenthesis(enrichTokens(tokens)));
|
|
14909
15080
|
}
|
|
14910
15081
|
|
|
15082
|
+
const functions$1 = functionRegistry.content;
|
|
15083
|
+
function isExportableToExcel(tokens) {
|
|
15084
|
+
try {
|
|
15085
|
+
const nonExportableFunctions = iterateAstNodes(parseTokens(tokens)).filter((ast) => ast.type === "FUNCALL" && !functions$1[ast.value.toUpperCase()]?.isExported);
|
|
15086
|
+
return nonExportableFunctions.length === 0;
|
|
15087
|
+
}
|
|
15088
|
+
catch (error) {
|
|
15089
|
+
return false;
|
|
15090
|
+
}
|
|
15091
|
+
}
|
|
15092
|
+
|
|
14911
15093
|
function isValidLocale(locale) {
|
|
14912
15094
|
if (!(locale &&
|
|
14913
15095
|
typeof locale === "object" &&
|
|
@@ -15127,6 +15309,8 @@
|
|
|
15127
15309
|
function createAction(item) {
|
|
15128
15310
|
const name = item.name;
|
|
15129
15311
|
const children = item.children;
|
|
15312
|
+
const description = item.description;
|
|
15313
|
+
const icon = item.icon;
|
|
15130
15314
|
return {
|
|
15131
15315
|
id: item.id || uuidGenerator$2.uuidv4(),
|
|
15132
15316
|
name: typeof name === "function" ? name : () => name,
|
|
@@ -15144,8 +15328,8 @@
|
|
|
15144
15328
|
: () => [],
|
|
15145
15329
|
isReadonlyAllowed: item.isReadonlyAllowed || false,
|
|
15146
15330
|
separator: item.separator || false,
|
|
15147
|
-
icon:
|
|
15148
|
-
description:
|
|
15331
|
+
icon: typeof icon === "function" ? icon : () => icon || "",
|
|
15332
|
+
description: typeof description === "function" ? description : () => description || "",
|
|
15149
15333
|
textColor: item.textColor,
|
|
15150
15334
|
sequence: item.sequence || 0,
|
|
15151
15335
|
};
|
|
@@ -16611,7 +16795,7 @@
|
|
|
16611
16795
|
function convertDateFormatForMoment(format) {
|
|
16612
16796
|
format = format.replace(/y/g, "Y");
|
|
16613
16797
|
format = format.replace(/d/g, "D");
|
|
16614
|
-
// "m" before "h"
|
|
16798
|
+
// "m" before "h" === month, "m" after "h" === minute
|
|
16615
16799
|
const indexH = format.indexOf("h");
|
|
16616
16800
|
if (indexH >= 0) {
|
|
16617
16801
|
format = format.slice(0, indexH).replace(/m/g, "M") + format.slice(indexH);
|
|
@@ -17465,6 +17649,7 @@
|
|
|
17465
17649
|
env.model.dispatch("SELECT_FIGURE", { id: figureId });
|
|
17466
17650
|
env.openSidePanel("ChartPanel");
|
|
17467
17651
|
},
|
|
17652
|
+
icon: "o-spreadsheet-Icon.EDIT",
|
|
17468
17653
|
},
|
|
17469
17654
|
getCopyMenuItem(figureId, env),
|
|
17470
17655
|
getCutMenuItem(figureId, env),
|
|
@@ -17480,8 +17665,14 @@
|
|
|
17480
17665
|
id: "reset_size",
|
|
17481
17666
|
name: _lt("Reset size"),
|
|
17482
17667
|
sequence: 4,
|
|
17483
|
-
execute: () => {
|
|
17484
|
-
const
|
|
17668
|
+
execute: async () => {
|
|
17669
|
+
const imagePath = env.model.getters.getImagePath(figureId);
|
|
17670
|
+
const size = env.model.getters.getImageSize(figureId) ??
|
|
17671
|
+
(await env.imageProvider?.getImageOriginalSize(imagePath));
|
|
17672
|
+
if (!env.model.getters.getImageSize(figureId)) {
|
|
17673
|
+
const image = env.model.getters.getImage(figureId);
|
|
17674
|
+
image.size = size;
|
|
17675
|
+
}
|
|
17485
17676
|
const { height, width } = getMaxFigureSize(env.model.getters, size);
|
|
17486
17677
|
env.model.dispatch("UPDATE_FIGURE", {
|
|
17487
17678
|
sheetId: env.model.getters.getActiveSheetId(),
|
|
@@ -17490,6 +17681,7 @@
|
|
|
17490
17681
|
width,
|
|
17491
17682
|
});
|
|
17492
17683
|
},
|
|
17684
|
+
icon: "o-spreadsheet-Icon.REFRESH",
|
|
17493
17685
|
},
|
|
17494
17686
|
getDeleteMenuItem(figureId, onFigureDeleted, env),
|
|
17495
17687
|
];
|
|
@@ -17506,6 +17698,7 @@
|
|
|
17506
17698
|
env.model.dispatch("COPY");
|
|
17507
17699
|
await env.clipboard.write(env.model.getters.getClipboardContent());
|
|
17508
17700
|
},
|
|
17701
|
+
icon: "o-spreadsheet-Icon.COPY",
|
|
17509
17702
|
};
|
|
17510
17703
|
}
|
|
17511
17704
|
function getCutMenuItem(figureId, env) {
|
|
@@ -17519,6 +17712,7 @@
|
|
|
17519
17712
|
env.model.dispatch("CUT");
|
|
17520
17713
|
await env.clipboard.write(env.model.getters.getClipboardContent());
|
|
17521
17714
|
},
|
|
17715
|
+
icon: "o-spreadsheet-Icon.CUT",
|
|
17522
17716
|
};
|
|
17523
17717
|
}
|
|
17524
17718
|
function getDeleteMenuItem(figureId, onFigureDeleted, env) {
|
|
@@ -17533,6 +17727,7 @@
|
|
|
17533
17727
|
});
|
|
17534
17728
|
onFigureDeleted();
|
|
17535
17729
|
},
|
|
17730
|
+
icon: "o-spreadsheet-Icon.DELETE",
|
|
17536
17731
|
};
|
|
17537
17732
|
}
|
|
17538
17733
|
|
|
@@ -18600,7 +18795,7 @@
|
|
|
18600
18795
|
};
|
|
18601
18796
|
const categorieFunctionAll = {
|
|
18602
18797
|
name: _lt("All"),
|
|
18603
|
-
children: allFunctionListMenuBuilder
|
|
18798
|
+
children: [allFunctionListMenuBuilder],
|
|
18604
18799
|
};
|
|
18605
18800
|
function allFunctionListMenuBuilder() {
|
|
18606
18801
|
const fnNames = functionRegistry.getKeys();
|
|
@@ -18932,7 +19127,10 @@
|
|
|
18932
19127
|
};
|
|
18933
19128
|
const formatNumberNumber = {
|
|
18934
19129
|
name: _lt("Number"),
|
|
18935
|
-
description:
|
|
19130
|
+
description: (env) => formatValue(1000.12, {
|
|
19131
|
+
format: "#,##0.00",
|
|
19132
|
+
locale: env.model.getters.getLocale(),
|
|
19133
|
+
}),
|
|
18936
19134
|
execute: (env) => setFormatter(env, "#,##0.00"),
|
|
18937
19135
|
isActive: (env) => isFormatSelected(env, "#,##0.00"),
|
|
18938
19136
|
};
|
|
@@ -18943,21 +19141,34 @@
|
|
|
18943
19141
|
};
|
|
18944
19142
|
const formatNumberPercent = {
|
|
18945
19143
|
name: _lt("Percent"),
|
|
18946
|
-
description:
|
|
19144
|
+
description: (env) => formatValue(0.1012, {
|
|
19145
|
+
format: "0.00%",
|
|
19146
|
+
locale: env.model.getters.getLocale(),
|
|
19147
|
+
}),
|
|
18947
19148
|
execute: FORMAT_PERCENT_ACTION,
|
|
18948
19149
|
isActive: (env) => isFormatSelected(env, "0.00%"),
|
|
18949
19150
|
};
|
|
18950
19151
|
const formatNumberCurrency = {
|
|
18951
19152
|
name: _lt("Currency"),
|
|
18952
|
-
description:
|
|
18953
|
-
|
|
18954
|
-
|
|
19153
|
+
description: (env) => formatValue(1000.12, {
|
|
19154
|
+
format: env.model.config.defaultCurrencyFormat,
|
|
19155
|
+
locale: env.model.getters.getLocale(),
|
|
19156
|
+
}),
|
|
19157
|
+
execute: (env) => setFormatter(env, env.model.config.defaultCurrencyFormat),
|
|
19158
|
+
isActive: (env) => isFormatSelected(env, env.model.config.defaultCurrencyFormat),
|
|
18955
19159
|
};
|
|
18956
19160
|
const formatNumberCurrencyRounded = {
|
|
18957
19161
|
name: _lt("Currency rounded"),
|
|
18958
|
-
description:
|
|
18959
|
-
|
|
18960
|
-
|
|
19162
|
+
description: (env) => formatValue(1000, {
|
|
19163
|
+
format: roundFormat(env.model.config.defaultCurrencyFormat),
|
|
19164
|
+
locale: env.model.getters.getLocale(),
|
|
19165
|
+
}),
|
|
19166
|
+
execute: (env) => setFormatter(env, roundFormat(env.model.config.defaultCurrencyFormat)),
|
|
19167
|
+
isActive: (env) => isFormatSelected(env, roundFormat(env.model.config.defaultCurrencyFormat)),
|
|
19168
|
+
isVisible: (env) => {
|
|
19169
|
+
const currencyFormat = env.model.config.defaultCurrencyFormat;
|
|
19170
|
+
return currencyFormat !== roundFormat(currencyFormat);
|
|
19171
|
+
},
|
|
18961
19172
|
};
|
|
18962
19173
|
const formatCustomCurrency = {
|
|
18963
19174
|
name: _lt("Custom currency"),
|
|
@@ -18966,19 +19177,37 @@
|
|
|
18966
19177
|
};
|
|
18967
19178
|
const formatNumberDate = {
|
|
18968
19179
|
name: _lt("Date"),
|
|
18969
|
-
description:
|
|
19180
|
+
description: (env) => {
|
|
19181
|
+
const locale = env.model.getters.getLocale();
|
|
19182
|
+
return formatValue(parseLiteral("9/26/2023", DEFAULT_LOCALE), {
|
|
19183
|
+
format: locale.dateFormat,
|
|
19184
|
+
locale,
|
|
19185
|
+
});
|
|
19186
|
+
},
|
|
18970
19187
|
execute: (env) => setFormatter(env, env.model.getters.getLocale().dateFormat),
|
|
18971
19188
|
isActive: (env) => isFormatSelected(env, env.model.getters.getLocale().dateFormat),
|
|
18972
19189
|
};
|
|
18973
19190
|
const formatNumberTime = {
|
|
18974
19191
|
name: _lt("Time"),
|
|
18975
|
-
description:
|
|
19192
|
+
description: (env) => {
|
|
19193
|
+
const locale = env.model.getters.getLocale();
|
|
19194
|
+
return formatValue(parseLiteral("9/26/2023 10:43:00 PM", DEFAULT_LOCALE), {
|
|
19195
|
+
format: locale.timeFormat,
|
|
19196
|
+
locale,
|
|
19197
|
+
});
|
|
19198
|
+
},
|
|
18976
19199
|
execute: (env) => setFormatter(env, env.model.getters.getLocale().timeFormat),
|
|
18977
19200
|
isActive: (env) => isFormatSelected(env, env.model.getters.getLocale().timeFormat),
|
|
18978
19201
|
};
|
|
18979
19202
|
const formatNumberDateTime = {
|
|
18980
19203
|
name: _lt("Date time"),
|
|
18981
|
-
description:
|
|
19204
|
+
description: (env) => {
|
|
19205
|
+
const locale = env.model.getters.getLocale();
|
|
19206
|
+
return formatValue(parseLiteral("9/26/2023 22:43:00", DEFAULT_LOCALE), {
|
|
19207
|
+
format: locale.dateFormat + " " + locale.timeFormat,
|
|
19208
|
+
locale,
|
|
19209
|
+
});
|
|
19210
|
+
},
|
|
18982
19211
|
execute: (env) => {
|
|
18983
19212
|
const locale = env.model.getters.getLocale();
|
|
18984
19213
|
setFormatter(env, locale.dateFormat + " " + locale.timeFormat);
|
|
@@ -19050,7 +19279,7 @@
|
|
|
19050
19279
|
};
|
|
19051
19280
|
const formatAlignmentHorizontal = {
|
|
19052
19281
|
name: _lt("Horizontal align"),
|
|
19053
|
-
icon:
|
|
19282
|
+
icon: (env) => getHorizontalAlignmentIcon(env),
|
|
19054
19283
|
};
|
|
19055
19284
|
const formatAlignmentLeft = {
|
|
19056
19285
|
name: _lt("Left"),
|
|
@@ -19075,46 +19304,50 @@
|
|
|
19075
19304
|
};
|
|
19076
19305
|
const formatAlignmentVertical = {
|
|
19077
19306
|
name: _lt("Vertical align"),
|
|
19078
|
-
icon:
|
|
19307
|
+
icon: (env) => getVerticalAlignmentIcon(env),
|
|
19079
19308
|
};
|
|
19080
19309
|
const formatAlignmentTop = {
|
|
19081
19310
|
name: _lt("Top"),
|
|
19082
19311
|
execute: (env) => setStyle(env, { verticalAlign: "top" }),
|
|
19083
|
-
isActive: (env) => (env
|
|
19312
|
+
isActive: (env) => getVerticalAlign(env) === "top",
|
|
19084
19313
|
icon: "o-spreadsheet-Icon.ALIGN_TOP",
|
|
19085
19314
|
};
|
|
19086
19315
|
const formatAlignmentMiddle = {
|
|
19087
19316
|
name: _lt("Middle"),
|
|
19088
19317
|
execute: (env) => setStyle(env, { verticalAlign: "middle" }),
|
|
19089
|
-
isActive: (env) => (env
|
|
19318
|
+
isActive: (env) => getVerticalAlign(env) === "middle",
|
|
19090
19319
|
icon: "o-spreadsheet-Icon.ALIGN_MIDDLE",
|
|
19091
19320
|
};
|
|
19092
19321
|
const formatAlignmentBottom = {
|
|
19093
19322
|
name: _lt("Bottom"),
|
|
19094
19323
|
execute: (env) => setStyle(env, { verticalAlign: "bottom" }),
|
|
19095
|
-
isActive: (env) => (env
|
|
19324
|
+
isActive: (env) => getVerticalAlign(env) === "bottom",
|
|
19096
19325
|
icon: "o-spreadsheet-Icon.ALIGN_BOTTOM",
|
|
19097
19326
|
};
|
|
19098
|
-
const
|
|
19327
|
+
const formatWrappingIcon = {
|
|
19099
19328
|
name: _lt("Wrapping"),
|
|
19100
19329
|
icon: "o-spreadsheet-Icon.WRAPPING_OVERFLOW",
|
|
19101
19330
|
};
|
|
19331
|
+
const formatWrapping = {
|
|
19332
|
+
name: _lt("Wrapping"),
|
|
19333
|
+
icon: (env) => getWrapModeIcon(env),
|
|
19334
|
+
};
|
|
19102
19335
|
const formatWrappingOverflow = {
|
|
19103
19336
|
name: _lt("Overflow"),
|
|
19104
19337
|
execute: (env) => setStyle(env, { wrapping: "overflow" }),
|
|
19105
|
-
isActive: (env) => (env
|
|
19338
|
+
isActive: (env) => getWrappingMode(env) === "overflow",
|
|
19106
19339
|
icon: "o-spreadsheet-Icon.WRAPPING_OVERFLOW",
|
|
19107
19340
|
};
|
|
19108
19341
|
const formatWrappingWrap = {
|
|
19109
19342
|
name: _lt("Wrap"),
|
|
19110
19343
|
execute: (env) => setStyle(env, { wrapping: "wrap" }),
|
|
19111
|
-
isActive: (env) => env
|
|
19344
|
+
isActive: (env) => getWrappingMode(env) === "wrap",
|
|
19112
19345
|
icon: "o-spreadsheet-Icon.WRAPPING_WRAP",
|
|
19113
19346
|
};
|
|
19114
19347
|
const formatWrappingClip = {
|
|
19115
19348
|
name: _lt("Clip"),
|
|
19116
19349
|
execute: (env) => setStyle(env, { wrapping: "clip" }),
|
|
19117
|
-
isActive: (env) => env
|
|
19350
|
+
isActive: (env) => getWrappingMode(env) === "clip",
|
|
19118
19351
|
icon: "o-spreadsheet-Icon.WRAPPING_CLIP",
|
|
19119
19352
|
};
|
|
19120
19353
|
const textColor = {
|
|
@@ -19130,14 +19363,6 @@
|
|
|
19130
19363
|
execute: OPEN_CF_SIDEPANEL_ACTION,
|
|
19131
19364
|
icon: "o-spreadsheet-Icon.CONDITIONAL_FORMAT",
|
|
19132
19365
|
};
|
|
19133
|
-
const paintFormat = {
|
|
19134
|
-
name: _lt("Paint Format"),
|
|
19135
|
-
execute: (env) => env.model.dispatch("ACTIVATE_PAINT_FORMAT", {
|
|
19136
|
-
target: env.model.getters.getSelectedZones(),
|
|
19137
|
-
}),
|
|
19138
|
-
icon: "o-spreadsheet-Icon.PAINT_FORMAT",
|
|
19139
|
-
isActive: (env) => env.model.getters.isPaintingFormat(),
|
|
19140
|
-
};
|
|
19141
19366
|
const clearFormat = {
|
|
19142
19367
|
name: _lt("Clear formatting"),
|
|
19143
19368
|
description: "Ctrl+<",
|
|
@@ -19177,6 +19402,53 @@
|
|
|
19177
19402
|
}
|
|
19178
19403
|
const cell = env.model.getters.getActiveCell();
|
|
19179
19404
|
return cell.defaultAlign;
|
|
19405
|
+
}
|
|
19406
|
+
function getVerticalAlign(env) {
|
|
19407
|
+
const style = env.model.getters.getCurrentStyle();
|
|
19408
|
+
if (style.verticalAlign) {
|
|
19409
|
+
return style.verticalAlign;
|
|
19410
|
+
}
|
|
19411
|
+
return DEFAULT_VERTICAL_ALIGN;
|
|
19412
|
+
}
|
|
19413
|
+
function getWrappingMode(env) {
|
|
19414
|
+
const style = env.model.getters.getCurrentStyle();
|
|
19415
|
+
if (style.wrapping) {
|
|
19416
|
+
return style.wrapping;
|
|
19417
|
+
}
|
|
19418
|
+
return DEFAULT_WRAPPING_MODE;
|
|
19419
|
+
}
|
|
19420
|
+
function getHorizontalAlignmentIcon(env) {
|
|
19421
|
+
const horizontalAlign = getHorizontalAlign(env);
|
|
19422
|
+
switch (horizontalAlign) {
|
|
19423
|
+
case "right":
|
|
19424
|
+
return "o-spreadsheet-Icon.ALIGN_RIGHT";
|
|
19425
|
+
case "center":
|
|
19426
|
+
return "o-spreadsheet-Icon.ALIGN_CENTER";
|
|
19427
|
+
default:
|
|
19428
|
+
return "o-spreadsheet-Icon.ALIGN_LEFT";
|
|
19429
|
+
}
|
|
19430
|
+
}
|
|
19431
|
+
function getVerticalAlignmentIcon(env) {
|
|
19432
|
+
const verticalAlign = getVerticalAlign(env);
|
|
19433
|
+
switch (verticalAlign) {
|
|
19434
|
+
case "top":
|
|
19435
|
+
return "o-spreadsheet-Icon.ALIGN_TOP";
|
|
19436
|
+
case "middle":
|
|
19437
|
+
return "o-spreadsheet-Icon.ALIGN_MIDDLE";
|
|
19438
|
+
default:
|
|
19439
|
+
return "o-spreadsheet-Icon.ALIGN_BOTTOM";
|
|
19440
|
+
}
|
|
19441
|
+
}
|
|
19442
|
+
function getWrapModeIcon(env) {
|
|
19443
|
+
const wrapMode = getWrappingMode(env);
|
|
19444
|
+
switch (wrapMode) {
|
|
19445
|
+
case "wrap":
|
|
19446
|
+
return "o-spreadsheet-Icon.WRAPPING_WRAP";
|
|
19447
|
+
case "clip":
|
|
19448
|
+
return "o-spreadsheet-Icon.WRAPPING_CLIP";
|
|
19449
|
+
default:
|
|
19450
|
+
return "o-spreadsheet-Icon.WRAPPING_OVERFLOW";
|
|
19451
|
+
}
|
|
19180
19452
|
}
|
|
19181
19453
|
|
|
19182
19454
|
var ACTION_FORMAT = /*#__PURE__*/Object.freeze({
|
|
@@ -19208,6 +19480,7 @@
|
|
|
19208
19480
|
formatAlignmentTop: formatAlignmentTop,
|
|
19209
19481
|
formatAlignmentMiddle: formatAlignmentMiddle,
|
|
19210
19482
|
formatAlignmentBottom: formatAlignmentBottom,
|
|
19483
|
+
formatWrappingIcon: formatWrappingIcon,
|
|
19211
19484
|
formatWrapping: formatWrapping,
|
|
19212
19485
|
formatWrappingOverflow: formatWrappingOverflow,
|
|
19213
19486
|
formatWrappingWrap: formatWrappingWrap,
|
|
@@ -19215,7 +19488,6 @@
|
|
|
19215
19488
|
textColor: textColor,
|
|
19216
19489
|
fillColor: fillColor,
|
|
19217
19490
|
formatCF: formatCF,
|
|
19218
|
-
paintFormat: paintFormat,
|
|
19219
19491
|
clearFormat: clearFormat
|
|
19220
19492
|
});
|
|
19221
19493
|
|
|
@@ -19978,7 +20250,7 @@
|
|
|
19978
20250
|
separator: true,
|
|
19979
20251
|
})
|
|
19980
20252
|
.addChild("format_wrapping", ["format"], {
|
|
19981
|
-
...
|
|
20253
|
+
...formatWrappingIcon,
|
|
19982
20254
|
sequence: 80,
|
|
19983
20255
|
separator: true,
|
|
19984
20256
|
})
|
|
@@ -22268,17 +22540,45 @@
|
|
|
22268
22540
|
}
|
|
22269
22541
|
get formatProposals() {
|
|
22270
22542
|
const currency = this.availableCurrencies[this.state.selectedCurrencyIndex];
|
|
22271
|
-
const
|
|
22272
|
-
const
|
|
22273
|
-
const secondPosition = currency.position === "before" ? "after" : "before";
|
|
22543
|
+
const position = currency.position;
|
|
22544
|
+
const opposite = currency.position === "before" ? "after" : "before";
|
|
22274
22545
|
const symbol = this.state.currencySymbol.trim() ? this.state.currencySymbol : "";
|
|
22275
22546
|
const code = this.state.currencyCode.trim() ? this.state.currencyCode : "";
|
|
22276
|
-
|
|
22277
|
-
|
|
22278
|
-
|
|
22279
|
-
|
|
22280
|
-
|
|
22281
|
-
|
|
22547
|
+
const decimalPlaces = currency.decimalPlaces;
|
|
22548
|
+
if (!symbol && !code) {
|
|
22549
|
+
return [];
|
|
22550
|
+
}
|
|
22551
|
+
const simple = symbol ? createCurrencyFormat({ symbol, position, decimalPlaces }) : "";
|
|
22552
|
+
const rounded = simple ? roundFormat(simple) : "";
|
|
22553
|
+
const simpleWithCode = createCurrencyFormat({ symbol, position, decimalPlaces, code });
|
|
22554
|
+
const roundedWithCode = roundFormat(simpleWithCode);
|
|
22555
|
+
const simpleOpposite = symbol
|
|
22556
|
+
? createCurrencyFormat({ symbol, position: opposite, decimalPlaces })
|
|
22557
|
+
: "";
|
|
22558
|
+
const roundedOpposite = simpleOpposite ? roundFormat(simpleOpposite) : "";
|
|
22559
|
+
const simpleOppositeWithCode = createCurrencyFormat({
|
|
22560
|
+
symbol,
|
|
22561
|
+
position: opposite,
|
|
22562
|
+
decimalPlaces,
|
|
22563
|
+
code,
|
|
22564
|
+
});
|
|
22565
|
+
const roundedOppositeWithCode = roundFormat(simpleOppositeWithCode);
|
|
22566
|
+
const formats = new Set([
|
|
22567
|
+
rounded,
|
|
22568
|
+
simple,
|
|
22569
|
+
roundedWithCode,
|
|
22570
|
+
simpleWithCode,
|
|
22571
|
+
roundedOpposite,
|
|
22572
|
+
simpleOpposite,
|
|
22573
|
+
roundedOppositeWithCode,
|
|
22574
|
+
simpleOppositeWithCode,
|
|
22575
|
+
]);
|
|
22576
|
+
return [...formats]
|
|
22577
|
+
.filter((format) => format !== "")
|
|
22578
|
+
.map((format) => ({
|
|
22579
|
+
format,
|
|
22580
|
+
example: formatValue(1000.0, { format, locale: this.env.model.getters.getLocale() }),
|
|
22581
|
+
}));
|
|
22282
22582
|
}
|
|
22283
22583
|
get isSameFormat() {
|
|
22284
22584
|
const selectedFormat = this.formatProposals[this.state.selectedFormatIndex];
|
|
@@ -22335,41 +22635,6 @@
|
|
|
22335
22635
|
initAvailableCurrencies() {
|
|
22336
22636
|
this.state.selectedCurrencyIndex = 0;
|
|
22337
22637
|
}
|
|
22338
|
-
initProposalBases(decimalPlaces) {
|
|
22339
|
-
const result = [{ format: "#,##0", example: "1,000" }];
|
|
22340
|
-
const decimalRepresentation = decimalPlaces ? "." + "0".repeat(decimalPlaces) : "";
|
|
22341
|
-
if (decimalRepresentation) {
|
|
22342
|
-
result.push({
|
|
22343
|
-
format: "#,##0" + decimalRepresentation,
|
|
22344
|
-
example: "1,000" + decimalRepresentation,
|
|
22345
|
-
});
|
|
22346
|
-
}
|
|
22347
|
-
return result;
|
|
22348
|
-
}
|
|
22349
|
-
createFormatProposals(proposalBases, symbol, code, position) {
|
|
22350
|
-
let formatProposals = [];
|
|
22351
|
-
// 1 - add proposal with symbol and without code
|
|
22352
|
-
if (symbol) {
|
|
22353
|
-
for (let base of proposalBases) {
|
|
22354
|
-
formatProposals.push(this.createFormatProposal(position, base.example, base.format, symbol));
|
|
22355
|
-
}
|
|
22356
|
-
}
|
|
22357
|
-
// 2 - if code exist --> add more proposal with symbol and with code
|
|
22358
|
-
if (code) {
|
|
22359
|
-
for (let base of proposalBases) {
|
|
22360
|
-
const expression = (position === "after" ? " " : "") + code + " " + symbol;
|
|
22361
|
-
formatProposals.push(this.createFormatProposal(position, base.example, base.format, expression));
|
|
22362
|
-
}
|
|
22363
|
-
}
|
|
22364
|
-
return formatProposals;
|
|
22365
|
-
}
|
|
22366
|
-
createFormatProposal(position, baseExample, formatBase, expression) {
|
|
22367
|
-
const formatExpression = "[$" + expression + "]";
|
|
22368
|
-
return {
|
|
22369
|
-
example: position === "before" ? expression + baseExample : baseExample + expression,
|
|
22370
|
-
format: position === "before" ? formatExpression + formatBase : formatBase + formatExpression,
|
|
22371
|
-
};
|
|
22372
|
-
}
|
|
22373
22638
|
getCommonFormat() {
|
|
22374
22639
|
const selectedZones = this.env.model.getters.getSelectedZones();
|
|
22375
22640
|
const sheetId = this.env.model.getters.getActiveSheetId();
|
|
@@ -23172,7 +23437,7 @@
|
|
|
23172
23437
|
selection.removeAllRanges();
|
|
23173
23438
|
selection.addRange(range);
|
|
23174
23439
|
}
|
|
23175
|
-
if (start
|
|
23440
|
+
if (start === end && start === 0) {
|
|
23176
23441
|
range.setStart(this.el, 0);
|
|
23177
23442
|
range.setEnd(this.el, 0);
|
|
23178
23443
|
}
|
|
@@ -23425,7 +23690,7 @@
|
|
|
23425
23690
|
}, 0);
|
|
23426
23691
|
}
|
|
23427
23692
|
}
|
|
23428
|
-
if (nodeToFind.nodeName === "P" && !isFirstParagraph && nodeToFind.textContent
|
|
23693
|
+
if (nodeToFind.nodeName === "P" && !isFirstParagraph && nodeToFind.textContent === "") {
|
|
23429
23694
|
usedCharacters++;
|
|
23430
23695
|
}
|
|
23431
23696
|
return usedCharacters;
|
|
@@ -23534,7 +23799,7 @@
|
|
|
23534
23799
|
argToFocus: Number,
|
|
23535
23800
|
};
|
|
23536
23801
|
|
|
23537
|
-
const functions
|
|
23802
|
+
const functions = functionRegistry.content;
|
|
23538
23803
|
const ASSISTANT_WIDTH = 300;
|
|
23539
23804
|
const selectionIndicatorClass = "selector-flag";
|
|
23540
23805
|
const selectionIndicatorColor = "#a9a9a9";
|
|
@@ -23568,10 +23833,6 @@
|
|
|
23568
23833
|
padding-right: 3px;
|
|
23569
23834
|
outline: none;
|
|
23570
23835
|
|
|
23571
|
-
&.unfocusable {
|
|
23572
|
-
pointer-events: none;
|
|
23573
|
-
}
|
|
23574
|
-
|
|
23575
23836
|
p {
|
|
23576
23837
|
margin-bottom: 0px;
|
|
23577
23838
|
|
|
@@ -23819,7 +24080,10 @@
|
|
|
23819
24080
|
}
|
|
23820
24081
|
}
|
|
23821
24082
|
showAutocomplete(searchTerm) {
|
|
23822
|
-
|
|
24083
|
+
const searchTermUpperCase = searchTerm.toUpperCase();
|
|
24084
|
+
if (!this.env.model.getters.getCurrentContent().startsWith("=") ||
|
|
24085
|
+
searchTermUpperCase === "TRUE" ||
|
|
24086
|
+
searchTermUpperCase === "FALSE") {
|
|
23823
24087
|
return;
|
|
23824
24088
|
}
|
|
23825
24089
|
this.autoCompleteState.showProvider = true;
|
|
@@ -23937,7 +24201,6 @@
|
|
|
23937
24201
|
switch (token.type) {
|
|
23938
24202
|
case "OPERATOR":
|
|
23939
24203
|
case "NUMBER":
|
|
23940
|
-
case "FUNCTION":
|
|
23941
24204
|
case "ARG_SEPARATOR":
|
|
23942
24205
|
case "STRING":
|
|
23943
24206
|
result.push({ value: token.value, color: tokenColors[token.type] || "#000" });
|
|
@@ -23947,10 +24210,14 @@
|
|
|
23947
24210
|
result.push({ value: token.value, color: this.rangeColor(xc, sheetName) || "#000" });
|
|
23948
24211
|
break;
|
|
23949
24212
|
case "SYMBOL":
|
|
23950
|
-
|
|
23951
|
-
|
|
24213
|
+
const value = token.value;
|
|
24214
|
+
const upperCaseValue = value.toUpperCase();
|
|
24215
|
+
if (upperCaseValue === "TRUE" || upperCaseValue === "FALSE") {
|
|
23952
24216
|
result.push({ value: token.value, color: tokenColors.NUMBER });
|
|
23953
24217
|
}
|
|
24218
|
+
else if (upperCaseValue in functionRegistry.content) {
|
|
24219
|
+
result.push({ value: token.value, color: tokenColors.FUNCTION });
|
|
24220
|
+
}
|
|
23954
24221
|
else {
|
|
23955
24222
|
result.push({ value: token.value, color: "#000" });
|
|
23956
24223
|
}
|
|
@@ -24046,25 +24313,28 @@
|
|
|
24046
24313
|
this.autoCompleteState.showProvider = false;
|
|
24047
24314
|
this.functionDescriptionState.showDescription = false;
|
|
24048
24315
|
if (content.startsWith("=")) {
|
|
24049
|
-
const
|
|
24050
|
-
if (
|
|
24051
|
-
|
|
24052
|
-
|
|
24053
|
-
|
|
24054
|
-
|
|
24055
|
-
|
|
24056
|
-
|
|
24057
|
-
|
|
24058
|
-
|
|
24059
|
-
|
|
24060
|
-
|
|
24061
|
-
|
|
24062
|
-
|
|
24063
|
-
|
|
24064
|
-
|
|
24065
|
-
|
|
24066
|
-
|
|
24067
|
-
|
|
24316
|
+
const token = this.env.model.getters.getTokenAtCursor();
|
|
24317
|
+
if (!token) {
|
|
24318
|
+
return;
|
|
24319
|
+
}
|
|
24320
|
+
if (token.type === "SYMBOL") {
|
|
24321
|
+
// initialize Autocomplete Dropdown
|
|
24322
|
+
this.showAutocomplete(token.value);
|
|
24323
|
+
return;
|
|
24324
|
+
}
|
|
24325
|
+
const tokenContext = token.functionContext;
|
|
24326
|
+
const parentFunction = tokenContext?.parent.toUpperCase();
|
|
24327
|
+
if (tokenContext &&
|
|
24328
|
+
parentFunction &&
|
|
24329
|
+
parentFunction in functions &&
|
|
24330
|
+
token.type !== "UNKNOWN") {
|
|
24331
|
+
// initialize Formula Assistant
|
|
24332
|
+
const description = functions[parentFunction];
|
|
24333
|
+
const argPosition = tokenContext.argPosition;
|
|
24334
|
+
this.functionDescriptionState.functionName = parentFunction;
|
|
24335
|
+
this.functionDescriptionState.functionDescription = description;
|
|
24336
|
+
this.functionDescriptionState.argToFocus = description.getArgToFocus(argPosition + 1) - 1;
|
|
24337
|
+
this.functionDescriptionState.showDescription = true;
|
|
24068
24338
|
}
|
|
24069
24339
|
}
|
|
24070
24340
|
}
|
|
@@ -24967,6 +25237,14 @@
|
|
|
24967
25237
|
};
|
|
24968
25238
|
}
|
|
24969
25239
|
|
|
25240
|
+
const CURSOR_SVG = /*xml*/ `
|
|
25241
|
+
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="14" height="16"><path d="M6.5.4c1.3-.8 2.9-.1 3.8 1.4l2.9 5.1c.2.4.9 1.6-.4 2.3l-1.6.9 1.8 3.1c.2.4.1 1-.2 1.2l-1.6 1c-.3.1-.9 0-1.1-.4l-1.8-3.1-1.6 1c-.6.4-1.7 0-2.2-.8L0 4.3"/><path fill="#fff" d="M9.1 2a1.4 1.1 60 0 0-1.7-.6L5.5 2.5l.9 1.6-1 .6-.9-1.6-.6.4 1.8 3.1-1.3.7-1.8-3.1-1 .6 3.8 6.6 6.8-3.98M3.9 8.8 10.82 5l.795 1.4-6.81 3.96"/></svg>
|
|
25242
|
+
`;
|
|
25243
|
+
css /* scss */ `
|
|
25244
|
+
.o-paint-format-cursor {
|
|
25245
|
+
cursor: url("data:image/svg+xml,${encodeURIComponent(CURSOR_SVG)}"), auto;
|
|
25246
|
+
}
|
|
25247
|
+
`;
|
|
24970
25248
|
function useCellHovered(env, gridRef, callback) {
|
|
24971
25249
|
let hoveredPosition = {
|
|
24972
25250
|
col: undefined,
|
|
@@ -25090,6 +25368,12 @@
|
|
|
25090
25368
|
}
|
|
25091
25369
|
return this.gridOverlay.el;
|
|
25092
25370
|
}
|
|
25371
|
+
get style() {
|
|
25372
|
+
return this.props.gridOverlayDimensions;
|
|
25373
|
+
}
|
|
25374
|
+
get isPaintingFormat() {
|
|
25375
|
+
return this.env.model.getters.isPaintingFormat();
|
|
25376
|
+
}
|
|
25093
25377
|
onMouseDown(ev) {
|
|
25094
25378
|
if (ev.button > 0) {
|
|
25095
25379
|
// not main button, probably a context menu
|
|
@@ -26267,6 +26551,9 @@
|
|
|
26267
26551
|
else if (this.menuState.isOpen) {
|
|
26268
26552
|
this.closeMenu();
|
|
26269
26553
|
}
|
|
26554
|
+
else if (this.env.model.getters.isPaintingFormat()) {
|
|
26555
|
+
this.env.model.dispatch("CANCEL_PAINT_FORMAT");
|
|
26556
|
+
}
|
|
26270
26557
|
else {
|
|
26271
26558
|
this.env.model.dispatch("CLEAN_CLIPBOARD_HIGHLIGHT");
|
|
26272
26559
|
}
|
|
@@ -26764,7 +27051,12 @@
|
|
|
26764
27051
|
};
|
|
26765
27052
|
const RELATIONSHIP_NSR = "http://schemas.openxmlformats.org/officeDocument/2006/relationships";
|
|
26766
27053
|
const HEIGHT_FACTOR = 0.75; // 100px => 75 u
|
|
26767
|
-
|
|
27054
|
+
/**
|
|
27055
|
+
* Excel says its default column width is 8.43 characters (64px)
|
|
27056
|
+
* which makes WIDTH_FACTOR = 0.1317, but it doesn't work well
|
|
27057
|
+
* 0.143 is a value from dev's experiments.
|
|
27058
|
+
*/
|
|
27059
|
+
const WIDTH_FACTOR = 0.143;
|
|
26768
27060
|
/** unit : maximum number of characters a column can hold at the standard font size. What. */
|
|
26769
27061
|
const EXCEL_DEFAULT_COL_WIDTH = 8.43;
|
|
26770
27062
|
/** unit : points */
|
|
@@ -27281,7 +27573,7 @@
|
|
|
27281
27573
|
64: "000000",
|
|
27282
27574
|
65: "FFFFFF", // system background
|
|
27283
27575
|
};
|
|
27284
|
-
const
|
|
27576
|
+
const IMAGE_MIMETYPE_TO_EXTENSION_MAPPING = {
|
|
27285
27577
|
"image/avif": "avif",
|
|
27286
27578
|
"image/bmp": "bmp",
|
|
27287
27579
|
"image/gif": "gif",
|
|
@@ -27290,6 +27582,17 @@
|
|
|
27290
27582
|
"image/png": "png",
|
|
27291
27583
|
"image/tiff": "tiff",
|
|
27292
27584
|
"image/webp": "webp",
|
|
27585
|
+
};
|
|
27586
|
+
const IMAGE_EXTENSION_TO_MIMETYPE_MAPPING = {
|
|
27587
|
+
avif: "image/avif",
|
|
27588
|
+
bmp: "image/bmp",
|
|
27589
|
+
gif: "image/gif",
|
|
27590
|
+
ico: "image/vnd.microsoft.icon",
|
|
27591
|
+
jpeg: "image/jpeg",
|
|
27592
|
+
png: "image/png",
|
|
27593
|
+
tiff: "image/tiff",
|
|
27594
|
+
webp: "image/webp",
|
|
27595
|
+
jpg: "image/jpeg",
|
|
27293
27596
|
};
|
|
27294
27597
|
|
|
27295
27598
|
/**
|
|
@@ -27453,8 +27756,8 @@
|
|
|
27453
27756
|
throw new Error("Multiple escaped blocks in format");
|
|
27454
27757
|
}
|
|
27455
27758
|
convertedFormat = convertedFormat.replace(/"(.*)"/g, "[$$$1]"); // replace '"..."' by '[$...]'
|
|
27456
|
-
convertedFormat = convertedFormat.replace(/_.{1}/g, ""); // _
|
|
27457
|
-
convertedFormat = convertedFormat.replace(/\*.{1}/g, ""); // *
|
|
27759
|
+
convertedFormat = convertedFormat.replace(/_.{1}/g, ""); // _ === ignore width of next char for align purposes. Not supported ATM
|
|
27760
|
+
convertedFormat = convertedFormat.replace(/\*.{1}/g, ""); // * === repeat next character enough to fill the line. Not supported ATM
|
|
27458
27761
|
convertedFormat = convertedFormat.replace(/\\ /g, " "); // unescape spaces
|
|
27459
27762
|
convertedFormat = convertedFormat.replace(/\\./g, (match) => match[1]); // unescape other characters
|
|
27460
27763
|
if (isXlsxDateFormat(convertedFormat)) {
|
|
@@ -28093,20 +28396,35 @@
|
|
|
28093
28396
|
convertEMUToDotValue(figure.anchors[0].rowOffset);
|
|
28094
28397
|
const y2 = getRowPosition(figure.anchors[1].row, sheetData) +
|
|
28095
28398
|
convertEMUToDotValue(figure.anchors[1].rowOffset);
|
|
28096
|
-
const
|
|
28097
|
-
|
|
28098
|
-
|
|
28099
|
-
|
|
28100
|
-
|
|
28101
|
-
|
|
28102
|
-
|
|
28103
|
-
|
|
28104
|
-
|
|
28105
|
-
|
|
28106
|
-
|
|
28107
|
-
|
|
28108
|
-
|
|
28109
|
-
|
|
28399
|
+
const figureData = { id, x: x1, y: y1 };
|
|
28400
|
+
if (isChartData(figure.data)) {
|
|
28401
|
+
return {
|
|
28402
|
+
...figureData,
|
|
28403
|
+
width: x2 - x1,
|
|
28404
|
+
height: y2 - y1,
|
|
28405
|
+
tag: "chart",
|
|
28406
|
+
data: convertChartData(figure.data),
|
|
28407
|
+
};
|
|
28408
|
+
}
|
|
28409
|
+
else if (isImageData(figure.data)) {
|
|
28410
|
+
return {
|
|
28411
|
+
...figureData,
|
|
28412
|
+
width: convertEMUToDotValue(figure.data.size.cx),
|
|
28413
|
+
height: convertEMUToDotValue(figure.data.size.cy),
|
|
28414
|
+
tag: "image",
|
|
28415
|
+
data: {
|
|
28416
|
+
path: figure.data.imageSrc,
|
|
28417
|
+
mimetype: figure.data.mimetype,
|
|
28418
|
+
},
|
|
28419
|
+
};
|
|
28420
|
+
}
|
|
28421
|
+
return undefined;
|
|
28422
|
+
}
|
|
28423
|
+
function isChartData(data) {
|
|
28424
|
+
return "dataSets" in data;
|
|
28425
|
+
}
|
|
28426
|
+
function isImageData(data) {
|
|
28427
|
+
return "imageSrc" in data;
|
|
28110
28428
|
}
|
|
28111
28429
|
function convertChartData(chartData) {
|
|
28112
28430
|
const dataSetsHaveTitle = chartData.dataSets[0].label !== undefined;
|
|
@@ -28731,29 +29049,26 @@
|
|
|
28731
29049
|
return new XMLString(concat(str));
|
|
28732
29050
|
}
|
|
28733
29051
|
/**
|
|
28734
|
-
* Removes the namespace of all the xml tags in the string.
|
|
29052
|
+
* Removes the escaped namespace of all the xml tags in the string.
|
|
28735
29053
|
*
|
|
28736
|
-
* Eg. : "
|
|
29054
|
+
* Eg. : "NAMESPACEnsNAMESPACEtest a" => "test a"
|
|
28737
29055
|
*/
|
|
28738
|
-
function
|
|
28739
|
-
return
|
|
29056
|
+
function removeTagEscapedNamespaces(tag) {
|
|
29057
|
+
return tag.replace(/NAMESPACE.*NAMESPACE(.*)/, "$1");
|
|
28740
29058
|
}
|
|
28741
29059
|
/**
|
|
28742
|
-
*
|
|
29060
|
+
* Encase the namespaces in the element's tags with NAMESPACE string
|
|
28743
29061
|
*
|
|
28744
|
-
*
|
|
28745
|
-
*/
|
|
28746
|
-
function escapeNamespaces(query) {
|
|
28747
|
-
return query.replace(/([a-z0-9]+):(?=[a-z0-9]+)/gi, "$1\\:");
|
|
28748
|
-
}
|
|
28749
|
-
/**
|
|
28750
|
-
* Return true if the querySelector ignores the namespaces when searching for a tag in the DOM.
|
|
29062
|
+
* e.g. <x:foo> becomes <NAMESPACExNAMESPACEFoo>
|
|
28751
29063
|
*
|
|
28752
|
-
*
|
|
29064
|
+
* That's useful because namespaces aren't supported by the HTML specification, so it's arbitrary whether a HTML parser/querySelector
|
|
29065
|
+
* implementation will support namespaces in the tags or not.
|
|
28753
29066
|
*/
|
|
28754
|
-
function
|
|
28755
|
-
|
|
28756
|
-
|
|
29067
|
+
function escapeTagNamespaces(str) {
|
|
29068
|
+
return str.replaceAll(/(<\/?)([a-zA-Z0-9]+):([a-zA-Z0-9]+)/g, "$1" + "NAMESPACE" + "$2" + "NAMESPACE" + "$3");
|
|
29069
|
+
}
|
|
29070
|
+
function escapeQueryNameSpaces(query) {
|
|
29071
|
+
return query.replaceAll(/([a-zA-Z0-9]+):([a-zA-Z0-9]+)/g, "NAMESPACE" + "$1" + "NAMESPACE" + "$2");
|
|
28757
29072
|
}
|
|
28758
29073
|
|
|
28759
29074
|
class AttributeValue {
|
|
@@ -28783,14 +29098,20 @@
|
|
|
28783
29098
|
// The xml file we are currently parsing. We should have one Extractor class by XLSXImportFile, but
|
|
28784
29099
|
// the XLSXImportFile contains both the main .xml file, and the .rels file
|
|
28785
29100
|
currentFile = undefined;
|
|
28786
|
-
|
|
28787
|
-
|
|
29101
|
+
/**
|
|
29102
|
+
* /!\ Important : There should be no namespaces in the tags of the XML files.
|
|
29103
|
+
*
|
|
29104
|
+
* This class use native querySelector and querySelectorAll, that's used for HTML (not XML). These aren't supposed to
|
|
29105
|
+
* handled namespaces, as they are not supported by the HTML specification. Some implementations (most browsers) do
|
|
29106
|
+
* actually support namespaces, but some don't (e.g. jsdom).
|
|
29107
|
+
*
|
|
29108
|
+
* The namespace should be escaped as with NAMESPACE string (eg. <t:foo> => <NAMESPACEtNAMESPACEfoo>).
|
|
29109
|
+
*/
|
|
28788
29110
|
constructor(rootFile, xlsxStructure, warningManager) {
|
|
28789
29111
|
this.rootFile = rootFile;
|
|
28790
29112
|
this.currentFile = rootFile.file.fileName;
|
|
28791
29113
|
this.xlsxFileStructure = xlsxStructure;
|
|
28792
29114
|
this.warningManager = warningManager;
|
|
28793
|
-
this.areNamespaceIgnored = areNamespaceIgnoredByQuerySelector();
|
|
28794
29115
|
this.relationships = {};
|
|
28795
29116
|
if (rootFile.rels) {
|
|
28796
29117
|
this.extractRelationships(rootFile.rels).map((rel) => {
|
|
@@ -28813,9 +29134,13 @@
|
|
|
28813
29134
|
/**
|
|
28814
29135
|
* Get the list of all the XLSX files in the XLSX file structure
|
|
28815
29136
|
*/
|
|
28816
|
-
|
|
28817
|
-
const
|
|
28818
|
-
|
|
29137
|
+
getListOfXMLFiles() {
|
|
29138
|
+
const XMLFiles = Object.entries(this.xlsxFileStructure)
|
|
29139
|
+
.filter(([key]) => key !== "images")
|
|
29140
|
+
.map(([_, value]) => value)
|
|
29141
|
+
.flat()
|
|
29142
|
+
.filter(isDefined$1);
|
|
29143
|
+
return XMLFiles;
|
|
28819
29144
|
}
|
|
28820
29145
|
/**
|
|
28821
29146
|
* Return an array containing the return value of the given function applied to all the XML elements
|
|
@@ -28987,43 +29312,38 @@
|
|
|
28987
29312
|
return color;
|
|
28988
29313
|
}
|
|
28989
29314
|
/**
|
|
28990
|
-
* Returns the
|
|
29315
|
+
* Returns the xml file targeted by a relationship.
|
|
28991
29316
|
*/
|
|
28992
29317
|
getTargetXmlFile(relationship) {
|
|
28993
29318
|
if (!relationship)
|
|
28994
29319
|
throw new Error("Undefined target file");
|
|
28995
|
-
|
|
28996
|
-
target = target.replace("../", "");
|
|
28997
|
-
target = target.replace("./", "");
|
|
29320
|
+
const target = this.processRelationshipTargetName(relationship.target);
|
|
28998
29321
|
// Use "endsWith" because targets are relative paths, and we know the files by their absolute path.
|
|
28999
|
-
const f = this.
|
|
29322
|
+
const f = this.getListOfXMLFiles().find((f) => f.file.fileName.endsWith(target));
|
|
29000
29323
|
if (!f || !f.file)
|
|
29001
29324
|
throw new Error("Cannot find target file");
|
|
29002
29325
|
return f;
|
|
29003
29326
|
}
|
|
29004
29327
|
/**
|
|
29005
|
-
*
|
|
29006
|
-
*
|
|
29007
|
-
* Why we need to do this :
|
|
29008
|
-
* - For an XML "<t:test />"
|
|
29009
|
-
* - on Jest(jsdom) : xml.querySelector("test") == null, xml.querySelector("t\\:test") == <t:test />
|
|
29010
|
-
* - on Browser : xml.querySelector("test") == <t:test />, xml.querySelector("t\\:test") == null
|
|
29328
|
+
* Returns the image parameters targeted by a relationship.
|
|
29011
29329
|
*/
|
|
29330
|
+
getTargetImageFile(relationship) {
|
|
29331
|
+
if (!relationship)
|
|
29332
|
+
throw new Error("Undefined target file");
|
|
29333
|
+
const target = this.processRelationshipTargetName(relationship.target);
|
|
29334
|
+
// Use "endsWith" because targets are relative paths, and we know the files by their absolute path.
|
|
29335
|
+
const f = this.xlsxFileStructure.images.find((f) => f.fileName.endsWith(target));
|
|
29336
|
+
if (!f)
|
|
29337
|
+
throw new Error("Cannot find target file");
|
|
29338
|
+
return f;
|
|
29339
|
+
}
|
|
29012
29340
|
querySelector(element, query) {
|
|
29013
|
-
|
|
29014
|
-
return element.querySelector(
|
|
29341
|
+
const escapedQuery = escapeQueryNameSpaces(query);
|
|
29342
|
+
return element.querySelector(escapedQuery);
|
|
29015
29343
|
}
|
|
29016
|
-
/**
|
|
29017
|
-
* Wrapper of querySelectorAll, but we'll remove the namespaces from the query if areNamespacesIgnored is true.
|
|
29018
|
-
*
|
|
29019
|
-
* Why we need to do this :
|
|
29020
|
-
* - For an XML "<t:test />"
|
|
29021
|
-
* - on Jest(jsdom) : xml.querySelectorAll("test") == [], xml.querySelectorAll("t\\:test") == [<t:test />]
|
|
29022
|
-
* - on Browser : xml.querySelectorAll("test") == [<t:test />], xml.querySelectorAll("t\\:test") == []
|
|
29023
|
-
*/
|
|
29024
29344
|
querySelectorAll(element, query) {
|
|
29025
|
-
|
|
29026
|
-
return element.querySelectorAll(
|
|
29345
|
+
const escapedQuery = escapeQueryNameSpaces(query);
|
|
29346
|
+
return element.querySelectorAll(escapedQuery);
|
|
29027
29347
|
}
|
|
29028
29348
|
/**
|
|
29029
29349
|
* Get a color from its id in the Theme's colorScheme.
|
|
@@ -29047,6 +29367,10 @@
|
|
|
29047
29367
|
return clrScheme[colorId].value;
|
|
29048
29368
|
}
|
|
29049
29369
|
}
|
|
29370
|
+
/** Remove signs of relative path. */
|
|
29371
|
+
processRelationshipTargetName(targetName) {
|
|
29372
|
+
return targetName.replace(/\.+\//, "");
|
|
29373
|
+
}
|
|
29050
29374
|
}
|
|
29051
29375
|
|
|
29052
29376
|
/**
|
|
@@ -29259,7 +29583,7 @@
|
|
|
29259
29583
|
throw new Error("Missing plot area in the chart definition.");
|
|
29260
29584
|
}
|
|
29261
29585
|
for (let child of plotAreaElement.children) {
|
|
29262
|
-
const tag =
|
|
29586
|
+
const tag = removeTagEscapedNamespaces(child.tagName);
|
|
29263
29587
|
if (XLSX_CHART_TYPES.some((chartType) => chartType === tag)) {
|
|
29264
29588
|
return tag;
|
|
29265
29589
|
}
|
|
@@ -29271,20 +29595,21 @@
|
|
|
29271
29595
|
class XlsxFigureExtractor extends XlsxBaseExtractor {
|
|
29272
29596
|
extractFigures() {
|
|
29273
29597
|
return this.mapOnElements({ parent: this.rootFile.file.xml, query: "xdr:wsDr", children: true }, (figureElement) => {
|
|
29274
|
-
const anchorType =
|
|
29598
|
+
const anchorType = removeTagEscapedNamespaces(figureElement.tagName);
|
|
29275
29599
|
if (anchorType !== "twoCellAnchor") {
|
|
29276
29600
|
throw new Error("Only twoCellAnchor are supported for xlsx drawings.");
|
|
29277
29601
|
}
|
|
29278
29602
|
const chartElement = this.querySelector(figureElement, "c:chart");
|
|
29279
|
-
|
|
29280
|
-
|
|
29603
|
+
const imageElement = this.querySelector(figureElement, "a:blip");
|
|
29604
|
+
if (!chartElement && !imageElement) {
|
|
29605
|
+
throw new Error("Only chart and image figures are currently supported.");
|
|
29281
29606
|
}
|
|
29282
29607
|
return {
|
|
29283
29608
|
anchors: [
|
|
29284
29609
|
this.extractFigureAnchor("xdr:from", figureElement),
|
|
29285
29610
|
this.extractFigureAnchor("xdr:to", figureElement),
|
|
29286
29611
|
],
|
|
29287
|
-
data: this.extractChart(chartElement),
|
|
29612
|
+
data: chartElement ? this.extractChart(chartElement) : this.extractImage(figureElement),
|
|
29288
29613
|
};
|
|
29289
29614
|
});
|
|
29290
29615
|
}
|
|
@@ -29309,6 +29634,24 @@
|
|
|
29309
29634
|
}
|
|
29310
29635
|
return chartDefinition;
|
|
29311
29636
|
}
|
|
29637
|
+
extractImage(figureElement) {
|
|
29638
|
+
const imageElement = this.querySelector(figureElement, "a:blip");
|
|
29639
|
+
const imageId = this.extractAttr(imageElement, "r:embed", { required: true }).asString();
|
|
29640
|
+
const image = this.getTargetImageFile(this.relationships[imageId]);
|
|
29641
|
+
if (!image) {
|
|
29642
|
+
throw new Error("Unable to extract image");
|
|
29643
|
+
}
|
|
29644
|
+
const shapePropertyElement = this.querySelector(figureElement, "a:xfrm");
|
|
29645
|
+
const extension = image.fileName.split(".").at(-1);
|
|
29646
|
+
return {
|
|
29647
|
+
imageSrc: image.imageSrc,
|
|
29648
|
+
mimetype: extension ? IMAGE_EXTENSION_TO_MIMETYPE_MAPPING[extension] : undefined,
|
|
29649
|
+
size: {
|
|
29650
|
+
cx: this.extractChildAttr(shapePropertyElement, "a:ext", "cx", { required: true }).asNum(),
|
|
29651
|
+
cy: this.extractChildAttr(shapePropertyElement, "a:ext", "cy", { required: true }).asNum(),
|
|
29652
|
+
},
|
|
29653
|
+
};
|
|
29654
|
+
}
|
|
29312
29655
|
}
|
|
29313
29656
|
|
|
29314
29657
|
/**
|
|
@@ -29878,13 +30221,22 @@
|
|
|
29878
30221
|
class XlsxReader {
|
|
29879
30222
|
warningManager;
|
|
29880
30223
|
xmls;
|
|
30224
|
+
images;
|
|
29881
30225
|
constructor(files) {
|
|
29882
30226
|
this.warningManager = new XLSXImportWarningManager();
|
|
29883
30227
|
this.xmls = {};
|
|
30228
|
+
this.images = [];
|
|
29884
30229
|
for (let key of Object.keys(files)) {
|
|
29885
30230
|
// Random files can be in xlsx (like a bin file for printer settings)
|
|
29886
30231
|
if (key.endsWith(".xml") || key.endsWith(".rels")) {
|
|
29887
|
-
|
|
30232
|
+
const contentString = escapeTagNamespaces(files[key]);
|
|
30233
|
+
this.xmls[key] = parseXML(new XMLString(contentString));
|
|
30234
|
+
}
|
|
30235
|
+
else if (key.includes("media/image")) {
|
|
30236
|
+
this.images.push({
|
|
30237
|
+
fileName: key,
|
|
30238
|
+
imageSrc: files[key]["imageSrc"],
|
|
30239
|
+
});
|
|
29888
30240
|
}
|
|
29889
30241
|
}
|
|
29890
30242
|
}
|
|
@@ -29938,6 +30290,7 @@
|
|
|
29938
30290
|
tables: getXLSXFilesOfType(CONTENT_TYPES.table, this.xmls),
|
|
29939
30291
|
pivots: getXLSXFilesOfType(CONTENT_TYPES.pivot, this.xmls),
|
|
29940
30292
|
externalLinks: getXLSXFilesOfType(CONTENT_TYPES.externalLink, this.xmls),
|
|
30293
|
+
images: this.images,
|
|
29941
30294
|
};
|
|
29942
30295
|
if (!xlsxFileStructure.workbook.rels) {
|
|
29943
30296
|
throw Error(_lt("Cannot find workbook relations file"));
|
|
@@ -30944,7 +31297,7 @@
|
|
|
30944
31297
|
}
|
|
30945
31298
|
/**
|
|
30946
31299
|
* Set the borders of a cell.
|
|
30947
|
-
* It overrides the current border if override
|
|
31300
|
+
* It overrides the current border if override === true.
|
|
30948
31301
|
*/
|
|
30949
31302
|
setBorder(sheetId, col, row, border, override = true) {
|
|
30950
31303
|
if (override || !this.borders?.[sheetId]?.[col]?.[row]?.vertical) {
|
|
@@ -32214,7 +32567,9 @@
|
|
|
32214
32567
|
const numHeader = this.getters.getNumberRows(sheetId);
|
|
32215
32568
|
let gridHeight = 0;
|
|
32216
32569
|
for (let i = 0; i < numHeader; i++) {
|
|
32217
|
-
|
|
32570
|
+
// TODO : since the row size is an UI value now, this doesn't work anymore. Using the default cell height is
|
|
32571
|
+
// a temporary solution at best, but is broken.
|
|
32572
|
+
gridHeight += this.getters.getUserRowSize(sheetId, i) || DEFAULT_CELL_HEIGHT;
|
|
32218
32573
|
}
|
|
32219
32574
|
const figures = this.getters.getFigures(sheetId);
|
|
32220
32575
|
for (const figure of figures) {
|
|
@@ -32640,33 +32995,15 @@
|
|
|
32640
32995
|
}
|
|
32641
32996
|
|
|
32642
32997
|
class HeaderSizePlugin extends CorePlugin {
|
|
32643
|
-
static getters = ["
|
|
32998
|
+
static getters = ["getUserRowSize", "getColSize"];
|
|
32644
32999
|
sizes = {};
|
|
32645
33000
|
handle(cmd) {
|
|
32646
33001
|
switch (cmd.type) {
|
|
32647
33002
|
case "CREATE_SHEET": {
|
|
32648
|
-
|
|
32649
|
-
const sizes = {
|
|
32650
|
-
COL: computedSizes.COL.map((size) => ({
|
|
32651
|
-
manualSize: undefined,
|
|
32652
|
-
computedSize: lazy(size),
|
|
32653
|
-
})),
|
|
32654
|
-
ROW: computedSizes.ROW.map((size) => ({
|
|
32655
|
-
manualSize: undefined,
|
|
32656
|
-
computedSize: lazy(size),
|
|
32657
|
-
})),
|
|
32658
|
-
};
|
|
32659
|
-
this.history.update("sizes", cmd.sheetId, sizes);
|
|
33003
|
+
this.history.update("sizes", cmd.sheetId, { COL: [], ROW: [] });
|
|
32660
33004
|
break;
|
|
32661
33005
|
}
|
|
32662
33006
|
case "DUPLICATE_SHEET":
|
|
32663
|
-
// make sure the values are computed in case the original sheet is deleted
|
|
32664
|
-
for (const row of this.sizes[cmd.sheetId].ROW) {
|
|
32665
|
-
row.computedSize();
|
|
32666
|
-
}
|
|
32667
|
-
for (const col of this.sizes[cmd.sheetId].COL) {
|
|
32668
|
-
col.computedSize();
|
|
32669
|
-
}
|
|
32670
33007
|
this.history.update("sizes", cmd.sheetIdTo, deepCopy(this.sizes[cmd.sheetId]));
|
|
32671
33008
|
break;
|
|
32672
33009
|
case "DELETE_SHEET":
|
|
@@ -32675,21 +33012,8 @@
|
|
|
32675
33012
|
this.history.update("sizes", sizes);
|
|
32676
33013
|
break;
|
|
32677
33014
|
case "REMOVE_COLUMNS_ROWS": {
|
|
32678
|
-
|
|
32679
|
-
|
|
32680
|
-
sizes.splice(headerIndex, 1);
|
|
32681
|
-
}
|
|
32682
|
-
const min = Math.min(...cmd.elements);
|
|
32683
|
-
sizes = sizes.map((size, row) => {
|
|
32684
|
-
if (cmd.dimension === "ROW" && row >= min) {
|
|
32685
|
-
// invalidate sizes
|
|
32686
|
-
return {
|
|
32687
|
-
manualSize: size.manualSize,
|
|
32688
|
-
computedSize: lazy(() => this.getRowTallestCellSize(cmd.sheetId, row)),
|
|
32689
|
-
};
|
|
32690
|
-
}
|
|
32691
|
-
return size;
|
|
32692
|
-
});
|
|
33015
|
+
const arr = this.sizes[cmd.sheetId][cmd.dimension];
|
|
33016
|
+
const sizes = removeIndexesFromArray(arr, cmd.elements);
|
|
32693
33017
|
this.history.update("sizes", cmd.sheetId, cmd.dimension, sizes);
|
|
32694
33018
|
break;
|
|
32695
33019
|
}
|
|
@@ -32698,51 +33022,18 @@
|
|
|
32698
33022
|
const addIndex = getAddHeaderStartIndex(cmd.position, cmd.base);
|
|
32699
33023
|
const baseSize = sizes[cmd.base];
|
|
32700
33024
|
sizes.splice(addIndex, 0, ...Array(cmd.quantity).fill(baseSize));
|
|
32701
|
-
sizes = sizes.map((size, row) => {
|
|
32702
|
-
if (cmd.dimension === "ROW" && row > cmd.base + cmd.quantity) {
|
|
32703
|
-
// invalidate sizes
|
|
32704
|
-
return {
|
|
32705
|
-
manualSize: size.manualSize,
|
|
32706
|
-
computedSize: lazy(() => this.getRowTallestCellSize(cmd.sheetId, row)),
|
|
32707
|
-
};
|
|
32708
|
-
}
|
|
32709
|
-
return size;
|
|
32710
|
-
});
|
|
32711
33025
|
this.history.update("sizes", cmd.sheetId, cmd.dimension, sizes);
|
|
32712
33026
|
break;
|
|
32713
33027
|
}
|
|
32714
33028
|
case "RESIZE_COLUMNS_ROWS":
|
|
32715
|
-
|
|
32716
|
-
|
|
32717
|
-
|
|
32718
|
-
const size = height;
|
|
32719
|
-
this.history.update("sizes", cmd.sheetId, cmd.dimension, el, {
|
|
32720
|
-
manualSize: cmd.size || undefined,
|
|
32721
|
-
computedSize: lazy(size),
|
|
32722
|
-
});
|
|
33029
|
+
if (cmd.dimension === "ROW") {
|
|
33030
|
+
for (const el of cmd.elements) {
|
|
33031
|
+
this.history.update("sizes", cmd.sheetId, cmd.dimension, el, cmd.size || undefined);
|
|
32723
33032
|
}
|
|
32724
|
-
else {
|
|
32725
|
-
this.history.update("sizes", cmd.sheetId, cmd.dimension, el, {
|
|
32726
|
-
manualSize: cmd.size || undefined,
|
|
32727
|
-
computedSize: lazy(cmd.size || DEFAULT_CELL_WIDTH),
|
|
32728
|
-
});
|
|
32729
|
-
}
|
|
32730
|
-
}
|
|
32731
|
-
break;
|
|
32732
|
-
case "UPDATE_CELL":
|
|
32733
|
-
if (!this.sizes[cmd.sheetId]?.["ROW"]?.[cmd.row]?.manualSize) {
|
|
32734
|
-
const { sheetId, row } = cmd;
|
|
32735
|
-
this.history.update("sizes", sheetId, "ROW", row, "computedSize", lazy(() => this.getRowTallestCellSize(sheetId, row)));
|
|
32736
33033
|
}
|
|
32737
|
-
|
|
32738
|
-
|
|
32739
|
-
|
|
32740
|
-
for (let target of cmd.target) {
|
|
32741
|
-
for (let row of range(target.top, target.bottom + 1)) {
|
|
32742
|
-
const rowHeight = this.getRowTallestCellSize(cmd.sheetId, row);
|
|
32743
|
-
if (rowHeight !== this.getRowSize(cmd.sheetId, row)) {
|
|
32744
|
-
this.history.update("sizes", cmd.sheetId, "ROW", row, "computedSize", lazy(rowHeight));
|
|
32745
|
-
}
|
|
33034
|
+
else {
|
|
33035
|
+
for (const el of cmd.elements) {
|
|
33036
|
+
this.history.update("sizes", cmd.sheetId, cmd.dimension, el, cmd.size || undefined);
|
|
32746
33037
|
}
|
|
32747
33038
|
}
|
|
32748
33039
|
break;
|
|
@@ -32750,94 +33041,29 @@
|
|
|
32750
33041
|
return;
|
|
32751
33042
|
}
|
|
32752
33043
|
getColSize(sheetId, index) {
|
|
32753
|
-
return this.
|
|
32754
|
-
}
|
|
32755
|
-
getRowSize(sheetId, index) {
|
|
32756
|
-
return this.getHeaderSize(sheetId, "ROW", index);
|
|
33044
|
+
return Math.round(this.sizes[sheetId]?.["COL"][index] || DEFAULT_CELL_WIDTH);
|
|
32757
33045
|
}
|
|
32758
|
-
|
|
32759
|
-
|
|
32760
|
-
|
|
32761
|
-
this.getDefaultHeaderSize(dimension));
|
|
32762
|
-
}
|
|
32763
|
-
computeSheetSizes(sheetId) {
|
|
32764
|
-
const sizes = { COL: [], ROW: [] };
|
|
32765
|
-
for (let col of range(0, this.getters.getNumberCols(sheetId))) {
|
|
32766
|
-
sizes.COL.push(this.getHeaderSize(sheetId, "COL", col));
|
|
32767
|
-
}
|
|
32768
|
-
for (let row of range(0, this.getters.getNumberRows(sheetId))) {
|
|
32769
|
-
let rowSize = this.sizes[sheetId]?.["ROW"]?.[row].manualSize;
|
|
32770
|
-
if (!rowSize) {
|
|
32771
|
-
const height = this.getRowTallestCellSize(sheetId, row);
|
|
32772
|
-
rowSize = height;
|
|
32773
|
-
}
|
|
32774
|
-
sizes.ROW.push(rowSize);
|
|
32775
|
-
}
|
|
32776
|
-
return sizes;
|
|
32777
|
-
}
|
|
32778
|
-
getDefaultHeaderSize(dimension) {
|
|
32779
|
-
return dimension === "COL" ? DEFAULT_CELL_WIDTH : DEFAULT_CELL_HEIGHT;
|
|
32780
|
-
}
|
|
32781
|
-
/**
|
|
32782
|
-
* Return the height the cell should have in the sheet, which is either DEFAULT_CELL_HEIGHT if the cell is in a multi-row
|
|
32783
|
-
* merge, or the height of the cell computed based on its font size.
|
|
32784
|
-
*/
|
|
32785
|
-
getCellHeight(position) {
|
|
32786
|
-
const merge = this.getters.getMerge(position);
|
|
32787
|
-
if (merge && merge.bottom !== merge.top) {
|
|
32788
|
-
return DEFAULT_CELL_HEIGHT;
|
|
32789
|
-
}
|
|
32790
|
-
const cell = this.getters.getCell(position);
|
|
32791
|
-
return getDefaultCellHeight(cell);
|
|
32792
|
-
}
|
|
32793
|
-
/**
|
|
32794
|
-
* Get the tallest cell of a row and its size.
|
|
32795
|
-
*
|
|
32796
|
-
* The tallest cell of the row correspond to the cell with the biggest font size,
|
|
32797
|
-
* and that is not part of a multi-line merge.
|
|
32798
|
-
*/
|
|
32799
|
-
getRowTallestCellSize(sheetId, row) {
|
|
32800
|
-
const cellIds = this.getters.getRowCells(sheetId, row);
|
|
32801
|
-
let maxHeight = 0;
|
|
32802
|
-
for (let i = 0; i < cellIds.length; i++) {
|
|
32803
|
-
const cell = this.getters.getCellById(cellIds[i]);
|
|
32804
|
-
if (!cell)
|
|
32805
|
-
continue;
|
|
32806
|
-
const position = this.getters.getCellPosition(cell.id);
|
|
32807
|
-
const cellHeight = this.getCellHeight(position);
|
|
32808
|
-
if (cellHeight > maxHeight && cellHeight > DEFAULT_CELL_HEIGHT) {
|
|
32809
|
-
maxHeight = cellHeight;
|
|
32810
|
-
}
|
|
32811
|
-
}
|
|
32812
|
-
if (maxHeight <= DEFAULT_CELL_HEIGHT) {
|
|
32813
|
-
return DEFAULT_CELL_HEIGHT;
|
|
32814
|
-
}
|
|
32815
|
-
return maxHeight;
|
|
33046
|
+
getUserRowSize(sheetId, index) {
|
|
33047
|
+
const rowSize = this.sizes[sheetId]?.["ROW"][index];
|
|
33048
|
+
return rowSize ? Math.round(rowSize) : undefined;
|
|
32816
33049
|
}
|
|
32817
33050
|
import(data) {
|
|
32818
33051
|
for (let sheet of data.sheets) {
|
|
32819
|
-
const
|
|
33052
|
+
const sizes = {
|
|
33053
|
+
COL: Array(sheet.colNumber).fill(undefined),
|
|
33054
|
+
ROW: Array(sheet.rowNumber).fill(undefined),
|
|
33055
|
+
};
|
|
32820
33056
|
for (let [rowIndex, row] of Object.entries(sheet.rows)) {
|
|
32821
33057
|
if (row.size) {
|
|
32822
|
-
|
|
33058
|
+
sizes["ROW"][rowIndex] = row.size;
|
|
32823
33059
|
}
|
|
32824
33060
|
}
|
|
32825
33061
|
for (let [colIndex, col] of Object.entries(sheet.cols)) {
|
|
32826
33062
|
if (col.size) {
|
|
32827
|
-
|
|
32828
|
-
}
|
|
32829
|
-
}
|
|
32830
|
-
|
|
32831
|
-
this.sizes[sheet.id] = {
|
|
32832
|
-
COL: computedSizes.COL.map((size, i) => ({
|
|
32833
|
-
manualSize: manualSizes.COL[i],
|
|
32834
|
-
computedSize: lazy(size),
|
|
32835
|
-
})),
|
|
32836
|
-
ROW: computedSizes.ROW.map((size, i) => ({
|
|
32837
|
-
manualSize: manualSizes.ROW[i],
|
|
32838
|
-
computedSize: lazy(size),
|
|
32839
|
-
})),
|
|
32840
|
-
};
|
|
33063
|
+
sizes["COL"][colIndex] = col.size;
|
|
33064
|
+
}
|
|
33065
|
+
}
|
|
33066
|
+
this.sizes[sheet.id] = sizes;
|
|
32841
33067
|
}
|
|
32842
33068
|
return;
|
|
32843
33069
|
}
|
|
@@ -32858,9 +33084,12 @@
|
|
|
32858
33084
|
if (sheet.rows === undefined) {
|
|
32859
33085
|
sheet.rows = {};
|
|
32860
33086
|
}
|
|
32861
|
-
for (
|
|
32862
|
-
if (exportDefaults || this.sizes[sheet.id]["ROW"][row]
|
|
32863
|
-
sheet.rows[row] = {
|
|
33087
|
+
for (const row of range(0, this.getters.getNumberRows(sheet.id))) {
|
|
33088
|
+
if (exportDefaults || this.sizes[sheet.id]["ROW"][row]) {
|
|
33089
|
+
sheet.rows[row] = {
|
|
33090
|
+
...sheet.rows[row],
|
|
33091
|
+
size: this.getUserRowSize(sheet.id, row) ?? DEFAULT_CELL_HEIGHT,
|
|
33092
|
+
};
|
|
32864
33093
|
}
|
|
32865
33094
|
}
|
|
32866
33095
|
// Export col sizes
|
|
@@ -32868,7 +33097,7 @@
|
|
|
32868
33097
|
sheet.cols = {};
|
|
32869
33098
|
}
|
|
32870
33099
|
for (let col of range(0, this.getters.getNumberCols(sheet.id))) {
|
|
32871
|
-
if (exportDefaults || this.sizes[sheet.id]["COL"][col]
|
|
33100
|
+
if (exportDefaults || this.sizes[sheet.id]["COL"][col]) {
|
|
32872
33101
|
sheet.cols[col] = { ...sheet.cols[col], size: this.getColSize(sheet.id, col) };
|
|
32873
33102
|
}
|
|
32874
33103
|
}
|
|
@@ -33088,7 +33317,7 @@
|
|
|
33088
33317
|
handle(cmd) {
|
|
33089
33318
|
switch (cmd.type) {
|
|
33090
33319
|
case "CREATE_IMAGE":
|
|
33091
|
-
this.
|
|
33320
|
+
this.addImage(cmd.figureId, cmd.sheetId, cmd.position, cmd.size);
|
|
33092
33321
|
this.history.update("images", cmd.sheetId, cmd.figureId, cmd.definition);
|
|
33093
33322
|
this.syncedImages.add(cmd.definition.path);
|
|
33094
33323
|
break;
|
|
@@ -33152,7 +33381,7 @@
|
|
|
33152
33381
|
// ---------------------------------------------------------------------------
|
|
33153
33382
|
// Private
|
|
33154
33383
|
// ---------------------------------------------------------------------------
|
|
33155
|
-
|
|
33384
|
+
addImage(id, sheetId, position, size) {
|
|
33156
33385
|
const figure = {
|
|
33157
33386
|
id,
|
|
33158
33387
|
x: position.x,
|
|
@@ -35659,7 +35888,6 @@
|
|
|
35659
35888
|
}
|
|
35660
35889
|
}
|
|
35661
35890
|
|
|
35662
|
-
const functions = functionRegistry.content;
|
|
35663
35891
|
//#region
|
|
35664
35892
|
// ---------------------------------------------------------------------------
|
|
35665
35893
|
// INTRODUCTION
|
|
@@ -35881,9 +36109,7 @@
|
|
|
35881
36109
|
let isExported = true;
|
|
35882
36110
|
const formulaCell = this.getCorrespondingFormulaCell(position);
|
|
35883
36111
|
if (formulaCell) {
|
|
35884
|
-
isExported = formulaCell.compiledFormula.tokens
|
|
35885
|
-
.filter((tk) => tk.type === "FUNCTION")
|
|
35886
|
-
.every((tk) => functions[tk.value.toUpperCase()].isExported);
|
|
36112
|
+
isExported = isExportableToExcel(formulaCell.compiledFormula.tokens);
|
|
35887
36113
|
isFormula = isExported;
|
|
35888
36114
|
if (!isExported) {
|
|
35889
36115
|
newContent = value.toString();
|
|
@@ -36427,7 +36653,7 @@
|
|
|
36427
36653
|
case "ContainsText":
|
|
36428
36654
|
return cell.value.toString().indexOf(values[0].toString()) > -1;
|
|
36429
36655
|
case "NotContains":
|
|
36430
|
-
return !cell.value || cell.value.toString().indexOf(values[0].toString())
|
|
36656
|
+
return !cell.value || cell.value.toString().indexOf(values[0].toString()) === -1;
|
|
36431
36657
|
case "GreaterThan":
|
|
36432
36658
|
return cell.value > values[0];
|
|
36433
36659
|
case "GreaterThanOrEqual":
|
|
@@ -36454,6 +36680,156 @@
|
|
|
36454
36680
|
};
|
|
36455
36681
|
}
|
|
36456
36682
|
|
|
36683
|
+
class UIRowSizePlugin extends UIPlugin {
|
|
36684
|
+
static getters = ["getRowSize"];
|
|
36685
|
+
tallestCellInRow = {};
|
|
36686
|
+
ctx = document.createElement("canvas").getContext("2d");
|
|
36687
|
+
handle(cmd) {
|
|
36688
|
+
switch (cmd.type) {
|
|
36689
|
+
case "START":
|
|
36690
|
+
for (const sheetId of this.getters.getSheetIds()) {
|
|
36691
|
+
this.initializeSheet(sheetId);
|
|
36692
|
+
}
|
|
36693
|
+
break;
|
|
36694
|
+
case "CREATE_SHEET": {
|
|
36695
|
+
this.initializeSheet(cmd.sheetId);
|
|
36696
|
+
break;
|
|
36697
|
+
}
|
|
36698
|
+
case "DUPLICATE_SHEET": {
|
|
36699
|
+
const tallestCells = deepCopy(this.tallestCellInRow[cmd.sheetId]);
|
|
36700
|
+
this.history.update("tallestCellInRow", cmd.sheetIdTo, tallestCells);
|
|
36701
|
+
break;
|
|
36702
|
+
}
|
|
36703
|
+
case "DELETE_SHEET":
|
|
36704
|
+
const tallestCells = { ...this.tallestCellInRow };
|
|
36705
|
+
delete tallestCells[cmd.sheetId];
|
|
36706
|
+
this.history.update("tallestCellInRow", tallestCells);
|
|
36707
|
+
break;
|
|
36708
|
+
case "REMOVE_COLUMNS_ROWS": {
|
|
36709
|
+
if (cmd.dimension === "COL") {
|
|
36710
|
+
return;
|
|
36711
|
+
}
|
|
36712
|
+
const tallestCells = removeIndexesFromArray(this.tallestCellInRow[cmd.sheetId], cmd.elements);
|
|
36713
|
+
this.history.update("tallestCellInRow", cmd.sheetId, tallestCells);
|
|
36714
|
+
break;
|
|
36715
|
+
}
|
|
36716
|
+
case "ADD_COLUMNS_ROWS": {
|
|
36717
|
+
if (cmd.dimension === "COL") {
|
|
36718
|
+
return;
|
|
36719
|
+
}
|
|
36720
|
+
const addIndex = getAddHeaderStartIndex(cmd.position, cmd.base);
|
|
36721
|
+
const newCells = Array(cmd.quantity).fill(undefined);
|
|
36722
|
+
const newTallestCells = insertItemsAtIndex(this.tallestCellInRow[cmd.sheetId], newCells, addIndex);
|
|
36723
|
+
this.history.update("tallestCellInRow", cmd.sheetId, newTallestCells);
|
|
36724
|
+
break;
|
|
36725
|
+
}
|
|
36726
|
+
case "RESIZE_COLUMNS_ROWS":
|
|
36727
|
+
{
|
|
36728
|
+
const sheetId = cmd.sheetId;
|
|
36729
|
+
if (cmd.dimension === "ROW") {
|
|
36730
|
+
for (const row of cmd.elements) {
|
|
36731
|
+
const tallestCell = this.getRowTallestCell(sheetId, row);
|
|
36732
|
+
this.history.update("tallestCellInRow", sheetId, row, tallestCell);
|
|
36733
|
+
}
|
|
36734
|
+
}
|
|
36735
|
+
else {
|
|
36736
|
+
// Recompute row heights on col size change, they might have changed because of wrapped text
|
|
36737
|
+
for (const row of range(0, this.getters.getNumberRows(sheetId))) {
|
|
36738
|
+
for (const col of cmd.elements) {
|
|
36739
|
+
this.updateRowSizeForCellChange(sheetId, row, col);
|
|
36740
|
+
}
|
|
36741
|
+
}
|
|
36742
|
+
}
|
|
36743
|
+
}
|
|
36744
|
+
break;
|
|
36745
|
+
case "UPDATE_CELL":
|
|
36746
|
+
this.updateRowSizeForCellChange(cmd.sheetId, cmd.row, cmd.col);
|
|
36747
|
+
break;
|
|
36748
|
+
case "ADD_MERGE":
|
|
36749
|
+
case "REMOVE_MERGE":
|
|
36750
|
+
for (const target of cmd.target) {
|
|
36751
|
+
for (const position of positions(target)) {
|
|
36752
|
+
this.updateRowSizeForCellChange(cmd.sheetId, position.row, position.col);
|
|
36753
|
+
}
|
|
36754
|
+
}
|
|
36755
|
+
}
|
|
36756
|
+
return;
|
|
36757
|
+
}
|
|
36758
|
+
getRowSize(sheetId, row) {
|
|
36759
|
+
return Math.round(this.getters.getUserRowSize(sheetId, row) ??
|
|
36760
|
+
this.tallestCellInRow[sheetId][row]?.size ??
|
|
36761
|
+
DEFAULT_CELL_HEIGHT);
|
|
36762
|
+
}
|
|
36763
|
+
updateRowSizeForCellChange(sheetId, row, col) {
|
|
36764
|
+
const tallestCellInRow = this.tallestCellInRow[sheetId]?.[row];
|
|
36765
|
+
if (tallestCellInRow?.cell.col === col) {
|
|
36766
|
+
const newTallestCell = this.getRowTallestCell(sheetId, row);
|
|
36767
|
+
this.history.update("tallestCellInRow", sheetId, row, newTallestCell);
|
|
36768
|
+
}
|
|
36769
|
+
const updatedCellHeight = this.getCellHeight({ sheetId, col, row });
|
|
36770
|
+
if (updatedCellHeight <= DEFAULT_CELL_HEIGHT) {
|
|
36771
|
+
return;
|
|
36772
|
+
}
|
|
36773
|
+
if ((!tallestCellInRow && updatedCellHeight > DEFAULT_CELL_HEIGHT) ||
|
|
36774
|
+
(tallestCellInRow && updatedCellHeight > tallestCellInRow.size)) {
|
|
36775
|
+
const newTallestCell = { cell: { sheetId, col, row }, size: updatedCellHeight };
|
|
36776
|
+
this.history.update("tallestCellInRow", sheetId, row, newTallestCell);
|
|
36777
|
+
}
|
|
36778
|
+
}
|
|
36779
|
+
initializeSheet(sheetId) {
|
|
36780
|
+
const tallestCells = [];
|
|
36781
|
+
for (let row = 0; row < this.getters.getNumberRows(sheetId); row++) {
|
|
36782
|
+
const tallestCell = this.getRowTallestCell(sheetId, row);
|
|
36783
|
+
tallestCells.push(tallestCell);
|
|
36784
|
+
}
|
|
36785
|
+
this.history.update("tallestCellInRow", sheetId, tallestCells);
|
|
36786
|
+
}
|
|
36787
|
+
/**
|
|
36788
|
+
* Return the height the cell should have in the sheet, which is either DEFAULT_CELL_HEIGHT if the cell is in a multi-row
|
|
36789
|
+
* merge, or the height of the cell computed based on its style/content.
|
|
36790
|
+
*/
|
|
36791
|
+
getCellHeight(position) {
|
|
36792
|
+
if (this.isInMultiRowMerge(position)) {
|
|
36793
|
+
return DEFAULT_CELL_HEIGHT;
|
|
36794
|
+
}
|
|
36795
|
+
const cell = this.getters.getCell(position);
|
|
36796
|
+
const colSize = this.getters.getColSize(position.sheetId, position.col);
|
|
36797
|
+
return getDefaultCellHeight(this.ctx, cell, colSize);
|
|
36798
|
+
}
|
|
36799
|
+
isInMultiRowMerge(position) {
|
|
36800
|
+
const merge = this.getters.getMerge(position);
|
|
36801
|
+
return !!merge && merge.bottom !== merge.top;
|
|
36802
|
+
}
|
|
36803
|
+
/**
|
|
36804
|
+
* Get the tallest cell of a row and its size.
|
|
36805
|
+
*/
|
|
36806
|
+
getRowTallestCell(sheetId, row) {
|
|
36807
|
+
const userRowSize = this.getters.getUserRowSize(sheetId, row);
|
|
36808
|
+
if (userRowSize !== undefined) {
|
|
36809
|
+
return undefined;
|
|
36810
|
+
}
|
|
36811
|
+
const cellIds = this.getters.getRowCells(sheetId, row);
|
|
36812
|
+
let maxHeight = 0;
|
|
36813
|
+
let tallestCell = undefined;
|
|
36814
|
+
for (let i = 0; i < cellIds.length; i++) {
|
|
36815
|
+
const cell = this.getters.getCellById(cellIds[i]);
|
|
36816
|
+
if (!cell) {
|
|
36817
|
+
continue;
|
|
36818
|
+
}
|
|
36819
|
+
const position = this.getters.getCellPosition(cell.id);
|
|
36820
|
+
const cellHeight = this.getCellHeight(position);
|
|
36821
|
+
if (cellHeight > maxHeight && cellHeight > DEFAULT_CELL_HEIGHT) {
|
|
36822
|
+
maxHeight = cellHeight;
|
|
36823
|
+
tallestCell = { cell: position, size: cellHeight };
|
|
36824
|
+
}
|
|
36825
|
+
}
|
|
36826
|
+
if (tallestCell && tallestCell.size > DEFAULT_CELL_HEIGHT) {
|
|
36827
|
+
return tallestCell;
|
|
36828
|
+
}
|
|
36829
|
+
return undefined;
|
|
36830
|
+
}
|
|
36831
|
+
}
|
|
36832
|
+
|
|
36457
36833
|
/**
|
|
36458
36834
|
* This plugin manage the autofill.
|
|
36459
36835
|
*
|
|
@@ -39887,10 +40263,8 @@
|
|
|
39887
40263
|
}
|
|
39888
40264
|
updateCellCommands.push(newCellValues);
|
|
39889
40265
|
}
|
|
39890
|
-
for (const cmd of updateCellCommands) {
|
|
39891
|
-
this.dispatch("UPDATE_CELL", cmd);
|
|
39892
|
-
}
|
|
39893
40266
|
}
|
|
40267
|
+
updateCellCommands.forEach((cmdPayload) => this.dispatch("UPDATE_CELL", cmdPayload));
|
|
39894
40268
|
}
|
|
39895
40269
|
/**
|
|
39896
40270
|
* Return the distances between main merge cells in the zone.
|
|
@@ -39991,24 +40365,28 @@
|
|
|
39991
40365
|
// Getters
|
|
39992
40366
|
// ---------------------------------------------------------------------------
|
|
39993
40367
|
getCellWidth(position) {
|
|
39994
|
-
const text = this.getCellText(position);
|
|
39995
40368
|
const style = this.getters.getCellComputedStyle(position);
|
|
39996
|
-
|
|
39997
|
-
|
|
40369
|
+
let contentWidth = 0;
|
|
40370
|
+
const content = this.getters.getEvaluatedCell(position).formattedValue;
|
|
40371
|
+
if (content) {
|
|
40372
|
+
const multiLineText = splitTextToWidth(this.ctx, content, style, undefined);
|
|
40373
|
+
contentWidth += Math.max(...multiLineText.map((line) => computeTextWidth(this.ctx, line, style)));
|
|
40374
|
+
}
|
|
39998
40375
|
const icon = this.getters.getConditionalIcon(position);
|
|
39999
40376
|
if (icon) {
|
|
40000
|
-
contentWidth += computeIconWidth(
|
|
40377
|
+
contentWidth += computeIconWidth(style);
|
|
40001
40378
|
}
|
|
40002
40379
|
const isFilterHeader = this.getters.isFilterHeader(position);
|
|
40003
40380
|
if (isFilterHeader) {
|
|
40004
40381
|
contentWidth += ICON_EDGE_LENGTH + FILTER_ICON_MARGIN;
|
|
40005
40382
|
}
|
|
40006
|
-
if (contentWidth
|
|
40007
|
-
|
|
40008
|
-
|
|
40009
|
-
|
|
40010
|
-
|
|
40011
|
-
|
|
40383
|
+
if (contentWidth === 0) {
|
|
40384
|
+
return 0;
|
|
40385
|
+
}
|
|
40386
|
+
contentWidth += 2 * PADDING_AUTORESIZE_HORIZONTAL;
|
|
40387
|
+
if (style.wrapping === "wrap") {
|
|
40388
|
+
const colWidth = this.getters.getColSize(this.getters.getActiveSheetId(), position.col);
|
|
40389
|
+
return Math.min(colWidth, contentWidth);
|
|
40012
40390
|
}
|
|
40013
40391
|
return contentWidth;
|
|
40014
40392
|
}
|
|
@@ -40031,56 +40409,7 @@
|
|
|
40031
40409
|
getCellMultiLineText(position, width) {
|
|
40032
40410
|
const style = this.getters.getCellStyle(position);
|
|
40033
40411
|
const text = this.getters.getCellText(position, this.getters.shouldShowFormulas());
|
|
40034
|
-
|
|
40035
|
-
for (const line of text.split("\n")) {
|
|
40036
|
-
const words = line.split(" ");
|
|
40037
|
-
if (!width) {
|
|
40038
|
-
brokenText.push(line);
|
|
40039
|
-
continue;
|
|
40040
|
-
}
|
|
40041
|
-
let textLine = "";
|
|
40042
|
-
let availableWidth = width;
|
|
40043
|
-
for (let word of words) {
|
|
40044
|
-
const splitWord = this.splitWordToSpecificWidth(this.ctx, word, width, style);
|
|
40045
|
-
const lastPart = splitWord.pop();
|
|
40046
|
-
const lastPartWidth = computeTextWidth(this.ctx, lastPart, style);
|
|
40047
|
-
// At this step: "splitWord" is an array composed of parts of word whose
|
|
40048
|
-
// length is at most equal to "width".
|
|
40049
|
-
// Last part contains the end of the word.
|
|
40050
|
-
// Note that: When word length is less than width, then lastPart is equal
|
|
40051
|
-
// to word and splitWord is empty
|
|
40052
|
-
if (splitWord.length) {
|
|
40053
|
-
if (textLine !== "") {
|
|
40054
|
-
brokenText.push(textLine);
|
|
40055
|
-
textLine = "";
|
|
40056
|
-
availableWidth = width;
|
|
40057
|
-
}
|
|
40058
|
-
splitWord.forEach((wordPart) => {
|
|
40059
|
-
brokenText.push(wordPart);
|
|
40060
|
-
});
|
|
40061
|
-
textLine = lastPart;
|
|
40062
|
-
availableWidth = width - lastPartWidth;
|
|
40063
|
-
}
|
|
40064
|
-
else {
|
|
40065
|
-
// here "lastPart" is equal to "word" and the "word" size is smaller than "width"
|
|
40066
|
-
const _word = textLine === "" ? lastPart : " " + lastPart;
|
|
40067
|
-
const wordWidth = computeTextWidth(this.ctx, _word, style);
|
|
40068
|
-
if (wordWidth <= availableWidth) {
|
|
40069
|
-
textLine += _word;
|
|
40070
|
-
availableWidth -= wordWidth;
|
|
40071
|
-
}
|
|
40072
|
-
else {
|
|
40073
|
-
brokenText.push(textLine);
|
|
40074
|
-
textLine = lastPart;
|
|
40075
|
-
availableWidth = width - lastPartWidth;
|
|
40076
|
-
}
|
|
40077
|
-
}
|
|
40078
|
-
}
|
|
40079
|
-
if (textLine !== "") {
|
|
40080
|
-
brokenText.push(textLine);
|
|
40081
|
-
}
|
|
40082
|
-
}
|
|
40083
|
-
return brokenText;
|
|
40412
|
+
return splitTextToWidth(this.ctx, text, style, width);
|
|
40084
40413
|
}
|
|
40085
40414
|
/**
|
|
40086
40415
|
* Returns the size, start and end coordinates of a column on an unfolded sheet
|
|
@@ -40139,26 +40468,6 @@
|
|
|
40139
40468
|
const sizes = cellsPositions.map((position) => this.getCellWidth({ sheetId, ...position }));
|
|
40140
40469
|
return Math.max(0, ...sizes);
|
|
40141
40470
|
}
|
|
40142
|
-
splitWordToSpecificWidth(ctx, word, width, style) {
|
|
40143
|
-
const wordWidth = computeTextWidth(ctx, word, style);
|
|
40144
|
-
if (wordWidth <= width) {
|
|
40145
|
-
return [word];
|
|
40146
|
-
}
|
|
40147
|
-
const splitWord = [];
|
|
40148
|
-
let wordPart = "";
|
|
40149
|
-
for (let l of word) {
|
|
40150
|
-
const wordPartWidth = computeTextWidth(ctx, wordPart + l, style);
|
|
40151
|
-
if (wordPartWidth > width) {
|
|
40152
|
-
splitWord.push(wordPart);
|
|
40153
|
-
wordPart = l;
|
|
40154
|
-
}
|
|
40155
|
-
else {
|
|
40156
|
-
wordPart += l;
|
|
40157
|
-
}
|
|
40158
|
-
}
|
|
40159
|
-
splitWord.push(wordPart);
|
|
40160
|
-
return splitWord;
|
|
40161
|
-
}
|
|
40162
40471
|
/**
|
|
40163
40472
|
* Check that any "sheetId" in the command matches an existing
|
|
40164
40473
|
* sheet.
|
|
@@ -41150,7 +41459,7 @@
|
|
|
41150
41459
|
.join("\n") || "\t");
|
|
41151
41460
|
}
|
|
41152
41461
|
getHTMLContent() {
|
|
41153
|
-
if (this.cells.length
|
|
41462
|
+
if (this.cells.length === 1 && this.cells[0].length === 1) {
|
|
41154
41463
|
return this.getters.getCellText(this.cells[0][0].position);
|
|
41155
41464
|
}
|
|
41156
41465
|
let htmlTable = '<table border="1" style="border-collapse:collapse">';
|
|
@@ -41475,7 +41784,7 @@
|
|
|
41475
41784
|
status = "invisible";
|
|
41476
41785
|
state;
|
|
41477
41786
|
lastPasteState;
|
|
41478
|
-
|
|
41787
|
+
paintFormatStatus = "inactive";
|
|
41479
41788
|
originSheetId;
|
|
41480
41789
|
// ---------------------------------------------------------------------------
|
|
41481
41790
|
// Command Handling
|
|
@@ -41490,7 +41799,7 @@
|
|
|
41490
41799
|
if (!this.state) {
|
|
41491
41800
|
return 24 /* CommandResult.EmptyClipboard */;
|
|
41492
41801
|
}
|
|
41493
|
-
const pasteOption = cmd.pasteOption || (this.
|
|
41802
|
+
const pasteOption = cmd.pasteOption || (this.paintFormatStatus !== "inactive" ? "onlyFormat" : undefined);
|
|
41494
41803
|
return this.state.isPasteAllowed(cmd.target, { pasteOption });
|
|
41495
41804
|
case "PASTE_FROM_OS_CLIPBOARD": {
|
|
41496
41805
|
const state = new ClipboardOsState(cmd.text, this.getters, this.dispatch, this.selection);
|
|
@@ -41506,6 +41815,11 @@
|
|
|
41506
41815
|
const state = this.getClipboardStateForCopyCells(cut, "CUT");
|
|
41507
41816
|
return state.isPasteAllowed(paste);
|
|
41508
41817
|
}
|
|
41818
|
+
case "ACTIVATE_PAINT_FORMAT": {
|
|
41819
|
+
if (this.paintFormatStatus !== "inactive") {
|
|
41820
|
+
return 95 /* CommandResult.AlreadyInPaintingFormatMode */;
|
|
41821
|
+
}
|
|
41822
|
+
}
|
|
41509
41823
|
}
|
|
41510
41824
|
return 0 /* CommandResult.Success */;
|
|
41511
41825
|
}
|
|
@@ -41522,11 +41836,13 @@
|
|
|
41522
41836
|
if (!this.state) {
|
|
41523
41837
|
break;
|
|
41524
41838
|
}
|
|
41525
|
-
const pasteOption = cmd.pasteOption || (this.
|
|
41526
|
-
this._isPaintingFormat = false;
|
|
41839
|
+
const pasteOption = cmd.pasteOption || (this.paintFormatStatus !== "inactive" ? "onlyFormat" : undefined);
|
|
41527
41840
|
this.state.paste(cmd.target, { pasteOption, shouldPasteCF: true, selectTarget: true });
|
|
41528
41841
|
this.lastPasteState = this.state;
|
|
41529
|
-
this.
|
|
41842
|
+
if (this.paintFormatStatus === "oneOff") {
|
|
41843
|
+
this.paintFormatStatus = "inactive";
|
|
41844
|
+
this.status = "invisible";
|
|
41845
|
+
}
|
|
41530
41846
|
break;
|
|
41531
41847
|
case "CLEAN_CLIPBOARD_HIGHLIGHT":
|
|
41532
41848
|
this.status = "invisible";
|
|
@@ -41594,8 +41910,13 @@
|
|
|
41594
41910
|
case "ACTIVATE_PAINT_FORMAT": {
|
|
41595
41911
|
const zones = this.getters.getSelectedZones();
|
|
41596
41912
|
this.state = this.getClipboardStateForCopyCells(zones, "COPY");
|
|
41597
|
-
this._isPaintingFormat = true;
|
|
41598
41913
|
this.status = "visible";
|
|
41914
|
+
if (cmd.persistent) {
|
|
41915
|
+
this.paintFormatStatus = "persistent";
|
|
41916
|
+
}
|
|
41917
|
+
else {
|
|
41918
|
+
this.paintFormatStatus = "oneOff";
|
|
41919
|
+
}
|
|
41599
41920
|
break;
|
|
41600
41921
|
}
|
|
41601
41922
|
case "DELETE_SHEET":
|
|
@@ -41607,6 +41928,11 @@
|
|
|
41607
41928
|
this.status = "invisible";
|
|
41608
41929
|
}
|
|
41609
41930
|
break;
|
|
41931
|
+
case "CANCEL_PAINT_FORMAT": {
|
|
41932
|
+
this.paintFormatStatus = "inactive";
|
|
41933
|
+
this.status = "invisible";
|
|
41934
|
+
break;
|
|
41935
|
+
}
|
|
41610
41936
|
default:
|
|
41611
41937
|
if (isCoreCommand(cmd)) {
|
|
41612
41938
|
this.status = "invisible";
|
|
@@ -41637,7 +41963,7 @@
|
|
|
41637
41963
|
return this.state ? this.state.operation === "CUT" : false;
|
|
41638
41964
|
}
|
|
41639
41965
|
isPaintingFormat() {
|
|
41640
|
-
return this.
|
|
41966
|
+
return this.paintFormatStatus !== "inactive";
|
|
41641
41967
|
}
|
|
41642
41968
|
// ---------------------------------------------------------------------------
|
|
41643
41969
|
// Private methods
|
|
@@ -44272,6 +44598,7 @@
|
|
|
44272
44598
|
.add("evaluation", EvaluationPlugin)
|
|
44273
44599
|
.add("evaluation_chart", EvaluationChartPlugin)
|
|
44274
44600
|
.add("evaluation_cf", EvaluationConditionalFormatPlugin)
|
|
44601
|
+
.add("row_size", UIRowSizePlugin)
|
|
44275
44602
|
.add("custom_colors", CustomColorsPlugin);
|
|
44276
44603
|
|
|
44277
44604
|
const clickableCellRegistry = new Registry();
|
|
@@ -44289,7 +44616,7 @@
|
|
|
44289
44616
|
async requestImage() {
|
|
44290
44617
|
const file = await this.getImageFromUser();
|
|
44291
44618
|
const path = await this.fileStore.upload(file);
|
|
44292
|
-
const size = await this.
|
|
44619
|
+
const size = await this.getImageOriginalSize(path);
|
|
44293
44620
|
return { path, size, mimetype: file.type };
|
|
44294
44621
|
}
|
|
44295
44622
|
getImageFromUser() {
|
|
@@ -44308,7 +44635,7 @@
|
|
|
44308
44635
|
input.click();
|
|
44309
44636
|
});
|
|
44310
44637
|
}
|
|
44311
|
-
|
|
44638
|
+
getImageOriginalSize(path) {
|
|
44312
44639
|
return new Promise((resolve, reject) => {
|
|
44313
44640
|
const image = new Image();
|
|
44314
44641
|
image.src = path;
|
|
@@ -45527,11 +45854,11 @@
|
|
|
45527
45854
|
}
|
|
45528
45855
|
get title() {
|
|
45529
45856
|
const name = this.actionButton.name(this.env);
|
|
45530
|
-
const description = this.actionButton.description;
|
|
45857
|
+
const description = this.actionButton.description(this.env);
|
|
45531
45858
|
return name + (description ? ` (${description})` : "");
|
|
45532
45859
|
}
|
|
45533
45860
|
get iconTitle() {
|
|
45534
|
-
return this.actionButton.icon;
|
|
45861
|
+
return this.actionButton.icon(this.env);
|
|
45535
45862
|
}
|
|
45536
45863
|
onClick(ev) {
|
|
45537
45864
|
if (this.isEnabled) {
|
|
@@ -45793,6 +46120,10 @@
|
|
|
45793
46120
|
top: 20%;
|
|
45794
46121
|
}
|
|
45795
46122
|
}
|
|
46123
|
+
|
|
46124
|
+
.user-select-text {
|
|
46125
|
+
user-select: text;
|
|
46126
|
+
}
|
|
45796
46127
|
`;
|
|
45797
46128
|
class TopBarComposer extends owl.Component {
|
|
45798
46129
|
static template = "o-spreadsheet-TopBarComposer";
|
|
@@ -45912,6 +46243,27 @@
|
|
|
45912
46243
|
class: String,
|
|
45913
46244
|
};
|
|
45914
46245
|
|
|
46246
|
+
class PaintFormatButton extends owl.Component {
|
|
46247
|
+
static template = "o-spreadsheet-PaintFormatButton";
|
|
46248
|
+
get isActive() {
|
|
46249
|
+
return this.env.model.getters.isPaintingFormat();
|
|
46250
|
+
}
|
|
46251
|
+
onDblClick() {
|
|
46252
|
+
this.env.model.dispatch("ACTIVATE_PAINT_FORMAT", { persistent: true });
|
|
46253
|
+
}
|
|
46254
|
+
togglePaintFormat() {
|
|
46255
|
+
if (this.isActive) {
|
|
46256
|
+
this.env.model.dispatch("CANCEL_PAINT_FORMAT");
|
|
46257
|
+
}
|
|
46258
|
+
else {
|
|
46259
|
+
this.env.model.dispatch("ACTIVATE_PAINT_FORMAT", { persistent: false });
|
|
46260
|
+
}
|
|
46261
|
+
}
|
|
46262
|
+
}
|
|
46263
|
+
PaintFormatButton.props = {
|
|
46264
|
+
class: { type: String, optional: true },
|
|
46265
|
+
};
|
|
46266
|
+
|
|
45915
46267
|
// -----------------------------------------------------------------------------
|
|
45916
46268
|
// TopBar
|
|
45917
46269
|
// -----------------------------------------------------------------------------
|
|
@@ -46012,6 +46364,7 @@
|
|
|
46012
46364
|
TopBarComposer,
|
|
46013
46365
|
FontSizeEditor,
|
|
46014
46366
|
ActionButton,
|
|
46367
|
+
PaintFormatButton,
|
|
46015
46368
|
BorderEditorWidget,
|
|
46016
46369
|
};
|
|
46017
46370
|
state = owl.useState({
|
|
@@ -47864,7 +48217,7 @@
|
|
|
47864
48217
|
: "nextCluster";
|
|
47865
48218
|
while (true) {
|
|
47866
48219
|
const nextCellPosition = this.getNextCellPosition(currentPosition, dim, dir);
|
|
47867
|
-
// Break if nextPosition
|
|
48220
|
+
// Break if nextPosition === currentPosition, which happens if there's no next valid position
|
|
47868
48221
|
if (currentPosition.col === nextCellPosition.col &&
|
|
47869
48222
|
currentPosition.row === nextCellPosition.row) {
|
|
47870
48223
|
break;
|
|
@@ -48831,6 +49184,8 @@
|
|
|
48831
49184
|
["name", `Image ${imageId}`],
|
|
48832
49185
|
["title", "Image"],
|
|
48833
49186
|
];
|
|
49187
|
+
const cx = convertDotValueToEMU(figure.width);
|
|
49188
|
+
const cy = convertDotValueToEMU(figure.height);
|
|
48834
49189
|
return escapeXml /*xml*/ `
|
|
48835
49190
|
<xdr:twoCellAnchor editAs="oneCell">
|
|
48836
49191
|
<xdr:from>
|
|
@@ -48857,6 +49212,9 @@
|
|
|
48857
49212
|
</a:stretch>
|
|
48858
49213
|
</xdr:blipFill>
|
|
48859
49214
|
<xdr:spPr>
|
|
49215
|
+
<a:xfrm>
|
|
49216
|
+
<a:ext cx="${cx}" cy="${cy}" />
|
|
49217
|
+
</a:xfrm>
|
|
48860
49218
|
<a:prstGeom prst="rect">
|
|
48861
49219
|
<a:avLst/>
|
|
48862
49220
|
</a:prstGeom>
|
|
@@ -49376,7 +49734,7 @@
|
|
|
49376
49734
|
const mimeType = image.data.mimetype;
|
|
49377
49735
|
if (mimeType === undefined)
|
|
49378
49736
|
continue;
|
|
49379
|
-
const extension =
|
|
49737
|
+
const extension = IMAGE_MIMETYPE_TO_EXTENSION_MAPPING[mimeType];
|
|
49380
49738
|
// only support exporting images with mimetypes specified in the mapping
|
|
49381
49739
|
if (extension === undefined)
|
|
49382
49740
|
continue;
|
|
@@ -49512,7 +49870,7 @@
|
|
|
49512
49870
|
function createContentTypes(files) {
|
|
49513
49871
|
const overrideNodes = [];
|
|
49514
49872
|
// hard-code supported image mimetypes
|
|
49515
|
-
const imageDefaultNodes = Object.entries(
|
|
49873
|
+
const imageDefaultNodes = Object.entries(IMAGE_MIMETYPE_TO_EXTENSION_MAPPING).map(([mimetype, extension]) => createDefaultXMLElement(extension, mimetype));
|
|
49516
49874
|
for (const file of files) {
|
|
49517
49875
|
if ("contentType" in file && file.contentType) {
|
|
49518
49876
|
overrideNodes.push(createOverride("/" + file.path, CONTENT_TYPES[file.contentType]));
|
|
@@ -49773,6 +50131,7 @@
|
|
|
49773
50131
|
...config,
|
|
49774
50132
|
mode: config.mode || "normal",
|
|
49775
50133
|
custom: config.custom || {},
|
|
50134
|
+
defaultCurrencyFormat: config.defaultCurrencyFormat || "[$$]#,##0.00",
|
|
49776
50135
|
external: this.setupExternalConfig(config.external || {}),
|
|
49777
50136
|
transportService,
|
|
49778
50137
|
client,
|
|
@@ -50070,6 +50429,7 @@
|
|
|
50070
50429
|
numberToLetters,
|
|
50071
50430
|
UuidGenerator,
|
|
50072
50431
|
formatValue,
|
|
50432
|
+
createCurrencyFormat,
|
|
50073
50433
|
computeTextWidth,
|
|
50074
50434
|
createEmptyWorkbookData,
|
|
50075
50435
|
createEmptySheet,
|
|
@@ -50142,6 +50502,7 @@
|
|
|
50142
50502
|
exports.invalidateCFEvaluationCommands = invalidateCFEvaluationCommands;
|
|
50143
50503
|
exports.invalidateDependenciesCommands = invalidateDependenciesCommands;
|
|
50144
50504
|
exports.invalidateEvaluationCommands = invalidateEvaluationCommands;
|
|
50505
|
+
exports.iterateAstNodes = iterateAstNodes;
|
|
50145
50506
|
exports.links = links;
|
|
50146
50507
|
exports.load = load;
|
|
50147
50508
|
exports.parse = parse;
|
|
@@ -50154,9 +50515,9 @@
|
|
|
50154
50515
|
Object.defineProperty(exports, '__esModule', { value: true });
|
|
50155
50516
|
|
|
50156
50517
|
|
|
50157
|
-
__info__.version = '16.4.0
|
|
50158
|
-
__info__.date = '2023-
|
|
50159
|
-
__info__.hash = '
|
|
50518
|
+
__info__.version = '16.4.0';
|
|
50519
|
+
__info__.date = '2023-07-24T06:26:19.494Z';
|
|
50520
|
+
__info__.hash = '4eb614c';
|
|
50160
50521
|
|
|
50161
50522
|
|
|
50162
50523
|
})(this.o_spreadsheet = this.o_spreadsheet || {}, owl);
|