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