@odoo/o-spreadsheet 17.3.0-alpha.1 → 17.3.0-alpha.3
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 +1577 -752
- package/dist/o-spreadsheet.d.ts +182 -162
- package/dist/o-spreadsheet.esm.js +1577 -752
- package/dist/o-spreadsheet.iife.js +1577 -752
- package/dist/o-spreadsheet.iife.min.js +392 -304
- package/dist/o_spreadsheet.xml +240 -127
- package/package.json +5 -5
|
@@ -3,9 +3,9 @@
|
|
|
3
3
|
/**
|
|
4
4
|
* This file is generated by o-spreadsheet build tools. Do not edit it.
|
|
5
5
|
* @see https://github.com/odoo/o-spreadsheet
|
|
6
|
-
* @version 17.3.0-alpha.
|
|
7
|
-
* @date 2024-
|
|
8
|
-
* @hash
|
|
6
|
+
* @version 17.3.0-alpha.3
|
|
7
|
+
* @date 2024-04-10T12:28:23.658Z
|
|
8
|
+
* @hash 80b5056
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
11
|
'use strict';
|
|
@@ -190,7 +190,7 @@ const DEFAULT_GAUGE_LOWER_COLOR = "#cc0000";
|
|
|
190
190
|
const DEFAULT_GAUGE_MIDDLE_COLOR = "#f1c232";
|
|
191
191
|
const DEFAULT_GAUGE_UPPER_COLOR = "#6aa84f";
|
|
192
192
|
const DEFAULT_SCORECARD_BASELINE_MODE = "difference";
|
|
193
|
-
const DEFAULT_SCORECARD_BASELINE_COLOR_UP = "#
|
|
193
|
+
const DEFAULT_SCORECARD_BASELINE_COLOR_UP = "#6AA84F";
|
|
194
194
|
const DEFAULT_SCORECARD_BASELINE_COLOR_DOWN = "#E06666";
|
|
195
195
|
const LINE_FILL_TRANSPARENCY = 0.4;
|
|
196
196
|
// session
|
|
@@ -466,7 +466,7 @@ function getItemId(item, itemsDic) {
|
|
|
466
466
|
}
|
|
467
467
|
// Generate new Id if the item didn't exist in the dictionary
|
|
468
468
|
const ids = Object.keys(itemsDic);
|
|
469
|
-
const maxId = ids.length === 0 ? 0 :
|
|
469
|
+
const maxId = ids.length === 0 ? 0 : largeMax(ids.map((id) => parseInt(id, 10)));
|
|
470
470
|
itemsDic[maxId + 1] = item;
|
|
471
471
|
return maxId + 1;
|
|
472
472
|
}
|
|
@@ -484,7 +484,7 @@ function debounce(func, wait, immediate) {
|
|
|
484
484
|
let timeout = undefined;
|
|
485
485
|
const debounced = function () {
|
|
486
486
|
const context = this;
|
|
487
|
-
const args = arguments;
|
|
487
|
+
const args = Array.from(arguments);
|
|
488
488
|
function later() {
|
|
489
489
|
timeout = undefined;
|
|
490
490
|
if (!immediate) {
|
|
@@ -562,7 +562,7 @@ function deepEquals(o1, o2) {
|
|
|
562
562
|
if (typeof o1 !== typeof o2)
|
|
563
563
|
return false;
|
|
564
564
|
if (typeof o1 !== "object")
|
|
565
|
-
return
|
|
565
|
+
return false;
|
|
566
566
|
// Objects can have different keys if the values are undefined
|
|
567
567
|
for (const key in o2) {
|
|
568
568
|
if (!(key in o1) && o2[key] !== undefined) {
|
|
@@ -686,6 +686,34 @@ function getSearchRegex(searchStr, searchOptions) {
|
|
|
686
686
|
}
|
|
687
687
|
return RegExp(searchValue, flags);
|
|
688
688
|
}
|
|
689
|
+
/**
|
|
690
|
+
* Alternative to Math.max that works with large arrays.
|
|
691
|
+
* Typically useful for arrays bigger than 100k elements.
|
|
692
|
+
*/
|
|
693
|
+
function largeMax(array) {
|
|
694
|
+
let len = array.length;
|
|
695
|
+
if (len < 100_000)
|
|
696
|
+
return Math.max(...array);
|
|
697
|
+
let max = -Infinity;
|
|
698
|
+
while (len--) {
|
|
699
|
+
max = array[len] > max ? array[len] : max;
|
|
700
|
+
}
|
|
701
|
+
return max;
|
|
702
|
+
}
|
|
703
|
+
/**
|
|
704
|
+
* Alternative to Math.min that works with large arrays.
|
|
705
|
+
* Typically useful for arrays bigger than 100k elements.
|
|
706
|
+
*/
|
|
707
|
+
function largeMin(array) {
|
|
708
|
+
let len = array.length;
|
|
709
|
+
if (len < 100_000)
|
|
710
|
+
return Math.min(...array);
|
|
711
|
+
let min = +Infinity;
|
|
712
|
+
while (len--) {
|
|
713
|
+
min = array[len] < min ? array[len] : min;
|
|
714
|
+
}
|
|
715
|
+
return min;
|
|
716
|
+
}
|
|
689
717
|
|
|
690
718
|
const RBA_REGEX = /rgba?\(|\s+|\)/gi;
|
|
691
719
|
const HEX_MATCH = /^#([A-F\d]{2}){3,4}$/;
|
|
@@ -1858,6 +1886,11 @@ const invalidateCFEvaluationCommands = new Set([
|
|
|
1858
1886
|
"REMOVE_CONDITIONAL_FORMAT",
|
|
1859
1887
|
"CHANGE_CONDITIONAL_FORMAT_PRIORITY",
|
|
1860
1888
|
]);
|
|
1889
|
+
const invalidateBordersCommands = new Set([
|
|
1890
|
+
"AUTOFILL_CELL",
|
|
1891
|
+
"SET_BORDER",
|
|
1892
|
+
"SET_ZONE_BORDERS",
|
|
1893
|
+
]);
|
|
1861
1894
|
const readonlyAllowedCommands = new Set([
|
|
1862
1895
|
"START",
|
|
1863
1896
|
"ACTIVATE_SHEET",
|
|
@@ -2088,6 +2121,7 @@ exports.CommandResult = void 0;
|
|
|
2088
2121
|
CommandResult["InvalidCopyPasteSelection"] = "InvalidCopyPasteSelection";
|
|
2089
2122
|
CommandResult["NoChanges"] = "NoChanges";
|
|
2090
2123
|
CommandResult["InvalidInputId"] = "InvalidInputId";
|
|
2124
|
+
CommandResult["SheetIsHidden"] = "SheetIsHidden";
|
|
2091
2125
|
})(exports.CommandResult || (exports.CommandResult = {}));
|
|
2092
2126
|
|
|
2093
2127
|
const PLAIN_TEXT_FORMAT = "@"; // see OpenXML spec §18.8.31
|
|
@@ -2154,6 +2188,7 @@ const CellErrorType = {
|
|
|
2154
2188
|
BadExpression: "#BAD_EXPR",
|
|
2155
2189
|
CircularDependency: "#CYCLE",
|
|
2156
2190
|
UnknownFunction: "#NAME?",
|
|
2191
|
+
DivisionByZero: "#DIV/0!",
|
|
2157
2192
|
GenericError: "#ERROR",
|
|
2158
2193
|
};
|
|
2159
2194
|
const errorTypes = new Set(Object.values(CellErrorType));
|
|
@@ -2192,9 +2227,9 @@ class UnknownFunctionError extends EvaluationError {
|
|
|
2192
2227
|
|
|
2193
2228
|
// HELPERS
|
|
2194
2229
|
const SORT_TYPES_ORDER = ["number", "string", "boolean", "undefined"];
|
|
2195
|
-
function assert(condition, message) {
|
|
2230
|
+
function assert(condition, message, value) {
|
|
2196
2231
|
if (!condition()) {
|
|
2197
|
-
throw new EvaluationError(message);
|
|
2232
|
+
throw new EvaluationError(message, value);
|
|
2198
2233
|
}
|
|
2199
2234
|
}
|
|
2200
2235
|
function inferFormat(data) {
|
|
@@ -2272,6 +2307,9 @@ function strictToInteger(value, locale) {
|
|
|
2272
2307
|
function assertNumberGreaterThanOrEqualToOne(value) {
|
|
2273
2308
|
assert(() => value >= 1, _t("The function [[FUNCTION_NAME]] expects a number value to be greater than or equal to 1, but receives %s.", value.toString()));
|
|
2274
2309
|
}
|
|
2310
|
+
function assertNotZero(value) {
|
|
2311
|
+
assert(() => value !== 0, _t("Evaluation of function [[FUNCTION_NAME]] caused a divide by zero error."), CellErrorType.DivisionByZero);
|
|
2312
|
+
}
|
|
2275
2313
|
function toString(data) {
|
|
2276
2314
|
const value = toValue(data);
|
|
2277
2315
|
switch (typeof value) {
|
|
@@ -3037,6 +3075,9 @@ function applyInternalFormat(value, internalFormat, locale) {
|
|
|
3037
3075
|
return formattedValue;
|
|
3038
3076
|
}
|
|
3039
3077
|
function applyInternalNumberFormat(value, format, locale) {
|
|
3078
|
+
if (value === Infinity) {
|
|
3079
|
+
return "∞" + (format.isPercent ? "%" : "");
|
|
3080
|
+
}
|
|
3040
3081
|
if (format.isPercent) {
|
|
3041
3082
|
value = value * 100;
|
|
3042
3083
|
}
|
|
@@ -3380,6 +3421,46 @@ function roundFormat(format) {
|
|
|
3380
3421
|
});
|
|
3381
3422
|
return convertInternalFormatToFormat(roundedFormat);
|
|
3382
3423
|
}
|
|
3424
|
+
function humanizeNumber({ value, format }, locale) {
|
|
3425
|
+
const numberFormat = formatLargeNumber({
|
|
3426
|
+
value,
|
|
3427
|
+
format,
|
|
3428
|
+
}, undefined, locale);
|
|
3429
|
+
return formatValue(value, { format: numberFormat, locale });
|
|
3430
|
+
}
|
|
3431
|
+
function formatLargeNumber(arg, unit, locale) {
|
|
3432
|
+
let value = 0;
|
|
3433
|
+
try {
|
|
3434
|
+
value = Math.abs(toNumber(arg?.value, locale));
|
|
3435
|
+
}
|
|
3436
|
+
catch (e) {
|
|
3437
|
+
return "";
|
|
3438
|
+
}
|
|
3439
|
+
const format = arg?.format;
|
|
3440
|
+
if (unit !== undefined) {
|
|
3441
|
+
const postFix = unit?.value;
|
|
3442
|
+
switch (postFix) {
|
|
3443
|
+
case "k":
|
|
3444
|
+
return createLargeNumberFormat(format, 1e3, "k");
|
|
3445
|
+
case "m":
|
|
3446
|
+
return createLargeNumberFormat(format, 1e6, "m");
|
|
3447
|
+
case "b":
|
|
3448
|
+
return createLargeNumberFormat(format, 1e9, "b");
|
|
3449
|
+
default:
|
|
3450
|
+
throw new EvaluationError(_t("The formatting unit should be 'k', 'm' or 'b'."));
|
|
3451
|
+
}
|
|
3452
|
+
}
|
|
3453
|
+
if (value < 1e5) {
|
|
3454
|
+
return createLargeNumberFormat(format, 0, "");
|
|
3455
|
+
}
|
|
3456
|
+
else if (value < 1e8) {
|
|
3457
|
+
return createLargeNumberFormat(format, 1e3, "k");
|
|
3458
|
+
}
|
|
3459
|
+
else if (value < 1e11) {
|
|
3460
|
+
return createLargeNumberFormat(format, 1e6, "m");
|
|
3461
|
+
}
|
|
3462
|
+
return createLargeNumberFormat(format, 1e9, "b");
|
|
3463
|
+
}
|
|
3383
3464
|
function createLargeNumberFormat(format, magnitude, postFix, locale) {
|
|
3384
3465
|
const internalFormat = parseFormat(format || "#,##0");
|
|
3385
3466
|
const largeNumberFormat = [];
|
|
@@ -4559,8 +4640,9 @@ function computeTextLinesHeight(textLineHeight, numberOfLines = 1) {
|
|
|
4559
4640
|
* Get the default height of the cell given its style.
|
|
4560
4641
|
*/
|
|
4561
4642
|
function getDefaultCellHeight(ctx, cell, colSize) {
|
|
4562
|
-
if (!cell || !cell.content)
|
|
4643
|
+
if (!cell || (!cell.isFormula && !cell.content)) {
|
|
4563
4644
|
return DEFAULT_CELL_HEIGHT;
|
|
4645
|
+
}
|
|
4564
4646
|
const maxWidth = cell.style?.wrapping === "wrap" ? colSize - 2 * MIN_CELL_TEXT_MARGIN : undefined;
|
|
4565
4647
|
const numberOfLines = cell.isFormula
|
|
4566
4648
|
? 1
|
|
@@ -4571,14 +4653,19 @@ function getDefaultCellHeight(ctx, cell, colSize) {
|
|
|
4571
4653
|
const textWidthCache = {};
|
|
4572
4654
|
function computeTextWidth(context, text, style, fontUnit = "pt") {
|
|
4573
4655
|
const font = computeTextFont(style, fontUnit);
|
|
4656
|
+
context.save();
|
|
4657
|
+
context.font = font;
|
|
4658
|
+
const width = computeCachedTextWidth(context, text);
|
|
4659
|
+
context.restore();
|
|
4660
|
+
return width;
|
|
4661
|
+
}
|
|
4662
|
+
function computeCachedTextWidth(context, text) {
|
|
4663
|
+
const font = context.font;
|
|
4574
4664
|
if (!textWidthCache[font]) {
|
|
4575
4665
|
textWidthCache[font] = {};
|
|
4576
4666
|
}
|
|
4577
4667
|
if (textWidthCache[font][text] === undefined) {
|
|
4578
|
-
context.save();
|
|
4579
|
-
context.font = font;
|
|
4580
4668
|
const textWidth = context.measureText(text).width;
|
|
4581
|
-
context.restore();
|
|
4582
4669
|
textWidthCache[font][text] = textWidth;
|
|
4583
4670
|
}
|
|
4584
4671
|
return textWidthCache[font][text];
|
|
@@ -4723,6 +4810,42 @@ const pxRegex = /([0-9\.]*)px/;
|
|
|
4723
4810
|
function getContextFontSize(font) {
|
|
4724
4811
|
return Number(font.match(pxRegex)?.[1]);
|
|
4725
4812
|
}
|
|
4813
|
+
// Inspired from https://stackoverflow.com/a/10511598
|
|
4814
|
+
function clipTextWithEllipsis(ctx, text, maxWidth) {
|
|
4815
|
+
let width = computeCachedTextWidth(ctx, text);
|
|
4816
|
+
if (width <= maxWidth) {
|
|
4817
|
+
return text;
|
|
4818
|
+
}
|
|
4819
|
+
const ellipsis = "…";
|
|
4820
|
+
const ellipsisWidth = computeCachedTextWidth(ctx, text);
|
|
4821
|
+
if (width <= ellipsisWidth) {
|
|
4822
|
+
return text;
|
|
4823
|
+
}
|
|
4824
|
+
let len = text.length;
|
|
4825
|
+
while (width >= maxWidth - ellipsisWidth && len-- > 0) {
|
|
4826
|
+
text = text.substring(0, len);
|
|
4827
|
+
width = computeCachedTextWidth(ctx, text);
|
|
4828
|
+
}
|
|
4829
|
+
return text + ellipsis;
|
|
4830
|
+
}
|
|
4831
|
+
function splitTextInTwoLines(text) {
|
|
4832
|
+
let spaces = "";
|
|
4833
|
+
while (text[0] === " ") {
|
|
4834
|
+
spaces += " ";
|
|
4835
|
+
text = text.slice(1);
|
|
4836
|
+
}
|
|
4837
|
+
const length = text.length;
|
|
4838
|
+
const middle = Math.floor(length / 2);
|
|
4839
|
+
const leftSpace = text.substring(0, middle).lastIndexOf(" ");
|
|
4840
|
+
const rightSpace = text.substring(middle).indexOf(" ") + middle;
|
|
4841
|
+
if (leftSpace === -1 && rightSpace === middle - 1) {
|
|
4842
|
+
return [spaces + text, ""];
|
|
4843
|
+
}
|
|
4844
|
+
if (leftSpace > length - rightSpace || rightSpace === middle - 1) {
|
|
4845
|
+
return [spaces + text.slice(0, leftSpace), spaces + text.slice(leftSpace + 1)];
|
|
4846
|
+
}
|
|
4847
|
+
return [spaces + text.slice(0, rightSpace), spaces + text.slice(rightSpace + 1)];
|
|
4848
|
+
}
|
|
4726
4849
|
function drawDecoratedText(context, text, position, underline = false, strikethrough = false, strokeWidth = getContextFontSize(context.font) / 10 //This value is defined to get a good looking stroke
|
|
4727
4850
|
) {
|
|
4728
4851
|
context.fillText(text, position.x, position.y);
|
|
@@ -6994,10 +7117,17 @@ urlRegistry.add("sheet_URL", {
|
|
|
6994
7117
|
},
|
|
6995
7118
|
open(url, env) {
|
|
6996
7119
|
const sheetId = parseSheetUrl(url);
|
|
6997
|
-
env.model.dispatch("ACTIVATE_SHEET", {
|
|
7120
|
+
const result = env.model.dispatch("ACTIVATE_SHEET", {
|
|
6998
7121
|
sheetIdFrom: env.model.getters.getActiveSheetId(),
|
|
6999
7122
|
sheetIdTo: sheetId,
|
|
7000
7123
|
});
|
|
7124
|
+
if (result.isCancelledBecause("SheetIsHidden" /* CommandResult.SheetIsHidden */)) {
|
|
7125
|
+
env.notifyUser({
|
|
7126
|
+
type: "warning",
|
|
7127
|
+
sticky: false,
|
|
7128
|
+
text: _t("Cannot open the link because the linked sheet is hidden."),
|
|
7129
|
+
});
|
|
7130
|
+
}
|
|
7001
7131
|
},
|
|
7002
7132
|
sequence: 0,
|
|
7003
7133
|
});
|
|
@@ -7113,7 +7243,7 @@ function textCell(value, format, formattedValue) {
|
|
|
7113
7243
|
}
|
|
7114
7244
|
function numberCell(value, format, formattedValue) {
|
|
7115
7245
|
return {
|
|
7116
|
-
value: value || 0,
|
|
7246
|
+
value: value || 0, // necessary to avoid "-0" and NaN values,
|
|
7117
7247
|
format,
|
|
7118
7248
|
formattedValue,
|
|
7119
7249
|
type: CellValueType.number,
|
|
@@ -7359,6 +7489,7 @@ const CellIsOperators = {
|
|
|
7359
7489
|
};
|
|
7360
7490
|
const ChartTerms = {
|
|
7361
7491
|
Series: _t("Series"),
|
|
7492
|
+
BackgroundColor: _t("Background color"),
|
|
7362
7493
|
Errors: {
|
|
7363
7494
|
Unexpected: _t("The chart definition is invalid for an unknown reason"),
|
|
7364
7495
|
// BASIC CHART ERRORS (LINE | BAR | PIE)
|
|
@@ -9394,32 +9525,63 @@ function shouldRemoveFirstLabel(labelRange, dataset, dataSetsHaveTitle) {
|
|
|
9394
9525
|
}
|
|
9395
9526
|
return true;
|
|
9396
9527
|
}
|
|
9397
|
-
|
|
9398
|
-
|
|
9399
|
-
|
|
9400
|
-
|
|
9528
|
+
function getChartPositionAtCenterOfViewport(getters, chartSize) {
|
|
9529
|
+
const { x, y } = getters.getMainViewportCoordinates();
|
|
9530
|
+
const { scrollX, scrollY } = getters.getActiveSheetScrollInfo();
|
|
9531
|
+
const { width, height } = getters.getVisibleRect(getters.getActiveMainViewport());
|
|
9532
|
+
const position = {
|
|
9533
|
+
x: x + scrollX + Math.max(0, (width - chartSize.width) / 2),
|
|
9534
|
+
y: y + scrollY + Math.max(0, (height - chartSize.height) / 2),
|
|
9535
|
+
}; // Position at the center of the scrollable viewport
|
|
9536
|
+
return position;
|
|
9537
|
+
}
|
|
9538
|
+
|
|
9539
|
+
function getBaselineText(baseline, keyValue, baselineMode, humanize, locale) {
|
|
9401
9540
|
if (!baseline) {
|
|
9402
9541
|
return "";
|
|
9403
9542
|
}
|
|
9404
9543
|
else if (baselineMode === "text" ||
|
|
9405
9544
|
keyValue?.type !== CellValueType.number ||
|
|
9406
9545
|
baseline.type !== CellValueType.number) {
|
|
9546
|
+
if (humanize) {
|
|
9547
|
+
return humanizeNumber(baseline, locale);
|
|
9548
|
+
}
|
|
9407
9549
|
return baseline.formattedValue;
|
|
9408
9550
|
}
|
|
9551
|
+
let { value, format } = baseline;
|
|
9552
|
+
if (baselineMode === "progress") {
|
|
9553
|
+
value = keyValue.value / value;
|
|
9554
|
+
format = "0.0%";
|
|
9555
|
+
}
|
|
9409
9556
|
else {
|
|
9410
|
-
|
|
9411
|
-
if (baselineMode === "percentage" &&
|
|
9412
|
-
|
|
9557
|
+
value = Math.abs(keyValue.value - value);
|
|
9558
|
+
if (baselineMode === "percentage" && value !== 0) {
|
|
9559
|
+
value = value / baseline.value;
|
|
9560
|
+
}
|
|
9561
|
+
if (baselineMode === "percentage") {
|
|
9562
|
+
format = "0.0%";
|
|
9413
9563
|
}
|
|
9414
|
-
if (
|
|
9415
|
-
|
|
9564
|
+
if (!format) {
|
|
9565
|
+
value = Math.round(value * 100) / 100;
|
|
9416
9566
|
}
|
|
9417
|
-
const baselineStr = Math.abs(parseFloat(diff.toFixed(2))).toLocaleString();
|
|
9418
|
-
return baselineMode === "percentage" ? baselineStr + "%" : baselineStr;
|
|
9419
9567
|
}
|
|
9568
|
+
if (humanize) {
|
|
9569
|
+
return humanizeNumber({ value, format }, locale);
|
|
9570
|
+
}
|
|
9571
|
+
return formatValue(value, { format, locale });
|
|
9572
|
+
}
|
|
9573
|
+
function getKeyValueText(keyValueCell, humanize, locale) {
|
|
9574
|
+
if (!keyValueCell) {
|
|
9575
|
+
return "";
|
|
9576
|
+
}
|
|
9577
|
+
if (humanize) {
|
|
9578
|
+
return humanizeNumber(keyValueCell, locale);
|
|
9579
|
+
}
|
|
9580
|
+
return keyValueCell.formattedValue ?? String(keyValueCell.value ?? "");
|
|
9420
9581
|
}
|
|
9421
9582
|
function getBaselineColor(baseline, baselineMode, keyValue, colorUp, colorDown) {
|
|
9422
9583
|
if (baselineMode === "text" ||
|
|
9584
|
+
baselineMode === "progress" ||
|
|
9423
9585
|
baseline?.type !== CellValueType.number ||
|
|
9424
9586
|
keyValue?.type !== CellValueType.number) {
|
|
9425
9587
|
return undefined;
|
|
@@ -9448,17 +9610,6 @@ function getBaselineArrowDirection(baseline, keyValue, baselineMode) {
|
|
|
9448
9610
|
}
|
|
9449
9611
|
return "neutral";
|
|
9450
9612
|
}
|
|
9451
|
-
function getChartPositionAtCenterOfViewport(getters, chartSize) {
|
|
9452
|
-
const { x, y } = getters.getMainViewportCoordinates();
|
|
9453
|
-
const { scrollX, scrollY } = getters.getActiveSheetScrollInfo();
|
|
9454
|
-
const { width, height } = getters.getVisibleRect(getters.getActiveMainViewport());
|
|
9455
|
-
const position = {
|
|
9456
|
-
x: x + scrollX + Math.max(0, (width - chartSize.width) / 2),
|
|
9457
|
-
y: y + scrollY + Math.max(0, (height - chartSize.height) / 2),
|
|
9458
|
-
}; // Position at the center of the scrollable viewport
|
|
9459
|
-
return position;
|
|
9460
|
-
}
|
|
9461
|
-
|
|
9462
9613
|
function checkKeyValue(definition) {
|
|
9463
9614
|
return definition.keyValue && !rangeReference.test(definition.keyValue)
|
|
9464
9615
|
? "InvalidScorecardKeyValue" /* CommandResult.InvalidScorecardKeyValue */
|
|
@@ -9476,10 +9627,12 @@ let ScorecardChart$1 = class ScorecardChart extends AbstractChart {
|
|
|
9476
9627
|
baseline;
|
|
9477
9628
|
baselineMode;
|
|
9478
9629
|
baselineDescr;
|
|
9630
|
+
progressBar = false;
|
|
9479
9631
|
background;
|
|
9480
9632
|
baselineColorUp;
|
|
9481
9633
|
baselineColorDown;
|
|
9482
9634
|
fontColor;
|
|
9635
|
+
humanize;
|
|
9483
9636
|
type = "scorecard";
|
|
9484
9637
|
constructor(definition, sheetId, getters) {
|
|
9485
9638
|
super(definition, sheetId, getters);
|
|
@@ -9490,6 +9643,7 @@ let ScorecardChart$1 = class ScorecardChart extends AbstractChart {
|
|
|
9490
9643
|
this.background = definition.background;
|
|
9491
9644
|
this.baselineColorUp = definition.baselineColorUp;
|
|
9492
9645
|
this.baselineColorDown = definition.baselineColorDown;
|
|
9646
|
+
this.humanize = definition.humanize ?? false;
|
|
9493
9647
|
}
|
|
9494
9648
|
static validateChartDefinition(validator, definition) {
|
|
9495
9649
|
return validator.checkValidations(definition, checkKeyValue, checkBaseline);
|
|
@@ -9559,6 +9713,7 @@ let ScorecardChart$1 = class ScorecardChart extends AbstractChart {
|
|
|
9559
9713
|
keyValue: keyValue
|
|
9560
9714
|
? this.getters.getRangeString(keyValue, targetSheetId || this.sheetId)
|
|
9561
9715
|
: undefined,
|
|
9716
|
+
humanize: this.humanize,
|
|
9562
9717
|
};
|
|
9563
9718
|
}
|
|
9564
9719
|
getDefinitionForExcel() {
|
|
@@ -9584,7 +9739,7 @@ function drawScoreChart(structure, canvas) {
|
|
|
9584
9739
|
if (structure.title) {
|
|
9585
9740
|
ctx.font = structure.title.style.font;
|
|
9586
9741
|
ctx.fillStyle = structure.title.style.color;
|
|
9587
|
-
ctx.fillText(structure.title.text, structure.title.position.x, structure.title.position.y);
|
|
9742
|
+
ctx.fillText(clipTextWithEllipsis(ctx, structure.title.text, canvas.width - structure.title.position.x), structure.title.position.x, structure.title.position.y);
|
|
9588
9743
|
}
|
|
9589
9744
|
if (structure.baseline) {
|
|
9590
9745
|
ctx.font = structure.baseline.style.font;
|
|
@@ -9611,20 +9766,41 @@ function drawScoreChart(structure, canvas) {
|
|
|
9611
9766
|
ctx.restore();
|
|
9612
9767
|
}
|
|
9613
9768
|
if (structure.baselineDescr) {
|
|
9614
|
-
|
|
9615
|
-
ctx.
|
|
9616
|
-
ctx.
|
|
9769
|
+
const descr = structure.baselineDescr[0];
|
|
9770
|
+
ctx.font = descr.style.font;
|
|
9771
|
+
ctx.fillStyle = descr.style.color;
|
|
9772
|
+
for (const description of structure.baselineDescr) {
|
|
9773
|
+
ctx.fillText(clipTextWithEllipsis(ctx, description.text, canvas.width - description.position.x), description.position.x, description.position.y);
|
|
9774
|
+
}
|
|
9617
9775
|
}
|
|
9618
9776
|
if (structure.key) {
|
|
9619
9777
|
ctx.font = structure.key.style.font;
|
|
9620
9778
|
ctx.fillStyle = structure.key.style.color;
|
|
9621
9779
|
drawDecoratedText(ctx, structure.key.text, structure.key.position, structure.key.style.underline, structure.key.style.strikethrough);
|
|
9622
9780
|
}
|
|
9781
|
+
if (structure.progressBar) {
|
|
9782
|
+
ctx.fillStyle = structure.progressBar.style.backgroundColor;
|
|
9783
|
+
ctx.beginPath();
|
|
9784
|
+
ctx.roundRect(structure.progressBar.position.x, structure.progressBar.position.y, structure.progressBar.dimension.width, structure.progressBar.dimension.height, structure.progressBar.dimension.height / 2);
|
|
9785
|
+
ctx.fill();
|
|
9786
|
+
ctx.fillStyle = structure.progressBar.style.color;
|
|
9787
|
+
ctx.beginPath();
|
|
9788
|
+
if (structure.progressBar.value > 0) {
|
|
9789
|
+
ctx.roundRect(structure.progressBar.position.x, structure.progressBar.position.y, structure.progressBar.dimension.width *
|
|
9790
|
+
Math.max(0, Math.min(1.0, structure.progressBar.value)), structure.progressBar.dimension.height, structure.progressBar.dimension.height / 2);
|
|
9791
|
+
}
|
|
9792
|
+
else {
|
|
9793
|
+
const width = structure.progressBar.dimension.width *
|
|
9794
|
+
Math.max(0, Math.min(1.0, -structure.progressBar.value));
|
|
9795
|
+
ctx.roundRect(structure.progressBar.position.x + structure.progressBar.dimension.width - width, structure.progressBar.position.y, width, structure.progressBar.dimension.height, structure.progressBar.dimension.height / 2);
|
|
9796
|
+
}
|
|
9797
|
+
ctx.fill();
|
|
9798
|
+
}
|
|
9623
9799
|
}
|
|
9624
9800
|
function createScorecardChartRuntime(chart, getters) {
|
|
9625
|
-
let keyValue = "";
|
|
9626
9801
|
let formattedKeyValue = "";
|
|
9627
9802
|
let keyValueCell;
|
|
9803
|
+
const locale = getters.getLocale();
|
|
9628
9804
|
if (chart.keyValue) {
|
|
9629
9805
|
const keyValuePosition = {
|
|
9630
9806
|
sheetId: chart.keyValue.sheetId,
|
|
@@ -9632,31 +9808,33 @@ function createScorecardChartRuntime(chart, getters) {
|
|
|
9632
9808
|
row: chart.keyValue.zone.top,
|
|
9633
9809
|
};
|
|
9634
9810
|
keyValueCell = getters.getEvaluatedCell(keyValuePosition);
|
|
9635
|
-
|
|
9636
|
-
formattedKeyValue = keyValueCell.formattedValue;
|
|
9811
|
+
formattedKeyValue = getKeyValueText(keyValueCell, chart.humanize ?? false, locale);
|
|
9637
9812
|
}
|
|
9638
9813
|
let baselineCell;
|
|
9639
9814
|
const baseline = chart.baseline;
|
|
9640
9815
|
if (baseline) {
|
|
9641
9816
|
const baselinePosition = {
|
|
9642
|
-
sheetId:
|
|
9643
|
-
col:
|
|
9644
|
-
row:
|
|
9817
|
+
sheetId: baseline.sheetId,
|
|
9818
|
+
col: baseline.zone.left,
|
|
9819
|
+
row: baseline.zone.top,
|
|
9645
9820
|
};
|
|
9646
9821
|
baselineCell = getters.getEvaluatedCell(baselinePosition);
|
|
9647
9822
|
}
|
|
9648
9823
|
const { background, fontColor } = getters.getStyleOfSingleCellChart(chart.background, chart.keyValue);
|
|
9649
|
-
const
|
|
9824
|
+
const baselineDisplay = getBaselineText(baselineCell, keyValueCell, chart.baselineMode, chart.humanize ?? false, locale);
|
|
9825
|
+
const baselineValue = chart.baselineMode === "progress" && isNumber(baselineDisplay, locale)
|
|
9826
|
+
? toNumber(baselineDisplay, locale)
|
|
9827
|
+
: 0;
|
|
9650
9828
|
return {
|
|
9651
9829
|
title: _t(chart.title),
|
|
9652
|
-
keyValue: formattedKeyValue
|
|
9653
|
-
baselineDisplay
|
|
9830
|
+
keyValue: formattedKeyValue,
|
|
9831
|
+
baselineDisplay,
|
|
9654
9832
|
baselineArrow: getBaselineArrowDirection(baselineCell, keyValueCell, chart.baselineMode),
|
|
9655
9833
|
baselineColor: getBaselineColor(baselineCell, chart.baselineMode, keyValueCell, chart.baselineColorUp, chart.baselineColorDown),
|
|
9656
|
-
baselineDescr: chart.baselineDescr ? _t(chart.baselineDescr) : "",
|
|
9834
|
+
baselineDescr: chart.baselineMode !== "progress" && chart.baselineDescr ? _t(chart.baselineDescr) : "",
|
|
9657
9835
|
fontColor,
|
|
9658
9836
|
background,
|
|
9659
|
-
baselineStyle: chart.baselineMode !== "percentage" && baseline
|
|
9837
|
+
baselineStyle: chart.baselineMode !== "percentage" && chart.baselineMode !== "progress" && baseline
|
|
9660
9838
|
? getters.getCellStyle({
|
|
9661
9839
|
sheetId: baseline.sheetId,
|
|
9662
9840
|
col: baseline.zone.left,
|
|
@@ -9670,17 +9848,21 @@ function createScorecardChartRuntime(chart, getters) {
|
|
|
9670
9848
|
row: chart.keyValue.zone.top,
|
|
9671
9849
|
})
|
|
9672
9850
|
: undefined,
|
|
9851
|
+
progressBar: chart.baselineMode === "progress"
|
|
9852
|
+
? {
|
|
9853
|
+
value: baselineValue,
|
|
9854
|
+
color: baselineValue > 0 ? chart.baselineColorUp : chart.baselineColorDown,
|
|
9855
|
+
}
|
|
9856
|
+
: undefined,
|
|
9673
9857
|
};
|
|
9674
9858
|
}
|
|
9675
9859
|
|
|
9676
9860
|
/* Sizes of boxes containing the texts, in percentage of the Chart size */
|
|
9677
9861
|
const TITLE_FONT_SIZE = 18;
|
|
9678
|
-
const
|
|
9679
|
-
|
|
9680
|
-
|
|
9681
|
-
const
|
|
9682
|
-
/* Padding at the border of the chart, in percentage of the chart width */
|
|
9683
|
-
const CHART_PADDING_RATIO = 0.02;
|
|
9862
|
+
const KEY_BOX_HEIGHT_RATIO = 0.8;
|
|
9863
|
+
/* Padding at the border of the chart */
|
|
9864
|
+
const CHART_PADDING = 10;
|
|
9865
|
+
const BOTTOM_PADDING_RATIO = 0.05;
|
|
9684
9866
|
/**
|
|
9685
9867
|
* Line height (in em)
|
|
9686
9868
|
* Having a line heigh =1em (=font size) don't work, the font will overflow.
|
|
@@ -9720,31 +9902,37 @@ class ScorecardChartConfigBuilder {
|
|
|
9720
9902
|
},
|
|
9721
9903
|
};
|
|
9722
9904
|
const style = this.getTextStyles();
|
|
9723
|
-
|
|
9905
|
+
let titleHeight = 0;
|
|
9724
9906
|
if (this.title) {
|
|
9907
|
+
({ height: titleHeight } = this.getFullTextDimensions(this.title, style.title.font));
|
|
9725
9908
|
structure.title = {
|
|
9726
9909
|
text: this.title,
|
|
9727
9910
|
style: style.title,
|
|
9728
9911
|
position: {
|
|
9729
|
-
x:
|
|
9730
|
-
y:
|
|
9912
|
+
x: CHART_PADDING,
|
|
9913
|
+
y: CHART_PADDING / 2 + titleHeight,
|
|
9731
9914
|
},
|
|
9732
9915
|
};
|
|
9733
9916
|
}
|
|
9734
9917
|
const baselineArrowSize = style.baselineArrow?.size ?? 0;
|
|
9735
|
-
|
|
9736
|
-
|
|
9918
|
+
let { height: baselineHeight, width: baselineWidth } = this.getTextDimensions(this.baseline, style.baselineValue.font);
|
|
9919
|
+
if (!this.baseline) {
|
|
9920
|
+
baselineHeight = this.getTextDimensions(this.baselineDescr, style.baselineDescr.font).height;
|
|
9921
|
+
}
|
|
9922
|
+
const baselineDescrWidth = style.baselineDescr.isSplit
|
|
9923
|
+
? Math.max(...splitTextInTwoLines(this.baselineDescr).map((line) => this.getTextDimensions(line, style.baselineDescr.font).width))
|
|
9924
|
+
: this.getTextDimensions(this.baselineDescr, style.baselineDescr.font).width;
|
|
9737
9925
|
structure.baseline = {
|
|
9738
9926
|
text: this.baseline,
|
|
9739
9927
|
style: style.baselineValue,
|
|
9740
9928
|
position: {
|
|
9741
9929
|
x: (this.width - baselineWidth - baselineDescrWidth + baselineArrowSize) / 2,
|
|
9742
9930
|
y: this.keyValue
|
|
9743
|
-
? this.height -
|
|
9744
|
-
: this.height - (this.height - titleHeight - baselineHeight) / 2 -
|
|
9931
|
+
? this.height * (1 - BOTTOM_PADDING_RATIO * (this.runtime.progressBar ? 1 : 2))
|
|
9932
|
+
: this.height - (this.height - titleHeight - baselineHeight) / 2 - CHART_PADDING,
|
|
9745
9933
|
},
|
|
9746
9934
|
};
|
|
9747
|
-
if (style.baselineArrow) {
|
|
9935
|
+
if (style.baselineArrow && !this.runtime.progressBar) {
|
|
9748
9936
|
structure.baselineArrow = {
|
|
9749
9937
|
direction: this.baselineArrow,
|
|
9750
9938
|
style: style.baselineArrow,
|
|
@@ -9755,23 +9943,68 @@ class ScorecardChartConfigBuilder {
|
|
|
9755
9943
|
};
|
|
9756
9944
|
}
|
|
9757
9945
|
if (this.baselineDescr) {
|
|
9758
|
-
|
|
9759
|
-
|
|
9760
|
-
|
|
9946
|
+
const position = {
|
|
9947
|
+
x: structure.baseline.position.x + baselineWidth,
|
|
9948
|
+
y: structure.baseline.position.y,
|
|
9949
|
+
};
|
|
9950
|
+
if (style.baselineDescr.isSplit) {
|
|
9951
|
+
const description = splitTextInTwoLines(this.baselineDescr);
|
|
9952
|
+
const measure = this.context.measureText(description[1]);
|
|
9953
|
+
const deltaY = measure.fontBoundingBoxAscent + measure.fontBoundingBoxDescent;
|
|
9954
|
+
structure.baselineDescr = [
|
|
9955
|
+
{
|
|
9956
|
+
text: description[0],
|
|
9957
|
+
style: style.baselineDescr,
|
|
9958
|
+
position: {
|
|
9959
|
+
x: position.x,
|
|
9960
|
+
y: position.y - deltaY,
|
|
9961
|
+
},
|
|
9962
|
+
},
|
|
9963
|
+
{
|
|
9964
|
+
text: description[1],
|
|
9965
|
+
style: style.baselineDescr,
|
|
9966
|
+
position,
|
|
9967
|
+
},
|
|
9968
|
+
];
|
|
9969
|
+
}
|
|
9970
|
+
else {
|
|
9971
|
+
structure.baselineDescr = [
|
|
9972
|
+
{
|
|
9973
|
+
text: this.baselineDescr,
|
|
9974
|
+
style: style.baselineDescr,
|
|
9975
|
+
position,
|
|
9976
|
+
},
|
|
9977
|
+
];
|
|
9978
|
+
}
|
|
9979
|
+
}
|
|
9980
|
+
let progressBarHeight = 0;
|
|
9981
|
+
if (this.runtime.progressBar) {
|
|
9982
|
+
progressBarHeight = this.height * 0.05;
|
|
9983
|
+
structure.progressBar = {
|
|
9761
9984
|
position: {
|
|
9762
|
-
x:
|
|
9763
|
-
y:
|
|
9985
|
+
x: 2 * CHART_PADDING,
|
|
9986
|
+
y: this.height * (1 - 2 * BOTTOM_PADDING_RATIO) - baselineHeight - progressBarHeight,
|
|
9987
|
+
},
|
|
9988
|
+
dimension: {
|
|
9989
|
+
height: progressBarHeight,
|
|
9990
|
+
width: this.width - 4 * CHART_PADDING,
|
|
9991
|
+
},
|
|
9992
|
+
value: this.runtime.progressBar.value,
|
|
9993
|
+
style: {
|
|
9994
|
+
color: this.runtime.progressBar.color,
|
|
9995
|
+
backgroundColor: this.secondaryFontColor,
|
|
9764
9996
|
},
|
|
9765
9997
|
};
|
|
9766
9998
|
}
|
|
9767
|
-
const {
|
|
9999
|
+
const { width: keyWidth, height: keyHeight } = this.getFullTextDimensions(this.keyValue, style.keyValue.font);
|
|
9768
10000
|
if (this.keyValue) {
|
|
9769
10001
|
structure.key = {
|
|
9770
10002
|
text: this.keyValue,
|
|
9771
10003
|
style: style.keyValue,
|
|
9772
10004
|
position: {
|
|
9773
10005
|
x: (this.width - keyWidth) / 2,
|
|
9774
|
-
y:
|
|
10006
|
+
y: this.height * (0.5 - BOTTOM_PADDING_RATIO * 2) +
|
|
10007
|
+
(titleHeight + keyHeight / (this.baseline || this.baselineDescr ? 2 : 1.2)) / 2,
|
|
9775
10008
|
},
|
|
9776
10009
|
};
|
|
9777
10010
|
}
|
|
@@ -9798,9 +10031,6 @@ class ScorecardChartConfigBuilder {
|
|
|
9798
10031
|
get secondaryFontColor() {
|
|
9799
10032
|
return relativeLuminance(this.backgroundColor) > 0.3 ? "#525252" : "#C8C8C8";
|
|
9800
10033
|
}
|
|
9801
|
-
get chartPadding() {
|
|
9802
|
-
return this.width * CHART_PADDING_RATIO;
|
|
9803
|
-
}
|
|
9804
10034
|
getTextDimensions(text, font) {
|
|
9805
10035
|
this.context.font = font;
|
|
9806
10036
|
const measure = this.context.measureText(text);
|
|
@@ -9809,16 +10039,44 @@ class ScorecardChartConfigBuilder {
|
|
|
9809
10039
|
height: measure.actualBoundingBoxAscent + measure.actualBoundingBoxDescent,
|
|
9810
10040
|
};
|
|
9811
10041
|
}
|
|
10042
|
+
getFullTextDimensions(text, font) {
|
|
10043
|
+
this.context.font = font;
|
|
10044
|
+
const measure = this.context.measureText(text);
|
|
10045
|
+
return {
|
|
10046
|
+
width: measure.width,
|
|
10047
|
+
height: measure.fontBoundingBoxAscent + measure.fontBoundingBoxDescent,
|
|
10048
|
+
};
|
|
10049
|
+
}
|
|
9812
10050
|
getTextStyles() {
|
|
9813
10051
|
// If the widest text overflows horizontally, scale it down, and apply the same scaling factors to all the other fonts.
|
|
9814
|
-
const maxLineWidth = this.width
|
|
9815
|
-
const
|
|
9816
|
-
const baseFontSize = widestElement.getElementMaxFontSize(this.getDrawableHeight(), this);
|
|
9817
|
-
const fontSizeMatchingWidth = getFontSizeMatchingWidth(maxLineWidth, baseFontSize, (fontSize) => widestElement.getElementWidth(fontSize, this.context, this));
|
|
9818
|
-
let scalingFactor = fontSizeMatchingWidth / baseFontSize;
|
|
10052
|
+
const maxLineWidth = this.width - 2 * CHART_PADDING;
|
|
10053
|
+
const drawableHeight = this.getDrawableHeight();
|
|
9819
10054
|
// Fonts sizes in px
|
|
9820
|
-
const
|
|
9821
|
-
const
|
|
10055
|
+
const keyValueElement = new KeyValueElement(this.runtime.keyValueStyle);
|
|
10056
|
+
const heightFont = keyValueElement.getElementMaxFontSize(drawableHeight, this);
|
|
10057
|
+
const widthFont = getFontSizeMatchingWidth(maxLineWidth, 600, (fontSize) => keyValueElement.getElementWidth(fontSize, this.context, this));
|
|
10058
|
+
const keyFontSize = Math.min(heightFont, widthFont);
|
|
10059
|
+
let baselineValueFontSize = Math.floor(keyFontSize * 0.5);
|
|
10060
|
+
this.context.font = getDefaultContextFont(baselineValueFontSize, this.runtime.baselineStyle?.bold, this.runtime.baselineStyle?.italic);
|
|
10061
|
+
const baselineText = this.baselineArrow !== "neutral" ? "A " + this.baseline : this.baseline;
|
|
10062
|
+
const baselineValueWidth = computeCachedTextWidth(this.context, baselineText);
|
|
10063
|
+
const remainingWidth = maxLineWidth - baselineValueWidth;
|
|
10064
|
+
let baselineDescrFontSize = getFontSizeMatchingWidth(remainingWidth, baselineValueFontSize, (fontSize) => computeTextWidth(this.context, this.baselineDescr, { fontSize }));
|
|
10065
|
+
let isBaselineSplit = false;
|
|
10066
|
+
if (baselineDescrFontSize < baselineValueFontSize / 2.5) {
|
|
10067
|
+
isBaselineSplit = true;
|
|
10068
|
+
baselineDescrFontSize = Math.floor(baselineValueFontSize / 2.5);
|
|
10069
|
+
for (const line of splitTextInTwoLines(this.baselineDescr)) {
|
|
10070
|
+
const lineWidth = getFontSizeMatchingWidth(remainingWidth, baselineValueFontSize, (fontSize) => {
|
|
10071
|
+
this.context.font = getDefaultContextFont(fontSize);
|
|
10072
|
+
return this.context.measureText(line).width;
|
|
10073
|
+
});
|
|
10074
|
+
baselineDescrFontSize = Math.min(baselineDescrFontSize, lineWidth);
|
|
10075
|
+
}
|
|
10076
|
+
}
|
|
10077
|
+
if (this.runtime.progressBar) {
|
|
10078
|
+
baselineValueFontSize /= 1.5;
|
|
10079
|
+
}
|
|
9822
10080
|
return {
|
|
9823
10081
|
title: {
|
|
9824
10082
|
font: getDefaultContextFont(TITLE_FONT_SIZE),
|
|
@@ -9831,7 +10089,7 @@ class ScorecardChartConfigBuilder {
|
|
|
9831
10089
|
underline: this.runtime.keyValueStyle?.underline,
|
|
9832
10090
|
},
|
|
9833
10091
|
baselineValue: {
|
|
9834
|
-
font: getDefaultContextFont(
|
|
10092
|
+
font: getDefaultContextFont(baselineValueFontSize, this.runtime.baselineStyle?.bold, this.runtime.baselineStyle?.italic),
|
|
9835
10093
|
strikethrough: this.runtime.baselineStyle?.strikethrough,
|
|
9836
10094
|
underline: this.runtime.baselineStyle?.underline,
|
|
9837
10095
|
color: this.runtime.baselineStyle?.textColor ||
|
|
@@ -9839,33 +10097,25 @@ class ScorecardChartConfigBuilder {
|
|
|
9839
10097
|
this.secondaryFontColor,
|
|
9840
10098
|
},
|
|
9841
10099
|
baselineDescr: {
|
|
9842
|
-
font: getDefaultContextFont(
|
|
10100
|
+
font: getDefaultContextFont(baselineDescrFontSize),
|
|
10101
|
+
isSplit: isBaselineSplit,
|
|
9843
10102
|
color: this.secondaryFontColor,
|
|
9844
10103
|
},
|
|
9845
|
-
baselineArrow: this.baselineArrow === "neutral"
|
|
10104
|
+
baselineArrow: this.baselineArrow === "neutral" || this.runtime.progressBar
|
|
9846
10105
|
? undefined
|
|
9847
10106
|
: {
|
|
9848
|
-
size: this.keyValue ? 0.8 *
|
|
10107
|
+
size: this.keyValue ? 0.8 * baselineValueFontSize : 0,
|
|
9849
10108
|
color: this.runtime.baselineColor || this.secondaryFontColor,
|
|
9850
10109
|
},
|
|
9851
10110
|
};
|
|
9852
10111
|
}
|
|
9853
10112
|
/** Get the height of the chart minus all the vertical paddings */
|
|
9854
10113
|
getDrawableHeight() {
|
|
9855
|
-
const verticalPadding =
|
|
9856
|
-
let availableHeight = this.height - verticalPadding;
|
|
10114
|
+
const verticalPadding = CHART_PADDING + this.height * BOTTOM_PADDING_RATIO;
|
|
10115
|
+
let availableHeight = this.height - 2 * verticalPadding;
|
|
9857
10116
|
availableHeight -= this.title ? TITLE_FONT_SIZE * LINE_HEIGHT : 0;
|
|
9858
10117
|
return availableHeight;
|
|
9859
10118
|
}
|
|
9860
|
-
/** Return the element with he widest text in the chart */
|
|
9861
|
-
getWidestElement() {
|
|
9862
|
-
const baseline = new BaselineElement(this.runtime.baselineStyle);
|
|
9863
|
-
const keyValue = new KeyValueElement(this.runtime.keyValueStyle);
|
|
9864
|
-
return baseline.getElementWidth(BASELINE_BOX_HEIGHT_RATIO, this.context, this) >
|
|
9865
|
-
keyValue.getElementWidth(KEY_BOX_HEIGHT_RATIO, this.context, this)
|
|
9866
|
-
? baseline
|
|
9867
|
-
: keyValue;
|
|
9868
|
-
}
|
|
9869
10119
|
}
|
|
9870
10120
|
class ScorecardScalableElement {
|
|
9871
10121
|
style;
|
|
@@ -9874,29 +10124,7 @@ class ScorecardScalableElement {
|
|
|
9874
10124
|
}
|
|
9875
10125
|
measureTextWidth(ctx, text, fontSize) {
|
|
9876
10126
|
ctx.font = getDefaultContextFont(fontSize, this.style.bold, this.style.italic);
|
|
9877
|
-
return ctx
|
|
9878
|
-
}
|
|
9879
|
-
}
|
|
9880
|
-
class BaselineElement extends ScorecardScalableElement {
|
|
9881
|
-
getElementWidth(fontSize, ctx, chart) {
|
|
9882
|
-
if (!chart.runtime) {
|
|
9883
|
-
return 0;
|
|
9884
|
-
}
|
|
9885
|
-
const baselineStr = chart.baseline;
|
|
9886
|
-
// Put mock text to simulate the width of the up/down arrow
|
|
9887
|
-
const largeText = chart.baselineArrow !== "neutral" ? "A " + baselineStr : baselineStr;
|
|
9888
|
-
let textWidth = this.measureTextWidth(ctx, largeText, fontSize);
|
|
9889
|
-
// Baseline descr font size should be smaller than baseline font size
|
|
9890
|
-
textWidth += this.measureTextWidth(ctx, chart.baselineDescr, fontSize * BASELINE_DESCR_FONT_RATIO);
|
|
9891
|
-
return textWidth;
|
|
9892
|
-
}
|
|
9893
|
-
getElementMaxFontSize(availableHeight, chart) {
|
|
9894
|
-
if (!chart.runtime) {
|
|
9895
|
-
return 0;
|
|
9896
|
-
}
|
|
9897
|
-
const haveBaseline = chart.baseline !== "" || chart.baselineDescr;
|
|
9898
|
-
const maxHeight = haveBaseline ? BASELINE_BOX_HEIGHT_RATIO * availableHeight : 0;
|
|
9899
|
-
return maxHeight / LINE_HEIGHT;
|
|
10127
|
+
return computeCachedTextWidth(ctx, text);
|
|
9900
10128
|
}
|
|
9901
10129
|
}
|
|
9902
10130
|
class KeyValueElement extends ScorecardScalableElement {
|
|
@@ -9957,11 +10185,11 @@ autoCompleteProviders.add("dataValidation", {
|
|
|
9957
10185
|
}
|
|
9958
10186
|
else {
|
|
9959
10187
|
const range = this.getters.getRangeFromSheetXC(position.sheetId, rule.criterion.values[0]);
|
|
9960
|
-
values = this.getters
|
|
10188
|
+
values = Array.from(new Set(this.getters
|
|
9961
10189
|
.getRangeValues(range)
|
|
9962
10190
|
.filter(isNotNull)
|
|
9963
10191
|
.map((value) => value.toString())
|
|
9964
|
-
.filter((val) => val !== "");
|
|
10192
|
+
.filter((val) => val !== "")));
|
|
9965
10193
|
}
|
|
9966
10194
|
return values.map((value) => ({ text: value }));
|
|
9967
10195
|
},
|
|
@@ -10826,33 +11054,6 @@ var array = /*#__PURE__*/Object.freeze({
|
|
|
10826
11054
|
// -----------------------------------------------------------------------------
|
|
10827
11055
|
// FORMAT.LARGE.NUMBER
|
|
10828
11056
|
// -----------------------------------------------------------------------------
|
|
10829
|
-
function formatLargeNumber(arg, unit, locale) {
|
|
10830
|
-
const value = Math.abs(toNumber(arg?.value, locale));
|
|
10831
|
-
const format = arg?.format;
|
|
10832
|
-
if (unit !== undefined) {
|
|
10833
|
-
const postFix = unit?.value;
|
|
10834
|
-
switch (postFix) {
|
|
10835
|
-
case "k":
|
|
10836
|
-
return createLargeNumberFormat(format, 1e3, "k");
|
|
10837
|
-
case "m":
|
|
10838
|
-
return createLargeNumberFormat(format, 1e6, "m");
|
|
10839
|
-
case "b":
|
|
10840
|
-
return createLargeNumberFormat(format, 1e9, "b");
|
|
10841
|
-
default:
|
|
10842
|
-
throw new EvaluationError(_t("The formatting unit should be 'k', 'm' or 'b'."));
|
|
10843
|
-
}
|
|
10844
|
-
}
|
|
10845
|
-
if (value < 1e5) {
|
|
10846
|
-
return createLargeNumberFormat(format, 0, "");
|
|
10847
|
-
}
|
|
10848
|
-
else if (value < 1e8) {
|
|
10849
|
-
return createLargeNumberFormat(format, 1e3, "k");
|
|
10850
|
-
}
|
|
10851
|
-
else if (value < 1e11) {
|
|
10852
|
-
return createLargeNumberFormat(format, 1e6, "m");
|
|
10853
|
-
}
|
|
10854
|
-
return createLargeNumberFormat(format, 1e9, "b");
|
|
10855
|
-
}
|
|
10856
11057
|
const FORMAT_LARGE_NUMBER = {
|
|
10857
11058
|
description: _t("Apply a large number format"),
|
|
10858
11059
|
args: [
|
|
@@ -11014,7 +11215,7 @@ const ATAN2 = {
|
|
|
11014
11215
|
compute: function (x, y) {
|
|
11015
11216
|
const _x = toNumber(x, this.locale);
|
|
11016
11217
|
const _y = toNumber(y, this.locale);
|
|
11017
|
-
assert(() => _x !== 0 || _y !== 0, _t("Function [[FUNCTION_NAME]] caused a divide by zero error."));
|
|
11218
|
+
assert(() => _x !== 0 || _y !== 0, _t("Function [[FUNCTION_NAME]] caused a divide by zero error."), CellErrorType.DivisionByZero);
|
|
11018
11219
|
return Math.atan2(_y, _x);
|
|
11019
11220
|
},
|
|
11020
11221
|
isExported: true,
|
|
@@ -11144,7 +11345,7 @@ const COT = {
|
|
|
11144
11345
|
returns: ["NUMBER"],
|
|
11145
11346
|
compute: function (angle) {
|
|
11146
11347
|
const _angle = toNumber(angle, this.locale);
|
|
11147
|
-
|
|
11348
|
+
assertNotZero(_angle);
|
|
11148
11349
|
return 1 / Math.tan(_angle);
|
|
11149
11350
|
},
|
|
11150
11351
|
isExported: true,
|
|
@@ -11158,7 +11359,7 @@ const COTH = {
|
|
|
11158
11359
|
returns: ["NUMBER"],
|
|
11159
11360
|
compute: function (value) {
|
|
11160
11361
|
const _value = toNumber(value, this.locale);
|
|
11161
|
-
|
|
11362
|
+
assertNotZero(_value);
|
|
11162
11363
|
return 1 / Math.tanh(_value);
|
|
11163
11364
|
},
|
|
11164
11365
|
isExported: true,
|
|
@@ -11286,7 +11487,7 @@ const CSC = {
|
|
|
11286
11487
|
returns: ["NUMBER"],
|
|
11287
11488
|
compute: function (angle) {
|
|
11288
11489
|
const _angle = toNumber(angle, this.locale);
|
|
11289
|
-
|
|
11490
|
+
assertNotZero(_angle);
|
|
11290
11491
|
return 1 / Math.sin(_angle);
|
|
11291
11492
|
},
|
|
11292
11493
|
isExported: true,
|
|
@@ -11300,7 +11501,7 @@ const CSCH = {
|
|
|
11300
11501
|
returns: ["NUMBER"],
|
|
11301
11502
|
compute: function (value) {
|
|
11302
11503
|
const _value = toNumber(value, this.locale);
|
|
11303
|
-
|
|
11504
|
+
assertNotZero(_value);
|
|
11304
11505
|
return 1 / Math.sinh(_value);
|
|
11305
11506
|
},
|
|
11306
11507
|
isExported: true,
|
|
@@ -11499,7 +11700,7 @@ const LN = {
|
|
|
11499
11700
|
// MOD
|
|
11500
11701
|
// -----------------------------------------------------------------------------
|
|
11501
11702
|
function mod(dividend, divisor) {
|
|
11502
|
-
assert(() => divisor !== 0, _t("The divisor must be different from 0."));
|
|
11703
|
+
assert(() => divisor !== 0, _t("The divisor must be different from 0."), CellErrorType.DivisionByZero);
|
|
11503
11704
|
const modulus = dividend % divisor;
|
|
11504
11705
|
// -42 % 10 = -2 but we want 8, so need the code below
|
|
11505
11706
|
if ((modulus > 0 && divisor < 0) || (modulus < 0 && divisor > 0)) {
|
|
@@ -12068,7 +12269,7 @@ function average(values, locale) {
|
|
|
12068
12269
|
count += 1;
|
|
12069
12270
|
return acc + a;
|
|
12070
12271
|
}, 0, locale);
|
|
12071
|
-
|
|
12272
|
+
assertNotZero(count);
|
|
12072
12273
|
return sum / count;
|
|
12073
12274
|
}
|
|
12074
12275
|
function countNumbers(values, locale) {
|
|
@@ -12135,7 +12336,7 @@ function filterAndFlatData(dataY, dataX) {
|
|
|
12135
12336
|
function covariance(dataY, dataX, isSample) {
|
|
12136
12337
|
const { flatDataX, flatDataY } = filterAndFlatData(dataY, dataX);
|
|
12137
12338
|
const count = flatDataY.length;
|
|
12138
|
-
assert(() => count !== 0 && (!isSample || count !== 1), _t("Evaluation of function [[FUNCTION_NAME]] caused a divide by zero error."));
|
|
12339
|
+
assert(() => count !== 0 && (!isSample || count !== 1), _t("Evaluation of function [[FUNCTION_NAME]] caused a divide by zero error."), CellErrorType.DivisionByZero);
|
|
12139
12340
|
let sumY = 0;
|
|
12140
12341
|
let sumX = 0;
|
|
12141
12342
|
for (let i = 0; i < count; i++) {
|
|
@@ -12158,7 +12359,7 @@ function variance(args, isSample, textAs0, locale) {
|
|
|
12158
12359
|
count += 1;
|
|
12159
12360
|
return acc + a;
|
|
12160
12361
|
}, 0, locale);
|
|
12161
|
-
assert(() => count !== 0 && (!isSample || count !== 1), _t("Evaluation of function [[FUNCTION_NAME]] caused a divide by zero error."));
|
|
12362
|
+
assert(() => count !== 0 && (!isSample || count !== 1), _t("Evaluation of function [[FUNCTION_NAME]] caused a divide by zero error."), CellErrorType.DivisionByZero);
|
|
12162
12363
|
const average = sum / count;
|
|
12163
12364
|
return (reduceFunction(args, (acc, a) => acc + Math.pow(a - average, 2), 0, locale) /
|
|
12164
12365
|
(count - (isSample ? 1 : 0)));
|
|
@@ -12355,7 +12556,7 @@ const AVEDEV = {
|
|
|
12355
12556
|
count += 1;
|
|
12356
12557
|
return acc + a;
|
|
12357
12558
|
}, 0, this.locale);
|
|
12358
|
-
|
|
12559
|
+
assertNotZero(count);
|
|
12359
12560
|
const average = sum / count;
|
|
12360
12561
|
return reduceNumbers(values, (acc, a) => acc + Math.abs(average - a), 0, this.locale) / count;
|
|
12361
12562
|
},
|
|
@@ -12427,7 +12628,7 @@ const AVERAGE_WEIGHTED = {
|
|
|
12427
12628
|
}
|
|
12428
12629
|
}
|
|
12429
12630
|
}
|
|
12430
|
-
|
|
12631
|
+
assertNotZero(count);
|
|
12431
12632
|
return { value: sum / count, format: inferFormat(args[0]) };
|
|
12432
12633
|
},
|
|
12433
12634
|
};
|
|
@@ -12447,7 +12648,7 @@ const AVERAGEA = {
|
|
|
12447
12648
|
count += 1;
|
|
12448
12649
|
return acc + a;
|
|
12449
12650
|
}, 0, this.locale);
|
|
12450
|
-
|
|
12651
|
+
assertNotZero(count);
|
|
12451
12652
|
return {
|
|
12452
12653
|
value: sum / count,
|
|
12453
12654
|
format: inferFormat(args[0]),
|
|
@@ -12477,7 +12678,7 @@ const AVERAGEIF = {
|
|
|
12477
12678
|
sum += value;
|
|
12478
12679
|
}
|
|
12479
12680
|
}, this.locale);
|
|
12480
|
-
|
|
12681
|
+
assertNotZero(count);
|
|
12481
12682
|
return sum / count;
|
|
12482
12683
|
},
|
|
12483
12684
|
isExported: true,
|
|
@@ -12506,7 +12707,7 @@ const AVERAGEIFS = {
|
|
|
12506
12707
|
sum += value;
|
|
12507
12708
|
}
|
|
12508
12709
|
}, this.locale);
|
|
12509
|
-
|
|
12710
|
+
assertNotZero(count);
|
|
12510
12711
|
return sum / count;
|
|
12511
12712
|
},
|
|
12512
12713
|
isExported: true,
|
|
@@ -17931,7 +18132,7 @@ const DIVIDE = {
|
|
|
17931
18132
|
returns: ["NUMBER"],
|
|
17932
18133
|
compute: function (dividend, divisor) {
|
|
17933
18134
|
const _divisor = toNumber(divisor, this.locale);
|
|
17934
|
-
assert(() => _divisor !== 0, _t("The divisor must be different from zero."));
|
|
18135
|
+
assert(() => _divisor !== 0, _t("The divisor must be different from zero."), CellErrorType.DivisionByZero);
|
|
17935
18136
|
return {
|
|
17936
18137
|
value: toNumber(dividend, this.locale) / _divisor,
|
|
17937
18138
|
format: dividend?.format || divisor?.format,
|
|
@@ -19408,10 +19609,10 @@ function aggregateDataForLabels(labels, datasets) {
|
|
|
19408
19609
|
}
|
|
19409
19610
|
}
|
|
19410
19611
|
return {
|
|
19411
|
-
labels:
|
|
19612
|
+
labels: Array.from(labelSet),
|
|
19412
19613
|
dataSetsValues: datasets.map((dataset, indexOfDataset) => ({
|
|
19413
19614
|
...dataset,
|
|
19414
|
-
data:
|
|
19615
|
+
data: Array.from(labelSet).map((label) => labelMap[label][indexOfDataset]),
|
|
19415
19616
|
})),
|
|
19416
19617
|
};
|
|
19417
19618
|
}
|
|
@@ -19430,8 +19631,8 @@ function truncateLabel(label) {
|
|
|
19430
19631
|
function getDefaultChartJsRuntime(chart, labels, fontColor, { format, locale }) {
|
|
19431
19632
|
const options = {
|
|
19432
19633
|
// https://www.chartjs.org/docs/latest/general/responsive.html
|
|
19433
|
-
responsive: true,
|
|
19434
|
-
maintainAspectRatio: false,
|
|
19634
|
+
responsive: true, // will resize when its container is resized
|
|
19635
|
+
maintainAspectRatio: false, // doesn't maintain the aspect ration (width/height =2 by default) so the user has the choice of the exact layout
|
|
19435
19636
|
layout: {
|
|
19436
19637
|
padding: { left: 20, right: 20, top: chart.title ? 10 : 25, bottom: 10 },
|
|
19437
19638
|
},
|
|
@@ -19477,7 +19678,7 @@ function getDefaultChartJsRuntime(chart, labels, fontColor, { format, locale })
|
|
|
19477
19678
|
labels: labels.map(truncateLabel),
|
|
19478
19679
|
datasets: [],
|
|
19479
19680
|
},
|
|
19480
|
-
platform: undefined,
|
|
19681
|
+
platform: undefined, // This key is optional and will be set by chart.js
|
|
19481
19682
|
plugins: [],
|
|
19482
19683
|
};
|
|
19483
19684
|
}
|
|
@@ -19754,7 +19955,7 @@ function getBarConfiguration(chart, labels, localeFormat) {
|
|
|
19754
19955
|
},
|
|
19755
19956
|
y: {
|
|
19756
19957
|
position: chart.verticalAxisPosition,
|
|
19757
|
-
beginAtZero: true,
|
|
19958
|
+
beginAtZero: true, // the origin of the y axis is always zero
|
|
19758
19959
|
ticks: {
|
|
19759
19960
|
color: fontColor,
|
|
19760
19961
|
callback: (value) => {
|
|
@@ -19805,6 +20006,204 @@ function createBarChartRuntime(chart, getters) {
|
|
|
19805
20006
|
return { chartJsConfig: config, background: chart.background || BACKGROUND_CHART_COLOR };
|
|
19806
20007
|
}
|
|
19807
20008
|
|
|
20009
|
+
class ComboChart extends AbstractChart {
|
|
20010
|
+
useBothYAxis;
|
|
20011
|
+
dataSets;
|
|
20012
|
+
labelRange;
|
|
20013
|
+
background;
|
|
20014
|
+
verticalAxisPosition;
|
|
20015
|
+
legendPosition;
|
|
20016
|
+
aggregated;
|
|
20017
|
+
dataSetsHaveTitle;
|
|
20018
|
+
type = "combo";
|
|
20019
|
+
constructor(definition, sheetId, getters) {
|
|
20020
|
+
super(definition, sheetId, getters);
|
|
20021
|
+
this.dataSets = createDataSets(getters, definition.dataSets, sheetId, definition.dataSetsHaveTitle);
|
|
20022
|
+
this.labelRange = createRange(getters, sheetId, definition.labelRange);
|
|
20023
|
+
this.background = definition.background;
|
|
20024
|
+
this.verticalAxisPosition = definition.verticalAxisPosition;
|
|
20025
|
+
this.legendPosition = definition.legendPosition;
|
|
20026
|
+
this.aggregated = definition.aggregated;
|
|
20027
|
+
this.dataSetsHaveTitle = definition.dataSetsHaveTitle;
|
|
20028
|
+
this.useBothYAxis = definition.useBothYAxis;
|
|
20029
|
+
}
|
|
20030
|
+
static transformDefinition(definition, executed) {
|
|
20031
|
+
return transformChartDefinitionWithDataSetsWithZone(definition, executed);
|
|
20032
|
+
}
|
|
20033
|
+
static validateChartDefinition(validator, definition) {
|
|
20034
|
+
return validator.checkValidations(definition, checkDataset, checkLabelRange);
|
|
20035
|
+
}
|
|
20036
|
+
getContextCreation() {
|
|
20037
|
+
return {
|
|
20038
|
+
background: this.background,
|
|
20039
|
+
title: this.title,
|
|
20040
|
+
range: this.dataSets.map((ds) => this.getters.getRangeString(ds.dataRange, this.sheetId)),
|
|
20041
|
+
auxiliaryRange: this.labelRange
|
|
20042
|
+
? this.getters.getRangeString(this.labelRange, this.sheetId)
|
|
20043
|
+
: undefined,
|
|
20044
|
+
aggregated: this.aggregated,
|
|
20045
|
+
};
|
|
20046
|
+
}
|
|
20047
|
+
getDefinition() {
|
|
20048
|
+
return this.getDefinitionWithSpecificDataSets(this.dataSets, this.labelRange);
|
|
20049
|
+
}
|
|
20050
|
+
getDefinitionWithSpecificDataSets(dataSets, labelRange, targetSheetId) {
|
|
20051
|
+
return {
|
|
20052
|
+
type: "combo",
|
|
20053
|
+
dataSetsHaveTitle: dataSets.length ? Boolean(dataSets[0].labelCell) : false,
|
|
20054
|
+
background: this.background,
|
|
20055
|
+
dataSets: dataSets.map((ds) => this.getters.getRangeString(ds.dataRange, targetSheetId || this.sheetId)),
|
|
20056
|
+
legendPosition: this.legendPosition,
|
|
20057
|
+
verticalAxisPosition: this.verticalAxisPosition,
|
|
20058
|
+
labelRange: labelRange
|
|
20059
|
+
? this.getters.getRangeString(labelRange, targetSheetId || this.sheetId)
|
|
20060
|
+
: undefined,
|
|
20061
|
+
title: this.title,
|
|
20062
|
+
aggregated: this.aggregated,
|
|
20063
|
+
useBothYAxis: this.useBothYAxis,
|
|
20064
|
+
};
|
|
20065
|
+
}
|
|
20066
|
+
getDefinitionForExcel() {
|
|
20067
|
+
// Excel does not support aggregating labels
|
|
20068
|
+
if (this.aggregated) {
|
|
20069
|
+
return undefined;
|
|
20070
|
+
}
|
|
20071
|
+
const dataSets = this.dataSets
|
|
20072
|
+
.map((ds) => toExcelDataset(this.getters, ds))
|
|
20073
|
+
.filter((ds) => ds.range !== "" && ds.range !== CellErrorType.InvalidReference);
|
|
20074
|
+
const labelRange = toExcelLabelRange(this.getters, this.labelRange, shouldRemoveFirstLabel(this.labelRange, this.dataSets[0], this.dataSetsHaveTitle));
|
|
20075
|
+
return {
|
|
20076
|
+
...this.getDefinition(),
|
|
20077
|
+
backgroundColor: toXlsxHexColor(this.background || BACKGROUND_CHART_COLOR),
|
|
20078
|
+
fontColor: toXlsxHexColor(chartFontColor(this.background)),
|
|
20079
|
+
dataSets,
|
|
20080
|
+
labelRange,
|
|
20081
|
+
};
|
|
20082
|
+
}
|
|
20083
|
+
updateRanges(applyChange) {
|
|
20084
|
+
const { dataSets, labelRange, isStale } = updateChartRangesWithDataSets(this.getters, applyChange, this.dataSets, this.labelRange);
|
|
20085
|
+
if (!isStale) {
|
|
20086
|
+
return this;
|
|
20087
|
+
}
|
|
20088
|
+
const definition = this.getDefinitionWithSpecificDataSets(dataSets, labelRange);
|
|
20089
|
+
return new ComboChart(definition, this.sheetId, this.getters);
|
|
20090
|
+
}
|
|
20091
|
+
static getDefinitionFromContextCreation(context) {
|
|
20092
|
+
return {
|
|
20093
|
+
background: context.background,
|
|
20094
|
+
dataSets: context.range ? context.range : [],
|
|
20095
|
+
dataSetsHaveTitle: false,
|
|
20096
|
+
aggregated: context.aggregated,
|
|
20097
|
+
legendPosition: "top",
|
|
20098
|
+
title: context.title || "",
|
|
20099
|
+
verticalAxisPosition: "left",
|
|
20100
|
+
labelRange: context.auxiliaryRange || undefined,
|
|
20101
|
+
type: "combo",
|
|
20102
|
+
useBothYAxis: false,
|
|
20103
|
+
};
|
|
20104
|
+
}
|
|
20105
|
+
copyForSheetId(sheetId) {
|
|
20106
|
+
const dataSets = copyDataSetsWithNewSheetId(this.sheetId, sheetId, this.dataSets);
|
|
20107
|
+
const labelRange = copyLabelRangeWithNewSheetId(this.sheetId, sheetId, this.labelRange);
|
|
20108
|
+
const definition = this.getDefinitionWithSpecificDataSets(dataSets, labelRange, sheetId);
|
|
20109
|
+
return new ComboChart(definition, sheetId, this.getters);
|
|
20110
|
+
}
|
|
20111
|
+
copyInSheetId(sheetId) {
|
|
20112
|
+
const definition = this.getDefinitionWithSpecificDataSets(this.dataSets, this.labelRange, sheetId);
|
|
20113
|
+
return new ComboChart(definition, sheetId, this.getters);
|
|
20114
|
+
}
|
|
20115
|
+
}
|
|
20116
|
+
function createComboChartRuntime(chart, getters) {
|
|
20117
|
+
const dataSetFormat = getChartDatasetFormat(getters, chart.dataSets);
|
|
20118
|
+
const locale = getters.getLocale();
|
|
20119
|
+
const labelValues = getChartLabelValues(getters, chart.dataSets, chart.labelRange);
|
|
20120
|
+
let labels = labelValues.formattedValues;
|
|
20121
|
+
let dataSetsValues = getChartDatasetValues(getters, chart.dataSets);
|
|
20122
|
+
if (chart.dataSetsHaveTitle &&
|
|
20123
|
+
dataSetsValues[0] &&
|
|
20124
|
+
labels.length > dataSetsValues[0].data.length) {
|
|
20125
|
+
labels.shift();
|
|
20126
|
+
}
|
|
20127
|
+
({ labels, dataSetsValues } = filterEmptyDataPoints(labels, dataSetsValues));
|
|
20128
|
+
if (chart.aggregated) {
|
|
20129
|
+
({ labels, dataSetsValues } = aggregateDataForLabels(labels, dataSetsValues));
|
|
20130
|
+
}
|
|
20131
|
+
const localeFormat = { format: dataSetFormat, locale };
|
|
20132
|
+
const fontColor = chartFontColor(chart.background);
|
|
20133
|
+
const config = getDefaultChartJsRuntime(chart, labels, fontColor, localeFormat);
|
|
20134
|
+
const legend = {
|
|
20135
|
+
labels: { color: fontColor },
|
|
20136
|
+
};
|
|
20137
|
+
if ((!chart.labelRange && chart.dataSets.length === 1) || chart.legendPosition === "none") {
|
|
20138
|
+
legend.display = false;
|
|
20139
|
+
}
|
|
20140
|
+
else {
|
|
20141
|
+
legend.position = chart.legendPosition;
|
|
20142
|
+
}
|
|
20143
|
+
config.options.plugins.legend = { ...config.options.plugins?.legend, ...legend };
|
|
20144
|
+
config.options.layout = {
|
|
20145
|
+
padding: { left: 20, right: 20, top: chart.title ? 10 : 25, bottom: 10 },
|
|
20146
|
+
};
|
|
20147
|
+
config.options.scales = {
|
|
20148
|
+
x: {
|
|
20149
|
+
ticks: {
|
|
20150
|
+
padding: 5,
|
|
20151
|
+
color: fontColor,
|
|
20152
|
+
},
|
|
20153
|
+
},
|
|
20154
|
+
};
|
|
20155
|
+
const verticalAxis = {
|
|
20156
|
+
beginAtZero: true, // the origin of the y axis is always zero
|
|
20157
|
+
ticks: {
|
|
20158
|
+
color: fontColor,
|
|
20159
|
+
callback: (value) => {
|
|
20160
|
+
value = Number(value);
|
|
20161
|
+
if (isNaN(value))
|
|
20162
|
+
return value;
|
|
20163
|
+
const { locale, format } = localeFormat;
|
|
20164
|
+
return formatValue(value, {
|
|
20165
|
+
locale,
|
|
20166
|
+
format: !format && Math.abs(value) >= 1000 ? "#,##" : format,
|
|
20167
|
+
});
|
|
20168
|
+
},
|
|
20169
|
+
},
|
|
20170
|
+
};
|
|
20171
|
+
if (chart.useBothYAxis) {
|
|
20172
|
+
config.options.scales.y = {
|
|
20173
|
+
...verticalAxis,
|
|
20174
|
+
position: "left",
|
|
20175
|
+
};
|
|
20176
|
+
config.options.scales.y1 = {
|
|
20177
|
+
...verticalAxis,
|
|
20178
|
+
position: "right",
|
|
20179
|
+
grid: {
|
|
20180
|
+
display: false,
|
|
20181
|
+
},
|
|
20182
|
+
};
|
|
20183
|
+
}
|
|
20184
|
+
else {
|
|
20185
|
+
config.options.scales.y = {
|
|
20186
|
+
...verticalAxis,
|
|
20187
|
+
position: chart.verticalAxisPosition,
|
|
20188
|
+
};
|
|
20189
|
+
}
|
|
20190
|
+
const colors = new ChartColors();
|
|
20191
|
+
for (let [index, { label, data }] of dataSetsValues.entries()) {
|
|
20192
|
+
const color = colors.next();
|
|
20193
|
+
const dataset = {
|
|
20194
|
+
label,
|
|
20195
|
+
data,
|
|
20196
|
+
borderColor: color,
|
|
20197
|
+
backgroundColor: color,
|
|
20198
|
+
yAxisID: index > 0 && chart.useBothYAxis ? "y1" : "y",
|
|
20199
|
+
type: index === 0 ? "bar" : "line",
|
|
20200
|
+
order: -index,
|
|
20201
|
+
};
|
|
20202
|
+
config.data.datasets.push(dataset);
|
|
20203
|
+
}
|
|
20204
|
+
return { chartJsConfig: config, background: chart.background || BACKGROUND_CHART_COLOR };
|
|
20205
|
+
}
|
|
20206
|
+
|
|
19808
20207
|
function isDataRangeValid(definition) {
|
|
19809
20208
|
return definition.dataRange && !rangeReference.test(definition.dataRange)
|
|
19810
20209
|
? "InvalidGaugeDataRange" /* CommandResult.InvalidGaugeDataRange */
|
|
@@ -20143,7 +20542,7 @@ function getBestTimeUnitForScale(labels, format, locale) {
|
|
|
20143
20542
|
return undefined;
|
|
20144
20543
|
}
|
|
20145
20544
|
const labelsTimestamps = labelDates.map((date) => date.getTime());
|
|
20146
|
-
const period =
|
|
20545
|
+
const period = largeMax(labelsTimestamps) - largeMin(labelsTimestamps);
|
|
20147
20546
|
const minUnit = getFormatMinDisplayUnit(format);
|
|
20148
20547
|
if (UNIT_LENGTH.second >= UNIT_LENGTH[minUnit] && Milliseconds.inSeconds(period) < 180) {
|
|
20149
20548
|
return "second";
|
|
@@ -20275,7 +20674,7 @@ function getLineOrScatterConfiguration(chart, labels, localeFormat) {
|
|
|
20275
20674
|
},
|
|
20276
20675
|
y: {
|
|
20277
20676
|
position: chart.verticalAxisPosition,
|
|
20278
|
-
beginAtZero: true,
|
|
20677
|
+
beginAtZero: true, // the origin of the y axis is always zero
|
|
20279
20678
|
ticks: {
|
|
20280
20679
|
color: fontColor,
|
|
20281
20680
|
callback: (value) => {
|
|
@@ -20361,7 +20760,7 @@ function createLineOrScatterChartRuntime(chart, getters) {
|
|
|
20361
20760
|
const dataset = {
|
|
20362
20761
|
label,
|
|
20363
20762
|
data,
|
|
20364
|
-
tension: 0,
|
|
20763
|
+
tension: 0, // 0 -> render straight lines, which is much faster
|
|
20365
20764
|
borderColor: color,
|
|
20366
20765
|
backgroundColor,
|
|
20367
20766
|
pointBackgroundColor: color,
|
|
@@ -20583,7 +20982,7 @@ class PieChart extends AbstractChart {
|
|
|
20583
20982
|
...this.getDefinition(),
|
|
20584
20983
|
backgroundColor: toXlsxHexColor(this.background || BACKGROUND_CHART_COLOR),
|
|
20585
20984
|
fontColor: toXlsxHexColor(chartFontColor(this.background)),
|
|
20586
|
-
verticalAxisPosition: "left",
|
|
20985
|
+
verticalAxisPosition: "left", //TODO ExcelChartDefinition should be adapted, but can be done later
|
|
20587
20986
|
dataSets,
|
|
20588
20987
|
labelRange,
|
|
20589
20988
|
};
|
|
@@ -20631,7 +21030,7 @@ function getPieConfiguration(chart, labels, localeFormat) {
|
|
|
20631
21030
|
}
|
|
20632
21031
|
function getPieColors(colors, dataSetsValues) {
|
|
20633
21032
|
const pieColors = [];
|
|
20634
|
-
const maxLength =
|
|
21033
|
+
const maxLength = largeMax(dataSetsValues.map((ds) => ds.data.length));
|
|
20635
21034
|
for (let i = 0; i <= maxLength; i++) {
|
|
20636
21035
|
pieColors.push(colors.next());
|
|
20637
21036
|
}
|
|
@@ -20778,7 +21177,21 @@ class ScatterChart extends AbstractChart {
|
|
|
20778
21177
|
return new ScatterChart(definition, this.sheetId, this.getters);
|
|
20779
21178
|
}
|
|
20780
21179
|
getDefinitionForExcel() {
|
|
20781
|
-
|
|
21180
|
+
// Excel does not support aggregating labels
|
|
21181
|
+
if (this.aggregated) {
|
|
21182
|
+
return undefined;
|
|
21183
|
+
}
|
|
21184
|
+
const dataSets = this.dataSets
|
|
21185
|
+
.map((ds) => toExcelDataset(this.getters, ds))
|
|
21186
|
+
.filter((ds) => ds.range !== "");
|
|
21187
|
+
const labelRange = toExcelLabelRange(this.getters, this.labelRange, shouldRemoveFirstLabel(this.labelRange, this.dataSets[0], this.dataSetsHaveTitle));
|
|
21188
|
+
return {
|
|
21189
|
+
...this.getDefinition(),
|
|
21190
|
+
backgroundColor: toXlsxHexColor(this.background || BACKGROUND_CHART_COLOR),
|
|
21191
|
+
fontColor: toXlsxHexColor(chartFontColor(this.background)),
|
|
21192
|
+
dataSets,
|
|
21193
|
+
labelRange,
|
|
21194
|
+
};
|
|
20782
21195
|
}
|
|
20783
21196
|
copyForSheetId(sheetId) {
|
|
20784
21197
|
const dataSets = copyDataSetsWithNewSheetId(this.sheetId, sheetId, this.dataSets);
|
|
@@ -20800,7 +21213,7 @@ function createScatterChartRuntime(chart, getters) {
|
|
|
20800
21213
|
configOptions.elements = {
|
|
20801
21214
|
point: {
|
|
20802
21215
|
radius: 3,
|
|
20803
|
-
hoverRadius: 3,
|
|
21216
|
+
hoverRadius: 3, // chartJS seems bugged, the point starts with the "hoverRadius" value
|
|
20804
21217
|
hitRadius: 8,
|
|
20805
21218
|
},
|
|
20806
21219
|
};
|
|
@@ -20840,6 +21253,16 @@ chartRegistry.add("bar", {
|
|
|
20840
21253
|
name: _t("Bar"),
|
|
20841
21254
|
sequence: 10,
|
|
20842
21255
|
});
|
|
21256
|
+
chartRegistry.add("combo", {
|
|
21257
|
+
match: (type) => type === "combo",
|
|
21258
|
+
createChart: (definition, sheetId, getters) => new ComboChart(definition, sheetId, getters),
|
|
21259
|
+
getChartRuntime: createComboChartRuntime,
|
|
21260
|
+
validateChartDefinition: (validator, definition) => ComboChart.validateChartDefinition(validator, definition),
|
|
21261
|
+
transformDefinition: (definition, executed) => ComboChart.transformDefinition(definition, executed),
|
|
21262
|
+
getChartDefinitionFromContextCreation: (context) => ComboChart.getDefinitionFromContextCreation(context),
|
|
21263
|
+
name: _t("Combo"),
|
|
21264
|
+
sequence: 15,
|
|
21265
|
+
});
|
|
20843
21266
|
chartRegistry.add("line", {
|
|
20844
21267
|
match: (type) => type === "line",
|
|
20845
21268
|
createChart: (definition, sheetId, getters) => new LineChart(definition, sheetId, getters),
|
|
@@ -20893,6 +21316,7 @@ chartRegistry.add("scatter", {
|
|
|
20893
21316
|
const chartComponentRegistry = new Registry();
|
|
20894
21317
|
chartComponentRegistry.add("line", ChartJsComponent);
|
|
20895
21318
|
chartComponentRegistry.add("bar", ChartJsComponent);
|
|
21319
|
+
chartComponentRegistry.add("combo", ChartJsComponent);
|
|
20896
21320
|
chartComponentRegistry.add("pie", ChartJsComponent);
|
|
20897
21321
|
chartComponentRegistry.add("gauge", GaugeChartComponent);
|
|
20898
21322
|
chartComponentRegistry.add("scatter", ChartJsComponent);
|
|
@@ -23151,7 +23575,7 @@ const lightTemplateWithHeader = (colorSet) => ({
|
|
|
23151
23575
|
style: { fillColor: colorSet.highlight, textColor: "#FFFFFF" },
|
|
23152
23576
|
border: { bottom: { color: colorSet.highlight, style: "thin" } },
|
|
23153
23577
|
},
|
|
23154
|
-
totalRow: { border: { top: { color: colorSet.highlight, style: "medium" } } },
|
|
23578
|
+
totalRow: { border: { top: { color: colorSet.highlight, style: "medium" } } }, // @compatibility: should be double line
|
|
23155
23579
|
firstRowStripe: { border: { bottom: { color: colorSet.highlight, style: "thin" } } },
|
|
23156
23580
|
secondRowStripe: { border: { bottom: { color: colorSet.highlight, style: "thin" } } },
|
|
23157
23581
|
});
|
|
@@ -23169,7 +23593,7 @@ const lightTemplateAllBorders = (colorSet) => ({
|
|
|
23169
23593
|
},
|
|
23170
23594
|
},
|
|
23171
23595
|
headerRow: { border: { bottom: { color: colorSet.highlight, style: "medium" } } },
|
|
23172
|
-
totalRow: { border: { top: { color: colorSet.highlight, style: "medium" } } },
|
|
23596
|
+
totalRow: { border: { top: { color: colorSet.highlight, style: "medium" } } }, // @compatibility: should be double line
|
|
23173
23597
|
firstRowStripe: { style: { fillColor: colorSet.light } },
|
|
23174
23598
|
firstColumnStripe: { style: { fillColor: colorSet.light } },
|
|
23175
23599
|
});
|
|
@@ -23188,7 +23612,7 @@ const mediumTemplateBandedBorders = (colorSet) => ({
|
|
|
23188
23612
|
headerRow: {
|
|
23189
23613
|
style: { fillColor: colorSet.highlight, textColor: "#FFFFFF" },
|
|
23190
23614
|
},
|
|
23191
|
-
totalRow: { border: { top: { color: colorSet.highlight, style: "medium" } } },
|
|
23615
|
+
totalRow: { border: { top: { color: colorSet.highlight, style: "medium" } } }, // @compatibility: should be double line
|
|
23192
23616
|
firstRowStripe: { style: { fillColor: colorSet.light } },
|
|
23193
23617
|
firstColumnStripe: { style: { fillColor: colorSet.light } },
|
|
23194
23618
|
});
|
|
@@ -23224,7 +23648,7 @@ const mediumTemplateMinimalBorders = (colorSet) => ({
|
|
|
23224
23648
|
bottom: { color: "#000000", style: "medium" },
|
|
23225
23649
|
},
|
|
23226
23650
|
},
|
|
23227
|
-
totalRow: { border: { top: { color: "#000000", style: "medium" } } },
|
|
23651
|
+
totalRow: { border: { top: { color: "#000000", style: "medium" } } }, // @compatibility: should be double line
|
|
23228
23652
|
headerRow: {
|
|
23229
23653
|
style: { fillColor: colorSet.highlight, textColor: "#FFFFFF" },
|
|
23230
23654
|
border: { bottom: { color: "#000000", style: "medium" } },
|
|
@@ -23248,7 +23672,7 @@ const mediumTemplateAllBorders = (colorSet) => ({
|
|
|
23248
23672
|
},
|
|
23249
23673
|
style: { fillColor: colorSet.light },
|
|
23250
23674
|
},
|
|
23251
|
-
totalRow: { border: { top: { color: colorSet.highlight, style: "medium" } } },
|
|
23675
|
+
totalRow: { border: { top: { color: colorSet.highlight, style: "medium" } } }, // @compatibility: should be double line
|
|
23252
23676
|
firstRowStripe: { style: { fillColor: colorSet.medium } },
|
|
23253
23677
|
firstColumnStripe: { style: { fillColor: colorSet.medium } },
|
|
23254
23678
|
});
|
|
@@ -23279,7 +23703,7 @@ const darkTemplateNoBorders = (colorSet) => ({
|
|
|
23279
23703
|
category: "dark",
|
|
23280
23704
|
colorName: colorSet.name,
|
|
23281
23705
|
wholeTable: { style: { fillColor: colorSet.light } },
|
|
23282
|
-
totalRow: { border: { top: { color: "#000000", style: "medium" } } },
|
|
23706
|
+
totalRow: { border: { top: { color: "#000000", style: "medium" } } }, // @compatibility: should be double line
|
|
23283
23707
|
headerRow: { style: { fillColor: colorSet.highlight, textColor: "#FFFFFF" } },
|
|
23284
23708
|
firstRowStripe: { style: { fillColor: colorSet.medium } },
|
|
23285
23709
|
firstColumnStripe: { style: { fillColor: colorSet.medium } },
|
|
@@ -23444,8 +23868,8 @@ const DELETE_CONTENT_ROWS_NAME = (env) => {
|
|
|
23444
23868
|
let last;
|
|
23445
23869
|
const activesRows = env.model.getters.getActiveRows();
|
|
23446
23870
|
if (activesRows.size !== 0) {
|
|
23447
|
-
first =
|
|
23448
|
-
last =
|
|
23871
|
+
first = largeMin([...activesRows]);
|
|
23872
|
+
last = largeMax([...activesRows]);
|
|
23449
23873
|
}
|
|
23450
23874
|
else {
|
|
23451
23875
|
const zone = env.model.getters.getSelectedZones()[0];
|
|
@@ -23473,8 +23897,8 @@ const DELETE_CONTENT_COLUMNS_NAME = (env) => {
|
|
|
23473
23897
|
let last;
|
|
23474
23898
|
const activeCols = env.model.getters.getActiveCols();
|
|
23475
23899
|
if (activeCols.size !== 0) {
|
|
23476
|
-
first =
|
|
23477
|
-
last =
|
|
23900
|
+
first = largeMin([...activeCols]);
|
|
23901
|
+
last = largeMax([...activeCols]);
|
|
23478
23902
|
}
|
|
23479
23903
|
else {
|
|
23480
23904
|
const zone = env.model.getters.getSelectedZones()[0];
|
|
@@ -23502,8 +23926,8 @@ const REMOVE_ROWS_NAME = (env) => {
|
|
|
23502
23926
|
let last;
|
|
23503
23927
|
const activesRows = env.model.getters.getActiveRows();
|
|
23504
23928
|
if (activesRows.size !== 0) {
|
|
23505
|
-
first =
|
|
23506
|
-
last =
|
|
23929
|
+
first = largeMin([...activesRows]);
|
|
23930
|
+
last = largeMax([...activesRows]);
|
|
23507
23931
|
}
|
|
23508
23932
|
else {
|
|
23509
23933
|
const zone = env.model.getters.getSelectedZones()[0];
|
|
@@ -23544,8 +23968,8 @@ const REMOVE_COLUMNS_NAME = (env) => {
|
|
|
23544
23968
|
let last;
|
|
23545
23969
|
const activeCols = env.model.getters.getActiveCols();
|
|
23546
23970
|
if (activeCols.size !== 0) {
|
|
23547
|
-
first =
|
|
23548
|
-
last =
|
|
23971
|
+
first = largeMin([...activeCols]);
|
|
23972
|
+
last = largeMax([...activeCols]);
|
|
23549
23973
|
}
|
|
23550
23974
|
else {
|
|
23551
23975
|
const zone = env.model.getters.getSelectedZones()[0];
|
|
@@ -23586,7 +24010,7 @@ const INSERT_ROWS_BEFORE_ACTION = (env) => {
|
|
|
23586
24010
|
let row;
|
|
23587
24011
|
let quantity;
|
|
23588
24012
|
if (activeRows.size) {
|
|
23589
|
-
row =
|
|
24013
|
+
row = largeMin([...activeRows]);
|
|
23590
24014
|
quantity = activeRows.size;
|
|
23591
24015
|
}
|
|
23592
24016
|
else {
|
|
@@ -23607,7 +24031,7 @@ const INSERT_ROWS_AFTER_ACTION = (env) => {
|
|
|
23607
24031
|
let row;
|
|
23608
24032
|
let quantity;
|
|
23609
24033
|
if (activeRows.size) {
|
|
23610
|
-
row =
|
|
24034
|
+
row = largeMax([...activeRows]);
|
|
23611
24035
|
quantity = activeRows.size;
|
|
23612
24036
|
}
|
|
23613
24037
|
else {
|
|
@@ -23628,7 +24052,7 @@ const INSERT_COLUMNS_BEFORE_ACTION = (env) => {
|
|
|
23628
24052
|
let column;
|
|
23629
24053
|
let quantity;
|
|
23630
24054
|
if (activeCols.size) {
|
|
23631
|
-
column =
|
|
24055
|
+
column = largeMin([...activeCols]);
|
|
23632
24056
|
quantity = activeCols.size;
|
|
23633
24057
|
}
|
|
23634
24058
|
else {
|
|
@@ -23649,7 +24073,7 @@ const INSERT_COLUMNS_AFTER_ACTION = (env) => {
|
|
|
23649
24073
|
let column;
|
|
23650
24074
|
let quantity;
|
|
23651
24075
|
if (activeCols.size) {
|
|
23652
|
-
column =
|
|
24076
|
+
column = largeMax([...activeCols]);
|
|
23653
24077
|
quantity = activeCols.size;
|
|
23654
24078
|
}
|
|
23655
24079
|
else {
|
|
@@ -27195,13 +27619,30 @@ class ColorPickerWidget extends owl.Component {
|
|
|
27195
27619
|
}
|
|
27196
27620
|
}
|
|
27197
27621
|
|
|
27198
|
-
|
|
27199
|
-
|
|
27200
|
-
|
|
27622
|
+
const TRANSPARENT_BACKGROUND_SVG = /*xml*/ `
|
|
27623
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="10" height="10">
|
|
27624
|
+
<path fill="#d9d9d9" d="M5 5h5v5H5zH0V0h5"/>
|
|
27625
|
+
</svg>
|
|
27626
|
+
`;
|
|
27627
|
+
css /* scss */ `
|
|
27628
|
+
.o-round-color-picker-button {
|
|
27629
|
+
width: 15px;
|
|
27630
|
+
height: 15px;
|
|
27631
|
+
cursor: pointer;
|
|
27632
|
+
border: 1px solid #aaa;
|
|
27633
|
+
background-position: 1px 1px;
|
|
27634
|
+
background-image: url("data:image/svg+xml,${encodeURIComponent(TRANSPARENT_BACKGROUND_SVG)}");
|
|
27635
|
+
}
|
|
27636
|
+
`;
|
|
27637
|
+
class RoundColorPicker extends owl.Component {
|
|
27638
|
+
static template = "o-spreadsheet.RoundColorPicker";
|
|
27639
|
+
static components = { ColorPickerWidget, Section, ColorPicker };
|
|
27201
27640
|
static props = {
|
|
27202
27641
|
currentColor: { type: String, optional: true },
|
|
27642
|
+
title: { type: String, optional: true },
|
|
27203
27643
|
onColorPicked: Function,
|
|
27204
27644
|
};
|
|
27645
|
+
colorPickerButtonRef = owl.useRef("colorPickerButton");
|
|
27205
27646
|
state;
|
|
27206
27647
|
setup() {
|
|
27207
27648
|
this.state = owl.useState({ pickerOpened: false });
|
|
@@ -27213,6 +27654,19 @@ class ChartColor extends owl.Component {
|
|
|
27213
27654
|
togglePicker() {
|
|
27214
27655
|
this.state.pickerOpened = !this.state.pickerOpened;
|
|
27215
27656
|
}
|
|
27657
|
+
onColorPicked(color) {
|
|
27658
|
+
this.props.onColorPicked(color);
|
|
27659
|
+
this.state.pickerOpened = false;
|
|
27660
|
+
}
|
|
27661
|
+
get colorPickerAnchorRect() {
|
|
27662
|
+
const button = this.colorPickerButtonRef.el;
|
|
27663
|
+
return getBoundingRectAsPOJO(button);
|
|
27664
|
+
}
|
|
27665
|
+
get buttonStyle() {
|
|
27666
|
+
return cssPropertiesToCss({
|
|
27667
|
+
background: this.props.currentColor,
|
|
27668
|
+
});
|
|
27669
|
+
}
|
|
27216
27670
|
}
|
|
27217
27671
|
|
|
27218
27672
|
class ChartTitle extends owl.Component {
|
|
@@ -27226,7 +27680,7 @@ class ChartTitle extends owl.Component {
|
|
|
27226
27680
|
|
|
27227
27681
|
class LineBarPieDesignPanel extends owl.Component {
|
|
27228
27682
|
static template = "o-spreadsheet-LineBarPieDesignPanel";
|
|
27229
|
-
static components = {
|
|
27683
|
+
static components = { RoundColorPicker, ChartTitle, Section };
|
|
27230
27684
|
static props = {
|
|
27231
27685
|
figureId: String,
|
|
27232
27686
|
definition: Object,
|
|
@@ -27249,12 +27703,31 @@ class LineBarPieDesignPanel extends owl.Component {
|
|
|
27249
27703
|
[attr]: ev.target.value,
|
|
27250
27704
|
});
|
|
27251
27705
|
}
|
|
27706
|
+
get backgroundColorTitle() {
|
|
27707
|
+
return ChartTerms.BackgroundColor;
|
|
27708
|
+
}
|
|
27252
27709
|
}
|
|
27253
27710
|
|
|
27254
27711
|
class BarChartDesignPanel extends LineBarPieDesignPanel {
|
|
27255
27712
|
static template = "o-spreadsheet-BarChartDesignPanel";
|
|
27256
27713
|
}
|
|
27257
27714
|
|
|
27715
|
+
class ComboChartConfigPanel extends LineBarPieConfigPanel {
|
|
27716
|
+
static template = "o-spreadsheet-ComboChartConfigPanel";
|
|
27717
|
+
get shouldUseRightAxis() {
|
|
27718
|
+
return _t("Use right axis for line series");
|
|
27719
|
+
}
|
|
27720
|
+
onUpdateUseRightAxis(useBothYAxis) {
|
|
27721
|
+
this.props.updateChart(this.props.figureId, {
|
|
27722
|
+
useBothYAxis,
|
|
27723
|
+
});
|
|
27724
|
+
}
|
|
27725
|
+
}
|
|
27726
|
+
|
|
27727
|
+
class ComboChartDesignPanel extends LineBarPieDesignPanel {
|
|
27728
|
+
static template = "o-spreadsheet-ComboChartDesignPanel";
|
|
27729
|
+
}
|
|
27730
|
+
|
|
27258
27731
|
class GaugeChartConfigPanel extends owl.Component {
|
|
27259
27732
|
static template = "o-spreadsheet-GaugeChartConfigPanel";
|
|
27260
27733
|
static components = { ChartErrorSection, ChartDataSeries };
|
|
@@ -27302,6 +27775,10 @@ css /* scss */ `
|
|
|
27302
27775
|
line-height: 18px;
|
|
27303
27776
|
width: 100%;
|
|
27304
27777
|
}
|
|
27778
|
+
td {
|
|
27779
|
+
box-sizing: border-box;
|
|
27780
|
+
height: 30px;
|
|
27781
|
+
}
|
|
27305
27782
|
th.o-gauge-color-set-colorPicker {
|
|
27306
27783
|
width: 8%;
|
|
27307
27784
|
}
|
|
@@ -27324,7 +27801,12 @@ css /* scss */ `
|
|
|
27324
27801
|
`;
|
|
27325
27802
|
class GaugeChartDesignPanel extends owl.Component {
|
|
27326
27803
|
static template = "o-spreadsheet-GaugeChartDesignPanel";
|
|
27327
|
-
static components = {
|
|
27804
|
+
static components = {
|
|
27805
|
+
ChartErrorSection,
|
|
27806
|
+
RoundColorPicker,
|
|
27807
|
+
ChartTitle,
|
|
27808
|
+
Section,
|
|
27809
|
+
};
|
|
27328
27810
|
static props = {
|
|
27329
27811
|
figureId: String,
|
|
27330
27812
|
definition: Object,
|
|
@@ -27336,9 +27818,6 @@ class GaugeChartDesignPanel extends owl.Component {
|
|
|
27336
27818
|
sectionRuleDispatchResult: undefined,
|
|
27337
27819
|
sectionRule: deepCopy(this.props.definition.sectionRule),
|
|
27338
27820
|
});
|
|
27339
|
-
setup() {
|
|
27340
|
-
owl.useExternalListener(window, "click", this.closeMenus);
|
|
27341
|
-
}
|
|
27342
27821
|
get title() {
|
|
27343
27822
|
return _t(this.props.definition.title);
|
|
27344
27823
|
}
|
|
@@ -27379,27 +27858,22 @@ class GaugeChartDesignPanel extends owl.Component {
|
|
|
27379
27858
|
const sectionRule = deepCopy(this.state.sectionRule);
|
|
27380
27859
|
sectionRule.colors[target] = color;
|
|
27381
27860
|
this.updateSectionRule(sectionRule);
|
|
27382
|
-
this.closeMenus();
|
|
27383
|
-
}
|
|
27384
|
-
toggleMenu(menu) {
|
|
27385
|
-
const isSelected = this.state.openedMenu === menu;
|
|
27386
|
-
this.closeMenus();
|
|
27387
|
-
if (!isSelected) {
|
|
27388
|
-
this.state.openedMenu = menu;
|
|
27389
|
-
}
|
|
27390
27861
|
}
|
|
27391
27862
|
updateSectionRule(sectionRule) {
|
|
27392
27863
|
this.state.sectionRuleDispatchResult = this.props.updateChart(this.props.figureId, {
|
|
27393
27864
|
sectionRule,
|
|
27394
27865
|
});
|
|
27866
|
+
if (this.state.sectionRuleDispatchResult.isSuccessful) {
|
|
27867
|
+
this.state.sectionRule = deepCopy(sectionRule);
|
|
27868
|
+
}
|
|
27395
27869
|
}
|
|
27396
27870
|
canUpdateSectionRule(sectionRule) {
|
|
27397
27871
|
this.state.sectionRuleDispatchResult = this.props.canUpdateChart(this.props.figureId, {
|
|
27398
27872
|
sectionRule,
|
|
27399
27873
|
});
|
|
27400
27874
|
}
|
|
27401
|
-
|
|
27402
|
-
|
|
27875
|
+
get backgroundColorTitle() {
|
|
27876
|
+
return ChartTerms.BackgroundColor;
|
|
27403
27877
|
}
|
|
27404
27878
|
}
|
|
27405
27879
|
|
|
@@ -27547,39 +28021,36 @@ class ScorecardChartConfigPanel extends owl.Component {
|
|
|
27547
28021
|
|
|
27548
28022
|
class ScorecardChartDesignPanel extends owl.Component {
|
|
27549
28023
|
static template = "o-spreadsheet-ScorecardChartDesignPanel";
|
|
27550
|
-
static components = {
|
|
28024
|
+
static components = { RoundColorPicker, ChartTitle, Section, Checkbox };
|
|
27551
28025
|
static props = {
|
|
27552
28026
|
figureId: String,
|
|
27553
28027
|
definition: Object,
|
|
27554
28028
|
updateChart: Function,
|
|
27555
28029
|
canUpdateChart: Function,
|
|
27556
28030
|
};
|
|
27557
|
-
state = owl.useState({
|
|
27558
|
-
openedColorPicker: undefined,
|
|
27559
|
-
});
|
|
27560
|
-
setup() {
|
|
27561
|
-
owl.useExternalListener(window, "click", this.closeMenus);
|
|
27562
|
-
}
|
|
27563
28031
|
get title() {
|
|
27564
28032
|
return _t(this.props.definition.title);
|
|
27565
28033
|
}
|
|
28034
|
+
get colorsSectionTitle() {
|
|
28035
|
+
return this.props.definition.baselineMode === "progress"
|
|
28036
|
+
? _t("Progress bar colors")
|
|
28037
|
+
: _t("Baseline colors");
|
|
28038
|
+
}
|
|
28039
|
+
get humanizeNumbersLabel() {
|
|
28040
|
+
return _t("Humanize numbers");
|
|
28041
|
+
}
|
|
27566
28042
|
updateTitle(title) {
|
|
27567
28043
|
this.props.updateChart(this.props.figureId, { title });
|
|
27568
28044
|
}
|
|
28045
|
+
updateHumanizeNumbers(humanize) {
|
|
28046
|
+
this.props.updateChart(this.props.figureId, { humanize });
|
|
28047
|
+
}
|
|
27569
28048
|
translate(term) {
|
|
27570
28049
|
return _t(term);
|
|
27571
28050
|
}
|
|
27572
28051
|
updateBaselineDescr(ev) {
|
|
27573
28052
|
this.props.updateChart(this.props.figureId, { baselineDescr: ev.target.value });
|
|
27574
28053
|
}
|
|
27575
|
-
toggleColorPicker(colorPickerId) {
|
|
27576
|
-
if (this.state.openedColorPicker === colorPickerId) {
|
|
27577
|
-
this.state.openedColorPicker = undefined;
|
|
27578
|
-
}
|
|
27579
|
-
else {
|
|
27580
|
-
this.state.openedColorPicker = colorPickerId;
|
|
27581
|
-
}
|
|
27582
|
-
}
|
|
27583
28054
|
setColor(color, colorPickerId) {
|
|
27584
28055
|
switch (colorPickerId) {
|
|
27585
28056
|
case "backgroundColor":
|
|
@@ -27592,10 +28063,9 @@ class ScorecardChartDesignPanel extends owl.Component {
|
|
|
27592
28063
|
this.props.updateChart(this.props.figureId, { baselineColorUp: color });
|
|
27593
28064
|
break;
|
|
27594
28065
|
}
|
|
27595
|
-
this.closeMenus();
|
|
27596
28066
|
}
|
|
27597
|
-
|
|
27598
|
-
|
|
28067
|
+
get backgroundColorTitle() {
|
|
28068
|
+
return ChartTerms.BackgroundColor;
|
|
27599
28069
|
}
|
|
27600
28070
|
}
|
|
27601
28071
|
|
|
@@ -27612,6 +28082,10 @@ chartSidePanelComponentRegistry
|
|
|
27612
28082
|
.add("bar", {
|
|
27613
28083
|
configuration: BarConfigPanel,
|
|
27614
28084
|
design: BarChartDesignPanel,
|
|
28085
|
+
})
|
|
28086
|
+
.add("combo", {
|
|
28087
|
+
configuration: ComboChartConfigPanel,
|
|
28088
|
+
design: ComboChartDesignPanel,
|
|
27615
28089
|
})
|
|
27616
28090
|
.add("pie", {
|
|
27617
28091
|
configuration: LineBarPieConfigPanel,
|
|
@@ -28540,6 +29014,7 @@ class ConditionalFormattingEditor extends owl.Component {
|
|
|
28540
29014
|
ColorPickerWidget,
|
|
28541
29015
|
ConditionalFormatPreviewList,
|
|
28542
29016
|
Section,
|
|
29017
|
+
RoundColorPicker,
|
|
28543
29018
|
};
|
|
28544
29019
|
icons = ICONS;
|
|
28545
29020
|
cellIsOperators = CellIsOperators;
|
|
@@ -30252,7 +30727,11 @@ class SplitIntoColumnsPanel extends owl.Component {
|
|
|
30252
30727
|
const composerStore = useStore(ComposerStore);
|
|
30253
30728
|
// The feature makes no sense if we are editing a cell, because then the selection isn't active
|
|
30254
30729
|
// Stop the edition when the panel is mounted, and close the panel if the user start editing a cell
|
|
30255
|
-
owl.useEffect(
|
|
30730
|
+
owl.useEffect((editionMode) => {
|
|
30731
|
+
if (editionMode !== "inactive") {
|
|
30732
|
+
this.props.onCloseSidePanel();
|
|
30733
|
+
}
|
|
30734
|
+
}, () => [composerStore.editionMode]);
|
|
30256
30735
|
owl.onMounted(() => {
|
|
30257
30736
|
composerStore.stopEdition();
|
|
30258
30737
|
});
|
|
@@ -31961,6 +32440,9 @@ class FunctionDescriptionProvider extends owl.Component {
|
|
|
31961
32440
|
this.assistantState.allowCellSelectionBehind = false;
|
|
31962
32441
|
}, 2000);
|
|
31963
32442
|
}
|
|
32443
|
+
get formulaArgSeparator() {
|
|
32444
|
+
return this.env.model.getters.getLocale().formulaArgSeparator + " ";
|
|
32445
|
+
}
|
|
31964
32446
|
}
|
|
31965
32447
|
|
|
31966
32448
|
const functions$2 = functionRegistry.content;
|
|
@@ -32037,6 +32519,7 @@ class Composer extends owl.Component {
|
|
|
32037
32519
|
onComposerCellFocused: { type: Function, optional: true },
|
|
32038
32520
|
onComposerContentFocused: Function,
|
|
32039
32521
|
isDefaultFocus: { type: Boolean, optional: true },
|
|
32522
|
+
onInputContextMenu: { type: Function, optional: true },
|
|
32040
32523
|
};
|
|
32041
32524
|
static components = { TextValueProvider, FunctionDescriptionProvider };
|
|
32042
32525
|
static defaultProps = {
|
|
@@ -32087,6 +32570,9 @@ class Composer extends owl.Component {
|
|
|
32087
32570
|
assistantStyle.right = `0px`;
|
|
32088
32571
|
}
|
|
32089
32572
|
}
|
|
32573
|
+
else if (this.props.delimitation) {
|
|
32574
|
+
assistantStyle["max-height"] = `${this.props.delimitation.height}px`;
|
|
32575
|
+
}
|
|
32090
32576
|
return cssPropertiesToCss(assistantStyle);
|
|
32091
32577
|
}
|
|
32092
32578
|
// we can't allow input events to be triggered while we remove and add back the content of the composer in processContent
|
|
@@ -32120,6 +32606,12 @@ class Composer extends owl.Component {
|
|
|
32120
32606
|
owl.useEffect(() => {
|
|
32121
32607
|
this.processContent();
|
|
32122
32608
|
});
|
|
32609
|
+
owl.onPatched(() => {
|
|
32610
|
+
// Required because typing '=SUM' and double-clicking another cell leaves ShowProvider/ShowDescription true
|
|
32611
|
+
if (this.composerStore.editionMode === "inactive") {
|
|
32612
|
+
this.processTokenAtCursor();
|
|
32613
|
+
}
|
|
32614
|
+
});
|
|
32123
32615
|
}
|
|
32124
32616
|
// ---------------------------------------------------------------------------
|
|
32125
32617
|
// Handlers
|
|
@@ -32367,6 +32859,11 @@ class Composer extends owl.Component {
|
|
|
32367
32859
|
}
|
|
32368
32860
|
}
|
|
32369
32861
|
}
|
|
32862
|
+
onContextMenu(ev) {
|
|
32863
|
+
if (this.composerStore.editionMode === "inactive") {
|
|
32864
|
+
this.props.onInputContextMenu?.(ev);
|
|
32865
|
+
}
|
|
32866
|
+
}
|
|
32370
32867
|
// ---------------------------------------------------------------------------
|
|
32371
32868
|
// Private
|
|
32372
32869
|
// ---------------------------------------------------------------------------
|
|
@@ -32588,6 +33085,7 @@ class GridComposer extends owl.Component {
|
|
|
32588
33085
|
static template = "o-spreadsheet-GridComposer";
|
|
32589
33086
|
static props = {
|
|
32590
33087
|
gridDims: Object,
|
|
33088
|
+
onInputContextMenu: Function,
|
|
32591
33089
|
};
|
|
32592
33090
|
static components = { Composer };
|
|
32593
33091
|
rect = this.defaultRect;
|
|
@@ -32635,6 +33133,7 @@ class GridComposer extends owl.Component {
|
|
|
32635
33133
|
isDefaultFocus: true,
|
|
32636
33134
|
onComposerContentFocused: () => this.composerFocusStore.focusGridComposerContent(),
|
|
32637
33135
|
onComposerCellFocused: (content) => this.composerFocusStore.focusGridComposerCell(content),
|
|
33136
|
+
onInputContextMenu: this.props.onInputContextMenu,
|
|
32638
33137
|
};
|
|
32639
33138
|
}
|
|
32640
33139
|
get containerStyle() {
|
|
@@ -32733,7 +33232,6 @@ class GridCellIcon extends owl.Component {
|
|
|
32733
33232
|
cellPosition: Object,
|
|
32734
33233
|
horizontalAlign: { type: String, optional: true },
|
|
32735
33234
|
verticalAlign: { type: String, optional: true },
|
|
32736
|
-
offset: { type: Object, optional: true },
|
|
32737
33235
|
slots: Object,
|
|
32738
33236
|
};
|
|
32739
33237
|
get iconStyle() {
|
|
@@ -32744,8 +33242,8 @@ class GridCellIcon extends owl.Component {
|
|
|
32744
33242
|
const x = this.getIconHorizontalPosition(rect, cellPosition);
|
|
32745
33243
|
const y = this.getIconVerticalPosition(rect, cellPosition);
|
|
32746
33244
|
return cssPropertiesToCss({
|
|
32747
|
-
top: `${y
|
|
32748
|
-
left: `${x
|
|
33245
|
+
top: `${y}px`,
|
|
33246
|
+
left: `${x}px`,
|
|
32749
33247
|
});
|
|
32750
33248
|
}
|
|
32751
33249
|
getIconVerticalPosition(rect, cellPosition) {
|
|
@@ -32785,83 +33283,6 @@ class GridCellIcon extends owl.Component {
|
|
|
32785
33283
|
}
|
|
32786
33284
|
}
|
|
32787
33285
|
|
|
32788
|
-
css /* scss */ `
|
|
32789
|
-
.o-filter-icon {
|
|
32790
|
-
color: ${FILTERS_COLOR};
|
|
32791
|
-
display: flex;
|
|
32792
|
-
align-items: center;
|
|
32793
|
-
justify-content: center;
|
|
32794
|
-
width: ${GRID_ICON_EDGE_LENGTH}px;
|
|
32795
|
-
height: ${GRID_ICON_EDGE_LENGTH}px;
|
|
32796
|
-
|
|
32797
|
-
&:hover {
|
|
32798
|
-
background: ${FILTERS_COLOR};
|
|
32799
|
-
color: #fff;
|
|
32800
|
-
}
|
|
32801
|
-
|
|
32802
|
-
&.o-high-contrast {
|
|
32803
|
-
color: #defade;
|
|
32804
|
-
}
|
|
32805
|
-
&.o-high-contrast:hover {
|
|
32806
|
-
color: ${FILTERS_COLOR};
|
|
32807
|
-
background: #fff;
|
|
32808
|
-
}
|
|
32809
|
-
}
|
|
32810
|
-
.o-filter-icon:hover {
|
|
32811
|
-
background: ${FILTERS_COLOR};
|
|
32812
|
-
color: #fff;
|
|
32813
|
-
}
|
|
32814
|
-
`;
|
|
32815
|
-
class FilterIcon extends owl.Component {
|
|
32816
|
-
static template = "o-spreadsheet-FilterIcon";
|
|
32817
|
-
static props = {
|
|
32818
|
-
cellPosition: Object,
|
|
32819
|
-
};
|
|
32820
|
-
cellPopovers;
|
|
32821
|
-
setup() {
|
|
32822
|
-
this.cellPopovers = useStore(CellPopoverStore);
|
|
32823
|
-
}
|
|
32824
|
-
onClick() {
|
|
32825
|
-
const position = this.props.cellPosition;
|
|
32826
|
-
const activePopover = this.cellPopovers.persistentCellPopover;
|
|
32827
|
-
const { col, row } = position;
|
|
32828
|
-
if (activePopover.isOpen &&
|
|
32829
|
-
activePopover.col === col &&
|
|
32830
|
-
activePopover.row === row &&
|
|
32831
|
-
activePopover.type === "FilterMenu") {
|
|
32832
|
-
this.cellPopovers.close();
|
|
32833
|
-
return;
|
|
32834
|
-
}
|
|
32835
|
-
this.cellPopovers.open({ col, row }, "FilterMenu");
|
|
32836
|
-
}
|
|
32837
|
-
get isFilterActive() {
|
|
32838
|
-
return this.env.model.getters.isFilterActive(this.props.cellPosition);
|
|
32839
|
-
}
|
|
32840
|
-
get iconClass() {
|
|
32841
|
-
const cellStyle = this.env.model.getters.getCellComputedStyle(this.props.cellPosition);
|
|
32842
|
-
const luminance = relativeLuminance(cellStyle.fillColor || "#fff");
|
|
32843
|
-
return luminance < 0.45 ? "o-high-contrast" : "";
|
|
32844
|
-
}
|
|
32845
|
-
}
|
|
32846
|
-
|
|
32847
|
-
class FilterIconsOverlay extends owl.Component {
|
|
32848
|
-
static template = "o-spreadsheet-FilterIconsOverlay";
|
|
32849
|
-
static props = {
|
|
32850
|
-
gridPosition: { type: Object, optional: true },
|
|
32851
|
-
};
|
|
32852
|
-
static components = {
|
|
32853
|
-
GridCellIcon,
|
|
32854
|
-
FilterIcon,
|
|
32855
|
-
};
|
|
32856
|
-
static defaultProps = {
|
|
32857
|
-
gridPosition: { x: 0, y: 0 },
|
|
32858
|
-
};
|
|
32859
|
-
getFilterHeadersPositions() {
|
|
32860
|
-
const sheetId = this.env.model.getters.getActiveSheetId();
|
|
32861
|
-
return this.env.model.getters.getFilterHeaders(sheetId);
|
|
32862
|
-
}
|
|
32863
|
-
}
|
|
32864
|
-
|
|
32865
33286
|
const CHECKBOX_WIDTH = 15;
|
|
32866
33287
|
const MARGIN = (GRID_ICON_EDGE_LENGTH - CHECKBOX_WIDTH) / 2;
|
|
32867
33288
|
css /* scss */ `
|
|
@@ -33529,6 +33950,80 @@ class FiguresContainer extends owl.Component {
|
|
|
33529
33950
|
}
|
|
33530
33951
|
}
|
|
33531
33952
|
|
|
33953
|
+
css /* scss */ `
|
|
33954
|
+
.o-filter-icon {
|
|
33955
|
+
color: ${FILTERS_COLOR};
|
|
33956
|
+
display: flex;
|
|
33957
|
+
align-items: center;
|
|
33958
|
+
justify-content: center;
|
|
33959
|
+
width: ${GRID_ICON_EDGE_LENGTH}px;
|
|
33960
|
+
height: ${GRID_ICON_EDGE_LENGTH}px;
|
|
33961
|
+
|
|
33962
|
+
&:hover {
|
|
33963
|
+
background: ${FILTERS_COLOR};
|
|
33964
|
+
color: #fff;
|
|
33965
|
+
}
|
|
33966
|
+
|
|
33967
|
+
&.o-high-contrast {
|
|
33968
|
+
color: #defade;
|
|
33969
|
+
}
|
|
33970
|
+
&.o-high-contrast:hover {
|
|
33971
|
+
color: ${FILTERS_COLOR};
|
|
33972
|
+
background: #fff;
|
|
33973
|
+
}
|
|
33974
|
+
}
|
|
33975
|
+
.o-filter-icon:hover {
|
|
33976
|
+
background: ${FILTERS_COLOR};
|
|
33977
|
+
color: #fff;
|
|
33978
|
+
}
|
|
33979
|
+
`;
|
|
33980
|
+
class FilterIcon extends owl.Component {
|
|
33981
|
+
static template = "o-spreadsheet-FilterIcon";
|
|
33982
|
+
static props = {
|
|
33983
|
+
cellPosition: Object,
|
|
33984
|
+
};
|
|
33985
|
+
cellPopovers;
|
|
33986
|
+
setup() {
|
|
33987
|
+
this.cellPopovers = useStore(CellPopoverStore);
|
|
33988
|
+
}
|
|
33989
|
+
onClick() {
|
|
33990
|
+
const position = this.props.cellPosition;
|
|
33991
|
+
const activePopover = this.cellPopovers.persistentCellPopover;
|
|
33992
|
+
const { col, row } = position;
|
|
33993
|
+
if (activePopover.isOpen &&
|
|
33994
|
+
activePopover.col === col &&
|
|
33995
|
+
activePopover.row === row &&
|
|
33996
|
+
activePopover.type === "FilterMenu") {
|
|
33997
|
+
this.cellPopovers.close();
|
|
33998
|
+
return;
|
|
33999
|
+
}
|
|
34000
|
+
this.cellPopovers.open({ col, row }, "FilterMenu");
|
|
34001
|
+
}
|
|
34002
|
+
get isFilterActive() {
|
|
34003
|
+
return this.env.model.getters.isFilterActive(this.props.cellPosition);
|
|
34004
|
+
}
|
|
34005
|
+
get iconClass() {
|
|
34006
|
+
const cellStyle = this.env.model.getters.getCellComputedStyle(this.props.cellPosition);
|
|
34007
|
+
const luminance = relativeLuminance(cellStyle.fillColor || "#fff");
|
|
34008
|
+
return luminance < 0.45 ? "o-high-contrast" : "";
|
|
34009
|
+
}
|
|
34010
|
+
}
|
|
34011
|
+
|
|
34012
|
+
class FilterIconsOverlay extends owl.Component {
|
|
34013
|
+
static template = "o-spreadsheet-FilterIconsOverlay";
|
|
34014
|
+
static props = {
|
|
34015
|
+
onMouseDown: Function,
|
|
34016
|
+
};
|
|
34017
|
+
static components = {
|
|
34018
|
+
GridCellIcon,
|
|
34019
|
+
FilterIcon,
|
|
34020
|
+
};
|
|
34021
|
+
getFilterHeadersPositions() {
|
|
34022
|
+
const sheetId = this.env.model.getters.getActiveSheetId();
|
|
34023
|
+
return this.env.model.getters.getFilterHeaders(sheetId);
|
|
34024
|
+
}
|
|
34025
|
+
}
|
|
34026
|
+
|
|
33532
34027
|
css /* scss */ `
|
|
33533
34028
|
.o-grid-add-rows {
|
|
33534
34029
|
input {
|
|
@@ -33742,7 +34237,12 @@ class GridOverlay extends owl.Component {
|
|
|
33742
34237
|
onGridMoved: Function,
|
|
33743
34238
|
gridOverlayDimensions: String,
|
|
33744
34239
|
};
|
|
33745
|
-
static components = {
|
|
34240
|
+
static components = {
|
|
34241
|
+
FiguresContainer,
|
|
34242
|
+
DataValidationOverlay,
|
|
34243
|
+
GridAddRowsFooter,
|
|
34244
|
+
FilterIconsOverlay,
|
|
34245
|
+
};
|
|
33746
34246
|
static defaultProps = {
|
|
33747
34247
|
onCellHovered: () => { },
|
|
33748
34248
|
onCellDoubleClicked: () => { },
|
|
@@ -33787,7 +34287,7 @@ class GridOverlay extends owl.Component {
|
|
|
33787
34287
|
get isPaintingFormat() {
|
|
33788
34288
|
return this.env.model.getters.isPaintingFormat();
|
|
33789
34289
|
}
|
|
33790
|
-
onMouseDown(ev) {
|
|
34290
|
+
onMouseDown(ev, modifiers) {
|
|
33791
34291
|
if (ev.button > 0) {
|
|
33792
34292
|
// not main button, probably a context menu
|
|
33793
34293
|
return;
|
|
@@ -33796,6 +34296,7 @@ class GridOverlay extends owl.Component {
|
|
|
33796
34296
|
this.props.onCellClicked(col, row, {
|
|
33797
34297
|
expandZone: ev.shiftKey,
|
|
33798
34298
|
addZone: isCtrlKey(ev),
|
|
34299
|
+
closePopover: modifiers?.closePopover ?? true,
|
|
33799
34300
|
});
|
|
33800
34301
|
}
|
|
33801
34302
|
onDoubleClick(ev) {
|
|
@@ -34099,11 +34600,6 @@ css /* scss */ `
|
|
|
34099
34600
|
height: 10000px;
|
|
34100
34601
|
background-color: ${SELECTION_BORDER_COLOR};
|
|
34101
34602
|
}
|
|
34102
|
-
.o-unhide-buttons {
|
|
34103
|
-
width: fit-content;
|
|
34104
|
-
gap: 5px;
|
|
34105
|
-
transform: translate(-50%, 0);
|
|
34106
|
-
}
|
|
34107
34603
|
.o-unhide:hover {
|
|
34108
34604
|
z-index: ${ComponentsImportance.Grid + 1};
|
|
34109
34605
|
background-color: lightgrey;
|
|
@@ -34265,10 +34761,6 @@ css /* scss */ `
|
|
|
34265
34761
|
height: 1px;
|
|
34266
34762
|
background-color: ${SELECTION_BORDER_COLOR};
|
|
34267
34763
|
}
|
|
34268
|
-
.o-unhide-buttons {
|
|
34269
|
-
height: fit-content;
|
|
34270
|
-
transform: translate(0, -50%);
|
|
34271
|
-
}
|
|
34272
34764
|
.o-unhide:hover {
|
|
34273
34765
|
z-index: ${ComponentsImportance.Grid + 1};
|
|
34274
34766
|
background-color: lightgrey;
|
|
@@ -35476,7 +35968,7 @@ class VerticalScrollBar extends owl.Component {
|
|
|
35476
35968
|
onScroll(offset) {
|
|
35477
35969
|
const { scrollX } = this.env.model.getters.getActiveSheetDOMScrollInfo();
|
|
35478
35970
|
this.env.model.dispatch("SET_VIEWPORT_OFFSET", {
|
|
35479
|
-
offsetX: scrollX,
|
|
35971
|
+
offsetX: scrollX, // offsetX is the same
|
|
35480
35972
|
offsetY: offset,
|
|
35481
35973
|
});
|
|
35482
35974
|
}
|
|
@@ -35570,7 +36062,6 @@ class Grid extends owl.Component {
|
|
|
35570
36062
|
Popover,
|
|
35571
36063
|
VerticalScrollBar,
|
|
35572
36064
|
HorizontalScrollBar,
|
|
35573
|
-
FilterIconsOverlay,
|
|
35574
36065
|
};
|
|
35575
36066
|
HEADER_HEIGHT = HEADER_HEIGHT;
|
|
35576
36067
|
HEADER_WIDTH = HEADER_WIDTH;
|
|
@@ -35764,8 +36255,8 @@ class Grid extends owl.Component {
|
|
|
35764
36255
|
"Ctrl+Shift+L": () => this.setHorizontalAlign("left"),
|
|
35765
36256
|
"Ctrl+Shift+R": () => this.setHorizontalAlign("right"),
|
|
35766
36257
|
"Ctrl+Shift+V": () => PASTE_AS_VALUE_ACTION(this.env),
|
|
35767
|
-
"Ctrl+Shift+<": () => this.clearFormatting(),
|
|
35768
|
-
"Ctrl+<": () => this.clearFormatting(),
|
|
36258
|
+
"Ctrl+Shift+<": () => this.clearFormatting(), // for qwerty
|
|
36259
|
+
"Ctrl+<": () => this.clearFormatting(), // for azerty
|
|
35769
36260
|
"Ctrl+Shift+ ": () => {
|
|
35770
36261
|
this.env.model.selection.selectAll();
|
|
35771
36262
|
},
|
|
@@ -35871,17 +36362,17 @@ class Grid extends owl.Component {
|
|
|
35871
36362
|
// ---------------------------------------------------------------------------
|
|
35872
36363
|
// Zone selection with mouse
|
|
35873
36364
|
// ---------------------------------------------------------------------------
|
|
35874
|
-
onCellClicked(col, row,
|
|
35875
|
-
if (this.cellPopovers.isOpen) {
|
|
36365
|
+
onCellClicked(col, row, modifiers) {
|
|
36366
|
+
if (modifiers.closePopover && this.cellPopovers.isOpen) {
|
|
35876
36367
|
this.cellPopovers.close();
|
|
35877
36368
|
}
|
|
35878
36369
|
if (this.composerStore.editionMode === "editing") {
|
|
35879
36370
|
this.composerStore.stopEdition();
|
|
35880
36371
|
}
|
|
35881
|
-
if (expandZone) {
|
|
36372
|
+
if (modifiers.expandZone) {
|
|
35882
36373
|
this.env.model.selection.setAnchorCorner(col, row);
|
|
35883
36374
|
}
|
|
35884
|
-
else if (addZone) {
|
|
36375
|
+
else if (modifiers.addZone) {
|
|
35885
36376
|
this.env.model.selection.addCellToSelection(col, row);
|
|
35886
36377
|
}
|
|
35887
36378
|
else {
|
|
@@ -36201,6 +36692,7 @@ const XLSX_CHART_TYPES = [
|
|
|
36201
36692
|
"surfaceChart",
|
|
36202
36693
|
"surface3DChart",
|
|
36203
36694
|
"bubbleChart",
|
|
36695
|
+
"comboChart",
|
|
36204
36696
|
];
|
|
36205
36697
|
|
|
36206
36698
|
/** In XLSX color format (no #) */
|
|
@@ -36532,10 +37024,10 @@ function convertCFCellIsOperator(xlsxCfOperator) {
|
|
|
36532
37024
|
const CF_TYPE_CONVERSION_MAP = {
|
|
36533
37025
|
aboveAverage: undefined,
|
|
36534
37026
|
expression: undefined,
|
|
36535
|
-
cellIs: undefined,
|
|
36536
|
-
colorScale: undefined,
|
|
37027
|
+
cellIs: undefined, // exist but isn't an operator in o_spreadsheet
|
|
37028
|
+
colorScale: undefined, // exist but isn't an operator in o_spreadsheet
|
|
36537
37029
|
dataBar: undefined,
|
|
36538
|
-
iconSet: undefined,
|
|
37030
|
+
iconSet: undefined, // exist but isn't an operator in o_spreadsheet
|
|
36539
37031
|
top10: undefined,
|
|
36540
37032
|
uniqueValues: undefined,
|
|
36541
37033
|
duplicateValues: undefined,
|
|
@@ -36602,7 +37094,7 @@ const CHART_TYPE_CONVERSION_MAP = {
|
|
|
36602
37094
|
line3DChart: undefined,
|
|
36603
37095
|
stockChart: undefined,
|
|
36604
37096
|
radarChart: undefined,
|
|
36605
|
-
scatterChart:
|
|
37097
|
+
scatterChart: "scatter",
|
|
36606
37098
|
pieChart: "pie",
|
|
36607
37099
|
pie3DChart: undefined,
|
|
36608
37100
|
doughnutChart: "pie",
|
|
@@ -36612,6 +37104,7 @@ const CHART_TYPE_CONVERSION_MAP = {
|
|
|
36612
37104
|
surfaceChart: undefined,
|
|
36613
37105
|
surface3DChart: undefined,
|
|
36614
37106
|
bubbleChart: undefined,
|
|
37107
|
+
comboChart: "combo",
|
|
36615
37108
|
};
|
|
36616
37109
|
/** Conversion map for the SUBTOTAL(index, formula) function in xlsx, index <=> actual function*/
|
|
36617
37110
|
const SUBTOTAL_FUNCTION_CONVERSION_MAP = {
|
|
@@ -36770,7 +37263,7 @@ const XLSX_INDEXED_COLORS = {
|
|
|
36770
37263
|
61: "993366",
|
|
36771
37264
|
62: "333399",
|
|
36772
37265
|
63: "333333",
|
|
36773
|
-
64: "000000",
|
|
37266
|
+
64: "000000", // system foreground
|
|
36774
37267
|
65: "FFFFFF", // system background
|
|
36775
37268
|
};
|
|
36776
37269
|
const IMAGE_MIMETYPE_TO_EXTENSION_MAPPING = {
|
|
@@ -37365,29 +37858,12 @@ function convertWidthFromExcel(width) {
|
|
|
37365
37858
|
return width;
|
|
37366
37859
|
return Math.round((width / WIDTH_FACTOR) * 100) / 100;
|
|
37367
37860
|
}
|
|
37368
|
-
function convertBorderDescr(descr) {
|
|
37369
|
-
if (!descr) {
|
|
37370
|
-
return undefined;
|
|
37371
|
-
}
|
|
37372
|
-
return {
|
|
37373
|
-
style: descr.style,
|
|
37374
|
-
color: { rgb: descr.color },
|
|
37375
|
-
};
|
|
37376
|
-
}
|
|
37377
37861
|
function extractStyle(cell, data) {
|
|
37378
37862
|
let style = {};
|
|
37379
37863
|
if (cell.style) {
|
|
37380
37864
|
style = data.styles[cell.style];
|
|
37381
37865
|
}
|
|
37382
37866
|
const format = extractFormat(cell, data);
|
|
37383
|
-
const exportedBorder = {};
|
|
37384
|
-
if (cell.border) {
|
|
37385
|
-
const border = data.borders[cell.border];
|
|
37386
|
-
exportedBorder.left = convertBorderDescr(border.left);
|
|
37387
|
-
exportedBorder.right = convertBorderDescr(border.right);
|
|
37388
|
-
exportedBorder.bottom = convertBorderDescr(border.bottom);
|
|
37389
|
-
exportedBorder.top = convertBorderDescr(border.top);
|
|
37390
|
-
}
|
|
37391
37867
|
const styles = {
|
|
37392
37868
|
font: {
|
|
37393
37869
|
size: style?.fontSize || DEFAULT_FONT_SIZE,
|
|
@@ -37401,7 +37877,7 @@ function extractStyle(cell, data) {
|
|
|
37401
37877
|
}
|
|
37402
37878
|
: { reservedAttribute: "none" },
|
|
37403
37879
|
numFmt: format ? { format: format, id: 0 /* id not used for export */ } : undefined,
|
|
37404
|
-
border:
|
|
37880
|
+
border: cell.border || 0,
|
|
37405
37881
|
alignment: {
|
|
37406
37882
|
horizontal: style.align,
|
|
37407
37883
|
vertical: style.verticalAlign
|
|
@@ -37423,15 +37899,12 @@ function extractFormat(cell, data) {
|
|
|
37423
37899
|
return undefined;
|
|
37424
37900
|
}
|
|
37425
37901
|
function normalizeStyle(construct, styles) {
|
|
37426
|
-
const { id: fontId } = pushElement(styles["font"], construct.fonts);
|
|
37427
|
-
const { id: fillId } = pushElement(styles["fill"], construct.fills);
|
|
37428
|
-
const { id: borderId } = pushElement(styles["border"], construct.borders);
|
|
37429
37902
|
// Normalize this
|
|
37430
37903
|
const numFmtId = convertFormat(styles["numFmt"], construct.numFmts);
|
|
37431
37904
|
const style = {
|
|
37432
|
-
fontId,
|
|
37433
|
-
fillId,
|
|
37434
|
-
borderId,
|
|
37905
|
+
fontId: pushElement(styles.font, construct.fonts),
|
|
37906
|
+
fillId: pushElement(styles.fill, construct.fills),
|
|
37907
|
+
borderId: styles.border,
|
|
37435
37908
|
numFmtId,
|
|
37436
37909
|
alignment: {
|
|
37437
37910
|
vertical: styles.alignment.vertical,
|
|
@@ -37439,8 +37912,7 @@ function normalizeStyle(construct, styles) {
|
|
|
37439
37912
|
wrapText: styles.alignment.wrapText,
|
|
37440
37913
|
},
|
|
37441
37914
|
};
|
|
37442
|
-
|
|
37443
|
-
return id;
|
|
37915
|
+
return pushElement(style, construct.styles);
|
|
37444
37916
|
}
|
|
37445
37917
|
function convertFormat(format, numFmtStructure) {
|
|
37446
37918
|
if (!format) {
|
|
@@ -37448,8 +37920,7 @@ function convertFormat(format, numFmtStructure) {
|
|
|
37448
37920
|
}
|
|
37449
37921
|
let formatId = XLSX_FORMAT_MAP[format.format];
|
|
37450
37922
|
if (!formatId) {
|
|
37451
|
-
|
|
37452
|
-
formatId = id + FIRST_NUMFMT_ID;
|
|
37923
|
+
formatId = pushElement(format, numFmtStructure) + FIRST_NUMFMT_ID;
|
|
37453
37924
|
}
|
|
37454
37925
|
return formatId;
|
|
37455
37926
|
}
|
|
@@ -37474,20 +37945,15 @@ function addRelsToFile(relsFiles, path, rel) {
|
|
|
37474
37945
|
return id;
|
|
37475
37946
|
}
|
|
37476
37947
|
function pushElement(property, propertyList) {
|
|
37477
|
-
|
|
37478
|
-
|
|
37479
|
-
|
|
37948
|
+
let len = propertyList.length;
|
|
37949
|
+
const operator = typeof property === "object" ? deepEquals : (a, b) => a === b;
|
|
37950
|
+
for (let i = 0; i < len; i++) {
|
|
37951
|
+
if (operator(property, propertyList[i])) {
|
|
37952
|
+
return i;
|
|
37480
37953
|
}
|
|
37481
37954
|
}
|
|
37482
|
-
|
|
37483
|
-
|
|
37484
|
-
propertyList.push(property);
|
|
37485
|
-
elemId = propertyList.length - 1;
|
|
37486
|
-
}
|
|
37487
|
-
return {
|
|
37488
|
-
id: elemId,
|
|
37489
|
-
list: propertyList,
|
|
37490
|
-
};
|
|
37955
|
+
propertyList[propertyList.length] = property;
|
|
37956
|
+
return propertyList.length - 1;
|
|
37491
37957
|
}
|
|
37492
37958
|
const chartIds = [];
|
|
37493
37959
|
/**
|
|
@@ -37962,7 +38428,7 @@ function convertHyperlink(link, cellValue, warningManager) {
|
|
|
37962
38428
|
function getSheetDims(sheet) {
|
|
37963
38429
|
const dims = [0, 0];
|
|
37964
38430
|
for (let row of sheet.rows) {
|
|
37965
|
-
dims[0] = Math.max(dims[0],
|
|
38431
|
+
dims[0] = Math.max(dims[0], largeMax(row.cells.map((cell) => toCartesian(cell.xc).col)));
|
|
37966
38432
|
dims[1] = Math.max(dims[1], row.index);
|
|
37967
38433
|
}
|
|
37968
38434
|
dims[0] = Math.max(dims[0], EXCEL_IMPORT_DEFAULT_NUMBER_OF_COLS);
|
|
@@ -38282,7 +38748,25 @@ function parseXML(xmlString, mimeType = "text/xml") {
|
|
|
38282
38748
|
}
|
|
38283
38749
|
return document;
|
|
38284
38750
|
}
|
|
38285
|
-
function
|
|
38751
|
+
function convertBorderDescr(descr) {
|
|
38752
|
+
if (!descr) {
|
|
38753
|
+
return undefined;
|
|
38754
|
+
}
|
|
38755
|
+
return {
|
|
38756
|
+
style: descr.style,
|
|
38757
|
+
color: { rgb: descr.color },
|
|
38758
|
+
};
|
|
38759
|
+
}
|
|
38760
|
+
function getDefaultXLSXStructure(data) {
|
|
38761
|
+
const xlsxBorders = Object.values(data.borders).map((border) => {
|
|
38762
|
+
return {
|
|
38763
|
+
left: convertBorderDescr(border.left),
|
|
38764
|
+
right: convertBorderDescr(border.right),
|
|
38765
|
+
bottom: convertBorderDescr(border.bottom),
|
|
38766
|
+
top: convertBorderDescr(border.top),
|
|
38767
|
+
};
|
|
38768
|
+
});
|
|
38769
|
+
const borders = [{}, ...xlsxBorders];
|
|
38286
38770
|
return {
|
|
38287
38771
|
relsFiles: [],
|
|
38288
38772
|
sharedStrings: [],
|
|
@@ -38305,7 +38789,7 @@ function getDefaultXLSXStructure() {
|
|
|
38305
38789
|
},
|
|
38306
38790
|
],
|
|
38307
38791
|
fills: [{ reservedAttribute: "none" }, { reservedAttribute: "gray125" }],
|
|
38308
|
-
borders
|
|
38792
|
+
borders,
|
|
38309
38793
|
numFmts: [],
|
|
38310
38794
|
dxfs: [],
|
|
38311
38795
|
};
|
|
@@ -38828,6 +39312,9 @@ class XlsxChartExtractor extends XlsxBaseExtractor {
|
|
|
38828
39312
|
if (!CHART_TYPE_CONVERSION_MAP[chartType]) {
|
|
38829
39313
|
throw new Error(`Unsupported chart type ${chartType}`);
|
|
38830
39314
|
}
|
|
39315
|
+
if (CHART_TYPE_CONVERSION_MAP[chartType] === "combo") {
|
|
39316
|
+
return this.extractComboChart(rootChartElement);
|
|
39317
|
+
}
|
|
38831
39318
|
// Title can be separated into multiple xml elements (for styling and such), we only import the text
|
|
38832
39319
|
const chartTitle = this.mapOnElements({ parent: rootChartElement, query: "c:title a:t" }, (textElement) => {
|
|
38833
39320
|
return textElement.textContent || "";
|
|
@@ -38838,8 +39325,8 @@ class XlsxChartExtractor extends XlsxBaseExtractor {
|
|
|
38838
39325
|
return {
|
|
38839
39326
|
title: chartTitle,
|
|
38840
39327
|
type: CHART_TYPE_CONVERSION_MAP[chartType],
|
|
38841
|
-
dataSets: this.extractChartDatasets(this.querySelector(rootChartElement, `c:${chartType}`)),
|
|
38842
|
-
labelRange: this.extractChildTextContent(rootChartElement,
|
|
39328
|
+
dataSets: this.extractChartDatasets(this.querySelector(rootChartElement, `c:${chartType}`), chartType),
|
|
39329
|
+
labelRange: this.extractChildTextContent(rootChartElement, `c:ser ${chartType === "scatterChart" ? "c:numRef" : "c:cat"} c:f`),
|
|
38843
39330
|
backgroundColor: this.extractChildAttr(rootChartElement, "c:chartSpace > c:spPr a:srgbClr", "val", {
|
|
38844
39331
|
default: "ffffff",
|
|
38845
39332
|
}).asString(),
|
|
@@ -38856,7 +39343,41 @@ class XlsxChartExtractor extends XlsxBaseExtractor {
|
|
|
38856
39343
|
};
|
|
38857
39344
|
})[0];
|
|
38858
39345
|
}
|
|
38859
|
-
|
|
39346
|
+
extractComboChart(chartElement) {
|
|
39347
|
+
// Title can be separated into multiple xml elements (for styling and such), we only import the text
|
|
39348
|
+
const chartTitle = this.mapOnElements({ parent: chartElement, query: "c:title a:t" }, (textElement) => {
|
|
39349
|
+
return textElement.textContent || "";
|
|
39350
|
+
}).join("");
|
|
39351
|
+
const barChartGrouping = this.extractChildAttr(chartElement, "c:grouping", "val", {
|
|
39352
|
+
default: "clustered",
|
|
39353
|
+
}).asString();
|
|
39354
|
+
return {
|
|
39355
|
+
title: chartTitle,
|
|
39356
|
+
type: "combo",
|
|
39357
|
+
dataSets: [
|
|
39358
|
+
...this.extractChartDatasets(this.querySelector(chartElement, `c:barChart`), "comboChart"),
|
|
39359
|
+
...this.extractChartDatasets(this.querySelector(chartElement, `c:lineChart`), "comboChart"),
|
|
39360
|
+
],
|
|
39361
|
+
labelRange: this.extractChildTextContent(chartElement, "c:ser c:cat c:f"),
|
|
39362
|
+
backgroundColor: this.extractChildAttr(chartElement, "c:chartSpace > c:spPr a:srgbClr", "val", {
|
|
39363
|
+
default: "ffffff",
|
|
39364
|
+
}).asString(),
|
|
39365
|
+
verticalAxisPosition: this.extractChildAttr(chartElement, "c:valAx > c:axPos", "val", {
|
|
39366
|
+
default: "l",
|
|
39367
|
+
}).asString() === "r"
|
|
39368
|
+
? "right"
|
|
39369
|
+
: "left",
|
|
39370
|
+
legendPosition: DRAWING_LEGEND_POSITION_CONVERSION_MAP[this.extractChildAttr(chartElement, "c:legendPos", "val", {
|
|
39371
|
+
default: "b",
|
|
39372
|
+
}).asString()],
|
|
39373
|
+
stacked: barChartGrouping === "stacked",
|
|
39374
|
+
fontColor: "000000",
|
|
39375
|
+
};
|
|
39376
|
+
}
|
|
39377
|
+
extractChartDatasets(chartElement, chartType) {
|
|
39378
|
+
if (chartType === "scatterChart") {
|
|
39379
|
+
return this.extractScatterChartDatasets(chartElement);
|
|
39380
|
+
}
|
|
38860
39381
|
return this.mapOnElements({ parent: chartElement, query: "c:ser" }, (chartDataElement) => {
|
|
38861
39382
|
return {
|
|
38862
39383
|
label: this.extractChildTextContent(chartDataElement, "c:tx c:f"),
|
|
@@ -38864,6 +39385,14 @@ class XlsxChartExtractor extends XlsxBaseExtractor {
|
|
|
38864
39385
|
};
|
|
38865
39386
|
});
|
|
38866
39387
|
}
|
|
39388
|
+
extractScatterChartDatasets(chartElement) {
|
|
39389
|
+
return this.mapOnElements({ parent: chartElement, query: "c:ser" }, (chartDataElement) => {
|
|
39390
|
+
return {
|
|
39391
|
+
label: this.extractChildTextContent(chartDataElement, "c:xVal c:f", { required: false }),
|
|
39392
|
+
range: this.extractChildTextContent(chartDataElement, "c:yVal c:f", { required: true }),
|
|
39393
|
+
};
|
|
39394
|
+
});
|
|
39395
|
+
}
|
|
38867
39396
|
/**
|
|
38868
39397
|
* The chart type in the XML isn't explicitly defined, but there is an XML element that define the
|
|
38869
39398
|
* chart, and this element tag name tells us which type of chart it is. We just need to find this XML element.
|
|
@@ -38873,12 +39402,21 @@ class XlsxChartExtractor extends XlsxBaseExtractor {
|
|
|
38873
39402
|
if (!plotAreaElement) {
|
|
38874
39403
|
throw new Error("Missing plot area in the chart definition.");
|
|
38875
39404
|
}
|
|
39405
|
+
let globalTag = undefined;
|
|
38876
39406
|
for (let child of plotAreaElement.children) {
|
|
38877
39407
|
const tag = removeTagEscapedNamespaces(child.tagName);
|
|
38878
39408
|
if (XLSX_CHART_TYPES.some((chartType) => chartType === tag)) {
|
|
38879
|
-
|
|
39409
|
+
if (!globalTag) {
|
|
39410
|
+
globalTag = tag;
|
|
39411
|
+
}
|
|
39412
|
+
else if (globalTag !== tag) {
|
|
39413
|
+
globalTag = "comboChart";
|
|
39414
|
+
}
|
|
38880
39415
|
}
|
|
38881
39416
|
}
|
|
39417
|
+
if (globalTag) {
|
|
39418
|
+
return globalTag;
|
|
39419
|
+
}
|
|
38882
39420
|
throw new Error("Unknown chart type");
|
|
38883
39421
|
}
|
|
38884
39422
|
}
|
|
@@ -40248,12 +40786,14 @@ class BasePlugin {
|
|
|
40248
40786
|
static getters = [];
|
|
40249
40787
|
history;
|
|
40250
40788
|
dispatch;
|
|
40251
|
-
|
|
40789
|
+
canDispatch;
|
|
40790
|
+
constructor(stateObserver, dispatch, canDispatch) {
|
|
40252
40791
|
this.history = Object.assign(Object.create(stateObserver), {
|
|
40253
40792
|
update: stateObserver.addChange.bind(stateObserver, this),
|
|
40254
40793
|
selectCell: () => { },
|
|
40255
40794
|
});
|
|
40256
40795
|
this.dispatch = dispatch;
|
|
40796
|
+
this.canDispatch = canDispatch;
|
|
40257
40797
|
}
|
|
40258
40798
|
/**
|
|
40259
40799
|
* Export for excel should be available for all plugins, even for the UI.
|
|
@@ -40332,8 +40872,8 @@ class BasePlugin {
|
|
|
40332
40872
|
class CorePlugin extends BasePlugin {
|
|
40333
40873
|
getters;
|
|
40334
40874
|
uuidGenerator;
|
|
40335
|
-
constructor({ getters, stateObserver, range, dispatch, uuidGenerator }) {
|
|
40336
|
-
super(stateObserver, dispatch);
|
|
40875
|
+
constructor({ getters, stateObserver, range, dispatch, canDispatch, uuidGenerator, }) {
|
|
40876
|
+
super(stateObserver, dispatch, canDispatch);
|
|
40337
40877
|
range.addRangeProvider(this.adaptRanges.bind(this));
|
|
40338
40878
|
this.getters = getters;
|
|
40339
40879
|
this.uuidGenerator = uuidGenerator;
|
|
@@ -42469,6 +43009,9 @@ class DataValidationPlugin extends CorePlugin {
|
|
|
42469
43009
|
if (newRule.criterion.type === "isBoolean") {
|
|
42470
43010
|
this.setCenterStyleToBooleanCells(newRule);
|
|
42471
43011
|
}
|
|
43012
|
+
else if (newRule.criterion.type === "isValueInList") {
|
|
43013
|
+
newRule.criterion.values = Array.from(new Set(newRule.criterion.values));
|
|
43014
|
+
}
|
|
42472
43015
|
const adaptedRules = this.removeRangesFromRules(sheetId, newRule.ranges, rules);
|
|
42473
43016
|
const ruleIndex = adaptedRules.findIndex((rule) => rule.id === newRule.id);
|
|
42474
43017
|
if (ruleIndex !== -1) {
|
|
@@ -42865,7 +43408,7 @@ class HeaderVisibilityPlugin extends CorePlugin {
|
|
|
42865
43408
|
if (hiddenElements.size >= elements) {
|
|
42866
43409
|
return "TooManyHiddenElements" /* CommandResult.TooManyHiddenElements */;
|
|
42867
43410
|
}
|
|
42868
|
-
else if (
|
|
43411
|
+
else if (largeMin(cmd.elements) < 0 || largeMax(cmd.elements) > elements) {
|
|
42869
43412
|
return "InvalidHeaderIndex" /* CommandResult.InvalidHeaderIndex */;
|
|
42870
43413
|
}
|
|
42871
43414
|
else {
|
|
@@ -43194,7 +43737,6 @@ class MergePlugin extends CorePlugin {
|
|
|
43194
43737
|
"isInSameMerge",
|
|
43195
43738
|
"isMergeHidden",
|
|
43196
43739
|
"getMainCellPosition",
|
|
43197
|
-
"getBottomLeftCell",
|
|
43198
43740
|
"expandZone",
|
|
43199
43741
|
"doesIntersectMerge",
|
|
43200
43742
|
"doesColumnsHaveCommonMerges",
|
|
@@ -43376,13 +43918,6 @@ class MergePlugin extends CorePlugin {
|
|
|
43376
43918
|
const mergeTopLeftPos = this.getMerge(position).topLeft;
|
|
43377
43919
|
return { sheetId: position.sheetId, col: mergeTopLeftPos.col, row: mergeTopLeftPos.row };
|
|
43378
43920
|
}
|
|
43379
|
-
getBottomLeftCell(position) {
|
|
43380
|
-
if (!this.isInMerge(position)) {
|
|
43381
|
-
return position;
|
|
43382
|
-
}
|
|
43383
|
-
const { bottom, left } = this.getMerge(position);
|
|
43384
|
-
return { sheetId: position.sheetId, col: left, row: bottom };
|
|
43385
|
-
}
|
|
43386
43921
|
isMergeHidden(sheetId, merge) {
|
|
43387
43922
|
const hiddenColsGroups = this.getters.getHiddenColsGroups(sheetId);
|
|
43388
43923
|
const hiddenRowsGroups = this.getters.getHiddenRowsGroups(sheetId);
|
|
@@ -43683,8 +44218,8 @@ class RangeAdapter {
|
|
|
43683
44218
|
let newRange = range;
|
|
43684
44219
|
let changeType = "NONE";
|
|
43685
44220
|
for (let group of groups) {
|
|
43686
|
-
const min =
|
|
43687
|
-
const max =
|
|
44221
|
+
const min = largeMin(group);
|
|
44222
|
+
const max = largeMax(group);
|
|
43688
44223
|
if (range.zone[start] <= min && min <= range.zone[end]) {
|
|
43689
44224
|
const toRemove = Math.min(range.zone[end], max) - min + 1;
|
|
43690
44225
|
changeType = "RESIZE";
|
|
@@ -44066,7 +44601,6 @@ class SheetPlugin extends CorePlugin {
|
|
|
44066
44601
|
"getSheetIds",
|
|
44067
44602
|
"getVisibleSheetIds",
|
|
44068
44603
|
"isSheetVisible",
|
|
44069
|
-
"getEvaluationSheets",
|
|
44070
44604
|
"doesHeaderExist",
|
|
44071
44605
|
"doesHeadersExist",
|
|
44072
44606
|
"getCell",
|
|
@@ -44133,8 +44667,8 @@ class SheetPlugin extends CorePlugin {
|
|
|
44133
44667
|
}
|
|
44134
44668
|
return "Success" /* CommandResult.Success */;
|
|
44135
44669
|
case "REMOVE_COLUMNS_ROWS": {
|
|
44136
|
-
const min =
|
|
44137
|
-
const max =
|
|
44670
|
+
const min = largeMin(cmd.elements);
|
|
44671
|
+
const max = largeMax(cmd.elements);
|
|
44138
44672
|
if (min < 0 || !this.doesHeaderExist(cmd.sheetId, cmd.dimension, max)) {
|
|
44139
44673
|
return "InvalidHeaderIndex" /* CommandResult.InvalidHeaderIndex */;
|
|
44140
44674
|
}
|
|
@@ -44325,9 +44859,6 @@ class SheetPlugin extends CorePlugin {
|
|
|
44325
44859
|
getVisibleSheetIds() {
|
|
44326
44860
|
return this.orderedSheetIds.filter(this.isSheetVisible.bind(this));
|
|
44327
44861
|
}
|
|
44328
|
-
getEvaluationSheets() {
|
|
44329
|
-
return this.sheets;
|
|
44330
|
-
}
|
|
44331
44862
|
doesHeaderExist(sheetId, dimension, index) {
|
|
44332
44863
|
return dimension === "COL"
|
|
44333
44864
|
? index >= 0 && index < this.getNumberCols(sheetId)
|
|
@@ -44336,13 +44867,6 @@ class SheetPlugin extends CorePlugin {
|
|
|
44336
44867
|
doesHeadersExist(sheetId, dimension, headerIndexes) {
|
|
44337
44868
|
return headerIndexes.every((index) => this.doesHeaderExist(sheetId, dimension, index));
|
|
44338
44869
|
}
|
|
44339
|
-
getRow(sheetId, index) {
|
|
44340
|
-
const row = this.getSheet(sheetId).rows[index];
|
|
44341
|
-
if (!row) {
|
|
44342
|
-
throw new Error(`Row ${row} not found.`);
|
|
44343
|
-
}
|
|
44344
|
-
return row;
|
|
44345
|
-
}
|
|
44346
44870
|
getCell({ sheetId, col, row }) {
|
|
44347
44871
|
const sheet = this.tryGetSheet(sheetId);
|
|
44348
44872
|
const cellId = sheet?.rows[row]?.cells[col];
|
|
@@ -45846,8 +46370,8 @@ class UIPlugin extends BasePlugin {
|
|
|
45846
46370
|
getters;
|
|
45847
46371
|
ui;
|
|
45848
46372
|
selection;
|
|
45849
|
-
constructor({ getters, stateObserver, dispatch, uiActions, selection }) {
|
|
45850
|
-
super(stateObserver, dispatch);
|
|
46373
|
+
constructor({ getters, stateObserver, dispatch, canDispatch, uiActions, selection, }) {
|
|
46374
|
+
super(stateObserver, dispatch, canDispatch);
|
|
45851
46375
|
this.getters = getters;
|
|
45852
46376
|
this.ui = uiActions;
|
|
45853
46377
|
this.selection = selection;
|
|
@@ -45911,12 +46435,6 @@ class CompilationParametersBuilder {
|
|
|
45911
46435
|
: _t("Function %s expects its parameters to be single values or single cell references, not ranges.", functionName.toString()));
|
|
45912
46436
|
}
|
|
45913
46437
|
const position = { sheetId: range.sheetId, col: range.zone.left, row: range.zone.top };
|
|
45914
|
-
return this.readCell(position);
|
|
45915
|
-
}
|
|
45916
|
-
readCell(position) {
|
|
45917
|
-
if (!this.getters.tryGetSheet(position.sheetId)) {
|
|
45918
|
-
throw new EvaluationError(_t("Invalid sheet name"));
|
|
45919
|
-
}
|
|
45920
46438
|
return this.computeCell(position);
|
|
45921
46439
|
}
|
|
45922
46440
|
/**
|
|
@@ -45952,7 +46470,7 @@ class CompilationParametersBuilder {
|
|
|
45952
46470
|
matrix[colIndex] = new Array(height);
|
|
45953
46471
|
for (let row = _zone.top; row <= _zone.bottom; row++) {
|
|
45954
46472
|
const rowIndex = row - _zone.top;
|
|
45955
|
-
matrix[colIndex][rowIndex] = this.
|
|
46473
|
+
matrix[colIndex][rowIndex] = this.computeCell({ sheetId, col, row });
|
|
45956
46474
|
}
|
|
45957
46475
|
}
|
|
45958
46476
|
this.rangeCache[cacheKey] = matrix;
|
|
@@ -47060,15 +47578,15 @@ class Evaluator {
|
|
|
47060
47578
|
getEvaluatedCell(position) {
|
|
47061
47579
|
return this.evaluatedCells.get(position) || EMPTY_CELL;
|
|
47062
47580
|
}
|
|
47063
|
-
|
|
47581
|
+
getSpreadZone(position) {
|
|
47064
47582
|
if (!this.spreadingRelations.isArrayFormula(position)) {
|
|
47065
|
-
return
|
|
47583
|
+
return undefined;
|
|
47066
47584
|
}
|
|
47067
47585
|
if (this.evaluatedCells.get(position)?.type === CellValueType.error) {
|
|
47068
|
-
return
|
|
47586
|
+
return positionToZone(position);
|
|
47069
47587
|
}
|
|
47070
47588
|
const spreadPositions = Array.from(this.spreadingRelations.getArrayResultPositions(position));
|
|
47071
|
-
return
|
|
47589
|
+
return union(positionToZone(position), unionPositionsToZone(spreadPositions));
|
|
47072
47590
|
}
|
|
47073
47591
|
getEvaluatedPositions() {
|
|
47074
47592
|
return this.evaluatedCells.keys();
|
|
@@ -47133,7 +47651,9 @@ class Evaluator {
|
|
|
47133
47651
|
this.blockedArrayFormulas = this.createEmptyPositionSet();
|
|
47134
47652
|
this.spreadingRelations = new SpreadingRelation(this.createEmptyPositionSet.bind(this));
|
|
47135
47653
|
this.formulaDependencies = lazy(() => {
|
|
47136
|
-
const dependencies = [...this.getAllCells()].flatMap((position) => this.getDirectDependencies(position)
|
|
47654
|
+
const dependencies = [...this.getAllCells()].flatMap((position) => this.getDirectDependencies(position)
|
|
47655
|
+
.filter((range) => !range.invalidSheetName && !range.invalidXc)
|
|
47656
|
+
.map((range) => ({
|
|
47137
47657
|
data: position,
|
|
47138
47658
|
boundingBox: {
|
|
47139
47659
|
zone: range.zone,
|
|
@@ -47473,7 +47993,7 @@ class EvaluationPlugin extends UIPlugin {
|
|
|
47473
47993
|
"getEvaluatedCell",
|
|
47474
47994
|
"getEvaluatedCells",
|
|
47475
47995
|
"getEvaluatedCellsInZone",
|
|
47476
|
-
"
|
|
47996
|
+
"getSpreadZone",
|
|
47477
47997
|
"getArrayFormulaSpreadingOn",
|
|
47478
47998
|
"isEmpty",
|
|
47479
47999
|
];
|
|
@@ -47579,8 +48099,11 @@ class EvaluationPlugin extends UIPlugin {
|
|
|
47579
48099
|
getEvaluatedCellsInZone(sheetId, zone) {
|
|
47580
48100
|
return positions(zone).map(({ col, row }) => this.getters.getEvaluatedCell({ sheetId, col, row }));
|
|
47581
48101
|
}
|
|
47582
|
-
|
|
47583
|
-
|
|
48102
|
+
/**
|
|
48103
|
+
* Return the spread zone the position is part of, if any
|
|
48104
|
+
*/
|
|
48105
|
+
getSpreadZone(position) {
|
|
48106
|
+
return this.evaluator.getSpreadZone(position);
|
|
47584
48107
|
}
|
|
47585
48108
|
getArrayFormulaSpreadingOn(position) {
|
|
47586
48109
|
return this.evaluator.getArrayFormulaSpreadingOn(position);
|
|
@@ -47620,7 +48143,7 @@ class EvaluationPlugin extends UIPlugin {
|
|
|
47620
48143
|
? getItemId(newFormat, data.formats)
|
|
47621
48144
|
: exportedCellData.format;
|
|
47622
48145
|
let content;
|
|
47623
|
-
if (formulaCell instanceof FormulaCellWithDependencies) {
|
|
48146
|
+
if (isFormula && formulaCell instanceof FormulaCellWithDependencies) {
|
|
47624
48147
|
content = formulaCell.contentWithFixedReferences;
|
|
47625
48148
|
}
|
|
47626
48149
|
else {
|
|
@@ -47668,17 +48191,17 @@ function isBadExpression(tokens) {
|
|
|
47668
48191
|
*/
|
|
47669
48192
|
function sortWithClusters(colorsToSort) {
|
|
47670
48193
|
const clusters = [
|
|
47671
|
-
{ leadColor: rgba(255, 0, 0), colors: [] },
|
|
47672
|
-
{ leadColor: rgba(255, 128, 0), colors: [] },
|
|
47673
|
-
{ leadColor: rgba(128, 128, 0), colors: [] },
|
|
47674
|
-
{ leadColor: rgba(128, 255, 0), colors: [] },
|
|
47675
|
-
{ leadColor: rgba(0, 255, 0), colors: [] },
|
|
47676
|
-
{ leadColor: rgba(0, 255, 128), colors: [] },
|
|
47677
|
-
{ leadColor: rgba(0, 255, 255), colors: [] },
|
|
47678
|
-
{ leadColor: rgba(0, 127, 255), colors: [] },
|
|
47679
|
-
{ leadColor: rgba(0, 0, 255), colors: [] },
|
|
47680
|
-
{ leadColor: rgba(127, 0, 255), colors: [] },
|
|
47681
|
-
{ leadColor: rgba(128, 0, 128), colors: [] },
|
|
48194
|
+
{ leadColor: rgba(255, 0, 0), colors: [] }, // red
|
|
48195
|
+
{ leadColor: rgba(255, 128, 0), colors: [] }, // orange
|
|
48196
|
+
{ leadColor: rgba(128, 128, 0), colors: [] }, // yellow
|
|
48197
|
+
{ leadColor: rgba(128, 255, 0), colors: [] }, // chartreuse
|
|
48198
|
+
{ leadColor: rgba(0, 255, 0), colors: [] }, // green
|
|
48199
|
+
{ leadColor: rgba(0, 255, 128), colors: [] }, // spring green
|
|
48200
|
+
{ leadColor: rgba(0, 255, 255), colors: [] }, // cyan
|
|
48201
|
+
{ leadColor: rgba(0, 127, 255), colors: [] }, // azure
|
|
48202
|
+
{ leadColor: rgba(0, 0, 255), colors: [] }, // blue
|
|
48203
|
+
{ leadColor: rgba(127, 0, 255), colors: [] }, // violet
|
|
48204
|
+
{ leadColor: rgba(128, 0, 128), colors: [] }, // magenta
|
|
47682
48205
|
{ leadColor: rgba(255, 0, 128), colors: [] }, // rose
|
|
47683
48206
|
];
|
|
47684
48207
|
for (const color of colorsToSort.map(colorToRGBA)) {
|
|
@@ -48069,13 +48592,13 @@ class EvaluationConditionalFormatPlugin extends UIPlugin {
|
|
|
48069
48592
|
.map((cell) => cell.value);
|
|
48070
48593
|
switch (threshold.type) {
|
|
48071
48594
|
case "value":
|
|
48072
|
-
const result = functionName === "max" ?
|
|
48595
|
+
const result = functionName === "max" ? largeMax(rangeValues) : largeMin(rangeValues);
|
|
48073
48596
|
return result;
|
|
48074
48597
|
case "number":
|
|
48075
48598
|
return Number(threshold.value);
|
|
48076
48599
|
case "percentage":
|
|
48077
|
-
const min =
|
|
48078
|
-
const max =
|
|
48600
|
+
const min = largeMin(rangeValues);
|
|
48601
|
+
const max = largeMax(rangeValues);
|
|
48079
48602
|
const delta = max - min;
|
|
48080
48603
|
return min + (delta * Number(threshold.value)) / 100;
|
|
48081
48604
|
case "percentile":
|
|
@@ -48542,8 +49065,8 @@ class DynamicTablesPlugin extends UIPlugin {
|
|
|
48542
49065
|
else if (deepEquals(parentSpreadingCell, topLeft) && getZoneArea(unionZone) === 1) {
|
|
48543
49066
|
return true;
|
|
48544
49067
|
}
|
|
48545
|
-
const
|
|
48546
|
-
return deepEquals(unionZone,
|
|
49068
|
+
const zone = this.getters.getSpreadZone(parentSpreadingCell);
|
|
49069
|
+
return deepEquals(unionZone, zone);
|
|
48547
49070
|
}
|
|
48548
49071
|
coreTableToTable(sheetId, table) {
|
|
48549
49072
|
if (table.type !== "dynamic") {
|
|
@@ -48551,8 +49074,7 @@ class DynamicTablesPlugin extends UIPlugin {
|
|
|
48551
49074
|
}
|
|
48552
49075
|
const tableZone = table.range.zone;
|
|
48553
49076
|
const tablePosition = { sheetId, col: tableZone.left, row: tableZone.top };
|
|
48554
|
-
const
|
|
48555
|
-
const zone = spreadPositions.length ? unionPositionsToZone(spreadPositions) : table.range.zone;
|
|
49077
|
+
const zone = this.getters.getSpreadZone(tablePosition) ?? table.range.zone;
|
|
48556
49078
|
const range = this.getters.getRangeFromZone(sheetId, zone);
|
|
48557
49079
|
const filters = this.getDynamicTableFilters(sheetId, table, zone);
|
|
48558
49080
|
return { id: table.id, range, filters, config: table.config };
|
|
@@ -49002,8 +49524,7 @@ class AutofillPlugin extends UIPlugin {
|
|
|
49002
49524
|
let row = zone.bottom;
|
|
49003
49525
|
if (col > 0) {
|
|
49004
49526
|
let leftPosition = { sheetId, col: col - 1, row };
|
|
49005
|
-
while (this.getters.
|
|
49006
|
-
this.getters.getCell(leftPosition)?.content) {
|
|
49527
|
+
while (this.getters.getEvaluatedCell(leftPosition).type !== CellValueType.empty) {
|
|
49007
49528
|
row += 1;
|
|
49008
49529
|
leftPosition = { sheetId, col: col - 1, row };
|
|
49009
49530
|
}
|
|
@@ -49012,8 +49533,7 @@ class AutofillPlugin extends UIPlugin {
|
|
|
49012
49533
|
col = zone.right;
|
|
49013
49534
|
if (col <= this.getters.getNumberCols(sheetId)) {
|
|
49014
49535
|
let rightPosition = { sheetId, col: col + 1, row };
|
|
49015
|
-
while (this.getters.
|
|
49016
|
-
this.getters.getCell(rightPosition)?.content) {
|
|
49536
|
+
while (this.getters.getEvaluatedCell(rightPosition).type !== CellValueType.empty) {
|
|
49017
49537
|
row += 1;
|
|
49018
49538
|
rightPosition = { sheetId, col: col + 1, row };
|
|
49019
49539
|
}
|
|
@@ -49323,13 +49843,13 @@ class AutomaticSumPlugin extends UIPlugin {
|
|
|
49323
49843
|
const cells = this.getters.getEvaluatedCellsInZone(sheet.id, zone);
|
|
49324
49844
|
const cellPositions = range(end, -1, -1);
|
|
49325
49845
|
const invalidCells = cellPositions.filter((position) => cells[position] && !cells[position].isAutoSummable);
|
|
49326
|
-
const maxValidPosition =
|
|
49846
|
+
const maxValidPosition = largeMax(invalidCells);
|
|
49327
49847
|
const numberSequences = groupConsecutive(cellPositions.filter((position) => this.isNumber(cells[position])));
|
|
49328
49848
|
const firstSequence = numberSequences[0] || [];
|
|
49329
|
-
if (
|
|
49849
|
+
if (largeMax(firstSequence) < maxValidPosition) {
|
|
49330
49850
|
return Infinity;
|
|
49331
49851
|
}
|
|
49332
|
-
return
|
|
49852
|
+
return largeMin(firstSequence);
|
|
49333
49853
|
}
|
|
49334
49854
|
shouldFindData(sheetId, zone) {
|
|
49335
49855
|
return this.getters.isEmpty(sheetId, zone) || this.getters.isSingleCellOrMerge(sheetId, zone);
|
|
@@ -50904,8 +51424,6 @@ class SheetUIPlugin extends UIPlugin {
|
|
|
50904
51424
|
static getters = [
|
|
50905
51425
|
"doesCellHaveGridIcon",
|
|
50906
51426
|
"getCellWidth",
|
|
50907
|
-
"getCellComputedBorder",
|
|
50908
|
-
"getCellComputedStyle",
|
|
50909
51427
|
"getTextWidth",
|
|
50910
51428
|
"getCellText",
|
|
50911
51429
|
"getCellMultiLineText",
|
|
@@ -50949,7 +51467,7 @@ class SheetUIPlugin extends UIPlugin {
|
|
|
50949
51467
|
// Getters
|
|
50950
51468
|
// ---------------------------------------------------------------------------
|
|
50951
51469
|
getCellWidth(position) {
|
|
50952
|
-
const style = this.getCellComputedStyle(position);
|
|
51470
|
+
const style = this.getters.getCellComputedStyle(position);
|
|
50953
51471
|
let contentWidth = 0;
|
|
50954
51472
|
const content = this.getters.getEvaluatedCell(position).formattedValue;
|
|
50955
51473
|
if (content) {
|
|
@@ -51042,35 +51560,12 @@ class SheetUIPlugin extends UIPlugin {
|
|
|
51042
51560
|
*/
|
|
51043
51561
|
isCellEmpty(position) {
|
|
51044
51562
|
const mainPosition = this.getters.getMainCellPosition(position);
|
|
51045
|
-
return
|
|
51046
|
-
this.getters.getCell(mainPosition)?.content);
|
|
51047
|
-
}
|
|
51048
|
-
getCellComputedBorder(position) {
|
|
51049
|
-
const cellBorder = this.getters.getCellBorder(position) || {};
|
|
51050
|
-
const cellTableBorder = this.getters.getCellTableBorder(position) || {};
|
|
51051
|
-
// Use removeFalsyAttributes to avoid overwriting borders with undefined values
|
|
51052
|
-
const border = { ...cellTableBorder, ...removeFalsyAttributes(cellBorder) };
|
|
51053
|
-
return isObjectEmptyRecursive(border) ? null : border;
|
|
51054
|
-
}
|
|
51055
|
-
getCellComputedStyle(position) {
|
|
51056
|
-
const cell = this.getters.getCell(position);
|
|
51057
|
-
const cfStyle = this.getters.getCellConditionalFormatStyle(position);
|
|
51058
|
-
const tableStyle = this.getters.getCellTableStyle(position);
|
|
51059
|
-
const computedStyle = {
|
|
51060
|
-
...removeFalsyAttributes(tableStyle),
|
|
51061
|
-
...removeFalsyAttributes(cell?.style),
|
|
51062
|
-
...removeFalsyAttributes(cfStyle),
|
|
51063
|
-
};
|
|
51064
|
-
const evaluatedCell = this.getters.getEvaluatedCell(position);
|
|
51065
|
-
if (evaluatedCell.link && !computedStyle.textColor) {
|
|
51066
|
-
computedStyle.textColor = LINK_COLOR;
|
|
51067
|
-
}
|
|
51068
|
-
return computedStyle;
|
|
51563
|
+
return this.getters.getEvaluatedCell(mainPosition).type === CellValueType.empty;
|
|
51069
51564
|
}
|
|
51070
51565
|
getColMaxWidth(sheetId, index) {
|
|
51071
51566
|
const cellsPositions = positions(this.getters.getColsZone(sheetId, index, index));
|
|
51072
51567
|
const sizes = cellsPositions.map((position) => this.getCellWidth({ sheetId, ...position }));
|
|
51073
|
-
return Math.max(0,
|
|
51568
|
+
return Math.max(0, largeMax(sizes));
|
|
51074
51569
|
}
|
|
51075
51570
|
/**
|
|
51076
51571
|
* Check that any "sheetId" in the command matches an existing
|
|
@@ -51099,6 +51594,241 @@ class SheetUIPlugin extends UIPlugin {
|
|
|
51099
51594
|
}
|
|
51100
51595
|
}
|
|
51101
51596
|
|
|
51597
|
+
class TableStylePlugin extends UIPlugin {
|
|
51598
|
+
static getters = ["getCellTableStyle", "getCellTableBorder"];
|
|
51599
|
+
tableStyles = {};
|
|
51600
|
+
handle(cmd) {
|
|
51601
|
+
if (invalidateEvaluationCommands.has(cmd.type) ||
|
|
51602
|
+
(cmd.type === "UPDATE_CELL" && "content" in cmd) ||
|
|
51603
|
+
cmd.type === "EVALUATE_CELLS") {
|
|
51604
|
+
this.tableStyles = {};
|
|
51605
|
+
return;
|
|
51606
|
+
}
|
|
51607
|
+
if (doesCommandInvalidatesTableStyle(cmd)) {
|
|
51608
|
+
delete this.tableStyles[cmd.sheetId];
|
|
51609
|
+
return;
|
|
51610
|
+
}
|
|
51611
|
+
}
|
|
51612
|
+
finalize() {
|
|
51613
|
+
for (const sheetId of this.getters.getSheetIds()) {
|
|
51614
|
+
if (!this.tableStyles[sheetId]) {
|
|
51615
|
+
this.tableStyles[sheetId] = {};
|
|
51616
|
+
}
|
|
51617
|
+
for (const table of this.getters.getTables(sheetId)) {
|
|
51618
|
+
if (!this.tableStyles[sheetId][table.id]) {
|
|
51619
|
+
this.tableStyles[sheetId][table.id] = this.computeTableStyle(sheetId, table);
|
|
51620
|
+
}
|
|
51621
|
+
}
|
|
51622
|
+
}
|
|
51623
|
+
}
|
|
51624
|
+
getCellTableStyle(position) {
|
|
51625
|
+
const table = this.getters.getTable(position);
|
|
51626
|
+
if (!table) {
|
|
51627
|
+
return undefined;
|
|
51628
|
+
}
|
|
51629
|
+
return this.tableStyles[position.sheetId][table.id]().styles[position.col]?.[position.row];
|
|
51630
|
+
}
|
|
51631
|
+
getCellTableBorder(position) {
|
|
51632
|
+
const table = this.getters.getTable(position);
|
|
51633
|
+
if (!table) {
|
|
51634
|
+
return undefined;
|
|
51635
|
+
}
|
|
51636
|
+
return this.tableStyles[position.sheetId][table.id]().borders[position.col]?.[position.row];
|
|
51637
|
+
}
|
|
51638
|
+
computeTableStyle(sheetId, table) {
|
|
51639
|
+
return lazy(() => {
|
|
51640
|
+
const { config, numberOfCols, numberOfRows } = this.getTableRuntimeConfig(sheetId, table);
|
|
51641
|
+
const relativeTableStyle = getComputedTableStyle(config, numberOfCols, numberOfRows);
|
|
51642
|
+
// Return the style with sheet coordinates instead of tables coordinates
|
|
51643
|
+
const mapping = this.getTableMapping(sheetId, table);
|
|
51644
|
+
const absoluteTableStyle = { borders: {}, styles: {} };
|
|
51645
|
+
for (let col = 0; col < numberOfCols; col++) {
|
|
51646
|
+
const colInSheet = mapping.colMapping[col];
|
|
51647
|
+
absoluteTableStyle.borders[colInSheet] = {};
|
|
51648
|
+
absoluteTableStyle.styles[colInSheet] = {};
|
|
51649
|
+
for (let row = 0; row < numberOfRows; row++) {
|
|
51650
|
+
const rowInSheet = mapping.rowMapping[row];
|
|
51651
|
+
absoluteTableStyle.borders[colInSheet][rowInSheet] = relativeTableStyle.borders[col][row];
|
|
51652
|
+
absoluteTableStyle.styles[colInSheet][rowInSheet] = relativeTableStyle.styles[col][row];
|
|
51653
|
+
}
|
|
51654
|
+
}
|
|
51655
|
+
return absoluteTableStyle;
|
|
51656
|
+
});
|
|
51657
|
+
}
|
|
51658
|
+
/**
|
|
51659
|
+
* Get the actual table config that will be used to compute the table style. It is different from
|
|
51660
|
+
* the config of the table because of hidden rows and columns in the sheet. For example remove the
|
|
51661
|
+
* hidden rows from config.numberOfHeaders.
|
|
51662
|
+
*/
|
|
51663
|
+
getTableRuntimeConfig(sheetId, table) {
|
|
51664
|
+
const tableZone = table.range.zone;
|
|
51665
|
+
const config = { ...table.config };
|
|
51666
|
+
let numberOfCols = tableZone.right - tableZone.left + 1;
|
|
51667
|
+
let numberOfRows = tableZone.bottom - tableZone.top + 1;
|
|
51668
|
+
for (let row = tableZone.top; row <= tableZone.bottom; row++) {
|
|
51669
|
+
if (!this.getters.isRowHidden(sheetId, row)) {
|
|
51670
|
+
continue;
|
|
51671
|
+
}
|
|
51672
|
+
numberOfRows--;
|
|
51673
|
+
if (row - tableZone.top < table.config.numberOfHeaders) {
|
|
51674
|
+
config.numberOfHeaders--;
|
|
51675
|
+
if (config.numberOfHeaders < 0) {
|
|
51676
|
+
config.numberOfHeaders = 0;
|
|
51677
|
+
}
|
|
51678
|
+
}
|
|
51679
|
+
if (row === tableZone.bottom) {
|
|
51680
|
+
config.totalRow = false;
|
|
51681
|
+
}
|
|
51682
|
+
}
|
|
51683
|
+
for (let col = tableZone.left; col <= tableZone.right; col++) {
|
|
51684
|
+
if (!this.getters.isColHidden(sheetId, col)) {
|
|
51685
|
+
continue;
|
|
51686
|
+
}
|
|
51687
|
+
numberOfCols--;
|
|
51688
|
+
if (col === tableZone.left) {
|
|
51689
|
+
config.firstColumn = false;
|
|
51690
|
+
}
|
|
51691
|
+
if (col === tableZone.right) {
|
|
51692
|
+
config.lastColumn = false;
|
|
51693
|
+
}
|
|
51694
|
+
}
|
|
51695
|
+
return {
|
|
51696
|
+
config,
|
|
51697
|
+
numberOfCols,
|
|
51698
|
+
numberOfRows,
|
|
51699
|
+
};
|
|
51700
|
+
}
|
|
51701
|
+
/**
|
|
51702
|
+
* Get a mapping: relative col/row position in the table <=> col/row in the sheet
|
|
51703
|
+
*/
|
|
51704
|
+
getTableMapping(sheetId, table) {
|
|
51705
|
+
const colMapping = {};
|
|
51706
|
+
const rowMapping = {};
|
|
51707
|
+
let colOffset = 0;
|
|
51708
|
+
let rowOffset = 0;
|
|
51709
|
+
const tableZone = table.range.zone;
|
|
51710
|
+
for (let col = tableZone.left; col <= tableZone.right; col++) {
|
|
51711
|
+
if (this.getters.isColHidden(sheetId, col)) {
|
|
51712
|
+
continue;
|
|
51713
|
+
}
|
|
51714
|
+
colMapping[colOffset] = col;
|
|
51715
|
+
colOffset++;
|
|
51716
|
+
for (let row = tableZone.top; row <= tableZone.bottom; row++) {
|
|
51717
|
+
if (this.getters.isRowHidden(sheetId, row)) {
|
|
51718
|
+
continue;
|
|
51719
|
+
}
|
|
51720
|
+
rowMapping[rowOffset] = row;
|
|
51721
|
+
rowOffset++;
|
|
51722
|
+
}
|
|
51723
|
+
}
|
|
51724
|
+
return {
|
|
51725
|
+
colMapping,
|
|
51726
|
+
rowMapping,
|
|
51727
|
+
};
|
|
51728
|
+
}
|
|
51729
|
+
}
|
|
51730
|
+
const invalidateTableStyleCommands = [
|
|
51731
|
+
"HIDE_COLUMNS_ROWS",
|
|
51732
|
+
"UNHIDE_COLUMNS_ROWS",
|
|
51733
|
+
"UNFOLD_HEADER_GROUP",
|
|
51734
|
+
"FOLD_HEADER_GROUP",
|
|
51735
|
+
"FOLD_ALL_HEADER_GROUPS",
|
|
51736
|
+
"UNFOLD_ALL_HEADER_GROUPS",
|
|
51737
|
+
"CREATE_TABLE",
|
|
51738
|
+
"UPDATE_TABLE",
|
|
51739
|
+
"UPDATE_FILTER",
|
|
51740
|
+
"REMOVE_TABLE",
|
|
51741
|
+
];
|
|
51742
|
+
const invalidateTableStyleCommandsSet = new Set(invalidateTableStyleCommands);
|
|
51743
|
+
function doesCommandInvalidatesTableStyle(cmd) {
|
|
51744
|
+
return invalidateTableStyleCommandsSet.has(cmd.type);
|
|
51745
|
+
}
|
|
51746
|
+
|
|
51747
|
+
class CellComputedStylePlugin extends UIPlugin {
|
|
51748
|
+
static getters = ["getCellComputedBorder", "getCellComputedStyle"];
|
|
51749
|
+
styles = {};
|
|
51750
|
+
borders = {};
|
|
51751
|
+
handle(cmd) {
|
|
51752
|
+
if (invalidateEvaluationCommands.has(cmd.type) ||
|
|
51753
|
+
cmd.type === "UPDATE_CELL" ||
|
|
51754
|
+
cmd.type === "EVALUATE_CELLS") {
|
|
51755
|
+
this.styles = {};
|
|
51756
|
+
this.borders = {};
|
|
51757
|
+
return;
|
|
51758
|
+
}
|
|
51759
|
+
if (doesCommandInvalidatesTableStyle(cmd)) {
|
|
51760
|
+
delete this.styles[cmd.sheetId];
|
|
51761
|
+
delete this.borders[cmd.sheetId];
|
|
51762
|
+
return;
|
|
51763
|
+
}
|
|
51764
|
+
if (invalidateCFEvaluationCommands.has(cmd.type)) {
|
|
51765
|
+
this.styles = {};
|
|
51766
|
+
return;
|
|
51767
|
+
}
|
|
51768
|
+
if (invalidateBordersCommands.has(cmd.type)) {
|
|
51769
|
+
this.borders = {};
|
|
51770
|
+
return;
|
|
51771
|
+
}
|
|
51772
|
+
}
|
|
51773
|
+
getCellComputedBorder(position) {
|
|
51774
|
+
const { sheetId, row, col } = position;
|
|
51775
|
+
if (this.borders[sheetId]?.[row]?.[col] !== undefined) {
|
|
51776
|
+
return this.borders[sheetId][row][col];
|
|
51777
|
+
}
|
|
51778
|
+
if (!this.borders[sheetId]) {
|
|
51779
|
+
this.borders[sheetId] = {};
|
|
51780
|
+
}
|
|
51781
|
+
if (!this.borders[sheetId][row]) {
|
|
51782
|
+
this.borders[sheetId][row] = {};
|
|
51783
|
+
}
|
|
51784
|
+
if (!this.borders[sheetId][row][col]) {
|
|
51785
|
+
this.borders[sheetId][row][col] = this.computeCellBorder(position);
|
|
51786
|
+
}
|
|
51787
|
+
return this.borders[sheetId][row][col];
|
|
51788
|
+
}
|
|
51789
|
+
getCellComputedStyle(position) {
|
|
51790
|
+
const { sheetId, row, col } = position;
|
|
51791
|
+
if (this.styles[sheetId]?.[row]?.[col] !== undefined) {
|
|
51792
|
+
return this.styles[sheetId][row][col];
|
|
51793
|
+
}
|
|
51794
|
+
if (!this.styles[sheetId]) {
|
|
51795
|
+
this.styles[sheetId] = {};
|
|
51796
|
+
}
|
|
51797
|
+
if (!this.styles[sheetId][row]) {
|
|
51798
|
+
this.styles[sheetId][row] = {};
|
|
51799
|
+
}
|
|
51800
|
+
if (!this.styles[sheetId][row][col]) {
|
|
51801
|
+
this.styles[sheetId][row][col] = this.computeCellStyle(position);
|
|
51802
|
+
}
|
|
51803
|
+
return this.styles[sheetId][row][col];
|
|
51804
|
+
}
|
|
51805
|
+
computeCellBorder(position) {
|
|
51806
|
+
const cellBorder = this.getters.getCellBorder(position) || {};
|
|
51807
|
+
const cellTableBorder = this.getters.getCellTableBorder(position) || {};
|
|
51808
|
+
// Use removeFalsyAttributes to avoid overwriting borders with undefined values
|
|
51809
|
+
const border = {
|
|
51810
|
+
...removeFalsyAttributes(cellTableBorder),
|
|
51811
|
+
...removeFalsyAttributes(cellBorder),
|
|
51812
|
+
};
|
|
51813
|
+
return isObjectEmptyRecursive(border) ? null : border;
|
|
51814
|
+
}
|
|
51815
|
+
computeCellStyle(position) {
|
|
51816
|
+
const cell = this.getters.getCell(position);
|
|
51817
|
+
const cfStyle = this.getters.getCellConditionalFormatStyle(position);
|
|
51818
|
+
const tableStyle = this.getters.getCellTableStyle(position);
|
|
51819
|
+
const computedStyle = {
|
|
51820
|
+
...removeFalsyAttributes(tableStyle),
|
|
51821
|
+
...removeFalsyAttributes(cell?.style),
|
|
51822
|
+
...removeFalsyAttributes(cfStyle),
|
|
51823
|
+
};
|
|
51824
|
+
const evaluatedCell = this.getters.getEvaluatedCell(position);
|
|
51825
|
+
if (evaluatedCell.link && !computedStyle.textColor) {
|
|
51826
|
+
computedStyle.textColor = LINK_COLOR;
|
|
51827
|
+
}
|
|
51828
|
+
return computedStyle;
|
|
51829
|
+
}
|
|
51830
|
+
}
|
|
51831
|
+
|
|
51102
51832
|
const genericRepeatsTransforms = [
|
|
51103
51833
|
repeatSheetDependantCommand,
|
|
51104
51834
|
repeatTargetDependantCommand,
|
|
@@ -51676,148 +52406,6 @@ class TableAutofillPlugin extends UIPlugin {
|
|
|
51676
52406
|
}
|
|
51677
52407
|
}
|
|
51678
52408
|
|
|
51679
|
-
class TableStylePlugin extends UIPlugin {
|
|
51680
|
-
static getters = ["getCellTableStyle", "getCellTableBorder"];
|
|
51681
|
-
tableStyles = {};
|
|
51682
|
-
handle(cmd) {
|
|
51683
|
-
if (invalidateEvaluationCommands.has(cmd.type) ||
|
|
51684
|
-
(cmd.type === "UPDATE_CELL" && "content" in cmd) ||
|
|
51685
|
-
cmd.type === "EVALUATE_CELLS") {
|
|
51686
|
-
this.tableStyles = {};
|
|
51687
|
-
return;
|
|
51688
|
-
}
|
|
51689
|
-
switch (cmd.type) {
|
|
51690
|
-
case "HIDE_COLUMNS_ROWS":
|
|
51691
|
-
case "UNHIDE_COLUMNS_ROWS":
|
|
51692
|
-
case "UNFOLD_HEADER_GROUP":
|
|
51693
|
-
case "FOLD_HEADER_GROUP":
|
|
51694
|
-
case "FOLD_ALL_HEADER_GROUPS":
|
|
51695
|
-
case "UNFOLD_ALL_HEADER_GROUPS":
|
|
51696
|
-
case "UPDATE_TABLE":
|
|
51697
|
-
case "UPDATE_FILTER":
|
|
51698
|
-
delete this.tableStyles[cmd.sheetId];
|
|
51699
|
-
break;
|
|
51700
|
-
}
|
|
51701
|
-
}
|
|
51702
|
-
finalize() {
|
|
51703
|
-
for (const sheetId of this.getters.getSheetIds()) {
|
|
51704
|
-
if (!this.tableStyles[sheetId]) {
|
|
51705
|
-
this.tableStyles[sheetId] = {};
|
|
51706
|
-
}
|
|
51707
|
-
for (const table of this.getters.getTables(sheetId)) {
|
|
51708
|
-
if (!this.tableStyles[sheetId][table.id]) {
|
|
51709
|
-
this.tableStyles[sheetId][table.id] = this.computeTableStyle(sheetId, table);
|
|
51710
|
-
}
|
|
51711
|
-
}
|
|
51712
|
-
}
|
|
51713
|
-
}
|
|
51714
|
-
getCellTableStyle(position) {
|
|
51715
|
-
const table = this.getters.getTable(position);
|
|
51716
|
-
if (!table) {
|
|
51717
|
-
return undefined;
|
|
51718
|
-
}
|
|
51719
|
-
return this.tableStyles[position.sheetId][table.id]().styles[position.col]?.[position.row];
|
|
51720
|
-
}
|
|
51721
|
-
getCellTableBorder(position) {
|
|
51722
|
-
const table = this.getters.getTable(position);
|
|
51723
|
-
if (!table) {
|
|
51724
|
-
return undefined;
|
|
51725
|
-
}
|
|
51726
|
-
return this.tableStyles[position.sheetId][table.id]().borders[position.col]?.[position.row];
|
|
51727
|
-
}
|
|
51728
|
-
computeTableStyle(sheetId, table) {
|
|
51729
|
-
return lazy(() => {
|
|
51730
|
-
const { config, numberOfCols, numberOfRows } = this.getTableRuntimeConfig(sheetId, table);
|
|
51731
|
-
const relativeTableStyle = getComputedTableStyle(config, numberOfCols, numberOfRows);
|
|
51732
|
-
// Return the style with sheet coordinates instead of tables coordinates
|
|
51733
|
-
const mapping = this.getTableMapping(sheetId, table);
|
|
51734
|
-
const absoluteTableStyle = { borders: {}, styles: {} };
|
|
51735
|
-
for (let col = 0; col < numberOfCols; col++) {
|
|
51736
|
-
const colInSheet = mapping.colMapping[col];
|
|
51737
|
-
absoluteTableStyle.borders[colInSheet] = {};
|
|
51738
|
-
absoluteTableStyle.styles[colInSheet] = {};
|
|
51739
|
-
for (let row = 0; row < numberOfRows; row++) {
|
|
51740
|
-
const rowInSheet = mapping.rowMapping[row];
|
|
51741
|
-
absoluteTableStyle.borders[colInSheet][rowInSheet] = relativeTableStyle.borders[col][row];
|
|
51742
|
-
absoluteTableStyle.styles[colInSheet][rowInSheet] = relativeTableStyle.styles[col][row];
|
|
51743
|
-
}
|
|
51744
|
-
}
|
|
51745
|
-
return absoluteTableStyle;
|
|
51746
|
-
});
|
|
51747
|
-
}
|
|
51748
|
-
/**
|
|
51749
|
-
* Get the actual table config that will be used to compute the table style. It is different from
|
|
51750
|
-
* the config of the table because of hidden rows and columns in the sheet. For example remove the
|
|
51751
|
-
* hidden rows from config.numberOfHeaders.
|
|
51752
|
-
*/
|
|
51753
|
-
getTableRuntimeConfig(sheetId, table) {
|
|
51754
|
-
const tableZone = table.range.zone;
|
|
51755
|
-
const config = { ...table.config };
|
|
51756
|
-
let numberOfCols = tableZone.right - tableZone.left + 1;
|
|
51757
|
-
let numberOfRows = tableZone.bottom - tableZone.top + 1;
|
|
51758
|
-
for (let row = tableZone.top; row <= tableZone.bottom; row++) {
|
|
51759
|
-
if (!this.getters.isRowHidden(sheetId, row)) {
|
|
51760
|
-
continue;
|
|
51761
|
-
}
|
|
51762
|
-
numberOfRows--;
|
|
51763
|
-
if (row - tableZone.top < table.config.numberOfHeaders) {
|
|
51764
|
-
config.numberOfHeaders--;
|
|
51765
|
-
if (config.numberOfHeaders < 0) {
|
|
51766
|
-
config.numberOfHeaders = 0;
|
|
51767
|
-
}
|
|
51768
|
-
}
|
|
51769
|
-
if (row === tableZone.bottom) {
|
|
51770
|
-
config.totalRow = false;
|
|
51771
|
-
}
|
|
51772
|
-
}
|
|
51773
|
-
for (let col = tableZone.left; col <= tableZone.right; col++) {
|
|
51774
|
-
if (!this.getters.isColHidden(sheetId, col)) {
|
|
51775
|
-
continue;
|
|
51776
|
-
}
|
|
51777
|
-
numberOfCols--;
|
|
51778
|
-
if (col === tableZone.left) {
|
|
51779
|
-
config.firstColumn = false;
|
|
51780
|
-
}
|
|
51781
|
-
if (col === tableZone.right) {
|
|
51782
|
-
config.lastColumn = false;
|
|
51783
|
-
}
|
|
51784
|
-
}
|
|
51785
|
-
return {
|
|
51786
|
-
config,
|
|
51787
|
-
numberOfCols,
|
|
51788
|
-
numberOfRows,
|
|
51789
|
-
};
|
|
51790
|
-
}
|
|
51791
|
-
/**
|
|
51792
|
-
* Get a mapping: relative col/row position in the table <=> col/row in the sheet
|
|
51793
|
-
*/
|
|
51794
|
-
getTableMapping(sheetId, table) {
|
|
51795
|
-
const colMapping = {};
|
|
51796
|
-
const rowMapping = {};
|
|
51797
|
-
let colOffset = 0;
|
|
51798
|
-
let rowOffset = 0;
|
|
51799
|
-
const tableZone = table.range.zone;
|
|
51800
|
-
for (let col = tableZone.left; col <= tableZone.right; col++) {
|
|
51801
|
-
if (this.getters.isColHidden(sheetId, col)) {
|
|
51802
|
-
continue;
|
|
51803
|
-
}
|
|
51804
|
-
colMapping[colOffset] = col;
|
|
51805
|
-
colOffset++;
|
|
51806
|
-
for (let row = tableZone.top; row <= tableZone.bottom; row++) {
|
|
51807
|
-
if (this.getters.isRowHidden(sheetId, row)) {
|
|
51808
|
-
continue;
|
|
51809
|
-
}
|
|
51810
|
-
rowMapping[rowOffset] = row;
|
|
51811
|
-
rowOffset++;
|
|
51812
|
-
}
|
|
51813
|
-
}
|
|
51814
|
-
return {
|
|
51815
|
-
colMapping,
|
|
51816
|
-
rowMapping,
|
|
51817
|
-
};
|
|
51818
|
-
}
|
|
51819
|
-
}
|
|
51820
|
-
|
|
51821
52409
|
/**
|
|
51822
52410
|
* Clipboard Plugin
|
|
51823
52411
|
*
|
|
@@ -52527,38 +53115,6 @@ class FilterEvaluationPlugin extends UIPlugin {
|
|
|
52527
53115
|
}
|
|
52528
53116
|
}
|
|
52529
53117
|
|
|
52530
|
-
const selectionStatisticFunctions = [
|
|
52531
|
-
{
|
|
52532
|
-
name: _t("Sum"),
|
|
52533
|
-
types: [CellValueType.number],
|
|
52534
|
-
compute: (values, locale) => sum([[values]], locale),
|
|
52535
|
-
},
|
|
52536
|
-
{
|
|
52537
|
-
name: _t("Avg"),
|
|
52538
|
-
types: [CellValueType.number],
|
|
52539
|
-
compute: (values, locale) => average([[values]], locale),
|
|
52540
|
-
},
|
|
52541
|
-
{
|
|
52542
|
-
name: _t("Min"),
|
|
52543
|
-
types: [CellValueType.number],
|
|
52544
|
-
compute: (values, locale) => min([[values]], locale),
|
|
52545
|
-
},
|
|
52546
|
-
{
|
|
52547
|
-
name: _t("Max"),
|
|
52548
|
-
types: [CellValueType.number],
|
|
52549
|
-
compute: (values, locale) => max([[values]], locale),
|
|
52550
|
-
},
|
|
52551
|
-
{
|
|
52552
|
-
name: _t("Count"),
|
|
52553
|
-
types: [CellValueType.number, CellValueType.text, CellValueType.boolean, CellValueType.error],
|
|
52554
|
-
compute: (values) => countAny([[values]]),
|
|
52555
|
-
},
|
|
52556
|
-
{
|
|
52557
|
-
name: _t("Count Numbers"),
|
|
52558
|
-
types: [CellValueType.number, CellValueType.text, CellValueType.boolean, CellValueType.error],
|
|
52559
|
-
compute: (values, locale) => countNumbers([[values]], locale),
|
|
52560
|
-
},
|
|
52561
|
-
];
|
|
52562
53118
|
/**
|
|
52563
53119
|
* SelectionPlugin
|
|
52564
53120
|
*/
|
|
@@ -52574,8 +53130,6 @@ class GridSelectionPlugin extends UIPlugin {
|
|
|
52574
53130
|
"getSelectedZones",
|
|
52575
53131
|
"getSelectedZone",
|
|
52576
53132
|
"getSelectedCells",
|
|
52577
|
-
"getStatisticFnResults",
|
|
52578
|
-
"getAggregate",
|
|
52579
53133
|
"getSelectedFigureId",
|
|
52580
53134
|
"getSelection",
|
|
52581
53135
|
"getActivePosition",
|
|
@@ -52610,7 +53164,10 @@ class GridSelectionPlugin extends UIPlugin {
|
|
|
52610
53164
|
switch (cmd.type) {
|
|
52611
53165
|
case "ACTIVATE_SHEET":
|
|
52612
53166
|
try {
|
|
52613
|
-
this.getters.getSheet(cmd.sheetIdTo);
|
|
53167
|
+
const sheet = this.getters.getSheet(cmd.sheetIdTo);
|
|
53168
|
+
if (!sheet.isVisible) {
|
|
53169
|
+
return "SheetIsHidden" /* CommandResult.SheetIsHidden */;
|
|
53170
|
+
}
|
|
52614
53171
|
break;
|
|
52615
53172
|
}
|
|
52616
53173
|
catch (error) {
|
|
@@ -52859,52 +53416,6 @@ class GridSelectionPlugin extends UIPlugin {
|
|
|
52859
53416
|
: this.getters.getNextVisibleCellPosition({ sheetId, col: 0, row: 0 });
|
|
52860
53417
|
}
|
|
52861
53418
|
}
|
|
52862
|
-
getStatisticFnResults() {
|
|
52863
|
-
const sheetId = this.getters.getActiveSheetId();
|
|
52864
|
-
const cells = new Set();
|
|
52865
|
-
for (const zone of this.gridSelection.zones) {
|
|
52866
|
-
for (const { col, row } of positions(zone)) {
|
|
52867
|
-
if (this.getters.isRowHidden(sheetId, row) || this.getters.isColHidden(sheetId, col)) {
|
|
52868
|
-
continue; // Skip hidden cells
|
|
52869
|
-
}
|
|
52870
|
-
const evaluatedCell = this.getters.getEvaluatedCell({ sheetId, col, row });
|
|
52871
|
-
if (evaluatedCell.type !== CellValueType.empty) {
|
|
52872
|
-
cells.add(evaluatedCell);
|
|
52873
|
-
}
|
|
52874
|
-
}
|
|
52875
|
-
}
|
|
52876
|
-
const locale = this.getters.getLocale();
|
|
52877
|
-
let statisticFnResults = {};
|
|
52878
|
-
for (let fn of selectionStatisticFunctions) {
|
|
52879
|
-
// We don't want to display statistical information when there is no interest:
|
|
52880
|
-
// We set the statistical result to undefined if the data handled by the selection
|
|
52881
|
-
// does not match the data handled by the function.
|
|
52882
|
-
// Ex: if there are only texts in the selection, we prefer that the SUM result
|
|
52883
|
-
// be displayed as undefined rather than 0.
|
|
52884
|
-
let fnResult = undefined;
|
|
52885
|
-
const evaluatedCells = [...cells].filter((c) => fn.types.includes(c.type));
|
|
52886
|
-
if (evaluatedCells.length) {
|
|
52887
|
-
fnResult = fn.compute(evaluatedCells, locale);
|
|
52888
|
-
}
|
|
52889
|
-
statisticFnResults[fn.name] = fnResult;
|
|
52890
|
-
}
|
|
52891
|
-
return statisticFnResults;
|
|
52892
|
-
}
|
|
52893
|
-
getAggregate() {
|
|
52894
|
-
let aggregate = 0;
|
|
52895
|
-
let n = 0;
|
|
52896
|
-
const sheetId = this.getters.getActiveSheetId();
|
|
52897
|
-
const cellPositions = this.gridSelection.zones.map(positions).flat();
|
|
52898
|
-
for (const { col, row } of cellPositions) {
|
|
52899
|
-
const cell = this.getters.getEvaluatedCell({ sheetId, col, row });
|
|
52900
|
-
if (cell.type === CellValueType.number) {
|
|
52901
|
-
n++;
|
|
52902
|
-
aggregate += cell.value;
|
|
52903
|
-
}
|
|
52904
|
-
}
|
|
52905
|
-
const locale = this.getters.getLocale();
|
|
52906
|
-
return n < 2 ? null : formatValue(aggregate, { locale });
|
|
52907
|
-
}
|
|
52908
53419
|
isSelected(zone) {
|
|
52909
53420
|
return !!this.getters.getSelectedZones().find((z) => isEqual(z, zone));
|
|
52910
53421
|
}
|
|
@@ -52946,9 +53457,6 @@ class GridSelectionPlugin extends UIPlugin {
|
|
|
52946
53457
|
// Other
|
|
52947
53458
|
// ---------------------------------------------------------------------------
|
|
52948
53459
|
activateSheet(sheetIdFrom, sheetIdTo) {
|
|
52949
|
-
if (!this.getters.isSheetVisible(sheetIdTo)) {
|
|
52950
|
-
this.dispatch("SHOW_SHEET", { sheetId: sheetIdTo });
|
|
52951
|
-
}
|
|
52952
53460
|
this.setActiveSheet(sheetIdTo);
|
|
52953
53461
|
this.sheetsData[sheetIdFrom] = {
|
|
52954
53462
|
gridSelection: deepCopy(this.gridSelection),
|
|
@@ -53955,7 +54463,7 @@ class SheetViewPlugin extends UIPlugin {
|
|
|
53955
54463
|
* column of the current viewport
|
|
53956
54464
|
*/
|
|
53957
54465
|
getColDimensionsInViewport(sheetId, col) {
|
|
53958
|
-
const left =
|
|
54466
|
+
const left = largeMin(this.getters.getSheetViewVisibleCols());
|
|
53959
54467
|
const start = this.getters.getColRowOffsetInViewport("COL", left, col);
|
|
53960
54468
|
const size = this.getters.getColSize(sheetId, col);
|
|
53961
54469
|
const isColHidden = this.getters.isColHidden(sheetId, col);
|
|
@@ -53970,7 +54478,7 @@ class SheetViewPlugin extends UIPlugin {
|
|
|
53970
54478
|
* of the current viewport
|
|
53971
54479
|
*/
|
|
53972
54480
|
getRowDimensionsInViewport(sheetId, row) {
|
|
53973
|
-
const top =
|
|
54481
|
+
const top = largeMin(this.getters.getSheetViewVisibleRows());
|
|
53974
54482
|
const start = this.getters.getColRowOffsetInViewport("ROW", top, row);
|
|
53975
54483
|
const size = this.getters.getRowSize(sheetId, row);
|
|
53976
54484
|
const isRowHidden = this.getters.isRowHidden(sheetId, row);
|
|
@@ -54322,6 +54830,7 @@ const statefulUIPluginRegistry = new Registry()
|
|
|
54322
54830
|
.add("evaluation_filter", FilterEvaluationPlugin)
|
|
54323
54831
|
.add("header_visibility_ui", HeaderVisibilityUIPlugin)
|
|
54324
54832
|
.add("table_style", TableStylePlugin)
|
|
54833
|
+
.add("cell_computed_style", CellComputedStylePlugin)
|
|
54325
54834
|
.add("header_positions", HeaderPositionsUIPlugin)
|
|
54326
54835
|
.add("viewport", SheetViewPlugin)
|
|
54327
54836
|
.add("clipboard", ClipboardPlugin);
|
|
@@ -54382,6 +54891,38 @@ class ImageProvider {
|
|
|
54382
54891
|
}
|
|
54383
54892
|
}
|
|
54384
54893
|
|
|
54894
|
+
class ArrayFormulaHighlight extends SpreadsheetStore {
|
|
54895
|
+
highlightStore = this.get(HighlightStore);
|
|
54896
|
+
constructor(get) {
|
|
54897
|
+
super(get);
|
|
54898
|
+
this.highlightStore.register(this);
|
|
54899
|
+
}
|
|
54900
|
+
get highlights() {
|
|
54901
|
+
const zone = this.getHighlightZone();
|
|
54902
|
+
if (!zone) {
|
|
54903
|
+
return [];
|
|
54904
|
+
}
|
|
54905
|
+
const sheetId = this.model.getters.getActiveSheetId();
|
|
54906
|
+
return [
|
|
54907
|
+
{
|
|
54908
|
+
sheetId,
|
|
54909
|
+
zone,
|
|
54910
|
+
color: "#17A2B8",
|
|
54911
|
+
noFill: true,
|
|
54912
|
+
thinLine: true,
|
|
54913
|
+
},
|
|
54914
|
+
];
|
|
54915
|
+
}
|
|
54916
|
+
getHighlightZone() {
|
|
54917
|
+
const position = this.model.getters.getActivePosition();
|
|
54918
|
+
const spreader = this.model.getters.getArrayFormulaSpreadingOn(position);
|
|
54919
|
+
const spreadZone = spreader
|
|
54920
|
+
? this.model.getters.getSpreadZone(spreader)
|
|
54921
|
+
: this.model.getters.getSpreadZone(position);
|
|
54922
|
+
return spreadZone;
|
|
54923
|
+
}
|
|
54924
|
+
}
|
|
54925
|
+
|
|
54385
54926
|
const RIPPLE_KEY_FRAMES = [
|
|
54386
54927
|
{ transform: "scale(0)" },
|
|
54387
54928
|
{ transform: "scale(0.8)", offset: 0.33 },
|
|
@@ -54684,12 +55225,14 @@ class BottomBarSheet extends owl.Component {
|
|
|
54684
55225
|
this.editionState = "initializing";
|
|
54685
55226
|
}
|
|
54686
55227
|
stopEdition() {
|
|
54687
|
-
|
|
55228
|
+
const input = this.sheetNameRef.el;
|
|
55229
|
+
if (!this.state.isEditing || !input)
|
|
54688
55230
|
return;
|
|
54689
55231
|
this.state.isEditing = false;
|
|
54690
55232
|
this.editionState = "initializing";
|
|
54691
|
-
|
|
55233
|
+
input.blur();
|
|
54692
55234
|
const inputValue = this.getInputContent() || "";
|
|
55235
|
+
input.innerText = inputValue;
|
|
54693
55236
|
interactiveRenameSheet(this.env, this.props.sheetId, inputValue, () => this.startEdition());
|
|
54694
55237
|
}
|
|
54695
55238
|
cancelEdition() {
|
|
@@ -54733,6 +55276,115 @@ class BottomBarSheet extends owl.Component {
|
|
|
54733
55276
|
}
|
|
54734
55277
|
}
|
|
54735
55278
|
|
|
55279
|
+
const selectionStatisticFunctions = [
|
|
55280
|
+
{
|
|
55281
|
+
name: _t("Sum"),
|
|
55282
|
+
types: [CellValueType.number],
|
|
55283
|
+
compute: (values, locale) => sum([[values]], locale),
|
|
55284
|
+
},
|
|
55285
|
+
{
|
|
55286
|
+
name: _t("Avg"),
|
|
55287
|
+
types: [CellValueType.number],
|
|
55288
|
+
compute: (values, locale) => average([[values]], locale),
|
|
55289
|
+
},
|
|
55290
|
+
{
|
|
55291
|
+
name: _t("Min"),
|
|
55292
|
+
types: [CellValueType.number],
|
|
55293
|
+
compute: (values, locale) => min([[values]], locale),
|
|
55294
|
+
},
|
|
55295
|
+
{
|
|
55296
|
+
name: _t("Max"),
|
|
55297
|
+
types: [CellValueType.number],
|
|
55298
|
+
compute: (values, locale) => max([[values]], locale),
|
|
55299
|
+
},
|
|
55300
|
+
{
|
|
55301
|
+
name: _t("Count"),
|
|
55302
|
+
types: [CellValueType.number, CellValueType.text, CellValueType.boolean, CellValueType.error],
|
|
55303
|
+
compute: (values) => countAny([[values]]),
|
|
55304
|
+
},
|
|
55305
|
+
{
|
|
55306
|
+
name: _t("Count Numbers"),
|
|
55307
|
+
types: [CellValueType.number, CellValueType.text, CellValueType.boolean, CellValueType.error],
|
|
55308
|
+
compute: (values, locale) => countNumbers([[values]], locale),
|
|
55309
|
+
},
|
|
55310
|
+
];
|
|
55311
|
+
class AggregateStatisticsStore extends SpreadsheetStore {
|
|
55312
|
+
statisticFnResults = this._computeStatisticFnResults();
|
|
55313
|
+
isDirty = false;
|
|
55314
|
+
constructor(get) {
|
|
55315
|
+
super(get);
|
|
55316
|
+
this.model.selection.observe(this, {
|
|
55317
|
+
handleEvent: this.handleEvent.bind(this),
|
|
55318
|
+
});
|
|
55319
|
+
}
|
|
55320
|
+
handle(cmd) {
|
|
55321
|
+
if (invalidateEvaluationCommands.has(cmd.type) ||
|
|
55322
|
+
(cmd.type === "UPDATE_CELL" && "content" in cmd)) {
|
|
55323
|
+
this.isDirty = true;
|
|
55324
|
+
}
|
|
55325
|
+
switch (cmd.type) {
|
|
55326
|
+
case "HIDE_COLUMNS_ROWS":
|
|
55327
|
+
case "UNHIDE_COLUMNS_ROWS":
|
|
55328
|
+
case "GROUP_HEADERS":
|
|
55329
|
+
case "UNGROUP_HEADERS":
|
|
55330
|
+
case "ACTIVATE_SHEET":
|
|
55331
|
+
case "ACTIVATE_NEXT_SHEET":
|
|
55332
|
+
case "ACTIVATE_PREVIOUS_SHEET":
|
|
55333
|
+
case "EVALUATE_CELLS":
|
|
55334
|
+
case "UNDO":
|
|
55335
|
+
case "REDO":
|
|
55336
|
+
this.isDirty = true;
|
|
55337
|
+
}
|
|
55338
|
+
}
|
|
55339
|
+
finalize() {
|
|
55340
|
+
if (this.isDirty) {
|
|
55341
|
+
this.isDirty = false;
|
|
55342
|
+
this.statisticFnResults = this._computeStatisticFnResults();
|
|
55343
|
+
}
|
|
55344
|
+
}
|
|
55345
|
+
handleEvent() {
|
|
55346
|
+
if (this.getters.isGridSelectionActive()) {
|
|
55347
|
+
this.statisticFnResults = this._computeStatisticFnResults();
|
|
55348
|
+
}
|
|
55349
|
+
}
|
|
55350
|
+
_computeStatisticFnResults() {
|
|
55351
|
+
const getters = this.getters;
|
|
55352
|
+
const sheetId = getters.getActiveSheetId();
|
|
55353
|
+
const cells = new Set();
|
|
55354
|
+
const zones = getters.getSelectedZones();
|
|
55355
|
+
for (const zone of zones) {
|
|
55356
|
+
for (let col = zone.left; col <= zone.right; col++) {
|
|
55357
|
+
for (let row = zone.top; row <= zone.bottom; row++) {
|
|
55358
|
+
if (getters.isRowHidden(sheetId, row) || getters.isColHidden(sheetId, col)) {
|
|
55359
|
+
continue; // Skip hidden cells
|
|
55360
|
+
}
|
|
55361
|
+
const evaluatedCell = getters.getEvaluatedCell({ sheetId, col, row });
|
|
55362
|
+
if (evaluatedCell.type !== CellValueType.empty) {
|
|
55363
|
+
cells.add(evaluatedCell);
|
|
55364
|
+
}
|
|
55365
|
+
}
|
|
55366
|
+
}
|
|
55367
|
+
}
|
|
55368
|
+
const locale = getters.getLocale();
|
|
55369
|
+
let statisticFnResults = {};
|
|
55370
|
+
const cellsArray = [...cells];
|
|
55371
|
+
for (let fn of selectionStatisticFunctions) {
|
|
55372
|
+
// We don't want to display statistical information when there is no interest:
|
|
55373
|
+
// We set the statistical result to undefined if the data handled by the selection
|
|
55374
|
+
// does not match the data handled by the function.
|
|
55375
|
+
// Ex: if there are only texts in the selection, we prefer that the SUM result
|
|
55376
|
+
// be displayed as undefined rather than 0.
|
|
55377
|
+
let fnResult = undefined;
|
|
55378
|
+
const evaluatedCells = cellsArray.filter((c) => fn.types.includes(c.type));
|
|
55379
|
+
if (evaluatedCells.length) {
|
|
55380
|
+
fnResult = fn.compute(evaluatedCells, locale);
|
|
55381
|
+
}
|
|
55382
|
+
statisticFnResults[fn.name] = fnResult;
|
|
55383
|
+
}
|
|
55384
|
+
return statisticFnResults;
|
|
55385
|
+
}
|
|
55386
|
+
}
|
|
55387
|
+
|
|
54736
55388
|
// -----------------------------------------------------------------------------
|
|
54737
55389
|
// SpreadSheet
|
|
54738
55390
|
// -----------------------------------------------------------------------------
|
|
@@ -54748,40 +55400,38 @@ css /* scss */ `
|
|
|
54748
55400
|
}
|
|
54749
55401
|
`;
|
|
54750
55402
|
class BottomBarStatistic extends owl.Component {
|
|
54751
|
-
static template = "o-spreadsheet-
|
|
55403
|
+
static template = "o-spreadsheet-BottomBarStatistic";
|
|
54752
55404
|
static props = {
|
|
54753
55405
|
openContextMenu: Function,
|
|
54754
55406
|
closeContextMenu: Function,
|
|
54755
55407
|
};
|
|
54756
55408
|
static components = { Ripple };
|
|
54757
55409
|
selectedStatisticFn = "";
|
|
54758
|
-
|
|
55410
|
+
store;
|
|
54759
55411
|
setup() {
|
|
54760
|
-
this.
|
|
55412
|
+
this.store = useStore(AggregateStatisticsStore);
|
|
54761
55413
|
owl.onWillUpdateProps(() => {
|
|
54762
|
-
|
|
54763
|
-
if (!deepEquals(newStatisticFnResults, this.statisticFnResults)) {
|
|
55414
|
+
if (Object.values(this.store.statisticFnResults).every((result) => result === undefined)) {
|
|
54764
55415
|
this.props.closeContextMenu();
|
|
54765
55416
|
}
|
|
54766
|
-
this.statisticFnResults = newStatisticFnResults;
|
|
54767
55417
|
});
|
|
54768
55418
|
}
|
|
54769
55419
|
getSelectedStatistic() {
|
|
54770
55420
|
// don't display button if no function has a result
|
|
54771
|
-
if (Object.values(this.statisticFnResults).every((result) => result === undefined)) {
|
|
55421
|
+
if (Object.values(this.store.statisticFnResults).every((result) => result === undefined)) {
|
|
54772
55422
|
return undefined;
|
|
54773
55423
|
}
|
|
54774
55424
|
if (this.selectedStatisticFn === "") {
|
|
54775
|
-
this.selectedStatisticFn = Object.keys(this.statisticFnResults)[0];
|
|
55425
|
+
this.selectedStatisticFn = Object.keys(this.store.statisticFnResults)[0];
|
|
54776
55426
|
}
|
|
54777
|
-
return this.getComposedFnName(this.selectedStatisticFn
|
|
55427
|
+
return this.getComposedFnName(this.selectedStatisticFn);
|
|
54778
55428
|
}
|
|
54779
55429
|
listSelectionStatistics(ev) {
|
|
54780
55430
|
const registry = new MenuItemRegistry();
|
|
54781
55431
|
let i = 0;
|
|
54782
|
-
for (let [fnName
|
|
55432
|
+
for (let [fnName] of Object.entries(this.store.statisticFnResults)) {
|
|
54783
55433
|
registry.add(fnName, {
|
|
54784
|
-
name: this.getComposedFnName(fnName
|
|
55434
|
+
name: () => this.getComposedFnName(fnName),
|
|
54785
55435
|
sequence: i,
|
|
54786
55436
|
isReadonlyAllowed: true,
|
|
54787
55437
|
execute: () => {
|
|
@@ -54794,8 +55444,9 @@ class BottomBarStatistic extends owl.Component {
|
|
|
54794
55444
|
const { top, left, width } = target.getBoundingClientRect();
|
|
54795
55445
|
this.props.openContextMenu(left + width, top, registry);
|
|
54796
55446
|
}
|
|
54797
|
-
getComposedFnName(fnName
|
|
55447
|
+
getComposedFnName(fnName) {
|
|
54798
55448
|
const locale = this.env.model.getters.getLocale();
|
|
55449
|
+
const fnValue = this.store.statisticFnResults[fnName];
|
|
54799
55450
|
return fnName + ": " + (fnValue !== undefined ? formatValue(fnValue, { locale }) : "__");
|
|
54800
55451
|
}
|
|
54801
55452
|
}
|
|
@@ -54904,10 +55555,14 @@ class BottomBar extends owl.Component {
|
|
|
54904
55555
|
name: sheet.name,
|
|
54905
55556
|
sequence: i,
|
|
54906
55557
|
isReadonlyAllowed: true,
|
|
54907
|
-
textColor: sheet.isVisible ? undefined : "
|
|
55558
|
+
textColor: sheet.isVisible ? undefined : "#808080",
|
|
54908
55559
|
execute: (env) => {
|
|
55560
|
+
if (!this.env.model.getters.isSheetVisible(sheetId)) {
|
|
55561
|
+
this.env.model.dispatch("SHOW_SHEET", { sheetId });
|
|
55562
|
+
}
|
|
54909
55563
|
env.model.dispatch("ACTIVATE_SHEET", { sheetIdFrom: from, sheetIdTo: sheetId });
|
|
54910
55564
|
},
|
|
55565
|
+
isEnabled: (env) => (env.model.getters.isReadonly() ? sheet.isVisible : true),
|
|
54911
55566
|
});
|
|
54912
55567
|
i++;
|
|
54913
55568
|
}
|
|
@@ -54980,7 +55635,7 @@ class BottomBar extends owl.Component {
|
|
|
54980
55635
|
this.sheetListRef.el.scrollTo({ top: 0, left: scroll, behavior: "smooth" });
|
|
54981
55636
|
}
|
|
54982
55637
|
onSheetMouseDown(sheetId, event) {
|
|
54983
|
-
if (event.button !== 0)
|
|
55638
|
+
if (event.button !== 0 || this.env.model.getters.isReadonly())
|
|
54984
55639
|
return;
|
|
54985
55640
|
this.closeMenu();
|
|
54986
55641
|
const visibleSheets = this.getVisibleSheets();
|
|
@@ -55016,7 +55671,7 @@ class BottomBar extends owl.Component {
|
|
|
55016
55671
|
.map((sheetEl) => sheetEl.getBoundingClientRect())
|
|
55017
55672
|
.map((rect) => ({
|
|
55018
55673
|
x: rect.x,
|
|
55019
|
-
width: rect.width - 1,
|
|
55674
|
+
width: rect.width - 1, // -1 to compensate negative margin
|
|
55020
55675
|
y: rect.y,
|
|
55021
55676
|
height: rect.height,
|
|
55022
55677
|
}));
|
|
@@ -55054,7 +55709,6 @@ class SpreadsheetDashboard extends owl.Component {
|
|
|
55054
55709
|
Popover,
|
|
55055
55710
|
VerticalScrollBar,
|
|
55056
55711
|
HorizontalScrollBar,
|
|
55057
|
-
FilterIconsOverlay,
|
|
55058
55712
|
};
|
|
55059
55713
|
cellPopovers;
|
|
55060
55714
|
onMouseWheel;
|
|
@@ -55243,7 +55897,7 @@ class RowGroup extends AbstractHeaderGroup {
|
|
|
55243
55897
|
}
|
|
55244
55898
|
return cssPropertiesToCss({
|
|
55245
55899
|
top: `${groupBox.headerRect.height / 2}px`,
|
|
55246
|
-
left: `calc(50% - 1px)`,
|
|
55900
|
+
left: `calc(50% - 1px)`, // -1px: we want the border to be on the center
|
|
55247
55901
|
width: `30%`,
|
|
55248
55902
|
height: `calc(100% - ${groupBox.headerRect.height / 2}px)`,
|
|
55249
55903
|
"border-left": `1px solid ${HEADER_GROUPING_BORDER_COLOR}`,
|
|
@@ -55295,7 +55949,7 @@ class ColGroup extends AbstractHeaderGroup {
|
|
|
55295
55949
|
return "";
|
|
55296
55950
|
}
|
|
55297
55951
|
return cssPropertiesToCss({
|
|
55298
|
-
top: `calc(50% - 1px)`,
|
|
55952
|
+
top: `calc(50% - 1px)`, // -1px: we want the border to be on the center
|
|
55299
55953
|
left: `${groupBox.headerRect.width / 2}px`,
|
|
55300
55954
|
width: `calc(100% - ${groupBox.headerRect.width / 2}px)`,
|
|
55301
55955
|
height: `30%`,
|
|
@@ -55941,6 +56595,13 @@ class TopBarComposer extends owl.Component {
|
|
|
55941
56595
|
"border-color": SELECTION_BORDER_COLOR,
|
|
55942
56596
|
});
|
|
55943
56597
|
}
|
|
56598
|
+
get delimitation() {
|
|
56599
|
+
const { width, height } = this.env.model.getters.getSheetViewDimensionWithHeaders();
|
|
56600
|
+
return {
|
|
56601
|
+
width,
|
|
56602
|
+
height,
|
|
56603
|
+
};
|
|
56604
|
+
}
|
|
55944
56605
|
onFocus(selection) {
|
|
55945
56606
|
this.composerFocusStore.focusTopBarComposer(selection);
|
|
55946
56607
|
}
|
|
@@ -56386,7 +57047,7 @@ css /* scss */ `
|
|
|
56386
57047
|
}
|
|
56387
57048
|
.o-disabled {
|
|
56388
57049
|
opacity: 0.4;
|
|
56389
|
-
|
|
57050
|
+
cursor: default;
|
|
56390
57051
|
pointer-events: none;
|
|
56391
57052
|
}
|
|
56392
57053
|
|
|
@@ -56552,7 +57213,7 @@ css /* scss */ `
|
|
|
56552
57213
|
|
|
56553
57214
|
.o-number-input {
|
|
56554
57215
|
/* Remove number input arrows */
|
|
56555
|
-
|
|
57216
|
+
appearance: textfield;
|
|
56556
57217
|
&::-webkit-outer-spin-button,
|
|
56557
57218
|
&::-webkit-inner-spin-button {
|
|
56558
57219
|
-webkit-appearance: none;
|
|
@@ -56595,6 +57256,7 @@ class Spreadsheet extends owl.Component {
|
|
|
56595
57256
|
this.notificationStore = useStore(NotificationStore);
|
|
56596
57257
|
this.composerFocusStore = useStore(ComposerFocusStore);
|
|
56597
57258
|
this.sidePanel = useStore(SidePanelStore);
|
|
57259
|
+
useStore(ArrayFormulaHighlight);
|
|
56598
57260
|
this.keyDownMapping = {
|
|
56599
57261
|
"CTRL+H": () => this.sidePanel.toggle("FindAndReplace", {}),
|
|
56600
57262
|
"CTRL+F": () => this.sidePanel.toggle("FindAndReplace", {}),
|
|
@@ -56697,7 +57359,7 @@ class Spreadsheet extends owl.Component {
|
|
|
56697
57359
|
const gridColSize = GROUP_LAYER_WIDTH * this.rowLayers.length;
|
|
56698
57360
|
const gridRowSize = GROUP_LAYER_WIDTH * this.colLayers.length;
|
|
56699
57361
|
return cssPropertiesToCss({
|
|
56700
|
-
"grid-template-columns": `${gridColSize ? gridColSize + 2 : 0}px auto`,
|
|
57362
|
+
"grid-template-columns": `${gridColSize ? gridColSize + 2 : 0}px auto`, // +2: margins
|
|
56701
57363
|
"grid-template-rows": `${gridRowSize ? gridRowSize + 2 : 0}px auto`,
|
|
56702
57364
|
});
|
|
56703
57365
|
}
|
|
@@ -57429,14 +58091,6 @@ class SelectiveHistory {
|
|
|
57429
58091
|
this.revertBefore(operationId);
|
|
57430
58092
|
this.tree.drop(operationId);
|
|
57431
58093
|
}
|
|
57432
|
-
getRevertedExecution() {
|
|
57433
|
-
const data = [];
|
|
57434
|
-
const operations = this.tree.revertedExecution(this.HEAD_BRANCH);
|
|
57435
|
-
for (const { operation } of operations) {
|
|
57436
|
-
data.push(operation.data);
|
|
57437
|
-
}
|
|
57438
|
-
return data;
|
|
57439
|
-
}
|
|
57440
58094
|
/**
|
|
57441
58095
|
* Revert the state as it was *before* the given operation was executed.
|
|
57442
58096
|
*/
|
|
@@ -58225,9 +58879,15 @@ function createChart(chart, chartSheetIndex, data) {
|
|
|
58225
58879
|
case "bar":
|
|
58226
58880
|
plot = addBarChart(chart.data);
|
|
58227
58881
|
break;
|
|
58882
|
+
case "combo":
|
|
58883
|
+
plot = addComboChart(chart.data);
|
|
58884
|
+
break;
|
|
58228
58885
|
case "line":
|
|
58229
58886
|
plot = addLineChart(chart.data);
|
|
58230
58887
|
break;
|
|
58888
|
+
case "scatter":
|
|
58889
|
+
plot = addScatterChart(chart.data);
|
|
58890
|
+
break;
|
|
58231
58891
|
case "pie":
|
|
58232
58892
|
plot = addDoughnutChart(chart.data, chartSheetIndex, data, { holeSize: 0 });
|
|
58233
58893
|
break;
|
|
@@ -58387,6 +59047,79 @@ function addBarChart(chart) {
|
|
|
58387
59047
|
${addAx(axisPos, "c:valAx", valAxId, catAxId, { fontColor: chart.fontColor })}
|
|
58388
59048
|
`;
|
|
58389
59049
|
}
|
|
59050
|
+
function addComboChart(chart) {
|
|
59051
|
+
// gapWitdh and overlap that define the space between clusters (in %) and the overlap between datasets (from -100: completely scattered to 100, completely overlapped)
|
|
59052
|
+
// see gapWidth : https://c-rex.net/projects/samples/ooxml/e1/Part4/OOXML_P4_DOCX_gapWidth_topic_ID0EFVEQB.html#topic_ID0EFVEQB
|
|
59053
|
+
// see overlap : https://c-rex.net/projects/samples/ooxml/e1/Part4/OOXML_P4_DOCX_overlap_topic_ID0ELYQQB.html#topic_ID0ELYQQB
|
|
59054
|
+
//
|
|
59055
|
+
// overlap and gapWitdh seems to be by default at -20 and 20 in chart.js.
|
|
59056
|
+
// See https://www.chartjs.org/docs/latest/charts/bar.html and https://www.chartjs.org/docs/latest/charts/bar.html#barpercentage-vs-categorypercentage
|
|
59057
|
+
const colors = new ChartColors();
|
|
59058
|
+
const dataSetsNodes = [];
|
|
59059
|
+
for (const [dsIndex, dataset] of Object.entries(chart.dataSets)) {
|
|
59060
|
+
const color = toXlsxHexColor(colors.next());
|
|
59061
|
+
const dataShapeProperty = shapeProperty({
|
|
59062
|
+
backgroundColor: color,
|
|
59063
|
+
line: { color },
|
|
59064
|
+
});
|
|
59065
|
+
dataSetsNodes.push(dsIndex === "0"
|
|
59066
|
+
? escapeXml /*xml*/ `
|
|
59067
|
+
<c:ser>
|
|
59068
|
+
<c:idx val="${dsIndex}"/>
|
|
59069
|
+
<c:order val="${dsIndex}"/>
|
|
59070
|
+
${dataset.label ? escapeXml /*xml*/ `<c:tx>${stringRef(dataset.label)}</c:tx>` : ""}
|
|
59071
|
+
${dataShapeProperty}
|
|
59072
|
+
${chart.labelRange ? escapeXml /*xml*/ `<c:cat>${stringRef(chart.labelRange)}</c:cat>` : ""} <!-- x-coordinate values -->
|
|
59073
|
+
<c:val> <!-- x-coordinate values -->
|
|
59074
|
+
${numberRef(dataset.range)}
|
|
59075
|
+
</c:val>
|
|
59076
|
+
</c:ser>
|
|
59077
|
+
`
|
|
59078
|
+
: escapeXml /*xml*/ `
|
|
59079
|
+
<c:ser>
|
|
59080
|
+
<c:idx val="${dsIndex}"/>
|
|
59081
|
+
<c:order val="${dsIndex}"/>
|
|
59082
|
+
<c:smooth val="0"/>
|
|
59083
|
+
<c:marker>
|
|
59084
|
+
<c:symbol val="circle" />
|
|
59085
|
+
<c:size val="5"/>
|
|
59086
|
+
</c:marker>
|
|
59087
|
+
${dataset.label ? escapeXml `<c:tx>${stringRef(dataset.label)}</c:tx>` : ""}
|
|
59088
|
+
${dataShapeProperty}
|
|
59089
|
+
${chart.labelRange ? escapeXml `<c:cat>${stringRef(chart.labelRange)}</c:cat>` : ""} <!-- x-coordinate values -->
|
|
59090
|
+
<c:val> <!-- x-coordinate values -->
|
|
59091
|
+
${numberRef(dataset.range)}
|
|
59092
|
+
</c:val>
|
|
59093
|
+
</c:ser>
|
|
59094
|
+
`);
|
|
59095
|
+
}
|
|
59096
|
+
// Excel does not support this feature
|
|
59097
|
+
const axisPos = chart.verticalAxisPosition === "left" ? "l" : "r";
|
|
59098
|
+
const overlap = chart.stacked ? 100 : -20;
|
|
59099
|
+
return escapeXml /*xml*/ `
|
|
59100
|
+
<c:barChart>
|
|
59101
|
+
<c:barDir val="col"/>
|
|
59102
|
+
<c:grouping val="clustered"/>
|
|
59103
|
+
<c:overlap val="${overlap}"/>
|
|
59104
|
+
<c:gapWidth val="70"/>
|
|
59105
|
+
<!-- each data marker in the series does not have a different color -->
|
|
59106
|
+
<c:varyColors val="0"/>
|
|
59107
|
+
${dataSetsNodes[0]}
|
|
59108
|
+
<c:axId val="${catAxId}" />
|
|
59109
|
+
<c:axId val="${valAxId}" />
|
|
59110
|
+
</c:barChart>
|
|
59111
|
+
<c:lineChart>
|
|
59112
|
+
<c:grouping val="standard"/>
|
|
59113
|
+
<!-- each data marker in the series does not have a different color -->
|
|
59114
|
+
<c:varyColors val="0"/>
|
|
59115
|
+
${joinXmlNodes(dataSetsNodes.slice(1))}
|
|
59116
|
+
<c:axId val="${catAxId}" />
|
|
59117
|
+
<c:axId val="${valAxId}" />
|
|
59118
|
+
</c:lineChart>
|
|
59119
|
+
${addAx("b", "c:catAx", catAxId, valAxId, { fontColor: chart.fontColor })}
|
|
59120
|
+
${addAx(axisPos, "c:valAx", valAxId, catAxId, { fontColor: chart.fontColor })}
|
|
59121
|
+
`;
|
|
59122
|
+
}
|
|
58390
59123
|
function addLineChart(chart) {
|
|
58391
59124
|
const colors = new ChartColors();
|
|
58392
59125
|
const dataSetsNodes = [];
|
|
@@ -58432,9 +59165,55 @@ function addLineChart(chart) {
|
|
|
58432
59165
|
${addAx(axisPos, "c:valAx", valAxId, catAxId, { fontColor: chart.fontColor })}
|
|
58433
59166
|
`;
|
|
58434
59167
|
}
|
|
59168
|
+
function addScatterChart(chart) {
|
|
59169
|
+
const colors = new ChartColors();
|
|
59170
|
+
const dataSetsNodes = [];
|
|
59171
|
+
for (const [dsIndex, dataset] of Object.entries(chart.dataSets)) {
|
|
59172
|
+
dataSetsNodes.push(escapeXml /*xml*/ `
|
|
59173
|
+
<c:ser>
|
|
59174
|
+
<c:idx val="${dsIndex}"/>
|
|
59175
|
+
<c:order val="${dsIndex}"/>
|
|
59176
|
+
<c:smooth val="0"/>
|
|
59177
|
+
<c:spPr>
|
|
59178
|
+
<a:ln w="19050" cap="rnd">
|
|
59179
|
+
<a:noFill/>
|
|
59180
|
+
<a:round/>
|
|
59181
|
+
</a:ln>
|
|
59182
|
+
<a:effectLst/>
|
|
59183
|
+
</c:spPr>
|
|
59184
|
+
<c:marker>
|
|
59185
|
+
<c:symbol val="circle" />
|
|
59186
|
+
<c:size val="5"/>
|
|
59187
|
+
${shapeProperty({ backgroundColor: toXlsxHexColor(colors.next()) })}
|
|
59188
|
+
</c:marker>
|
|
59189
|
+
${chart.labelRange
|
|
59190
|
+
? escapeXml /*xml*/ `<c:xVal> <!-- x-coordinate values -->
|
|
59191
|
+
${numberRef(chart.labelRange)}
|
|
59192
|
+
</c:xVal>`
|
|
59193
|
+
: ""}
|
|
59194
|
+
<c:yVal> <!-- y-coordinate values -->
|
|
59195
|
+
${numberRef(dataset.range)}
|
|
59196
|
+
</c:yVal>
|
|
59197
|
+
</c:ser>
|
|
59198
|
+
`);
|
|
59199
|
+
}
|
|
59200
|
+
const axisPos = chart.verticalAxisPosition === "left" ? "l" : "r";
|
|
59201
|
+
return escapeXml /*xml*/ `
|
|
59202
|
+
<c:scatterChart>
|
|
59203
|
+
<!-- each data marker in the series does not have a different color -->
|
|
59204
|
+
<c:varyColors val="0"/>
|
|
59205
|
+
<c:scatterStyle val="lineMarker"/>
|
|
59206
|
+
${joinXmlNodes(dataSetsNodes)}
|
|
59207
|
+
<c:axId val="${catAxId}" />
|
|
59208
|
+
<c:axId val="${valAxId}" />
|
|
59209
|
+
</c:scatterChart>
|
|
59210
|
+
${addAx("b", "c:valAx", catAxId, valAxId, { fontColor: chart.fontColor })}
|
|
59211
|
+
${addAx(axisPos, "c:valAx", valAxId, catAxId, { fontColor: chart.fontColor })}
|
|
59212
|
+
`;
|
|
59213
|
+
}
|
|
58435
59214
|
function addDoughnutChart(chart, chartSheetIndex, data, { holeSize } = { holeSize: 50 }) {
|
|
58436
59215
|
const colors = new ChartColors();
|
|
58437
|
-
const maxLength =
|
|
59216
|
+
const maxLength = largeMax(chart.dataSets.map((ds) => getRangeSize(ds.range, chartSheetIndex, data)));
|
|
58438
59217
|
const doughnutColors = range(0, maxLength).map(() => toXlsxHexColor(colors.next()));
|
|
58439
59218
|
const dataSetsNodes = [];
|
|
58440
59219
|
for (const [dsIndex, dataset] of Object.entries(chart.dataSets).reverse()) {
|
|
@@ -58572,8 +59351,7 @@ function addContent(content, sharedStrings, forceString = false) {
|
|
|
58572
59351
|
attrs.push(["t", "b"]);
|
|
58573
59352
|
}
|
|
58574
59353
|
else if (forceString || !isNumber(value, DEFAULT_LOCALE)) {
|
|
58575
|
-
|
|
58576
|
-
value = id.toString();
|
|
59354
|
+
value = pushElement(content, sharedStrings);
|
|
58577
59355
|
attrs.push(["t", "s"]);
|
|
58578
59356
|
}
|
|
58579
59357
|
return { attrs, node: escapeXml /*xml*/ `<v>${value}</v>` };
|
|
@@ -58698,8 +59476,7 @@ function addCellIsRule(cf, rule, dxfs) {
|
|
|
58698
59476
|
if (rule.style.fillColor) {
|
|
58699
59477
|
dxf.fill = { fgColor: { rgb: rule.style.fillColor } };
|
|
58700
59478
|
}
|
|
58701
|
-
|
|
58702
|
-
ruleAttributes.push(["dxfId", id]);
|
|
59479
|
+
ruleAttributes.push(["dxfId", pushElement(dxf, dxfs)]);
|
|
58703
59480
|
return escapeXml /*xml*/ `
|
|
58704
59481
|
<conditionalFormatting sqref="${cf.ranges.join(" ")}">
|
|
58705
59482
|
<cfRule ${formatAttributes(ruleAttributes)}>
|
|
@@ -59312,7 +60089,7 @@ function addTableColumns(table, sheetData) {
|
|
|
59312
60089
|
const colHeaderXc = toXC(tableZone.left + i, tableZone.top);
|
|
59313
60090
|
const colName = sheetData.cells[colHeaderXc]?.content || `col${i}`;
|
|
59314
60091
|
const colAttributes = [
|
|
59315
|
-
["id", i + 1],
|
|
60092
|
+
["id", i + 1], // id cannot be 0
|
|
59316
60093
|
["name", colName],
|
|
59317
60094
|
];
|
|
59318
60095
|
columns.push(escapeXml /*xml*/ `<tableColumn ${formatAttributes(colAttributes)}/>`);
|
|
@@ -59528,8 +60305,10 @@ function addSheetViews(sheet) {
|
|
|
59528
60305
|
* https://www.ecma-international.org/publications-and-standards/standards/ecma-376/
|
|
59529
60306
|
*/
|
|
59530
60307
|
function getXLSX(data) {
|
|
60308
|
+
data = fixLengthySheetNames(data);
|
|
60309
|
+
data = purgeSingleRowTables(data);
|
|
59531
60310
|
const files = [];
|
|
59532
|
-
const construct = getDefaultXLSXStructure();
|
|
60311
|
+
const construct = getDefaultXLSXStructure(data);
|
|
59533
60312
|
files.push(createWorkbook(data, construct));
|
|
59534
60313
|
files.push(...createWorksheets(data, construct));
|
|
59535
60314
|
files.push(createStylesSheet(construct));
|
|
@@ -59775,6 +60554,50 @@ function createRelRoot() {
|
|
|
59775
60554
|
`;
|
|
59776
60555
|
return createXMLFile(parseXML(xml), "_rels/.rels");
|
|
59777
60556
|
}
|
|
60557
|
+
/**
|
|
60558
|
+
* Excel sheet names are maximum 31 characters while o-spreadsheet do not have this limit.
|
|
60559
|
+
* This method converts the sheet names to be within the 31 characters limit.
|
|
60560
|
+
* The cells/charts referencing this sheet will be updated accordingly.
|
|
60561
|
+
*/
|
|
60562
|
+
function fixLengthySheetNames(data) {
|
|
60563
|
+
const nameMapping = {};
|
|
60564
|
+
const newNames = new Set();
|
|
60565
|
+
for (const sheet of data.sheets) {
|
|
60566
|
+
let newName = sheet.name.slice(0, 31);
|
|
60567
|
+
let i = 1;
|
|
60568
|
+
while (newNames.has(newName)) {
|
|
60569
|
+
newName = newName.slice(0, 31 - String(i).length) + i++;
|
|
60570
|
+
}
|
|
60571
|
+
newNames.add(newName);
|
|
60572
|
+
if (newName !== sheet.name) {
|
|
60573
|
+
nameMapping[sheet.name] = newName;
|
|
60574
|
+
sheet.name = newName;
|
|
60575
|
+
}
|
|
60576
|
+
}
|
|
60577
|
+
if (!Object.keys(nameMapping).length) {
|
|
60578
|
+
return data;
|
|
60579
|
+
}
|
|
60580
|
+
const sheetWithNewNames = Object.keys(nameMapping).sort((a, b) => b.length - a.length);
|
|
60581
|
+
let stringifiedData = JSON.stringify(data);
|
|
60582
|
+
for (const sheetName of sheetWithNewNames) {
|
|
60583
|
+
const regex = new RegExp(`'?${escapeRegExp(sheetName)}'?!`, "g");
|
|
60584
|
+
stringifiedData = stringifiedData.replaceAll(regex, (match) => {
|
|
60585
|
+
const newName = nameMapping[sheetName];
|
|
60586
|
+
return match.replace(sheetName, newName);
|
|
60587
|
+
});
|
|
60588
|
+
}
|
|
60589
|
+
return JSON.parse(stringifiedData);
|
|
60590
|
+
}
|
|
60591
|
+
/** Excel files do not support tables with a single row the defined range
|
|
60592
|
+
* Since those tables are not really useful (no filtering/limited styling)
|
|
60593
|
+
* This function filters out all tables with a single row.
|
|
60594
|
+
*/
|
|
60595
|
+
function purgeSingleRowTables(data) {
|
|
60596
|
+
for (const sheet of data.sheets) {
|
|
60597
|
+
sheet.tables = sheet.tables.filter((table) => zoneToDimension(toZone(table.range)).numberOfRows > 1);
|
|
60598
|
+
}
|
|
60599
|
+
return data;
|
|
60600
|
+
}
|
|
59778
60601
|
|
|
59779
60602
|
var Status;
|
|
59780
60603
|
(function (Status) {
|
|
@@ -60028,6 +60851,7 @@ class Model extends EventBus {
|
|
|
60028
60851
|
stateObserver: this.state,
|
|
60029
60852
|
range: this.range,
|
|
60030
60853
|
dispatch: this.dispatchFromCorePlugin,
|
|
60854
|
+
canDispatch: this.canDispatch,
|
|
60031
60855
|
uuidGenerator: this.uuidGenerator,
|
|
60032
60856
|
custom: this.config.custom,
|
|
60033
60857
|
external: this.config.external,
|
|
@@ -60038,6 +60862,7 @@ class Model extends EventBus {
|
|
|
60038
60862
|
getters: this.getters,
|
|
60039
60863
|
stateObserver: this.state,
|
|
60040
60864
|
dispatch: this.dispatch,
|
|
60865
|
+
canDispatch: this.canDispatch,
|
|
60041
60866
|
selection: this.selection,
|
|
60042
60867
|
moveClient: this.session.move.bind(this.session),
|
|
60043
60868
|
custom: this.config.custom,
|
|
@@ -60361,7 +61186,7 @@ const links = {
|
|
|
60361
61186
|
const components = {
|
|
60362
61187
|
Checkbox,
|
|
60363
61188
|
Section,
|
|
60364
|
-
|
|
61189
|
+
RoundColorPicker,
|
|
60365
61190
|
ChartDataSeries,
|
|
60366
61191
|
ChartErrorSection,
|
|
60367
61192
|
ChartLabelRange,
|
|
@@ -60464,6 +61289,6 @@ exports.tokenColors = tokenColors;
|
|
|
60464
61289
|
exports.tokenize = tokenize;
|
|
60465
61290
|
|
|
60466
61291
|
|
|
60467
|
-
__info__.version = "17.3.0-alpha.
|
|
60468
|
-
__info__.date = "2024-
|
|
60469
|
-
__info__.hash = "
|
|
61292
|
+
__info__.version = "17.3.0-alpha.3";
|
|
61293
|
+
__info__.date = "2024-04-10T12:28:23.658Z";
|
|
61294
|
+
__info__.hash = "80b5056";
|