@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
|
@@ -239,6 +239,7 @@ const DEFAULT_FONT_SIZE = 10;
|
|
|
239
239
|
const HEADER_FONT_SIZE = 11;
|
|
240
240
|
const DEFAULT_FONT = "'Roboto', arial";
|
|
241
241
|
const DEFAULT_VERTICAL_ALIGN = "bottom";
|
|
242
|
+
const DEFAULT_WRAPPING_MODE = "overflow";
|
|
242
243
|
// Borders
|
|
243
244
|
const DEFAULT_BORDER_DESC = { style: "thin", color: "#000000" };
|
|
244
245
|
const DEFAULT_FILTER_BORDER_DESC = { style: "thin", color: FILTERS_COLOR };
|
|
@@ -396,82 +397,6 @@ function getCanonicalSheetName(sheetName) {
|
|
|
396
397
|
function clip(val, min, max) {
|
|
397
398
|
return val < min ? min : val > max ? max : val;
|
|
398
399
|
}
|
|
399
|
-
function computeTextLinesHeight(textLineHeight, numberOfLines = 1) {
|
|
400
|
-
return numberOfLines * (textLineHeight + MIN_CELL_TEXT_MARGIN) - MIN_CELL_TEXT_MARGIN;
|
|
401
|
-
}
|
|
402
|
-
/**
|
|
403
|
-
* Get the default height of the cell given its style.
|
|
404
|
-
*/
|
|
405
|
-
function getDefaultCellHeight(cell) {
|
|
406
|
-
if (!cell || !cell.content) {
|
|
407
|
-
return DEFAULT_CELL_HEIGHT;
|
|
408
|
-
}
|
|
409
|
-
const fontSize = computeTextFontSizeInPixels(cell.style);
|
|
410
|
-
// the number of lines should be computed from the formula result, but it's not evaluated at this point
|
|
411
|
-
const numberOfLines = cell.isFormula ? 1 : cell.content.split(NEWLINE).length;
|
|
412
|
-
return computeTextLinesHeight(fontSize, numberOfLines) + 2 * PADDING_AUTORESIZE_VERTICAL;
|
|
413
|
-
}
|
|
414
|
-
function computeTextWidth(context, text, style) {
|
|
415
|
-
const font = computeTextFont(style);
|
|
416
|
-
if (!textWidthCache[font]) {
|
|
417
|
-
textWidthCache[font] = {};
|
|
418
|
-
}
|
|
419
|
-
if (textWidthCache[font][text] === undefined) {
|
|
420
|
-
context.save();
|
|
421
|
-
context.font = font;
|
|
422
|
-
const textWidth = context.measureText(text).width;
|
|
423
|
-
context.restore();
|
|
424
|
-
textWidthCache[font][text] = textWidth;
|
|
425
|
-
}
|
|
426
|
-
return textWidthCache[font][text];
|
|
427
|
-
}
|
|
428
|
-
const textWidthCache = {};
|
|
429
|
-
function fontSizeInPixels(fontSize) {
|
|
430
|
-
return Math.round((fontSize * 96) / 72);
|
|
431
|
-
}
|
|
432
|
-
function computeTextFont(style) {
|
|
433
|
-
const italic = style.italic ? "italic " : "";
|
|
434
|
-
const weight = style.bold ? "bold" : DEFAULT_FONT_WEIGHT;
|
|
435
|
-
const size = computeTextFontSizeInPixels(style);
|
|
436
|
-
return `${italic}${weight} ${size}px ${DEFAULT_FONT}`;
|
|
437
|
-
}
|
|
438
|
-
function computeTextFontSizeInPixels(style) {
|
|
439
|
-
const sizeInPt = style?.fontSize || DEFAULT_FONT_SIZE;
|
|
440
|
-
return fontSizeInPixels(sizeInPt);
|
|
441
|
-
}
|
|
442
|
-
/**
|
|
443
|
-
* Return the font size that makes the width of a text match the given line width.
|
|
444
|
-
* Minimum font size is 1.
|
|
445
|
-
*
|
|
446
|
-
* @param getTextWidth function that takes a fontSize as argument, and return the width of the text with this font size.
|
|
447
|
-
*/
|
|
448
|
-
function getFontSizeMatchingWidth(lineWidth, maxFontSize, getTextWidth, precision = 0.25) {
|
|
449
|
-
let minFontSize = 1;
|
|
450
|
-
if (getTextWidth(minFontSize) > lineWidth)
|
|
451
|
-
return minFontSize;
|
|
452
|
-
if (getTextWidth(maxFontSize) < lineWidth)
|
|
453
|
-
return maxFontSize;
|
|
454
|
-
// Dichotomic search
|
|
455
|
-
let fontSize = (minFontSize + maxFontSize) / 2;
|
|
456
|
-
let currentTextWidth = getTextWidth(fontSize);
|
|
457
|
-
// Use a maximum number of iterations to be safe, because measuring text isn't 100% precise
|
|
458
|
-
let iterations = 0;
|
|
459
|
-
while (Math.abs(currentTextWidth - lineWidth) > precision && iterations < 20) {
|
|
460
|
-
if (currentTextWidth >= lineWidth) {
|
|
461
|
-
maxFontSize = (minFontSize + maxFontSize) / 2;
|
|
462
|
-
}
|
|
463
|
-
else {
|
|
464
|
-
minFontSize = (minFontSize + maxFontSize) / 2;
|
|
465
|
-
}
|
|
466
|
-
fontSize = (minFontSize + maxFontSize) / 2;
|
|
467
|
-
currentTextWidth = getTextWidth(fontSize);
|
|
468
|
-
iterations++;
|
|
469
|
-
}
|
|
470
|
-
return fontSize;
|
|
471
|
-
}
|
|
472
|
-
function computeIconWidth(style) {
|
|
473
|
-
return computeTextFontSizeInPixels(style) + 2 * MIN_CF_ICON_MARGIN;
|
|
474
|
-
}
|
|
475
400
|
/**
|
|
476
401
|
* Create a range from start (included) to end (excluded).
|
|
477
402
|
* range(10, 13) => [10, 11, 12]
|
|
@@ -726,10 +651,6 @@ function removeFalsyAttributes(obj) {
|
|
|
726
651
|
Object.keys(cleanObject).forEach((key) => !cleanObject[key] && delete cleanObject[key]);
|
|
727
652
|
return cleanObject;
|
|
728
653
|
}
|
|
729
|
-
/** Transform a string to lower case. If the string is undefined, return an empty string */
|
|
730
|
-
function toLowerCase(str) {
|
|
731
|
-
return str ? str.toLowerCase() : "";
|
|
732
|
-
}
|
|
733
654
|
function transpose2dArray(matrix, callback = (val) => val) {
|
|
734
655
|
if (!matrix.length)
|
|
735
656
|
return [];
|
|
@@ -818,6 +739,14 @@ function memoize(func) {
|
|
|
818
739
|
return cache.get(args[0]);
|
|
819
740
|
},
|
|
820
741
|
}[funcName];
|
|
742
|
+
}
|
|
743
|
+
function removeIndexesFromArray(array, indexes) {
|
|
744
|
+
return array.filter((_, index) => !indexes.includes(index));
|
|
745
|
+
}
|
|
746
|
+
function insertItemsAtIndex(array, items, index) {
|
|
747
|
+
const newArray = [...array];
|
|
748
|
+
newArray.splice(index, 0, ...items);
|
|
749
|
+
return newArray;
|
|
821
750
|
}
|
|
822
751
|
|
|
823
752
|
const RBA_REGEX = /rgba?\(|\s+|\)/gi;
|
|
@@ -961,13 +890,13 @@ function rgbaToHex(rgba) {
|
|
|
961
890
|
let g = rgba.g.toString(16);
|
|
962
891
|
let b = rgba.b.toString(16);
|
|
963
892
|
let a = Math.round(rgba.a * 255).toString(16);
|
|
964
|
-
if (r.length
|
|
893
|
+
if (r.length === 1)
|
|
965
894
|
r = "0" + r;
|
|
966
|
-
if (g.length
|
|
895
|
+
if (g.length === 1)
|
|
967
896
|
g = "0" + g;
|
|
968
|
-
if (b.length
|
|
897
|
+
if (b.length === 1)
|
|
969
898
|
b = "0" + b;
|
|
970
|
-
if (a.length
|
|
899
|
+
if (a.length === 1)
|
|
971
900
|
a = "0" + a;
|
|
972
901
|
if (a === "ff")
|
|
973
902
|
a = "";
|
|
@@ -1070,13 +999,13 @@ function rgbaToHSLA(rgba) {
|
|
|
1070
999
|
let l = 0;
|
|
1071
1000
|
// Calculate hue
|
|
1072
1001
|
// No difference
|
|
1073
|
-
if (delta
|
|
1002
|
+
if (delta === 0)
|
|
1074
1003
|
h = 0;
|
|
1075
1004
|
// Red is max
|
|
1076
|
-
else if (cMax
|
|
1005
|
+
else if (cMax === r)
|
|
1077
1006
|
h = ((g - b) / delta) % 6;
|
|
1078
1007
|
// Green is max
|
|
1079
|
-
else if (cMax
|
|
1008
|
+
else if (cMax === g)
|
|
1080
1009
|
h = (b - r) / delta + 2;
|
|
1081
1010
|
// Blue is max
|
|
1082
1011
|
else
|
|
@@ -1087,7 +1016,7 @@ function rgbaToHSLA(rgba) {
|
|
|
1087
1016
|
h += 360;
|
|
1088
1017
|
l = (cMax + cMin) / 2;
|
|
1089
1018
|
// Calculate saturation
|
|
1090
|
-
s = delta
|
|
1019
|
+
s = delta === 0 ? 0 : delta / (1 - Math.abs(2 * l - 1));
|
|
1091
1020
|
// Multiply l and s by 100
|
|
1092
1021
|
s = +(s * 100).toFixed(1);
|
|
1093
1022
|
l = +(l * 100).toFixed(1);
|
|
@@ -1524,7 +1453,7 @@ function addMonthsToDate(date, months, keepEndOfMonth) {
|
|
|
1524
1453
|
}
|
|
1525
1454
|
function isLeapYear(year) {
|
|
1526
1455
|
const _year = Math.trunc(year);
|
|
1527
|
-
return (_year % 4 === 0 && _year % 100 != 0) || _year % 400
|
|
1456
|
+
return (_year % 4 === 0 && _year % 100 != 0) || _year % 400 === 0;
|
|
1528
1457
|
}
|
|
1529
1458
|
function getYearFrac(startDate, endDate, _dayCountConvention) {
|
|
1530
1459
|
if (startDate === endDate) {
|
|
@@ -2185,6 +2114,39 @@ function detectNumberFormat(content) {
|
|
|
2185
2114
|
}
|
|
2186
2115
|
return undefined;
|
|
2187
2116
|
}
|
|
2117
|
+
function createCurrencyFormat(currency) {
|
|
2118
|
+
const decimalPlaces = currency.decimalPlaces ?? 2;
|
|
2119
|
+
const position = currency.position ?? "before";
|
|
2120
|
+
const code = currency.code ?? "";
|
|
2121
|
+
const symbol = currency.symbol ?? "";
|
|
2122
|
+
const decimalRepresentation = decimalPlaces ? "." + "0".repeat(decimalPlaces) : "";
|
|
2123
|
+
const numberFormat = "#,##0" + decimalRepresentation;
|
|
2124
|
+
let textExpression = `${code} ${symbol}`.trim();
|
|
2125
|
+
if (position === "after" && code) {
|
|
2126
|
+
textExpression = " " + textExpression;
|
|
2127
|
+
}
|
|
2128
|
+
return insertTextInFormat(textExpression, position, numberFormat);
|
|
2129
|
+
}
|
|
2130
|
+
function insertTextInFormat(text, position, format) {
|
|
2131
|
+
const textExpression = `[$${text}]`;
|
|
2132
|
+
return position === "before" ? textExpression + format : format + textExpression;
|
|
2133
|
+
}
|
|
2134
|
+
function roundFormat(format) {
|
|
2135
|
+
const internalFormat = parseFormat(format);
|
|
2136
|
+
const roundedFormat = internalFormat.map((formatPart) => {
|
|
2137
|
+
if (formatPart.type === "NUMBER") {
|
|
2138
|
+
return {
|
|
2139
|
+
type: formatPart.type,
|
|
2140
|
+
format: {
|
|
2141
|
+
...formatPart.format,
|
|
2142
|
+
decimalPart: undefined,
|
|
2143
|
+
},
|
|
2144
|
+
};
|
|
2145
|
+
}
|
|
2146
|
+
return formatPart;
|
|
2147
|
+
});
|
|
2148
|
+
return convertInternalFormatToFormat(roundedFormat);
|
|
2149
|
+
}
|
|
2188
2150
|
function createLargeNumberFormat(format, magnitude, postFix, locale) {
|
|
2189
2151
|
const internalFormat = parseFormat(format || "#,##0");
|
|
2190
2152
|
const largeNumberFormat = internalFormat
|
|
@@ -3278,6 +3240,169 @@ function createDefaultRows(rowNumber) {
|
|
|
3278
3240
|
return rows;
|
|
3279
3241
|
}
|
|
3280
3242
|
|
|
3243
|
+
function computeTextLinesHeight(textLineHeight, numberOfLines = 1) {
|
|
3244
|
+
return numberOfLines * (textLineHeight + MIN_CELL_TEXT_MARGIN) - MIN_CELL_TEXT_MARGIN;
|
|
3245
|
+
}
|
|
3246
|
+
/**
|
|
3247
|
+
* Get the default height of the cell given its style.
|
|
3248
|
+
*/
|
|
3249
|
+
function getDefaultCellHeight(ctx, cell, colSize) {
|
|
3250
|
+
if (!cell || !cell.content)
|
|
3251
|
+
return DEFAULT_CELL_HEIGHT;
|
|
3252
|
+
const maxWidth = cell.style?.wrapping ? colSize - 2 * MIN_CELL_TEXT_MARGIN : undefined;
|
|
3253
|
+
const numberOfLines = cell.isFormula
|
|
3254
|
+
? 1
|
|
3255
|
+
: splitTextToWidth(ctx, cell.content, cell.style, maxWidth).length;
|
|
3256
|
+
const fontSize = computeTextFontSizeInPixels(cell.style);
|
|
3257
|
+
return computeTextLinesHeight(fontSize, numberOfLines) + 2 * PADDING_AUTORESIZE_VERTICAL;
|
|
3258
|
+
}
|
|
3259
|
+
const textWidthCache = {};
|
|
3260
|
+
function computeTextWidth(context, text, style) {
|
|
3261
|
+
const font = computeTextFont(style);
|
|
3262
|
+
if (!textWidthCache[font]) {
|
|
3263
|
+
textWidthCache[font] = {};
|
|
3264
|
+
}
|
|
3265
|
+
if (textWidthCache[font][text] === undefined) {
|
|
3266
|
+
context.save();
|
|
3267
|
+
context.font = font;
|
|
3268
|
+
const textWidth = context.measureText(text).width;
|
|
3269
|
+
context.restore();
|
|
3270
|
+
textWidthCache[font][text] = textWidth;
|
|
3271
|
+
}
|
|
3272
|
+
return textWidthCache[font][text];
|
|
3273
|
+
}
|
|
3274
|
+
function fontSizeInPixels(fontSize) {
|
|
3275
|
+
return Math.round((fontSize * 96) / 72);
|
|
3276
|
+
}
|
|
3277
|
+
function computeTextFont(style) {
|
|
3278
|
+
const italic = style.italic ? "italic " : "";
|
|
3279
|
+
const weight = style.bold ? "bold" : DEFAULT_FONT_WEIGHT;
|
|
3280
|
+
const size = computeTextFontSizeInPixels(style);
|
|
3281
|
+
return `${italic}${weight} ${size}px ${DEFAULT_FONT}`;
|
|
3282
|
+
}
|
|
3283
|
+
function computeTextFontSizeInPixels(style) {
|
|
3284
|
+
const sizeInPt = style?.fontSize || DEFAULT_FONT_SIZE;
|
|
3285
|
+
return fontSizeInPixels(sizeInPt);
|
|
3286
|
+
}
|
|
3287
|
+
function splitWordToSpecificWidth(ctx, word, width, style) {
|
|
3288
|
+
const wordWidth = computeTextWidth(ctx, word, style);
|
|
3289
|
+
if (wordWidth <= width) {
|
|
3290
|
+
return [word];
|
|
3291
|
+
}
|
|
3292
|
+
const splitWord = [];
|
|
3293
|
+
let wordPart = "";
|
|
3294
|
+
for (let l of word) {
|
|
3295
|
+
const wordPartWidth = computeTextWidth(ctx, wordPart + l, style);
|
|
3296
|
+
if (wordPartWidth > width) {
|
|
3297
|
+
splitWord.push(wordPart);
|
|
3298
|
+
wordPart = l;
|
|
3299
|
+
}
|
|
3300
|
+
else {
|
|
3301
|
+
wordPart += l;
|
|
3302
|
+
}
|
|
3303
|
+
}
|
|
3304
|
+
splitWord.push(wordPart);
|
|
3305
|
+
return splitWord;
|
|
3306
|
+
}
|
|
3307
|
+
/**
|
|
3308
|
+
* Return the given text, split in multiple lines if needed. The text will be split in multiple
|
|
3309
|
+
* line if it contains NEWLINE characters, or if it's longer than the given width.
|
|
3310
|
+
*/
|
|
3311
|
+
function splitTextToWidth(ctx, text, style, width) {
|
|
3312
|
+
if (!style)
|
|
3313
|
+
style = {};
|
|
3314
|
+
const brokenText = [];
|
|
3315
|
+
// Checking if text contains NEWLINE before split makes it very slightly slower if text contains it,
|
|
3316
|
+
// but 5-10x faster if it doesn't
|
|
3317
|
+
const lines = text.includes(NEWLINE) ? text.split(NEWLINE) : [text];
|
|
3318
|
+
for (const line of lines) {
|
|
3319
|
+
const words = line.includes(" ") ? line.split(" ") : [line];
|
|
3320
|
+
if (!width) {
|
|
3321
|
+
brokenText.push(line);
|
|
3322
|
+
continue;
|
|
3323
|
+
}
|
|
3324
|
+
let textLine = "";
|
|
3325
|
+
let availableWidth = width;
|
|
3326
|
+
for (let word of words) {
|
|
3327
|
+
const splitWord = splitWordToSpecificWidth(ctx, word, width, style);
|
|
3328
|
+
const lastPart = splitWord.pop();
|
|
3329
|
+
const lastPartWidth = computeTextWidth(ctx, lastPart, style);
|
|
3330
|
+
// At this step: "splitWord" is an array composed of parts of word whose
|
|
3331
|
+
// length is at most equal to "width".
|
|
3332
|
+
// Last part contains the end of the word.
|
|
3333
|
+
// Note that: When word length is less than width, then lastPart is equal
|
|
3334
|
+
// to word and splitWord is empty
|
|
3335
|
+
if (splitWord.length) {
|
|
3336
|
+
if (textLine !== "") {
|
|
3337
|
+
brokenText.push(textLine);
|
|
3338
|
+
textLine = "";
|
|
3339
|
+
availableWidth = width;
|
|
3340
|
+
}
|
|
3341
|
+
splitWord.forEach((wordPart) => {
|
|
3342
|
+
brokenText.push(wordPart);
|
|
3343
|
+
});
|
|
3344
|
+
textLine = lastPart;
|
|
3345
|
+
availableWidth = width - lastPartWidth;
|
|
3346
|
+
}
|
|
3347
|
+
else {
|
|
3348
|
+
// here "lastPart" is equal to "word" and the "word" size is smaller than "width"
|
|
3349
|
+
const _word = textLine === "" ? lastPart : " " + lastPart;
|
|
3350
|
+
const wordWidth = computeTextWidth(ctx, _word, style);
|
|
3351
|
+
if (wordWidth <= availableWidth) {
|
|
3352
|
+
textLine += _word;
|
|
3353
|
+
availableWidth -= wordWidth;
|
|
3354
|
+
}
|
|
3355
|
+
else {
|
|
3356
|
+
brokenText.push(textLine);
|
|
3357
|
+
textLine = lastPart;
|
|
3358
|
+
availableWidth = width - lastPartWidth;
|
|
3359
|
+
}
|
|
3360
|
+
}
|
|
3361
|
+
}
|
|
3362
|
+
if (textLine !== "") {
|
|
3363
|
+
brokenText.push(textLine);
|
|
3364
|
+
}
|
|
3365
|
+
}
|
|
3366
|
+
return brokenText;
|
|
3367
|
+
}
|
|
3368
|
+
/**
|
|
3369
|
+
* Return the font size that makes the width of a text match the given line width.
|
|
3370
|
+
* Minimum font size is 1.
|
|
3371
|
+
*
|
|
3372
|
+
* @param getTextWidth function that takes a fontSize as argument, and return the width of the text with this font size.
|
|
3373
|
+
*/
|
|
3374
|
+
function getFontSizeMatchingWidth(lineWidth, maxFontSize, getTextWidth, precision = 0.25) {
|
|
3375
|
+
let minFontSize = 1;
|
|
3376
|
+
if (getTextWidth(minFontSize) > lineWidth)
|
|
3377
|
+
return minFontSize;
|
|
3378
|
+
if (getTextWidth(maxFontSize) < lineWidth)
|
|
3379
|
+
return maxFontSize;
|
|
3380
|
+
// Dichotomic search
|
|
3381
|
+
let fontSize = (minFontSize + maxFontSize) / 2;
|
|
3382
|
+
let currentTextWidth = getTextWidth(fontSize);
|
|
3383
|
+
// Use a maximum number of iterations to be safe, because measuring text isn't 100% precise
|
|
3384
|
+
let iterations = 0;
|
|
3385
|
+
while (Math.abs(currentTextWidth - lineWidth) > precision && iterations < 20) {
|
|
3386
|
+
if (currentTextWidth >= lineWidth) {
|
|
3387
|
+
maxFontSize = (minFontSize + maxFontSize) / 2;
|
|
3388
|
+
}
|
|
3389
|
+
else {
|
|
3390
|
+
minFontSize = (minFontSize + maxFontSize) / 2;
|
|
3391
|
+
}
|
|
3392
|
+
fontSize = (minFontSize + maxFontSize) / 2;
|
|
3393
|
+
currentTextWidth = getTextWidth(fontSize);
|
|
3394
|
+
iterations++;
|
|
3395
|
+
}
|
|
3396
|
+
return fontSize;
|
|
3397
|
+
}
|
|
3398
|
+
function computeIconWidth(style) {
|
|
3399
|
+
return computeTextFontSizeInPixels(style) + 2 * MIN_CF_ICON_MARGIN;
|
|
3400
|
+
}
|
|
3401
|
+
/** Transform a string to lower case. If the string is undefined, return an empty string */
|
|
3402
|
+
function toLowerCase(str) {
|
|
3403
|
+
return str ? str.toLowerCase() : "";
|
|
3404
|
+
}
|
|
3405
|
+
|
|
3281
3406
|
/*
|
|
3282
3407
|
* https://stackoverflow.com/questions/105034/create-guid-uuid-in-javascript
|
|
3283
3408
|
* */
|
|
@@ -3300,7 +3425,7 @@ class UuidGenerator {
|
|
|
3300
3425
|
else {
|
|
3301
3426
|
// mainly for jest and other browsers that do not have the crypto functionality
|
|
3302
3427
|
return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function (c) {
|
|
3303
|
-
var r = (Math.random() * 16) | 0, v = c
|
|
3428
|
+
var r = (Math.random() * 16) | 0, v = c === "x" ? r : (r & 0x3) | 0x8;
|
|
3304
3429
|
return v.toString(16);
|
|
3305
3430
|
});
|
|
3306
3431
|
}
|
|
@@ -4128,6 +4253,7 @@ exports.CommandResult = void 0;
|
|
|
4128
4253
|
CommandResult[CommandResult["NoSplitSeparatorInSelection"] = 92] = "NoSplitSeparatorInSelection";
|
|
4129
4254
|
CommandResult[CommandResult["NoActiveSheet"] = 93] = "NoActiveSheet";
|
|
4130
4255
|
CommandResult[CommandResult["InvalidLocale"] = 94] = "InvalidLocale";
|
|
4256
|
+
CommandResult[CommandResult["AlreadyInPaintingFormatMode"] = 95] = "AlreadyInPaintingFormatMode";
|
|
4131
4257
|
})(exports.CommandResult || (exports.CommandResult = {}));
|
|
4132
4258
|
|
|
4133
4259
|
const borderStyles = ["thin", "medium", "thick", "dashed", "dotted"];
|
|
@@ -5943,8 +6069,8 @@ class Menu extends owl.Component {
|
|
|
5943
6069
|
return this.props.menuItems.some((menuItem) => !!menuItem.icon || !!menuItem.isActive);
|
|
5944
6070
|
}
|
|
5945
6071
|
getIconName(menu) {
|
|
5946
|
-
if (menu.icon) {
|
|
5947
|
-
return menu.icon;
|
|
6072
|
+
if (menu.icon(this.env)) {
|
|
6073
|
+
return menu.icon(this.env);
|
|
5948
6074
|
}
|
|
5949
6075
|
if (menu.isActive?.(this.env)) {
|
|
5950
6076
|
return "o-spreadsheet-Icon.CHECK";
|
|
@@ -6378,7 +6504,7 @@ function invertMatrix(M) {
|
|
|
6378
6504
|
for (let pivot = 0; pivot < dim; pivot++) {
|
|
6379
6505
|
let diagonalElement = C[pivot][pivot];
|
|
6380
6506
|
// if we have a 0 on the diagonal we'll need to swap with a lower row
|
|
6381
|
-
if (diagonalElement
|
|
6507
|
+
if (diagonalElement === 0) {
|
|
6382
6508
|
//look through every row below the i'th row
|
|
6383
6509
|
for (let row = pivot + 1; row < dim; row++) {
|
|
6384
6510
|
//if the ii'th row has a non-0 in the i'th col, swap it with that row
|
|
@@ -6405,7 +6531,7 @@ function invertMatrix(M) {
|
|
|
6405
6531
|
// the other rows so that there will be 0's in this column in the
|
|
6406
6532
|
// rows above and below this one
|
|
6407
6533
|
for (let row = 0; row < dim; row++) {
|
|
6408
|
-
if (row
|
|
6534
|
+
if (row === pivot) {
|
|
6409
6535
|
continue;
|
|
6410
6536
|
}
|
|
6411
6537
|
// We want to change this element to 0
|
|
@@ -12470,7 +12596,7 @@ const ISNA = {
|
|
|
12470
12596
|
return false;
|
|
12471
12597
|
}
|
|
12472
12598
|
catch (e) {
|
|
12473
|
-
return e?.errorType
|
|
12599
|
+
return e?.errorType === CellErrorType.NotAvailable;
|
|
12474
12600
|
}
|
|
12475
12601
|
},
|
|
12476
12602
|
isExported: true,
|
|
@@ -12596,6 +12722,18 @@ const AND = {
|
|
|
12596
12722
|
isExported: true,
|
|
12597
12723
|
};
|
|
12598
12724
|
// -----------------------------------------------------------------------------
|
|
12725
|
+
// FALSE
|
|
12726
|
+
// -----------------------------------------------------------------------------
|
|
12727
|
+
const FALSE = {
|
|
12728
|
+
description: _lt("Logical value `false`."),
|
|
12729
|
+
args: [],
|
|
12730
|
+
returns: ["BOOLEAN"],
|
|
12731
|
+
compute: function () {
|
|
12732
|
+
return false;
|
|
12733
|
+
},
|
|
12734
|
+
isExported: true,
|
|
12735
|
+
};
|
|
12736
|
+
// -----------------------------------------------------------------------------
|
|
12599
12737
|
// IF
|
|
12600
12738
|
// -----------------------------------------------------------------------------
|
|
12601
12739
|
const IF = {
|
|
@@ -12731,6 +12869,18 @@ const OR = {
|
|
|
12731
12869
|
isExported: true,
|
|
12732
12870
|
};
|
|
12733
12871
|
// -----------------------------------------------------------------------------
|
|
12872
|
+
// TRUE
|
|
12873
|
+
// -----------------------------------------------------------------------------
|
|
12874
|
+
const TRUE = {
|
|
12875
|
+
description: _lt("Logical value `true`."),
|
|
12876
|
+
args: [],
|
|
12877
|
+
returns: ["BOOLEAN"],
|
|
12878
|
+
compute: function () {
|
|
12879
|
+
return true;
|
|
12880
|
+
},
|
|
12881
|
+
isExported: true,
|
|
12882
|
+
};
|
|
12883
|
+
// -----------------------------------------------------------------------------
|
|
12734
12884
|
// XOR
|
|
12735
12885
|
// -----------------------------------------------------------------------------
|
|
12736
12886
|
const XOR = {
|
|
@@ -12757,12 +12907,14 @@ const XOR = {
|
|
|
12757
12907
|
var logical = /*#__PURE__*/Object.freeze({
|
|
12758
12908
|
__proto__: null,
|
|
12759
12909
|
AND: AND,
|
|
12910
|
+
FALSE: FALSE,
|
|
12760
12911
|
IF: IF,
|
|
12761
12912
|
IFERROR: IFERROR,
|
|
12762
12913
|
IFNA: IFNA,
|
|
12763
12914
|
IFS: IFS,
|
|
12764
12915
|
NOT: NOT,
|
|
12765
12916
|
OR: OR,
|
|
12917
|
+
TRUE: TRUE,
|
|
12766
12918
|
XOR: XOR
|
|
12767
12919
|
});
|
|
12768
12920
|
|
|
@@ -13949,7 +14101,6 @@ function indentCode(code) {
|
|
|
13949
14101
|
* is useful for the composer, which needs to be able to work with incomplete
|
|
13950
14102
|
* formulas.
|
|
13951
14103
|
*/
|
|
13952
|
-
const functions$3 = functionRegistry.content;
|
|
13953
14104
|
const POSTFIX_UNARY_OPERATORS = ["%"];
|
|
13954
14105
|
const OPERATORS = "+,-,*,/,:,=,<>,>=,>,<=,<,^,&".split(",").concat(POSTFIX_UNARY_OPERATORS);
|
|
13955
14106
|
function tokenize(str, locale = DEFAULT_LOCALE) {
|
|
@@ -14080,17 +14231,11 @@ function tokenizeSymbol(chars) {
|
|
|
14080
14231
|
}
|
|
14081
14232
|
if (result.length) {
|
|
14082
14233
|
const value = result;
|
|
14083
|
-
const isFunction = value.toUpperCase() in functions$3;
|
|
14084
|
-
if (isFunction) {
|
|
14085
|
-
return { type: "FUNCTION", value };
|
|
14086
|
-
}
|
|
14087
14234
|
const isReference = rangeReference.test(value);
|
|
14088
14235
|
if (isReference) {
|
|
14089
14236
|
return { type: "REFERENCE", value };
|
|
14090
14237
|
}
|
|
14091
|
-
|
|
14092
|
-
return { type: "SYMBOL", value };
|
|
14093
|
-
}
|
|
14238
|
+
return { type: "SYMBOL", value };
|
|
14094
14239
|
}
|
|
14095
14240
|
return null;
|
|
14096
14241
|
}
|
|
@@ -14190,9 +14335,6 @@ function parseOperand(tokens) {
|
|
|
14190
14335
|
return { type: "NUMBER", value: parseNumber(current.value, DEFAULT_LOCALE) };
|
|
14191
14336
|
case "STRING":
|
|
14192
14337
|
return { type: "STRING", value: removeStringQuotes(current.value) };
|
|
14193
|
-
case "FUNCTION":
|
|
14194
|
-
const args = parseFunctionArgs(tokens);
|
|
14195
|
-
return { type: "FUNCALL", value: current.value, args };
|
|
14196
14338
|
case "INVALID_REFERENCE":
|
|
14197
14339
|
throw new InvalidReferenceError();
|
|
14198
14340
|
case "REFERENCE":
|
|
@@ -14209,13 +14351,15 @@ function parseOperand(tokens) {
|
|
|
14209
14351
|
value: current.value,
|
|
14210
14352
|
};
|
|
14211
14353
|
case "SYMBOL":
|
|
14212
|
-
|
|
14213
|
-
|
|
14354
|
+
const value = current.value;
|
|
14355
|
+
const nextToken = tokens[0];
|
|
14356
|
+
if (nextToken?.type === "LEFT_PAREN" && functionRegex.test(current.value)) {
|
|
14357
|
+
const args = parseFunctionArgs(tokens);
|
|
14358
|
+
return { type: "FUNCALL", value: value, args };
|
|
14214
14359
|
}
|
|
14215
|
-
|
|
14216
|
-
|
|
14217
|
-
|
|
14218
|
-
}
|
|
14360
|
+
const upperCaseValue = value.toUpperCase();
|
|
14361
|
+
if (upperCaseValue === "TRUE" || upperCaseValue === "FALSE") {
|
|
14362
|
+
return { type: "BOOLEAN", value: upperCaseValue === "TRUE" };
|
|
14219
14363
|
}
|
|
14220
14364
|
throw new BadExpressionError(_lt("Invalid formula"));
|
|
14221
14365
|
case "LEFT_PAREN":
|
|
@@ -14317,36 +14461,60 @@ function parseTokens(tokens) {
|
|
|
14317
14461
|
* to nodes of a specific type.
|
|
14318
14462
|
* Useful if you want to convert some part of a formula.
|
|
14319
14463
|
*
|
|
14320
|
-
*
|
|
14321
|
-
* ```ts
|
|
14464
|
+
* @example
|
|
14322
14465
|
* convertAstNodes(ast, "FUNCALL", convertFormulaToExcel)
|
|
14323
14466
|
*
|
|
14324
14467
|
* function convertFormulaToExcel(ast: ASTFuncall) {
|
|
14325
14468
|
* // ...
|
|
14326
14469
|
* return modifiedAst
|
|
14327
14470
|
* }
|
|
14328
|
-
* ```
|
|
14329
14471
|
*/
|
|
14330
14472
|
function convertAstNodes(ast, type, fn) {
|
|
14331
|
-
|
|
14332
|
-
ast
|
|
14473
|
+
return mapAst(ast, (ast) => {
|
|
14474
|
+
if (ast.type === type) {
|
|
14475
|
+
return fn(ast);
|
|
14476
|
+
}
|
|
14477
|
+
return ast;
|
|
14478
|
+
});
|
|
14479
|
+
}
|
|
14480
|
+
function iterateAstNodes(ast) {
|
|
14481
|
+
return Array.from(astIterator(ast));
|
|
14482
|
+
}
|
|
14483
|
+
function* astIterator(ast) {
|
|
14484
|
+
yield ast;
|
|
14485
|
+
switch (ast.type) {
|
|
14486
|
+
case "FUNCALL":
|
|
14487
|
+
for (const arg of ast.args) {
|
|
14488
|
+
yield* astIterator(arg);
|
|
14489
|
+
}
|
|
14490
|
+
break;
|
|
14491
|
+
case "UNARY_OPERATION":
|
|
14492
|
+
yield* astIterator(ast.operand);
|
|
14493
|
+
break;
|
|
14494
|
+
case "BIN_OPERATION":
|
|
14495
|
+
yield* astIterator(ast.left);
|
|
14496
|
+
yield* astIterator(ast.right);
|
|
14497
|
+
break;
|
|
14333
14498
|
}
|
|
14499
|
+
}
|
|
14500
|
+
function mapAst(ast, fn) {
|
|
14501
|
+
ast = fn(ast);
|
|
14334
14502
|
switch (ast.type) {
|
|
14335
14503
|
case "FUNCALL":
|
|
14336
14504
|
return {
|
|
14337
14505
|
...ast,
|
|
14338
|
-
args: ast.args.map((child) =>
|
|
14506
|
+
args: ast.args.map((child) => mapAst(child, fn)),
|
|
14339
14507
|
};
|
|
14340
14508
|
case "UNARY_OPERATION":
|
|
14341
14509
|
return {
|
|
14342
14510
|
...ast,
|
|
14343
|
-
operand:
|
|
14511
|
+
operand: mapAst(ast.operand, fn),
|
|
14344
14512
|
};
|
|
14345
14513
|
case "BIN_OPERATION":
|
|
14346
14514
|
return {
|
|
14347
14515
|
...ast,
|
|
14348
|
-
right:
|
|
14349
|
-
left:
|
|
14516
|
+
right: mapAst(ast.right, fn),
|
|
14517
|
+
left: mapAst(ast.left, fn),
|
|
14350
14518
|
};
|
|
14351
14519
|
default:
|
|
14352
14520
|
return ast;
|
|
@@ -14616,6 +14784,9 @@ function compile(formula) {
|
|
|
14616
14784
|
const { args } = ast;
|
|
14617
14785
|
const functionName = ast.value.toUpperCase();
|
|
14618
14786
|
const functionDefinition = functions$2[functionName];
|
|
14787
|
+
if (!functionDefinition) {
|
|
14788
|
+
throw new UnknownFunctionError(ast.value);
|
|
14789
|
+
}
|
|
14619
14790
|
assertEnoughArgs(ast);
|
|
14620
14791
|
const compiledArgs = [];
|
|
14621
14792
|
for (let i = 0; i < args.length; i++) {
|
|
@@ -14874,7 +15045,7 @@ function mapParentFunction(tokens) {
|
|
|
14874
15045
|
functionStarted = "";
|
|
14875
15046
|
}
|
|
14876
15047
|
switch (token.type) {
|
|
14877
|
-
case "
|
|
15048
|
+
case "SYMBOL":
|
|
14878
15049
|
functionStarted = token.value;
|
|
14879
15050
|
break;
|
|
14880
15051
|
case "LEFT_PAREN":
|
|
@@ -14911,6 +15082,17 @@ function composerTokenize(formula, locale) {
|
|
|
14911
15082
|
return mapParentFunction(mapParenthesis(enrichTokens(tokens)));
|
|
14912
15083
|
}
|
|
14913
15084
|
|
|
15085
|
+
const functions$1 = functionRegistry.content;
|
|
15086
|
+
function isExportableToExcel(tokens) {
|
|
15087
|
+
try {
|
|
15088
|
+
const nonExportableFunctions = iterateAstNodes(parseTokens(tokens)).filter((ast) => ast.type === "FUNCALL" && !functions$1[ast.value.toUpperCase()]?.isExported);
|
|
15089
|
+
return nonExportableFunctions.length === 0;
|
|
15090
|
+
}
|
|
15091
|
+
catch (error) {
|
|
15092
|
+
return false;
|
|
15093
|
+
}
|
|
15094
|
+
}
|
|
15095
|
+
|
|
14914
15096
|
function isValidLocale(locale) {
|
|
14915
15097
|
if (!(locale &&
|
|
14916
15098
|
typeof locale === "object" &&
|
|
@@ -15130,6 +15312,8 @@ const uuidGenerator$2 = new UuidGenerator();
|
|
|
15130
15312
|
function createAction(item) {
|
|
15131
15313
|
const name = item.name;
|
|
15132
15314
|
const children = item.children;
|
|
15315
|
+
const description = item.description;
|
|
15316
|
+
const icon = item.icon;
|
|
15133
15317
|
return {
|
|
15134
15318
|
id: item.id || uuidGenerator$2.uuidv4(),
|
|
15135
15319
|
name: typeof name === "function" ? name : () => name,
|
|
@@ -15147,8 +15331,8 @@ function createAction(item) {
|
|
|
15147
15331
|
: () => [],
|
|
15148
15332
|
isReadonlyAllowed: item.isReadonlyAllowed || false,
|
|
15149
15333
|
separator: item.separator || false,
|
|
15150
|
-
icon:
|
|
15151
|
-
description:
|
|
15334
|
+
icon: typeof icon === "function" ? icon : () => icon || "",
|
|
15335
|
+
description: typeof description === "function" ? description : () => description || "",
|
|
15152
15336
|
textColor: item.textColor,
|
|
15153
15337
|
sequence: item.sequence || 0,
|
|
15154
15338
|
};
|
|
@@ -16614,7 +16798,7 @@ function getChartTimeOptions(labels, labelFormat, locale) {
|
|
|
16614
16798
|
function convertDateFormatForMoment(format) {
|
|
16615
16799
|
format = format.replace(/y/g, "Y");
|
|
16616
16800
|
format = format.replace(/d/g, "D");
|
|
16617
|
-
// "m" before "h"
|
|
16801
|
+
// "m" before "h" === month, "m" after "h" === minute
|
|
16618
16802
|
const indexH = format.indexOf("h");
|
|
16619
16803
|
if (indexH >= 0) {
|
|
16620
16804
|
format = format.slice(0, indexH).replace(/m/g, "M") + format.slice(indexH);
|
|
@@ -17468,6 +17652,7 @@ function getChartMenu(figureId, onFigureDeleted, env) {
|
|
|
17468
17652
|
env.model.dispatch("SELECT_FIGURE", { id: figureId });
|
|
17469
17653
|
env.openSidePanel("ChartPanel");
|
|
17470
17654
|
},
|
|
17655
|
+
icon: "o-spreadsheet-Icon.EDIT",
|
|
17471
17656
|
},
|
|
17472
17657
|
getCopyMenuItem(figureId, env),
|
|
17473
17658
|
getCutMenuItem(figureId, env),
|
|
@@ -17483,8 +17668,14 @@ function getImageMenuRegistry(figureId, onFigureDeleted, env) {
|
|
|
17483
17668
|
id: "reset_size",
|
|
17484
17669
|
name: _lt("Reset size"),
|
|
17485
17670
|
sequence: 4,
|
|
17486
|
-
execute: () => {
|
|
17487
|
-
const
|
|
17671
|
+
execute: async () => {
|
|
17672
|
+
const imagePath = env.model.getters.getImagePath(figureId);
|
|
17673
|
+
const size = env.model.getters.getImageSize(figureId) ??
|
|
17674
|
+
(await env.imageProvider?.getImageOriginalSize(imagePath));
|
|
17675
|
+
if (!env.model.getters.getImageSize(figureId)) {
|
|
17676
|
+
const image = env.model.getters.getImage(figureId);
|
|
17677
|
+
image.size = size;
|
|
17678
|
+
}
|
|
17488
17679
|
const { height, width } = getMaxFigureSize(env.model.getters, size);
|
|
17489
17680
|
env.model.dispatch("UPDATE_FIGURE", {
|
|
17490
17681
|
sheetId: env.model.getters.getActiveSheetId(),
|
|
@@ -17493,6 +17684,7 @@ function getImageMenuRegistry(figureId, onFigureDeleted, env) {
|
|
|
17493
17684
|
width,
|
|
17494
17685
|
});
|
|
17495
17686
|
},
|
|
17687
|
+
icon: "o-spreadsheet-Icon.REFRESH",
|
|
17496
17688
|
},
|
|
17497
17689
|
getDeleteMenuItem(figureId, onFigureDeleted, env),
|
|
17498
17690
|
];
|
|
@@ -17509,6 +17701,7 @@ function getCopyMenuItem(figureId, env) {
|
|
|
17509
17701
|
env.model.dispatch("COPY");
|
|
17510
17702
|
await env.clipboard.write(env.model.getters.getClipboardContent());
|
|
17511
17703
|
},
|
|
17704
|
+
icon: "o-spreadsheet-Icon.COPY",
|
|
17512
17705
|
};
|
|
17513
17706
|
}
|
|
17514
17707
|
function getCutMenuItem(figureId, env) {
|
|
@@ -17522,6 +17715,7 @@ function getCutMenuItem(figureId, env) {
|
|
|
17522
17715
|
env.model.dispatch("CUT");
|
|
17523
17716
|
await env.clipboard.write(env.model.getters.getClipboardContent());
|
|
17524
17717
|
},
|
|
17718
|
+
icon: "o-spreadsheet-Icon.CUT",
|
|
17525
17719
|
};
|
|
17526
17720
|
}
|
|
17527
17721
|
function getDeleteMenuItem(figureId, onFigureDeleted, env) {
|
|
@@ -17536,6 +17730,7 @@ function getDeleteMenuItem(figureId, onFigureDeleted, env) {
|
|
|
17536
17730
|
});
|
|
17537
17731
|
onFigureDeleted();
|
|
17538
17732
|
},
|
|
17733
|
+
icon: "o-spreadsheet-Icon.DELETE",
|
|
17539
17734
|
};
|
|
17540
17735
|
}
|
|
17541
17736
|
|
|
@@ -18603,7 +18798,7 @@ const insertFunctionMin = {
|
|
|
18603
18798
|
};
|
|
18604
18799
|
const categorieFunctionAll = {
|
|
18605
18800
|
name: _lt("All"),
|
|
18606
|
-
children: allFunctionListMenuBuilder
|
|
18801
|
+
children: [allFunctionListMenuBuilder],
|
|
18607
18802
|
};
|
|
18608
18803
|
function allFunctionListMenuBuilder() {
|
|
18609
18804
|
const fnNames = functionRegistry.getKeys();
|
|
@@ -18935,7 +19130,10 @@ const formatNumberAutomatic = {
|
|
|
18935
19130
|
};
|
|
18936
19131
|
const formatNumberNumber = {
|
|
18937
19132
|
name: _lt("Number"),
|
|
18938
|
-
description:
|
|
19133
|
+
description: (env) => formatValue(1000.12, {
|
|
19134
|
+
format: "#,##0.00",
|
|
19135
|
+
locale: env.model.getters.getLocale(),
|
|
19136
|
+
}),
|
|
18939
19137
|
execute: (env) => setFormatter(env, "#,##0.00"),
|
|
18940
19138
|
isActive: (env) => isFormatSelected(env, "#,##0.00"),
|
|
18941
19139
|
};
|
|
@@ -18946,21 +19144,34 @@ const formatPercent = {
|
|
|
18946
19144
|
};
|
|
18947
19145
|
const formatNumberPercent = {
|
|
18948
19146
|
name: _lt("Percent"),
|
|
18949
|
-
description:
|
|
19147
|
+
description: (env) => formatValue(0.1012, {
|
|
19148
|
+
format: "0.00%",
|
|
19149
|
+
locale: env.model.getters.getLocale(),
|
|
19150
|
+
}),
|
|
18950
19151
|
execute: FORMAT_PERCENT_ACTION,
|
|
18951
19152
|
isActive: (env) => isFormatSelected(env, "0.00%"),
|
|
18952
19153
|
};
|
|
18953
19154
|
const formatNumberCurrency = {
|
|
18954
19155
|
name: _lt("Currency"),
|
|
18955
|
-
description:
|
|
18956
|
-
|
|
18957
|
-
|
|
19156
|
+
description: (env) => formatValue(1000.12, {
|
|
19157
|
+
format: env.model.config.defaultCurrencyFormat,
|
|
19158
|
+
locale: env.model.getters.getLocale(),
|
|
19159
|
+
}),
|
|
19160
|
+
execute: (env) => setFormatter(env, env.model.config.defaultCurrencyFormat),
|
|
19161
|
+
isActive: (env) => isFormatSelected(env, env.model.config.defaultCurrencyFormat),
|
|
18958
19162
|
};
|
|
18959
19163
|
const formatNumberCurrencyRounded = {
|
|
18960
19164
|
name: _lt("Currency rounded"),
|
|
18961
|
-
description:
|
|
18962
|
-
|
|
18963
|
-
|
|
19165
|
+
description: (env) => formatValue(1000, {
|
|
19166
|
+
format: roundFormat(env.model.config.defaultCurrencyFormat),
|
|
19167
|
+
locale: env.model.getters.getLocale(),
|
|
19168
|
+
}),
|
|
19169
|
+
execute: (env) => setFormatter(env, roundFormat(env.model.config.defaultCurrencyFormat)),
|
|
19170
|
+
isActive: (env) => isFormatSelected(env, roundFormat(env.model.config.defaultCurrencyFormat)),
|
|
19171
|
+
isVisible: (env) => {
|
|
19172
|
+
const currencyFormat = env.model.config.defaultCurrencyFormat;
|
|
19173
|
+
return currencyFormat !== roundFormat(currencyFormat);
|
|
19174
|
+
},
|
|
18964
19175
|
};
|
|
18965
19176
|
const formatCustomCurrency = {
|
|
18966
19177
|
name: _lt("Custom currency"),
|
|
@@ -18969,19 +19180,37 @@ const formatCustomCurrency = {
|
|
|
18969
19180
|
};
|
|
18970
19181
|
const formatNumberDate = {
|
|
18971
19182
|
name: _lt("Date"),
|
|
18972
|
-
description:
|
|
19183
|
+
description: (env) => {
|
|
19184
|
+
const locale = env.model.getters.getLocale();
|
|
19185
|
+
return formatValue(parseLiteral("9/26/2023", DEFAULT_LOCALE), {
|
|
19186
|
+
format: locale.dateFormat,
|
|
19187
|
+
locale,
|
|
19188
|
+
});
|
|
19189
|
+
},
|
|
18973
19190
|
execute: (env) => setFormatter(env, env.model.getters.getLocale().dateFormat),
|
|
18974
19191
|
isActive: (env) => isFormatSelected(env, env.model.getters.getLocale().dateFormat),
|
|
18975
19192
|
};
|
|
18976
19193
|
const formatNumberTime = {
|
|
18977
19194
|
name: _lt("Time"),
|
|
18978
|
-
description:
|
|
19195
|
+
description: (env) => {
|
|
19196
|
+
const locale = env.model.getters.getLocale();
|
|
19197
|
+
return formatValue(parseLiteral("9/26/2023 10:43:00 PM", DEFAULT_LOCALE), {
|
|
19198
|
+
format: locale.timeFormat,
|
|
19199
|
+
locale,
|
|
19200
|
+
});
|
|
19201
|
+
},
|
|
18979
19202
|
execute: (env) => setFormatter(env, env.model.getters.getLocale().timeFormat),
|
|
18980
19203
|
isActive: (env) => isFormatSelected(env, env.model.getters.getLocale().timeFormat),
|
|
18981
19204
|
};
|
|
18982
19205
|
const formatNumberDateTime = {
|
|
18983
19206
|
name: _lt("Date time"),
|
|
18984
|
-
description:
|
|
19207
|
+
description: (env) => {
|
|
19208
|
+
const locale = env.model.getters.getLocale();
|
|
19209
|
+
return formatValue(parseLiteral("9/26/2023 22:43:00", DEFAULT_LOCALE), {
|
|
19210
|
+
format: locale.dateFormat + " " + locale.timeFormat,
|
|
19211
|
+
locale,
|
|
19212
|
+
});
|
|
19213
|
+
},
|
|
18985
19214
|
execute: (env) => {
|
|
18986
19215
|
const locale = env.model.getters.getLocale();
|
|
18987
19216
|
setFormatter(env, locale.dateFormat + " " + locale.timeFormat);
|
|
@@ -19053,7 +19282,7 @@ const formatAlignment = {
|
|
|
19053
19282
|
};
|
|
19054
19283
|
const formatAlignmentHorizontal = {
|
|
19055
19284
|
name: _lt("Horizontal align"),
|
|
19056
|
-
icon:
|
|
19285
|
+
icon: (env) => getHorizontalAlignmentIcon(env),
|
|
19057
19286
|
};
|
|
19058
19287
|
const formatAlignmentLeft = {
|
|
19059
19288
|
name: _lt("Left"),
|
|
@@ -19078,46 +19307,50 @@ const formatAlignmentRight = {
|
|
|
19078
19307
|
};
|
|
19079
19308
|
const formatAlignmentVertical = {
|
|
19080
19309
|
name: _lt("Vertical align"),
|
|
19081
|
-
icon:
|
|
19310
|
+
icon: (env) => getVerticalAlignmentIcon(env),
|
|
19082
19311
|
};
|
|
19083
19312
|
const formatAlignmentTop = {
|
|
19084
19313
|
name: _lt("Top"),
|
|
19085
19314
|
execute: (env) => setStyle(env, { verticalAlign: "top" }),
|
|
19086
|
-
isActive: (env) => (env
|
|
19315
|
+
isActive: (env) => getVerticalAlign(env) === "top",
|
|
19087
19316
|
icon: "o-spreadsheet-Icon.ALIGN_TOP",
|
|
19088
19317
|
};
|
|
19089
19318
|
const formatAlignmentMiddle = {
|
|
19090
19319
|
name: _lt("Middle"),
|
|
19091
19320
|
execute: (env) => setStyle(env, { verticalAlign: "middle" }),
|
|
19092
|
-
isActive: (env) => (env
|
|
19321
|
+
isActive: (env) => getVerticalAlign(env) === "middle",
|
|
19093
19322
|
icon: "o-spreadsheet-Icon.ALIGN_MIDDLE",
|
|
19094
19323
|
};
|
|
19095
19324
|
const formatAlignmentBottom = {
|
|
19096
19325
|
name: _lt("Bottom"),
|
|
19097
19326
|
execute: (env) => setStyle(env, { verticalAlign: "bottom" }),
|
|
19098
|
-
isActive: (env) => (env
|
|
19327
|
+
isActive: (env) => getVerticalAlign(env) === "bottom",
|
|
19099
19328
|
icon: "o-spreadsheet-Icon.ALIGN_BOTTOM",
|
|
19100
19329
|
};
|
|
19101
|
-
const
|
|
19330
|
+
const formatWrappingIcon = {
|
|
19102
19331
|
name: _lt("Wrapping"),
|
|
19103
19332
|
icon: "o-spreadsheet-Icon.WRAPPING_OVERFLOW",
|
|
19104
19333
|
};
|
|
19334
|
+
const formatWrapping = {
|
|
19335
|
+
name: _lt("Wrapping"),
|
|
19336
|
+
icon: (env) => getWrapModeIcon(env),
|
|
19337
|
+
};
|
|
19105
19338
|
const formatWrappingOverflow = {
|
|
19106
19339
|
name: _lt("Overflow"),
|
|
19107
19340
|
execute: (env) => setStyle(env, { wrapping: "overflow" }),
|
|
19108
|
-
isActive: (env) => (env
|
|
19341
|
+
isActive: (env) => getWrappingMode(env) === "overflow",
|
|
19109
19342
|
icon: "o-spreadsheet-Icon.WRAPPING_OVERFLOW",
|
|
19110
19343
|
};
|
|
19111
19344
|
const formatWrappingWrap = {
|
|
19112
19345
|
name: _lt("Wrap"),
|
|
19113
19346
|
execute: (env) => setStyle(env, { wrapping: "wrap" }),
|
|
19114
|
-
isActive: (env) => env
|
|
19347
|
+
isActive: (env) => getWrappingMode(env) === "wrap",
|
|
19115
19348
|
icon: "o-spreadsheet-Icon.WRAPPING_WRAP",
|
|
19116
19349
|
};
|
|
19117
19350
|
const formatWrappingClip = {
|
|
19118
19351
|
name: _lt("Clip"),
|
|
19119
19352
|
execute: (env) => setStyle(env, { wrapping: "clip" }),
|
|
19120
|
-
isActive: (env) => env
|
|
19353
|
+
isActive: (env) => getWrappingMode(env) === "clip",
|
|
19121
19354
|
icon: "o-spreadsheet-Icon.WRAPPING_CLIP",
|
|
19122
19355
|
};
|
|
19123
19356
|
const textColor = {
|
|
@@ -19133,14 +19366,6 @@ const formatCF = {
|
|
|
19133
19366
|
execute: OPEN_CF_SIDEPANEL_ACTION,
|
|
19134
19367
|
icon: "o-spreadsheet-Icon.CONDITIONAL_FORMAT",
|
|
19135
19368
|
};
|
|
19136
|
-
const paintFormat = {
|
|
19137
|
-
name: _lt("Paint Format"),
|
|
19138
|
-
execute: (env) => env.model.dispatch("ACTIVATE_PAINT_FORMAT", {
|
|
19139
|
-
target: env.model.getters.getSelectedZones(),
|
|
19140
|
-
}),
|
|
19141
|
-
icon: "o-spreadsheet-Icon.PAINT_FORMAT",
|
|
19142
|
-
isActive: (env) => env.model.getters.isPaintingFormat(),
|
|
19143
|
-
};
|
|
19144
19369
|
const clearFormat = {
|
|
19145
19370
|
name: _lt("Clear formatting"),
|
|
19146
19371
|
description: "Ctrl+<",
|
|
@@ -19180,6 +19405,53 @@ function getHorizontalAlign(env) {
|
|
|
19180
19405
|
}
|
|
19181
19406
|
const cell = env.model.getters.getActiveCell();
|
|
19182
19407
|
return cell.defaultAlign;
|
|
19408
|
+
}
|
|
19409
|
+
function getVerticalAlign(env) {
|
|
19410
|
+
const style = env.model.getters.getCurrentStyle();
|
|
19411
|
+
if (style.verticalAlign) {
|
|
19412
|
+
return style.verticalAlign;
|
|
19413
|
+
}
|
|
19414
|
+
return DEFAULT_VERTICAL_ALIGN;
|
|
19415
|
+
}
|
|
19416
|
+
function getWrappingMode(env) {
|
|
19417
|
+
const style = env.model.getters.getCurrentStyle();
|
|
19418
|
+
if (style.wrapping) {
|
|
19419
|
+
return style.wrapping;
|
|
19420
|
+
}
|
|
19421
|
+
return DEFAULT_WRAPPING_MODE;
|
|
19422
|
+
}
|
|
19423
|
+
function getHorizontalAlignmentIcon(env) {
|
|
19424
|
+
const horizontalAlign = getHorizontalAlign(env);
|
|
19425
|
+
switch (horizontalAlign) {
|
|
19426
|
+
case "right":
|
|
19427
|
+
return "o-spreadsheet-Icon.ALIGN_RIGHT";
|
|
19428
|
+
case "center":
|
|
19429
|
+
return "o-spreadsheet-Icon.ALIGN_CENTER";
|
|
19430
|
+
default:
|
|
19431
|
+
return "o-spreadsheet-Icon.ALIGN_LEFT";
|
|
19432
|
+
}
|
|
19433
|
+
}
|
|
19434
|
+
function getVerticalAlignmentIcon(env) {
|
|
19435
|
+
const verticalAlign = getVerticalAlign(env);
|
|
19436
|
+
switch (verticalAlign) {
|
|
19437
|
+
case "top":
|
|
19438
|
+
return "o-spreadsheet-Icon.ALIGN_TOP";
|
|
19439
|
+
case "middle":
|
|
19440
|
+
return "o-spreadsheet-Icon.ALIGN_MIDDLE";
|
|
19441
|
+
default:
|
|
19442
|
+
return "o-spreadsheet-Icon.ALIGN_BOTTOM";
|
|
19443
|
+
}
|
|
19444
|
+
}
|
|
19445
|
+
function getWrapModeIcon(env) {
|
|
19446
|
+
const wrapMode = getWrappingMode(env);
|
|
19447
|
+
switch (wrapMode) {
|
|
19448
|
+
case "wrap":
|
|
19449
|
+
return "o-spreadsheet-Icon.WRAPPING_WRAP";
|
|
19450
|
+
case "clip":
|
|
19451
|
+
return "o-spreadsheet-Icon.WRAPPING_CLIP";
|
|
19452
|
+
default:
|
|
19453
|
+
return "o-spreadsheet-Icon.WRAPPING_OVERFLOW";
|
|
19454
|
+
}
|
|
19183
19455
|
}
|
|
19184
19456
|
|
|
19185
19457
|
var ACTION_FORMAT = /*#__PURE__*/Object.freeze({
|
|
@@ -19211,6 +19483,7 @@ var ACTION_FORMAT = /*#__PURE__*/Object.freeze({
|
|
|
19211
19483
|
formatAlignmentTop: formatAlignmentTop,
|
|
19212
19484
|
formatAlignmentMiddle: formatAlignmentMiddle,
|
|
19213
19485
|
formatAlignmentBottom: formatAlignmentBottom,
|
|
19486
|
+
formatWrappingIcon: formatWrappingIcon,
|
|
19214
19487
|
formatWrapping: formatWrapping,
|
|
19215
19488
|
formatWrappingOverflow: formatWrappingOverflow,
|
|
19216
19489
|
formatWrappingWrap: formatWrappingWrap,
|
|
@@ -19218,7 +19491,6 @@ var ACTION_FORMAT = /*#__PURE__*/Object.freeze({
|
|
|
19218
19491
|
textColor: textColor,
|
|
19219
19492
|
fillColor: fillColor,
|
|
19220
19493
|
formatCF: formatCF,
|
|
19221
|
-
paintFormat: paintFormat,
|
|
19222
19494
|
clearFormat: clearFormat
|
|
19223
19495
|
});
|
|
19224
19496
|
|
|
@@ -19981,7 +20253,7 @@ topbarMenuRegistry
|
|
|
19981
20253
|
separator: true,
|
|
19982
20254
|
})
|
|
19983
20255
|
.addChild("format_wrapping", ["format"], {
|
|
19984
|
-
...
|
|
20256
|
+
...formatWrappingIcon,
|
|
19985
20257
|
sequence: 80,
|
|
19986
20258
|
separator: true,
|
|
19987
20259
|
})
|
|
@@ -22271,17 +22543,45 @@ class CustomCurrencyPanel extends owl.Component {
|
|
|
22271
22543
|
}
|
|
22272
22544
|
get formatProposals() {
|
|
22273
22545
|
const currency = this.availableCurrencies[this.state.selectedCurrencyIndex];
|
|
22274
|
-
const
|
|
22275
|
-
const
|
|
22276
|
-
const secondPosition = currency.position === "before" ? "after" : "before";
|
|
22546
|
+
const position = currency.position;
|
|
22547
|
+
const opposite = currency.position === "before" ? "after" : "before";
|
|
22277
22548
|
const symbol = this.state.currencySymbol.trim() ? this.state.currencySymbol : "";
|
|
22278
22549
|
const code = this.state.currencyCode.trim() ? this.state.currencyCode : "";
|
|
22279
|
-
|
|
22280
|
-
|
|
22281
|
-
|
|
22282
|
-
|
|
22283
|
-
|
|
22284
|
-
|
|
22550
|
+
const decimalPlaces = currency.decimalPlaces;
|
|
22551
|
+
if (!symbol && !code) {
|
|
22552
|
+
return [];
|
|
22553
|
+
}
|
|
22554
|
+
const simple = symbol ? createCurrencyFormat({ symbol, position, decimalPlaces }) : "";
|
|
22555
|
+
const rounded = simple ? roundFormat(simple) : "";
|
|
22556
|
+
const simpleWithCode = createCurrencyFormat({ symbol, position, decimalPlaces, code });
|
|
22557
|
+
const roundedWithCode = roundFormat(simpleWithCode);
|
|
22558
|
+
const simpleOpposite = symbol
|
|
22559
|
+
? createCurrencyFormat({ symbol, position: opposite, decimalPlaces })
|
|
22560
|
+
: "";
|
|
22561
|
+
const roundedOpposite = simpleOpposite ? roundFormat(simpleOpposite) : "";
|
|
22562
|
+
const simpleOppositeWithCode = createCurrencyFormat({
|
|
22563
|
+
symbol,
|
|
22564
|
+
position: opposite,
|
|
22565
|
+
decimalPlaces,
|
|
22566
|
+
code,
|
|
22567
|
+
});
|
|
22568
|
+
const roundedOppositeWithCode = roundFormat(simpleOppositeWithCode);
|
|
22569
|
+
const formats = new Set([
|
|
22570
|
+
rounded,
|
|
22571
|
+
simple,
|
|
22572
|
+
roundedWithCode,
|
|
22573
|
+
simpleWithCode,
|
|
22574
|
+
roundedOpposite,
|
|
22575
|
+
simpleOpposite,
|
|
22576
|
+
roundedOppositeWithCode,
|
|
22577
|
+
simpleOppositeWithCode,
|
|
22578
|
+
]);
|
|
22579
|
+
return [...formats]
|
|
22580
|
+
.filter((format) => format !== "")
|
|
22581
|
+
.map((format) => ({
|
|
22582
|
+
format,
|
|
22583
|
+
example: formatValue(1000.0, { format, locale: this.env.model.getters.getLocale() }),
|
|
22584
|
+
}));
|
|
22285
22585
|
}
|
|
22286
22586
|
get isSameFormat() {
|
|
22287
22587
|
const selectedFormat = this.formatProposals[this.state.selectedFormatIndex];
|
|
@@ -22338,41 +22638,6 @@ class CustomCurrencyPanel extends owl.Component {
|
|
|
22338
22638
|
initAvailableCurrencies() {
|
|
22339
22639
|
this.state.selectedCurrencyIndex = 0;
|
|
22340
22640
|
}
|
|
22341
|
-
initProposalBases(decimalPlaces) {
|
|
22342
|
-
const result = [{ format: "#,##0", example: "1,000" }];
|
|
22343
|
-
const decimalRepresentation = decimalPlaces ? "." + "0".repeat(decimalPlaces) : "";
|
|
22344
|
-
if (decimalRepresentation) {
|
|
22345
|
-
result.push({
|
|
22346
|
-
format: "#,##0" + decimalRepresentation,
|
|
22347
|
-
example: "1,000" + decimalRepresentation,
|
|
22348
|
-
});
|
|
22349
|
-
}
|
|
22350
|
-
return result;
|
|
22351
|
-
}
|
|
22352
|
-
createFormatProposals(proposalBases, symbol, code, position) {
|
|
22353
|
-
let formatProposals = [];
|
|
22354
|
-
// 1 - add proposal with symbol and without code
|
|
22355
|
-
if (symbol) {
|
|
22356
|
-
for (let base of proposalBases) {
|
|
22357
|
-
formatProposals.push(this.createFormatProposal(position, base.example, base.format, symbol));
|
|
22358
|
-
}
|
|
22359
|
-
}
|
|
22360
|
-
// 2 - if code exist --> add more proposal with symbol and with code
|
|
22361
|
-
if (code) {
|
|
22362
|
-
for (let base of proposalBases) {
|
|
22363
|
-
const expression = (position === "after" ? " " : "") + code + " " + symbol;
|
|
22364
|
-
formatProposals.push(this.createFormatProposal(position, base.example, base.format, expression));
|
|
22365
|
-
}
|
|
22366
|
-
}
|
|
22367
|
-
return formatProposals;
|
|
22368
|
-
}
|
|
22369
|
-
createFormatProposal(position, baseExample, formatBase, expression) {
|
|
22370
|
-
const formatExpression = "[$" + expression + "]";
|
|
22371
|
-
return {
|
|
22372
|
-
example: position === "before" ? expression + baseExample : baseExample + expression,
|
|
22373
|
-
format: position === "before" ? formatExpression + formatBase : formatBase + formatExpression,
|
|
22374
|
-
};
|
|
22375
|
-
}
|
|
22376
22641
|
getCommonFormat() {
|
|
22377
22642
|
const selectedZones = this.env.model.getters.getSelectedZones();
|
|
22378
22643
|
const sheetId = this.env.model.getters.getActiveSheetId();
|
|
@@ -23175,7 +23440,7 @@ class ContentEditableHelper {
|
|
|
23175
23440
|
selection.removeAllRanges();
|
|
23176
23441
|
selection.addRange(range);
|
|
23177
23442
|
}
|
|
23178
|
-
if (start
|
|
23443
|
+
if (start === end && start === 0) {
|
|
23179
23444
|
range.setStart(this.el, 0);
|
|
23180
23445
|
range.setEnd(this.el, 0);
|
|
23181
23446
|
}
|
|
@@ -23428,7 +23693,7 @@ class ContentEditableHelper {
|
|
|
23428
23693
|
}, 0);
|
|
23429
23694
|
}
|
|
23430
23695
|
}
|
|
23431
|
-
if (nodeToFind.nodeName === "P" && !isFirstParagraph && nodeToFind.textContent
|
|
23696
|
+
if (nodeToFind.nodeName === "P" && !isFirstParagraph && nodeToFind.textContent === "") {
|
|
23432
23697
|
usedCharacters++;
|
|
23433
23698
|
}
|
|
23434
23699
|
return usedCharacters;
|
|
@@ -23537,7 +23802,7 @@ FunctionDescriptionProvider.props = {
|
|
|
23537
23802
|
argToFocus: Number,
|
|
23538
23803
|
};
|
|
23539
23804
|
|
|
23540
|
-
const functions
|
|
23805
|
+
const functions = functionRegistry.content;
|
|
23541
23806
|
const ASSISTANT_WIDTH = 300;
|
|
23542
23807
|
const selectionIndicatorClass = "selector-flag";
|
|
23543
23808
|
const selectionIndicatorColor = "#a9a9a9";
|
|
@@ -23571,10 +23836,6 @@ css /* scss */ `
|
|
|
23571
23836
|
padding-right: 3px;
|
|
23572
23837
|
outline: none;
|
|
23573
23838
|
|
|
23574
|
-
&.unfocusable {
|
|
23575
|
-
pointer-events: none;
|
|
23576
|
-
}
|
|
23577
|
-
|
|
23578
23839
|
p {
|
|
23579
23840
|
margin-bottom: 0px;
|
|
23580
23841
|
|
|
@@ -23822,7 +24083,10 @@ class Composer extends owl.Component {
|
|
|
23822
24083
|
}
|
|
23823
24084
|
}
|
|
23824
24085
|
showAutocomplete(searchTerm) {
|
|
23825
|
-
|
|
24086
|
+
const searchTermUpperCase = searchTerm.toUpperCase();
|
|
24087
|
+
if (!this.env.model.getters.getCurrentContent().startsWith("=") ||
|
|
24088
|
+
searchTermUpperCase === "TRUE" ||
|
|
24089
|
+
searchTermUpperCase === "FALSE") {
|
|
23826
24090
|
return;
|
|
23827
24091
|
}
|
|
23828
24092
|
this.autoCompleteState.showProvider = true;
|
|
@@ -23940,7 +24204,6 @@ class Composer extends owl.Component {
|
|
|
23940
24204
|
switch (token.type) {
|
|
23941
24205
|
case "OPERATOR":
|
|
23942
24206
|
case "NUMBER":
|
|
23943
|
-
case "FUNCTION":
|
|
23944
24207
|
case "ARG_SEPARATOR":
|
|
23945
24208
|
case "STRING":
|
|
23946
24209
|
result.push({ value: token.value, color: tokenColors[token.type] || "#000" });
|
|
@@ -23950,10 +24213,14 @@ class Composer extends owl.Component {
|
|
|
23950
24213
|
result.push({ value: token.value, color: this.rangeColor(xc, sheetName) || "#000" });
|
|
23951
24214
|
break;
|
|
23952
24215
|
case "SYMBOL":
|
|
23953
|
-
|
|
23954
|
-
|
|
24216
|
+
const value = token.value;
|
|
24217
|
+
const upperCaseValue = value.toUpperCase();
|
|
24218
|
+
if (upperCaseValue === "TRUE" || upperCaseValue === "FALSE") {
|
|
23955
24219
|
result.push({ value: token.value, color: tokenColors.NUMBER });
|
|
23956
24220
|
}
|
|
24221
|
+
else if (upperCaseValue in functionRegistry.content) {
|
|
24222
|
+
result.push({ value: token.value, color: tokenColors.FUNCTION });
|
|
24223
|
+
}
|
|
23957
24224
|
else {
|
|
23958
24225
|
result.push({ value: token.value, color: "#000" });
|
|
23959
24226
|
}
|
|
@@ -24049,25 +24316,28 @@ class Composer extends owl.Component {
|
|
|
24049
24316
|
this.autoCompleteState.showProvider = false;
|
|
24050
24317
|
this.functionDescriptionState.showDescription = false;
|
|
24051
24318
|
if (content.startsWith("=")) {
|
|
24052
|
-
const
|
|
24053
|
-
if (
|
|
24054
|
-
|
|
24055
|
-
|
|
24056
|
-
|
|
24057
|
-
|
|
24058
|
-
|
|
24059
|
-
|
|
24060
|
-
|
|
24061
|
-
|
|
24062
|
-
|
|
24063
|
-
|
|
24064
|
-
|
|
24065
|
-
|
|
24066
|
-
|
|
24067
|
-
|
|
24068
|
-
|
|
24069
|
-
|
|
24070
|
-
|
|
24319
|
+
const token = this.env.model.getters.getTokenAtCursor();
|
|
24320
|
+
if (!token) {
|
|
24321
|
+
return;
|
|
24322
|
+
}
|
|
24323
|
+
if (token.type === "SYMBOL") {
|
|
24324
|
+
// initialize Autocomplete Dropdown
|
|
24325
|
+
this.showAutocomplete(token.value);
|
|
24326
|
+
return;
|
|
24327
|
+
}
|
|
24328
|
+
const tokenContext = token.functionContext;
|
|
24329
|
+
const parentFunction = tokenContext?.parent.toUpperCase();
|
|
24330
|
+
if (tokenContext &&
|
|
24331
|
+
parentFunction &&
|
|
24332
|
+
parentFunction in functions &&
|
|
24333
|
+
token.type !== "UNKNOWN") {
|
|
24334
|
+
// initialize Formula Assistant
|
|
24335
|
+
const description = functions[parentFunction];
|
|
24336
|
+
const argPosition = tokenContext.argPosition;
|
|
24337
|
+
this.functionDescriptionState.functionName = parentFunction;
|
|
24338
|
+
this.functionDescriptionState.functionDescription = description;
|
|
24339
|
+
this.functionDescriptionState.argToFocus = description.getArgToFocus(argPosition + 1) - 1;
|
|
24340
|
+
this.functionDescriptionState.showDescription = true;
|
|
24071
24341
|
}
|
|
24072
24342
|
}
|
|
24073
24343
|
}
|
|
@@ -24970,6 +25240,14 @@ function useInterval(callback, delay) {
|
|
|
24970
25240
|
};
|
|
24971
25241
|
}
|
|
24972
25242
|
|
|
25243
|
+
const CURSOR_SVG = /*xml*/ `
|
|
25244
|
+
<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>
|
|
25245
|
+
`;
|
|
25246
|
+
css /* scss */ `
|
|
25247
|
+
.o-paint-format-cursor {
|
|
25248
|
+
cursor: url("data:image/svg+xml,${encodeURIComponent(CURSOR_SVG)}"), auto;
|
|
25249
|
+
}
|
|
25250
|
+
`;
|
|
24973
25251
|
function useCellHovered(env, gridRef, callback) {
|
|
24974
25252
|
let hoveredPosition = {
|
|
24975
25253
|
col: undefined,
|
|
@@ -25093,6 +25371,12 @@ class GridOverlay extends owl.Component {
|
|
|
25093
25371
|
}
|
|
25094
25372
|
return this.gridOverlay.el;
|
|
25095
25373
|
}
|
|
25374
|
+
get style() {
|
|
25375
|
+
return this.props.gridOverlayDimensions;
|
|
25376
|
+
}
|
|
25377
|
+
get isPaintingFormat() {
|
|
25378
|
+
return this.env.model.getters.isPaintingFormat();
|
|
25379
|
+
}
|
|
25096
25380
|
onMouseDown(ev) {
|
|
25097
25381
|
if (ev.button > 0) {
|
|
25098
25382
|
// not main button, probably a context menu
|
|
@@ -26270,6 +26554,9 @@ class Grid extends owl.Component {
|
|
|
26270
26554
|
else if (this.menuState.isOpen) {
|
|
26271
26555
|
this.closeMenu();
|
|
26272
26556
|
}
|
|
26557
|
+
else if (this.env.model.getters.isPaintingFormat()) {
|
|
26558
|
+
this.env.model.dispatch("CANCEL_PAINT_FORMAT");
|
|
26559
|
+
}
|
|
26273
26560
|
else {
|
|
26274
26561
|
this.env.model.dispatch("CLEAN_CLIPBOARD_HIGHLIGHT");
|
|
26275
26562
|
}
|
|
@@ -26767,7 +27054,12 @@ const XLSX_RELATION_TYPE = {
|
|
|
26767
27054
|
};
|
|
26768
27055
|
const RELATIONSHIP_NSR = "http://schemas.openxmlformats.org/officeDocument/2006/relationships";
|
|
26769
27056
|
const HEIGHT_FACTOR = 0.75; // 100px => 75 u
|
|
26770
|
-
|
|
27057
|
+
/**
|
|
27058
|
+
* Excel says its default column width is 8.43 characters (64px)
|
|
27059
|
+
* which makes WIDTH_FACTOR = 0.1317, but it doesn't work well
|
|
27060
|
+
* 0.143 is a value from dev's experiments.
|
|
27061
|
+
*/
|
|
27062
|
+
const WIDTH_FACTOR = 0.143;
|
|
26771
27063
|
/** unit : maximum number of characters a column can hold at the standard font size. What. */
|
|
26772
27064
|
const EXCEL_DEFAULT_COL_WIDTH = 8.43;
|
|
26773
27065
|
/** unit : points */
|
|
@@ -27284,7 +27576,7 @@ const XLSX_INDEXED_COLORS = {
|
|
|
27284
27576
|
64: "000000",
|
|
27285
27577
|
65: "FFFFFF", // system background
|
|
27286
27578
|
};
|
|
27287
|
-
const
|
|
27579
|
+
const IMAGE_MIMETYPE_TO_EXTENSION_MAPPING = {
|
|
27288
27580
|
"image/avif": "avif",
|
|
27289
27581
|
"image/bmp": "bmp",
|
|
27290
27582
|
"image/gif": "gif",
|
|
@@ -27293,6 +27585,17 @@ const IMAGE_MIMETYPE_EXTENSION_MAPPING = {
|
|
|
27293
27585
|
"image/png": "png",
|
|
27294
27586
|
"image/tiff": "tiff",
|
|
27295
27587
|
"image/webp": "webp",
|
|
27588
|
+
};
|
|
27589
|
+
const IMAGE_EXTENSION_TO_MIMETYPE_MAPPING = {
|
|
27590
|
+
avif: "image/avif",
|
|
27591
|
+
bmp: "image/bmp",
|
|
27592
|
+
gif: "image/gif",
|
|
27593
|
+
ico: "image/vnd.microsoft.icon",
|
|
27594
|
+
jpeg: "image/jpeg",
|
|
27595
|
+
png: "image/png",
|
|
27596
|
+
tiff: "image/tiff",
|
|
27597
|
+
webp: "image/webp",
|
|
27598
|
+
jpg: "image/jpeg",
|
|
27296
27599
|
};
|
|
27297
27600
|
|
|
27298
27601
|
/**
|
|
@@ -27456,8 +27759,8 @@ function convertXlsxFormat(numFmtId, formats, warningManager) {
|
|
|
27456
27759
|
throw new Error("Multiple escaped blocks in format");
|
|
27457
27760
|
}
|
|
27458
27761
|
convertedFormat = convertedFormat.replace(/"(.*)"/g, "[$$$1]"); // replace '"..."' by '[$...]'
|
|
27459
|
-
convertedFormat = convertedFormat.replace(/_.{1}/g, ""); // _
|
|
27460
|
-
convertedFormat = convertedFormat.replace(/\*.{1}/g, ""); // *
|
|
27762
|
+
convertedFormat = convertedFormat.replace(/_.{1}/g, ""); // _ === ignore width of next char for align purposes. Not supported ATM
|
|
27763
|
+
convertedFormat = convertedFormat.replace(/\*.{1}/g, ""); // * === repeat next character enough to fill the line. Not supported ATM
|
|
27461
27764
|
convertedFormat = convertedFormat.replace(/\\ /g, " "); // unescape spaces
|
|
27462
27765
|
convertedFormat = convertedFormat.replace(/\\./g, (match) => match[1]); // unescape other characters
|
|
27463
27766
|
if (isXlsxDateFormat(convertedFormat)) {
|
|
@@ -28096,20 +28399,35 @@ function convertFigure(figure, id, sheetData) {
|
|
|
28096
28399
|
convertEMUToDotValue(figure.anchors[0].rowOffset);
|
|
28097
28400
|
const y2 = getRowPosition(figure.anchors[1].row, sheetData) +
|
|
28098
28401
|
convertEMUToDotValue(figure.anchors[1].rowOffset);
|
|
28099
|
-
const
|
|
28100
|
-
|
|
28101
|
-
|
|
28102
|
-
|
|
28103
|
-
|
|
28104
|
-
|
|
28105
|
-
|
|
28106
|
-
|
|
28107
|
-
|
|
28108
|
-
|
|
28109
|
-
|
|
28110
|
-
|
|
28111
|
-
|
|
28112
|
-
|
|
28402
|
+
const figureData = { id, x: x1, y: y1 };
|
|
28403
|
+
if (isChartData(figure.data)) {
|
|
28404
|
+
return {
|
|
28405
|
+
...figureData,
|
|
28406
|
+
width: x2 - x1,
|
|
28407
|
+
height: y2 - y1,
|
|
28408
|
+
tag: "chart",
|
|
28409
|
+
data: convertChartData(figure.data),
|
|
28410
|
+
};
|
|
28411
|
+
}
|
|
28412
|
+
else if (isImageData(figure.data)) {
|
|
28413
|
+
return {
|
|
28414
|
+
...figureData,
|
|
28415
|
+
width: convertEMUToDotValue(figure.data.size.cx),
|
|
28416
|
+
height: convertEMUToDotValue(figure.data.size.cy),
|
|
28417
|
+
tag: "image",
|
|
28418
|
+
data: {
|
|
28419
|
+
path: figure.data.imageSrc,
|
|
28420
|
+
mimetype: figure.data.mimetype,
|
|
28421
|
+
},
|
|
28422
|
+
};
|
|
28423
|
+
}
|
|
28424
|
+
return undefined;
|
|
28425
|
+
}
|
|
28426
|
+
function isChartData(data) {
|
|
28427
|
+
return "dataSets" in data;
|
|
28428
|
+
}
|
|
28429
|
+
function isImageData(data) {
|
|
28430
|
+
return "imageSrc" in data;
|
|
28113
28431
|
}
|
|
28114
28432
|
function convertChartData(chartData) {
|
|
28115
28433
|
const dataSetsHaveTitle = chartData.dataSets[0].label !== undefined;
|
|
@@ -28734,29 +29052,26 @@ function escapeXml(strings, ...expressions) {
|
|
|
28734
29052
|
return new XMLString(concat(str));
|
|
28735
29053
|
}
|
|
28736
29054
|
/**
|
|
28737
|
-
* Removes the namespace of all the xml tags in the string.
|
|
29055
|
+
* Removes the escaped namespace of all the xml tags in the string.
|
|
28738
29056
|
*
|
|
28739
|
-
* Eg. : "
|
|
29057
|
+
* Eg. : "NAMESPACEnsNAMESPACEtest a" => "test a"
|
|
28740
29058
|
*/
|
|
28741
|
-
function
|
|
28742
|
-
return
|
|
29059
|
+
function removeTagEscapedNamespaces(tag) {
|
|
29060
|
+
return tag.replace(/NAMESPACE.*NAMESPACE(.*)/, "$1");
|
|
28743
29061
|
}
|
|
28744
29062
|
/**
|
|
28745
|
-
*
|
|
29063
|
+
* Encase the namespaces in the element's tags with NAMESPACE string
|
|
28746
29064
|
*
|
|
28747
|
-
*
|
|
28748
|
-
*/
|
|
28749
|
-
function escapeNamespaces(query) {
|
|
28750
|
-
return query.replace(/([a-z0-9]+):(?=[a-z0-9]+)/gi, "$1\\:");
|
|
28751
|
-
}
|
|
28752
|
-
/**
|
|
28753
|
-
* Return true if the querySelector ignores the namespaces when searching for a tag in the DOM.
|
|
29065
|
+
* e.g. <x:foo> becomes <NAMESPACExNAMESPACEFoo>
|
|
28754
29066
|
*
|
|
28755
|
-
*
|
|
29067
|
+
* That's useful because namespaces aren't supported by the HTML specification, so it's arbitrary whether a HTML parser/querySelector
|
|
29068
|
+
* implementation will support namespaces in the tags or not.
|
|
28756
29069
|
*/
|
|
28757
|
-
function
|
|
28758
|
-
|
|
28759
|
-
|
|
29070
|
+
function escapeTagNamespaces(str) {
|
|
29071
|
+
return str.replaceAll(/(<\/?)([a-zA-Z0-9]+):([a-zA-Z0-9]+)/g, "$1" + "NAMESPACE" + "$2" + "NAMESPACE" + "$3");
|
|
29072
|
+
}
|
|
29073
|
+
function escapeQueryNameSpaces(query) {
|
|
29074
|
+
return query.replaceAll(/([a-zA-Z0-9]+):([a-zA-Z0-9]+)/g, "NAMESPACE" + "$1" + "NAMESPACE" + "$2");
|
|
28760
29075
|
}
|
|
28761
29076
|
|
|
28762
29077
|
class AttributeValue {
|
|
@@ -28786,14 +29101,20 @@ class XlsxBaseExtractor {
|
|
|
28786
29101
|
// The xml file we are currently parsing. We should have one Extractor class by XLSXImportFile, but
|
|
28787
29102
|
// the XLSXImportFile contains both the main .xml file, and the .rels file
|
|
28788
29103
|
currentFile = undefined;
|
|
28789
|
-
|
|
28790
|
-
|
|
29104
|
+
/**
|
|
29105
|
+
* /!\ Important : There should be no namespaces in the tags of the XML files.
|
|
29106
|
+
*
|
|
29107
|
+
* This class use native querySelector and querySelectorAll, that's used for HTML (not XML). These aren't supposed to
|
|
29108
|
+
* handled namespaces, as they are not supported by the HTML specification. Some implementations (most browsers) do
|
|
29109
|
+
* actually support namespaces, but some don't (e.g. jsdom).
|
|
29110
|
+
*
|
|
29111
|
+
* The namespace should be escaped as with NAMESPACE string (eg. <t:foo> => <NAMESPACEtNAMESPACEfoo>).
|
|
29112
|
+
*/
|
|
28791
29113
|
constructor(rootFile, xlsxStructure, warningManager) {
|
|
28792
29114
|
this.rootFile = rootFile;
|
|
28793
29115
|
this.currentFile = rootFile.file.fileName;
|
|
28794
29116
|
this.xlsxFileStructure = xlsxStructure;
|
|
28795
29117
|
this.warningManager = warningManager;
|
|
28796
|
-
this.areNamespaceIgnored = areNamespaceIgnoredByQuerySelector();
|
|
28797
29118
|
this.relationships = {};
|
|
28798
29119
|
if (rootFile.rels) {
|
|
28799
29120
|
this.extractRelationships(rootFile.rels).map((rel) => {
|
|
@@ -28816,9 +29137,13 @@ class XlsxBaseExtractor {
|
|
|
28816
29137
|
/**
|
|
28817
29138
|
* Get the list of all the XLSX files in the XLSX file structure
|
|
28818
29139
|
*/
|
|
28819
|
-
|
|
28820
|
-
const
|
|
28821
|
-
|
|
29140
|
+
getListOfXMLFiles() {
|
|
29141
|
+
const XMLFiles = Object.entries(this.xlsxFileStructure)
|
|
29142
|
+
.filter(([key]) => key !== "images")
|
|
29143
|
+
.map(([_, value]) => value)
|
|
29144
|
+
.flat()
|
|
29145
|
+
.filter(isDefined$1);
|
|
29146
|
+
return XMLFiles;
|
|
28822
29147
|
}
|
|
28823
29148
|
/**
|
|
28824
29149
|
* Return an array containing the return value of the given function applied to all the XML elements
|
|
@@ -28990,43 +29315,38 @@ class XlsxBaseExtractor {
|
|
|
28990
29315
|
return color;
|
|
28991
29316
|
}
|
|
28992
29317
|
/**
|
|
28993
|
-
* Returns the
|
|
29318
|
+
* Returns the xml file targeted by a relationship.
|
|
28994
29319
|
*/
|
|
28995
29320
|
getTargetXmlFile(relationship) {
|
|
28996
29321
|
if (!relationship)
|
|
28997
29322
|
throw new Error("Undefined target file");
|
|
28998
|
-
|
|
28999
|
-
target = target.replace("../", "");
|
|
29000
|
-
target = target.replace("./", "");
|
|
29323
|
+
const target = this.processRelationshipTargetName(relationship.target);
|
|
29001
29324
|
// Use "endsWith" because targets are relative paths, and we know the files by their absolute path.
|
|
29002
|
-
const f = this.
|
|
29325
|
+
const f = this.getListOfXMLFiles().find((f) => f.file.fileName.endsWith(target));
|
|
29003
29326
|
if (!f || !f.file)
|
|
29004
29327
|
throw new Error("Cannot find target file");
|
|
29005
29328
|
return f;
|
|
29006
29329
|
}
|
|
29007
29330
|
/**
|
|
29008
|
-
*
|
|
29009
|
-
*
|
|
29010
|
-
* Why we need to do this :
|
|
29011
|
-
* - For an XML "<t:test />"
|
|
29012
|
-
* - on Jest(jsdom) : xml.querySelector("test") == null, xml.querySelector("t\\:test") == <t:test />
|
|
29013
|
-
* - on Browser : xml.querySelector("test") == <t:test />, xml.querySelector("t\\:test") == null
|
|
29331
|
+
* Returns the image parameters targeted by a relationship.
|
|
29014
29332
|
*/
|
|
29333
|
+
getTargetImageFile(relationship) {
|
|
29334
|
+
if (!relationship)
|
|
29335
|
+
throw new Error("Undefined target file");
|
|
29336
|
+
const target = this.processRelationshipTargetName(relationship.target);
|
|
29337
|
+
// Use "endsWith" because targets are relative paths, and we know the files by their absolute path.
|
|
29338
|
+
const f = this.xlsxFileStructure.images.find((f) => f.fileName.endsWith(target));
|
|
29339
|
+
if (!f)
|
|
29340
|
+
throw new Error("Cannot find target file");
|
|
29341
|
+
return f;
|
|
29342
|
+
}
|
|
29015
29343
|
querySelector(element, query) {
|
|
29016
|
-
|
|
29017
|
-
return element.querySelector(
|
|
29344
|
+
const escapedQuery = escapeQueryNameSpaces(query);
|
|
29345
|
+
return element.querySelector(escapedQuery);
|
|
29018
29346
|
}
|
|
29019
|
-
/**
|
|
29020
|
-
* Wrapper of querySelectorAll, but we'll remove the namespaces from the query if areNamespacesIgnored is true.
|
|
29021
|
-
*
|
|
29022
|
-
* Why we need to do this :
|
|
29023
|
-
* - For an XML "<t:test />"
|
|
29024
|
-
* - on Jest(jsdom) : xml.querySelectorAll("test") == [], xml.querySelectorAll("t\\:test") == [<t:test />]
|
|
29025
|
-
* - on Browser : xml.querySelectorAll("test") == [<t:test />], xml.querySelectorAll("t\\:test") == []
|
|
29026
|
-
*/
|
|
29027
29347
|
querySelectorAll(element, query) {
|
|
29028
|
-
|
|
29029
|
-
return element.querySelectorAll(
|
|
29348
|
+
const escapedQuery = escapeQueryNameSpaces(query);
|
|
29349
|
+
return element.querySelectorAll(escapedQuery);
|
|
29030
29350
|
}
|
|
29031
29351
|
/**
|
|
29032
29352
|
* Get a color from its id in the Theme's colorScheme.
|
|
@@ -29050,6 +29370,10 @@ class XlsxBaseExtractor {
|
|
|
29050
29370
|
return clrScheme[colorId].value;
|
|
29051
29371
|
}
|
|
29052
29372
|
}
|
|
29373
|
+
/** Remove signs of relative path. */
|
|
29374
|
+
processRelationshipTargetName(targetName) {
|
|
29375
|
+
return targetName.replace(/\.+\//, "");
|
|
29376
|
+
}
|
|
29053
29377
|
}
|
|
29054
29378
|
|
|
29055
29379
|
/**
|
|
@@ -29262,7 +29586,7 @@ class XlsxChartExtractor extends XlsxBaseExtractor {
|
|
|
29262
29586
|
throw new Error("Missing plot area in the chart definition.");
|
|
29263
29587
|
}
|
|
29264
29588
|
for (let child of plotAreaElement.children) {
|
|
29265
|
-
const tag =
|
|
29589
|
+
const tag = removeTagEscapedNamespaces(child.tagName);
|
|
29266
29590
|
if (XLSX_CHART_TYPES.some((chartType) => chartType === tag)) {
|
|
29267
29591
|
return tag;
|
|
29268
29592
|
}
|
|
@@ -29274,20 +29598,21 @@ class XlsxChartExtractor extends XlsxBaseExtractor {
|
|
|
29274
29598
|
class XlsxFigureExtractor extends XlsxBaseExtractor {
|
|
29275
29599
|
extractFigures() {
|
|
29276
29600
|
return this.mapOnElements({ parent: this.rootFile.file.xml, query: "xdr:wsDr", children: true }, (figureElement) => {
|
|
29277
|
-
const anchorType =
|
|
29601
|
+
const anchorType = removeTagEscapedNamespaces(figureElement.tagName);
|
|
29278
29602
|
if (anchorType !== "twoCellAnchor") {
|
|
29279
29603
|
throw new Error("Only twoCellAnchor are supported for xlsx drawings.");
|
|
29280
29604
|
}
|
|
29281
29605
|
const chartElement = this.querySelector(figureElement, "c:chart");
|
|
29282
|
-
|
|
29283
|
-
|
|
29606
|
+
const imageElement = this.querySelector(figureElement, "a:blip");
|
|
29607
|
+
if (!chartElement && !imageElement) {
|
|
29608
|
+
throw new Error("Only chart and image figures are currently supported.");
|
|
29284
29609
|
}
|
|
29285
29610
|
return {
|
|
29286
29611
|
anchors: [
|
|
29287
29612
|
this.extractFigureAnchor("xdr:from", figureElement),
|
|
29288
29613
|
this.extractFigureAnchor("xdr:to", figureElement),
|
|
29289
29614
|
],
|
|
29290
|
-
data: this.extractChart(chartElement),
|
|
29615
|
+
data: chartElement ? this.extractChart(chartElement) : this.extractImage(figureElement),
|
|
29291
29616
|
};
|
|
29292
29617
|
});
|
|
29293
29618
|
}
|
|
@@ -29312,6 +29637,24 @@ class XlsxFigureExtractor extends XlsxBaseExtractor {
|
|
|
29312
29637
|
}
|
|
29313
29638
|
return chartDefinition;
|
|
29314
29639
|
}
|
|
29640
|
+
extractImage(figureElement) {
|
|
29641
|
+
const imageElement = this.querySelector(figureElement, "a:blip");
|
|
29642
|
+
const imageId = this.extractAttr(imageElement, "r:embed", { required: true }).asString();
|
|
29643
|
+
const image = this.getTargetImageFile(this.relationships[imageId]);
|
|
29644
|
+
if (!image) {
|
|
29645
|
+
throw new Error("Unable to extract image");
|
|
29646
|
+
}
|
|
29647
|
+
const shapePropertyElement = this.querySelector(figureElement, "a:xfrm");
|
|
29648
|
+
const extension = image.fileName.split(".").at(-1);
|
|
29649
|
+
return {
|
|
29650
|
+
imageSrc: image.imageSrc,
|
|
29651
|
+
mimetype: extension ? IMAGE_EXTENSION_TO_MIMETYPE_MAPPING[extension] : undefined,
|
|
29652
|
+
size: {
|
|
29653
|
+
cx: this.extractChildAttr(shapePropertyElement, "a:ext", "cx", { required: true }).asNum(),
|
|
29654
|
+
cy: this.extractChildAttr(shapePropertyElement, "a:ext", "cy", { required: true }).asNum(),
|
|
29655
|
+
},
|
|
29656
|
+
};
|
|
29657
|
+
}
|
|
29315
29658
|
}
|
|
29316
29659
|
|
|
29317
29660
|
/**
|
|
@@ -29881,13 +30224,22 @@ const EXCEL_IMPORT_VERSION = 12;
|
|
|
29881
30224
|
class XlsxReader {
|
|
29882
30225
|
warningManager;
|
|
29883
30226
|
xmls;
|
|
30227
|
+
images;
|
|
29884
30228
|
constructor(files) {
|
|
29885
30229
|
this.warningManager = new XLSXImportWarningManager();
|
|
29886
30230
|
this.xmls = {};
|
|
30231
|
+
this.images = [];
|
|
29887
30232
|
for (let key of Object.keys(files)) {
|
|
29888
30233
|
// Random files can be in xlsx (like a bin file for printer settings)
|
|
29889
30234
|
if (key.endsWith(".xml") || key.endsWith(".rels")) {
|
|
29890
|
-
|
|
30235
|
+
const contentString = escapeTagNamespaces(files[key]);
|
|
30236
|
+
this.xmls[key] = parseXML(new XMLString(contentString));
|
|
30237
|
+
}
|
|
30238
|
+
else if (key.includes("media/image")) {
|
|
30239
|
+
this.images.push({
|
|
30240
|
+
fileName: key,
|
|
30241
|
+
imageSrc: files[key]["imageSrc"],
|
|
30242
|
+
});
|
|
29891
30243
|
}
|
|
29892
30244
|
}
|
|
29893
30245
|
}
|
|
@@ -29941,6 +30293,7 @@ class XlsxReader {
|
|
|
29941
30293
|
tables: getXLSXFilesOfType(CONTENT_TYPES.table, this.xmls),
|
|
29942
30294
|
pivots: getXLSXFilesOfType(CONTENT_TYPES.pivot, this.xmls),
|
|
29943
30295
|
externalLinks: getXLSXFilesOfType(CONTENT_TYPES.externalLink, this.xmls),
|
|
30296
|
+
images: this.images,
|
|
29944
30297
|
};
|
|
29945
30298
|
if (!xlsxFileStructure.workbook.rels) {
|
|
29946
30299
|
throw Error(_lt("Cannot find workbook relations file"));
|
|
@@ -30947,7 +31300,7 @@ class BordersPlugin extends CorePlugin {
|
|
|
30947
31300
|
}
|
|
30948
31301
|
/**
|
|
30949
31302
|
* Set the borders of a cell.
|
|
30950
|
-
* It overrides the current border if override
|
|
31303
|
+
* It overrides the current border if override === true.
|
|
30951
31304
|
*/
|
|
30952
31305
|
setBorder(sheetId, col, row, border, override = true) {
|
|
30953
31306
|
if (override || !this.borders?.[sheetId]?.[col]?.[row]?.vertical) {
|
|
@@ -32217,7 +32570,9 @@ class FigurePlugin extends CorePlugin {
|
|
|
32217
32570
|
const numHeader = this.getters.getNumberRows(sheetId);
|
|
32218
32571
|
let gridHeight = 0;
|
|
32219
32572
|
for (let i = 0; i < numHeader; i++) {
|
|
32220
|
-
|
|
32573
|
+
// TODO : since the row size is an UI value now, this doesn't work anymore. Using the default cell height is
|
|
32574
|
+
// a temporary solution at best, but is broken.
|
|
32575
|
+
gridHeight += this.getters.getUserRowSize(sheetId, i) || DEFAULT_CELL_HEIGHT;
|
|
32221
32576
|
}
|
|
32222
32577
|
const figures = this.getters.getFigures(sheetId);
|
|
32223
32578
|
for (const figure of figures) {
|
|
@@ -32643,33 +32998,15 @@ class FiltersPlugin extends CorePlugin {
|
|
|
32643
32998
|
}
|
|
32644
32999
|
|
|
32645
33000
|
class HeaderSizePlugin extends CorePlugin {
|
|
32646
|
-
static getters = ["
|
|
33001
|
+
static getters = ["getUserRowSize", "getColSize"];
|
|
32647
33002
|
sizes = {};
|
|
32648
33003
|
handle(cmd) {
|
|
32649
33004
|
switch (cmd.type) {
|
|
32650
33005
|
case "CREATE_SHEET": {
|
|
32651
|
-
|
|
32652
|
-
const sizes = {
|
|
32653
|
-
COL: computedSizes.COL.map((size) => ({
|
|
32654
|
-
manualSize: undefined,
|
|
32655
|
-
computedSize: lazy(size),
|
|
32656
|
-
})),
|
|
32657
|
-
ROW: computedSizes.ROW.map((size) => ({
|
|
32658
|
-
manualSize: undefined,
|
|
32659
|
-
computedSize: lazy(size),
|
|
32660
|
-
})),
|
|
32661
|
-
};
|
|
32662
|
-
this.history.update("sizes", cmd.sheetId, sizes);
|
|
33006
|
+
this.history.update("sizes", cmd.sheetId, { COL: [], ROW: [] });
|
|
32663
33007
|
break;
|
|
32664
33008
|
}
|
|
32665
33009
|
case "DUPLICATE_SHEET":
|
|
32666
|
-
// make sure the values are computed in case the original sheet is deleted
|
|
32667
|
-
for (const row of this.sizes[cmd.sheetId].ROW) {
|
|
32668
|
-
row.computedSize();
|
|
32669
|
-
}
|
|
32670
|
-
for (const col of this.sizes[cmd.sheetId].COL) {
|
|
32671
|
-
col.computedSize();
|
|
32672
|
-
}
|
|
32673
33010
|
this.history.update("sizes", cmd.sheetIdTo, deepCopy(this.sizes[cmd.sheetId]));
|
|
32674
33011
|
break;
|
|
32675
33012
|
case "DELETE_SHEET":
|
|
@@ -32678,21 +33015,8 @@ class HeaderSizePlugin extends CorePlugin {
|
|
|
32678
33015
|
this.history.update("sizes", sizes);
|
|
32679
33016
|
break;
|
|
32680
33017
|
case "REMOVE_COLUMNS_ROWS": {
|
|
32681
|
-
|
|
32682
|
-
|
|
32683
|
-
sizes.splice(headerIndex, 1);
|
|
32684
|
-
}
|
|
32685
|
-
const min = Math.min(...cmd.elements);
|
|
32686
|
-
sizes = sizes.map((size, row) => {
|
|
32687
|
-
if (cmd.dimension === "ROW" && row >= min) {
|
|
32688
|
-
// invalidate sizes
|
|
32689
|
-
return {
|
|
32690
|
-
manualSize: size.manualSize,
|
|
32691
|
-
computedSize: lazy(() => this.getRowTallestCellSize(cmd.sheetId, row)),
|
|
32692
|
-
};
|
|
32693
|
-
}
|
|
32694
|
-
return size;
|
|
32695
|
-
});
|
|
33018
|
+
const arr = this.sizes[cmd.sheetId][cmd.dimension];
|
|
33019
|
+
const sizes = removeIndexesFromArray(arr, cmd.elements);
|
|
32696
33020
|
this.history.update("sizes", cmd.sheetId, cmd.dimension, sizes);
|
|
32697
33021
|
break;
|
|
32698
33022
|
}
|
|
@@ -32701,51 +33025,18 @@ class HeaderSizePlugin extends CorePlugin {
|
|
|
32701
33025
|
const addIndex = getAddHeaderStartIndex(cmd.position, cmd.base);
|
|
32702
33026
|
const baseSize = sizes[cmd.base];
|
|
32703
33027
|
sizes.splice(addIndex, 0, ...Array(cmd.quantity).fill(baseSize));
|
|
32704
|
-
sizes = sizes.map((size, row) => {
|
|
32705
|
-
if (cmd.dimension === "ROW" && row > cmd.base + cmd.quantity) {
|
|
32706
|
-
// invalidate sizes
|
|
32707
|
-
return {
|
|
32708
|
-
manualSize: size.manualSize,
|
|
32709
|
-
computedSize: lazy(() => this.getRowTallestCellSize(cmd.sheetId, row)),
|
|
32710
|
-
};
|
|
32711
|
-
}
|
|
32712
|
-
return size;
|
|
32713
|
-
});
|
|
32714
33028
|
this.history.update("sizes", cmd.sheetId, cmd.dimension, sizes);
|
|
32715
33029
|
break;
|
|
32716
33030
|
}
|
|
32717
33031
|
case "RESIZE_COLUMNS_ROWS":
|
|
32718
|
-
|
|
32719
|
-
|
|
32720
|
-
|
|
32721
|
-
const size = height;
|
|
32722
|
-
this.history.update("sizes", cmd.sheetId, cmd.dimension, el, {
|
|
32723
|
-
manualSize: cmd.size || undefined,
|
|
32724
|
-
computedSize: lazy(size),
|
|
32725
|
-
});
|
|
33032
|
+
if (cmd.dimension === "ROW") {
|
|
33033
|
+
for (const el of cmd.elements) {
|
|
33034
|
+
this.history.update("sizes", cmd.sheetId, cmd.dimension, el, cmd.size || undefined);
|
|
32726
33035
|
}
|
|
32727
|
-
else {
|
|
32728
|
-
this.history.update("sizes", cmd.sheetId, cmd.dimension, el, {
|
|
32729
|
-
manualSize: cmd.size || undefined,
|
|
32730
|
-
computedSize: lazy(cmd.size || DEFAULT_CELL_WIDTH),
|
|
32731
|
-
});
|
|
32732
|
-
}
|
|
32733
|
-
}
|
|
32734
|
-
break;
|
|
32735
|
-
case "UPDATE_CELL":
|
|
32736
|
-
if (!this.sizes[cmd.sheetId]?.["ROW"]?.[cmd.row]?.manualSize) {
|
|
32737
|
-
const { sheetId, row } = cmd;
|
|
32738
|
-
this.history.update("sizes", sheetId, "ROW", row, "computedSize", lazy(() => this.getRowTallestCellSize(sheetId, row)));
|
|
32739
33036
|
}
|
|
32740
|
-
|
|
32741
|
-
|
|
32742
|
-
|
|
32743
|
-
for (let target of cmd.target) {
|
|
32744
|
-
for (let row of range(target.top, target.bottom + 1)) {
|
|
32745
|
-
const rowHeight = this.getRowTallestCellSize(cmd.sheetId, row);
|
|
32746
|
-
if (rowHeight !== this.getRowSize(cmd.sheetId, row)) {
|
|
32747
|
-
this.history.update("sizes", cmd.sheetId, "ROW", row, "computedSize", lazy(rowHeight));
|
|
32748
|
-
}
|
|
33037
|
+
else {
|
|
33038
|
+
for (const el of cmd.elements) {
|
|
33039
|
+
this.history.update("sizes", cmd.sheetId, cmd.dimension, el, cmd.size || undefined);
|
|
32749
33040
|
}
|
|
32750
33041
|
}
|
|
32751
33042
|
break;
|
|
@@ -32753,94 +33044,29 @@ class HeaderSizePlugin extends CorePlugin {
|
|
|
32753
33044
|
return;
|
|
32754
33045
|
}
|
|
32755
33046
|
getColSize(sheetId, index) {
|
|
32756
|
-
return this.
|
|
32757
|
-
}
|
|
32758
|
-
getRowSize(sheetId, index) {
|
|
32759
|
-
return this.getHeaderSize(sheetId, "ROW", index);
|
|
33047
|
+
return Math.round(this.sizes[sheetId]?.["COL"][index] || DEFAULT_CELL_WIDTH);
|
|
32760
33048
|
}
|
|
32761
|
-
|
|
32762
|
-
|
|
32763
|
-
|
|
32764
|
-
this.getDefaultHeaderSize(dimension));
|
|
32765
|
-
}
|
|
32766
|
-
computeSheetSizes(sheetId) {
|
|
32767
|
-
const sizes = { COL: [], ROW: [] };
|
|
32768
|
-
for (let col of range(0, this.getters.getNumberCols(sheetId))) {
|
|
32769
|
-
sizes.COL.push(this.getHeaderSize(sheetId, "COL", col));
|
|
32770
|
-
}
|
|
32771
|
-
for (let row of range(0, this.getters.getNumberRows(sheetId))) {
|
|
32772
|
-
let rowSize = this.sizes[sheetId]?.["ROW"]?.[row].manualSize;
|
|
32773
|
-
if (!rowSize) {
|
|
32774
|
-
const height = this.getRowTallestCellSize(sheetId, row);
|
|
32775
|
-
rowSize = height;
|
|
32776
|
-
}
|
|
32777
|
-
sizes.ROW.push(rowSize);
|
|
32778
|
-
}
|
|
32779
|
-
return sizes;
|
|
32780
|
-
}
|
|
32781
|
-
getDefaultHeaderSize(dimension) {
|
|
32782
|
-
return dimension === "COL" ? DEFAULT_CELL_WIDTH : DEFAULT_CELL_HEIGHT;
|
|
32783
|
-
}
|
|
32784
|
-
/**
|
|
32785
|
-
* Return the height the cell should have in the sheet, which is either DEFAULT_CELL_HEIGHT if the cell is in a multi-row
|
|
32786
|
-
* merge, or the height of the cell computed based on its font size.
|
|
32787
|
-
*/
|
|
32788
|
-
getCellHeight(position) {
|
|
32789
|
-
const merge = this.getters.getMerge(position);
|
|
32790
|
-
if (merge && merge.bottom !== merge.top) {
|
|
32791
|
-
return DEFAULT_CELL_HEIGHT;
|
|
32792
|
-
}
|
|
32793
|
-
const cell = this.getters.getCell(position);
|
|
32794
|
-
return getDefaultCellHeight(cell);
|
|
32795
|
-
}
|
|
32796
|
-
/**
|
|
32797
|
-
* Get the tallest cell of a row and its size.
|
|
32798
|
-
*
|
|
32799
|
-
* The tallest cell of the row correspond to the cell with the biggest font size,
|
|
32800
|
-
* and that is not part of a multi-line merge.
|
|
32801
|
-
*/
|
|
32802
|
-
getRowTallestCellSize(sheetId, row) {
|
|
32803
|
-
const cellIds = this.getters.getRowCells(sheetId, row);
|
|
32804
|
-
let maxHeight = 0;
|
|
32805
|
-
for (let i = 0; i < cellIds.length; i++) {
|
|
32806
|
-
const cell = this.getters.getCellById(cellIds[i]);
|
|
32807
|
-
if (!cell)
|
|
32808
|
-
continue;
|
|
32809
|
-
const position = this.getters.getCellPosition(cell.id);
|
|
32810
|
-
const cellHeight = this.getCellHeight(position);
|
|
32811
|
-
if (cellHeight > maxHeight && cellHeight > DEFAULT_CELL_HEIGHT) {
|
|
32812
|
-
maxHeight = cellHeight;
|
|
32813
|
-
}
|
|
32814
|
-
}
|
|
32815
|
-
if (maxHeight <= DEFAULT_CELL_HEIGHT) {
|
|
32816
|
-
return DEFAULT_CELL_HEIGHT;
|
|
32817
|
-
}
|
|
32818
|
-
return maxHeight;
|
|
33049
|
+
getUserRowSize(sheetId, index) {
|
|
33050
|
+
const rowSize = this.sizes[sheetId]?.["ROW"][index];
|
|
33051
|
+
return rowSize ? Math.round(rowSize) : undefined;
|
|
32819
33052
|
}
|
|
32820
33053
|
import(data) {
|
|
32821
33054
|
for (let sheet of data.sheets) {
|
|
32822
|
-
const
|
|
33055
|
+
const sizes = {
|
|
33056
|
+
COL: Array(sheet.colNumber).fill(undefined),
|
|
33057
|
+
ROW: Array(sheet.rowNumber).fill(undefined),
|
|
33058
|
+
};
|
|
32823
33059
|
for (let [rowIndex, row] of Object.entries(sheet.rows)) {
|
|
32824
33060
|
if (row.size) {
|
|
32825
|
-
|
|
33061
|
+
sizes["ROW"][rowIndex] = row.size;
|
|
32826
33062
|
}
|
|
32827
33063
|
}
|
|
32828
33064
|
for (let [colIndex, col] of Object.entries(sheet.cols)) {
|
|
32829
33065
|
if (col.size) {
|
|
32830
|
-
|
|
32831
|
-
}
|
|
32832
|
-
}
|
|
32833
|
-
|
|
32834
|
-
this.sizes[sheet.id] = {
|
|
32835
|
-
COL: computedSizes.COL.map((size, i) => ({
|
|
32836
|
-
manualSize: manualSizes.COL[i],
|
|
32837
|
-
computedSize: lazy(size),
|
|
32838
|
-
})),
|
|
32839
|
-
ROW: computedSizes.ROW.map((size, i) => ({
|
|
32840
|
-
manualSize: manualSizes.ROW[i],
|
|
32841
|
-
computedSize: lazy(size),
|
|
32842
|
-
})),
|
|
32843
|
-
};
|
|
33066
|
+
sizes["COL"][colIndex] = col.size;
|
|
33067
|
+
}
|
|
33068
|
+
}
|
|
33069
|
+
this.sizes[sheet.id] = sizes;
|
|
32844
33070
|
}
|
|
32845
33071
|
return;
|
|
32846
33072
|
}
|
|
@@ -32861,9 +33087,12 @@ class HeaderSizePlugin extends CorePlugin {
|
|
|
32861
33087
|
if (sheet.rows === undefined) {
|
|
32862
33088
|
sheet.rows = {};
|
|
32863
33089
|
}
|
|
32864
|
-
for (
|
|
32865
|
-
if (exportDefaults || this.sizes[sheet.id]["ROW"][row]
|
|
32866
|
-
sheet.rows[row] = {
|
|
33090
|
+
for (const row of range(0, this.getters.getNumberRows(sheet.id))) {
|
|
33091
|
+
if (exportDefaults || this.sizes[sheet.id]["ROW"][row]) {
|
|
33092
|
+
sheet.rows[row] = {
|
|
33093
|
+
...sheet.rows[row],
|
|
33094
|
+
size: this.getUserRowSize(sheet.id, row) ?? DEFAULT_CELL_HEIGHT,
|
|
33095
|
+
};
|
|
32867
33096
|
}
|
|
32868
33097
|
}
|
|
32869
33098
|
// Export col sizes
|
|
@@ -32871,7 +33100,7 @@ class HeaderSizePlugin extends CorePlugin {
|
|
|
32871
33100
|
sheet.cols = {};
|
|
32872
33101
|
}
|
|
32873
33102
|
for (let col of range(0, this.getters.getNumberCols(sheet.id))) {
|
|
32874
|
-
if (exportDefaults || this.sizes[sheet.id]["COL"][col]
|
|
33103
|
+
if (exportDefaults || this.sizes[sheet.id]["COL"][col]) {
|
|
32875
33104
|
sheet.cols[col] = { ...sheet.cols[col], size: this.getColSize(sheet.id, col) };
|
|
32876
33105
|
}
|
|
32877
33106
|
}
|
|
@@ -33091,7 +33320,7 @@ class ImagePlugin extends CorePlugin {
|
|
|
33091
33320
|
handle(cmd) {
|
|
33092
33321
|
switch (cmd.type) {
|
|
33093
33322
|
case "CREATE_IMAGE":
|
|
33094
|
-
this.
|
|
33323
|
+
this.addImage(cmd.figureId, cmd.sheetId, cmd.position, cmd.size);
|
|
33095
33324
|
this.history.update("images", cmd.sheetId, cmd.figureId, cmd.definition);
|
|
33096
33325
|
this.syncedImages.add(cmd.definition.path);
|
|
33097
33326
|
break;
|
|
@@ -33155,7 +33384,7 @@ class ImagePlugin extends CorePlugin {
|
|
|
33155
33384
|
// ---------------------------------------------------------------------------
|
|
33156
33385
|
// Private
|
|
33157
33386
|
// ---------------------------------------------------------------------------
|
|
33158
|
-
|
|
33387
|
+
addImage(id, sheetId, position, size) {
|
|
33159
33388
|
const figure = {
|
|
33160
33389
|
id,
|
|
33161
33390
|
x: position.x,
|
|
@@ -35662,7 +35891,6 @@ class PositionBitsEncoder {
|
|
|
35662
35891
|
}
|
|
35663
35892
|
}
|
|
35664
35893
|
|
|
35665
|
-
const functions = functionRegistry.content;
|
|
35666
35894
|
//#region
|
|
35667
35895
|
// ---------------------------------------------------------------------------
|
|
35668
35896
|
// INTRODUCTION
|
|
@@ -35884,9 +36112,7 @@ class EvaluationPlugin extends UIPlugin {
|
|
|
35884
36112
|
let isExported = true;
|
|
35885
36113
|
const formulaCell = this.getCorrespondingFormulaCell(position);
|
|
35886
36114
|
if (formulaCell) {
|
|
35887
|
-
isExported = formulaCell.compiledFormula.tokens
|
|
35888
|
-
.filter((tk) => tk.type === "FUNCTION")
|
|
35889
|
-
.every((tk) => functions[tk.value.toUpperCase()].isExported);
|
|
36115
|
+
isExported = isExportableToExcel(formulaCell.compiledFormula.tokens);
|
|
35890
36116
|
isFormula = isExported;
|
|
35891
36117
|
if (!isExported) {
|
|
35892
36118
|
newContent = value.toString();
|
|
@@ -36430,7 +36656,7 @@ class EvaluationConditionalFormatPlugin extends UIPlugin {
|
|
|
36430
36656
|
case "ContainsText":
|
|
36431
36657
|
return cell.value.toString().indexOf(values[0].toString()) > -1;
|
|
36432
36658
|
case "NotContains":
|
|
36433
|
-
return !cell.value || cell.value.toString().indexOf(values[0].toString())
|
|
36659
|
+
return !cell.value || cell.value.toString().indexOf(values[0].toString()) === -1;
|
|
36434
36660
|
case "GreaterThan":
|
|
36435
36661
|
return cell.value > values[0];
|
|
36436
36662
|
case "GreaterThanOrEqual":
|
|
@@ -36457,6 +36683,156 @@ class EvaluationConditionalFormatPlugin extends UIPlugin {
|
|
|
36457
36683
|
};
|
|
36458
36684
|
}
|
|
36459
36685
|
|
|
36686
|
+
class UIRowSizePlugin extends UIPlugin {
|
|
36687
|
+
static getters = ["getRowSize"];
|
|
36688
|
+
tallestCellInRow = {};
|
|
36689
|
+
ctx = document.createElement("canvas").getContext("2d");
|
|
36690
|
+
handle(cmd) {
|
|
36691
|
+
switch (cmd.type) {
|
|
36692
|
+
case "START":
|
|
36693
|
+
for (const sheetId of this.getters.getSheetIds()) {
|
|
36694
|
+
this.initializeSheet(sheetId);
|
|
36695
|
+
}
|
|
36696
|
+
break;
|
|
36697
|
+
case "CREATE_SHEET": {
|
|
36698
|
+
this.initializeSheet(cmd.sheetId);
|
|
36699
|
+
break;
|
|
36700
|
+
}
|
|
36701
|
+
case "DUPLICATE_SHEET": {
|
|
36702
|
+
const tallestCells = deepCopy(this.tallestCellInRow[cmd.sheetId]);
|
|
36703
|
+
this.history.update("tallestCellInRow", cmd.sheetIdTo, tallestCells);
|
|
36704
|
+
break;
|
|
36705
|
+
}
|
|
36706
|
+
case "DELETE_SHEET":
|
|
36707
|
+
const tallestCells = { ...this.tallestCellInRow };
|
|
36708
|
+
delete tallestCells[cmd.sheetId];
|
|
36709
|
+
this.history.update("tallestCellInRow", tallestCells);
|
|
36710
|
+
break;
|
|
36711
|
+
case "REMOVE_COLUMNS_ROWS": {
|
|
36712
|
+
if (cmd.dimension === "COL") {
|
|
36713
|
+
return;
|
|
36714
|
+
}
|
|
36715
|
+
const tallestCells = removeIndexesFromArray(this.tallestCellInRow[cmd.sheetId], cmd.elements);
|
|
36716
|
+
this.history.update("tallestCellInRow", cmd.sheetId, tallestCells);
|
|
36717
|
+
break;
|
|
36718
|
+
}
|
|
36719
|
+
case "ADD_COLUMNS_ROWS": {
|
|
36720
|
+
if (cmd.dimension === "COL") {
|
|
36721
|
+
return;
|
|
36722
|
+
}
|
|
36723
|
+
const addIndex = getAddHeaderStartIndex(cmd.position, cmd.base);
|
|
36724
|
+
const newCells = Array(cmd.quantity).fill(undefined);
|
|
36725
|
+
const newTallestCells = insertItemsAtIndex(this.tallestCellInRow[cmd.sheetId], newCells, addIndex);
|
|
36726
|
+
this.history.update("tallestCellInRow", cmd.sheetId, newTallestCells);
|
|
36727
|
+
break;
|
|
36728
|
+
}
|
|
36729
|
+
case "RESIZE_COLUMNS_ROWS":
|
|
36730
|
+
{
|
|
36731
|
+
const sheetId = cmd.sheetId;
|
|
36732
|
+
if (cmd.dimension === "ROW") {
|
|
36733
|
+
for (const row of cmd.elements) {
|
|
36734
|
+
const tallestCell = this.getRowTallestCell(sheetId, row);
|
|
36735
|
+
this.history.update("tallestCellInRow", sheetId, row, tallestCell);
|
|
36736
|
+
}
|
|
36737
|
+
}
|
|
36738
|
+
else {
|
|
36739
|
+
// Recompute row heights on col size change, they might have changed because of wrapped text
|
|
36740
|
+
for (const row of range(0, this.getters.getNumberRows(sheetId))) {
|
|
36741
|
+
for (const col of cmd.elements) {
|
|
36742
|
+
this.updateRowSizeForCellChange(sheetId, row, col);
|
|
36743
|
+
}
|
|
36744
|
+
}
|
|
36745
|
+
}
|
|
36746
|
+
}
|
|
36747
|
+
break;
|
|
36748
|
+
case "UPDATE_CELL":
|
|
36749
|
+
this.updateRowSizeForCellChange(cmd.sheetId, cmd.row, cmd.col);
|
|
36750
|
+
break;
|
|
36751
|
+
case "ADD_MERGE":
|
|
36752
|
+
case "REMOVE_MERGE":
|
|
36753
|
+
for (const target of cmd.target) {
|
|
36754
|
+
for (const position of positions(target)) {
|
|
36755
|
+
this.updateRowSizeForCellChange(cmd.sheetId, position.row, position.col);
|
|
36756
|
+
}
|
|
36757
|
+
}
|
|
36758
|
+
}
|
|
36759
|
+
return;
|
|
36760
|
+
}
|
|
36761
|
+
getRowSize(sheetId, row) {
|
|
36762
|
+
return Math.round(this.getters.getUserRowSize(sheetId, row) ??
|
|
36763
|
+
this.tallestCellInRow[sheetId][row]?.size ??
|
|
36764
|
+
DEFAULT_CELL_HEIGHT);
|
|
36765
|
+
}
|
|
36766
|
+
updateRowSizeForCellChange(sheetId, row, col) {
|
|
36767
|
+
const tallestCellInRow = this.tallestCellInRow[sheetId]?.[row];
|
|
36768
|
+
if (tallestCellInRow?.cell.col === col) {
|
|
36769
|
+
const newTallestCell = this.getRowTallestCell(sheetId, row);
|
|
36770
|
+
this.history.update("tallestCellInRow", sheetId, row, newTallestCell);
|
|
36771
|
+
}
|
|
36772
|
+
const updatedCellHeight = this.getCellHeight({ sheetId, col, row });
|
|
36773
|
+
if (updatedCellHeight <= DEFAULT_CELL_HEIGHT) {
|
|
36774
|
+
return;
|
|
36775
|
+
}
|
|
36776
|
+
if ((!tallestCellInRow && updatedCellHeight > DEFAULT_CELL_HEIGHT) ||
|
|
36777
|
+
(tallestCellInRow && updatedCellHeight > tallestCellInRow.size)) {
|
|
36778
|
+
const newTallestCell = { cell: { sheetId, col, row }, size: updatedCellHeight };
|
|
36779
|
+
this.history.update("tallestCellInRow", sheetId, row, newTallestCell);
|
|
36780
|
+
}
|
|
36781
|
+
}
|
|
36782
|
+
initializeSheet(sheetId) {
|
|
36783
|
+
const tallestCells = [];
|
|
36784
|
+
for (let row = 0; row < this.getters.getNumberRows(sheetId); row++) {
|
|
36785
|
+
const tallestCell = this.getRowTallestCell(sheetId, row);
|
|
36786
|
+
tallestCells.push(tallestCell);
|
|
36787
|
+
}
|
|
36788
|
+
this.history.update("tallestCellInRow", sheetId, tallestCells);
|
|
36789
|
+
}
|
|
36790
|
+
/**
|
|
36791
|
+
* Return the height the cell should have in the sheet, which is either DEFAULT_CELL_HEIGHT if the cell is in a multi-row
|
|
36792
|
+
* merge, or the height of the cell computed based on its style/content.
|
|
36793
|
+
*/
|
|
36794
|
+
getCellHeight(position) {
|
|
36795
|
+
if (this.isInMultiRowMerge(position)) {
|
|
36796
|
+
return DEFAULT_CELL_HEIGHT;
|
|
36797
|
+
}
|
|
36798
|
+
const cell = this.getters.getCell(position);
|
|
36799
|
+
const colSize = this.getters.getColSize(position.sheetId, position.col);
|
|
36800
|
+
return getDefaultCellHeight(this.ctx, cell, colSize);
|
|
36801
|
+
}
|
|
36802
|
+
isInMultiRowMerge(position) {
|
|
36803
|
+
const merge = this.getters.getMerge(position);
|
|
36804
|
+
return !!merge && merge.bottom !== merge.top;
|
|
36805
|
+
}
|
|
36806
|
+
/**
|
|
36807
|
+
* Get the tallest cell of a row and its size.
|
|
36808
|
+
*/
|
|
36809
|
+
getRowTallestCell(sheetId, row) {
|
|
36810
|
+
const userRowSize = this.getters.getUserRowSize(sheetId, row);
|
|
36811
|
+
if (userRowSize !== undefined) {
|
|
36812
|
+
return undefined;
|
|
36813
|
+
}
|
|
36814
|
+
const cellIds = this.getters.getRowCells(sheetId, row);
|
|
36815
|
+
let maxHeight = 0;
|
|
36816
|
+
let tallestCell = undefined;
|
|
36817
|
+
for (let i = 0; i < cellIds.length; i++) {
|
|
36818
|
+
const cell = this.getters.getCellById(cellIds[i]);
|
|
36819
|
+
if (!cell) {
|
|
36820
|
+
continue;
|
|
36821
|
+
}
|
|
36822
|
+
const position = this.getters.getCellPosition(cell.id);
|
|
36823
|
+
const cellHeight = this.getCellHeight(position);
|
|
36824
|
+
if (cellHeight > maxHeight && cellHeight > DEFAULT_CELL_HEIGHT) {
|
|
36825
|
+
maxHeight = cellHeight;
|
|
36826
|
+
tallestCell = { cell: position, size: cellHeight };
|
|
36827
|
+
}
|
|
36828
|
+
}
|
|
36829
|
+
if (tallestCell && tallestCell.size > DEFAULT_CELL_HEIGHT) {
|
|
36830
|
+
return tallestCell;
|
|
36831
|
+
}
|
|
36832
|
+
return undefined;
|
|
36833
|
+
}
|
|
36834
|
+
}
|
|
36835
|
+
|
|
36460
36836
|
/**
|
|
36461
36837
|
* This plugin manage the autofill.
|
|
36462
36838
|
*
|
|
@@ -39890,10 +40266,8 @@ class SortPlugin extends UIPlugin {
|
|
|
39890
40266
|
}
|
|
39891
40267
|
updateCellCommands.push(newCellValues);
|
|
39892
40268
|
}
|
|
39893
|
-
for (const cmd of updateCellCommands) {
|
|
39894
|
-
this.dispatch("UPDATE_CELL", cmd);
|
|
39895
|
-
}
|
|
39896
40269
|
}
|
|
40270
|
+
updateCellCommands.forEach((cmdPayload) => this.dispatch("UPDATE_CELL", cmdPayload));
|
|
39897
40271
|
}
|
|
39898
40272
|
/**
|
|
39899
40273
|
* Return the distances between main merge cells in the zone.
|
|
@@ -39994,24 +40368,28 @@ class SheetUIPlugin extends UIPlugin {
|
|
|
39994
40368
|
// Getters
|
|
39995
40369
|
// ---------------------------------------------------------------------------
|
|
39996
40370
|
getCellWidth(position) {
|
|
39997
|
-
const text = this.getCellText(position);
|
|
39998
40371
|
const style = this.getters.getCellComputedStyle(position);
|
|
39999
|
-
|
|
40000
|
-
|
|
40372
|
+
let contentWidth = 0;
|
|
40373
|
+
const content = this.getters.getEvaluatedCell(position).formattedValue;
|
|
40374
|
+
if (content) {
|
|
40375
|
+
const multiLineText = splitTextToWidth(this.ctx, content, style, undefined);
|
|
40376
|
+
contentWidth += Math.max(...multiLineText.map((line) => computeTextWidth(this.ctx, line, style)));
|
|
40377
|
+
}
|
|
40001
40378
|
const icon = this.getters.getConditionalIcon(position);
|
|
40002
40379
|
if (icon) {
|
|
40003
|
-
contentWidth += computeIconWidth(
|
|
40380
|
+
contentWidth += computeIconWidth(style);
|
|
40004
40381
|
}
|
|
40005
40382
|
const isFilterHeader = this.getters.isFilterHeader(position);
|
|
40006
40383
|
if (isFilterHeader) {
|
|
40007
40384
|
contentWidth += ICON_EDGE_LENGTH + FILTER_ICON_MARGIN;
|
|
40008
40385
|
}
|
|
40009
|
-
if (contentWidth
|
|
40010
|
-
|
|
40011
|
-
|
|
40012
|
-
|
|
40013
|
-
|
|
40014
|
-
|
|
40386
|
+
if (contentWidth === 0) {
|
|
40387
|
+
return 0;
|
|
40388
|
+
}
|
|
40389
|
+
contentWidth += 2 * PADDING_AUTORESIZE_HORIZONTAL;
|
|
40390
|
+
if (style.wrapping === "wrap") {
|
|
40391
|
+
const colWidth = this.getters.getColSize(this.getters.getActiveSheetId(), position.col);
|
|
40392
|
+
return Math.min(colWidth, contentWidth);
|
|
40015
40393
|
}
|
|
40016
40394
|
return contentWidth;
|
|
40017
40395
|
}
|
|
@@ -40034,56 +40412,7 @@ class SheetUIPlugin extends UIPlugin {
|
|
|
40034
40412
|
getCellMultiLineText(position, width) {
|
|
40035
40413
|
const style = this.getters.getCellStyle(position);
|
|
40036
40414
|
const text = this.getters.getCellText(position, this.getters.shouldShowFormulas());
|
|
40037
|
-
|
|
40038
|
-
for (const line of text.split("\n")) {
|
|
40039
|
-
const words = line.split(" ");
|
|
40040
|
-
if (!width) {
|
|
40041
|
-
brokenText.push(line);
|
|
40042
|
-
continue;
|
|
40043
|
-
}
|
|
40044
|
-
let textLine = "";
|
|
40045
|
-
let availableWidth = width;
|
|
40046
|
-
for (let word of words) {
|
|
40047
|
-
const splitWord = this.splitWordToSpecificWidth(this.ctx, word, width, style);
|
|
40048
|
-
const lastPart = splitWord.pop();
|
|
40049
|
-
const lastPartWidth = computeTextWidth(this.ctx, lastPart, style);
|
|
40050
|
-
// At this step: "splitWord" is an array composed of parts of word whose
|
|
40051
|
-
// length is at most equal to "width".
|
|
40052
|
-
// Last part contains the end of the word.
|
|
40053
|
-
// Note that: When word length is less than width, then lastPart is equal
|
|
40054
|
-
// to word and splitWord is empty
|
|
40055
|
-
if (splitWord.length) {
|
|
40056
|
-
if (textLine !== "") {
|
|
40057
|
-
brokenText.push(textLine);
|
|
40058
|
-
textLine = "";
|
|
40059
|
-
availableWidth = width;
|
|
40060
|
-
}
|
|
40061
|
-
splitWord.forEach((wordPart) => {
|
|
40062
|
-
brokenText.push(wordPart);
|
|
40063
|
-
});
|
|
40064
|
-
textLine = lastPart;
|
|
40065
|
-
availableWidth = width - lastPartWidth;
|
|
40066
|
-
}
|
|
40067
|
-
else {
|
|
40068
|
-
// here "lastPart" is equal to "word" and the "word" size is smaller than "width"
|
|
40069
|
-
const _word = textLine === "" ? lastPart : " " + lastPart;
|
|
40070
|
-
const wordWidth = computeTextWidth(this.ctx, _word, style);
|
|
40071
|
-
if (wordWidth <= availableWidth) {
|
|
40072
|
-
textLine += _word;
|
|
40073
|
-
availableWidth -= wordWidth;
|
|
40074
|
-
}
|
|
40075
|
-
else {
|
|
40076
|
-
brokenText.push(textLine);
|
|
40077
|
-
textLine = lastPart;
|
|
40078
|
-
availableWidth = width - lastPartWidth;
|
|
40079
|
-
}
|
|
40080
|
-
}
|
|
40081
|
-
}
|
|
40082
|
-
if (textLine !== "") {
|
|
40083
|
-
brokenText.push(textLine);
|
|
40084
|
-
}
|
|
40085
|
-
}
|
|
40086
|
-
return brokenText;
|
|
40415
|
+
return splitTextToWidth(this.ctx, text, style, width);
|
|
40087
40416
|
}
|
|
40088
40417
|
/**
|
|
40089
40418
|
* Returns the size, start and end coordinates of a column on an unfolded sheet
|
|
@@ -40142,26 +40471,6 @@ class SheetUIPlugin extends UIPlugin {
|
|
|
40142
40471
|
const sizes = cellsPositions.map((position) => this.getCellWidth({ sheetId, ...position }));
|
|
40143
40472
|
return Math.max(0, ...sizes);
|
|
40144
40473
|
}
|
|
40145
|
-
splitWordToSpecificWidth(ctx, word, width, style) {
|
|
40146
|
-
const wordWidth = computeTextWidth(ctx, word, style);
|
|
40147
|
-
if (wordWidth <= width) {
|
|
40148
|
-
return [word];
|
|
40149
|
-
}
|
|
40150
|
-
const splitWord = [];
|
|
40151
|
-
let wordPart = "";
|
|
40152
|
-
for (let l of word) {
|
|
40153
|
-
const wordPartWidth = computeTextWidth(ctx, wordPart + l, style);
|
|
40154
|
-
if (wordPartWidth > width) {
|
|
40155
|
-
splitWord.push(wordPart);
|
|
40156
|
-
wordPart = l;
|
|
40157
|
-
}
|
|
40158
|
-
else {
|
|
40159
|
-
wordPart += l;
|
|
40160
|
-
}
|
|
40161
|
-
}
|
|
40162
|
-
splitWord.push(wordPart);
|
|
40163
|
-
return splitWord;
|
|
40164
|
-
}
|
|
40165
40474
|
/**
|
|
40166
40475
|
* Check that any "sheetId" in the command matches an existing
|
|
40167
40476
|
* sheet.
|
|
@@ -41153,7 +41462,7 @@ class ClipboardCellsState extends ClipboardCellsAbstractState {
|
|
|
41153
41462
|
.join("\n") || "\t");
|
|
41154
41463
|
}
|
|
41155
41464
|
getHTMLContent() {
|
|
41156
|
-
if (this.cells.length
|
|
41465
|
+
if (this.cells.length === 1 && this.cells[0].length === 1) {
|
|
41157
41466
|
return this.getters.getCellText(this.cells[0][0].position);
|
|
41158
41467
|
}
|
|
41159
41468
|
let htmlTable = '<table border="1" style="border-collapse:collapse">';
|
|
@@ -41478,7 +41787,7 @@ class ClipboardPlugin extends UIPlugin {
|
|
|
41478
41787
|
status = "invisible";
|
|
41479
41788
|
state;
|
|
41480
41789
|
lastPasteState;
|
|
41481
|
-
|
|
41790
|
+
paintFormatStatus = "inactive";
|
|
41482
41791
|
originSheetId;
|
|
41483
41792
|
// ---------------------------------------------------------------------------
|
|
41484
41793
|
// Command Handling
|
|
@@ -41493,7 +41802,7 @@ class ClipboardPlugin extends UIPlugin {
|
|
|
41493
41802
|
if (!this.state) {
|
|
41494
41803
|
return 24 /* CommandResult.EmptyClipboard */;
|
|
41495
41804
|
}
|
|
41496
|
-
const pasteOption = cmd.pasteOption || (this.
|
|
41805
|
+
const pasteOption = cmd.pasteOption || (this.paintFormatStatus !== "inactive" ? "onlyFormat" : undefined);
|
|
41497
41806
|
return this.state.isPasteAllowed(cmd.target, { pasteOption });
|
|
41498
41807
|
case "PASTE_FROM_OS_CLIPBOARD": {
|
|
41499
41808
|
const state = new ClipboardOsState(cmd.text, this.getters, this.dispatch, this.selection);
|
|
@@ -41509,6 +41818,11 @@ class ClipboardPlugin extends UIPlugin {
|
|
|
41509
41818
|
const state = this.getClipboardStateForCopyCells(cut, "CUT");
|
|
41510
41819
|
return state.isPasteAllowed(paste);
|
|
41511
41820
|
}
|
|
41821
|
+
case "ACTIVATE_PAINT_FORMAT": {
|
|
41822
|
+
if (this.paintFormatStatus !== "inactive") {
|
|
41823
|
+
return 95 /* CommandResult.AlreadyInPaintingFormatMode */;
|
|
41824
|
+
}
|
|
41825
|
+
}
|
|
41512
41826
|
}
|
|
41513
41827
|
return 0 /* CommandResult.Success */;
|
|
41514
41828
|
}
|
|
@@ -41525,11 +41839,13 @@ class ClipboardPlugin extends UIPlugin {
|
|
|
41525
41839
|
if (!this.state) {
|
|
41526
41840
|
break;
|
|
41527
41841
|
}
|
|
41528
|
-
const pasteOption = cmd.pasteOption || (this.
|
|
41529
|
-
this._isPaintingFormat = false;
|
|
41842
|
+
const pasteOption = cmd.pasteOption || (this.paintFormatStatus !== "inactive" ? "onlyFormat" : undefined);
|
|
41530
41843
|
this.state.paste(cmd.target, { pasteOption, shouldPasteCF: true, selectTarget: true });
|
|
41531
41844
|
this.lastPasteState = this.state;
|
|
41532
|
-
this.
|
|
41845
|
+
if (this.paintFormatStatus === "oneOff") {
|
|
41846
|
+
this.paintFormatStatus = "inactive";
|
|
41847
|
+
this.status = "invisible";
|
|
41848
|
+
}
|
|
41533
41849
|
break;
|
|
41534
41850
|
case "CLEAN_CLIPBOARD_HIGHLIGHT":
|
|
41535
41851
|
this.status = "invisible";
|
|
@@ -41597,8 +41913,13 @@ class ClipboardPlugin extends UIPlugin {
|
|
|
41597
41913
|
case "ACTIVATE_PAINT_FORMAT": {
|
|
41598
41914
|
const zones = this.getters.getSelectedZones();
|
|
41599
41915
|
this.state = this.getClipboardStateForCopyCells(zones, "COPY");
|
|
41600
|
-
this._isPaintingFormat = true;
|
|
41601
41916
|
this.status = "visible";
|
|
41917
|
+
if (cmd.persistent) {
|
|
41918
|
+
this.paintFormatStatus = "persistent";
|
|
41919
|
+
}
|
|
41920
|
+
else {
|
|
41921
|
+
this.paintFormatStatus = "oneOff";
|
|
41922
|
+
}
|
|
41602
41923
|
break;
|
|
41603
41924
|
}
|
|
41604
41925
|
case "DELETE_SHEET":
|
|
@@ -41610,6 +41931,11 @@ class ClipboardPlugin extends UIPlugin {
|
|
|
41610
41931
|
this.status = "invisible";
|
|
41611
41932
|
}
|
|
41612
41933
|
break;
|
|
41934
|
+
case "CANCEL_PAINT_FORMAT": {
|
|
41935
|
+
this.paintFormatStatus = "inactive";
|
|
41936
|
+
this.status = "invisible";
|
|
41937
|
+
break;
|
|
41938
|
+
}
|
|
41613
41939
|
default:
|
|
41614
41940
|
if (isCoreCommand(cmd)) {
|
|
41615
41941
|
this.status = "invisible";
|
|
@@ -41640,7 +41966,7 @@ class ClipboardPlugin extends UIPlugin {
|
|
|
41640
41966
|
return this.state ? this.state.operation === "CUT" : false;
|
|
41641
41967
|
}
|
|
41642
41968
|
isPaintingFormat() {
|
|
41643
|
-
return this.
|
|
41969
|
+
return this.paintFormatStatus !== "inactive";
|
|
41644
41970
|
}
|
|
41645
41971
|
// ---------------------------------------------------------------------------
|
|
41646
41972
|
// Private methods
|
|
@@ -44275,6 +44601,7 @@ const coreViewsPluginRegistry = new Registry()
|
|
|
44275
44601
|
.add("evaluation", EvaluationPlugin)
|
|
44276
44602
|
.add("evaluation_chart", EvaluationChartPlugin)
|
|
44277
44603
|
.add("evaluation_cf", EvaluationConditionalFormatPlugin)
|
|
44604
|
+
.add("row_size", UIRowSizePlugin)
|
|
44278
44605
|
.add("custom_colors", CustomColorsPlugin);
|
|
44279
44606
|
|
|
44280
44607
|
const clickableCellRegistry = new Registry();
|
|
@@ -44292,7 +44619,7 @@ class ImageProvider {
|
|
|
44292
44619
|
async requestImage() {
|
|
44293
44620
|
const file = await this.getImageFromUser();
|
|
44294
44621
|
const path = await this.fileStore.upload(file);
|
|
44295
|
-
const size = await this.
|
|
44622
|
+
const size = await this.getImageOriginalSize(path);
|
|
44296
44623
|
return { path, size, mimetype: file.type };
|
|
44297
44624
|
}
|
|
44298
44625
|
getImageFromUser() {
|
|
@@ -44311,7 +44638,7 @@ class ImageProvider {
|
|
|
44311
44638
|
input.click();
|
|
44312
44639
|
});
|
|
44313
44640
|
}
|
|
44314
|
-
|
|
44641
|
+
getImageOriginalSize(path) {
|
|
44315
44642
|
return new Promise((resolve, reject) => {
|
|
44316
44643
|
const image = new Image();
|
|
44317
44644
|
image.src = path;
|
|
@@ -45530,11 +45857,11 @@ class ActionButton extends owl.Component {
|
|
|
45530
45857
|
}
|
|
45531
45858
|
get title() {
|
|
45532
45859
|
const name = this.actionButton.name(this.env);
|
|
45533
|
-
const description = this.actionButton.description;
|
|
45860
|
+
const description = this.actionButton.description(this.env);
|
|
45534
45861
|
return name + (description ? ` (${description})` : "");
|
|
45535
45862
|
}
|
|
45536
45863
|
get iconTitle() {
|
|
45537
|
-
return this.actionButton.icon;
|
|
45864
|
+
return this.actionButton.icon(this.env);
|
|
45538
45865
|
}
|
|
45539
45866
|
onClick(ev) {
|
|
45540
45867
|
if (this.isEnabled) {
|
|
@@ -45796,6 +46123,10 @@ css /* scss */ `
|
|
|
45796
46123
|
top: 20%;
|
|
45797
46124
|
}
|
|
45798
46125
|
}
|
|
46126
|
+
|
|
46127
|
+
.user-select-text {
|
|
46128
|
+
user-select: text;
|
|
46129
|
+
}
|
|
45799
46130
|
`;
|
|
45800
46131
|
class TopBarComposer extends owl.Component {
|
|
45801
46132
|
static template = "o-spreadsheet-TopBarComposer";
|
|
@@ -45915,6 +46246,27 @@ FontSizeEditor.props = {
|
|
|
45915
46246
|
class: String,
|
|
45916
46247
|
};
|
|
45917
46248
|
|
|
46249
|
+
class PaintFormatButton extends owl.Component {
|
|
46250
|
+
static template = "o-spreadsheet-PaintFormatButton";
|
|
46251
|
+
get isActive() {
|
|
46252
|
+
return this.env.model.getters.isPaintingFormat();
|
|
46253
|
+
}
|
|
46254
|
+
onDblClick() {
|
|
46255
|
+
this.env.model.dispatch("ACTIVATE_PAINT_FORMAT", { persistent: true });
|
|
46256
|
+
}
|
|
46257
|
+
togglePaintFormat() {
|
|
46258
|
+
if (this.isActive) {
|
|
46259
|
+
this.env.model.dispatch("CANCEL_PAINT_FORMAT");
|
|
46260
|
+
}
|
|
46261
|
+
else {
|
|
46262
|
+
this.env.model.dispatch("ACTIVATE_PAINT_FORMAT", { persistent: false });
|
|
46263
|
+
}
|
|
46264
|
+
}
|
|
46265
|
+
}
|
|
46266
|
+
PaintFormatButton.props = {
|
|
46267
|
+
class: { type: String, optional: true },
|
|
46268
|
+
};
|
|
46269
|
+
|
|
45918
46270
|
// -----------------------------------------------------------------------------
|
|
45919
46271
|
// TopBar
|
|
45920
46272
|
// -----------------------------------------------------------------------------
|
|
@@ -46015,6 +46367,7 @@ class TopBar extends owl.Component {
|
|
|
46015
46367
|
TopBarComposer,
|
|
46016
46368
|
FontSizeEditor,
|
|
46017
46369
|
ActionButton,
|
|
46370
|
+
PaintFormatButton,
|
|
46018
46371
|
BorderEditorWidget,
|
|
46019
46372
|
};
|
|
46020
46373
|
state = owl.useState({
|
|
@@ -47867,7 +48220,7 @@ class SelectionStreamProcessorImpl {
|
|
|
47867
48220
|
: "nextCluster";
|
|
47868
48221
|
while (true) {
|
|
47869
48222
|
const nextCellPosition = this.getNextCellPosition(currentPosition, dim, dir);
|
|
47870
|
-
// Break if nextPosition
|
|
48223
|
+
// Break if nextPosition === currentPosition, which happens if there's no next valid position
|
|
47871
48224
|
if (currentPosition.col === nextCellPosition.col &&
|
|
47872
48225
|
currentPosition.row === nextCellPosition.row) {
|
|
47873
48226
|
break;
|
|
@@ -48834,6 +49187,8 @@ function createImageDrawing(figure, sheet, imageRelId) {
|
|
|
48834
49187
|
["name", `Image ${imageId}`],
|
|
48835
49188
|
["title", "Image"],
|
|
48836
49189
|
];
|
|
49190
|
+
const cx = convertDotValueToEMU(figure.width);
|
|
49191
|
+
const cy = convertDotValueToEMU(figure.height);
|
|
48837
49192
|
return escapeXml /*xml*/ `
|
|
48838
49193
|
<xdr:twoCellAnchor editAs="oneCell">
|
|
48839
49194
|
<xdr:from>
|
|
@@ -48860,6 +49215,9 @@ function createImageDrawing(figure, sheet, imageRelId) {
|
|
|
48860
49215
|
</a:stretch>
|
|
48861
49216
|
</xdr:blipFill>
|
|
48862
49217
|
<xdr:spPr>
|
|
49218
|
+
<a:xfrm>
|
|
49219
|
+
<a:ext cx="${cx}" cy="${cy}" />
|
|
49220
|
+
</a:xfrm>
|
|
48863
49221
|
<a:prstGeom prst="rect">
|
|
48864
49222
|
<a:avLst/>
|
|
48865
49223
|
</a:prstGeom>
|
|
@@ -49379,7 +49737,7 @@ function createWorksheets(data, construct) {
|
|
|
49379
49737
|
const mimeType = image.data.mimetype;
|
|
49380
49738
|
if (mimeType === undefined)
|
|
49381
49739
|
continue;
|
|
49382
|
-
const extension =
|
|
49740
|
+
const extension = IMAGE_MIMETYPE_TO_EXTENSION_MAPPING[mimeType];
|
|
49383
49741
|
// only support exporting images with mimetypes specified in the mapping
|
|
49384
49742
|
if (extension === undefined)
|
|
49385
49743
|
continue;
|
|
@@ -49515,7 +49873,7 @@ function createRelsFiles(relsFiles) {
|
|
|
49515
49873
|
function createContentTypes(files) {
|
|
49516
49874
|
const overrideNodes = [];
|
|
49517
49875
|
// hard-code supported image mimetypes
|
|
49518
|
-
const imageDefaultNodes = Object.entries(
|
|
49876
|
+
const imageDefaultNodes = Object.entries(IMAGE_MIMETYPE_TO_EXTENSION_MAPPING).map(([mimetype, extension]) => createDefaultXMLElement(extension, mimetype));
|
|
49519
49877
|
for (const file of files) {
|
|
49520
49878
|
if ("contentType" in file && file.contentType) {
|
|
49521
49879
|
overrideNodes.push(createOverride("/" + file.path, CONTENT_TYPES[file.contentType]));
|
|
@@ -49776,6 +50134,7 @@ class Model extends EventBus {
|
|
|
49776
50134
|
...config,
|
|
49777
50135
|
mode: config.mode || "normal",
|
|
49778
50136
|
custom: config.custom || {},
|
|
50137
|
+
defaultCurrencyFormat: config.defaultCurrencyFormat || "[$$]#,##0.00",
|
|
49779
50138
|
external: this.setupExternalConfig(config.external || {}),
|
|
49780
50139
|
transportService,
|
|
49781
50140
|
client,
|
|
@@ -50073,6 +50432,7 @@ const helpers = {
|
|
|
50073
50432
|
numberToLetters,
|
|
50074
50433
|
UuidGenerator,
|
|
50075
50434
|
formatValue,
|
|
50435
|
+
createCurrencyFormat,
|
|
50076
50436
|
computeTextWidth,
|
|
50077
50437
|
createEmptyWorkbookData,
|
|
50078
50438
|
createEmptySheet,
|
|
@@ -50145,6 +50505,7 @@ exports.helpers = helpers;
|
|
|
50145
50505
|
exports.invalidateCFEvaluationCommands = invalidateCFEvaluationCommands;
|
|
50146
50506
|
exports.invalidateDependenciesCommands = invalidateDependenciesCommands;
|
|
50147
50507
|
exports.invalidateEvaluationCommands = invalidateEvaluationCommands;
|
|
50508
|
+
exports.iterateAstNodes = iterateAstNodes;
|
|
50148
50509
|
exports.links = links;
|
|
50149
50510
|
exports.load = load;
|
|
50150
50511
|
exports.parse = parse;
|
|
@@ -50155,6 +50516,6 @@ exports.setTranslationMethod = setTranslationMethod;
|
|
|
50155
50516
|
exports.tokenize = tokenize;
|
|
50156
50517
|
|
|
50157
50518
|
|
|
50158
|
-
__info__.version = '16.4.0
|
|
50159
|
-
__info__.date = '2023-
|
|
50160
|
-
__info__.hash = '
|
|
50519
|
+
__info__.version = '16.4.0';
|
|
50520
|
+
__info__.date = '2023-07-24T06:26:19.494Z';
|
|
50521
|
+
__info__.hash = '4eb614c';
|