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