@odoo/o-spreadsheet 16.4.0-alpha.7 → 16.5.0-alpha.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 +684 -475
- package/dist/o-spreadsheet.d.ts +140 -110
- package/dist/o-spreadsheet.esm.js +684 -475
- package/dist/o-spreadsheet.iife.js +684 -475
- package/dist/o-spreadsheet.iife.min.js +271 -267
- package/package.json +11 -11
|
@@ -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,
|
|
@@ -15186,6 +15312,8 @@ const uuidGenerator$2 = new UuidGenerator();
|
|
|
15186
15312
|
function createAction(item) {
|
|
15187
15313
|
const name = item.name;
|
|
15188
15314
|
const children = item.children;
|
|
15315
|
+
const description = item.description;
|
|
15316
|
+
const icon = item.icon;
|
|
15189
15317
|
return {
|
|
15190
15318
|
id: item.id || uuidGenerator$2.uuidv4(),
|
|
15191
15319
|
name: typeof name === "function" ? name : () => name,
|
|
@@ -15203,8 +15331,8 @@ function createAction(item) {
|
|
|
15203
15331
|
: () => [],
|
|
15204
15332
|
isReadonlyAllowed: item.isReadonlyAllowed || false,
|
|
15205
15333
|
separator: item.separator || false,
|
|
15206
|
-
icon:
|
|
15207
|
-
description:
|
|
15334
|
+
icon: typeof icon === "function" ? icon : () => icon || "",
|
|
15335
|
+
description: typeof description === "function" ? description : () => description || "",
|
|
15208
15336
|
textColor: item.textColor,
|
|
15209
15337
|
sequence: item.sequence || 0,
|
|
15210
15338
|
};
|
|
@@ -16670,7 +16798,7 @@ function getChartTimeOptions(labels, labelFormat, locale) {
|
|
|
16670
16798
|
function convertDateFormatForMoment(format) {
|
|
16671
16799
|
format = format.replace(/y/g, "Y");
|
|
16672
16800
|
format = format.replace(/d/g, "D");
|
|
16673
|
-
// "m" before "h"
|
|
16801
|
+
// "m" before "h" === month, "m" after "h" === minute
|
|
16674
16802
|
const indexH = format.indexOf("h");
|
|
16675
16803
|
if (indexH >= 0) {
|
|
16676
16804
|
format = format.slice(0, indexH).replace(/m/g, "M") + format.slice(indexH);
|
|
@@ -17524,6 +17652,7 @@ function getChartMenu(figureId, onFigureDeleted, env) {
|
|
|
17524
17652
|
env.model.dispatch("SELECT_FIGURE", { id: figureId });
|
|
17525
17653
|
env.openSidePanel("ChartPanel");
|
|
17526
17654
|
},
|
|
17655
|
+
icon: "o-spreadsheet-Icon.EDIT",
|
|
17527
17656
|
},
|
|
17528
17657
|
getCopyMenuItem(figureId, env),
|
|
17529
17658
|
getCutMenuItem(figureId, env),
|
|
@@ -17555,6 +17684,7 @@ function getImageMenuRegistry(figureId, onFigureDeleted, env) {
|
|
|
17555
17684
|
width,
|
|
17556
17685
|
});
|
|
17557
17686
|
},
|
|
17687
|
+
icon: "o-spreadsheet-Icon.REFRESH",
|
|
17558
17688
|
},
|
|
17559
17689
|
getDeleteMenuItem(figureId, onFigureDeleted, env),
|
|
17560
17690
|
];
|
|
@@ -17571,6 +17701,7 @@ function getCopyMenuItem(figureId, env) {
|
|
|
17571
17701
|
env.model.dispatch("COPY");
|
|
17572
17702
|
await env.clipboard.write(env.model.getters.getClipboardContent());
|
|
17573
17703
|
},
|
|
17704
|
+
icon: "o-spreadsheet-Icon.COPY",
|
|
17574
17705
|
};
|
|
17575
17706
|
}
|
|
17576
17707
|
function getCutMenuItem(figureId, env) {
|
|
@@ -17584,6 +17715,7 @@ function getCutMenuItem(figureId, env) {
|
|
|
17584
17715
|
env.model.dispatch("CUT");
|
|
17585
17716
|
await env.clipboard.write(env.model.getters.getClipboardContent());
|
|
17586
17717
|
},
|
|
17718
|
+
icon: "o-spreadsheet-Icon.CUT",
|
|
17587
17719
|
};
|
|
17588
17720
|
}
|
|
17589
17721
|
function getDeleteMenuItem(figureId, onFigureDeleted, env) {
|
|
@@ -17598,6 +17730,7 @@ function getDeleteMenuItem(figureId, onFigureDeleted, env) {
|
|
|
17598
17730
|
});
|
|
17599
17731
|
onFigureDeleted();
|
|
17600
17732
|
},
|
|
17733
|
+
icon: "o-spreadsheet-Icon.DELETE",
|
|
17601
17734
|
};
|
|
17602
17735
|
}
|
|
17603
17736
|
|
|
@@ -18665,7 +18798,7 @@ const insertFunctionMin = {
|
|
|
18665
18798
|
};
|
|
18666
18799
|
const categorieFunctionAll = {
|
|
18667
18800
|
name: _lt("All"),
|
|
18668
|
-
children: allFunctionListMenuBuilder
|
|
18801
|
+
children: [allFunctionListMenuBuilder],
|
|
18669
18802
|
};
|
|
18670
18803
|
function allFunctionListMenuBuilder() {
|
|
18671
18804
|
const fnNames = functionRegistry.getKeys();
|
|
@@ -18997,7 +19130,10 @@ const formatNumberAutomatic = {
|
|
|
18997
19130
|
};
|
|
18998
19131
|
const formatNumberNumber = {
|
|
18999
19132
|
name: _lt("Number"),
|
|
19000
|
-
description:
|
|
19133
|
+
description: (env) => formatValue(1000.12, {
|
|
19134
|
+
format: "#,##0.00",
|
|
19135
|
+
locale: env.model.getters.getLocale(),
|
|
19136
|
+
}),
|
|
19001
19137
|
execute: (env) => setFormatter(env, "#,##0.00"),
|
|
19002
19138
|
isActive: (env) => isFormatSelected(env, "#,##0.00"),
|
|
19003
19139
|
};
|
|
@@ -19008,21 +19144,34 @@ const formatPercent = {
|
|
|
19008
19144
|
};
|
|
19009
19145
|
const formatNumberPercent = {
|
|
19010
19146
|
name: _lt("Percent"),
|
|
19011
|
-
description:
|
|
19147
|
+
description: (env) => formatValue(0.1012, {
|
|
19148
|
+
format: "0.00%",
|
|
19149
|
+
locale: env.model.getters.getLocale(),
|
|
19150
|
+
}),
|
|
19012
19151
|
execute: FORMAT_PERCENT_ACTION,
|
|
19013
19152
|
isActive: (env) => isFormatSelected(env, "0.00%"),
|
|
19014
19153
|
};
|
|
19015
19154
|
const formatNumberCurrency = {
|
|
19016
19155
|
name: _lt("Currency"),
|
|
19017
|
-
description:
|
|
19018
|
-
|
|
19019
|
-
|
|
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),
|
|
19020
19162
|
};
|
|
19021
19163
|
const formatNumberCurrencyRounded = {
|
|
19022
19164
|
name: _lt("Currency rounded"),
|
|
19023
|
-
description:
|
|
19024
|
-
|
|
19025
|
-
|
|
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
|
+
},
|
|
19026
19175
|
};
|
|
19027
19176
|
const formatCustomCurrency = {
|
|
19028
19177
|
name: _lt("Custom currency"),
|
|
@@ -19031,19 +19180,37 @@ const formatCustomCurrency = {
|
|
|
19031
19180
|
};
|
|
19032
19181
|
const formatNumberDate = {
|
|
19033
19182
|
name: _lt("Date"),
|
|
19034
|
-
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
|
+
},
|
|
19035
19190
|
execute: (env) => setFormatter(env, env.model.getters.getLocale().dateFormat),
|
|
19036
19191
|
isActive: (env) => isFormatSelected(env, env.model.getters.getLocale().dateFormat),
|
|
19037
19192
|
};
|
|
19038
19193
|
const formatNumberTime = {
|
|
19039
19194
|
name: _lt("Time"),
|
|
19040
|
-
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
|
+
},
|
|
19041
19202
|
execute: (env) => setFormatter(env, env.model.getters.getLocale().timeFormat),
|
|
19042
19203
|
isActive: (env) => isFormatSelected(env, env.model.getters.getLocale().timeFormat),
|
|
19043
19204
|
};
|
|
19044
19205
|
const formatNumberDateTime = {
|
|
19045
19206
|
name: _lt("Date time"),
|
|
19046
|
-
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
|
+
},
|
|
19047
19214
|
execute: (env) => {
|
|
19048
19215
|
const locale = env.model.getters.getLocale();
|
|
19049
19216
|
setFormatter(env, locale.dateFormat + " " + locale.timeFormat);
|
|
@@ -19115,7 +19282,7 @@ const formatAlignment = {
|
|
|
19115
19282
|
};
|
|
19116
19283
|
const formatAlignmentHorizontal = {
|
|
19117
19284
|
name: _lt("Horizontal align"),
|
|
19118
|
-
icon:
|
|
19285
|
+
icon: (env) => getHorizontalAlignmentIcon(env),
|
|
19119
19286
|
};
|
|
19120
19287
|
const formatAlignmentLeft = {
|
|
19121
19288
|
name: _lt("Left"),
|
|
@@ -19140,46 +19307,50 @@ const formatAlignmentRight = {
|
|
|
19140
19307
|
};
|
|
19141
19308
|
const formatAlignmentVertical = {
|
|
19142
19309
|
name: _lt("Vertical align"),
|
|
19143
|
-
icon:
|
|
19310
|
+
icon: (env) => getVerticalAlignmentIcon(env),
|
|
19144
19311
|
};
|
|
19145
19312
|
const formatAlignmentTop = {
|
|
19146
19313
|
name: _lt("Top"),
|
|
19147
19314
|
execute: (env) => setStyle(env, { verticalAlign: "top" }),
|
|
19148
|
-
isActive: (env) => (env
|
|
19315
|
+
isActive: (env) => getVerticalAlign(env) === "top",
|
|
19149
19316
|
icon: "o-spreadsheet-Icon.ALIGN_TOP",
|
|
19150
19317
|
};
|
|
19151
19318
|
const formatAlignmentMiddle = {
|
|
19152
19319
|
name: _lt("Middle"),
|
|
19153
19320
|
execute: (env) => setStyle(env, { verticalAlign: "middle" }),
|
|
19154
|
-
isActive: (env) => (env
|
|
19321
|
+
isActive: (env) => getVerticalAlign(env) === "middle",
|
|
19155
19322
|
icon: "o-spreadsheet-Icon.ALIGN_MIDDLE",
|
|
19156
19323
|
};
|
|
19157
19324
|
const formatAlignmentBottom = {
|
|
19158
19325
|
name: _lt("Bottom"),
|
|
19159
19326
|
execute: (env) => setStyle(env, { verticalAlign: "bottom" }),
|
|
19160
|
-
isActive: (env) => (env
|
|
19327
|
+
isActive: (env) => getVerticalAlign(env) === "bottom",
|
|
19161
19328
|
icon: "o-spreadsheet-Icon.ALIGN_BOTTOM",
|
|
19162
19329
|
};
|
|
19163
|
-
const
|
|
19330
|
+
const formatWrappingIcon = {
|
|
19164
19331
|
name: _lt("Wrapping"),
|
|
19165
19332
|
icon: "o-spreadsheet-Icon.WRAPPING_OVERFLOW",
|
|
19166
19333
|
};
|
|
19334
|
+
const formatWrapping = {
|
|
19335
|
+
name: _lt("Wrapping"),
|
|
19336
|
+
icon: (env) => getWrapModeIcon(env),
|
|
19337
|
+
};
|
|
19167
19338
|
const formatWrappingOverflow = {
|
|
19168
19339
|
name: _lt("Overflow"),
|
|
19169
19340
|
execute: (env) => setStyle(env, { wrapping: "overflow" }),
|
|
19170
|
-
isActive: (env) => (env
|
|
19341
|
+
isActive: (env) => getWrappingMode(env) === "overflow",
|
|
19171
19342
|
icon: "o-spreadsheet-Icon.WRAPPING_OVERFLOW",
|
|
19172
19343
|
};
|
|
19173
19344
|
const formatWrappingWrap = {
|
|
19174
19345
|
name: _lt("Wrap"),
|
|
19175
19346
|
execute: (env) => setStyle(env, { wrapping: "wrap" }),
|
|
19176
|
-
isActive: (env) => env
|
|
19347
|
+
isActive: (env) => getWrappingMode(env) === "wrap",
|
|
19177
19348
|
icon: "o-spreadsheet-Icon.WRAPPING_WRAP",
|
|
19178
19349
|
};
|
|
19179
19350
|
const formatWrappingClip = {
|
|
19180
19351
|
name: _lt("Clip"),
|
|
19181
19352
|
execute: (env) => setStyle(env, { wrapping: "clip" }),
|
|
19182
|
-
isActive: (env) => env
|
|
19353
|
+
isActive: (env) => getWrappingMode(env) === "clip",
|
|
19183
19354
|
icon: "o-spreadsheet-Icon.WRAPPING_CLIP",
|
|
19184
19355
|
};
|
|
19185
19356
|
const textColor = {
|
|
@@ -19195,14 +19366,6 @@ const formatCF = {
|
|
|
19195
19366
|
execute: OPEN_CF_SIDEPANEL_ACTION,
|
|
19196
19367
|
icon: "o-spreadsheet-Icon.CONDITIONAL_FORMAT",
|
|
19197
19368
|
};
|
|
19198
|
-
const paintFormat = {
|
|
19199
|
-
name: _lt("Paint Format"),
|
|
19200
|
-
execute: (env) => env.model.dispatch("ACTIVATE_PAINT_FORMAT", {
|
|
19201
|
-
target: env.model.getters.getSelectedZones(),
|
|
19202
|
-
}),
|
|
19203
|
-
icon: "o-spreadsheet-Icon.PAINT_FORMAT",
|
|
19204
|
-
isActive: (env) => env.model.getters.isPaintingFormat(),
|
|
19205
|
-
};
|
|
19206
19369
|
const clearFormat = {
|
|
19207
19370
|
name: _lt("Clear formatting"),
|
|
19208
19371
|
description: "Ctrl+<",
|
|
@@ -19242,6 +19405,53 @@ function getHorizontalAlign(env) {
|
|
|
19242
19405
|
}
|
|
19243
19406
|
const cell = env.model.getters.getActiveCell();
|
|
19244
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
|
+
}
|
|
19245
19455
|
}
|
|
19246
19456
|
|
|
19247
19457
|
var ACTION_FORMAT = /*#__PURE__*/Object.freeze({
|
|
@@ -19273,6 +19483,7 @@ var ACTION_FORMAT = /*#__PURE__*/Object.freeze({
|
|
|
19273
19483
|
formatAlignmentTop: formatAlignmentTop,
|
|
19274
19484
|
formatAlignmentMiddle: formatAlignmentMiddle,
|
|
19275
19485
|
formatAlignmentBottom: formatAlignmentBottom,
|
|
19486
|
+
formatWrappingIcon: formatWrappingIcon,
|
|
19276
19487
|
formatWrapping: formatWrapping,
|
|
19277
19488
|
formatWrappingOverflow: formatWrappingOverflow,
|
|
19278
19489
|
formatWrappingWrap: formatWrappingWrap,
|
|
@@ -19280,7 +19491,6 @@ var ACTION_FORMAT = /*#__PURE__*/Object.freeze({
|
|
|
19280
19491
|
textColor: textColor,
|
|
19281
19492
|
fillColor: fillColor,
|
|
19282
19493
|
formatCF: formatCF,
|
|
19283
|
-
paintFormat: paintFormat,
|
|
19284
19494
|
clearFormat: clearFormat
|
|
19285
19495
|
});
|
|
19286
19496
|
|
|
@@ -20043,7 +20253,7 @@ topbarMenuRegistry
|
|
|
20043
20253
|
separator: true,
|
|
20044
20254
|
})
|
|
20045
20255
|
.addChild("format_wrapping", ["format"], {
|
|
20046
|
-
...
|
|
20256
|
+
...formatWrappingIcon,
|
|
20047
20257
|
sequence: 80,
|
|
20048
20258
|
separator: true,
|
|
20049
20259
|
})
|
|
@@ -22333,17 +22543,45 @@ class CustomCurrencyPanel extends owl.Component {
|
|
|
22333
22543
|
}
|
|
22334
22544
|
get formatProposals() {
|
|
22335
22545
|
const currency = this.availableCurrencies[this.state.selectedCurrencyIndex];
|
|
22336
|
-
const
|
|
22337
|
-
const
|
|
22338
|
-
const secondPosition = currency.position === "before" ? "after" : "before";
|
|
22546
|
+
const position = currency.position;
|
|
22547
|
+
const opposite = currency.position === "before" ? "after" : "before";
|
|
22339
22548
|
const symbol = this.state.currencySymbol.trim() ? this.state.currencySymbol : "";
|
|
22340
22549
|
const code = this.state.currencyCode.trim() ? this.state.currencyCode : "";
|
|
22341
|
-
|
|
22342
|
-
|
|
22343
|
-
|
|
22344
|
-
|
|
22345
|
-
|
|
22346
|
-
|
|
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
|
+
}));
|
|
22347
22585
|
}
|
|
22348
22586
|
get isSameFormat() {
|
|
22349
22587
|
const selectedFormat = this.formatProposals[this.state.selectedFormatIndex];
|
|
@@ -22400,41 +22638,6 @@ class CustomCurrencyPanel extends owl.Component {
|
|
|
22400
22638
|
initAvailableCurrencies() {
|
|
22401
22639
|
this.state.selectedCurrencyIndex = 0;
|
|
22402
22640
|
}
|
|
22403
|
-
initProposalBases(decimalPlaces) {
|
|
22404
|
-
const result = [{ format: "#,##0", example: "1,000" }];
|
|
22405
|
-
const decimalRepresentation = decimalPlaces ? "." + "0".repeat(decimalPlaces) : "";
|
|
22406
|
-
if (decimalRepresentation) {
|
|
22407
|
-
result.push({
|
|
22408
|
-
format: "#,##0" + decimalRepresentation,
|
|
22409
|
-
example: "1,000" + decimalRepresentation,
|
|
22410
|
-
});
|
|
22411
|
-
}
|
|
22412
|
-
return result;
|
|
22413
|
-
}
|
|
22414
|
-
createFormatProposals(proposalBases, symbol, code, position) {
|
|
22415
|
-
let formatProposals = [];
|
|
22416
|
-
// 1 - add proposal with symbol and without code
|
|
22417
|
-
if (symbol) {
|
|
22418
|
-
for (let base of proposalBases) {
|
|
22419
|
-
formatProposals.push(this.createFormatProposal(position, base.example, base.format, symbol));
|
|
22420
|
-
}
|
|
22421
|
-
}
|
|
22422
|
-
// 2 - if code exist --> add more proposal with symbol and with code
|
|
22423
|
-
if (code) {
|
|
22424
|
-
for (let base of proposalBases) {
|
|
22425
|
-
const expression = (position === "after" ? " " : "") + code + " " + symbol;
|
|
22426
|
-
formatProposals.push(this.createFormatProposal(position, base.example, base.format, expression));
|
|
22427
|
-
}
|
|
22428
|
-
}
|
|
22429
|
-
return formatProposals;
|
|
22430
|
-
}
|
|
22431
|
-
createFormatProposal(position, baseExample, formatBase, expression) {
|
|
22432
|
-
const formatExpression = "[$" + expression + "]";
|
|
22433
|
-
return {
|
|
22434
|
-
example: position === "before" ? expression + baseExample : baseExample + expression,
|
|
22435
|
-
format: position === "before" ? formatExpression + formatBase : formatBase + formatExpression,
|
|
22436
|
-
};
|
|
22437
|
-
}
|
|
22438
22641
|
getCommonFormat() {
|
|
22439
22642
|
const selectedZones = this.env.model.getters.getSelectedZones();
|
|
22440
22643
|
const sheetId = this.env.model.getters.getActiveSheetId();
|
|
@@ -23237,7 +23440,7 @@ class ContentEditableHelper {
|
|
|
23237
23440
|
selection.removeAllRanges();
|
|
23238
23441
|
selection.addRange(range);
|
|
23239
23442
|
}
|
|
23240
|
-
if (start
|
|
23443
|
+
if (start === end && start === 0) {
|
|
23241
23444
|
range.setStart(this.el, 0);
|
|
23242
23445
|
range.setEnd(this.el, 0);
|
|
23243
23446
|
}
|
|
@@ -23490,7 +23693,7 @@ class ContentEditableHelper {
|
|
|
23490
23693
|
}, 0);
|
|
23491
23694
|
}
|
|
23492
23695
|
}
|
|
23493
|
-
if (nodeToFind.nodeName === "P" && !isFirstParagraph && nodeToFind.textContent
|
|
23696
|
+
if (nodeToFind.nodeName === "P" && !isFirstParagraph && nodeToFind.textContent === "") {
|
|
23494
23697
|
usedCharacters++;
|
|
23495
23698
|
}
|
|
23496
23699
|
return usedCharacters;
|
|
@@ -25037,6 +25240,14 @@ function useInterval(callback, delay) {
|
|
|
25037
25240
|
};
|
|
25038
25241
|
}
|
|
25039
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
|
+
`;
|
|
25040
25251
|
function useCellHovered(env, gridRef, callback) {
|
|
25041
25252
|
let hoveredPosition = {
|
|
25042
25253
|
col: undefined,
|
|
@@ -25160,6 +25371,12 @@ class GridOverlay extends owl.Component {
|
|
|
25160
25371
|
}
|
|
25161
25372
|
return this.gridOverlay.el;
|
|
25162
25373
|
}
|
|
25374
|
+
get style() {
|
|
25375
|
+
return this.props.gridOverlayDimensions;
|
|
25376
|
+
}
|
|
25377
|
+
get isPaintingFormat() {
|
|
25378
|
+
return this.env.model.getters.isPaintingFormat();
|
|
25379
|
+
}
|
|
25163
25380
|
onMouseDown(ev) {
|
|
25164
25381
|
if (ev.button > 0) {
|
|
25165
25382
|
// not main button, probably a context menu
|
|
@@ -26337,6 +26554,9 @@ class Grid extends owl.Component {
|
|
|
26337
26554
|
else if (this.menuState.isOpen) {
|
|
26338
26555
|
this.closeMenu();
|
|
26339
26556
|
}
|
|
26557
|
+
else if (this.env.model.getters.isPaintingFormat()) {
|
|
26558
|
+
this.env.model.dispatch("CANCEL_PAINT_FORMAT");
|
|
26559
|
+
}
|
|
26340
26560
|
else {
|
|
26341
26561
|
this.env.model.dispatch("CLEAN_CLIPBOARD_HIGHLIGHT");
|
|
26342
26562
|
}
|
|
@@ -27539,8 +27759,8 @@ function convertXlsxFormat(numFmtId, formats, warningManager) {
|
|
|
27539
27759
|
throw new Error("Multiple escaped blocks in format");
|
|
27540
27760
|
}
|
|
27541
27761
|
convertedFormat = convertedFormat.replace(/"(.*)"/g, "[$$$1]"); // replace '"..."' by '[$...]'
|
|
27542
|
-
convertedFormat = convertedFormat.replace(/_.{1}/g, ""); // _
|
|
27543
|
-
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
|
|
27544
27764
|
convertedFormat = convertedFormat.replace(/\\ /g, " "); // unescape spaces
|
|
27545
27765
|
convertedFormat = convertedFormat.replace(/\\./g, (match) => match[1]); // unescape other characters
|
|
27546
27766
|
if (isXlsxDateFormat(convertedFormat)) {
|
|
@@ -28832,29 +29052,26 @@ function escapeXml(strings, ...expressions) {
|
|
|
28832
29052
|
return new XMLString(concat(str));
|
|
28833
29053
|
}
|
|
28834
29054
|
/**
|
|
28835
|
-
* Removes the namespace of all the xml tags in the string.
|
|
29055
|
+
* Removes the escaped namespace of all the xml tags in the string.
|
|
28836
29056
|
*
|
|
28837
|
-
* Eg. : "
|
|
29057
|
+
* Eg. : "NAMESPACEnsNAMESPACEtest a" => "test a"
|
|
28838
29058
|
*/
|
|
28839
|
-
function
|
|
28840
|
-
return
|
|
29059
|
+
function removeTagEscapedNamespaces(tag) {
|
|
29060
|
+
return tag.replace(/NAMESPACE.*NAMESPACE(.*)/, "$1");
|
|
28841
29061
|
}
|
|
28842
29062
|
/**
|
|
28843
|
-
*
|
|
29063
|
+
* Encase the namespaces in the element's tags with NAMESPACE string
|
|
28844
29064
|
*
|
|
28845
|
-
*
|
|
28846
|
-
*/
|
|
28847
|
-
function escapeNamespaces(query) {
|
|
28848
|
-
return query.replace(/([a-z0-9]+):(?=[a-z0-9]+)/gi, "$1\\:");
|
|
28849
|
-
}
|
|
28850
|
-
/**
|
|
28851
|
-
* Return true if the querySelector ignores the namespaces when searching for a tag in the DOM.
|
|
29065
|
+
* e.g. <x:foo> becomes <NAMESPACExNAMESPACEFoo>
|
|
28852
29066
|
*
|
|
28853
|
-
*
|
|
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.
|
|
28854
29069
|
*/
|
|
28855
|
-
function
|
|
28856
|
-
|
|
28857
|
-
|
|
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");
|
|
28858
29075
|
}
|
|
28859
29076
|
|
|
28860
29077
|
class AttributeValue {
|
|
@@ -28884,14 +29101,20 @@ class XlsxBaseExtractor {
|
|
|
28884
29101
|
// The xml file we are currently parsing. We should have one Extractor class by XLSXImportFile, but
|
|
28885
29102
|
// the XLSXImportFile contains both the main .xml file, and the .rels file
|
|
28886
29103
|
currentFile = undefined;
|
|
28887
|
-
|
|
28888
|
-
|
|
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
|
+
*/
|
|
28889
29113
|
constructor(rootFile, xlsxStructure, warningManager) {
|
|
28890
29114
|
this.rootFile = rootFile;
|
|
28891
29115
|
this.currentFile = rootFile.file.fileName;
|
|
28892
29116
|
this.xlsxFileStructure = xlsxStructure;
|
|
28893
29117
|
this.warningManager = warningManager;
|
|
28894
|
-
this.areNamespaceIgnored = areNamespaceIgnoredByQuerySelector();
|
|
28895
29118
|
this.relationships = {};
|
|
28896
29119
|
if (rootFile.rels) {
|
|
28897
29120
|
this.extractRelationships(rootFile.rels).map((rel) => {
|
|
@@ -29117,29 +29340,13 @@ class XlsxBaseExtractor {
|
|
|
29117
29340
|
throw new Error("Cannot find target file");
|
|
29118
29341
|
return f;
|
|
29119
29342
|
}
|
|
29120
|
-
/**
|
|
29121
|
-
* Wrapper of querySelector, but we'll remove the namespaces from the query if areNamespacesIgnored is true.
|
|
29122
|
-
*
|
|
29123
|
-
* Why we need to do this :
|
|
29124
|
-
* - For an XML "<t:test />"
|
|
29125
|
-
* - on Jest(jsdom) : xml.querySelector("test") == null, xml.querySelector("t\\:test") == <t:test />
|
|
29126
|
-
* - on Browser : xml.querySelector("test") == <t:test />, xml.querySelector("t\\:test") == null
|
|
29127
|
-
*/
|
|
29128
29343
|
querySelector(element, query) {
|
|
29129
|
-
|
|
29130
|
-
return element.querySelector(
|
|
29344
|
+
const escapedQuery = escapeQueryNameSpaces(query);
|
|
29345
|
+
return element.querySelector(escapedQuery);
|
|
29131
29346
|
}
|
|
29132
|
-
/**
|
|
29133
|
-
* Wrapper of querySelectorAll, but we'll remove the namespaces from the query if areNamespacesIgnored is true.
|
|
29134
|
-
*
|
|
29135
|
-
* Why we need to do this :
|
|
29136
|
-
* - For an XML "<t:test />"
|
|
29137
|
-
* - on Jest(jsdom) : xml.querySelectorAll("test") == [], xml.querySelectorAll("t\\:test") == [<t:test />]
|
|
29138
|
-
* - on Browser : xml.querySelectorAll("test") == [<t:test />], xml.querySelectorAll("t\\:test") == []
|
|
29139
|
-
*/
|
|
29140
29347
|
querySelectorAll(element, query) {
|
|
29141
|
-
|
|
29142
|
-
return element.querySelectorAll(
|
|
29348
|
+
const escapedQuery = escapeQueryNameSpaces(query);
|
|
29349
|
+
return element.querySelectorAll(escapedQuery);
|
|
29143
29350
|
}
|
|
29144
29351
|
/**
|
|
29145
29352
|
* Get a color from its id in the Theme's colorScheme.
|
|
@@ -29379,7 +29586,7 @@ class XlsxChartExtractor extends XlsxBaseExtractor {
|
|
|
29379
29586
|
throw new Error("Missing plot area in the chart definition.");
|
|
29380
29587
|
}
|
|
29381
29588
|
for (let child of plotAreaElement.children) {
|
|
29382
|
-
const tag =
|
|
29589
|
+
const tag = removeTagEscapedNamespaces(child.tagName);
|
|
29383
29590
|
if (XLSX_CHART_TYPES.some((chartType) => chartType === tag)) {
|
|
29384
29591
|
return tag;
|
|
29385
29592
|
}
|
|
@@ -29391,7 +29598,7 @@ class XlsxChartExtractor extends XlsxBaseExtractor {
|
|
|
29391
29598
|
class XlsxFigureExtractor extends XlsxBaseExtractor {
|
|
29392
29599
|
extractFigures() {
|
|
29393
29600
|
return this.mapOnElements({ parent: this.rootFile.file.xml, query: "xdr:wsDr", children: true }, (figureElement) => {
|
|
29394
|
-
const anchorType =
|
|
29601
|
+
const anchorType = removeTagEscapedNamespaces(figureElement.tagName);
|
|
29395
29602
|
if (anchorType !== "twoCellAnchor") {
|
|
29396
29603
|
throw new Error("Only twoCellAnchor are supported for xlsx drawings.");
|
|
29397
29604
|
}
|
|
@@ -30025,7 +30232,8 @@ class XlsxReader {
|
|
|
30025
30232
|
for (let key of Object.keys(files)) {
|
|
30026
30233
|
// Random files can be in xlsx (like a bin file for printer settings)
|
|
30027
30234
|
if (key.endsWith(".xml") || key.endsWith(".rels")) {
|
|
30028
|
-
|
|
30235
|
+
const contentString = escapeTagNamespaces(files[key]);
|
|
30236
|
+
this.xmls[key] = parseXML(new XMLString(contentString));
|
|
30029
30237
|
}
|
|
30030
30238
|
else if (key.includes("media/image")) {
|
|
30031
30239
|
this.images.push({
|
|
@@ -31092,7 +31300,7 @@ class BordersPlugin extends CorePlugin {
|
|
|
31092
31300
|
}
|
|
31093
31301
|
/**
|
|
31094
31302
|
* Set the borders of a cell.
|
|
31095
|
-
* It overrides the current border if override
|
|
31303
|
+
* It overrides the current border if override === true.
|
|
31096
31304
|
*/
|
|
31097
31305
|
setBorder(sheetId, col, row, border, override = true) {
|
|
31098
31306
|
if (override || !this.borders?.[sheetId]?.[col]?.[row]?.vertical) {
|
|
@@ -32362,7 +32570,9 @@ class FigurePlugin extends CorePlugin {
|
|
|
32362
32570
|
const numHeader = this.getters.getNumberRows(sheetId);
|
|
32363
32571
|
let gridHeight = 0;
|
|
32364
32572
|
for (let i = 0; i < numHeader; i++) {
|
|
32365
|
-
|
|
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;
|
|
32366
32576
|
}
|
|
32367
32577
|
const figures = this.getters.getFigures(sheetId);
|
|
32368
32578
|
for (const figure of figures) {
|
|
@@ -32788,33 +32998,15 @@ class FiltersPlugin extends CorePlugin {
|
|
|
32788
32998
|
}
|
|
32789
32999
|
|
|
32790
33000
|
class HeaderSizePlugin extends CorePlugin {
|
|
32791
|
-
static getters = ["
|
|
33001
|
+
static getters = ["getUserRowSize", "getColSize"];
|
|
32792
33002
|
sizes = {};
|
|
32793
33003
|
handle(cmd) {
|
|
32794
33004
|
switch (cmd.type) {
|
|
32795
33005
|
case "CREATE_SHEET": {
|
|
32796
|
-
|
|
32797
|
-
const sizes = {
|
|
32798
|
-
COL: computedSizes.COL.map((size) => ({
|
|
32799
|
-
manualSize: undefined,
|
|
32800
|
-
computedSize: lazy(size),
|
|
32801
|
-
})),
|
|
32802
|
-
ROW: computedSizes.ROW.map((size) => ({
|
|
32803
|
-
manualSize: undefined,
|
|
32804
|
-
computedSize: lazy(size),
|
|
32805
|
-
})),
|
|
32806
|
-
};
|
|
32807
|
-
this.history.update("sizes", cmd.sheetId, sizes);
|
|
33006
|
+
this.history.update("sizes", cmd.sheetId, { COL: [], ROW: [] });
|
|
32808
33007
|
break;
|
|
32809
33008
|
}
|
|
32810
33009
|
case "DUPLICATE_SHEET":
|
|
32811
|
-
// make sure the values are computed in case the original sheet is deleted
|
|
32812
|
-
for (const row of this.sizes[cmd.sheetId].ROW) {
|
|
32813
|
-
row.computedSize();
|
|
32814
|
-
}
|
|
32815
|
-
for (const col of this.sizes[cmd.sheetId].COL) {
|
|
32816
|
-
col.computedSize();
|
|
32817
|
-
}
|
|
32818
33010
|
this.history.update("sizes", cmd.sheetIdTo, deepCopy(this.sizes[cmd.sheetId]));
|
|
32819
33011
|
break;
|
|
32820
33012
|
case "DELETE_SHEET":
|
|
@@ -32823,21 +33015,8 @@ class HeaderSizePlugin extends CorePlugin {
|
|
|
32823
33015
|
this.history.update("sizes", sizes);
|
|
32824
33016
|
break;
|
|
32825
33017
|
case "REMOVE_COLUMNS_ROWS": {
|
|
32826
|
-
|
|
32827
|
-
|
|
32828
|
-
sizes.splice(headerIndex, 1);
|
|
32829
|
-
}
|
|
32830
|
-
const min = Math.min(...cmd.elements);
|
|
32831
|
-
sizes = sizes.map((size, row) => {
|
|
32832
|
-
if (cmd.dimension === "ROW" && row >= min) {
|
|
32833
|
-
// invalidate sizes
|
|
32834
|
-
return {
|
|
32835
|
-
manualSize: size.manualSize,
|
|
32836
|
-
computedSize: lazy(() => this.getRowTallestCellSize(cmd.sheetId, row)),
|
|
32837
|
-
};
|
|
32838
|
-
}
|
|
32839
|
-
return size;
|
|
32840
|
-
});
|
|
33018
|
+
const arr = this.sizes[cmd.sheetId][cmd.dimension];
|
|
33019
|
+
const sizes = removeIndexesFromArray(arr, cmd.elements);
|
|
32841
33020
|
this.history.update("sizes", cmd.sheetId, cmd.dimension, sizes);
|
|
32842
33021
|
break;
|
|
32843
33022
|
}
|
|
@@ -32846,51 +33025,18 @@ class HeaderSizePlugin extends CorePlugin {
|
|
|
32846
33025
|
const addIndex = getAddHeaderStartIndex(cmd.position, cmd.base);
|
|
32847
33026
|
const baseSize = sizes[cmd.base];
|
|
32848
33027
|
sizes.splice(addIndex, 0, ...Array(cmd.quantity).fill(baseSize));
|
|
32849
|
-
sizes = sizes.map((size, row) => {
|
|
32850
|
-
if (cmd.dimension === "ROW" && row > cmd.base + cmd.quantity) {
|
|
32851
|
-
// invalidate sizes
|
|
32852
|
-
return {
|
|
32853
|
-
manualSize: size.manualSize,
|
|
32854
|
-
computedSize: lazy(() => this.getRowTallestCellSize(cmd.sheetId, row)),
|
|
32855
|
-
};
|
|
32856
|
-
}
|
|
32857
|
-
return size;
|
|
32858
|
-
});
|
|
32859
33028
|
this.history.update("sizes", cmd.sheetId, cmd.dimension, sizes);
|
|
32860
33029
|
break;
|
|
32861
33030
|
}
|
|
32862
33031
|
case "RESIZE_COLUMNS_ROWS":
|
|
32863
|
-
|
|
32864
|
-
|
|
32865
|
-
|
|
32866
|
-
const size = height;
|
|
32867
|
-
this.history.update("sizes", cmd.sheetId, cmd.dimension, el, {
|
|
32868
|
-
manualSize: cmd.size || undefined,
|
|
32869
|
-
computedSize: lazy(size),
|
|
32870
|
-
});
|
|
32871
|
-
}
|
|
32872
|
-
else {
|
|
32873
|
-
this.history.update("sizes", cmd.sheetId, cmd.dimension, el, {
|
|
32874
|
-
manualSize: cmd.size || undefined,
|
|
32875
|
-
computedSize: lazy(cmd.size || DEFAULT_CELL_WIDTH),
|
|
32876
|
-
});
|
|
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);
|
|
32877
33035
|
}
|
|
32878
33036
|
}
|
|
32879
|
-
|
|
32880
|
-
|
|
32881
|
-
|
|
32882
|
-
const { sheetId, row } = cmd;
|
|
32883
|
-
this.history.update("sizes", sheetId, "ROW", row, "computedSize", lazy(() => this.getRowTallestCellSize(sheetId, row)));
|
|
32884
|
-
}
|
|
32885
|
-
break;
|
|
32886
|
-
case "ADD_MERGE":
|
|
32887
|
-
case "REMOVE_MERGE":
|
|
32888
|
-
for (let target of cmd.target) {
|
|
32889
|
-
for (let row of range(target.top, target.bottom + 1)) {
|
|
32890
|
-
const rowHeight = this.getRowTallestCellSize(cmd.sheetId, row);
|
|
32891
|
-
if (rowHeight !== this.getRowSize(cmd.sheetId, row)) {
|
|
32892
|
-
this.history.update("sizes", cmd.sheetId, "ROW", row, "computedSize", lazy(rowHeight));
|
|
32893
|
-
}
|
|
33037
|
+
else {
|
|
33038
|
+
for (const el of cmd.elements) {
|
|
33039
|
+
this.history.update("sizes", cmd.sheetId, cmd.dimension, el, cmd.size || undefined);
|
|
32894
33040
|
}
|
|
32895
33041
|
}
|
|
32896
33042
|
break;
|
|
@@ -32898,94 +33044,29 @@ class HeaderSizePlugin extends CorePlugin {
|
|
|
32898
33044
|
return;
|
|
32899
33045
|
}
|
|
32900
33046
|
getColSize(sheetId, index) {
|
|
32901
|
-
return this.
|
|
32902
|
-
}
|
|
32903
|
-
getRowSize(sheetId, index) {
|
|
32904
|
-
return this.getHeaderSize(sheetId, "ROW", index);
|
|
33047
|
+
return Math.round(this.sizes[sheetId]?.["COL"][index] || DEFAULT_CELL_WIDTH);
|
|
32905
33048
|
}
|
|
32906
|
-
|
|
32907
|
-
|
|
32908
|
-
|
|
32909
|
-
this.getDefaultHeaderSize(dimension));
|
|
32910
|
-
}
|
|
32911
|
-
computeSheetSizes(sheetId) {
|
|
32912
|
-
const sizes = { COL: [], ROW: [] };
|
|
32913
|
-
for (let col of range(0, this.getters.getNumberCols(sheetId))) {
|
|
32914
|
-
sizes.COL.push(this.getHeaderSize(sheetId, "COL", col));
|
|
32915
|
-
}
|
|
32916
|
-
for (let row of range(0, this.getters.getNumberRows(sheetId))) {
|
|
32917
|
-
let rowSize = this.sizes[sheetId]?.["ROW"]?.[row].manualSize;
|
|
32918
|
-
if (!rowSize) {
|
|
32919
|
-
const height = this.getRowTallestCellSize(sheetId, row);
|
|
32920
|
-
rowSize = height;
|
|
32921
|
-
}
|
|
32922
|
-
sizes.ROW.push(rowSize);
|
|
32923
|
-
}
|
|
32924
|
-
return sizes;
|
|
32925
|
-
}
|
|
32926
|
-
getDefaultHeaderSize(dimension) {
|
|
32927
|
-
return dimension === "COL" ? DEFAULT_CELL_WIDTH : DEFAULT_CELL_HEIGHT;
|
|
32928
|
-
}
|
|
32929
|
-
/**
|
|
32930
|
-
* Return the height the cell should have in the sheet, which is either DEFAULT_CELL_HEIGHT if the cell is in a multi-row
|
|
32931
|
-
* merge, or the height of the cell computed based on its font size.
|
|
32932
|
-
*/
|
|
32933
|
-
getCellHeight(position) {
|
|
32934
|
-
const merge = this.getters.getMerge(position);
|
|
32935
|
-
if (merge && merge.bottom !== merge.top) {
|
|
32936
|
-
return DEFAULT_CELL_HEIGHT;
|
|
32937
|
-
}
|
|
32938
|
-
const cell = this.getters.getCell(position);
|
|
32939
|
-
return getDefaultCellHeight(cell);
|
|
32940
|
-
}
|
|
32941
|
-
/**
|
|
32942
|
-
* Get the tallest cell of a row and its size.
|
|
32943
|
-
*
|
|
32944
|
-
* The tallest cell of the row correspond to the cell with the biggest font size,
|
|
32945
|
-
* and that is not part of a multi-line merge.
|
|
32946
|
-
*/
|
|
32947
|
-
getRowTallestCellSize(sheetId, row) {
|
|
32948
|
-
const cellIds = this.getters.getRowCells(sheetId, row);
|
|
32949
|
-
let maxHeight = 0;
|
|
32950
|
-
for (let i = 0; i < cellIds.length; i++) {
|
|
32951
|
-
const cell = this.getters.getCellById(cellIds[i]);
|
|
32952
|
-
if (!cell)
|
|
32953
|
-
continue;
|
|
32954
|
-
const position = this.getters.getCellPosition(cell.id);
|
|
32955
|
-
const cellHeight = this.getCellHeight(position);
|
|
32956
|
-
if (cellHeight > maxHeight && cellHeight > DEFAULT_CELL_HEIGHT) {
|
|
32957
|
-
maxHeight = cellHeight;
|
|
32958
|
-
}
|
|
32959
|
-
}
|
|
32960
|
-
if (maxHeight <= DEFAULT_CELL_HEIGHT) {
|
|
32961
|
-
return DEFAULT_CELL_HEIGHT;
|
|
32962
|
-
}
|
|
32963
|
-
return maxHeight;
|
|
33049
|
+
getUserRowSize(sheetId, index) {
|
|
33050
|
+
const rowSize = this.sizes[sheetId]?.["ROW"][index];
|
|
33051
|
+
return rowSize ? Math.round(rowSize) : undefined;
|
|
32964
33052
|
}
|
|
32965
33053
|
import(data) {
|
|
32966
33054
|
for (let sheet of data.sheets) {
|
|
32967
|
-
const
|
|
33055
|
+
const sizes = {
|
|
33056
|
+
COL: Array(sheet.colNumber).fill(undefined),
|
|
33057
|
+
ROW: Array(sheet.rowNumber).fill(undefined),
|
|
33058
|
+
};
|
|
32968
33059
|
for (let [rowIndex, row] of Object.entries(sheet.rows)) {
|
|
32969
33060
|
if (row.size) {
|
|
32970
|
-
|
|
33061
|
+
sizes["ROW"][rowIndex] = row.size;
|
|
32971
33062
|
}
|
|
32972
33063
|
}
|
|
32973
33064
|
for (let [colIndex, col] of Object.entries(sheet.cols)) {
|
|
32974
33065
|
if (col.size) {
|
|
32975
|
-
|
|
32976
|
-
}
|
|
32977
|
-
}
|
|
32978
|
-
|
|
32979
|
-
this.sizes[sheet.id] = {
|
|
32980
|
-
COL: computedSizes.COL.map((size, i) => ({
|
|
32981
|
-
manualSize: manualSizes.COL[i],
|
|
32982
|
-
computedSize: lazy(size),
|
|
32983
|
-
})),
|
|
32984
|
-
ROW: computedSizes.ROW.map((size, i) => ({
|
|
32985
|
-
manualSize: manualSizes.ROW[i],
|
|
32986
|
-
computedSize: lazy(size),
|
|
32987
|
-
})),
|
|
32988
|
-
};
|
|
33066
|
+
sizes["COL"][colIndex] = col.size;
|
|
33067
|
+
}
|
|
33068
|
+
}
|
|
33069
|
+
this.sizes[sheet.id] = sizes;
|
|
32989
33070
|
}
|
|
32990
33071
|
return;
|
|
32991
33072
|
}
|
|
@@ -33006,9 +33087,12 @@ class HeaderSizePlugin extends CorePlugin {
|
|
|
33006
33087
|
if (sheet.rows === undefined) {
|
|
33007
33088
|
sheet.rows = {};
|
|
33008
33089
|
}
|
|
33009
|
-
for (
|
|
33010
|
-
if (exportDefaults || this.sizes[sheet.id]["ROW"][row]
|
|
33011
|
-
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
|
+
};
|
|
33012
33096
|
}
|
|
33013
33097
|
}
|
|
33014
33098
|
// Export col sizes
|
|
@@ -33016,7 +33100,7 @@ class HeaderSizePlugin extends CorePlugin {
|
|
|
33016
33100
|
sheet.cols = {};
|
|
33017
33101
|
}
|
|
33018
33102
|
for (let col of range(0, this.getters.getNumberCols(sheet.id))) {
|
|
33019
|
-
if (exportDefaults || this.sizes[sheet.id]["COL"][col]
|
|
33103
|
+
if (exportDefaults || this.sizes[sheet.id]["COL"][col]) {
|
|
33020
33104
|
sheet.cols[col] = { ...sheet.cols[col], size: this.getColSize(sheet.id, col) };
|
|
33021
33105
|
}
|
|
33022
33106
|
}
|
|
@@ -36572,7 +36656,7 @@ class EvaluationConditionalFormatPlugin extends UIPlugin {
|
|
|
36572
36656
|
case "ContainsText":
|
|
36573
36657
|
return cell.value.toString().indexOf(values[0].toString()) > -1;
|
|
36574
36658
|
case "NotContains":
|
|
36575
|
-
return !cell.value || cell.value.toString().indexOf(values[0].toString())
|
|
36659
|
+
return !cell.value || cell.value.toString().indexOf(values[0].toString()) === -1;
|
|
36576
36660
|
case "GreaterThan":
|
|
36577
36661
|
return cell.value > values[0];
|
|
36578
36662
|
case "GreaterThanOrEqual":
|
|
@@ -36599,6 +36683,156 @@ class EvaluationConditionalFormatPlugin extends UIPlugin {
|
|
|
36599
36683
|
};
|
|
36600
36684
|
}
|
|
36601
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
|
+
|
|
36602
36836
|
/**
|
|
36603
36837
|
* This plugin manage the autofill.
|
|
36604
36838
|
*
|
|
@@ -40032,10 +40266,8 @@ class SortPlugin extends UIPlugin {
|
|
|
40032
40266
|
}
|
|
40033
40267
|
updateCellCommands.push(newCellValues);
|
|
40034
40268
|
}
|
|
40035
|
-
for (const cmd of updateCellCommands) {
|
|
40036
|
-
this.dispatch("UPDATE_CELL", cmd);
|
|
40037
|
-
}
|
|
40038
40269
|
}
|
|
40270
|
+
updateCellCommands.forEach((cmdPayload) => this.dispatch("UPDATE_CELL", cmdPayload));
|
|
40039
40271
|
}
|
|
40040
40272
|
/**
|
|
40041
40273
|
* Return the distances between main merge cells in the zone.
|
|
@@ -40136,24 +40368,28 @@ class SheetUIPlugin extends UIPlugin {
|
|
|
40136
40368
|
// Getters
|
|
40137
40369
|
// ---------------------------------------------------------------------------
|
|
40138
40370
|
getCellWidth(position) {
|
|
40139
|
-
const text = this.getCellText(position);
|
|
40140
40371
|
const style = this.getters.getCellComputedStyle(position);
|
|
40141
|
-
|
|
40142
|
-
|
|
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
|
+
}
|
|
40143
40378
|
const icon = this.getters.getConditionalIcon(position);
|
|
40144
40379
|
if (icon) {
|
|
40145
|
-
contentWidth += computeIconWidth(
|
|
40380
|
+
contentWidth += computeIconWidth(style);
|
|
40146
40381
|
}
|
|
40147
40382
|
const isFilterHeader = this.getters.isFilterHeader(position);
|
|
40148
40383
|
if (isFilterHeader) {
|
|
40149
40384
|
contentWidth += ICON_EDGE_LENGTH + FILTER_ICON_MARGIN;
|
|
40150
40385
|
}
|
|
40151
|
-
if (contentWidth
|
|
40152
|
-
|
|
40153
|
-
|
|
40154
|
-
|
|
40155
|
-
|
|
40156
|
-
|
|
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);
|
|
40157
40393
|
}
|
|
40158
40394
|
return contentWidth;
|
|
40159
40395
|
}
|
|
@@ -40176,56 +40412,7 @@ class SheetUIPlugin extends UIPlugin {
|
|
|
40176
40412
|
getCellMultiLineText(position, width) {
|
|
40177
40413
|
const style = this.getters.getCellStyle(position);
|
|
40178
40414
|
const text = this.getters.getCellText(position, this.getters.shouldShowFormulas());
|
|
40179
|
-
|
|
40180
|
-
for (const line of text.split("\n")) {
|
|
40181
|
-
const words = line.split(" ");
|
|
40182
|
-
if (!width) {
|
|
40183
|
-
brokenText.push(line);
|
|
40184
|
-
continue;
|
|
40185
|
-
}
|
|
40186
|
-
let textLine = "";
|
|
40187
|
-
let availableWidth = width;
|
|
40188
|
-
for (let word of words) {
|
|
40189
|
-
const splitWord = this.splitWordToSpecificWidth(this.ctx, word, width, style);
|
|
40190
|
-
const lastPart = splitWord.pop();
|
|
40191
|
-
const lastPartWidth = computeTextWidth(this.ctx, lastPart, style);
|
|
40192
|
-
// At this step: "splitWord" is an array composed of parts of word whose
|
|
40193
|
-
// length is at most equal to "width".
|
|
40194
|
-
// Last part contains the end of the word.
|
|
40195
|
-
// Note that: When word length is less than width, then lastPart is equal
|
|
40196
|
-
// to word and splitWord is empty
|
|
40197
|
-
if (splitWord.length) {
|
|
40198
|
-
if (textLine !== "") {
|
|
40199
|
-
brokenText.push(textLine);
|
|
40200
|
-
textLine = "";
|
|
40201
|
-
availableWidth = width;
|
|
40202
|
-
}
|
|
40203
|
-
splitWord.forEach((wordPart) => {
|
|
40204
|
-
brokenText.push(wordPart);
|
|
40205
|
-
});
|
|
40206
|
-
textLine = lastPart;
|
|
40207
|
-
availableWidth = width - lastPartWidth;
|
|
40208
|
-
}
|
|
40209
|
-
else {
|
|
40210
|
-
// here "lastPart" is equal to "word" and the "word" size is smaller than "width"
|
|
40211
|
-
const _word = textLine === "" ? lastPart : " " + lastPart;
|
|
40212
|
-
const wordWidth = computeTextWidth(this.ctx, _word, style);
|
|
40213
|
-
if (wordWidth <= availableWidth) {
|
|
40214
|
-
textLine += _word;
|
|
40215
|
-
availableWidth -= wordWidth;
|
|
40216
|
-
}
|
|
40217
|
-
else {
|
|
40218
|
-
brokenText.push(textLine);
|
|
40219
|
-
textLine = lastPart;
|
|
40220
|
-
availableWidth = width - lastPartWidth;
|
|
40221
|
-
}
|
|
40222
|
-
}
|
|
40223
|
-
}
|
|
40224
|
-
if (textLine !== "") {
|
|
40225
|
-
brokenText.push(textLine);
|
|
40226
|
-
}
|
|
40227
|
-
}
|
|
40228
|
-
return brokenText;
|
|
40415
|
+
return splitTextToWidth(this.ctx, text, style, width);
|
|
40229
40416
|
}
|
|
40230
40417
|
/**
|
|
40231
40418
|
* Returns the size, start and end coordinates of a column on an unfolded sheet
|
|
@@ -40284,26 +40471,6 @@ class SheetUIPlugin extends UIPlugin {
|
|
|
40284
40471
|
const sizes = cellsPositions.map((position) => this.getCellWidth({ sheetId, ...position }));
|
|
40285
40472
|
return Math.max(0, ...sizes);
|
|
40286
40473
|
}
|
|
40287
|
-
splitWordToSpecificWidth(ctx, word, width, style) {
|
|
40288
|
-
const wordWidth = computeTextWidth(ctx, word, style);
|
|
40289
|
-
if (wordWidth <= width) {
|
|
40290
|
-
return [word];
|
|
40291
|
-
}
|
|
40292
|
-
const splitWord = [];
|
|
40293
|
-
let wordPart = "";
|
|
40294
|
-
for (let l of word) {
|
|
40295
|
-
const wordPartWidth = computeTextWidth(ctx, wordPart + l, style);
|
|
40296
|
-
if (wordPartWidth > width) {
|
|
40297
|
-
splitWord.push(wordPart);
|
|
40298
|
-
wordPart = l;
|
|
40299
|
-
}
|
|
40300
|
-
else {
|
|
40301
|
-
wordPart += l;
|
|
40302
|
-
}
|
|
40303
|
-
}
|
|
40304
|
-
splitWord.push(wordPart);
|
|
40305
|
-
return splitWord;
|
|
40306
|
-
}
|
|
40307
40474
|
/**
|
|
40308
40475
|
* Check that any "sheetId" in the command matches an existing
|
|
40309
40476
|
* sheet.
|
|
@@ -41295,7 +41462,7 @@ class ClipboardCellsState extends ClipboardCellsAbstractState {
|
|
|
41295
41462
|
.join("\n") || "\t");
|
|
41296
41463
|
}
|
|
41297
41464
|
getHTMLContent() {
|
|
41298
|
-
if (this.cells.length
|
|
41465
|
+
if (this.cells.length === 1 && this.cells[0].length === 1) {
|
|
41299
41466
|
return this.getters.getCellText(this.cells[0][0].position);
|
|
41300
41467
|
}
|
|
41301
41468
|
let htmlTable = '<table border="1" style="border-collapse:collapse">';
|
|
@@ -41620,7 +41787,7 @@ class ClipboardPlugin extends UIPlugin {
|
|
|
41620
41787
|
status = "invisible";
|
|
41621
41788
|
state;
|
|
41622
41789
|
lastPasteState;
|
|
41623
|
-
|
|
41790
|
+
paintFormatStatus = "inactive";
|
|
41624
41791
|
originSheetId;
|
|
41625
41792
|
// ---------------------------------------------------------------------------
|
|
41626
41793
|
// Command Handling
|
|
@@ -41635,7 +41802,7 @@ class ClipboardPlugin extends UIPlugin {
|
|
|
41635
41802
|
if (!this.state) {
|
|
41636
41803
|
return 24 /* CommandResult.EmptyClipboard */;
|
|
41637
41804
|
}
|
|
41638
|
-
const pasteOption = cmd.pasteOption || (this.
|
|
41805
|
+
const pasteOption = cmd.pasteOption || (this.paintFormatStatus !== "inactive" ? "onlyFormat" : undefined);
|
|
41639
41806
|
return this.state.isPasteAllowed(cmd.target, { pasteOption });
|
|
41640
41807
|
case "PASTE_FROM_OS_CLIPBOARD": {
|
|
41641
41808
|
const state = new ClipboardOsState(cmd.text, this.getters, this.dispatch, this.selection);
|
|
@@ -41651,6 +41818,11 @@ class ClipboardPlugin extends UIPlugin {
|
|
|
41651
41818
|
const state = this.getClipboardStateForCopyCells(cut, "CUT");
|
|
41652
41819
|
return state.isPasteAllowed(paste);
|
|
41653
41820
|
}
|
|
41821
|
+
case "ACTIVATE_PAINT_FORMAT": {
|
|
41822
|
+
if (this.paintFormatStatus !== "inactive") {
|
|
41823
|
+
return 95 /* CommandResult.AlreadyInPaintingFormatMode */;
|
|
41824
|
+
}
|
|
41825
|
+
}
|
|
41654
41826
|
}
|
|
41655
41827
|
return 0 /* CommandResult.Success */;
|
|
41656
41828
|
}
|
|
@@ -41667,11 +41839,13 @@ class ClipboardPlugin extends UIPlugin {
|
|
|
41667
41839
|
if (!this.state) {
|
|
41668
41840
|
break;
|
|
41669
41841
|
}
|
|
41670
|
-
const pasteOption = cmd.pasteOption || (this.
|
|
41671
|
-
this._isPaintingFormat = false;
|
|
41842
|
+
const pasteOption = cmd.pasteOption || (this.paintFormatStatus !== "inactive" ? "onlyFormat" : undefined);
|
|
41672
41843
|
this.state.paste(cmd.target, { pasteOption, shouldPasteCF: true, selectTarget: true });
|
|
41673
41844
|
this.lastPasteState = this.state;
|
|
41674
|
-
this.
|
|
41845
|
+
if (this.paintFormatStatus === "oneOff") {
|
|
41846
|
+
this.paintFormatStatus = "inactive";
|
|
41847
|
+
this.status = "invisible";
|
|
41848
|
+
}
|
|
41675
41849
|
break;
|
|
41676
41850
|
case "CLEAN_CLIPBOARD_HIGHLIGHT":
|
|
41677
41851
|
this.status = "invisible";
|
|
@@ -41739,8 +41913,13 @@ class ClipboardPlugin extends UIPlugin {
|
|
|
41739
41913
|
case "ACTIVATE_PAINT_FORMAT": {
|
|
41740
41914
|
const zones = this.getters.getSelectedZones();
|
|
41741
41915
|
this.state = this.getClipboardStateForCopyCells(zones, "COPY");
|
|
41742
|
-
this._isPaintingFormat = true;
|
|
41743
41916
|
this.status = "visible";
|
|
41917
|
+
if (cmd.persistent) {
|
|
41918
|
+
this.paintFormatStatus = "persistent";
|
|
41919
|
+
}
|
|
41920
|
+
else {
|
|
41921
|
+
this.paintFormatStatus = "oneOff";
|
|
41922
|
+
}
|
|
41744
41923
|
break;
|
|
41745
41924
|
}
|
|
41746
41925
|
case "DELETE_SHEET":
|
|
@@ -41752,6 +41931,11 @@ class ClipboardPlugin extends UIPlugin {
|
|
|
41752
41931
|
this.status = "invisible";
|
|
41753
41932
|
}
|
|
41754
41933
|
break;
|
|
41934
|
+
case "CANCEL_PAINT_FORMAT": {
|
|
41935
|
+
this.paintFormatStatus = "inactive";
|
|
41936
|
+
this.status = "invisible";
|
|
41937
|
+
break;
|
|
41938
|
+
}
|
|
41755
41939
|
default:
|
|
41756
41940
|
if (isCoreCommand(cmd)) {
|
|
41757
41941
|
this.status = "invisible";
|
|
@@ -41782,7 +41966,7 @@ class ClipboardPlugin extends UIPlugin {
|
|
|
41782
41966
|
return this.state ? this.state.operation === "CUT" : false;
|
|
41783
41967
|
}
|
|
41784
41968
|
isPaintingFormat() {
|
|
41785
|
-
return this.
|
|
41969
|
+
return this.paintFormatStatus !== "inactive";
|
|
41786
41970
|
}
|
|
41787
41971
|
// ---------------------------------------------------------------------------
|
|
41788
41972
|
// Private methods
|
|
@@ -44417,6 +44601,7 @@ const coreViewsPluginRegistry = new Registry()
|
|
|
44417
44601
|
.add("evaluation", EvaluationPlugin)
|
|
44418
44602
|
.add("evaluation_chart", EvaluationChartPlugin)
|
|
44419
44603
|
.add("evaluation_cf", EvaluationConditionalFormatPlugin)
|
|
44604
|
+
.add("row_size", UIRowSizePlugin)
|
|
44420
44605
|
.add("custom_colors", CustomColorsPlugin);
|
|
44421
44606
|
|
|
44422
44607
|
const clickableCellRegistry = new Registry();
|
|
@@ -45672,11 +45857,11 @@ class ActionButton extends owl.Component {
|
|
|
45672
45857
|
}
|
|
45673
45858
|
get title() {
|
|
45674
45859
|
const name = this.actionButton.name(this.env);
|
|
45675
|
-
const description = this.actionButton.description;
|
|
45860
|
+
const description = this.actionButton.description(this.env);
|
|
45676
45861
|
return name + (description ? ` (${description})` : "");
|
|
45677
45862
|
}
|
|
45678
45863
|
get iconTitle() {
|
|
45679
|
-
return this.actionButton.icon;
|
|
45864
|
+
return this.actionButton.icon(this.env);
|
|
45680
45865
|
}
|
|
45681
45866
|
onClick(ev) {
|
|
45682
45867
|
if (this.isEnabled) {
|
|
@@ -46061,6 +46246,27 @@ FontSizeEditor.props = {
|
|
|
46061
46246
|
class: String,
|
|
46062
46247
|
};
|
|
46063
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
|
+
|
|
46064
46270
|
// -----------------------------------------------------------------------------
|
|
46065
46271
|
// TopBar
|
|
46066
46272
|
// -----------------------------------------------------------------------------
|
|
@@ -46161,6 +46367,7 @@ class TopBar extends owl.Component {
|
|
|
46161
46367
|
TopBarComposer,
|
|
46162
46368
|
FontSizeEditor,
|
|
46163
46369
|
ActionButton,
|
|
46370
|
+
PaintFormatButton,
|
|
46164
46371
|
BorderEditorWidget,
|
|
46165
46372
|
};
|
|
46166
46373
|
state = owl.useState({
|
|
@@ -48013,7 +48220,7 @@ class SelectionStreamProcessorImpl {
|
|
|
48013
48220
|
: "nextCluster";
|
|
48014
48221
|
while (true) {
|
|
48015
48222
|
const nextCellPosition = this.getNextCellPosition(currentPosition, dim, dir);
|
|
48016
|
-
// Break if nextPosition
|
|
48223
|
+
// Break if nextPosition === currentPosition, which happens if there's no next valid position
|
|
48017
48224
|
if (currentPosition.col === nextCellPosition.col &&
|
|
48018
48225
|
currentPosition.row === nextCellPosition.row) {
|
|
48019
48226
|
break;
|
|
@@ -49927,6 +50134,7 @@ class Model extends EventBus {
|
|
|
49927
50134
|
...config,
|
|
49928
50135
|
mode: config.mode || "normal",
|
|
49929
50136
|
custom: config.custom || {},
|
|
50137
|
+
defaultCurrencyFormat: config.defaultCurrencyFormat || "[$$]#,##0.00",
|
|
49930
50138
|
external: this.setupExternalConfig(config.external || {}),
|
|
49931
50139
|
transportService,
|
|
49932
50140
|
client,
|
|
@@ -50224,6 +50432,7 @@ const helpers = {
|
|
|
50224
50432
|
numberToLetters,
|
|
50225
50433
|
UuidGenerator,
|
|
50226
50434
|
formatValue,
|
|
50435
|
+
createCurrencyFormat,
|
|
50227
50436
|
computeTextWidth,
|
|
50228
50437
|
createEmptyWorkbookData,
|
|
50229
50438
|
createEmptySheet,
|
|
@@ -50307,6 +50516,6 @@ exports.setTranslationMethod = setTranslationMethod;
|
|
|
50307
50516
|
exports.tokenize = tokenize;
|
|
50308
50517
|
|
|
50309
50518
|
|
|
50310
|
-
__info__.version = '16.
|
|
50311
|
-
__info__.date = '2023-07-
|
|
50312
|
-
__info__.hash = '
|
|
50519
|
+
__info__.version = '16.5.0-alpha.0';
|
|
50520
|
+
__info__.date = '2023-07-24T06:48:58.196Z';
|
|
50521
|
+
__info__.hash = '3ba2c3c';
|