@odoo/o-spreadsheet 16.4.0-alpha.7 → 16.4.1
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 +712 -491
- package/dist/o-spreadsheet.d.ts +142 -113
- package/dist/o-spreadsheet.esm.js +712 -491
- package/dist/o-spreadsheet.iife.js +712 -491
- package/dist/o-spreadsheet.iife.min.js +324 -315
- package/package.json +12 -12
|
@@ -112,8 +112,8 @@ const SELECTION_BORDER_COLOR = "#3266ca";
|
|
|
112
112
|
const HEADER_BORDER_COLOR = "#C0C0C0";
|
|
113
113
|
const CELL_BORDER_COLOR = "#E2E3E3";
|
|
114
114
|
const BACKGROUND_CHART_COLOR = "#FFFFFF";
|
|
115
|
-
const MENU_ITEM_DISABLED_COLOR = "#CACACA";
|
|
116
115
|
const BG_HOVER_COLOR = "#EBEBEB";
|
|
116
|
+
const DISABLED_TEXT_COLOR = "#CACACA";
|
|
117
117
|
const DEFAULT_COLOR_SCALE_MIDPOINT_COLOR = 0xb6d7a8;
|
|
118
118
|
const LINK_COLOR = "#01666b";
|
|
119
119
|
const FILTERS_COLOR = "#188038";
|
|
@@ -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
|
}
|
|
@@ -3930,7 +4055,6 @@ const readonlyAllowedCommands = new Set([
|
|
|
3930
4055
|
"SET_VIEWPORT_OFFSET",
|
|
3931
4056
|
"SELECT_SEARCH_NEXT_MATCH",
|
|
3932
4057
|
"SELECT_SEARCH_PREVIOUS_MATCH",
|
|
3933
|
-
"REFRESH_SEARCH",
|
|
3934
4058
|
"UPDATE_SEARCH",
|
|
3935
4059
|
"CLEAR_SEARCH",
|
|
3936
4060
|
"EVALUATE_CELLS",
|
|
@@ -4128,6 +4252,7 @@ exports.CommandResult = void 0;
|
|
|
4128
4252
|
CommandResult[CommandResult["NoSplitSeparatorInSelection"] = 92] = "NoSplitSeparatorInSelection";
|
|
4129
4253
|
CommandResult[CommandResult["NoActiveSheet"] = 93] = "NoActiveSheet";
|
|
4130
4254
|
CommandResult[CommandResult["InvalidLocale"] = 94] = "InvalidLocale";
|
|
4255
|
+
CommandResult[CommandResult["AlreadyInPaintingFormatMode"] = 95] = "AlreadyInPaintingFormatMode";
|
|
4131
4256
|
})(exports.CommandResult || (exports.CommandResult = {}));
|
|
4132
4257
|
|
|
4133
4258
|
const borderStyles = ["thin", "medium", "thick", "dashed", "dotted"];
|
|
@@ -5868,7 +5993,7 @@ css /* scss */ `
|
|
|
5868
5993
|
}
|
|
5869
5994
|
}
|
|
5870
5995
|
&.disabled {
|
|
5871
|
-
color: ${
|
|
5996
|
+
color: ${DISABLED_TEXT_COLOR};
|
|
5872
5997
|
cursor: not-allowed;
|
|
5873
5998
|
}
|
|
5874
5999
|
}
|
|
@@ -5943,8 +6068,8 @@ class Menu extends owl.Component {
|
|
|
5943
6068
|
return this.props.menuItems.some((menuItem) => !!menuItem.icon || !!menuItem.isActive);
|
|
5944
6069
|
}
|
|
5945
6070
|
getIconName(menu) {
|
|
5946
|
-
if (menu.icon) {
|
|
5947
|
-
return menu.icon;
|
|
6071
|
+
if (menu.icon(this.env)) {
|
|
6072
|
+
return menu.icon(this.env);
|
|
5948
6073
|
}
|
|
5949
6074
|
if (menu.isActive?.(this.env)) {
|
|
5950
6075
|
return "o-spreadsheet-Icon.CHECK";
|
|
@@ -6378,7 +6503,7 @@ function invertMatrix(M) {
|
|
|
6378
6503
|
for (let pivot = 0; pivot < dim; pivot++) {
|
|
6379
6504
|
let diagonalElement = C[pivot][pivot];
|
|
6380
6505
|
// if we have a 0 on the diagonal we'll need to swap with a lower row
|
|
6381
|
-
if (diagonalElement
|
|
6506
|
+
if (diagonalElement === 0) {
|
|
6382
6507
|
//look through every row below the i'th row
|
|
6383
6508
|
for (let row = pivot + 1; row < dim; row++) {
|
|
6384
6509
|
//if the ii'th row has a non-0 in the i'th col, swap it with that row
|
|
@@ -6405,7 +6530,7 @@ function invertMatrix(M) {
|
|
|
6405
6530
|
// the other rows so that there will be 0's in this column in the
|
|
6406
6531
|
// rows above and below this one
|
|
6407
6532
|
for (let row = 0; row < dim; row++) {
|
|
6408
|
-
if (row
|
|
6533
|
+
if (row === pivot) {
|
|
6409
6534
|
continue;
|
|
6410
6535
|
}
|
|
6411
6536
|
// We want to change this element to 0
|
|
@@ -12470,7 +12595,7 @@ const ISNA = {
|
|
|
12470
12595
|
return false;
|
|
12471
12596
|
}
|
|
12472
12597
|
catch (e) {
|
|
12473
|
-
return e?.errorType
|
|
12598
|
+
return e?.errorType === CellErrorType.NotAvailable;
|
|
12474
12599
|
}
|
|
12475
12600
|
},
|
|
12476
12601
|
isExported: true,
|
|
@@ -15186,6 +15311,8 @@ const uuidGenerator$2 = new UuidGenerator();
|
|
|
15186
15311
|
function createAction(item) {
|
|
15187
15312
|
const name = item.name;
|
|
15188
15313
|
const children = item.children;
|
|
15314
|
+
const description = item.description;
|
|
15315
|
+
const icon = item.icon;
|
|
15189
15316
|
return {
|
|
15190
15317
|
id: item.id || uuidGenerator$2.uuidv4(),
|
|
15191
15318
|
name: typeof name === "function" ? name : () => name,
|
|
@@ -15203,8 +15330,8 @@ function createAction(item) {
|
|
|
15203
15330
|
: () => [],
|
|
15204
15331
|
isReadonlyAllowed: item.isReadonlyAllowed || false,
|
|
15205
15332
|
separator: item.separator || false,
|
|
15206
|
-
icon:
|
|
15207
|
-
description:
|
|
15333
|
+
icon: typeof icon === "function" ? icon : () => icon || "",
|
|
15334
|
+
description: typeof description === "function" ? description : () => description || "",
|
|
15208
15335
|
textColor: item.textColor,
|
|
15209
15336
|
sequence: item.sequence || 0,
|
|
15210
15337
|
};
|
|
@@ -16670,7 +16797,7 @@ function getChartTimeOptions(labels, labelFormat, locale) {
|
|
|
16670
16797
|
function convertDateFormatForMoment(format) {
|
|
16671
16798
|
format = format.replace(/y/g, "Y");
|
|
16672
16799
|
format = format.replace(/d/g, "D");
|
|
16673
|
-
// "m" before "h"
|
|
16800
|
+
// "m" before "h" === month, "m" after "h" === minute
|
|
16674
16801
|
const indexH = format.indexOf("h");
|
|
16675
16802
|
if (indexH >= 0) {
|
|
16676
16803
|
format = format.slice(0, indexH).replace(/m/g, "M") + format.slice(indexH);
|
|
@@ -17524,6 +17651,7 @@ function getChartMenu(figureId, onFigureDeleted, env) {
|
|
|
17524
17651
|
env.model.dispatch("SELECT_FIGURE", { id: figureId });
|
|
17525
17652
|
env.openSidePanel("ChartPanel");
|
|
17526
17653
|
},
|
|
17654
|
+
icon: "o-spreadsheet-Icon.EDIT",
|
|
17527
17655
|
},
|
|
17528
17656
|
getCopyMenuItem(figureId, env),
|
|
17529
17657
|
getCutMenuItem(figureId, env),
|
|
@@ -17555,6 +17683,7 @@ function getImageMenuRegistry(figureId, onFigureDeleted, env) {
|
|
|
17555
17683
|
width,
|
|
17556
17684
|
});
|
|
17557
17685
|
},
|
|
17686
|
+
icon: "o-spreadsheet-Icon.REFRESH",
|
|
17558
17687
|
},
|
|
17559
17688
|
getDeleteMenuItem(figureId, onFigureDeleted, env),
|
|
17560
17689
|
];
|
|
@@ -17571,6 +17700,7 @@ function getCopyMenuItem(figureId, env) {
|
|
|
17571
17700
|
env.model.dispatch("COPY");
|
|
17572
17701
|
await env.clipboard.write(env.model.getters.getClipboardContent());
|
|
17573
17702
|
},
|
|
17703
|
+
icon: "o-spreadsheet-Icon.COPY",
|
|
17574
17704
|
};
|
|
17575
17705
|
}
|
|
17576
17706
|
function getCutMenuItem(figureId, env) {
|
|
@@ -17584,6 +17714,7 @@ function getCutMenuItem(figureId, env) {
|
|
|
17584
17714
|
env.model.dispatch("CUT");
|
|
17585
17715
|
await env.clipboard.write(env.model.getters.getClipboardContent());
|
|
17586
17716
|
},
|
|
17717
|
+
icon: "o-spreadsheet-Icon.CUT",
|
|
17587
17718
|
};
|
|
17588
17719
|
}
|
|
17589
17720
|
function getDeleteMenuItem(figureId, onFigureDeleted, env) {
|
|
@@ -17598,6 +17729,7 @@ function getDeleteMenuItem(figureId, onFigureDeleted, env) {
|
|
|
17598
17729
|
});
|
|
17599
17730
|
onFigureDeleted();
|
|
17600
17731
|
},
|
|
17732
|
+
icon: "o-spreadsheet-Icon.DELETE",
|
|
17601
17733
|
};
|
|
17602
17734
|
}
|
|
17603
17735
|
|
|
@@ -18665,7 +18797,7 @@ const insertFunctionMin = {
|
|
|
18665
18797
|
};
|
|
18666
18798
|
const categorieFunctionAll = {
|
|
18667
18799
|
name: _lt("All"),
|
|
18668
|
-
children: allFunctionListMenuBuilder
|
|
18800
|
+
children: [allFunctionListMenuBuilder],
|
|
18669
18801
|
};
|
|
18670
18802
|
function allFunctionListMenuBuilder() {
|
|
18671
18803
|
const fnNames = functionRegistry.getKeys();
|
|
@@ -18997,7 +19129,10 @@ const formatNumberAutomatic = {
|
|
|
18997
19129
|
};
|
|
18998
19130
|
const formatNumberNumber = {
|
|
18999
19131
|
name: _lt("Number"),
|
|
19000
|
-
description:
|
|
19132
|
+
description: (env) => formatValue(1000.12, {
|
|
19133
|
+
format: "#,##0.00",
|
|
19134
|
+
locale: env.model.getters.getLocale(),
|
|
19135
|
+
}),
|
|
19001
19136
|
execute: (env) => setFormatter(env, "#,##0.00"),
|
|
19002
19137
|
isActive: (env) => isFormatSelected(env, "#,##0.00"),
|
|
19003
19138
|
};
|
|
@@ -19008,21 +19143,34 @@ const formatPercent = {
|
|
|
19008
19143
|
};
|
|
19009
19144
|
const formatNumberPercent = {
|
|
19010
19145
|
name: _lt("Percent"),
|
|
19011
|
-
description:
|
|
19146
|
+
description: (env) => formatValue(0.1012, {
|
|
19147
|
+
format: "0.00%",
|
|
19148
|
+
locale: env.model.getters.getLocale(),
|
|
19149
|
+
}),
|
|
19012
19150
|
execute: FORMAT_PERCENT_ACTION,
|
|
19013
19151
|
isActive: (env) => isFormatSelected(env, "0.00%"),
|
|
19014
19152
|
};
|
|
19015
19153
|
const formatNumberCurrency = {
|
|
19016
19154
|
name: _lt("Currency"),
|
|
19017
|
-
description:
|
|
19018
|
-
|
|
19019
|
-
|
|
19155
|
+
description: (env) => formatValue(1000.12, {
|
|
19156
|
+
format: env.model.config.defaultCurrencyFormat,
|
|
19157
|
+
locale: env.model.getters.getLocale(),
|
|
19158
|
+
}),
|
|
19159
|
+
execute: (env) => setFormatter(env, env.model.config.defaultCurrencyFormat),
|
|
19160
|
+
isActive: (env) => isFormatSelected(env, env.model.config.defaultCurrencyFormat),
|
|
19020
19161
|
};
|
|
19021
19162
|
const formatNumberCurrencyRounded = {
|
|
19022
19163
|
name: _lt("Currency rounded"),
|
|
19023
|
-
description:
|
|
19024
|
-
|
|
19025
|
-
|
|
19164
|
+
description: (env) => formatValue(1000, {
|
|
19165
|
+
format: roundFormat(env.model.config.defaultCurrencyFormat),
|
|
19166
|
+
locale: env.model.getters.getLocale(),
|
|
19167
|
+
}),
|
|
19168
|
+
execute: (env) => setFormatter(env, roundFormat(env.model.config.defaultCurrencyFormat)),
|
|
19169
|
+
isActive: (env) => isFormatSelected(env, roundFormat(env.model.config.defaultCurrencyFormat)),
|
|
19170
|
+
isVisible: (env) => {
|
|
19171
|
+
const currencyFormat = env.model.config.defaultCurrencyFormat;
|
|
19172
|
+
return currencyFormat !== roundFormat(currencyFormat);
|
|
19173
|
+
},
|
|
19026
19174
|
};
|
|
19027
19175
|
const formatCustomCurrency = {
|
|
19028
19176
|
name: _lt("Custom currency"),
|
|
@@ -19031,19 +19179,37 @@ const formatCustomCurrency = {
|
|
|
19031
19179
|
};
|
|
19032
19180
|
const formatNumberDate = {
|
|
19033
19181
|
name: _lt("Date"),
|
|
19034
|
-
description:
|
|
19182
|
+
description: (env) => {
|
|
19183
|
+
const locale = env.model.getters.getLocale();
|
|
19184
|
+
return formatValue(parseLiteral("9/26/2023", DEFAULT_LOCALE), {
|
|
19185
|
+
format: locale.dateFormat,
|
|
19186
|
+
locale,
|
|
19187
|
+
});
|
|
19188
|
+
},
|
|
19035
19189
|
execute: (env) => setFormatter(env, env.model.getters.getLocale().dateFormat),
|
|
19036
19190
|
isActive: (env) => isFormatSelected(env, env.model.getters.getLocale().dateFormat),
|
|
19037
19191
|
};
|
|
19038
19192
|
const formatNumberTime = {
|
|
19039
19193
|
name: _lt("Time"),
|
|
19040
|
-
description:
|
|
19194
|
+
description: (env) => {
|
|
19195
|
+
const locale = env.model.getters.getLocale();
|
|
19196
|
+
return formatValue(parseLiteral("9/26/2023 10:43:00 PM", DEFAULT_LOCALE), {
|
|
19197
|
+
format: locale.timeFormat,
|
|
19198
|
+
locale,
|
|
19199
|
+
});
|
|
19200
|
+
},
|
|
19041
19201
|
execute: (env) => setFormatter(env, env.model.getters.getLocale().timeFormat),
|
|
19042
19202
|
isActive: (env) => isFormatSelected(env, env.model.getters.getLocale().timeFormat),
|
|
19043
19203
|
};
|
|
19044
19204
|
const formatNumberDateTime = {
|
|
19045
19205
|
name: _lt("Date time"),
|
|
19046
|
-
description:
|
|
19206
|
+
description: (env) => {
|
|
19207
|
+
const locale = env.model.getters.getLocale();
|
|
19208
|
+
return formatValue(parseLiteral("9/26/2023 22:43:00", DEFAULT_LOCALE), {
|
|
19209
|
+
format: locale.dateFormat + " " + locale.timeFormat,
|
|
19210
|
+
locale,
|
|
19211
|
+
});
|
|
19212
|
+
},
|
|
19047
19213
|
execute: (env) => {
|
|
19048
19214
|
const locale = env.model.getters.getLocale();
|
|
19049
19215
|
setFormatter(env, locale.dateFormat + " " + locale.timeFormat);
|
|
@@ -19115,7 +19281,7 @@ const formatAlignment = {
|
|
|
19115
19281
|
};
|
|
19116
19282
|
const formatAlignmentHorizontal = {
|
|
19117
19283
|
name: _lt("Horizontal align"),
|
|
19118
|
-
icon:
|
|
19284
|
+
icon: (env) => getHorizontalAlignmentIcon(env),
|
|
19119
19285
|
};
|
|
19120
19286
|
const formatAlignmentLeft = {
|
|
19121
19287
|
name: _lt("Left"),
|
|
@@ -19140,46 +19306,50 @@ const formatAlignmentRight = {
|
|
|
19140
19306
|
};
|
|
19141
19307
|
const formatAlignmentVertical = {
|
|
19142
19308
|
name: _lt("Vertical align"),
|
|
19143
|
-
icon:
|
|
19309
|
+
icon: (env) => getVerticalAlignmentIcon(env),
|
|
19144
19310
|
};
|
|
19145
19311
|
const formatAlignmentTop = {
|
|
19146
19312
|
name: _lt("Top"),
|
|
19147
19313
|
execute: (env) => setStyle(env, { verticalAlign: "top" }),
|
|
19148
|
-
isActive: (env) => (env
|
|
19314
|
+
isActive: (env) => getVerticalAlign(env) === "top",
|
|
19149
19315
|
icon: "o-spreadsheet-Icon.ALIGN_TOP",
|
|
19150
19316
|
};
|
|
19151
19317
|
const formatAlignmentMiddle = {
|
|
19152
19318
|
name: _lt("Middle"),
|
|
19153
19319
|
execute: (env) => setStyle(env, { verticalAlign: "middle" }),
|
|
19154
|
-
isActive: (env) => (env
|
|
19320
|
+
isActive: (env) => getVerticalAlign(env) === "middle",
|
|
19155
19321
|
icon: "o-spreadsheet-Icon.ALIGN_MIDDLE",
|
|
19156
19322
|
};
|
|
19157
19323
|
const formatAlignmentBottom = {
|
|
19158
19324
|
name: _lt("Bottom"),
|
|
19159
19325
|
execute: (env) => setStyle(env, { verticalAlign: "bottom" }),
|
|
19160
|
-
isActive: (env) => (env
|
|
19326
|
+
isActive: (env) => getVerticalAlign(env) === "bottom",
|
|
19161
19327
|
icon: "o-spreadsheet-Icon.ALIGN_BOTTOM",
|
|
19162
19328
|
};
|
|
19163
|
-
const
|
|
19329
|
+
const formatWrappingIcon = {
|
|
19164
19330
|
name: _lt("Wrapping"),
|
|
19165
19331
|
icon: "o-spreadsheet-Icon.WRAPPING_OVERFLOW",
|
|
19166
19332
|
};
|
|
19333
|
+
const formatWrapping = {
|
|
19334
|
+
name: _lt("Wrapping"),
|
|
19335
|
+
icon: (env) => getWrapModeIcon(env),
|
|
19336
|
+
};
|
|
19167
19337
|
const formatWrappingOverflow = {
|
|
19168
19338
|
name: _lt("Overflow"),
|
|
19169
19339
|
execute: (env) => setStyle(env, { wrapping: "overflow" }),
|
|
19170
|
-
isActive: (env) => (env
|
|
19340
|
+
isActive: (env) => getWrappingMode(env) === "overflow",
|
|
19171
19341
|
icon: "o-spreadsheet-Icon.WRAPPING_OVERFLOW",
|
|
19172
19342
|
};
|
|
19173
19343
|
const formatWrappingWrap = {
|
|
19174
19344
|
name: _lt("Wrap"),
|
|
19175
19345
|
execute: (env) => setStyle(env, { wrapping: "wrap" }),
|
|
19176
|
-
isActive: (env) => env
|
|
19346
|
+
isActive: (env) => getWrappingMode(env) === "wrap",
|
|
19177
19347
|
icon: "o-spreadsheet-Icon.WRAPPING_WRAP",
|
|
19178
19348
|
};
|
|
19179
19349
|
const formatWrappingClip = {
|
|
19180
19350
|
name: _lt("Clip"),
|
|
19181
19351
|
execute: (env) => setStyle(env, { wrapping: "clip" }),
|
|
19182
|
-
isActive: (env) => env
|
|
19352
|
+
isActive: (env) => getWrappingMode(env) === "clip",
|
|
19183
19353
|
icon: "o-spreadsheet-Icon.WRAPPING_CLIP",
|
|
19184
19354
|
};
|
|
19185
19355
|
const textColor = {
|
|
@@ -19195,14 +19365,6 @@ const formatCF = {
|
|
|
19195
19365
|
execute: OPEN_CF_SIDEPANEL_ACTION,
|
|
19196
19366
|
icon: "o-spreadsheet-Icon.CONDITIONAL_FORMAT",
|
|
19197
19367
|
};
|
|
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
19368
|
const clearFormat = {
|
|
19207
19369
|
name: _lt("Clear formatting"),
|
|
19208
19370
|
description: "Ctrl+<",
|
|
@@ -19242,6 +19404,53 @@ function getHorizontalAlign(env) {
|
|
|
19242
19404
|
}
|
|
19243
19405
|
const cell = env.model.getters.getActiveCell();
|
|
19244
19406
|
return cell.defaultAlign;
|
|
19407
|
+
}
|
|
19408
|
+
function getVerticalAlign(env) {
|
|
19409
|
+
const style = env.model.getters.getCurrentStyle();
|
|
19410
|
+
if (style.verticalAlign) {
|
|
19411
|
+
return style.verticalAlign;
|
|
19412
|
+
}
|
|
19413
|
+
return DEFAULT_VERTICAL_ALIGN;
|
|
19414
|
+
}
|
|
19415
|
+
function getWrappingMode(env) {
|
|
19416
|
+
const style = env.model.getters.getCurrentStyle();
|
|
19417
|
+
if (style.wrapping) {
|
|
19418
|
+
return style.wrapping;
|
|
19419
|
+
}
|
|
19420
|
+
return DEFAULT_WRAPPING_MODE;
|
|
19421
|
+
}
|
|
19422
|
+
function getHorizontalAlignmentIcon(env) {
|
|
19423
|
+
const horizontalAlign = getHorizontalAlign(env);
|
|
19424
|
+
switch (horizontalAlign) {
|
|
19425
|
+
case "right":
|
|
19426
|
+
return "o-spreadsheet-Icon.ALIGN_RIGHT";
|
|
19427
|
+
case "center":
|
|
19428
|
+
return "o-spreadsheet-Icon.ALIGN_CENTER";
|
|
19429
|
+
default:
|
|
19430
|
+
return "o-spreadsheet-Icon.ALIGN_LEFT";
|
|
19431
|
+
}
|
|
19432
|
+
}
|
|
19433
|
+
function getVerticalAlignmentIcon(env) {
|
|
19434
|
+
const verticalAlign = getVerticalAlign(env);
|
|
19435
|
+
switch (verticalAlign) {
|
|
19436
|
+
case "top":
|
|
19437
|
+
return "o-spreadsheet-Icon.ALIGN_TOP";
|
|
19438
|
+
case "middle":
|
|
19439
|
+
return "o-spreadsheet-Icon.ALIGN_MIDDLE";
|
|
19440
|
+
default:
|
|
19441
|
+
return "o-spreadsheet-Icon.ALIGN_BOTTOM";
|
|
19442
|
+
}
|
|
19443
|
+
}
|
|
19444
|
+
function getWrapModeIcon(env) {
|
|
19445
|
+
const wrapMode = getWrappingMode(env);
|
|
19446
|
+
switch (wrapMode) {
|
|
19447
|
+
case "wrap":
|
|
19448
|
+
return "o-spreadsheet-Icon.WRAPPING_WRAP";
|
|
19449
|
+
case "clip":
|
|
19450
|
+
return "o-spreadsheet-Icon.WRAPPING_CLIP";
|
|
19451
|
+
default:
|
|
19452
|
+
return "o-spreadsheet-Icon.WRAPPING_OVERFLOW";
|
|
19453
|
+
}
|
|
19245
19454
|
}
|
|
19246
19455
|
|
|
19247
19456
|
var ACTION_FORMAT = /*#__PURE__*/Object.freeze({
|
|
@@ -19273,6 +19482,7 @@ var ACTION_FORMAT = /*#__PURE__*/Object.freeze({
|
|
|
19273
19482
|
formatAlignmentTop: formatAlignmentTop,
|
|
19274
19483
|
formatAlignmentMiddle: formatAlignmentMiddle,
|
|
19275
19484
|
formatAlignmentBottom: formatAlignmentBottom,
|
|
19485
|
+
formatWrappingIcon: formatWrappingIcon,
|
|
19276
19486
|
formatWrapping: formatWrapping,
|
|
19277
19487
|
formatWrappingOverflow: formatWrappingOverflow,
|
|
19278
19488
|
formatWrappingWrap: formatWrappingWrap,
|
|
@@ -19280,7 +19490,6 @@ var ACTION_FORMAT = /*#__PURE__*/Object.freeze({
|
|
|
19280
19490
|
textColor: textColor,
|
|
19281
19491
|
fillColor: fillColor,
|
|
19282
19492
|
formatCF: formatCF,
|
|
19283
|
-
paintFormat: paintFormat,
|
|
19284
19493
|
clearFormat: clearFormat
|
|
19285
19494
|
});
|
|
19286
19495
|
|
|
@@ -20043,7 +20252,7 @@ topbarMenuRegistry
|
|
|
20043
20252
|
separator: true,
|
|
20044
20253
|
})
|
|
20045
20254
|
.addChild("format_wrapping", ["format"], {
|
|
20046
|
-
...
|
|
20255
|
+
...formatWrappingIcon,
|
|
20047
20256
|
sequence: 80,
|
|
20048
20257
|
separator: true,
|
|
20049
20258
|
})
|
|
@@ -22333,17 +22542,45 @@ class CustomCurrencyPanel extends owl.Component {
|
|
|
22333
22542
|
}
|
|
22334
22543
|
get formatProposals() {
|
|
22335
22544
|
const currency = this.availableCurrencies[this.state.selectedCurrencyIndex];
|
|
22336
|
-
const
|
|
22337
|
-
const
|
|
22338
|
-
const secondPosition = currency.position === "before" ? "after" : "before";
|
|
22545
|
+
const position = currency.position;
|
|
22546
|
+
const opposite = currency.position === "before" ? "after" : "before";
|
|
22339
22547
|
const symbol = this.state.currencySymbol.trim() ? this.state.currencySymbol : "";
|
|
22340
22548
|
const code = this.state.currencyCode.trim() ? this.state.currencyCode : "";
|
|
22341
|
-
|
|
22342
|
-
|
|
22343
|
-
|
|
22344
|
-
|
|
22345
|
-
|
|
22346
|
-
|
|
22549
|
+
const decimalPlaces = currency.decimalPlaces;
|
|
22550
|
+
if (!symbol && !code) {
|
|
22551
|
+
return [];
|
|
22552
|
+
}
|
|
22553
|
+
const simple = symbol ? createCurrencyFormat({ symbol, position, decimalPlaces }) : "";
|
|
22554
|
+
const rounded = simple ? roundFormat(simple) : "";
|
|
22555
|
+
const simpleWithCode = createCurrencyFormat({ symbol, position, decimalPlaces, code });
|
|
22556
|
+
const roundedWithCode = roundFormat(simpleWithCode);
|
|
22557
|
+
const simpleOpposite = symbol
|
|
22558
|
+
? createCurrencyFormat({ symbol, position: opposite, decimalPlaces })
|
|
22559
|
+
: "";
|
|
22560
|
+
const roundedOpposite = simpleOpposite ? roundFormat(simpleOpposite) : "";
|
|
22561
|
+
const simpleOppositeWithCode = createCurrencyFormat({
|
|
22562
|
+
symbol,
|
|
22563
|
+
position: opposite,
|
|
22564
|
+
decimalPlaces,
|
|
22565
|
+
code,
|
|
22566
|
+
});
|
|
22567
|
+
const roundedOppositeWithCode = roundFormat(simpleOppositeWithCode);
|
|
22568
|
+
const formats = new Set([
|
|
22569
|
+
rounded,
|
|
22570
|
+
simple,
|
|
22571
|
+
roundedWithCode,
|
|
22572
|
+
simpleWithCode,
|
|
22573
|
+
roundedOpposite,
|
|
22574
|
+
simpleOpposite,
|
|
22575
|
+
roundedOppositeWithCode,
|
|
22576
|
+
simpleOppositeWithCode,
|
|
22577
|
+
]);
|
|
22578
|
+
return [...formats]
|
|
22579
|
+
.filter((format) => format !== "")
|
|
22580
|
+
.map((format) => ({
|
|
22581
|
+
format,
|
|
22582
|
+
example: formatValue(1000.0, { format, locale: this.env.model.getters.getLocale() }),
|
|
22583
|
+
}));
|
|
22347
22584
|
}
|
|
22348
22585
|
get isSameFormat() {
|
|
22349
22586
|
const selectedFormat = this.formatProposals[this.state.selectedFormatIndex];
|
|
@@ -22400,41 +22637,6 @@ class CustomCurrencyPanel extends owl.Component {
|
|
|
22400
22637
|
initAvailableCurrencies() {
|
|
22401
22638
|
this.state.selectedCurrencyIndex = 0;
|
|
22402
22639
|
}
|
|
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
22640
|
getCommonFormat() {
|
|
22439
22641
|
const selectedZones = this.env.model.getters.getSelectedZones();
|
|
22440
22642
|
const sheetId = this.env.model.getters.getActiveSheetId();
|
|
@@ -22491,6 +22693,7 @@ class FindAndReplacePanel extends owl.Component {
|
|
|
22491
22693
|
state = owl.useState(this.initialState());
|
|
22492
22694
|
debounceTimeoutId;
|
|
22493
22695
|
showFormulaState = false;
|
|
22696
|
+
debouncedUpdateSearch;
|
|
22494
22697
|
findAndReplaceRef = owl.useRef("findAndReplace");
|
|
22495
22698
|
get hasSearchResult() {
|
|
22496
22699
|
return this.env.model.getters.getCurrentSelectedMatchIndex() !== null;
|
|
@@ -22500,11 +22703,16 @@ class FindAndReplacePanel extends owl.Component {
|
|
|
22500
22703
|
}
|
|
22501
22704
|
setup() {
|
|
22502
22705
|
this.showFormulaState = this.env.model.getters.shouldShowFormulas();
|
|
22706
|
+
this.debouncedUpdateSearch = debounce(this.updateSearch.bind(this), 200);
|
|
22503
22707
|
owl.onMounted(() => this.focusInput());
|
|
22504
22708
|
owl.onWillUnmount(() => {
|
|
22505
22709
|
this.env.model.dispatch("CLEAR_SEARCH");
|
|
22506
22710
|
this.env.model.dispatch("SET_FORMULA_VISIBILITY", { show: this.showFormulaState });
|
|
22507
22711
|
});
|
|
22712
|
+
owl.useEffect(() => {
|
|
22713
|
+
this.state.searchOptions.searchFormulas = this.env.model.getters.shouldShowFormulas();
|
|
22714
|
+
this.searchFormulas();
|
|
22715
|
+
}, () => [this.env.model.getters.shouldShowFormulas()]);
|
|
22508
22716
|
}
|
|
22509
22717
|
onInput(ev) {
|
|
22510
22718
|
this.state.toSearch = ev.target.value;
|
|
@@ -22524,10 +22732,6 @@ class FindAndReplacePanel extends owl.Component {
|
|
|
22524
22732
|
this.replace();
|
|
22525
22733
|
}
|
|
22526
22734
|
}
|
|
22527
|
-
onFocusSidePanel() {
|
|
22528
|
-
this.state.searchOptions.searchFormulas = this.env.model.getters.shouldShowFormulas();
|
|
22529
|
-
this.env.model.dispatch("REFRESH_SEARCH");
|
|
22530
|
-
}
|
|
22531
22735
|
searchFormulas() {
|
|
22532
22736
|
this.env.model.dispatch("SET_FORMULA_VISIBILITY", {
|
|
22533
22737
|
show: this.state.searchOptions.searchFormulas,
|
|
@@ -22546,13 +22750,6 @@ class FindAndReplacePanel extends owl.Component {
|
|
|
22546
22750
|
searchOptions: this.state.searchOptions,
|
|
22547
22751
|
});
|
|
22548
22752
|
}
|
|
22549
|
-
debouncedUpdateSearch() {
|
|
22550
|
-
clearTimeout(this.debounceTimeoutId);
|
|
22551
|
-
this.debounceTimeoutId = setTimeout(() => {
|
|
22552
|
-
this.updateSearch();
|
|
22553
|
-
this.debounceTimeoutId = undefined;
|
|
22554
|
-
}, 400);
|
|
22555
|
-
}
|
|
22556
22753
|
replace() {
|
|
22557
22754
|
this.env.model.dispatch("REPLACE_SEARCH", {
|
|
22558
22755
|
replaceWith: this.state.replaceWith,
|
|
@@ -23237,7 +23434,7 @@ class ContentEditableHelper {
|
|
|
23237
23434
|
selection.removeAllRanges();
|
|
23238
23435
|
selection.addRange(range);
|
|
23239
23436
|
}
|
|
23240
|
-
if (start
|
|
23437
|
+
if (start === end && start === 0) {
|
|
23241
23438
|
range.setStart(this.el, 0);
|
|
23242
23439
|
range.setEnd(this.el, 0);
|
|
23243
23440
|
}
|
|
@@ -23490,7 +23687,7 @@ class ContentEditableHelper {
|
|
|
23490
23687
|
}, 0);
|
|
23491
23688
|
}
|
|
23492
23689
|
}
|
|
23493
|
-
if (nodeToFind.nodeName === "P" && !isFirstParagraph && nodeToFind.textContent
|
|
23690
|
+
if (nodeToFind.nodeName === "P" && !isFirstParagraph && nodeToFind.textContent === "") {
|
|
23494
23691
|
usedCharacters++;
|
|
23495
23692
|
}
|
|
23496
23693
|
return usedCharacters;
|
|
@@ -25037,6 +25234,14 @@ function useInterval(callback, delay) {
|
|
|
25037
25234
|
};
|
|
25038
25235
|
}
|
|
25039
25236
|
|
|
25237
|
+
const CURSOR_SVG = /*xml*/ `
|
|
25238
|
+
<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>
|
|
25239
|
+
`;
|
|
25240
|
+
css /* scss */ `
|
|
25241
|
+
.o-paint-format-cursor {
|
|
25242
|
+
cursor: url("data:image/svg+xml,${encodeURIComponent(CURSOR_SVG)}"), auto;
|
|
25243
|
+
}
|
|
25244
|
+
`;
|
|
25040
25245
|
function useCellHovered(env, gridRef, callback) {
|
|
25041
25246
|
let hoveredPosition = {
|
|
25042
25247
|
col: undefined,
|
|
@@ -25160,6 +25365,12 @@ class GridOverlay extends owl.Component {
|
|
|
25160
25365
|
}
|
|
25161
25366
|
return this.gridOverlay.el;
|
|
25162
25367
|
}
|
|
25368
|
+
get style() {
|
|
25369
|
+
return this.props.gridOverlayDimensions;
|
|
25370
|
+
}
|
|
25371
|
+
get isPaintingFormat() {
|
|
25372
|
+
return this.env.model.getters.isPaintingFormat();
|
|
25373
|
+
}
|
|
25163
25374
|
onMouseDown(ev) {
|
|
25164
25375
|
if (ev.button > 0) {
|
|
25165
25376
|
// not main button, probably a context menu
|
|
@@ -26337,6 +26548,9 @@ class Grid extends owl.Component {
|
|
|
26337
26548
|
else if (this.menuState.isOpen) {
|
|
26338
26549
|
this.closeMenu();
|
|
26339
26550
|
}
|
|
26551
|
+
else if (this.env.model.getters.isPaintingFormat()) {
|
|
26552
|
+
this.env.model.dispatch("CANCEL_PAINT_FORMAT");
|
|
26553
|
+
}
|
|
26340
26554
|
else {
|
|
26341
26555
|
this.env.model.dispatch("CLEAN_CLIPBOARD_HIGHLIGHT");
|
|
26342
26556
|
}
|
|
@@ -27539,8 +27753,8 @@ function convertXlsxFormat(numFmtId, formats, warningManager) {
|
|
|
27539
27753
|
throw new Error("Multiple escaped blocks in format");
|
|
27540
27754
|
}
|
|
27541
27755
|
convertedFormat = convertedFormat.replace(/"(.*)"/g, "[$$$1]"); // replace '"..."' by '[$...]'
|
|
27542
|
-
convertedFormat = convertedFormat.replace(/_.{1}/g, ""); // _
|
|
27543
|
-
convertedFormat = convertedFormat.replace(/\*.{1}/g, ""); // *
|
|
27756
|
+
convertedFormat = convertedFormat.replace(/_.{1}/g, ""); // _ === ignore width of next char for align purposes. Not supported ATM
|
|
27757
|
+
convertedFormat = convertedFormat.replace(/\*.{1}/g, ""); // * === repeat next character enough to fill the line. Not supported ATM
|
|
27544
27758
|
convertedFormat = convertedFormat.replace(/\\ /g, " "); // unescape spaces
|
|
27545
27759
|
convertedFormat = convertedFormat.replace(/\\./g, (match) => match[1]); // unescape other characters
|
|
27546
27760
|
if (isXlsxDateFormat(convertedFormat)) {
|
|
@@ -28832,29 +29046,26 @@ function escapeXml(strings, ...expressions) {
|
|
|
28832
29046
|
return new XMLString(concat(str));
|
|
28833
29047
|
}
|
|
28834
29048
|
/**
|
|
28835
|
-
* Removes the namespace of all the xml tags in the string.
|
|
29049
|
+
* Removes the escaped namespace of all the xml tags in the string.
|
|
28836
29050
|
*
|
|
28837
|
-
* Eg. : "
|
|
29051
|
+
* Eg. : "NAMESPACEnsNAMESPACEtest a" => "test a"
|
|
28838
29052
|
*/
|
|
28839
|
-
function
|
|
28840
|
-
return
|
|
29053
|
+
function removeTagEscapedNamespaces(tag) {
|
|
29054
|
+
return tag.replace(/NAMESPACE.*NAMESPACE(.*)/, "$1");
|
|
28841
29055
|
}
|
|
28842
29056
|
/**
|
|
28843
|
-
*
|
|
29057
|
+
* Encase the namespaces in the element's tags with NAMESPACE string
|
|
28844
29058
|
*
|
|
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.
|
|
29059
|
+
* e.g. <x:foo> becomes <NAMESPACExNAMESPACEFoo>
|
|
28852
29060
|
*
|
|
28853
|
-
*
|
|
29061
|
+
* That's useful because namespaces aren't supported by the HTML specification, so it's arbitrary whether a HTML parser/querySelector
|
|
29062
|
+
* implementation will support namespaces in the tags or not.
|
|
28854
29063
|
*/
|
|
28855
|
-
function
|
|
28856
|
-
|
|
28857
|
-
|
|
29064
|
+
function escapeTagNamespaces(str) {
|
|
29065
|
+
return str.replaceAll(/(<\/?)([a-zA-Z0-9]+):([a-zA-Z0-9]+)/g, "$1" + "NAMESPACE" + "$2" + "NAMESPACE" + "$3");
|
|
29066
|
+
}
|
|
29067
|
+
function escapeQueryNameSpaces(query) {
|
|
29068
|
+
return query.replaceAll(/([a-zA-Z0-9]+):([a-zA-Z0-9]+)/g, "NAMESPACE" + "$1" + "NAMESPACE" + "$2");
|
|
28858
29069
|
}
|
|
28859
29070
|
|
|
28860
29071
|
class AttributeValue {
|
|
@@ -28884,14 +29095,20 @@ class XlsxBaseExtractor {
|
|
|
28884
29095
|
// The xml file we are currently parsing. We should have one Extractor class by XLSXImportFile, but
|
|
28885
29096
|
// the XLSXImportFile contains both the main .xml file, and the .rels file
|
|
28886
29097
|
currentFile = undefined;
|
|
28887
|
-
|
|
28888
|
-
|
|
29098
|
+
/**
|
|
29099
|
+
* /!\ Important : There should be no namespaces in the tags of the XML files.
|
|
29100
|
+
*
|
|
29101
|
+
* This class use native querySelector and querySelectorAll, that's used for HTML (not XML). These aren't supposed to
|
|
29102
|
+
* handled namespaces, as they are not supported by the HTML specification. Some implementations (most browsers) do
|
|
29103
|
+
* actually support namespaces, but some don't (e.g. jsdom).
|
|
29104
|
+
*
|
|
29105
|
+
* The namespace should be escaped as with NAMESPACE string (eg. <t:foo> => <NAMESPACEtNAMESPACEfoo>).
|
|
29106
|
+
*/
|
|
28889
29107
|
constructor(rootFile, xlsxStructure, warningManager) {
|
|
28890
29108
|
this.rootFile = rootFile;
|
|
28891
29109
|
this.currentFile = rootFile.file.fileName;
|
|
28892
29110
|
this.xlsxFileStructure = xlsxStructure;
|
|
28893
29111
|
this.warningManager = warningManager;
|
|
28894
|
-
this.areNamespaceIgnored = areNamespaceIgnoredByQuerySelector();
|
|
28895
29112
|
this.relationships = {};
|
|
28896
29113
|
if (rootFile.rels) {
|
|
28897
29114
|
this.extractRelationships(rootFile.rels).map((rel) => {
|
|
@@ -29117,29 +29334,13 @@ class XlsxBaseExtractor {
|
|
|
29117
29334
|
throw new Error("Cannot find target file");
|
|
29118
29335
|
return f;
|
|
29119
29336
|
}
|
|
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
29337
|
querySelector(element, query) {
|
|
29129
|
-
|
|
29130
|
-
return element.querySelector(
|
|
29338
|
+
const escapedQuery = escapeQueryNameSpaces(query);
|
|
29339
|
+
return element.querySelector(escapedQuery);
|
|
29131
29340
|
}
|
|
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
29341
|
querySelectorAll(element, query) {
|
|
29141
|
-
|
|
29142
|
-
return element.querySelectorAll(
|
|
29342
|
+
const escapedQuery = escapeQueryNameSpaces(query);
|
|
29343
|
+
return element.querySelectorAll(escapedQuery);
|
|
29143
29344
|
}
|
|
29144
29345
|
/**
|
|
29145
29346
|
* Get a color from its id in the Theme's colorScheme.
|
|
@@ -29379,7 +29580,7 @@ class XlsxChartExtractor extends XlsxBaseExtractor {
|
|
|
29379
29580
|
throw new Error("Missing plot area in the chart definition.");
|
|
29380
29581
|
}
|
|
29381
29582
|
for (let child of plotAreaElement.children) {
|
|
29382
|
-
const tag =
|
|
29583
|
+
const tag = removeTagEscapedNamespaces(child.tagName);
|
|
29383
29584
|
if (XLSX_CHART_TYPES.some((chartType) => chartType === tag)) {
|
|
29384
29585
|
return tag;
|
|
29385
29586
|
}
|
|
@@ -29391,7 +29592,7 @@ class XlsxChartExtractor extends XlsxBaseExtractor {
|
|
|
29391
29592
|
class XlsxFigureExtractor extends XlsxBaseExtractor {
|
|
29392
29593
|
extractFigures() {
|
|
29393
29594
|
return this.mapOnElements({ parent: this.rootFile.file.xml, query: "xdr:wsDr", children: true }, (figureElement) => {
|
|
29394
|
-
const anchorType =
|
|
29595
|
+
const anchorType = removeTagEscapedNamespaces(figureElement.tagName);
|
|
29395
29596
|
if (anchorType !== "twoCellAnchor") {
|
|
29396
29597
|
throw new Error("Only twoCellAnchor are supported for xlsx drawings.");
|
|
29397
29598
|
}
|
|
@@ -30025,7 +30226,8 @@ class XlsxReader {
|
|
|
30025
30226
|
for (let key of Object.keys(files)) {
|
|
30026
30227
|
// Random files can be in xlsx (like a bin file for printer settings)
|
|
30027
30228
|
if (key.endsWith(".xml") || key.endsWith(".rels")) {
|
|
30028
|
-
|
|
30229
|
+
const contentString = escapeTagNamespaces(files[key]);
|
|
30230
|
+
this.xmls[key] = parseXML(new XMLString(contentString));
|
|
30029
30231
|
}
|
|
30030
30232
|
else if (key.includes("media/image")) {
|
|
30031
30233
|
this.images.push({
|
|
@@ -31092,7 +31294,7 @@ class BordersPlugin extends CorePlugin {
|
|
|
31092
31294
|
}
|
|
31093
31295
|
/**
|
|
31094
31296
|
* Set the borders of a cell.
|
|
31095
|
-
* It overrides the current border if override
|
|
31297
|
+
* It overrides the current border if override === true.
|
|
31096
31298
|
*/
|
|
31097
31299
|
setBorder(sheetId, col, row, border, override = true) {
|
|
31098
31300
|
if (override || !this.borders?.[sheetId]?.[col]?.[row]?.vertical) {
|
|
@@ -32362,7 +32564,9 @@ class FigurePlugin extends CorePlugin {
|
|
|
32362
32564
|
const numHeader = this.getters.getNumberRows(sheetId);
|
|
32363
32565
|
let gridHeight = 0;
|
|
32364
32566
|
for (let i = 0; i < numHeader; i++) {
|
|
32365
|
-
|
|
32567
|
+
// TODO : since the row size is an UI value now, this doesn't work anymore. Using the default cell height is
|
|
32568
|
+
// a temporary solution at best, but is broken.
|
|
32569
|
+
gridHeight += this.getters.getUserRowSize(sheetId, i) || DEFAULT_CELL_HEIGHT;
|
|
32366
32570
|
}
|
|
32367
32571
|
const figures = this.getters.getFigures(sheetId);
|
|
32368
32572
|
for (const figure of figures) {
|
|
@@ -32788,33 +32992,15 @@ class FiltersPlugin extends CorePlugin {
|
|
|
32788
32992
|
}
|
|
32789
32993
|
|
|
32790
32994
|
class HeaderSizePlugin extends CorePlugin {
|
|
32791
|
-
static getters = ["
|
|
32995
|
+
static getters = ["getUserRowSize", "getColSize"];
|
|
32792
32996
|
sizes = {};
|
|
32793
32997
|
handle(cmd) {
|
|
32794
32998
|
switch (cmd.type) {
|
|
32795
32999
|
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);
|
|
33000
|
+
this.history.update("sizes", cmd.sheetId, { COL: [], ROW: [] });
|
|
32808
33001
|
break;
|
|
32809
33002
|
}
|
|
32810
33003
|
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
33004
|
this.history.update("sizes", cmd.sheetIdTo, deepCopy(this.sizes[cmd.sheetId]));
|
|
32819
33005
|
break;
|
|
32820
33006
|
case "DELETE_SHEET":
|
|
@@ -32823,21 +33009,8 @@ class HeaderSizePlugin extends CorePlugin {
|
|
|
32823
33009
|
this.history.update("sizes", sizes);
|
|
32824
33010
|
break;
|
|
32825
33011
|
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
|
-
});
|
|
33012
|
+
const arr = this.sizes[cmd.sheetId][cmd.dimension];
|
|
33013
|
+
const sizes = removeIndexesFromArray(arr, cmd.elements);
|
|
32841
33014
|
this.history.update("sizes", cmd.sheetId, cmd.dimension, sizes);
|
|
32842
33015
|
break;
|
|
32843
33016
|
}
|
|
@@ -32846,51 +33019,18 @@ class HeaderSizePlugin extends CorePlugin {
|
|
|
32846
33019
|
const addIndex = getAddHeaderStartIndex(cmd.position, cmd.base);
|
|
32847
33020
|
const baseSize = sizes[cmd.base];
|
|
32848
33021
|
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
33022
|
this.history.update("sizes", cmd.sheetId, cmd.dimension, sizes);
|
|
32860
33023
|
break;
|
|
32861
33024
|
}
|
|
32862
33025
|
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
|
-
});
|
|
33026
|
+
if (cmd.dimension === "ROW") {
|
|
33027
|
+
for (const el of cmd.elements) {
|
|
33028
|
+
this.history.update("sizes", cmd.sheetId, cmd.dimension, el, cmd.size || undefined);
|
|
32877
33029
|
}
|
|
32878
33030
|
}
|
|
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
|
-
}
|
|
33031
|
+
else {
|
|
33032
|
+
for (const el of cmd.elements) {
|
|
33033
|
+
this.history.update("sizes", cmd.sheetId, cmd.dimension, el, cmd.size || undefined);
|
|
32894
33034
|
}
|
|
32895
33035
|
}
|
|
32896
33036
|
break;
|
|
@@ -32898,94 +33038,29 @@ class HeaderSizePlugin extends CorePlugin {
|
|
|
32898
33038
|
return;
|
|
32899
33039
|
}
|
|
32900
33040
|
getColSize(sheetId, index) {
|
|
32901
|
-
return this.
|
|
32902
|
-
}
|
|
32903
|
-
getRowSize(sheetId, index) {
|
|
32904
|
-
return this.getHeaderSize(sheetId, "ROW", index);
|
|
32905
|
-
}
|
|
32906
|
-
getHeaderSize(sheetId, dimension, index) {
|
|
32907
|
-
return Math.round(this.sizes[sheetId]?.[dimension][index]?.manualSize ||
|
|
32908
|
-
this.sizes[sheetId]?.[dimension][index]?.computedSize() ||
|
|
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;
|
|
33041
|
+
return Math.round(this.sizes[sheetId]?.["COL"][index] || DEFAULT_CELL_WIDTH);
|
|
32925
33042
|
}
|
|
32926
|
-
|
|
32927
|
-
|
|
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;
|
|
33043
|
+
getUserRowSize(sheetId, index) {
|
|
33044
|
+
const rowSize = this.sizes[sheetId]?.["ROW"][index];
|
|
33045
|
+
return rowSize ? Math.round(rowSize) : undefined;
|
|
32964
33046
|
}
|
|
32965
33047
|
import(data) {
|
|
32966
33048
|
for (let sheet of data.sheets) {
|
|
32967
|
-
const
|
|
33049
|
+
const sizes = {
|
|
33050
|
+
COL: Array(sheet.colNumber).fill(undefined),
|
|
33051
|
+
ROW: Array(sheet.rowNumber).fill(undefined),
|
|
33052
|
+
};
|
|
32968
33053
|
for (let [rowIndex, row] of Object.entries(sheet.rows)) {
|
|
32969
33054
|
if (row.size) {
|
|
32970
|
-
|
|
33055
|
+
sizes["ROW"][rowIndex] = row.size;
|
|
32971
33056
|
}
|
|
32972
33057
|
}
|
|
32973
33058
|
for (let [colIndex, col] of Object.entries(sheet.cols)) {
|
|
32974
33059
|
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
|
-
};
|
|
33060
|
+
sizes["COL"][colIndex] = col.size;
|
|
33061
|
+
}
|
|
33062
|
+
}
|
|
33063
|
+
this.sizes[sheet.id] = sizes;
|
|
32989
33064
|
}
|
|
32990
33065
|
return;
|
|
32991
33066
|
}
|
|
@@ -33006,9 +33081,12 @@ class HeaderSizePlugin extends CorePlugin {
|
|
|
33006
33081
|
if (sheet.rows === undefined) {
|
|
33007
33082
|
sheet.rows = {};
|
|
33008
33083
|
}
|
|
33009
|
-
for (
|
|
33010
|
-
if (exportDefaults || this.sizes[sheet.id]["ROW"][row]
|
|
33011
|
-
sheet.rows[row] = {
|
|
33084
|
+
for (const row of range(0, this.getters.getNumberRows(sheet.id))) {
|
|
33085
|
+
if (exportDefaults || this.sizes[sheet.id]["ROW"][row]) {
|
|
33086
|
+
sheet.rows[row] = {
|
|
33087
|
+
...sheet.rows[row],
|
|
33088
|
+
size: this.getUserRowSize(sheet.id, row) ?? DEFAULT_CELL_HEIGHT,
|
|
33089
|
+
};
|
|
33012
33090
|
}
|
|
33013
33091
|
}
|
|
33014
33092
|
// Export col sizes
|
|
@@ -33016,7 +33094,7 @@ class HeaderSizePlugin extends CorePlugin {
|
|
|
33016
33094
|
sheet.cols = {};
|
|
33017
33095
|
}
|
|
33018
33096
|
for (let col of range(0, this.getters.getNumberCols(sheet.id))) {
|
|
33019
|
-
if (exportDefaults || this.sizes[sheet.id]["COL"][col]
|
|
33097
|
+
if (exportDefaults || this.sizes[sheet.id]["COL"][col]) {
|
|
33020
33098
|
sheet.cols[col] = { ...sheet.cols[col], size: this.getColSize(sheet.id, col) };
|
|
33021
33099
|
}
|
|
33022
33100
|
}
|
|
@@ -36572,7 +36650,7 @@ class EvaluationConditionalFormatPlugin extends UIPlugin {
|
|
|
36572
36650
|
case "ContainsText":
|
|
36573
36651
|
return cell.value.toString().indexOf(values[0].toString()) > -1;
|
|
36574
36652
|
case "NotContains":
|
|
36575
|
-
return !cell.value || cell.value.toString().indexOf(values[0].toString())
|
|
36653
|
+
return !cell.value || cell.value.toString().indexOf(values[0].toString()) === -1;
|
|
36576
36654
|
case "GreaterThan":
|
|
36577
36655
|
return cell.value > values[0];
|
|
36578
36656
|
case "GreaterThanOrEqual":
|
|
@@ -36599,6 +36677,156 @@ class EvaluationConditionalFormatPlugin extends UIPlugin {
|
|
|
36599
36677
|
};
|
|
36600
36678
|
}
|
|
36601
36679
|
|
|
36680
|
+
class UIRowSizePlugin extends UIPlugin {
|
|
36681
|
+
static getters = ["getRowSize"];
|
|
36682
|
+
tallestCellInRow = {};
|
|
36683
|
+
ctx = document.createElement("canvas").getContext("2d");
|
|
36684
|
+
handle(cmd) {
|
|
36685
|
+
switch (cmd.type) {
|
|
36686
|
+
case "START":
|
|
36687
|
+
for (const sheetId of this.getters.getSheetIds()) {
|
|
36688
|
+
this.initializeSheet(sheetId);
|
|
36689
|
+
}
|
|
36690
|
+
break;
|
|
36691
|
+
case "CREATE_SHEET": {
|
|
36692
|
+
this.initializeSheet(cmd.sheetId);
|
|
36693
|
+
break;
|
|
36694
|
+
}
|
|
36695
|
+
case "DUPLICATE_SHEET": {
|
|
36696
|
+
const tallestCells = deepCopy(this.tallestCellInRow[cmd.sheetId]);
|
|
36697
|
+
this.history.update("tallestCellInRow", cmd.sheetIdTo, tallestCells);
|
|
36698
|
+
break;
|
|
36699
|
+
}
|
|
36700
|
+
case "DELETE_SHEET":
|
|
36701
|
+
const tallestCells = { ...this.tallestCellInRow };
|
|
36702
|
+
delete tallestCells[cmd.sheetId];
|
|
36703
|
+
this.history.update("tallestCellInRow", tallestCells);
|
|
36704
|
+
break;
|
|
36705
|
+
case "REMOVE_COLUMNS_ROWS": {
|
|
36706
|
+
if (cmd.dimension === "COL") {
|
|
36707
|
+
return;
|
|
36708
|
+
}
|
|
36709
|
+
const tallestCells = removeIndexesFromArray(this.tallestCellInRow[cmd.sheetId], cmd.elements);
|
|
36710
|
+
this.history.update("tallestCellInRow", cmd.sheetId, tallestCells);
|
|
36711
|
+
break;
|
|
36712
|
+
}
|
|
36713
|
+
case "ADD_COLUMNS_ROWS": {
|
|
36714
|
+
if (cmd.dimension === "COL") {
|
|
36715
|
+
return;
|
|
36716
|
+
}
|
|
36717
|
+
const addIndex = getAddHeaderStartIndex(cmd.position, cmd.base);
|
|
36718
|
+
const newCells = Array(cmd.quantity).fill(undefined);
|
|
36719
|
+
const newTallestCells = insertItemsAtIndex(this.tallestCellInRow[cmd.sheetId], newCells, addIndex);
|
|
36720
|
+
this.history.update("tallestCellInRow", cmd.sheetId, newTallestCells);
|
|
36721
|
+
break;
|
|
36722
|
+
}
|
|
36723
|
+
case "RESIZE_COLUMNS_ROWS":
|
|
36724
|
+
{
|
|
36725
|
+
const sheetId = cmd.sheetId;
|
|
36726
|
+
if (cmd.dimension === "ROW") {
|
|
36727
|
+
for (const row of cmd.elements) {
|
|
36728
|
+
const tallestCell = this.getRowTallestCell(sheetId, row);
|
|
36729
|
+
this.history.update("tallestCellInRow", sheetId, row, tallestCell);
|
|
36730
|
+
}
|
|
36731
|
+
}
|
|
36732
|
+
else {
|
|
36733
|
+
// Recompute row heights on col size change, they might have changed because of wrapped text
|
|
36734
|
+
for (const row of range(0, this.getters.getNumberRows(sheetId))) {
|
|
36735
|
+
for (const col of cmd.elements) {
|
|
36736
|
+
this.updateRowSizeForCellChange(sheetId, row, col);
|
|
36737
|
+
}
|
|
36738
|
+
}
|
|
36739
|
+
}
|
|
36740
|
+
}
|
|
36741
|
+
break;
|
|
36742
|
+
case "UPDATE_CELL":
|
|
36743
|
+
this.updateRowSizeForCellChange(cmd.sheetId, cmd.row, cmd.col);
|
|
36744
|
+
break;
|
|
36745
|
+
case "ADD_MERGE":
|
|
36746
|
+
case "REMOVE_MERGE":
|
|
36747
|
+
for (const target of cmd.target) {
|
|
36748
|
+
for (const position of positions(target)) {
|
|
36749
|
+
this.updateRowSizeForCellChange(cmd.sheetId, position.row, position.col);
|
|
36750
|
+
}
|
|
36751
|
+
}
|
|
36752
|
+
}
|
|
36753
|
+
return;
|
|
36754
|
+
}
|
|
36755
|
+
getRowSize(sheetId, row) {
|
|
36756
|
+
return Math.round(this.getters.getUserRowSize(sheetId, row) ??
|
|
36757
|
+
this.tallestCellInRow[sheetId][row]?.size ??
|
|
36758
|
+
DEFAULT_CELL_HEIGHT);
|
|
36759
|
+
}
|
|
36760
|
+
updateRowSizeForCellChange(sheetId, row, col) {
|
|
36761
|
+
const tallestCellInRow = this.tallestCellInRow[sheetId]?.[row];
|
|
36762
|
+
if (tallestCellInRow?.cell.col === col) {
|
|
36763
|
+
const newTallestCell = this.getRowTallestCell(sheetId, row);
|
|
36764
|
+
this.history.update("tallestCellInRow", sheetId, row, newTallestCell);
|
|
36765
|
+
}
|
|
36766
|
+
const updatedCellHeight = this.getCellHeight({ sheetId, col, row });
|
|
36767
|
+
if (updatedCellHeight <= DEFAULT_CELL_HEIGHT) {
|
|
36768
|
+
return;
|
|
36769
|
+
}
|
|
36770
|
+
if ((!tallestCellInRow && updatedCellHeight > DEFAULT_CELL_HEIGHT) ||
|
|
36771
|
+
(tallestCellInRow && updatedCellHeight > tallestCellInRow.size)) {
|
|
36772
|
+
const newTallestCell = { cell: { sheetId, col, row }, size: updatedCellHeight };
|
|
36773
|
+
this.history.update("tallestCellInRow", sheetId, row, newTallestCell);
|
|
36774
|
+
}
|
|
36775
|
+
}
|
|
36776
|
+
initializeSheet(sheetId) {
|
|
36777
|
+
const tallestCells = [];
|
|
36778
|
+
for (let row = 0; row < this.getters.getNumberRows(sheetId); row++) {
|
|
36779
|
+
const tallestCell = this.getRowTallestCell(sheetId, row);
|
|
36780
|
+
tallestCells.push(tallestCell);
|
|
36781
|
+
}
|
|
36782
|
+
this.history.update("tallestCellInRow", sheetId, tallestCells);
|
|
36783
|
+
}
|
|
36784
|
+
/**
|
|
36785
|
+
* Return the height the cell should have in the sheet, which is either DEFAULT_CELL_HEIGHT if the cell is in a multi-row
|
|
36786
|
+
* merge, or the height of the cell computed based on its style/content.
|
|
36787
|
+
*/
|
|
36788
|
+
getCellHeight(position) {
|
|
36789
|
+
if (this.isInMultiRowMerge(position)) {
|
|
36790
|
+
return DEFAULT_CELL_HEIGHT;
|
|
36791
|
+
}
|
|
36792
|
+
const cell = this.getters.getCell(position);
|
|
36793
|
+
const colSize = this.getters.getColSize(position.sheetId, position.col);
|
|
36794
|
+
return getDefaultCellHeight(this.ctx, cell, colSize);
|
|
36795
|
+
}
|
|
36796
|
+
isInMultiRowMerge(position) {
|
|
36797
|
+
const merge = this.getters.getMerge(position);
|
|
36798
|
+
return !!merge && merge.bottom !== merge.top;
|
|
36799
|
+
}
|
|
36800
|
+
/**
|
|
36801
|
+
* Get the tallest cell of a row and its size.
|
|
36802
|
+
*/
|
|
36803
|
+
getRowTallestCell(sheetId, row) {
|
|
36804
|
+
const userRowSize = this.getters.getUserRowSize(sheetId, row);
|
|
36805
|
+
if (userRowSize !== undefined) {
|
|
36806
|
+
return undefined;
|
|
36807
|
+
}
|
|
36808
|
+
const cellIds = this.getters.getRowCells(sheetId, row);
|
|
36809
|
+
let maxHeight = 0;
|
|
36810
|
+
let tallestCell = undefined;
|
|
36811
|
+
for (let i = 0; i < cellIds.length; i++) {
|
|
36812
|
+
const cell = this.getters.getCellById(cellIds[i]);
|
|
36813
|
+
if (!cell) {
|
|
36814
|
+
continue;
|
|
36815
|
+
}
|
|
36816
|
+
const position = this.getters.getCellPosition(cell.id);
|
|
36817
|
+
const cellHeight = this.getCellHeight(position);
|
|
36818
|
+
if (cellHeight > maxHeight && cellHeight > DEFAULT_CELL_HEIGHT) {
|
|
36819
|
+
maxHeight = cellHeight;
|
|
36820
|
+
tallestCell = { cell: position, size: cellHeight };
|
|
36821
|
+
}
|
|
36822
|
+
}
|
|
36823
|
+
if (tallestCell && tallestCell.size > DEFAULT_CELL_HEIGHT) {
|
|
36824
|
+
return tallestCell;
|
|
36825
|
+
}
|
|
36826
|
+
return undefined;
|
|
36827
|
+
}
|
|
36828
|
+
}
|
|
36829
|
+
|
|
36602
36830
|
/**
|
|
36603
36831
|
* This plugin manage the autofill.
|
|
36604
36832
|
*
|
|
@@ -38300,6 +38528,7 @@ class FindAndReplacePlugin extends UIPlugin {
|
|
|
38300
38528
|
searchFormulas: false,
|
|
38301
38529
|
};
|
|
38302
38530
|
toSearch = "";
|
|
38531
|
+
isSearchDirty = false;
|
|
38303
38532
|
// ---------------------------------------------------------------------------
|
|
38304
38533
|
// Command Handling
|
|
38305
38534
|
// ---------------------------------------------------------------------------
|
|
@@ -38327,14 +38556,21 @@ class FindAndReplacePlugin extends UIPlugin {
|
|
|
38327
38556
|
case "REDO":
|
|
38328
38557
|
case "REMOVE_COLUMNS_ROWS":
|
|
38329
38558
|
case "ADD_COLUMNS_ROWS":
|
|
38330
|
-
|
|
38559
|
+
case "EVALUATE_CELLS":
|
|
38560
|
+
case "UPDATE_CELL":
|
|
38561
|
+
this.isSearchDirty = true;
|
|
38331
38562
|
break;
|
|
38332
38563
|
case "ACTIVATE_SHEET":
|
|
38333
|
-
case "REFRESH_SEARCH":
|
|
38334
38564
|
this.refreshSearch();
|
|
38335
38565
|
break;
|
|
38336
38566
|
}
|
|
38337
38567
|
}
|
|
38568
|
+
finalize() {
|
|
38569
|
+
if (this.isSearchDirty) {
|
|
38570
|
+
this.refreshSearch();
|
|
38571
|
+
this.isSearchDirty = false;
|
|
38572
|
+
}
|
|
38573
|
+
}
|
|
38338
38574
|
// ---------------------------------------------------------------------------
|
|
38339
38575
|
// Getters
|
|
38340
38576
|
// ---------------------------------------------------------------------------
|
|
@@ -38390,6 +38626,11 @@ class FindAndReplacePlugin extends UIPlugin {
|
|
|
38390
38626
|
if (this.toSearch) {
|
|
38391
38627
|
for (const cell of Object.values(cells)) {
|
|
38392
38628
|
const { col, row } = this.getters.getCellPosition(cell.id);
|
|
38629
|
+
const isColHidden = this.getters.isColHidden(sheetId, col);
|
|
38630
|
+
const isRowHidden = this.getters.isRowHidden(sheetId, row);
|
|
38631
|
+
if (isColHidden || isRowHidden) {
|
|
38632
|
+
continue;
|
|
38633
|
+
}
|
|
38393
38634
|
if (cell &&
|
|
38394
38635
|
this.currentSearchRegex &&
|
|
38395
38636
|
this.currentSearchRegex.test(this.getSearchableString({ sheetId, col, row }))) {
|
|
@@ -40032,10 +40273,8 @@ class SortPlugin extends UIPlugin {
|
|
|
40032
40273
|
}
|
|
40033
40274
|
updateCellCommands.push(newCellValues);
|
|
40034
40275
|
}
|
|
40035
|
-
for (const cmd of updateCellCommands) {
|
|
40036
|
-
this.dispatch("UPDATE_CELL", cmd);
|
|
40037
|
-
}
|
|
40038
40276
|
}
|
|
40277
|
+
updateCellCommands.forEach((cmdPayload) => this.dispatch("UPDATE_CELL", cmdPayload));
|
|
40039
40278
|
}
|
|
40040
40279
|
/**
|
|
40041
40280
|
* Return the distances between main merge cells in the zone.
|
|
@@ -40136,24 +40375,28 @@ class SheetUIPlugin extends UIPlugin {
|
|
|
40136
40375
|
// Getters
|
|
40137
40376
|
// ---------------------------------------------------------------------------
|
|
40138
40377
|
getCellWidth(position) {
|
|
40139
|
-
const text = this.getCellText(position);
|
|
40140
40378
|
const style = this.getters.getCellComputedStyle(position);
|
|
40141
|
-
|
|
40142
|
-
|
|
40379
|
+
let contentWidth = 0;
|
|
40380
|
+
const content = this.getters.getEvaluatedCell(position).formattedValue;
|
|
40381
|
+
if (content) {
|
|
40382
|
+
const multiLineText = splitTextToWidth(this.ctx, content, style, undefined);
|
|
40383
|
+
contentWidth += Math.max(...multiLineText.map((line) => computeTextWidth(this.ctx, line, style)));
|
|
40384
|
+
}
|
|
40143
40385
|
const icon = this.getters.getConditionalIcon(position);
|
|
40144
40386
|
if (icon) {
|
|
40145
|
-
contentWidth += computeIconWidth(
|
|
40387
|
+
contentWidth += computeIconWidth(style);
|
|
40146
40388
|
}
|
|
40147
40389
|
const isFilterHeader = this.getters.isFilterHeader(position);
|
|
40148
40390
|
if (isFilterHeader) {
|
|
40149
40391
|
contentWidth += ICON_EDGE_LENGTH + FILTER_ICON_MARGIN;
|
|
40150
40392
|
}
|
|
40151
|
-
if (contentWidth
|
|
40152
|
-
|
|
40153
|
-
|
|
40154
|
-
|
|
40155
|
-
|
|
40156
|
-
|
|
40393
|
+
if (contentWidth === 0) {
|
|
40394
|
+
return 0;
|
|
40395
|
+
}
|
|
40396
|
+
contentWidth += 2 * PADDING_AUTORESIZE_HORIZONTAL;
|
|
40397
|
+
if (style.wrapping === "wrap") {
|
|
40398
|
+
const colWidth = this.getters.getColSize(this.getters.getActiveSheetId(), position.col);
|
|
40399
|
+
return Math.min(colWidth, contentWidth);
|
|
40157
40400
|
}
|
|
40158
40401
|
return contentWidth;
|
|
40159
40402
|
}
|
|
@@ -40176,56 +40419,7 @@ class SheetUIPlugin extends UIPlugin {
|
|
|
40176
40419
|
getCellMultiLineText(position, width) {
|
|
40177
40420
|
const style = this.getters.getCellStyle(position);
|
|
40178
40421
|
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;
|
|
40422
|
+
return splitTextToWidth(this.ctx, text, style, width);
|
|
40229
40423
|
}
|
|
40230
40424
|
/**
|
|
40231
40425
|
* Returns the size, start and end coordinates of a column on an unfolded sheet
|
|
@@ -40284,26 +40478,6 @@ class SheetUIPlugin extends UIPlugin {
|
|
|
40284
40478
|
const sizes = cellsPositions.map((position) => this.getCellWidth({ sheetId, ...position }));
|
|
40285
40479
|
return Math.max(0, ...sizes);
|
|
40286
40480
|
}
|
|
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
40481
|
/**
|
|
40308
40482
|
* Check that any "sheetId" in the command matches an existing
|
|
40309
40483
|
* sheet.
|
|
@@ -41295,7 +41469,7 @@ class ClipboardCellsState extends ClipboardCellsAbstractState {
|
|
|
41295
41469
|
.join("\n") || "\t");
|
|
41296
41470
|
}
|
|
41297
41471
|
getHTMLContent() {
|
|
41298
|
-
if (this.cells.length
|
|
41472
|
+
if (this.cells.length === 1 && this.cells[0].length === 1) {
|
|
41299
41473
|
return this.getters.getCellText(this.cells[0][0].position);
|
|
41300
41474
|
}
|
|
41301
41475
|
let htmlTable = '<table border="1" style="border-collapse:collapse">';
|
|
@@ -41620,7 +41794,7 @@ class ClipboardPlugin extends UIPlugin {
|
|
|
41620
41794
|
status = "invisible";
|
|
41621
41795
|
state;
|
|
41622
41796
|
lastPasteState;
|
|
41623
|
-
|
|
41797
|
+
paintFormatStatus = "inactive";
|
|
41624
41798
|
originSheetId;
|
|
41625
41799
|
// ---------------------------------------------------------------------------
|
|
41626
41800
|
// Command Handling
|
|
@@ -41635,7 +41809,7 @@ class ClipboardPlugin extends UIPlugin {
|
|
|
41635
41809
|
if (!this.state) {
|
|
41636
41810
|
return 24 /* CommandResult.EmptyClipboard */;
|
|
41637
41811
|
}
|
|
41638
|
-
const pasteOption = cmd.pasteOption || (this.
|
|
41812
|
+
const pasteOption = cmd.pasteOption || (this.paintFormatStatus !== "inactive" ? "onlyFormat" : undefined);
|
|
41639
41813
|
return this.state.isPasteAllowed(cmd.target, { pasteOption });
|
|
41640
41814
|
case "PASTE_FROM_OS_CLIPBOARD": {
|
|
41641
41815
|
const state = new ClipboardOsState(cmd.text, this.getters, this.dispatch, this.selection);
|
|
@@ -41651,6 +41825,11 @@ class ClipboardPlugin extends UIPlugin {
|
|
|
41651
41825
|
const state = this.getClipboardStateForCopyCells(cut, "CUT");
|
|
41652
41826
|
return state.isPasteAllowed(paste);
|
|
41653
41827
|
}
|
|
41828
|
+
case "ACTIVATE_PAINT_FORMAT": {
|
|
41829
|
+
if (this.paintFormatStatus !== "inactive") {
|
|
41830
|
+
return 95 /* CommandResult.AlreadyInPaintingFormatMode */;
|
|
41831
|
+
}
|
|
41832
|
+
}
|
|
41654
41833
|
}
|
|
41655
41834
|
return 0 /* CommandResult.Success */;
|
|
41656
41835
|
}
|
|
@@ -41667,11 +41846,13 @@ class ClipboardPlugin extends UIPlugin {
|
|
|
41667
41846
|
if (!this.state) {
|
|
41668
41847
|
break;
|
|
41669
41848
|
}
|
|
41670
|
-
const pasteOption = cmd.pasteOption || (this.
|
|
41671
|
-
this._isPaintingFormat = false;
|
|
41849
|
+
const pasteOption = cmd.pasteOption || (this.paintFormatStatus !== "inactive" ? "onlyFormat" : undefined);
|
|
41672
41850
|
this.state.paste(cmd.target, { pasteOption, shouldPasteCF: true, selectTarget: true });
|
|
41673
41851
|
this.lastPasteState = this.state;
|
|
41674
|
-
this.
|
|
41852
|
+
if (this.paintFormatStatus === "oneOff") {
|
|
41853
|
+
this.paintFormatStatus = "inactive";
|
|
41854
|
+
this.status = "invisible";
|
|
41855
|
+
}
|
|
41675
41856
|
break;
|
|
41676
41857
|
case "CLEAN_CLIPBOARD_HIGHLIGHT":
|
|
41677
41858
|
this.status = "invisible";
|
|
@@ -41739,8 +41920,13 @@ class ClipboardPlugin extends UIPlugin {
|
|
|
41739
41920
|
case "ACTIVATE_PAINT_FORMAT": {
|
|
41740
41921
|
const zones = this.getters.getSelectedZones();
|
|
41741
41922
|
this.state = this.getClipboardStateForCopyCells(zones, "COPY");
|
|
41742
|
-
this._isPaintingFormat = true;
|
|
41743
41923
|
this.status = "visible";
|
|
41924
|
+
if (cmd.persistent) {
|
|
41925
|
+
this.paintFormatStatus = "persistent";
|
|
41926
|
+
}
|
|
41927
|
+
else {
|
|
41928
|
+
this.paintFormatStatus = "oneOff";
|
|
41929
|
+
}
|
|
41744
41930
|
break;
|
|
41745
41931
|
}
|
|
41746
41932
|
case "DELETE_SHEET":
|
|
@@ -41752,6 +41938,11 @@ class ClipboardPlugin extends UIPlugin {
|
|
|
41752
41938
|
this.status = "invisible";
|
|
41753
41939
|
}
|
|
41754
41940
|
break;
|
|
41941
|
+
case "CANCEL_PAINT_FORMAT": {
|
|
41942
|
+
this.paintFormatStatus = "inactive";
|
|
41943
|
+
this.status = "invisible";
|
|
41944
|
+
break;
|
|
41945
|
+
}
|
|
41755
41946
|
default:
|
|
41756
41947
|
if (isCoreCommand(cmd)) {
|
|
41757
41948
|
this.status = "invisible";
|
|
@@ -41782,7 +41973,7 @@ class ClipboardPlugin extends UIPlugin {
|
|
|
41782
41973
|
return this.state ? this.state.operation === "CUT" : false;
|
|
41783
41974
|
}
|
|
41784
41975
|
isPaintingFormat() {
|
|
41785
|
-
return this.
|
|
41976
|
+
return this.paintFormatStatus !== "inactive";
|
|
41786
41977
|
}
|
|
41787
41978
|
// ---------------------------------------------------------------------------
|
|
41788
41979
|
// Private methods
|
|
@@ -44417,6 +44608,7 @@ const coreViewsPluginRegistry = new Registry()
|
|
|
44417
44608
|
.add("evaluation", EvaluationPlugin)
|
|
44418
44609
|
.add("evaluation_chart", EvaluationChartPlugin)
|
|
44419
44610
|
.add("evaluation_cf", EvaluationConditionalFormatPlugin)
|
|
44611
|
+
.add("row_size", UIRowSizePlugin)
|
|
44420
44612
|
.add("custom_colors", CustomColorsPlugin);
|
|
44421
44613
|
|
|
44422
44614
|
const clickableCellRegistry = new Registry();
|
|
@@ -45539,6 +45731,11 @@ css /* scss */ `
|
|
|
45539
45731
|
.o-sidePanelButton:enabled {
|
|
45540
45732
|
cursor: pointer;
|
|
45541
45733
|
}
|
|
45734
|
+
|
|
45735
|
+
.o-sidePanelButton:disabled {
|
|
45736
|
+
color: ${DISABLED_TEXT_COLOR};
|
|
45737
|
+
}
|
|
45738
|
+
|
|
45542
45739
|
.o-sidePanelButton:last-child {
|
|
45543
45740
|
margin-right: 0px;
|
|
45544
45741
|
}
|
|
@@ -45672,11 +45869,11 @@ class ActionButton extends owl.Component {
|
|
|
45672
45869
|
}
|
|
45673
45870
|
get title() {
|
|
45674
45871
|
const name = this.actionButton.name(this.env);
|
|
45675
|
-
const description = this.actionButton.description;
|
|
45872
|
+
const description = this.actionButton.description(this.env);
|
|
45676
45873
|
return name + (description ? ` (${description})` : "");
|
|
45677
45874
|
}
|
|
45678
45875
|
get iconTitle() {
|
|
45679
|
-
return this.actionButton.icon;
|
|
45876
|
+
return this.actionButton.icon(this.env);
|
|
45680
45877
|
}
|
|
45681
45878
|
onClick(ev) {
|
|
45682
45879
|
if (this.isEnabled) {
|
|
@@ -46061,6 +46258,27 @@ FontSizeEditor.props = {
|
|
|
46061
46258
|
class: String,
|
|
46062
46259
|
};
|
|
46063
46260
|
|
|
46261
|
+
class PaintFormatButton extends owl.Component {
|
|
46262
|
+
static template = "o-spreadsheet-PaintFormatButton";
|
|
46263
|
+
get isActive() {
|
|
46264
|
+
return this.env.model.getters.isPaintingFormat();
|
|
46265
|
+
}
|
|
46266
|
+
onDblClick() {
|
|
46267
|
+
this.env.model.dispatch("ACTIVATE_PAINT_FORMAT", { persistent: true });
|
|
46268
|
+
}
|
|
46269
|
+
togglePaintFormat() {
|
|
46270
|
+
if (this.isActive) {
|
|
46271
|
+
this.env.model.dispatch("CANCEL_PAINT_FORMAT");
|
|
46272
|
+
}
|
|
46273
|
+
else {
|
|
46274
|
+
this.env.model.dispatch("ACTIVATE_PAINT_FORMAT", { persistent: false });
|
|
46275
|
+
}
|
|
46276
|
+
}
|
|
46277
|
+
}
|
|
46278
|
+
PaintFormatButton.props = {
|
|
46279
|
+
class: { type: String, optional: true },
|
|
46280
|
+
};
|
|
46281
|
+
|
|
46064
46282
|
// -----------------------------------------------------------------------------
|
|
46065
46283
|
// TopBar
|
|
46066
46284
|
// -----------------------------------------------------------------------------
|
|
@@ -46161,6 +46379,7 @@ class TopBar extends owl.Component {
|
|
|
46161
46379
|
TopBarComposer,
|
|
46162
46380
|
FontSizeEditor,
|
|
46163
46381
|
ActionButton,
|
|
46382
|
+
PaintFormatButton,
|
|
46164
46383
|
BorderEditorWidget,
|
|
46165
46384
|
};
|
|
46166
46385
|
state = owl.useState({
|
|
@@ -48013,7 +48232,7 @@ class SelectionStreamProcessorImpl {
|
|
|
48013
48232
|
: "nextCluster";
|
|
48014
48233
|
while (true) {
|
|
48015
48234
|
const nextCellPosition = this.getNextCellPosition(currentPosition, dim, dir);
|
|
48016
|
-
// Break if nextPosition
|
|
48235
|
+
// Break if nextPosition === currentPosition, which happens if there's no next valid position
|
|
48017
48236
|
if (currentPosition.col === nextCellPosition.col &&
|
|
48018
48237
|
currentPosition.row === nextCellPosition.row) {
|
|
48019
48238
|
break;
|
|
@@ -49927,6 +50146,7 @@ class Model extends EventBus {
|
|
|
49927
50146
|
...config,
|
|
49928
50147
|
mode: config.mode || "normal",
|
|
49929
50148
|
custom: config.custom || {},
|
|
50149
|
+
defaultCurrencyFormat: config.defaultCurrencyFormat || "[$$]#,##0.00",
|
|
49930
50150
|
external: this.setupExternalConfig(config.external || {}),
|
|
49931
50151
|
transportService,
|
|
49932
50152
|
client,
|
|
@@ -50224,6 +50444,7 @@ const helpers = {
|
|
|
50224
50444
|
numberToLetters,
|
|
50225
50445
|
UuidGenerator,
|
|
50226
50446
|
formatValue,
|
|
50447
|
+
createCurrencyFormat,
|
|
50227
50448
|
computeTextWidth,
|
|
50228
50449
|
createEmptyWorkbookData,
|
|
50229
50450
|
createEmptySheet,
|
|
@@ -50307,6 +50528,6 @@ exports.setTranslationMethod = setTranslationMethod;
|
|
|
50307
50528
|
exports.tokenize = tokenize;
|
|
50308
50529
|
|
|
50309
50530
|
|
|
50310
|
-
__info__.version = '16.4.
|
|
50311
|
-
__info__.date = '2023-07-
|
|
50312
|
-
__info__.hash = '
|
|
50531
|
+
__info__.version = '16.4.1';
|
|
50532
|
+
__info__.date = '2023-07-26T12:13:04.992Z';
|
|
50533
|
+
__info__.hash = 'e687fa9';
|