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