@odoo/o-spreadsheet 17.3.0-alpha.2 → 17.3.0-alpha.4
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 +1267 -456
- package/dist/o-spreadsheet.d.ts +285 -215
- package/dist/o-spreadsheet.esm.js +1267 -456
- package/dist/o-spreadsheet.iife.js +1267 -456
- package/dist/o-spreadsheet.iife.min.js +404 -344
- package/dist/o_spreadsheet.xml +240 -92
- package/package.json +2 -1
|
@@ -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-04-
|
|
8
|
-
* @hash
|
|
6
|
+
* @version 17.3.0-alpha.4
|
|
7
|
+
* @date 2024-04-15T11:02:51.551Z
|
|
8
|
+
* @hash a32a1df
|
|
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';
|
|
@@ -37,6 +37,9 @@ const GRID_BORDER_COLOR = "#E2E3E3";
|
|
|
37
37
|
const FROZEN_PANE_HEADER_BORDER_COLOR = "#BCBCBC";
|
|
38
38
|
const FROZEN_PANE_BORDER_COLOR = "#DADFE8";
|
|
39
39
|
const COMPOSER_ASSISTANT_COLOR = "#9B359B";
|
|
40
|
+
const CHART_WATERFALL_POSITIVE_COLOR = "#006FBE";
|
|
41
|
+
const CHART_WATERFALL_NEGATIVE_COLOR = "#E40000";
|
|
42
|
+
const CHART_WATERFALL_SUBTOTAL_COLOR = "#AAAAAA";
|
|
40
43
|
// Color picker defaults as upper case HEX to match `toHex`helper
|
|
41
44
|
const COLOR_PICKER_DEFAULTS = [
|
|
42
45
|
"#000000",
|
|
@@ -188,7 +191,7 @@ const DEFAULT_GAUGE_LOWER_COLOR = "#cc0000";
|
|
|
188
191
|
const DEFAULT_GAUGE_MIDDLE_COLOR = "#f1c232";
|
|
189
192
|
const DEFAULT_GAUGE_UPPER_COLOR = "#6aa84f";
|
|
190
193
|
const DEFAULT_SCORECARD_BASELINE_MODE = "difference";
|
|
191
|
-
const DEFAULT_SCORECARD_BASELINE_COLOR_UP = "#
|
|
194
|
+
const DEFAULT_SCORECARD_BASELINE_COLOR_UP = "#6AA84F";
|
|
192
195
|
const DEFAULT_SCORECARD_BASELINE_COLOR_DOWN = "#E06666";
|
|
193
196
|
const LINE_FILL_TRANSPARENCY = 0.4;
|
|
194
197
|
// session
|
|
@@ -560,7 +563,7 @@ function deepEquals(o1, o2) {
|
|
|
560
563
|
if (typeof o1 !== typeof o2)
|
|
561
564
|
return false;
|
|
562
565
|
if (typeof o1 !== "object")
|
|
563
|
-
return
|
|
566
|
+
return false;
|
|
564
567
|
// Objects can have different keys if the values are undefined
|
|
565
568
|
for (const key in o2) {
|
|
566
569
|
if (!(key in o1) && o2[key] !== undefined) {
|
|
@@ -1884,6 +1887,11 @@ const invalidateCFEvaluationCommands = new Set([
|
|
|
1884
1887
|
"REMOVE_CONDITIONAL_FORMAT",
|
|
1885
1888
|
"CHANGE_CONDITIONAL_FORMAT_PRIORITY",
|
|
1886
1889
|
]);
|
|
1890
|
+
const invalidateBordersCommands = new Set([
|
|
1891
|
+
"AUTOFILL_CELL",
|
|
1892
|
+
"SET_BORDER",
|
|
1893
|
+
"SET_ZONE_BORDERS",
|
|
1894
|
+
]);
|
|
1887
1895
|
const readonlyAllowedCommands = new Set([
|
|
1888
1896
|
"START",
|
|
1889
1897
|
"ACTIVATE_SHEET",
|
|
@@ -2115,6 +2123,7 @@ var CommandResult;
|
|
|
2115
2123
|
CommandResult["NoChanges"] = "NoChanges";
|
|
2116
2124
|
CommandResult["InvalidInputId"] = "InvalidInputId";
|
|
2117
2125
|
CommandResult["SheetIsHidden"] = "SheetIsHidden";
|
|
2126
|
+
CommandResult["InvalidTableResize"] = "InvalidTableResize";
|
|
2118
2127
|
})(CommandResult || (CommandResult = {}));
|
|
2119
2128
|
|
|
2120
2129
|
const PLAIN_TEXT_FORMAT = "@"; // see OpenXML spec §18.8.31
|
|
@@ -2181,6 +2190,7 @@ const CellErrorType = {
|
|
|
2181
2190
|
BadExpression: "#BAD_EXPR",
|
|
2182
2191
|
CircularDependency: "#CYCLE",
|
|
2183
2192
|
UnknownFunction: "#NAME?",
|
|
2193
|
+
DivisionByZero: "#DIV/0!",
|
|
2184
2194
|
GenericError: "#ERROR",
|
|
2185
2195
|
};
|
|
2186
2196
|
const errorTypes = new Set(Object.values(CellErrorType));
|
|
@@ -2219,9 +2229,9 @@ class UnknownFunctionError extends EvaluationError {
|
|
|
2219
2229
|
|
|
2220
2230
|
// HELPERS
|
|
2221
2231
|
const SORT_TYPES_ORDER = ["number", "string", "boolean", "undefined"];
|
|
2222
|
-
function assert(condition, message) {
|
|
2232
|
+
function assert(condition, message, value) {
|
|
2223
2233
|
if (!condition()) {
|
|
2224
|
-
throw new EvaluationError(message);
|
|
2234
|
+
throw new EvaluationError(message, value);
|
|
2225
2235
|
}
|
|
2226
2236
|
}
|
|
2227
2237
|
function inferFormat(data) {
|
|
@@ -2299,6 +2309,9 @@ function strictToInteger(value, locale) {
|
|
|
2299
2309
|
function assertNumberGreaterThanOrEqualToOne(value) {
|
|
2300
2310
|
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()));
|
|
2301
2311
|
}
|
|
2312
|
+
function assertNotZero(value) {
|
|
2313
|
+
assert(() => value !== 0, _t("Evaluation of function [[FUNCTION_NAME]] caused a divide by zero error."), CellErrorType.DivisionByZero);
|
|
2314
|
+
}
|
|
2302
2315
|
function toString(data) {
|
|
2303
2316
|
const value = toValue(data);
|
|
2304
2317
|
switch (typeof value) {
|
|
@@ -2508,6 +2521,15 @@ function matrixMap(matrix, fn) {
|
|
|
2508
2521
|
}
|
|
2509
2522
|
return generateMatrix(matrix.length, matrix[0].length, (col, row) => fn(matrix[col][row]));
|
|
2510
2523
|
}
|
|
2524
|
+
function matrixForEach(matrix, fn) {
|
|
2525
|
+
const numberOfCols = matrix.length;
|
|
2526
|
+
const numberOfRows = matrix[0]?.length ?? 0;
|
|
2527
|
+
for (let col = 0; col < numberOfCols; col++) {
|
|
2528
|
+
for (let row = 0; row < numberOfRows; row++) {
|
|
2529
|
+
fn(matrix[col][row]);
|
|
2530
|
+
}
|
|
2531
|
+
}
|
|
2532
|
+
}
|
|
2511
2533
|
function transposeMatrix(matrix) {
|
|
2512
2534
|
if (!matrix.length) {
|
|
2513
2535
|
return [];
|
|
@@ -3064,6 +3086,9 @@ function applyInternalFormat(value, internalFormat, locale) {
|
|
|
3064
3086
|
return formattedValue;
|
|
3065
3087
|
}
|
|
3066
3088
|
function applyInternalNumberFormat(value, format, locale) {
|
|
3089
|
+
if (value === Infinity) {
|
|
3090
|
+
return "∞" + (format.isPercent ? "%" : "");
|
|
3091
|
+
}
|
|
3067
3092
|
if (format.isPercent) {
|
|
3068
3093
|
value = value * 100;
|
|
3069
3094
|
}
|
|
@@ -3407,6 +3432,46 @@ function roundFormat(format) {
|
|
|
3407
3432
|
});
|
|
3408
3433
|
return convertInternalFormatToFormat(roundedFormat);
|
|
3409
3434
|
}
|
|
3435
|
+
function humanizeNumber({ value, format }, locale) {
|
|
3436
|
+
const numberFormat = formatLargeNumber({
|
|
3437
|
+
value,
|
|
3438
|
+
format,
|
|
3439
|
+
}, undefined, locale);
|
|
3440
|
+
return formatValue(value, { format: numberFormat, locale });
|
|
3441
|
+
}
|
|
3442
|
+
function formatLargeNumber(arg, unit, locale) {
|
|
3443
|
+
let value = 0;
|
|
3444
|
+
try {
|
|
3445
|
+
value = Math.abs(toNumber(arg?.value, locale));
|
|
3446
|
+
}
|
|
3447
|
+
catch (e) {
|
|
3448
|
+
return "";
|
|
3449
|
+
}
|
|
3450
|
+
const format = arg?.format;
|
|
3451
|
+
if (unit !== undefined) {
|
|
3452
|
+
const postFix = unit?.value;
|
|
3453
|
+
switch (postFix) {
|
|
3454
|
+
case "k":
|
|
3455
|
+
return createLargeNumberFormat(format, 1e3, "k");
|
|
3456
|
+
case "m":
|
|
3457
|
+
return createLargeNumberFormat(format, 1e6, "m");
|
|
3458
|
+
case "b":
|
|
3459
|
+
return createLargeNumberFormat(format, 1e9, "b");
|
|
3460
|
+
default:
|
|
3461
|
+
throw new EvaluationError(_t("The formatting unit should be 'k', 'm' or 'b'."));
|
|
3462
|
+
}
|
|
3463
|
+
}
|
|
3464
|
+
if (value < 1e5) {
|
|
3465
|
+
return createLargeNumberFormat(format, 0, "");
|
|
3466
|
+
}
|
|
3467
|
+
else if (value < 1e8) {
|
|
3468
|
+
return createLargeNumberFormat(format, 1e3, "k");
|
|
3469
|
+
}
|
|
3470
|
+
else if (value < 1e11) {
|
|
3471
|
+
return createLargeNumberFormat(format, 1e6, "m");
|
|
3472
|
+
}
|
|
3473
|
+
return createLargeNumberFormat(format, 1e9, "b");
|
|
3474
|
+
}
|
|
3410
3475
|
function createLargeNumberFormat(format, magnitude, postFix, locale) {
|
|
3411
3476
|
const internalFormat = parseFormat(format || "#,##0");
|
|
3412
3477
|
const largeNumberFormat = [];
|
|
@@ -4599,14 +4664,19 @@ function getDefaultCellHeight(ctx, cell, colSize) {
|
|
|
4599
4664
|
const textWidthCache = {};
|
|
4600
4665
|
function computeTextWidth(context, text, style, fontUnit = "pt") {
|
|
4601
4666
|
const font = computeTextFont(style, fontUnit);
|
|
4667
|
+
context.save();
|
|
4668
|
+
context.font = font;
|
|
4669
|
+
const width = computeCachedTextWidth(context, text);
|
|
4670
|
+
context.restore();
|
|
4671
|
+
return width;
|
|
4672
|
+
}
|
|
4673
|
+
function computeCachedTextWidth(context, text) {
|
|
4674
|
+
const font = context.font;
|
|
4602
4675
|
if (!textWidthCache[font]) {
|
|
4603
4676
|
textWidthCache[font] = {};
|
|
4604
4677
|
}
|
|
4605
4678
|
if (textWidthCache[font][text] === undefined) {
|
|
4606
|
-
context.save();
|
|
4607
|
-
context.font = font;
|
|
4608
4679
|
const textWidth = context.measureText(text).width;
|
|
4609
|
-
context.restore();
|
|
4610
4680
|
textWidthCache[font][text] = textWidth;
|
|
4611
4681
|
}
|
|
4612
4682
|
return textWidthCache[font][text];
|
|
@@ -4751,6 +4821,42 @@ const pxRegex = /([0-9\.]*)px/;
|
|
|
4751
4821
|
function getContextFontSize(font) {
|
|
4752
4822
|
return Number(font.match(pxRegex)?.[1]);
|
|
4753
4823
|
}
|
|
4824
|
+
// Inspired from https://stackoverflow.com/a/10511598
|
|
4825
|
+
function clipTextWithEllipsis(ctx, text, maxWidth) {
|
|
4826
|
+
let width = computeCachedTextWidth(ctx, text);
|
|
4827
|
+
if (width <= maxWidth) {
|
|
4828
|
+
return text;
|
|
4829
|
+
}
|
|
4830
|
+
const ellipsis = "…";
|
|
4831
|
+
const ellipsisWidth = computeCachedTextWidth(ctx, text);
|
|
4832
|
+
if (width <= ellipsisWidth) {
|
|
4833
|
+
return text;
|
|
4834
|
+
}
|
|
4835
|
+
let len = text.length;
|
|
4836
|
+
while (width >= maxWidth - ellipsisWidth && len-- > 0) {
|
|
4837
|
+
text = text.substring(0, len);
|
|
4838
|
+
width = computeCachedTextWidth(ctx, text);
|
|
4839
|
+
}
|
|
4840
|
+
return text + ellipsis;
|
|
4841
|
+
}
|
|
4842
|
+
function splitTextInTwoLines(text) {
|
|
4843
|
+
let spaces = "";
|
|
4844
|
+
while (text[0] === " ") {
|
|
4845
|
+
spaces += " ";
|
|
4846
|
+
text = text.slice(1);
|
|
4847
|
+
}
|
|
4848
|
+
const length = text.length;
|
|
4849
|
+
const middle = Math.floor(length / 2);
|
|
4850
|
+
const leftSpace = text.substring(0, middle).lastIndexOf(" ");
|
|
4851
|
+
const rightSpace = text.substring(middle).indexOf(" ") + middle;
|
|
4852
|
+
if (leftSpace === -1 && rightSpace === middle - 1) {
|
|
4853
|
+
return [spaces + text, ""];
|
|
4854
|
+
}
|
|
4855
|
+
if (leftSpace > length - rightSpace || rightSpace === middle - 1) {
|
|
4856
|
+
return [spaces + text.slice(0, leftSpace), spaces + text.slice(leftSpace + 1)];
|
|
4857
|
+
}
|
|
4858
|
+
return [spaces + text.slice(0, rightSpace), spaces + text.slice(rightSpace + 1)];
|
|
4859
|
+
}
|
|
4754
4860
|
function drawDecoratedText(context, text, position, underline = false, strikethrough = false, strokeWidth = getContextFontSize(context.font) / 10 //This value is defined to get a good looking stroke
|
|
4755
4861
|
) {
|
|
4756
4862
|
context.fillText(text, position.x, position.y);
|
|
@@ -7394,6 +7500,7 @@ const CellIsOperators = {
|
|
|
7394
7500
|
};
|
|
7395
7501
|
const ChartTerms = {
|
|
7396
7502
|
Series: _t("Series"),
|
|
7503
|
+
BackgroundColor: _t("Background color"),
|
|
7397
7504
|
Errors: {
|
|
7398
7505
|
Unexpected: _t("The chart definition is invalid for an unknown reason"),
|
|
7399
7506
|
// BASIC CHART ERRORS (LINE | BAR | PIE)
|
|
@@ -9057,6 +9164,62 @@ class ComposerFocusStore extends SpreadsheetStore {
|
|
|
9057
9164
|
}
|
|
9058
9165
|
}
|
|
9059
9166
|
|
|
9167
|
+
/** This is a chartJS plugin that will draw connector lines between the bars of a Waterfall chart */
|
|
9168
|
+
const waterfallLinesPlugin = {
|
|
9169
|
+
id: "waterfallLinesPlugin",
|
|
9170
|
+
beforeDraw(chart, args, options) {
|
|
9171
|
+
if (!options.showConnectorLines) {
|
|
9172
|
+
return;
|
|
9173
|
+
}
|
|
9174
|
+
// Note: private properties are not in the typing of chartJS (and some of the existing types are missing properties)
|
|
9175
|
+
// so we don't type anything in this file
|
|
9176
|
+
const drawData = chart._metasets?.[0]?.data;
|
|
9177
|
+
if (!drawData) {
|
|
9178
|
+
return;
|
|
9179
|
+
}
|
|
9180
|
+
const ctx = chart.ctx;
|
|
9181
|
+
ctx.save();
|
|
9182
|
+
ctx.setLineDash([3, 2]);
|
|
9183
|
+
for (let i = 0; i < drawData.length; i++) {
|
|
9184
|
+
const bar = drawData[i];
|
|
9185
|
+
if (bar.height === 0) {
|
|
9186
|
+
continue;
|
|
9187
|
+
}
|
|
9188
|
+
const nextBar = getNextNonEmptyBar(drawData, i);
|
|
9189
|
+
if (!nextBar) {
|
|
9190
|
+
break;
|
|
9191
|
+
}
|
|
9192
|
+
const rect = getBarElementRect(bar);
|
|
9193
|
+
const nextBarRect = getBarElementRect(nextBar);
|
|
9194
|
+
const rawBarValues = bar.$context.raw;
|
|
9195
|
+
const value = rawBarValues[1] - rawBarValues[0];
|
|
9196
|
+
const lineY = Math.round(value < 0 ? rect.bottom - 1 : rect.top);
|
|
9197
|
+
const lineStart = Math.round(rect.right);
|
|
9198
|
+
const lineEnd = Math.round(nextBarRect.left);
|
|
9199
|
+
ctx.strokeStyle = "#999";
|
|
9200
|
+
ctx.beginPath();
|
|
9201
|
+
ctx.moveTo(lineStart + 1, lineY + 0.5);
|
|
9202
|
+
ctx.lineTo(lineEnd, lineY + 0.5);
|
|
9203
|
+
ctx.stroke();
|
|
9204
|
+
}
|
|
9205
|
+
ctx.restore();
|
|
9206
|
+
},
|
|
9207
|
+
};
|
|
9208
|
+
function getBarElementRect(bar) {
|
|
9209
|
+
const flipped = bar.base < bar.y; // Bar are flipped for negative values in the dataset
|
|
9210
|
+
return {
|
|
9211
|
+
left: bar.x - bar.width / 2,
|
|
9212
|
+
right: bar.x + bar.width / 2,
|
|
9213
|
+
bottom: flipped ? bar.base + bar.height : bar.y + bar.height,
|
|
9214
|
+
top: flipped ? bar.base : bar.y,
|
|
9215
|
+
};
|
|
9216
|
+
}
|
|
9217
|
+
function getNextNonEmptyBar(bars, startIndex) {
|
|
9218
|
+
return bars.find((bar, i) => i > startIndex && bar.height !== 0);
|
|
9219
|
+
}
|
|
9220
|
+
|
|
9221
|
+
// @ts-ignore
|
|
9222
|
+
window.Chart?.register(waterfallLinesPlugin);
|
|
9060
9223
|
class ChartJsComponent extends Component {
|
|
9061
9224
|
static template = "o-spreadsheet-ChartJsComponent";
|
|
9062
9225
|
static props = {
|
|
@@ -9101,10 +9264,7 @@ class ChartJsComponent extends Component {
|
|
|
9101
9264
|
else {
|
|
9102
9265
|
this.chart.data.datasets = [];
|
|
9103
9266
|
}
|
|
9104
|
-
this.chart.config.options
|
|
9105
|
-
this.chart.config.options.plugins.legend = chartData.options.plugins.legend;
|
|
9106
|
-
this.chart.config.options.scales = chartData.options?.scales;
|
|
9107
|
-
// ?
|
|
9267
|
+
this.chart.config.options = chartData.options;
|
|
9108
9268
|
this.chart.update("active");
|
|
9109
9269
|
}
|
|
9110
9270
|
}
|
|
@@ -9429,32 +9589,63 @@ function shouldRemoveFirstLabel(labelRange, dataset, dataSetsHaveTitle) {
|
|
|
9429
9589
|
}
|
|
9430
9590
|
return true;
|
|
9431
9591
|
}
|
|
9432
|
-
|
|
9433
|
-
|
|
9434
|
-
|
|
9435
|
-
|
|
9592
|
+
function getChartPositionAtCenterOfViewport(getters, chartSize) {
|
|
9593
|
+
const { x, y } = getters.getMainViewportCoordinates();
|
|
9594
|
+
const { scrollX, scrollY } = getters.getActiveSheetScrollInfo();
|
|
9595
|
+
const { width, height } = getters.getVisibleRect(getters.getActiveMainViewport());
|
|
9596
|
+
const position = {
|
|
9597
|
+
x: x + scrollX + Math.max(0, (width - chartSize.width) / 2),
|
|
9598
|
+
y: y + scrollY + Math.max(0, (height - chartSize.height) / 2),
|
|
9599
|
+
}; // Position at the center of the scrollable viewport
|
|
9600
|
+
return position;
|
|
9601
|
+
}
|
|
9602
|
+
|
|
9603
|
+
function getBaselineText(baseline, keyValue, baselineMode, humanize, locale) {
|
|
9436
9604
|
if (!baseline) {
|
|
9437
9605
|
return "";
|
|
9438
9606
|
}
|
|
9439
9607
|
else if (baselineMode === "text" ||
|
|
9440
9608
|
keyValue?.type !== CellValueType.number ||
|
|
9441
9609
|
baseline.type !== CellValueType.number) {
|
|
9610
|
+
if (humanize) {
|
|
9611
|
+
return humanizeNumber(baseline, locale);
|
|
9612
|
+
}
|
|
9442
9613
|
return baseline.formattedValue;
|
|
9443
9614
|
}
|
|
9615
|
+
let { value, format } = baseline;
|
|
9616
|
+
if (baselineMode === "progress") {
|
|
9617
|
+
value = keyValue.value / value;
|
|
9618
|
+
format = "0.0%";
|
|
9619
|
+
}
|
|
9444
9620
|
else {
|
|
9445
|
-
|
|
9446
|
-
if (baselineMode === "percentage" &&
|
|
9447
|
-
|
|
9621
|
+
value = Math.abs(keyValue.value - value);
|
|
9622
|
+
if (baselineMode === "percentage" && value !== 0) {
|
|
9623
|
+
value = value / baseline.value;
|
|
9448
9624
|
}
|
|
9449
|
-
if (baselineMode
|
|
9450
|
-
|
|
9625
|
+
if (baselineMode === "percentage") {
|
|
9626
|
+
format = "0.0%";
|
|
9451
9627
|
}
|
|
9452
|
-
|
|
9453
|
-
|
|
9628
|
+
if (!format) {
|
|
9629
|
+
value = Math.round(value * 100) / 100;
|
|
9630
|
+
}
|
|
9631
|
+
}
|
|
9632
|
+
if (humanize) {
|
|
9633
|
+
return humanizeNumber({ value, format }, locale);
|
|
9634
|
+
}
|
|
9635
|
+
return formatValue(value, { format, locale });
|
|
9636
|
+
}
|
|
9637
|
+
function getKeyValueText(keyValueCell, humanize, locale) {
|
|
9638
|
+
if (!keyValueCell) {
|
|
9639
|
+
return "";
|
|
9454
9640
|
}
|
|
9641
|
+
if (humanize) {
|
|
9642
|
+
return humanizeNumber(keyValueCell, locale);
|
|
9643
|
+
}
|
|
9644
|
+
return keyValueCell.formattedValue ?? String(keyValueCell.value ?? "");
|
|
9455
9645
|
}
|
|
9456
9646
|
function getBaselineColor(baseline, baselineMode, keyValue, colorUp, colorDown) {
|
|
9457
9647
|
if (baselineMode === "text" ||
|
|
9648
|
+
baselineMode === "progress" ||
|
|
9458
9649
|
baseline?.type !== CellValueType.number ||
|
|
9459
9650
|
keyValue?.type !== CellValueType.number) {
|
|
9460
9651
|
return undefined;
|
|
@@ -9483,17 +9674,6 @@ function getBaselineArrowDirection(baseline, keyValue, baselineMode) {
|
|
|
9483
9674
|
}
|
|
9484
9675
|
return "neutral";
|
|
9485
9676
|
}
|
|
9486
|
-
function getChartPositionAtCenterOfViewport(getters, chartSize) {
|
|
9487
|
-
const { x, y } = getters.getMainViewportCoordinates();
|
|
9488
|
-
const { scrollX, scrollY } = getters.getActiveSheetScrollInfo();
|
|
9489
|
-
const { width, height } = getters.getVisibleRect(getters.getActiveMainViewport());
|
|
9490
|
-
const position = {
|
|
9491
|
-
x: x + scrollX + Math.max(0, (width - chartSize.width) / 2),
|
|
9492
|
-
y: y + scrollY + Math.max(0, (height - chartSize.height) / 2),
|
|
9493
|
-
}; // Position at the center of the scrollable viewport
|
|
9494
|
-
return position;
|
|
9495
|
-
}
|
|
9496
|
-
|
|
9497
9677
|
function checkKeyValue(definition) {
|
|
9498
9678
|
return definition.keyValue && !rangeReference.test(definition.keyValue)
|
|
9499
9679
|
? "InvalidScorecardKeyValue" /* CommandResult.InvalidScorecardKeyValue */
|
|
@@ -9511,10 +9691,12 @@ let ScorecardChart$1 = class ScorecardChart extends AbstractChart {
|
|
|
9511
9691
|
baseline;
|
|
9512
9692
|
baselineMode;
|
|
9513
9693
|
baselineDescr;
|
|
9694
|
+
progressBar = false;
|
|
9514
9695
|
background;
|
|
9515
9696
|
baselineColorUp;
|
|
9516
9697
|
baselineColorDown;
|
|
9517
9698
|
fontColor;
|
|
9699
|
+
humanize;
|
|
9518
9700
|
type = "scorecard";
|
|
9519
9701
|
constructor(definition, sheetId, getters) {
|
|
9520
9702
|
super(definition, sheetId, getters);
|
|
@@ -9525,6 +9707,7 @@ let ScorecardChart$1 = class ScorecardChart extends AbstractChart {
|
|
|
9525
9707
|
this.background = definition.background;
|
|
9526
9708
|
this.baselineColorUp = definition.baselineColorUp;
|
|
9527
9709
|
this.baselineColorDown = definition.baselineColorDown;
|
|
9710
|
+
this.humanize = definition.humanize ?? false;
|
|
9528
9711
|
}
|
|
9529
9712
|
static validateChartDefinition(validator, definition) {
|
|
9530
9713
|
return validator.checkValidations(definition, checkKeyValue, checkBaseline);
|
|
@@ -9594,6 +9777,7 @@ let ScorecardChart$1 = class ScorecardChart extends AbstractChart {
|
|
|
9594
9777
|
keyValue: keyValue
|
|
9595
9778
|
? this.getters.getRangeString(keyValue, targetSheetId || this.sheetId)
|
|
9596
9779
|
: undefined,
|
|
9780
|
+
humanize: this.humanize,
|
|
9597
9781
|
};
|
|
9598
9782
|
}
|
|
9599
9783
|
getDefinitionForExcel() {
|
|
@@ -9619,7 +9803,7 @@ function drawScoreChart(structure, canvas) {
|
|
|
9619
9803
|
if (structure.title) {
|
|
9620
9804
|
ctx.font = structure.title.style.font;
|
|
9621
9805
|
ctx.fillStyle = structure.title.style.color;
|
|
9622
|
-
ctx.fillText(structure.title.text, structure.title.position.x, structure.title.position.y);
|
|
9806
|
+
ctx.fillText(clipTextWithEllipsis(ctx, structure.title.text, canvas.width - structure.title.position.x), structure.title.position.x, structure.title.position.y);
|
|
9623
9807
|
}
|
|
9624
9808
|
if (structure.baseline) {
|
|
9625
9809
|
ctx.font = structure.baseline.style.font;
|
|
@@ -9646,20 +9830,41 @@ function drawScoreChart(structure, canvas) {
|
|
|
9646
9830
|
ctx.restore();
|
|
9647
9831
|
}
|
|
9648
9832
|
if (structure.baselineDescr) {
|
|
9649
|
-
|
|
9650
|
-
ctx.
|
|
9651
|
-
ctx.
|
|
9833
|
+
const descr = structure.baselineDescr[0];
|
|
9834
|
+
ctx.font = descr.style.font;
|
|
9835
|
+
ctx.fillStyle = descr.style.color;
|
|
9836
|
+
for (const description of structure.baselineDescr) {
|
|
9837
|
+
ctx.fillText(clipTextWithEllipsis(ctx, description.text, canvas.width - description.position.x), description.position.x, description.position.y);
|
|
9838
|
+
}
|
|
9652
9839
|
}
|
|
9653
9840
|
if (structure.key) {
|
|
9654
9841
|
ctx.font = structure.key.style.font;
|
|
9655
9842
|
ctx.fillStyle = structure.key.style.color;
|
|
9656
9843
|
drawDecoratedText(ctx, structure.key.text, structure.key.position, structure.key.style.underline, structure.key.style.strikethrough);
|
|
9657
9844
|
}
|
|
9845
|
+
if (structure.progressBar) {
|
|
9846
|
+
ctx.fillStyle = structure.progressBar.style.backgroundColor;
|
|
9847
|
+
ctx.beginPath();
|
|
9848
|
+
ctx.roundRect(structure.progressBar.position.x, structure.progressBar.position.y, structure.progressBar.dimension.width, structure.progressBar.dimension.height, structure.progressBar.dimension.height / 2);
|
|
9849
|
+
ctx.fill();
|
|
9850
|
+
ctx.fillStyle = structure.progressBar.style.color;
|
|
9851
|
+
ctx.beginPath();
|
|
9852
|
+
if (structure.progressBar.value > 0) {
|
|
9853
|
+
ctx.roundRect(structure.progressBar.position.x, structure.progressBar.position.y, structure.progressBar.dimension.width *
|
|
9854
|
+
Math.max(0, Math.min(1.0, structure.progressBar.value)), structure.progressBar.dimension.height, structure.progressBar.dimension.height / 2);
|
|
9855
|
+
}
|
|
9856
|
+
else {
|
|
9857
|
+
const width = structure.progressBar.dimension.width *
|
|
9858
|
+
Math.max(0, Math.min(1.0, -structure.progressBar.value));
|
|
9859
|
+
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);
|
|
9860
|
+
}
|
|
9861
|
+
ctx.fill();
|
|
9862
|
+
}
|
|
9658
9863
|
}
|
|
9659
9864
|
function createScorecardChartRuntime(chart, getters) {
|
|
9660
|
-
let keyValue = "";
|
|
9661
9865
|
let formattedKeyValue = "";
|
|
9662
9866
|
let keyValueCell;
|
|
9867
|
+
const locale = getters.getLocale();
|
|
9663
9868
|
if (chart.keyValue) {
|
|
9664
9869
|
const keyValuePosition = {
|
|
9665
9870
|
sheetId: chart.keyValue.sheetId,
|
|
@@ -9667,31 +9872,33 @@ function createScorecardChartRuntime(chart, getters) {
|
|
|
9667
9872
|
row: chart.keyValue.zone.top,
|
|
9668
9873
|
};
|
|
9669
9874
|
keyValueCell = getters.getEvaluatedCell(keyValuePosition);
|
|
9670
|
-
|
|
9671
|
-
formattedKeyValue = keyValueCell.formattedValue;
|
|
9875
|
+
formattedKeyValue = getKeyValueText(keyValueCell, chart.humanize ?? false, locale);
|
|
9672
9876
|
}
|
|
9673
9877
|
let baselineCell;
|
|
9674
9878
|
const baseline = chart.baseline;
|
|
9675
9879
|
if (baseline) {
|
|
9676
9880
|
const baselinePosition = {
|
|
9677
|
-
sheetId:
|
|
9678
|
-
col:
|
|
9679
|
-
row:
|
|
9881
|
+
sheetId: baseline.sheetId,
|
|
9882
|
+
col: baseline.zone.left,
|
|
9883
|
+
row: baseline.zone.top,
|
|
9680
9884
|
};
|
|
9681
9885
|
baselineCell = getters.getEvaluatedCell(baselinePosition);
|
|
9682
9886
|
}
|
|
9683
9887
|
const { background, fontColor } = getters.getStyleOfSingleCellChart(chart.background, chart.keyValue);
|
|
9684
|
-
const
|
|
9888
|
+
const baselineDisplay = getBaselineText(baselineCell, keyValueCell, chart.baselineMode, chart.humanize ?? false, locale);
|
|
9889
|
+
const baselineValue = chart.baselineMode === "progress" && isNumber(baselineDisplay, locale)
|
|
9890
|
+
? toNumber(baselineDisplay, locale)
|
|
9891
|
+
: 0;
|
|
9685
9892
|
return {
|
|
9686
9893
|
title: _t(chart.title),
|
|
9687
|
-
keyValue: formattedKeyValue
|
|
9688
|
-
baselineDisplay
|
|
9894
|
+
keyValue: formattedKeyValue,
|
|
9895
|
+
baselineDisplay,
|
|
9689
9896
|
baselineArrow: getBaselineArrowDirection(baselineCell, keyValueCell, chart.baselineMode),
|
|
9690
9897
|
baselineColor: getBaselineColor(baselineCell, chart.baselineMode, keyValueCell, chart.baselineColorUp, chart.baselineColorDown),
|
|
9691
|
-
baselineDescr: chart.baselineDescr ? _t(chart.baselineDescr) : "",
|
|
9898
|
+
baselineDescr: chart.baselineMode !== "progress" && chart.baselineDescr ? _t(chart.baselineDescr) : "",
|
|
9692
9899
|
fontColor,
|
|
9693
9900
|
background,
|
|
9694
|
-
baselineStyle: chart.baselineMode !== "percentage" && baseline
|
|
9901
|
+
baselineStyle: chart.baselineMode !== "percentage" && chart.baselineMode !== "progress" && baseline
|
|
9695
9902
|
? getters.getCellStyle({
|
|
9696
9903
|
sheetId: baseline.sheetId,
|
|
9697
9904
|
col: baseline.zone.left,
|
|
@@ -9705,17 +9912,21 @@ function createScorecardChartRuntime(chart, getters) {
|
|
|
9705
9912
|
row: chart.keyValue.zone.top,
|
|
9706
9913
|
})
|
|
9707
9914
|
: undefined,
|
|
9915
|
+
progressBar: chart.baselineMode === "progress"
|
|
9916
|
+
? {
|
|
9917
|
+
value: baselineValue,
|
|
9918
|
+
color: baselineValue > 0 ? chart.baselineColorUp : chart.baselineColorDown,
|
|
9919
|
+
}
|
|
9920
|
+
: undefined,
|
|
9708
9921
|
};
|
|
9709
9922
|
}
|
|
9710
9923
|
|
|
9711
9924
|
/* Sizes of boxes containing the texts, in percentage of the Chart size */
|
|
9712
9925
|
const TITLE_FONT_SIZE = 18;
|
|
9713
|
-
const
|
|
9714
|
-
|
|
9715
|
-
|
|
9716
|
-
const
|
|
9717
|
-
/* Padding at the border of the chart, in percentage of the chart width */
|
|
9718
|
-
const CHART_PADDING_RATIO = 0.02;
|
|
9926
|
+
const KEY_BOX_HEIGHT_RATIO = 0.8;
|
|
9927
|
+
/* Padding at the border of the chart */
|
|
9928
|
+
const CHART_PADDING = 10;
|
|
9929
|
+
const BOTTOM_PADDING_RATIO = 0.05;
|
|
9719
9930
|
/**
|
|
9720
9931
|
* Line height (in em)
|
|
9721
9932
|
* Having a line heigh =1em (=font size) don't work, the font will overflow.
|
|
@@ -9755,31 +9966,37 @@ class ScorecardChartConfigBuilder {
|
|
|
9755
9966
|
},
|
|
9756
9967
|
};
|
|
9757
9968
|
const style = this.getTextStyles();
|
|
9758
|
-
|
|
9969
|
+
let titleHeight = 0;
|
|
9759
9970
|
if (this.title) {
|
|
9971
|
+
({ height: titleHeight } = this.getFullTextDimensions(this.title, style.title.font));
|
|
9760
9972
|
structure.title = {
|
|
9761
9973
|
text: this.title,
|
|
9762
9974
|
style: style.title,
|
|
9763
9975
|
position: {
|
|
9764
|
-
x:
|
|
9765
|
-
y:
|
|
9976
|
+
x: CHART_PADDING,
|
|
9977
|
+
y: CHART_PADDING / 2 + titleHeight,
|
|
9766
9978
|
},
|
|
9767
9979
|
};
|
|
9768
9980
|
}
|
|
9769
9981
|
const baselineArrowSize = style.baselineArrow?.size ?? 0;
|
|
9770
|
-
|
|
9771
|
-
|
|
9982
|
+
let { height: baselineHeight, width: baselineWidth } = this.getTextDimensions(this.baseline, style.baselineValue.font);
|
|
9983
|
+
if (!this.baseline) {
|
|
9984
|
+
baselineHeight = this.getTextDimensions(this.baselineDescr, style.baselineDescr.font).height;
|
|
9985
|
+
}
|
|
9986
|
+
const baselineDescrWidth = style.baselineDescr.isSplit
|
|
9987
|
+
? Math.max(...splitTextInTwoLines(this.baselineDescr).map((line) => this.getTextDimensions(line, style.baselineDescr.font).width))
|
|
9988
|
+
: this.getTextDimensions(this.baselineDescr, style.baselineDescr.font).width;
|
|
9772
9989
|
structure.baseline = {
|
|
9773
9990
|
text: this.baseline,
|
|
9774
9991
|
style: style.baselineValue,
|
|
9775
9992
|
position: {
|
|
9776
9993
|
x: (this.width - baselineWidth - baselineDescrWidth + baselineArrowSize) / 2,
|
|
9777
9994
|
y: this.keyValue
|
|
9778
|
-
? this.height -
|
|
9779
|
-
: this.height - (this.height - titleHeight - baselineHeight) / 2 -
|
|
9995
|
+
? this.height * (1 - BOTTOM_PADDING_RATIO * (this.runtime.progressBar ? 1 : 2))
|
|
9996
|
+
: this.height - (this.height - titleHeight - baselineHeight) / 2 - CHART_PADDING,
|
|
9780
9997
|
},
|
|
9781
9998
|
};
|
|
9782
|
-
if (style.baselineArrow) {
|
|
9999
|
+
if (style.baselineArrow && !this.runtime.progressBar) {
|
|
9783
10000
|
structure.baselineArrow = {
|
|
9784
10001
|
direction: this.baselineArrow,
|
|
9785
10002
|
style: style.baselineArrow,
|
|
@@ -9790,23 +10007,68 @@ class ScorecardChartConfigBuilder {
|
|
|
9790
10007
|
};
|
|
9791
10008
|
}
|
|
9792
10009
|
if (this.baselineDescr) {
|
|
9793
|
-
|
|
9794
|
-
|
|
9795
|
-
|
|
10010
|
+
const position = {
|
|
10011
|
+
x: structure.baseline.position.x + baselineWidth,
|
|
10012
|
+
y: structure.baseline.position.y,
|
|
10013
|
+
};
|
|
10014
|
+
if (style.baselineDescr.isSplit) {
|
|
10015
|
+
const description = splitTextInTwoLines(this.baselineDescr);
|
|
10016
|
+
const measure = this.context.measureText(description[1]);
|
|
10017
|
+
const deltaY = measure.fontBoundingBoxAscent + measure.fontBoundingBoxDescent;
|
|
10018
|
+
structure.baselineDescr = [
|
|
10019
|
+
{
|
|
10020
|
+
text: description[0],
|
|
10021
|
+
style: style.baselineDescr,
|
|
10022
|
+
position: {
|
|
10023
|
+
x: position.x,
|
|
10024
|
+
y: position.y - deltaY,
|
|
10025
|
+
},
|
|
10026
|
+
},
|
|
10027
|
+
{
|
|
10028
|
+
text: description[1],
|
|
10029
|
+
style: style.baselineDescr,
|
|
10030
|
+
position,
|
|
10031
|
+
},
|
|
10032
|
+
];
|
|
10033
|
+
}
|
|
10034
|
+
else {
|
|
10035
|
+
structure.baselineDescr = [
|
|
10036
|
+
{
|
|
10037
|
+
text: this.baselineDescr,
|
|
10038
|
+
style: style.baselineDescr,
|
|
10039
|
+
position,
|
|
10040
|
+
},
|
|
10041
|
+
];
|
|
10042
|
+
}
|
|
10043
|
+
}
|
|
10044
|
+
let progressBarHeight = 0;
|
|
10045
|
+
if (this.runtime.progressBar) {
|
|
10046
|
+
progressBarHeight = this.height * 0.05;
|
|
10047
|
+
structure.progressBar = {
|
|
9796
10048
|
position: {
|
|
9797
|
-
x:
|
|
9798
|
-
y:
|
|
10049
|
+
x: 2 * CHART_PADDING,
|
|
10050
|
+
y: this.height * (1 - 2 * BOTTOM_PADDING_RATIO) - baselineHeight - progressBarHeight,
|
|
10051
|
+
},
|
|
10052
|
+
dimension: {
|
|
10053
|
+
height: progressBarHeight,
|
|
10054
|
+
width: this.width - 4 * CHART_PADDING,
|
|
10055
|
+
},
|
|
10056
|
+
value: this.runtime.progressBar.value,
|
|
10057
|
+
style: {
|
|
10058
|
+
color: this.runtime.progressBar.color,
|
|
10059
|
+
backgroundColor: this.secondaryFontColor,
|
|
9799
10060
|
},
|
|
9800
10061
|
};
|
|
9801
10062
|
}
|
|
9802
|
-
const {
|
|
10063
|
+
const { width: keyWidth, height: keyHeight } = this.getFullTextDimensions(this.keyValue, style.keyValue.font);
|
|
9803
10064
|
if (this.keyValue) {
|
|
9804
10065
|
structure.key = {
|
|
9805
10066
|
text: this.keyValue,
|
|
9806
10067
|
style: style.keyValue,
|
|
9807
10068
|
position: {
|
|
9808
10069
|
x: (this.width - keyWidth) / 2,
|
|
9809
|
-
y:
|
|
10070
|
+
y: this.height * (0.5 - BOTTOM_PADDING_RATIO * 2) +
|
|
10071
|
+
(titleHeight + keyHeight / (this.baseline || this.baselineDescr ? 2 : 1.2)) / 2,
|
|
9810
10072
|
},
|
|
9811
10073
|
};
|
|
9812
10074
|
}
|
|
@@ -9833,9 +10095,6 @@ class ScorecardChartConfigBuilder {
|
|
|
9833
10095
|
get secondaryFontColor() {
|
|
9834
10096
|
return relativeLuminance(this.backgroundColor) > 0.3 ? "#525252" : "#C8C8C8";
|
|
9835
10097
|
}
|
|
9836
|
-
get chartPadding() {
|
|
9837
|
-
return this.width * CHART_PADDING_RATIO;
|
|
9838
|
-
}
|
|
9839
10098
|
getTextDimensions(text, font) {
|
|
9840
10099
|
this.context.font = font;
|
|
9841
10100
|
const measure = this.context.measureText(text);
|
|
@@ -9844,16 +10103,44 @@ class ScorecardChartConfigBuilder {
|
|
|
9844
10103
|
height: measure.actualBoundingBoxAscent + measure.actualBoundingBoxDescent,
|
|
9845
10104
|
};
|
|
9846
10105
|
}
|
|
10106
|
+
getFullTextDimensions(text, font) {
|
|
10107
|
+
this.context.font = font;
|
|
10108
|
+
const measure = this.context.measureText(text);
|
|
10109
|
+
return {
|
|
10110
|
+
width: measure.width,
|
|
10111
|
+
height: measure.fontBoundingBoxAscent + measure.fontBoundingBoxDescent,
|
|
10112
|
+
};
|
|
10113
|
+
}
|
|
9847
10114
|
getTextStyles() {
|
|
9848
10115
|
// If the widest text overflows horizontally, scale it down, and apply the same scaling factors to all the other fonts.
|
|
9849
|
-
const maxLineWidth = this.width
|
|
9850
|
-
const
|
|
9851
|
-
const baseFontSize = widestElement.getElementMaxFontSize(this.getDrawableHeight(), this);
|
|
9852
|
-
const fontSizeMatchingWidth = getFontSizeMatchingWidth(maxLineWidth, baseFontSize, (fontSize) => widestElement.getElementWidth(fontSize, this.context, this));
|
|
9853
|
-
let scalingFactor = fontSizeMatchingWidth / baseFontSize;
|
|
10116
|
+
const maxLineWidth = this.width - 2 * CHART_PADDING;
|
|
10117
|
+
const drawableHeight = this.getDrawableHeight();
|
|
9854
10118
|
// Fonts sizes in px
|
|
9855
|
-
const
|
|
9856
|
-
const
|
|
10119
|
+
const keyValueElement = new KeyValueElement(this.runtime.keyValueStyle);
|
|
10120
|
+
const heightFont = keyValueElement.getElementMaxFontSize(drawableHeight, this);
|
|
10121
|
+
const widthFont = getFontSizeMatchingWidth(maxLineWidth, 600, (fontSize) => keyValueElement.getElementWidth(fontSize, this.context, this));
|
|
10122
|
+
const keyFontSize = Math.min(heightFont, widthFont);
|
|
10123
|
+
let baselineValueFontSize = Math.floor(keyFontSize * 0.5);
|
|
10124
|
+
this.context.font = getDefaultContextFont(baselineValueFontSize, this.runtime.baselineStyle?.bold, this.runtime.baselineStyle?.italic);
|
|
10125
|
+
const baselineText = this.baselineArrow !== "neutral" ? "A " + this.baseline : this.baseline;
|
|
10126
|
+
const baselineValueWidth = computeCachedTextWidth(this.context, baselineText);
|
|
10127
|
+
const remainingWidth = maxLineWidth - baselineValueWidth;
|
|
10128
|
+
let baselineDescrFontSize = getFontSizeMatchingWidth(remainingWidth, baselineValueFontSize, (fontSize) => computeTextWidth(this.context, this.baselineDescr, { fontSize }));
|
|
10129
|
+
let isBaselineSplit = false;
|
|
10130
|
+
if (baselineDescrFontSize < baselineValueFontSize / 2.5) {
|
|
10131
|
+
isBaselineSplit = true;
|
|
10132
|
+
baselineDescrFontSize = Math.floor(baselineValueFontSize / 2.5);
|
|
10133
|
+
for (const line of splitTextInTwoLines(this.baselineDescr)) {
|
|
10134
|
+
const lineWidth = getFontSizeMatchingWidth(remainingWidth, baselineValueFontSize, (fontSize) => {
|
|
10135
|
+
this.context.font = getDefaultContextFont(fontSize);
|
|
10136
|
+
return this.context.measureText(line).width;
|
|
10137
|
+
});
|
|
10138
|
+
baselineDescrFontSize = Math.min(baselineDescrFontSize, lineWidth);
|
|
10139
|
+
}
|
|
10140
|
+
}
|
|
10141
|
+
if (this.runtime.progressBar) {
|
|
10142
|
+
baselineValueFontSize /= 1.5;
|
|
10143
|
+
}
|
|
9857
10144
|
return {
|
|
9858
10145
|
title: {
|
|
9859
10146
|
font: getDefaultContextFont(TITLE_FONT_SIZE),
|
|
@@ -9866,7 +10153,7 @@ class ScorecardChartConfigBuilder {
|
|
|
9866
10153
|
underline: this.runtime.keyValueStyle?.underline,
|
|
9867
10154
|
},
|
|
9868
10155
|
baselineValue: {
|
|
9869
|
-
font: getDefaultContextFont(
|
|
10156
|
+
font: getDefaultContextFont(baselineValueFontSize, this.runtime.baselineStyle?.bold, this.runtime.baselineStyle?.italic),
|
|
9870
10157
|
strikethrough: this.runtime.baselineStyle?.strikethrough,
|
|
9871
10158
|
underline: this.runtime.baselineStyle?.underline,
|
|
9872
10159
|
color: this.runtime.baselineStyle?.textColor ||
|
|
@@ -9874,33 +10161,25 @@ class ScorecardChartConfigBuilder {
|
|
|
9874
10161
|
this.secondaryFontColor,
|
|
9875
10162
|
},
|
|
9876
10163
|
baselineDescr: {
|
|
9877
|
-
font: getDefaultContextFont(
|
|
10164
|
+
font: getDefaultContextFont(baselineDescrFontSize),
|
|
10165
|
+
isSplit: isBaselineSplit,
|
|
9878
10166
|
color: this.secondaryFontColor,
|
|
9879
10167
|
},
|
|
9880
|
-
baselineArrow: this.baselineArrow === "neutral"
|
|
10168
|
+
baselineArrow: this.baselineArrow === "neutral" || this.runtime.progressBar
|
|
9881
10169
|
? undefined
|
|
9882
10170
|
: {
|
|
9883
|
-
size: this.keyValue ? 0.8 *
|
|
10171
|
+
size: this.keyValue ? 0.8 * baselineValueFontSize : 0,
|
|
9884
10172
|
color: this.runtime.baselineColor || this.secondaryFontColor,
|
|
9885
10173
|
},
|
|
9886
10174
|
};
|
|
9887
10175
|
}
|
|
9888
10176
|
/** Get the height of the chart minus all the vertical paddings */
|
|
9889
10177
|
getDrawableHeight() {
|
|
9890
|
-
const verticalPadding =
|
|
9891
|
-
let availableHeight = this.height - verticalPadding;
|
|
10178
|
+
const verticalPadding = CHART_PADDING + this.height * BOTTOM_PADDING_RATIO;
|
|
10179
|
+
let availableHeight = this.height - 2 * verticalPadding;
|
|
9892
10180
|
availableHeight -= this.title ? TITLE_FONT_SIZE * LINE_HEIGHT : 0;
|
|
9893
10181
|
return availableHeight;
|
|
9894
10182
|
}
|
|
9895
|
-
/** Return the element with he widest text in the chart */
|
|
9896
|
-
getWidestElement() {
|
|
9897
|
-
const baseline = new BaselineElement(this.runtime.baselineStyle);
|
|
9898
|
-
const keyValue = new KeyValueElement(this.runtime.keyValueStyle);
|
|
9899
|
-
return baseline.getElementWidth(BASELINE_BOX_HEIGHT_RATIO, this.context, this) >
|
|
9900
|
-
keyValue.getElementWidth(KEY_BOX_HEIGHT_RATIO, this.context, this)
|
|
9901
|
-
? baseline
|
|
9902
|
-
: keyValue;
|
|
9903
|
-
}
|
|
9904
10183
|
}
|
|
9905
10184
|
class ScorecardScalableElement {
|
|
9906
10185
|
style;
|
|
@@ -9909,29 +10188,7 @@ class ScorecardScalableElement {
|
|
|
9909
10188
|
}
|
|
9910
10189
|
measureTextWidth(ctx, text, fontSize) {
|
|
9911
10190
|
ctx.font = getDefaultContextFont(fontSize, this.style.bold, this.style.italic);
|
|
9912
|
-
return ctx
|
|
9913
|
-
}
|
|
9914
|
-
}
|
|
9915
|
-
class BaselineElement extends ScorecardScalableElement {
|
|
9916
|
-
getElementWidth(fontSize, ctx, chart) {
|
|
9917
|
-
if (!chart.runtime) {
|
|
9918
|
-
return 0;
|
|
9919
|
-
}
|
|
9920
|
-
const baselineStr = chart.baseline;
|
|
9921
|
-
// Put mock text to simulate the width of the up/down arrow
|
|
9922
|
-
const largeText = chart.baselineArrow !== "neutral" ? "A " + baselineStr : baselineStr;
|
|
9923
|
-
let textWidth = this.measureTextWidth(ctx, largeText, fontSize);
|
|
9924
|
-
// Baseline descr font size should be smaller than baseline font size
|
|
9925
|
-
textWidth += this.measureTextWidth(ctx, chart.baselineDescr, fontSize * BASELINE_DESCR_FONT_RATIO);
|
|
9926
|
-
return textWidth;
|
|
9927
|
-
}
|
|
9928
|
-
getElementMaxFontSize(availableHeight, chart) {
|
|
9929
|
-
if (!chart.runtime) {
|
|
9930
|
-
return 0;
|
|
9931
|
-
}
|
|
9932
|
-
const haveBaseline = chart.baseline !== "" || chart.baselineDescr;
|
|
9933
|
-
const maxHeight = haveBaseline ? BASELINE_BOX_HEIGHT_RATIO * availableHeight : 0;
|
|
9934
|
-
return maxHeight / LINE_HEIGHT;
|
|
10191
|
+
return computeCachedTextWidth(ctx, text);
|
|
9935
10192
|
}
|
|
9936
10193
|
}
|
|
9937
10194
|
class KeyValueElement extends ScorecardScalableElement {
|
|
@@ -10861,33 +11118,6 @@ var array = /*#__PURE__*/Object.freeze({
|
|
|
10861
11118
|
// -----------------------------------------------------------------------------
|
|
10862
11119
|
// FORMAT.LARGE.NUMBER
|
|
10863
11120
|
// -----------------------------------------------------------------------------
|
|
10864
|
-
function formatLargeNumber(arg, unit, locale) {
|
|
10865
|
-
const value = Math.abs(toNumber(arg?.value, locale));
|
|
10866
|
-
const format = arg?.format;
|
|
10867
|
-
if (unit !== undefined) {
|
|
10868
|
-
const postFix = unit?.value;
|
|
10869
|
-
switch (postFix) {
|
|
10870
|
-
case "k":
|
|
10871
|
-
return createLargeNumberFormat(format, 1e3, "k");
|
|
10872
|
-
case "m":
|
|
10873
|
-
return createLargeNumberFormat(format, 1e6, "m");
|
|
10874
|
-
case "b":
|
|
10875
|
-
return createLargeNumberFormat(format, 1e9, "b");
|
|
10876
|
-
default:
|
|
10877
|
-
throw new EvaluationError(_t("The formatting unit should be 'k', 'm' or 'b'."));
|
|
10878
|
-
}
|
|
10879
|
-
}
|
|
10880
|
-
if (value < 1e5) {
|
|
10881
|
-
return createLargeNumberFormat(format, 0, "");
|
|
10882
|
-
}
|
|
10883
|
-
else if (value < 1e8) {
|
|
10884
|
-
return createLargeNumberFormat(format, 1e3, "k");
|
|
10885
|
-
}
|
|
10886
|
-
else if (value < 1e11) {
|
|
10887
|
-
return createLargeNumberFormat(format, 1e6, "m");
|
|
10888
|
-
}
|
|
10889
|
-
return createLargeNumberFormat(format, 1e9, "b");
|
|
10890
|
-
}
|
|
10891
11121
|
const FORMAT_LARGE_NUMBER = {
|
|
10892
11122
|
description: _t("Apply a large number format"),
|
|
10893
11123
|
args: [
|
|
@@ -11049,7 +11279,7 @@ const ATAN2 = {
|
|
|
11049
11279
|
compute: function (x, y) {
|
|
11050
11280
|
const _x = toNumber(x, this.locale);
|
|
11051
11281
|
const _y = toNumber(y, this.locale);
|
|
11052
|
-
assert(() => _x !== 0 || _y !== 0, _t("Function [[FUNCTION_NAME]] caused a divide by zero error."));
|
|
11282
|
+
assert(() => _x !== 0 || _y !== 0, _t("Function [[FUNCTION_NAME]] caused a divide by zero error."), CellErrorType.DivisionByZero);
|
|
11053
11283
|
return Math.atan2(_y, _x);
|
|
11054
11284
|
},
|
|
11055
11285
|
isExported: true,
|
|
@@ -11179,7 +11409,7 @@ const COT = {
|
|
|
11179
11409
|
returns: ["NUMBER"],
|
|
11180
11410
|
compute: function (angle) {
|
|
11181
11411
|
const _angle = toNumber(angle, this.locale);
|
|
11182
|
-
|
|
11412
|
+
assertNotZero(_angle);
|
|
11183
11413
|
return 1 / Math.tan(_angle);
|
|
11184
11414
|
},
|
|
11185
11415
|
isExported: true,
|
|
@@ -11193,7 +11423,7 @@ const COTH = {
|
|
|
11193
11423
|
returns: ["NUMBER"],
|
|
11194
11424
|
compute: function (value) {
|
|
11195
11425
|
const _value = toNumber(value, this.locale);
|
|
11196
|
-
|
|
11426
|
+
assertNotZero(_value);
|
|
11197
11427
|
return 1 / Math.tanh(_value);
|
|
11198
11428
|
},
|
|
11199
11429
|
isExported: true,
|
|
@@ -11321,7 +11551,7 @@ const CSC = {
|
|
|
11321
11551
|
returns: ["NUMBER"],
|
|
11322
11552
|
compute: function (angle) {
|
|
11323
11553
|
const _angle = toNumber(angle, this.locale);
|
|
11324
|
-
|
|
11554
|
+
assertNotZero(_angle);
|
|
11325
11555
|
return 1 / Math.sin(_angle);
|
|
11326
11556
|
},
|
|
11327
11557
|
isExported: true,
|
|
@@ -11335,7 +11565,7 @@ const CSCH = {
|
|
|
11335
11565
|
returns: ["NUMBER"],
|
|
11336
11566
|
compute: function (value) {
|
|
11337
11567
|
const _value = toNumber(value, this.locale);
|
|
11338
|
-
|
|
11568
|
+
assertNotZero(_value);
|
|
11339
11569
|
return 1 / Math.sinh(_value);
|
|
11340
11570
|
},
|
|
11341
11571
|
isExported: true,
|
|
@@ -11534,7 +11764,7 @@ const LN = {
|
|
|
11534
11764
|
// MOD
|
|
11535
11765
|
// -----------------------------------------------------------------------------
|
|
11536
11766
|
function mod(dividend, divisor) {
|
|
11537
|
-
assert(() => divisor !== 0, _t("The divisor must be different from 0."));
|
|
11767
|
+
assert(() => divisor !== 0, _t("The divisor must be different from 0."), CellErrorType.DivisionByZero);
|
|
11538
11768
|
const modulus = dividend % divisor;
|
|
11539
11769
|
// -42 % 10 = -2 but we want 8, so need the code below
|
|
11540
11770
|
if ((modulus > 0 && divisor < 0) || (modulus < 0 && divisor > 0)) {
|
|
@@ -12103,7 +12333,7 @@ function average(values, locale) {
|
|
|
12103
12333
|
count += 1;
|
|
12104
12334
|
return acc + a;
|
|
12105
12335
|
}, 0, locale);
|
|
12106
|
-
|
|
12336
|
+
assertNotZero(count);
|
|
12107
12337
|
return sum / count;
|
|
12108
12338
|
}
|
|
12109
12339
|
function countNumbers(values, locale) {
|
|
@@ -12170,7 +12400,7 @@ function filterAndFlatData(dataY, dataX) {
|
|
|
12170
12400
|
function covariance(dataY, dataX, isSample) {
|
|
12171
12401
|
const { flatDataX, flatDataY } = filterAndFlatData(dataY, dataX);
|
|
12172
12402
|
const count = flatDataY.length;
|
|
12173
|
-
assert(() => count !== 0 && (!isSample || count !== 1), _t("Evaluation of function [[FUNCTION_NAME]] caused a divide by zero error."));
|
|
12403
|
+
assert(() => count !== 0 && (!isSample || count !== 1), _t("Evaluation of function [[FUNCTION_NAME]] caused a divide by zero error."), CellErrorType.DivisionByZero);
|
|
12174
12404
|
let sumY = 0;
|
|
12175
12405
|
let sumX = 0;
|
|
12176
12406
|
for (let i = 0; i < count; i++) {
|
|
@@ -12193,7 +12423,7 @@ function variance(args, isSample, textAs0, locale) {
|
|
|
12193
12423
|
count += 1;
|
|
12194
12424
|
return acc + a;
|
|
12195
12425
|
}, 0, locale);
|
|
12196
|
-
assert(() => count !== 0 && (!isSample || count !== 1), _t("Evaluation of function [[FUNCTION_NAME]] caused a divide by zero error."));
|
|
12426
|
+
assert(() => count !== 0 && (!isSample || count !== 1), _t("Evaluation of function [[FUNCTION_NAME]] caused a divide by zero error."), CellErrorType.DivisionByZero);
|
|
12197
12427
|
const average = sum / count;
|
|
12198
12428
|
return (reduceFunction(args, (acc, a) => acc + Math.pow(a - average, 2), 0, locale) /
|
|
12199
12429
|
(count - (isSample ? 1 : 0)));
|
|
@@ -12390,7 +12620,7 @@ const AVEDEV = {
|
|
|
12390
12620
|
count += 1;
|
|
12391
12621
|
return acc + a;
|
|
12392
12622
|
}, 0, this.locale);
|
|
12393
|
-
|
|
12623
|
+
assertNotZero(count);
|
|
12394
12624
|
const average = sum / count;
|
|
12395
12625
|
return reduceNumbers(values, (acc, a) => acc + Math.abs(average - a), 0, this.locale) / count;
|
|
12396
12626
|
},
|
|
@@ -12462,7 +12692,7 @@ const AVERAGE_WEIGHTED = {
|
|
|
12462
12692
|
}
|
|
12463
12693
|
}
|
|
12464
12694
|
}
|
|
12465
|
-
|
|
12695
|
+
assertNotZero(count);
|
|
12466
12696
|
return { value: sum / count, format: inferFormat(args[0]) };
|
|
12467
12697
|
},
|
|
12468
12698
|
};
|
|
@@ -12482,7 +12712,7 @@ const AVERAGEA = {
|
|
|
12482
12712
|
count += 1;
|
|
12483
12713
|
return acc + a;
|
|
12484
12714
|
}, 0, this.locale);
|
|
12485
|
-
|
|
12715
|
+
assertNotZero(count);
|
|
12486
12716
|
return {
|
|
12487
12717
|
value: sum / count,
|
|
12488
12718
|
format: inferFormat(args[0]),
|
|
@@ -12512,7 +12742,7 @@ const AVERAGEIF = {
|
|
|
12512
12742
|
sum += value;
|
|
12513
12743
|
}
|
|
12514
12744
|
}, this.locale);
|
|
12515
|
-
|
|
12745
|
+
assertNotZero(count);
|
|
12516
12746
|
return sum / count;
|
|
12517
12747
|
},
|
|
12518
12748
|
isExported: true,
|
|
@@ -12541,7 +12771,7 @@ const AVERAGEIFS = {
|
|
|
12541
12771
|
sum += value;
|
|
12542
12772
|
}
|
|
12543
12773
|
}, this.locale);
|
|
12544
|
-
|
|
12774
|
+
assertNotZero(count);
|
|
12545
12775
|
return sum / count;
|
|
12546
12776
|
},
|
|
12547
12777
|
isExported: true,
|
|
@@ -17966,7 +18196,7 @@ const DIVIDE = {
|
|
|
17966
18196
|
returns: ["NUMBER"],
|
|
17967
18197
|
compute: function (dividend, divisor) {
|
|
17968
18198
|
const _divisor = toNumber(divisor, this.locale);
|
|
17969
|
-
assert(() => _divisor !== 0, _t("The divisor must be different from zero."));
|
|
18199
|
+
assert(() => _divisor !== 0, _t("The divisor must be different from zero."), CellErrorType.DivisionByZero);
|
|
17970
18200
|
return {
|
|
17971
18201
|
value: toNumber(dividend, this.locale) / _divisor,
|
|
17972
18202
|
format: dividend?.format || divisor?.format,
|
|
@@ -18659,7 +18889,7 @@ class FunctionRegistry extends Registry {
|
|
|
18659
18889
|
}
|
|
18660
18890
|
const descr = addMetaInfoFromArg(addDescr);
|
|
18661
18891
|
validateArguments(descr.args);
|
|
18662
|
-
this.mapping[name] = addErrorHandling(addResultHandling(addInputHandling(descr)), name);
|
|
18892
|
+
this.mapping[name] = addErrorHandling(addResultHandling(addInputHandling(descr), name), name);
|
|
18663
18893
|
super.add(name, descr);
|
|
18664
18894
|
return this;
|
|
18665
18895
|
}
|
|
@@ -18698,9 +18928,7 @@ function handleError(e, functionName) {
|
|
|
18698
18928
|
// so we fallback to a generic error
|
|
18699
18929
|
if (hasStringValue(e) && isEvaluationError(e.value)) {
|
|
18700
18930
|
if (hasStringMessage(e)) {
|
|
18701
|
-
|
|
18702
|
-
e.message = e.message.replace("[[FUNCTION_NAME]]", functionName);
|
|
18703
|
-
}
|
|
18931
|
+
replaceFunctionNamePlaceholder(e, functionName);
|
|
18704
18932
|
}
|
|
18705
18933
|
return e;
|
|
18706
18934
|
}
|
|
@@ -18715,21 +18943,29 @@ function hasStringMessage(obj) {
|
|
|
18715
18943
|
return (obj?.message !== undefined &&
|
|
18716
18944
|
typeof obj.message === "string");
|
|
18717
18945
|
}
|
|
18718
|
-
function addResultHandling(compute) {
|
|
18719
|
-
return function (...args) {
|
|
18946
|
+
function addResultHandling(compute, functionName) {
|
|
18947
|
+
return function computeWithResultHandling(...args) {
|
|
18720
18948
|
const result = compute.apply(this, args);
|
|
18721
18949
|
if (!isMatrix(result)) {
|
|
18722
18950
|
if (typeof result === "object" && result !== null && "value" in result) {
|
|
18951
|
+
replaceFunctionNamePlaceholder(result, functionName);
|
|
18723
18952
|
return result;
|
|
18724
18953
|
}
|
|
18725
18954
|
return { value: result };
|
|
18726
18955
|
}
|
|
18727
18956
|
if (typeof result[0][0] === "object" && result[0][0] !== null && "value" in result[0][0]) {
|
|
18957
|
+
matrixForEach(result, (result) => replaceFunctionNamePlaceholder(result, functionName));
|
|
18728
18958
|
return result;
|
|
18729
18959
|
}
|
|
18730
18960
|
return matrixMap(result, (row) => ({ value: row }));
|
|
18731
18961
|
};
|
|
18732
18962
|
}
|
|
18963
|
+
function replaceFunctionNamePlaceholder(fPayload, functionName) {
|
|
18964
|
+
// for performance reasons: change in place and only if needed
|
|
18965
|
+
if (fPayload.message?.includes("[[FUNCTION_NAME]]")) {
|
|
18966
|
+
fPayload.message = fPayload.message.replace("[[FUNCTION_NAME]]", functionName);
|
|
18967
|
+
}
|
|
18968
|
+
}
|
|
18733
18969
|
const functionRegistry = new FunctionRegistry();
|
|
18734
18970
|
for (let category of categories) {
|
|
18735
18971
|
const fns = category.functions;
|
|
@@ -21011,7 +21247,21 @@ class ScatterChart extends AbstractChart {
|
|
|
21011
21247
|
return new ScatterChart(definition, this.sheetId, this.getters);
|
|
21012
21248
|
}
|
|
21013
21249
|
getDefinitionForExcel() {
|
|
21014
|
-
|
|
21250
|
+
// Excel does not support aggregating labels
|
|
21251
|
+
if (this.aggregated) {
|
|
21252
|
+
return undefined;
|
|
21253
|
+
}
|
|
21254
|
+
const dataSets = this.dataSets
|
|
21255
|
+
.map((ds) => toExcelDataset(this.getters, ds))
|
|
21256
|
+
.filter((ds) => ds.range !== "");
|
|
21257
|
+
const labelRange = toExcelLabelRange(this.getters, this.labelRange, shouldRemoveFirstLabel(this.labelRange, this.dataSets[0], this.dataSetsHaveTitle));
|
|
21258
|
+
return {
|
|
21259
|
+
...this.getDefinition(),
|
|
21260
|
+
backgroundColor: toXlsxHexColor(this.background || BACKGROUND_CHART_COLOR),
|
|
21261
|
+
fontColor: toXlsxHexColor(chartFontColor(this.background)),
|
|
21262
|
+
dataSets,
|
|
21263
|
+
labelRange,
|
|
21264
|
+
};
|
|
21015
21265
|
}
|
|
21016
21266
|
copyForSheetId(sheetId) {
|
|
21017
21267
|
const dataSets = copyDataSetsWithNewSheetId(this.sheetId, sheetId, this.dataSets);
|
|
@@ -21058,6 +21308,263 @@ function createScatterChartRuntime(chart, getters) {
|
|
|
21058
21308
|
return { chartJsConfig, background };
|
|
21059
21309
|
}
|
|
21060
21310
|
|
|
21311
|
+
class WaterfallChart extends AbstractChart {
|
|
21312
|
+
dataSets;
|
|
21313
|
+
labelRange;
|
|
21314
|
+
background;
|
|
21315
|
+
verticalAxisPosition;
|
|
21316
|
+
legendPosition;
|
|
21317
|
+
aggregated;
|
|
21318
|
+
type = "waterfall";
|
|
21319
|
+
dataSetsHaveTitle;
|
|
21320
|
+
showSubTotals;
|
|
21321
|
+
firstValueAsSubtotal;
|
|
21322
|
+
showConnectorLines;
|
|
21323
|
+
positiveValuesColor;
|
|
21324
|
+
negativeValuesColor;
|
|
21325
|
+
subTotalValuesColor;
|
|
21326
|
+
constructor(definition, sheetId, getters) {
|
|
21327
|
+
super(definition, sheetId, getters);
|
|
21328
|
+
this.dataSets = createDataSets(getters, definition.dataSets, sheetId, definition.dataSetsHaveTitle);
|
|
21329
|
+
this.labelRange = createRange(getters, sheetId, definition.labelRange);
|
|
21330
|
+
this.background = definition.background;
|
|
21331
|
+
this.verticalAxisPosition = definition.verticalAxisPosition;
|
|
21332
|
+
this.legendPosition = definition.legendPosition;
|
|
21333
|
+
this.aggregated = definition.aggregated;
|
|
21334
|
+
this.dataSetsHaveTitle = definition.dataSetsHaveTitle;
|
|
21335
|
+
this.showSubTotals = definition.showSubTotals;
|
|
21336
|
+
this.showConnectorLines = definition.showConnectorLines;
|
|
21337
|
+
this.positiveValuesColor = definition.positiveValuesColor;
|
|
21338
|
+
this.negativeValuesColor = definition.negativeValuesColor;
|
|
21339
|
+
this.subTotalValuesColor = definition.subTotalValuesColor;
|
|
21340
|
+
this.firstValueAsSubtotal = definition.firstValueAsSubtotal;
|
|
21341
|
+
}
|
|
21342
|
+
static transformDefinition(definition, executed) {
|
|
21343
|
+
return transformChartDefinitionWithDataSetsWithZone(definition, executed);
|
|
21344
|
+
}
|
|
21345
|
+
static validateChartDefinition(validator, definition) {
|
|
21346
|
+
return validator.checkValidations(definition, checkDataset, checkLabelRange);
|
|
21347
|
+
}
|
|
21348
|
+
static getDefinitionFromContextCreation(context) {
|
|
21349
|
+
return {
|
|
21350
|
+
background: context.background,
|
|
21351
|
+
dataSets: context.range ? context.range : [],
|
|
21352
|
+
dataSetsHaveTitle: false,
|
|
21353
|
+
aggregated: context.aggregated ?? false,
|
|
21354
|
+
legendPosition: "top",
|
|
21355
|
+
title: context.title || "",
|
|
21356
|
+
type: "waterfall",
|
|
21357
|
+
verticalAxisPosition: "left",
|
|
21358
|
+
labelRange: context.auxiliaryRange || undefined,
|
|
21359
|
+
showSubTotals: true,
|
|
21360
|
+
showConnectorLines: true,
|
|
21361
|
+
firstValueAsSubtotal: false,
|
|
21362
|
+
};
|
|
21363
|
+
}
|
|
21364
|
+
getContextCreation() {
|
|
21365
|
+
return {
|
|
21366
|
+
background: this.background,
|
|
21367
|
+
title: this.title,
|
|
21368
|
+
range: this.dataSets.map((ds) => this.getters.getRangeString(ds.dataRange, this.sheetId)),
|
|
21369
|
+
auxiliaryRange: this.labelRange
|
|
21370
|
+
? this.getters.getRangeString(this.labelRange, this.sheetId)
|
|
21371
|
+
: undefined,
|
|
21372
|
+
aggregated: this.aggregated,
|
|
21373
|
+
};
|
|
21374
|
+
}
|
|
21375
|
+
copyForSheetId(sheetId) {
|
|
21376
|
+
const dataSets = copyDataSetsWithNewSheetId(this.sheetId, sheetId, this.dataSets);
|
|
21377
|
+
const labelRange = copyLabelRangeWithNewSheetId(this.sheetId, sheetId, this.labelRange);
|
|
21378
|
+
const definition = this.getDefinitionWithSpecificDataSets(dataSets, labelRange, sheetId);
|
|
21379
|
+
return new WaterfallChart(definition, sheetId, this.getters);
|
|
21380
|
+
}
|
|
21381
|
+
copyInSheetId(sheetId) {
|
|
21382
|
+
const definition = this.getDefinitionWithSpecificDataSets(this.dataSets, this.labelRange, sheetId);
|
|
21383
|
+
return new WaterfallChart(definition, sheetId, this.getters);
|
|
21384
|
+
}
|
|
21385
|
+
getDefinition() {
|
|
21386
|
+
return this.getDefinitionWithSpecificDataSets(this.dataSets, this.labelRange);
|
|
21387
|
+
}
|
|
21388
|
+
getDefinitionWithSpecificDataSets(dataSets, labelRange, targetSheetId) {
|
|
21389
|
+
return {
|
|
21390
|
+
type: "waterfall",
|
|
21391
|
+
dataSetsHaveTitle: dataSets.length ? Boolean(dataSets[0].labelCell) : false,
|
|
21392
|
+
background: this.background,
|
|
21393
|
+
dataSets: dataSets.map((ds) => this.getters.getRangeString(ds.dataRange, targetSheetId || this.sheetId)),
|
|
21394
|
+
legendPosition: this.legendPosition,
|
|
21395
|
+
verticalAxisPosition: this.verticalAxisPosition,
|
|
21396
|
+
labelRange: labelRange
|
|
21397
|
+
? this.getters.getRangeString(labelRange, targetSheetId || this.sheetId)
|
|
21398
|
+
: undefined,
|
|
21399
|
+
title: this.title,
|
|
21400
|
+
aggregated: this.aggregated,
|
|
21401
|
+
showSubTotals: this.showSubTotals,
|
|
21402
|
+
showConnectorLines: this.showConnectorLines,
|
|
21403
|
+
positiveValuesColor: this.positiveValuesColor,
|
|
21404
|
+
negativeValuesColor: this.negativeValuesColor,
|
|
21405
|
+
subTotalValuesColor: this.subTotalValuesColor,
|
|
21406
|
+
firstValueAsSubtotal: this.firstValueAsSubtotal,
|
|
21407
|
+
};
|
|
21408
|
+
}
|
|
21409
|
+
getDefinitionForExcel() {
|
|
21410
|
+
// TODO: implement export excel
|
|
21411
|
+
return undefined;
|
|
21412
|
+
}
|
|
21413
|
+
updateRanges(applyChange) {
|
|
21414
|
+
const { dataSets, labelRange, isStale } = updateChartRangesWithDataSets(this.getters, applyChange, this.dataSets, this.labelRange);
|
|
21415
|
+
if (!isStale) {
|
|
21416
|
+
return this;
|
|
21417
|
+
}
|
|
21418
|
+
const definition = this.getDefinitionWithSpecificDataSets(dataSets, labelRange);
|
|
21419
|
+
return new WaterfallChart(definition, this.sheetId, this.getters);
|
|
21420
|
+
}
|
|
21421
|
+
}
|
|
21422
|
+
function getWaterfallConfiguration(chart, labels, dataSeriesLabels, localeFormat) {
|
|
21423
|
+
const { locale, format } = localeFormat;
|
|
21424
|
+
const fontColor = chartFontColor(chart.background);
|
|
21425
|
+
const config = getDefaultChartJsRuntime(chart, labels, fontColor, localeFormat);
|
|
21426
|
+
const negativeColor = chart.negativeValuesColor || CHART_WATERFALL_NEGATIVE_COLOR;
|
|
21427
|
+
const positiveColor = chart.positiveValuesColor || CHART_WATERFALL_POSITIVE_COLOR;
|
|
21428
|
+
const subTotalColor = chart.subTotalValuesColor || CHART_WATERFALL_SUBTOTAL_COLOR;
|
|
21429
|
+
const legend = {
|
|
21430
|
+
labels: {
|
|
21431
|
+
generateLabels: () => {
|
|
21432
|
+
const legendValues = [
|
|
21433
|
+
{ text: _t("Positive values"), fontColor, fillStyle: positiveColor },
|
|
21434
|
+
{ text: _t("Negative values"), fontColor, fillStyle: negativeColor },
|
|
21435
|
+
];
|
|
21436
|
+
if (chart.showSubTotals || chart.firstValueAsSubtotal) {
|
|
21437
|
+
legendValues.push({
|
|
21438
|
+
text: _t("Subtotals"),
|
|
21439
|
+
fontColor,
|
|
21440
|
+
fillStyle: subTotalColor,
|
|
21441
|
+
});
|
|
21442
|
+
}
|
|
21443
|
+
return legendValues;
|
|
21444
|
+
},
|
|
21445
|
+
},
|
|
21446
|
+
};
|
|
21447
|
+
if (chart.legendPosition === "none") {
|
|
21448
|
+
legend.display = false;
|
|
21449
|
+
}
|
|
21450
|
+
else {
|
|
21451
|
+
legend.position = chart.legendPosition;
|
|
21452
|
+
}
|
|
21453
|
+
config.options.plugins.legend = { ...config.options.plugins?.legend, ...legend };
|
|
21454
|
+
config.options.layout = {
|
|
21455
|
+
padding: { left: 20, right: 20, top: chart.title ? 10 : 25, bottom: 10 },
|
|
21456
|
+
};
|
|
21457
|
+
config.options.scales = {
|
|
21458
|
+
x: {
|
|
21459
|
+
ticks: {
|
|
21460
|
+
padding: 5,
|
|
21461
|
+
color: fontColor,
|
|
21462
|
+
},
|
|
21463
|
+
grid: {
|
|
21464
|
+
display: false,
|
|
21465
|
+
},
|
|
21466
|
+
},
|
|
21467
|
+
y: {
|
|
21468
|
+
position: chart.verticalAxisPosition,
|
|
21469
|
+
ticks: {
|
|
21470
|
+
color: fontColor,
|
|
21471
|
+
callback: (value) => {
|
|
21472
|
+
value = Number(value);
|
|
21473
|
+
if (isNaN(value))
|
|
21474
|
+
return value;
|
|
21475
|
+
return formatValue(value, {
|
|
21476
|
+
locale,
|
|
21477
|
+
format: !format && Math.abs(value) > 1000 ? "#,##" : format,
|
|
21478
|
+
});
|
|
21479
|
+
},
|
|
21480
|
+
},
|
|
21481
|
+
grid: {
|
|
21482
|
+
lineWidth: (context) => {
|
|
21483
|
+
return context.tick.value === 0 ? 2 : 1;
|
|
21484
|
+
},
|
|
21485
|
+
},
|
|
21486
|
+
},
|
|
21487
|
+
};
|
|
21488
|
+
config.options.plugins.tooltip = {
|
|
21489
|
+
callbacks: {
|
|
21490
|
+
label: function (tooltipItem) {
|
|
21491
|
+
const [lastValue, currentValue] = tooltipItem.raw;
|
|
21492
|
+
const yLabel = currentValue - lastValue;
|
|
21493
|
+
const dataSeriesIndex = Math.floor(tooltipItem.dataIndex / labels.length);
|
|
21494
|
+
const dataSeriesLabel = dataSeriesLabels[dataSeriesIndex];
|
|
21495
|
+
const toolTipFormat = !format && Math.abs(yLabel) > 1000 ? "#,##" : format;
|
|
21496
|
+
const yLabelStr = formatValue(yLabel, { format: toolTipFormat, locale });
|
|
21497
|
+
return dataSeriesLabel ? `${dataSeriesLabel}: ${yLabelStr}` : yLabelStr;
|
|
21498
|
+
},
|
|
21499
|
+
},
|
|
21500
|
+
};
|
|
21501
|
+
config.options.plugins.waterfallLinesPlugin = { showConnectorLines: chart.showConnectorLines };
|
|
21502
|
+
return config;
|
|
21503
|
+
}
|
|
21504
|
+
function createWaterfallChartRuntime(chart, getters) {
|
|
21505
|
+
const labelValues = getChartLabelValues(getters, chart.dataSets, chart.labelRange);
|
|
21506
|
+
let labels = labelValues.formattedValues;
|
|
21507
|
+
let dataSetsValues = getChartDatasetValues(getters, chart.dataSets);
|
|
21508
|
+
if (chart.dataSetsHaveTitle &&
|
|
21509
|
+
dataSetsValues[0] &&
|
|
21510
|
+
labels.length > dataSetsValues[0].data.length) {
|
|
21511
|
+
labels.shift();
|
|
21512
|
+
}
|
|
21513
|
+
({ labels, dataSetsValues } = filterEmptyDataPoints(labels, dataSetsValues));
|
|
21514
|
+
if (chart.aggregated) {
|
|
21515
|
+
({ labels, dataSetsValues } = aggregateDataForLabels(labels, dataSetsValues));
|
|
21516
|
+
}
|
|
21517
|
+
if (chart.showSubTotals) {
|
|
21518
|
+
labels.push(_t("Subtotal"));
|
|
21519
|
+
}
|
|
21520
|
+
const dataSetFormat = getChartDatasetFormat(getters, chart.dataSets);
|
|
21521
|
+
const locale = getters.getLocale();
|
|
21522
|
+
const dataSeriesLabels = dataSetsValues.map((dataSet) => dataSet.label);
|
|
21523
|
+
const config = getWaterfallConfiguration(chart, labels, dataSeriesLabels, {
|
|
21524
|
+
format: dataSetFormat,
|
|
21525
|
+
locale,
|
|
21526
|
+
});
|
|
21527
|
+
config.type = "bar";
|
|
21528
|
+
const negativeColor = chart.negativeValuesColor || CHART_WATERFALL_NEGATIVE_COLOR;
|
|
21529
|
+
const positiveColor = chart.positiveValuesColor || CHART_WATERFALL_POSITIVE_COLOR;
|
|
21530
|
+
const subTotalColor = chart.subTotalValuesColor || CHART_WATERFALL_SUBTOTAL_COLOR;
|
|
21531
|
+
const backgroundColor = [];
|
|
21532
|
+
const datasetValues = [];
|
|
21533
|
+
const dataset = {
|
|
21534
|
+
label: "",
|
|
21535
|
+
data: datasetValues,
|
|
21536
|
+
backgroundColor,
|
|
21537
|
+
};
|
|
21538
|
+
const labelsWithSubTotals = [];
|
|
21539
|
+
let lastValue = 0;
|
|
21540
|
+
for (const dataSetsValue of dataSetsValues) {
|
|
21541
|
+
for (let i = 0; i < dataSetsValue.data.length; i++) {
|
|
21542
|
+
const data = dataSetsValue.data[i];
|
|
21543
|
+
labelsWithSubTotals.push(labels[i]);
|
|
21544
|
+
if (isNaN(Number(data))) {
|
|
21545
|
+
datasetValues.push([lastValue, lastValue]);
|
|
21546
|
+
backgroundColor.push("");
|
|
21547
|
+
continue;
|
|
21548
|
+
}
|
|
21549
|
+
datasetValues.push([lastValue, data + lastValue]);
|
|
21550
|
+
let color = data >= 0 ? positiveColor : negativeColor;
|
|
21551
|
+
if (i === 0 && dataSetsValue === dataSetsValues[0] && chart.firstValueAsSubtotal) {
|
|
21552
|
+
color = subTotalColor;
|
|
21553
|
+
}
|
|
21554
|
+
backgroundColor.push(color);
|
|
21555
|
+
lastValue += data;
|
|
21556
|
+
}
|
|
21557
|
+
if (chart.showSubTotals) {
|
|
21558
|
+
labelsWithSubTotals.push(_t("Subtotal"));
|
|
21559
|
+
datasetValues.push([0, lastValue]);
|
|
21560
|
+
backgroundColor.push(subTotalColor);
|
|
21561
|
+
}
|
|
21562
|
+
}
|
|
21563
|
+
config.data.datasets.push(dataset);
|
|
21564
|
+
config.data.labels = labelsWithSubTotals.map(truncateLabel);
|
|
21565
|
+
return { chartJsConfig: config, background: chart.background || BACKGROUND_CHART_COLOR };
|
|
21566
|
+
}
|
|
21567
|
+
|
|
21061
21568
|
/**
|
|
21062
21569
|
* This registry is intended to map a cell content (raw string) to
|
|
21063
21570
|
* an instance of a cell.
|
|
@@ -21067,9 +21574,9 @@ chartRegistry.add("bar", {
|
|
|
21067
21574
|
match: (type) => type === "bar",
|
|
21068
21575
|
createChart: (definition, sheetId, getters) => new BarChart(definition, sheetId, getters),
|
|
21069
21576
|
getChartRuntime: createBarChartRuntime,
|
|
21070
|
-
validateChartDefinition:
|
|
21071
|
-
transformDefinition:
|
|
21072
|
-
getChartDefinitionFromContextCreation:
|
|
21577
|
+
validateChartDefinition: BarChart.validateChartDefinition,
|
|
21578
|
+
transformDefinition: BarChart.transformDefinition,
|
|
21579
|
+
getChartDefinitionFromContextCreation: BarChart.getDefinitionFromContextCreation,
|
|
21073
21580
|
name: _t("Bar"),
|
|
21074
21581
|
sequence: 10,
|
|
21075
21582
|
});
|
|
@@ -21077,9 +21584,9 @@ chartRegistry.add("combo", {
|
|
|
21077
21584
|
match: (type) => type === "combo",
|
|
21078
21585
|
createChart: (definition, sheetId, getters) => new ComboChart(definition, sheetId, getters),
|
|
21079
21586
|
getChartRuntime: createComboChartRuntime,
|
|
21080
|
-
validateChartDefinition:
|
|
21081
|
-
transformDefinition:
|
|
21082
|
-
getChartDefinitionFromContextCreation:
|
|
21587
|
+
validateChartDefinition: ComboChart.validateChartDefinition,
|
|
21588
|
+
transformDefinition: ComboChart.transformDefinition,
|
|
21589
|
+
getChartDefinitionFromContextCreation: ComboChart.getDefinitionFromContextCreation,
|
|
21083
21590
|
name: _t("Combo"),
|
|
21084
21591
|
sequence: 15,
|
|
21085
21592
|
});
|
|
@@ -21087,9 +21594,9 @@ chartRegistry.add("line", {
|
|
|
21087
21594
|
match: (type) => type === "line",
|
|
21088
21595
|
createChart: (definition, sheetId, getters) => new LineChart(definition, sheetId, getters),
|
|
21089
21596
|
getChartRuntime: createLineChartRuntime,
|
|
21090
|
-
validateChartDefinition:
|
|
21091
|
-
transformDefinition:
|
|
21092
|
-
getChartDefinitionFromContextCreation:
|
|
21597
|
+
validateChartDefinition: LineChart.validateChartDefinition,
|
|
21598
|
+
transformDefinition: LineChart.transformDefinition,
|
|
21599
|
+
getChartDefinitionFromContextCreation: LineChart.getDefinitionFromContextCreation,
|
|
21093
21600
|
name: _t("Line"),
|
|
21094
21601
|
sequence: 20,
|
|
21095
21602
|
});
|
|
@@ -21097,9 +21604,9 @@ chartRegistry.add("pie", {
|
|
|
21097
21604
|
match: (type) => type === "pie",
|
|
21098
21605
|
createChart: (definition, sheetId, getters) => new PieChart(definition, sheetId, getters),
|
|
21099
21606
|
getChartRuntime: createPieChartRuntime,
|
|
21100
|
-
validateChartDefinition:
|
|
21101
|
-
transformDefinition:
|
|
21102
|
-
getChartDefinitionFromContextCreation:
|
|
21607
|
+
validateChartDefinition: PieChart.validateChartDefinition,
|
|
21608
|
+
transformDefinition: PieChart.transformDefinition,
|
|
21609
|
+
getChartDefinitionFromContextCreation: PieChart.getDefinitionFromContextCreation,
|
|
21103
21610
|
name: _t("Pie"),
|
|
21104
21611
|
sequence: 30,
|
|
21105
21612
|
});
|
|
@@ -21107,9 +21614,9 @@ chartRegistry.add("scorecard", {
|
|
|
21107
21614
|
match: (type) => type === "scorecard",
|
|
21108
21615
|
createChart: (definition, sheetId, getters) => new ScorecardChart$1(definition, sheetId, getters),
|
|
21109
21616
|
getChartRuntime: createScorecardChartRuntime,
|
|
21110
|
-
validateChartDefinition:
|
|
21111
|
-
transformDefinition:
|
|
21112
|
-
getChartDefinitionFromContextCreation:
|
|
21617
|
+
validateChartDefinition: ScorecardChart$1.validateChartDefinition,
|
|
21618
|
+
transformDefinition: ScorecardChart$1.transformDefinition,
|
|
21619
|
+
getChartDefinitionFromContextCreation: ScorecardChart$1.getDefinitionFromContextCreation,
|
|
21113
21620
|
name: _t("Scorecard"),
|
|
21114
21621
|
sequence: 40,
|
|
21115
21622
|
});
|
|
@@ -21117,9 +21624,9 @@ chartRegistry.add("gauge", {
|
|
|
21117
21624
|
match: (type) => type === "gauge",
|
|
21118
21625
|
createChart: (definition, sheetId, getters) => new GaugeChart(definition, sheetId, getters),
|
|
21119
21626
|
getChartRuntime: createGaugeChartRuntime,
|
|
21120
|
-
validateChartDefinition:
|
|
21121
|
-
transformDefinition:
|
|
21122
|
-
getChartDefinitionFromContextCreation:
|
|
21627
|
+
validateChartDefinition: GaugeChart.validateChartDefinition,
|
|
21628
|
+
transformDefinition: GaugeChart.transformDefinition,
|
|
21629
|
+
getChartDefinitionFromContextCreation: GaugeChart.getDefinitionFromContextCreation,
|
|
21123
21630
|
name: _t("Gauge"),
|
|
21124
21631
|
sequence: 50,
|
|
21125
21632
|
});
|
|
@@ -21127,12 +21634,22 @@ chartRegistry.add("scatter", {
|
|
|
21127
21634
|
match: (type) => type === "scatter",
|
|
21128
21635
|
createChart: (definition, sheetId, getters) => new ScatterChart(definition, sheetId, getters),
|
|
21129
21636
|
getChartRuntime: createScatterChartRuntime,
|
|
21130
|
-
validateChartDefinition:
|
|
21131
|
-
transformDefinition:
|
|
21132
|
-
getChartDefinitionFromContextCreation:
|
|
21637
|
+
validateChartDefinition: ScatterChart.validateChartDefinition,
|
|
21638
|
+
transformDefinition: ScatterChart.transformDefinition,
|
|
21639
|
+
getChartDefinitionFromContextCreation: ScatterChart.getDefinitionFromContextCreation,
|
|
21133
21640
|
name: _t("Scatter"),
|
|
21134
21641
|
sequence: 60,
|
|
21135
21642
|
});
|
|
21643
|
+
chartRegistry.add("waterfall", {
|
|
21644
|
+
match: (type) => type === "waterfall",
|
|
21645
|
+
createChart: (definition, sheetId, getters) => new WaterfallChart(definition, sheetId, getters),
|
|
21646
|
+
getChartRuntime: createWaterfallChartRuntime,
|
|
21647
|
+
validateChartDefinition: WaterfallChart.validateChartDefinition,
|
|
21648
|
+
transformDefinition: WaterfallChart.transformDefinition,
|
|
21649
|
+
getChartDefinitionFromContextCreation: WaterfallChart.getDefinitionFromContextCreation,
|
|
21650
|
+
name: _t("Waterfall"),
|
|
21651
|
+
sequence: 70,
|
|
21652
|
+
});
|
|
21136
21653
|
const chartComponentRegistry = new Registry();
|
|
21137
21654
|
chartComponentRegistry.add("line", ChartJsComponent);
|
|
21138
21655
|
chartComponentRegistry.add("bar", ChartJsComponent);
|
|
@@ -21141,6 +21658,7 @@ chartComponentRegistry.add("pie", ChartJsComponent);
|
|
|
21141
21658
|
chartComponentRegistry.add("gauge", GaugeChartComponent);
|
|
21142
21659
|
chartComponentRegistry.add("scatter", ChartJsComponent);
|
|
21143
21660
|
chartComponentRegistry.add("scorecard", ScorecardChart);
|
|
21661
|
+
chartComponentRegistry.add("waterfall", ChartJsComponent);
|
|
21144
21662
|
|
|
21145
21663
|
/**
|
|
21146
21664
|
* Registry intended to support usual currencies. It is mainly used to create
|
|
@@ -23120,6 +23638,7 @@ class LinkEditor extends Component {
|
|
|
23120
23638
|
this.save();
|
|
23121
23639
|
}
|
|
23122
23640
|
ev.stopPropagation();
|
|
23641
|
+
ev.preventDefault();
|
|
23123
23642
|
break;
|
|
23124
23643
|
case "Escape":
|
|
23125
23644
|
this.cancel();
|
|
@@ -26773,8 +27292,8 @@ class ChartLabelRange extends Component {
|
|
|
26773
27292
|
};
|
|
26774
27293
|
}
|
|
26775
27294
|
|
|
26776
|
-
class
|
|
26777
|
-
static template = "o-spreadsheet-
|
|
27295
|
+
class GenericChartConfigPanel extends Component {
|
|
27296
|
+
static template = "o-spreadsheet-GenericChartConfigPanel";
|
|
26778
27297
|
static components = {
|
|
26779
27298
|
SelectionInput,
|
|
26780
27299
|
ValidationMessages,
|
|
@@ -26891,7 +27410,7 @@ class LineBarPieConfigPanel extends Component {
|
|
|
26891
27410
|
}
|
|
26892
27411
|
}
|
|
26893
27412
|
|
|
26894
|
-
class BarConfigPanel extends
|
|
27413
|
+
class BarConfigPanel extends GenericChartConfigPanel {
|
|
26895
27414
|
static template = "o-spreadsheet-BarConfigPanel";
|
|
26896
27415
|
get stackedLabel() {
|
|
26897
27416
|
return _t("Stacked barchart");
|
|
@@ -27439,13 +27958,30 @@ class ColorPickerWidget extends Component {
|
|
|
27439
27958
|
}
|
|
27440
27959
|
}
|
|
27441
27960
|
|
|
27442
|
-
|
|
27443
|
-
|
|
27444
|
-
|
|
27961
|
+
const TRANSPARENT_BACKGROUND_SVG = /*xml*/ `
|
|
27962
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="10" height="10">
|
|
27963
|
+
<path fill="#d9d9d9" d="M5 5h5v5H5zH0V0h5"/>
|
|
27964
|
+
</svg>
|
|
27965
|
+
`;
|
|
27966
|
+
css /* scss */ `
|
|
27967
|
+
.o-round-color-picker-button {
|
|
27968
|
+
width: 15px;
|
|
27969
|
+
height: 15px;
|
|
27970
|
+
cursor: pointer;
|
|
27971
|
+
border: 1px solid #aaa;
|
|
27972
|
+
background-position: 1px 1px;
|
|
27973
|
+
background-image: url("data:image/svg+xml,${encodeURIComponent(TRANSPARENT_BACKGROUND_SVG)}");
|
|
27974
|
+
}
|
|
27975
|
+
`;
|
|
27976
|
+
class RoundColorPicker extends Component {
|
|
27977
|
+
static template = "o-spreadsheet.RoundColorPicker";
|
|
27978
|
+
static components = { ColorPickerWidget, Section, ColorPicker };
|
|
27445
27979
|
static props = {
|
|
27446
27980
|
currentColor: { type: String, optional: true },
|
|
27981
|
+
title: { type: String, optional: true },
|
|
27447
27982
|
onColorPicked: Function,
|
|
27448
27983
|
};
|
|
27984
|
+
colorPickerButtonRef = useRef("colorPickerButton");
|
|
27449
27985
|
state;
|
|
27450
27986
|
setup() {
|
|
27451
27987
|
this.state = useState({ pickerOpened: false });
|
|
@@ -27457,6 +27993,19 @@ class ChartColor extends Component {
|
|
|
27457
27993
|
togglePicker() {
|
|
27458
27994
|
this.state.pickerOpened = !this.state.pickerOpened;
|
|
27459
27995
|
}
|
|
27996
|
+
onColorPicked(color) {
|
|
27997
|
+
this.props.onColorPicked(color);
|
|
27998
|
+
this.state.pickerOpened = false;
|
|
27999
|
+
}
|
|
28000
|
+
get colorPickerAnchorRect() {
|
|
28001
|
+
const button = this.colorPickerButtonRef.el;
|
|
28002
|
+
return getBoundingRectAsPOJO(button);
|
|
28003
|
+
}
|
|
28004
|
+
get buttonStyle() {
|
|
28005
|
+
return cssPropertiesToCss({
|
|
28006
|
+
background: this.props.currentColor,
|
|
28007
|
+
});
|
|
28008
|
+
}
|
|
27460
28009
|
}
|
|
27461
28010
|
|
|
27462
28011
|
class ChartTitle extends Component {
|
|
@@ -27468,9 +28017,9 @@ class ChartTitle extends Component {
|
|
|
27468
28017
|
}
|
|
27469
28018
|
}
|
|
27470
28019
|
|
|
27471
|
-
class
|
|
27472
|
-
static template = "o-spreadsheet-
|
|
27473
|
-
static components = {
|
|
28020
|
+
class GenericChartDesignPanel extends Component {
|
|
28021
|
+
static template = "o-spreadsheet-GenericChartDesignPanel";
|
|
28022
|
+
static components = { RoundColorPicker, ChartTitle, Section };
|
|
27474
28023
|
static props = {
|
|
27475
28024
|
figureId: String,
|
|
27476
28025
|
definition: Object,
|
|
@@ -27493,13 +28042,16 @@ class LineBarPieDesignPanel extends Component {
|
|
|
27493
28042
|
[attr]: ev.target.value,
|
|
27494
28043
|
});
|
|
27495
28044
|
}
|
|
28045
|
+
get backgroundColorTitle() {
|
|
28046
|
+
return ChartTerms.BackgroundColor;
|
|
28047
|
+
}
|
|
27496
28048
|
}
|
|
27497
28049
|
|
|
27498
|
-
class BarChartDesignPanel extends
|
|
28050
|
+
class BarChartDesignPanel extends GenericChartDesignPanel {
|
|
27499
28051
|
static template = "o-spreadsheet-BarChartDesignPanel";
|
|
27500
28052
|
}
|
|
27501
28053
|
|
|
27502
|
-
class ComboChartConfigPanel extends
|
|
28054
|
+
class ComboChartConfigPanel extends GenericChartConfigPanel {
|
|
27503
28055
|
static template = "o-spreadsheet-ComboChartConfigPanel";
|
|
27504
28056
|
get shouldUseRightAxis() {
|
|
27505
28057
|
return _t("Use right axis for line series");
|
|
@@ -27511,7 +28063,7 @@ class ComboChartConfigPanel extends LineBarPieConfigPanel {
|
|
|
27511
28063
|
}
|
|
27512
28064
|
}
|
|
27513
28065
|
|
|
27514
|
-
class ComboChartDesignPanel extends
|
|
28066
|
+
class ComboChartDesignPanel extends GenericChartDesignPanel {
|
|
27515
28067
|
static template = "o-spreadsheet-ComboChartDesignPanel";
|
|
27516
28068
|
}
|
|
27517
28069
|
|
|
@@ -27562,6 +28114,10 @@ css /* scss */ `
|
|
|
27562
28114
|
line-height: 18px;
|
|
27563
28115
|
width: 100%;
|
|
27564
28116
|
}
|
|
28117
|
+
td {
|
|
28118
|
+
box-sizing: border-box;
|
|
28119
|
+
height: 30px;
|
|
28120
|
+
}
|
|
27565
28121
|
th.o-gauge-color-set-colorPicker {
|
|
27566
28122
|
width: 8%;
|
|
27567
28123
|
}
|
|
@@ -27584,7 +28140,12 @@ css /* scss */ `
|
|
|
27584
28140
|
`;
|
|
27585
28141
|
class GaugeChartDesignPanel extends Component {
|
|
27586
28142
|
static template = "o-spreadsheet-GaugeChartDesignPanel";
|
|
27587
|
-
static components = {
|
|
28143
|
+
static components = {
|
|
28144
|
+
ChartErrorSection,
|
|
28145
|
+
RoundColorPicker,
|
|
28146
|
+
ChartTitle,
|
|
28147
|
+
Section,
|
|
28148
|
+
};
|
|
27588
28149
|
static props = {
|
|
27589
28150
|
figureId: String,
|
|
27590
28151
|
definition: Object,
|
|
@@ -27596,9 +28157,6 @@ class GaugeChartDesignPanel extends Component {
|
|
|
27596
28157
|
sectionRuleDispatchResult: undefined,
|
|
27597
28158
|
sectionRule: deepCopy(this.props.definition.sectionRule),
|
|
27598
28159
|
});
|
|
27599
|
-
setup() {
|
|
27600
|
-
useExternalListener(window, "click", this.closeMenus);
|
|
27601
|
-
}
|
|
27602
28160
|
get title() {
|
|
27603
28161
|
return _t(this.props.definition.title);
|
|
27604
28162
|
}
|
|
@@ -27639,31 +28197,26 @@ class GaugeChartDesignPanel extends Component {
|
|
|
27639
28197
|
const sectionRule = deepCopy(this.state.sectionRule);
|
|
27640
28198
|
sectionRule.colors[target] = color;
|
|
27641
28199
|
this.updateSectionRule(sectionRule);
|
|
27642
|
-
this.closeMenus();
|
|
27643
|
-
}
|
|
27644
|
-
toggleMenu(menu) {
|
|
27645
|
-
const isSelected = this.state.openedMenu === menu;
|
|
27646
|
-
this.closeMenus();
|
|
27647
|
-
if (!isSelected) {
|
|
27648
|
-
this.state.openedMenu = menu;
|
|
27649
|
-
}
|
|
27650
28200
|
}
|
|
27651
28201
|
updateSectionRule(sectionRule) {
|
|
27652
28202
|
this.state.sectionRuleDispatchResult = this.props.updateChart(this.props.figureId, {
|
|
27653
28203
|
sectionRule,
|
|
27654
28204
|
});
|
|
28205
|
+
if (this.state.sectionRuleDispatchResult.isSuccessful) {
|
|
28206
|
+
this.state.sectionRule = deepCopy(sectionRule);
|
|
28207
|
+
}
|
|
27655
28208
|
}
|
|
27656
28209
|
canUpdateSectionRule(sectionRule) {
|
|
27657
28210
|
this.state.sectionRuleDispatchResult = this.props.canUpdateChart(this.props.figureId, {
|
|
27658
28211
|
sectionRule,
|
|
27659
28212
|
});
|
|
27660
28213
|
}
|
|
27661
|
-
|
|
27662
|
-
|
|
28214
|
+
get backgroundColorTitle() {
|
|
28215
|
+
return ChartTerms.BackgroundColor;
|
|
27663
28216
|
}
|
|
27664
28217
|
}
|
|
27665
28218
|
|
|
27666
|
-
class LineConfigPanel extends
|
|
28219
|
+
class LineConfigPanel extends GenericChartConfigPanel {
|
|
27667
28220
|
static template = "o-spreadsheet-LineConfigPanel";
|
|
27668
28221
|
get canTreatLabelsAsText() {
|
|
27669
28222
|
const chart = this.env.model.getters.getChart(this.props.figureId);
|
|
@@ -27712,11 +28265,11 @@ class LineConfigPanel extends LineBarPieConfigPanel {
|
|
|
27712
28265
|
}
|
|
27713
28266
|
}
|
|
27714
28267
|
|
|
27715
|
-
class LineChartDesignPanel extends
|
|
28268
|
+
class LineChartDesignPanel extends GenericChartDesignPanel {
|
|
27716
28269
|
static template = "o-spreadsheet-LineChartDesignPanel";
|
|
27717
28270
|
}
|
|
27718
28271
|
|
|
27719
|
-
class ScatterConfigPanel extends
|
|
28272
|
+
class ScatterConfigPanel extends GenericChartConfigPanel {
|
|
27720
28273
|
static template = "o-spreadsheet-ScatterConfigPanel";
|
|
27721
28274
|
get canTreatLabelsAsText() {
|
|
27722
28275
|
const chart = this.env.model.getters.getChart(this.props.figureId);
|
|
@@ -27807,39 +28360,36 @@ class ScorecardChartConfigPanel extends Component {
|
|
|
27807
28360
|
|
|
27808
28361
|
class ScorecardChartDesignPanel extends Component {
|
|
27809
28362
|
static template = "o-spreadsheet-ScorecardChartDesignPanel";
|
|
27810
|
-
static components = {
|
|
28363
|
+
static components = { RoundColorPicker, ChartTitle, Section, Checkbox };
|
|
27811
28364
|
static props = {
|
|
27812
28365
|
figureId: String,
|
|
27813
28366
|
definition: Object,
|
|
27814
28367
|
updateChart: Function,
|
|
27815
28368
|
canUpdateChart: Function,
|
|
27816
28369
|
};
|
|
27817
|
-
state = useState({
|
|
27818
|
-
openedColorPicker: undefined,
|
|
27819
|
-
});
|
|
27820
|
-
setup() {
|
|
27821
|
-
useExternalListener(window, "click", this.closeMenus);
|
|
27822
|
-
}
|
|
27823
28370
|
get title() {
|
|
27824
28371
|
return _t(this.props.definition.title);
|
|
27825
28372
|
}
|
|
28373
|
+
get colorsSectionTitle() {
|
|
28374
|
+
return this.props.definition.baselineMode === "progress"
|
|
28375
|
+
? _t("Progress bar colors")
|
|
28376
|
+
: _t("Baseline colors");
|
|
28377
|
+
}
|
|
28378
|
+
get humanizeNumbersLabel() {
|
|
28379
|
+
return _t("Humanize numbers");
|
|
28380
|
+
}
|
|
27826
28381
|
updateTitle(title) {
|
|
27827
28382
|
this.props.updateChart(this.props.figureId, { title });
|
|
27828
28383
|
}
|
|
28384
|
+
updateHumanizeNumbers(humanize) {
|
|
28385
|
+
this.props.updateChart(this.props.figureId, { humanize });
|
|
28386
|
+
}
|
|
27829
28387
|
translate(term) {
|
|
27830
28388
|
return _t(term);
|
|
27831
28389
|
}
|
|
27832
28390
|
updateBaselineDescr(ev) {
|
|
27833
28391
|
this.props.updateChart(this.props.figureId, { baselineDescr: ev.target.value });
|
|
27834
28392
|
}
|
|
27835
|
-
toggleColorPicker(colorPickerId) {
|
|
27836
|
-
if (this.state.openedColorPicker === colorPickerId) {
|
|
27837
|
-
this.state.openedColorPicker = undefined;
|
|
27838
|
-
}
|
|
27839
|
-
else {
|
|
27840
|
-
this.state.openedColorPicker = colorPickerId;
|
|
27841
|
-
}
|
|
27842
|
-
}
|
|
27843
28393
|
setColor(color, colorPickerId) {
|
|
27844
28394
|
switch (colorPickerId) {
|
|
27845
28395
|
case "backgroundColor":
|
|
@@ -27852,10 +28402,49 @@ class ScorecardChartDesignPanel extends Component {
|
|
|
27852
28402
|
this.props.updateChart(this.props.figureId, { baselineColorUp: color });
|
|
27853
28403
|
break;
|
|
27854
28404
|
}
|
|
27855
|
-
this.closeMenus();
|
|
27856
28405
|
}
|
|
27857
|
-
|
|
27858
|
-
|
|
28406
|
+
get backgroundColorTitle() {
|
|
28407
|
+
return ChartTerms.BackgroundColor;
|
|
28408
|
+
}
|
|
28409
|
+
}
|
|
28410
|
+
|
|
28411
|
+
class WaterfallChartDesignPanel extends GenericChartDesignPanel {
|
|
28412
|
+
static template = "o-spreadsheet-WaterfallChartDesignPanel";
|
|
28413
|
+
static components = { ...GenericChartDesignPanel.components, Checkbox, RoundColorPicker };
|
|
28414
|
+
state = useState({ pickerOpened: false });
|
|
28415
|
+
setup() {
|
|
28416
|
+
super.setup();
|
|
28417
|
+
useExternalListener(window, "click", this.closePicker);
|
|
28418
|
+
}
|
|
28419
|
+
onUpdateShowSubTotals(showSubTotals) {
|
|
28420
|
+
this.props.updateChart(this.props.figureId, { showSubTotals });
|
|
28421
|
+
}
|
|
28422
|
+
onUpdateShowConnectorLines(showConnectorLines) {
|
|
28423
|
+
this.props.updateChart(this.props.figureId, { showConnectorLines });
|
|
28424
|
+
}
|
|
28425
|
+
onUpdateFirstValueAsSubtotal(firstValueAsSubtotal) {
|
|
28426
|
+
this.props.updateChart(this.props.figureId, { firstValueAsSubtotal });
|
|
28427
|
+
}
|
|
28428
|
+
updateColor(colorName, color) {
|
|
28429
|
+
this.props.updateChart(this.props.figureId, { [colorName]: color });
|
|
28430
|
+
}
|
|
28431
|
+
closePicker() {
|
|
28432
|
+
this.state.pickerOpened = false;
|
|
28433
|
+
}
|
|
28434
|
+
togglePicker() {
|
|
28435
|
+
this.state.pickerOpened = !this.state.pickerOpened;
|
|
28436
|
+
}
|
|
28437
|
+
get positiveValuesColor() {
|
|
28438
|
+
return (this.props.definition.positiveValuesColor ||
|
|
28439
|
+
CHART_WATERFALL_POSITIVE_COLOR);
|
|
28440
|
+
}
|
|
28441
|
+
get negativeValuesColor() {
|
|
28442
|
+
return (this.props.definition.negativeValuesColor ||
|
|
28443
|
+
CHART_WATERFALL_NEGATIVE_COLOR);
|
|
28444
|
+
}
|
|
28445
|
+
get subTotalValuesColor() {
|
|
28446
|
+
return (this.props.definition.subTotalValuesColor ||
|
|
28447
|
+
CHART_WATERFALL_SUBTOTAL_COLOR);
|
|
27859
28448
|
}
|
|
27860
28449
|
}
|
|
27861
28450
|
|
|
@@ -27878,8 +28467,8 @@ chartSidePanelComponentRegistry
|
|
|
27878
28467
|
design: ComboChartDesignPanel,
|
|
27879
28468
|
})
|
|
27880
28469
|
.add("pie", {
|
|
27881
|
-
configuration:
|
|
27882
|
-
design:
|
|
28470
|
+
configuration: GenericChartConfigPanel,
|
|
28471
|
+
design: GenericChartDesignPanel,
|
|
27883
28472
|
})
|
|
27884
28473
|
.add("gauge", {
|
|
27885
28474
|
configuration: GaugeChartConfigPanel,
|
|
@@ -27888,6 +28477,10 @@ chartSidePanelComponentRegistry
|
|
|
27888
28477
|
.add("scorecard", {
|
|
27889
28478
|
configuration: ScorecardChartConfigPanel,
|
|
27890
28479
|
design: ScorecardChartDesignPanel,
|
|
28480
|
+
})
|
|
28481
|
+
.add("waterfall", {
|
|
28482
|
+
configuration: GenericChartConfigPanel,
|
|
28483
|
+
design: WaterfallChartDesignPanel,
|
|
27891
28484
|
});
|
|
27892
28485
|
|
|
27893
28486
|
class MainChartPanelStore extends SpreadsheetStore {
|
|
@@ -28804,6 +29397,7 @@ class ConditionalFormattingEditor extends Component {
|
|
|
28804
29397
|
ColorPickerWidget,
|
|
28805
29398
|
ConditionalFormatPreviewList,
|
|
28806
29399
|
Section,
|
|
29400
|
+
RoundColorPicker,
|
|
28807
29401
|
};
|
|
28808
29402
|
icons = ICONS;
|
|
28809
29403
|
cellIsOperators = CellIsOperators;
|
|
@@ -30624,6 +31218,9 @@ function createFilter(id, range, config, createRange) {
|
|
|
30624
31218
|
filteredRange: filteredZone.top > filteredZone.bottom ? undefined : filteredRange,
|
|
30625
31219
|
};
|
|
30626
31220
|
}
|
|
31221
|
+
function isStaticTable(table) {
|
|
31222
|
+
return table.type === "static" || table.type === "forceStatic";
|
|
31223
|
+
}
|
|
30627
31224
|
function getComputedTableStyle(tableConfig, numberOfCols, numberOfRows) {
|
|
30628
31225
|
return {
|
|
30629
31226
|
borders: getAllTableBorders(tableConfig, numberOfCols, numberOfRows),
|
|
@@ -31158,7 +31755,12 @@ class TablePanel extends Component {
|
|
|
31158
31755
|
const extendedZone = this.env.model.getters.getContiguousZone(sheetId, newRange.zone);
|
|
31159
31756
|
newRange = this.env.model.getters.getRangeFromZone(sheetId, extendedZone);
|
|
31160
31757
|
}
|
|
31161
|
-
const
|
|
31758
|
+
const newTableZone = newRange.zone;
|
|
31759
|
+
const oldTableZone = this.props.table.range.zone;
|
|
31760
|
+
const cmdToCall = newTableZone.top === oldTableZone.top && newTableZone.left === oldTableZone.left
|
|
31761
|
+
? "RESIZE_TABLE"
|
|
31762
|
+
: "UPDATE_TABLE";
|
|
31763
|
+
const result = this.env.model.dispatch(cmdToCall, {
|
|
31162
31764
|
sheetId,
|
|
31163
31765
|
zone: this.props.table.range.zone,
|
|
31164
31766
|
newTableRange: newRange.rangeData,
|
|
@@ -31647,6 +32249,38 @@ class DOMFocusableElementStore {
|
|
|
31647
32249
|
}
|
|
31648
32250
|
}
|
|
31649
32251
|
|
|
32252
|
+
class ArrayFormulaHighlight extends SpreadsheetStore {
|
|
32253
|
+
highlightStore = this.get(HighlightStore);
|
|
32254
|
+
constructor(get) {
|
|
32255
|
+
super(get);
|
|
32256
|
+
this.highlightStore.register(this);
|
|
32257
|
+
}
|
|
32258
|
+
get highlights() {
|
|
32259
|
+
const zone = this.getHighlightZone();
|
|
32260
|
+
if (!zone) {
|
|
32261
|
+
return [];
|
|
32262
|
+
}
|
|
32263
|
+
const sheetId = this.model.getters.getActiveSheetId();
|
|
32264
|
+
return [
|
|
32265
|
+
{
|
|
32266
|
+
sheetId,
|
|
32267
|
+
zone,
|
|
32268
|
+
color: "#17A2B8",
|
|
32269
|
+
noFill: true,
|
|
32270
|
+
thinLine: true,
|
|
32271
|
+
},
|
|
32272
|
+
];
|
|
32273
|
+
}
|
|
32274
|
+
getHighlightZone() {
|
|
32275
|
+
const position = this.model.getters.getActivePosition();
|
|
32276
|
+
const spreader = this.model.getters.getArrayFormulaSpreadingOn(position);
|
|
32277
|
+
const spreadZone = spreader
|
|
32278
|
+
? this.model.getters.getSpreadZone(spreader)
|
|
32279
|
+
: this.model.getters.getSpreadZone(position);
|
|
32280
|
+
return spreadZone;
|
|
32281
|
+
}
|
|
32282
|
+
}
|
|
32283
|
+
|
|
31650
32284
|
// -----------------------------------------------------------------------------
|
|
31651
32285
|
// Autofill
|
|
31652
32286
|
// -----------------------------------------------------------------------------
|
|
@@ -32308,6 +32942,7 @@ class Composer extends Component {
|
|
|
32308
32942
|
onComposerCellFocused: { type: Function, optional: true },
|
|
32309
32943
|
onComposerContentFocused: Function,
|
|
32310
32944
|
isDefaultFocus: { type: Boolean, optional: true },
|
|
32945
|
+
onInputContextMenu: { type: Function, optional: true },
|
|
32311
32946
|
};
|
|
32312
32947
|
static components = { TextValueProvider, FunctionDescriptionProvider };
|
|
32313
32948
|
static defaultProps = {
|
|
@@ -32358,6 +32993,9 @@ class Composer extends Component {
|
|
|
32358
32993
|
assistantStyle.right = `0px`;
|
|
32359
32994
|
}
|
|
32360
32995
|
}
|
|
32996
|
+
else if (this.props.delimitation) {
|
|
32997
|
+
assistantStyle["max-height"] = `${this.props.delimitation.height}px`;
|
|
32998
|
+
}
|
|
32361
32999
|
return cssPropertiesToCss(assistantStyle);
|
|
32362
33000
|
}
|
|
32363
33001
|
// we can't allow input events to be triggered while we remove and add back the content of the composer in processContent
|
|
@@ -32644,6 +33282,11 @@ class Composer extends Component {
|
|
|
32644
33282
|
}
|
|
32645
33283
|
}
|
|
32646
33284
|
}
|
|
33285
|
+
onContextMenu(ev) {
|
|
33286
|
+
if (this.composerStore.editionMode === "inactive") {
|
|
33287
|
+
this.props.onInputContextMenu?.(ev);
|
|
33288
|
+
}
|
|
33289
|
+
}
|
|
32647
33290
|
// ---------------------------------------------------------------------------
|
|
32648
33291
|
// Private
|
|
32649
33292
|
// ---------------------------------------------------------------------------
|
|
@@ -32865,6 +33508,7 @@ class GridComposer extends Component {
|
|
|
32865
33508
|
static template = "o-spreadsheet-GridComposer";
|
|
32866
33509
|
static props = {
|
|
32867
33510
|
gridDims: Object,
|
|
33511
|
+
onInputContextMenu: Function,
|
|
32868
33512
|
};
|
|
32869
33513
|
static components = { Composer };
|
|
32870
33514
|
rect = this.defaultRect;
|
|
@@ -32912,6 +33556,7 @@ class GridComposer extends Component {
|
|
|
32912
33556
|
isDefaultFocus: true,
|
|
32913
33557
|
onComposerContentFocused: () => this.composerFocusStore.focusGridComposerContent(),
|
|
32914
33558
|
onComposerCellFocused: (content) => this.composerFocusStore.focusGridComposerCell(content),
|
|
33559
|
+
onInputContextMenu: this.props.onInputContextMenu,
|
|
32915
33560
|
};
|
|
32916
33561
|
}
|
|
32917
33562
|
get containerStyle() {
|
|
@@ -33010,7 +33655,6 @@ class GridCellIcon extends Component {
|
|
|
33010
33655
|
cellPosition: Object,
|
|
33011
33656
|
horizontalAlign: { type: String, optional: true },
|
|
33012
33657
|
verticalAlign: { type: String, optional: true },
|
|
33013
|
-
offset: { type: Object, optional: true },
|
|
33014
33658
|
slots: Object,
|
|
33015
33659
|
};
|
|
33016
33660
|
get iconStyle() {
|
|
@@ -33021,8 +33665,8 @@ class GridCellIcon extends Component {
|
|
|
33021
33665
|
const x = this.getIconHorizontalPosition(rect, cellPosition);
|
|
33022
33666
|
const y = this.getIconVerticalPosition(rect, cellPosition);
|
|
33023
33667
|
return cssPropertiesToCss({
|
|
33024
|
-
top: `${y
|
|
33025
|
-
left: `${x
|
|
33668
|
+
top: `${y}px`,
|
|
33669
|
+
left: `${x}px`,
|
|
33026
33670
|
});
|
|
33027
33671
|
}
|
|
33028
33672
|
getIconVerticalPosition(rect, cellPosition) {
|
|
@@ -33062,83 +33706,6 @@ class GridCellIcon extends Component {
|
|
|
33062
33706
|
}
|
|
33063
33707
|
}
|
|
33064
33708
|
|
|
33065
|
-
css /* scss */ `
|
|
33066
|
-
.o-filter-icon {
|
|
33067
|
-
color: ${FILTERS_COLOR};
|
|
33068
|
-
display: flex;
|
|
33069
|
-
align-items: center;
|
|
33070
|
-
justify-content: center;
|
|
33071
|
-
width: ${GRID_ICON_EDGE_LENGTH}px;
|
|
33072
|
-
height: ${GRID_ICON_EDGE_LENGTH}px;
|
|
33073
|
-
|
|
33074
|
-
&:hover {
|
|
33075
|
-
background: ${FILTERS_COLOR};
|
|
33076
|
-
color: #fff;
|
|
33077
|
-
}
|
|
33078
|
-
|
|
33079
|
-
&.o-high-contrast {
|
|
33080
|
-
color: #defade;
|
|
33081
|
-
}
|
|
33082
|
-
&.o-high-contrast:hover {
|
|
33083
|
-
color: ${FILTERS_COLOR};
|
|
33084
|
-
background: #fff;
|
|
33085
|
-
}
|
|
33086
|
-
}
|
|
33087
|
-
.o-filter-icon:hover {
|
|
33088
|
-
background: ${FILTERS_COLOR};
|
|
33089
|
-
color: #fff;
|
|
33090
|
-
}
|
|
33091
|
-
`;
|
|
33092
|
-
class FilterIcon extends Component {
|
|
33093
|
-
static template = "o-spreadsheet-FilterIcon";
|
|
33094
|
-
static props = {
|
|
33095
|
-
cellPosition: Object,
|
|
33096
|
-
};
|
|
33097
|
-
cellPopovers;
|
|
33098
|
-
setup() {
|
|
33099
|
-
this.cellPopovers = useStore(CellPopoverStore);
|
|
33100
|
-
}
|
|
33101
|
-
onClick() {
|
|
33102
|
-
const position = this.props.cellPosition;
|
|
33103
|
-
const activePopover = this.cellPopovers.persistentCellPopover;
|
|
33104
|
-
const { col, row } = position;
|
|
33105
|
-
if (activePopover.isOpen &&
|
|
33106
|
-
activePopover.col === col &&
|
|
33107
|
-
activePopover.row === row &&
|
|
33108
|
-
activePopover.type === "FilterMenu") {
|
|
33109
|
-
this.cellPopovers.close();
|
|
33110
|
-
return;
|
|
33111
|
-
}
|
|
33112
|
-
this.cellPopovers.open({ col, row }, "FilterMenu");
|
|
33113
|
-
}
|
|
33114
|
-
get isFilterActive() {
|
|
33115
|
-
return this.env.model.getters.isFilterActive(this.props.cellPosition);
|
|
33116
|
-
}
|
|
33117
|
-
get iconClass() {
|
|
33118
|
-
const cellStyle = this.env.model.getters.getCellComputedStyle(this.props.cellPosition);
|
|
33119
|
-
const luminance = relativeLuminance(cellStyle.fillColor || "#fff");
|
|
33120
|
-
return luminance < 0.45 ? "o-high-contrast" : "";
|
|
33121
|
-
}
|
|
33122
|
-
}
|
|
33123
|
-
|
|
33124
|
-
class FilterIconsOverlay extends Component {
|
|
33125
|
-
static template = "o-spreadsheet-FilterIconsOverlay";
|
|
33126
|
-
static props = {
|
|
33127
|
-
gridPosition: { type: Object, optional: true },
|
|
33128
|
-
};
|
|
33129
|
-
static components = {
|
|
33130
|
-
GridCellIcon,
|
|
33131
|
-
FilterIcon,
|
|
33132
|
-
};
|
|
33133
|
-
static defaultProps = {
|
|
33134
|
-
gridPosition: { x: 0, y: 0 },
|
|
33135
|
-
};
|
|
33136
|
-
getFilterHeadersPositions() {
|
|
33137
|
-
const sheetId = this.env.model.getters.getActiveSheetId();
|
|
33138
|
-
return this.env.model.getters.getFilterHeaders(sheetId);
|
|
33139
|
-
}
|
|
33140
|
-
}
|
|
33141
|
-
|
|
33142
33709
|
const CHECKBOX_WIDTH = 15;
|
|
33143
33710
|
const MARGIN = (GRID_ICON_EDGE_LENGTH - CHECKBOX_WIDTH) / 2;
|
|
33144
33711
|
css /* scss */ `
|
|
@@ -33490,6 +34057,10 @@ css /*SCSS*/ `
|
|
|
33490
34057
|
height: 0px;
|
|
33491
34058
|
}
|
|
33492
34059
|
}
|
|
34060
|
+
.o-figure-container {
|
|
34061
|
+
-webkit-user-select: none; // safari
|
|
34062
|
+
user-select: none;
|
|
34063
|
+
}
|
|
33493
34064
|
`;
|
|
33494
34065
|
/**
|
|
33495
34066
|
* Each figure ⭐ is positioned inside a container `div` placed and sized
|
|
@@ -33806,6 +34377,80 @@ class FiguresContainer extends Component {
|
|
|
33806
34377
|
}
|
|
33807
34378
|
}
|
|
33808
34379
|
|
|
34380
|
+
css /* scss */ `
|
|
34381
|
+
.o-filter-icon {
|
|
34382
|
+
color: ${FILTERS_COLOR};
|
|
34383
|
+
display: flex;
|
|
34384
|
+
align-items: center;
|
|
34385
|
+
justify-content: center;
|
|
34386
|
+
width: ${GRID_ICON_EDGE_LENGTH}px;
|
|
34387
|
+
height: ${GRID_ICON_EDGE_LENGTH}px;
|
|
34388
|
+
|
|
34389
|
+
&:hover {
|
|
34390
|
+
background: ${FILTERS_COLOR};
|
|
34391
|
+
color: #fff;
|
|
34392
|
+
}
|
|
34393
|
+
|
|
34394
|
+
&.o-high-contrast {
|
|
34395
|
+
color: #defade;
|
|
34396
|
+
}
|
|
34397
|
+
&.o-high-contrast:hover {
|
|
34398
|
+
color: ${FILTERS_COLOR};
|
|
34399
|
+
background: #fff;
|
|
34400
|
+
}
|
|
34401
|
+
}
|
|
34402
|
+
.o-filter-icon:hover {
|
|
34403
|
+
background: ${FILTERS_COLOR};
|
|
34404
|
+
color: #fff;
|
|
34405
|
+
}
|
|
34406
|
+
`;
|
|
34407
|
+
class FilterIcon extends Component {
|
|
34408
|
+
static template = "o-spreadsheet-FilterIcon";
|
|
34409
|
+
static props = {
|
|
34410
|
+
cellPosition: Object,
|
|
34411
|
+
};
|
|
34412
|
+
cellPopovers;
|
|
34413
|
+
setup() {
|
|
34414
|
+
this.cellPopovers = useStore(CellPopoverStore);
|
|
34415
|
+
}
|
|
34416
|
+
onClick() {
|
|
34417
|
+
const position = this.props.cellPosition;
|
|
34418
|
+
const activePopover = this.cellPopovers.persistentCellPopover;
|
|
34419
|
+
const { col, row } = position;
|
|
34420
|
+
if (activePopover.isOpen &&
|
|
34421
|
+
activePopover.col === col &&
|
|
34422
|
+
activePopover.row === row &&
|
|
34423
|
+
activePopover.type === "FilterMenu") {
|
|
34424
|
+
this.cellPopovers.close();
|
|
34425
|
+
return;
|
|
34426
|
+
}
|
|
34427
|
+
this.cellPopovers.open({ col, row }, "FilterMenu");
|
|
34428
|
+
}
|
|
34429
|
+
get isFilterActive() {
|
|
34430
|
+
return this.env.model.getters.isFilterActive(this.props.cellPosition);
|
|
34431
|
+
}
|
|
34432
|
+
get iconClass() {
|
|
34433
|
+
const cellStyle = this.env.model.getters.getCellComputedStyle(this.props.cellPosition);
|
|
34434
|
+
const luminance = relativeLuminance(cellStyle.fillColor || "#fff");
|
|
34435
|
+
return luminance < 0.45 ? "o-high-contrast" : "";
|
|
34436
|
+
}
|
|
34437
|
+
}
|
|
34438
|
+
|
|
34439
|
+
class FilterIconsOverlay extends Component {
|
|
34440
|
+
static template = "o-spreadsheet-FilterIconsOverlay";
|
|
34441
|
+
static props = {
|
|
34442
|
+
onMouseDown: Function,
|
|
34443
|
+
};
|
|
34444
|
+
static components = {
|
|
34445
|
+
GridCellIcon,
|
|
34446
|
+
FilterIcon,
|
|
34447
|
+
};
|
|
34448
|
+
getFilterHeadersPositions() {
|
|
34449
|
+
const sheetId = this.env.model.getters.getActiveSheetId();
|
|
34450
|
+
return this.env.model.getters.getFilterHeaders(sheetId);
|
|
34451
|
+
}
|
|
34452
|
+
}
|
|
34453
|
+
|
|
33809
34454
|
css /* scss */ `
|
|
33810
34455
|
.o-grid-add-rows {
|
|
33811
34456
|
input {
|
|
@@ -34019,7 +34664,12 @@ class GridOverlay extends Component {
|
|
|
34019
34664
|
onGridMoved: Function,
|
|
34020
34665
|
gridOverlayDimensions: String,
|
|
34021
34666
|
};
|
|
34022
|
-
static components = {
|
|
34667
|
+
static components = {
|
|
34668
|
+
FiguresContainer,
|
|
34669
|
+
DataValidationOverlay,
|
|
34670
|
+
GridAddRowsFooter,
|
|
34671
|
+
FilterIconsOverlay,
|
|
34672
|
+
};
|
|
34023
34673
|
static defaultProps = {
|
|
34024
34674
|
onCellHovered: () => { },
|
|
34025
34675
|
onCellDoubleClicked: () => { },
|
|
@@ -34064,7 +34714,7 @@ class GridOverlay extends Component {
|
|
|
34064
34714
|
get isPaintingFormat() {
|
|
34065
34715
|
return this.env.model.getters.isPaintingFormat();
|
|
34066
34716
|
}
|
|
34067
|
-
onMouseDown(ev) {
|
|
34717
|
+
onMouseDown(ev, modifiers) {
|
|
34068
34718
|
if (ev.button > 0) {
|
|
34069
34719
|
// not main button, probably a context menu
|
|
34070
34720
|
return;
|
|
@@ -34073,6 +34723,7 @@ class GridOverlay extends Component {
|
|
|
34073
34723
|
this.props.onCellClicked(col, row, {
|
|
34074
34724
|
expandZone: ev.shiftKey,
|
|
34075
34725
|
addZone: isCtrlKey(ev),
|
|
34726
|
+
closePopover: modifiers?.closePopover ?? true,
|
|
34076
34727
|
});
|
|
34077
34728
|
}
|
|
34078
34729
|
onDoubleClick(ev) {
|
|
@@ -35811,6 +36462,77 @@ class SidePanelStore extends SpreadsheetStore {
|
|
|
35811
36462
|
}
|
|
35812
36463
|
}
|
|
35813
36464
|
|
|
36465
|
+
const SIZE = 3;
|
|
36466
|
+
const COLOR = "#777";
|
|
36467
|
+
css /* scss */ `
|
|
36468
|
+
.o-table-resizer {
|
|
36469
|
+
width: ${SIZE}px;
|
|
36470
|
+
height: ${SIZE}px;
|
|
36471
|
+
border-bottom: ${SIZE}px solid ${COLOR};
|
|
36472
|
+
border-right: ${SIZE}px solid ${COLOR};
|
|
36473
|
+
cursor: nwse-resize;
|
|
36474
|
+
}
|
|
36475
|
+
`;
|
|
36476
|
+
class TableResizer extends Component {
|
|
36477
|
+
static template = "o-spreadsheet-TableResizer";
|
|
36478
|
+
static props = { table: Object };
|
|
36479
|
+
state = useState({ highlightZone: undefined });
|
|
36480
|
+
setup() {
|
|
36481
|
+
useHighlights(this);
|
|
36482
|
+
}
|
|
36483
|
+
get containerStyle() {
|
|
36484
|
+
const tableZone = this.props.table.range.zone;
|
|
36485
|
+
const bottomRight = { ...tableZone, left: tableZone.right, top: tableZone.bottom };
|
|
36486
|
+
const rect = this.env.model.getters.getVisibleRect(bottomRight);
|
|
36487
|
+
if (rect.height === 0 || rect.width === 0) {
|
|
36488
|
+
return cssPropertiesToCss({ display: "none" });
|
|
36489
|
+
}
|
|
36490
|
+
return cssPropertiesToCss({
|
|
36491
|
+
top: `${rect.y + rect.height - SIZE * 2}px`,
|
|
36492
|
+
left: `${rect.x + rect.width - SIZE * 2}px`,
|
|
36493
|
+
});
|
|
36494
|
+
}
|
|
36495
|
+
onMouseDown(ev) {
|
|
36496
|
+
const tableZone = this.props.table.range.zone;
|
|
36497
|
+
const topLeft = { col: tableZone.left, row: tableZone.top };
|
|
36498
|
+
document.body.style.cursor = "nwse-resize";
|
|
36499
|
+
const onMouseUp = () => {
|
|
36500
|
+
document.body.style.cursor = "";
|
|
36501
|
+
const newTableZone = this.state.highlightZone;
|
|
36502
|
+
if (!newTableZone)
|
|
36503
|
+
return;
|
|
36504
|
+
const sheetId = this.props.table.range.sheetId;
|
|
36505
|
+
this.env.model.dispatch("RESIZE_TABLE", {
|
|
36506
|
+
sheetId,
|
|
36507
|
+
zone: this.props.table.range.zone,
|
|
36508
|
+
newTableRange: this.env.model.getters.getRangeDataFromZone(sheetId, newTableZone),
|
|
36509
|
+
});
|
|
36510
|
+
this.state.highlightZone = undefined;
|
|
36511
|
+
};
|
|
36512
|
+
const onMouseMove = (col, row, ev) => {
|
|
36513
|
+
this.state.highlightZone = {
|
|
36514
|
+
left: topLeft.col,
|
|
36515
|
+
top: topLeft.row,
|
|
36516
|
+
right: Math.max(col, topLeft.col),
|
|
36517
|
+
bottom: Math.max(row, topLeft.row),
|
|
36518
|
+
};
|
|
36519
|
+
};
|
|
36520
|
+
dragAndDropBeyondTheViewport(this.env, onMouseMove, onMouseUp);
|
|
36521
|
+
}
|
|
36522
|
+
get highlights() {
|
|
36523
|
+
if (!this.state.highlightZone)
|
|
36524
|
+
return [];
|
|
36525
|
+
return [
|
|
36526
|
+
{
|
|
36527
|
+
zone: this.state.highlightZone,
|
|
36528
|
+
sheetId: this.props.table.range.sheetId,
|
|
36529
|
+
color: COLOR,
|
|
36530
|
+
noFill: true,
|
|
36531
|
+
},
|
|
36532
|
+
];
|
|
36533
|
+
}
|
|
36534
|
+
}
|
|
36535
|
+
|
|
35814
36536
|
const registries$1 = {
|
|
35815
36537
|
ROW: rowMenuRegistry,
|
|
35816
36538
|
COL: colMenuRegistry,
|
|
@@ -35838,7 +36560,7 @@ class Grid extends Component {
|
|
|
35838
36560
|
Popover,
|
|
35839
36561
|
VerticalScrollBar,
|
|
35840
36562
|
HorizontalScrollBar,
|
|
35841
|
-
|
|
36563
|
+
TableResizer,
|
|
35842
36564
|
};
|
|
35843
36565
|
HEADER_HEIGHT = HEADER_HEIGHT;
|
|
35844
36566
|
HEADER_WIDTH = HEADER_WIDTH;
|
|
@@ -35867,6 +36589,7 @@ class Grid extends Component {
|
|
|
35867
36589
|
this.composerFocusStore = useStore(ComposerFocusStore);
|
|
35868
36590
|
this.DOMFocusableElementStore = useStore(DOMFocusableElementStore);
|
|
35869
36591
|
this.sidePanel = useStore(SidePanelStore);
|
|
36592
|
+
useStore(ArrayFormulaHighlight);
|
|
35870
36593
|
useChildSubEnv({ getPopoverContainerRect: () => this.getGridRect() });
|
|
35871
36594
|
useExternalListener(document.body, "cut", this.copy.bind(this, true));
|
|
35872
36595
|
useExternalListener(document.body, "copy", this.copy.bind(this, false));
|
|
@@ -36139,17 +36862,17 @@ class Grid extends Component {
|
|
|
36139
36862
|
// ---------------------------------------------------------------------------
|
|
36140
36863
|
// Zone selection with mouse
|
|
36141
36864
|
// ---------------------------------------------------------------------------
|
|
36142
|
-
onCellClicked(col, row,
|
|
36143
|
-
if (this.cellPopovers.isOpen) {
|
|
36865
|
+
onCellClicked(col, row, modifiers) {
|
|
36866
|
+
if (modifiers.closePopover && this.cellPopovers.isOpen) {
|
|
36144
36867
|
this.cellPopovers.close();
|
|
36145
36868
|
}
|
|
36146
36869
|
if (this.composerStore.editionMode === "editing") {
|
|
36147
36870
|
this.composerStore.stopEdition();
|
|
36148
36871
|
}
|
|
36149
|
-
if (expandZone) {
|
|
36872
|
+
if (modifiers.expandZone) {
|
|
36150
36873
|
this.env.model.selection.setAnchorCorner(col, row);
|
|
36151
36874
|
}
|
|
36152
|
-
else if (addZone) {
|
|
36875
|
+
else if (modifiers.addZone) {
|
|
36153
36876
|
this.env.model.selection.addCellToSelection(col, row);
|
|
36154
36877
|
}
|
|
36155
36878
|
else {
|
|
@@ -36435,6 +37158,10 @@ class Grid extends Component {
|
|
|
36435
37158
|
onComposerContentFocused() {
|
|
36436
37159
|
this.composerFocusStore.focusGridComposerContent();
|
|
36437
37160
|
}
|
|
37161
|
+
get staticTables() {
|
|
37162
|
+
const sheetId = this.env.model.getters.getActiveSheetId();
|
|
37163
|
+
return this.env.model.getters.getCoreTables(sheetId).filter(isStaticTable);
|
|
37164
|
+
}
|
|
36438
37165
|
}
|
|
36439
37166
|
|
|
36440
37167
|
/**
|
|
@@ -36871,7 +37598,7 @@ const CHART_TYPE_CONVERSION_MAP = {
|
|
|
36871
37598
|
line3DChart: undefined,
|
|
36872
37599
|
stockChart: undefined,
|
|
36873
37600
|
radarChart: undefined,
|
|
36874
|
-
scatterChart:
|
|
37601
|
+
scatterChart: "scatter",
|
|
36875
37602
|
pieChart: "pie",
|
|
36876
37603
|
pie3DChart: undefined,
|
|
36877
37604
|
doughnutChart: "pie",
|
|
@@ -37635,29 +38362,12 @@ function convertWidthFromExcel(width) {
|
|
|
37635
38362
|
return width;
|
|
37636
38363
|
return Math.round((width / WIDTH_FACTOR) * 100) / 100;
|
|
37637
38364
|
}
|
|
37638
|
-
function convertBorderDescr(descr) {
|
|
37639
|
-
if (!descr) {
|
|
37640
|
-
return undefined;
|
|
37641
|
-
}
|
|
37642
|
-
return {
|
|
37643
|
-
style: descr.style,
|
|
37644
|
-
color: { rgb: descr.color },
|
|
37645
|
-
};
|
|
37646
|
-
}
|
|
37647
38365
|
function extractStyle(cell, data) {
|
|
37648
38366
|
let style = {};
|
|
37649
38367
|
if (cell.style) {
|
|
37650
38368
|
style = data.styles[cell.style];
|
|
37651
38369
|
}
|
|
37652
38370
|
const format = extractFormat(cell, data);
|
|
37653
|
-
const exportedBorder = {};
|
|
37654
|
-
if (cell.border) {
|
|
37655
|
-
const border = data.borders[cell.border];
|
|
37656
|
-
exportedBorder.left = convertBorderDescr(border.left);
|
|
37657
|
-
exportedBorder.right = convertBorderDescr(border.right);
|
|
37658
|
-
exportedBorder.bottom = convertBorderDescr(border.bottom);
|
|
37659
|
-
exportedBorder.top = convertBorderDescr(border.top);
|
|
37660
|
-
}
|
|
37661
38371
|
const styles = {
|
|
37662
38372
|
font: {
|
|
37663
38373
|
size: style?.fontSize || DEFAULT_FONT_SIZE,
|
|
@@ -37671,7 +38381,7 @@ function extractStyle(cell, data) {
|
|
|
37671
38381
|
}
|
|
37672
38382
|
: { reservedAttribute: "none" },
|
|
37673
38383
|
numFmt: format ? { format: format, id: 0 /* id not used for export */ } : undefined,
|
|
37674
|
-
border:
|
|
38384
|
+
border: cell.border || 0,
|
|
37675
38385
|
alignment: {
|
|
37676
38386
|
horizontal: style.align,
|
|
37677
38387
|
vertical: style.verticalAlign
|
|
@@ -37693,15 +38403,12 @@ function extractFormat(cell, data) {
|
|
|
37693
38403
|
return undefined;
|
|
37694
38404
|
}
|
|
37695
38405
|
function normalizeStyle(construct, styles) {
|
|
37696
|
-
const { id: fontId } = pushElement(styles["font"], construct.fonts);
|
|
37697
|
-
const { id: fillId } = pushElement(styles["fill"], construct.fills);
|
|
37698
|
-
const { id: borderId } = pushElement(styles["border"], construct.borders);
|
|
37699
38406
|
// Normalize this
|
|
37700
38407
|
const numFmtId = convertFormat(styles["numFmt"], construct.numFmts);
|
|
37701
38408
|
const style = {
|
|
37702
|
-
fontId,
|
|
37703
|
-
fillId,
|
|
37704
|
-
borderId,
|
|
38409
|
+
fontId: pushElement(styles.font, construct.fonts),
|
|
38410
|
+
fillId: pushElement(styles.fill, construct.fills),
|
|
38411
|
+
borderId: styles.border,
|
|
37705
38412
|
numFmtId,
|
|
37706
38413
|
alignment: {
|
|
37707
38414
|
vertical: styles.alignment.vertical,
|
|
@@ -37709,8 +38416,7 @@ function normalizeStyle(construct, styles) {
|
|
|
37709
38416
|
wrapText: styles.alignment.wrapText,
|
|
37710
38417
|
},
|
|
37711
38418
|
};
|
|
37712
|
-
|
|
37713
|
-
return id;
|
|
38419
|
+
return pushElement(style, construct.styles);
|
|
37714
38420
|
}
|
|
37715
38421
|
function convertFormat(format, numFmtStructure) {
|
|
37716
38422
|
if (!format) {
|
|
@@ -37718,8 +38424,7 @@ function convertFormat(format, numFmtStructure) {
|
|
|
37718
38424
|
}
|
|
37719
38425
|
let formatId = XLSX_FORMAT_MAP[format.format];
|
|
37720
38426
|
if (!formatId) {
|
|
37721
|
-
|
|
37722
|
-
formatId = id + FIRST_NUMFMT_ID;
|
|
38427
|
+
formatId = pushElement(format, numFmtStructure) + FIRST_NUMFMT_ID;
|
|
37723
38428
|
}
|
|
37724
38429
|
return formatId;
|
|
37725
38430
|
}
|
|
@@ -37744,20 +38449,15 @@ function addRelsToFile(relsFiles, path, rel) {
|
|
|
37744
38449
|
return id;
|
|
37745
38450
|
}
|
|
37746
38451
|
function pushElement(property, propertyList) {
|
|
37747
|
-
|
|
37748
|
-
|
|
37749
|
-
|
|
38452
|
+
let len = propertyList.length;
|
|
38453
|
+
const operator = typeof property === "object" ? deepEquals : (a, b) => a === b;
|
|
38454
|
+
for (let i = 0; i < len; i++) {
|
|
38455
|
+
if (operator(property, propertyList[i])) {
|
|
38456
|
+
return i;
|
|
37750
38457
|
}
|
|
37751
38458
|
}
|
|
37752
|
-
|
|
37753
|
-
|
|
37754
|
-
propertyList.push(property);
|
|
37755
|
-
elemId = propertyList.length - 1;
|
|
37756
|
-
}
|
|
37757
|
-
return {
|
|
37758
|
-
id: elemId,
|
|
37759
|
-
list: propertyList,
|
|
37760
|
-
};
|
|
38459
|
+
propertyList[propertyList.length] = property;
|
|
38460
|
+
return propertyList.length - 1;
|
|
37761
38461
|
}
|
|
37762
38462
|
const chartIds = [];
|
|
37763
38463
|
/**
|
|
@@ -38552,7 +39252,25 @@ function parseXML(xmlString, mimeType = "text/xml") {
|
|
|
38552
39252
|
}
|
|
38553
39253
|
return document;
|
|
38554
39254
|
}
|
|
38555
|
-
function
|
|
39255
|
+
function convertBorderDescr(descr) {
|
|
39256
|
+
if (!descr) {
|
|
39257
|
+
return undefined;
|
|
39258
|
+
}
|
|
39259
|
+
return {
|
|
39260
|
+
style: descr.style,
|
|
39261
|
+
color: { rgb: descr.color },
|
|
39262
|
+
};
|
|
39263
|
+
}
|
|
39264
|
+
function getDefaultXLSXStructure(data) {
|
|
39265
|
+
const xlsxBorders = Object.values(data.borders).map((border) => {
|
|
39266
|
+
return {
|
|
39267
|
+
left: convertBorderDescr(border.left),
|
|
39268
|
+
right: convertBorderDescr(border.right),
|
|
39269
|
+
bottom: convertBorderDescr(border.bottom),
|
|
39270
|
+
top: convertBorderDescr(border.top),
|
|
39271
|
+
};
|
|
39272
|
+
});
|
|
39273
|
+
const borders = [{}, ...xlsxBorders];
|
|
38556
39274
|
return {
|
|
38557
39275
|
relsFiles: [],
|
|
38558
39276
|
sharedStrings: [],
|
|
@@ -38575,7 +39293,7 @@ function getDefaultXLSXStructure() {
|
|
|
38575
39293
|
},
|
|
38576
39294
|
],
|
|
38577
39295
|
fills: [{ reservedAttribute: "none" }, { reservedAttribute: "gray125" }],
|
|
38578
|
-
borders
|
|
39296
|
+
borders,
|
|
38579
39297
|
numFmts: [],
|
|
38580
39298
|
dxfs: [],
|
|
38581
39299
|
};
|
|
@@ -39111,8 +39829,8 @@ class XlsxChartExtractor extends XlsxBaseExtractor {
|
|
|
39111
39829
|
return {
|
|
39112
39830
|
title: chartTitle,
|
|
39113
39831
|
type: CHART_TYPE_CONVERSION_MAP[chartType],
|
|
39114
|
-
dataSets: this.extractChartDatasets(this.querySelector(rootChartElement, `c:${chartType}`)),
|
|
39115
|
-
labelRange: this.extractChildTextContent(rootChartElement,
|
|
39832
|
+
dataSets: this.extractChartDatasets(this.querySelector(rootChartElement, `c:${chartType}`), chartType),
|
|
39833
|
+
labelRange: this.extractChildTextContent(rootChartElement, `c:ser ${chartType === "scatterChart" ? "c:numRef" : "c:cat"} c:f`),
|
|
39116
39834
|
backgroundColor: this.extractChildAttr(rootChartElement, "c:chartSpace > c:spPr a:srgbClr", "val", {
|
|
39117
39835
|
default: "ffffff",
|
|
39118
39836
|
}).asString(),
|
|
@@ -39141,8 +39859,8 @@ class XlsxChartExtractor extends XlsxBaseExtractor {
|
|
|
39141
39859
|
title: chartTitle,
|
|
39142
39860
|
type: "combo",
|
|
39143
39861
|
dataSets: [
|
|
39144
|
-
...this.extractChartDatasets(this.querySelector(chartElement, `c:barChart`)),
|
|
39145
|
-
...this.extractChartDatasets(this.querySelector(chartElement, `c:lineChart`)),
|
|
39862
|
+
...this.extractChartDatasets(this.querySelector(chartElement, `c:barChart`), "comboChart"),
|
|
39863
|
+
...this.extractChartDatasets(this.querySelector(chartElement, `c:lineChart`), "comboChart"),
|
|
39146
39864
|
],
|
|
39147
39865
|
labelRange: this.extractChildTextContent(chartElement, "c:ser c:cat c:f"),
|
|
39148
39866
|
backgroundColor: this.extractChildAttr(chartElement, "c:chartSpace > c:spPr a:srgbClr", "val", {
|
|
@@ -39160,7 +39878,10 @@ class XlsxChartExtractor extends XlsxBaseExtractor {
|
|
|
39160
39878
|
fontColor: "000000",
|
|
39161
39879
|
};
|
|
39162
39880
|
}
|
|
39163
|
-
extractChartDatasets(chartElement) {
|
|
39881
|
+
extractChartDatasets(chartElement, chartType) {
|
|
39882
|
+
if (chartType === "scatterChart") {
|
|
39883
|
+
return this.extractScatterChartDatasets(chartElement);
|
|
39884
|
+
}
|
|
39164
39885
|
return this.mapOnElements({ parent: chartElement, query: "c:ser" }, (chartDataElement) => {
|
|
39165
39886
|
return {
|
|
39166
39887
|
label: this.extractChildTextContent(chartDataElement, "c:tx c:f"),
|
|
@@ -39168,6 +39889,14 @@ class XlsxChartExtractor extends XlsxBaseExtractor {
|
|
|
39168
39889
|
};
|
|
39169
39890
|
});
|
|
39170
39891
|
}
|
|
39892
|
+
extractScatterChartDatasets(chartElement) {
|
|
39893
|
+
return this.mapOnElements({ parent: chartElement, query: "c:ser" }, (chartDataElement) => {
|
|
39894
|
+
return {
|
|
39895
|
+
label: this.extractChildTextContent(chartDataElement, "c:xVal c:f", { required: false }),
|
|
39896
|
+
range: this.extractChildTextContent(chartDataElement, "c:yVal c:f", { required: true }),
|
|
39897
|
+
};
|
|
39898
|
+
});
|
|
39899
|
+
}
|
|
39171
39900
|
/**
|
|
39172
39901
|
* The chart type in the XML isn't explicitly defined, but there is an XML element that define the
|
|
39173
39902
|
* chart, and this element tag name tells us which type of chart it is. We just need to find this XML element.
|
|
@@ -40561,12 +41290,14 @@ class BasePlugin {
|
|
|
40561
41290
|
static getters = [];
|
|
40562
41291
|
history;
|
|
40563
41292
|
dispatch;
|
|
40564
|
-
|
|
41293
|
+
canDispatch;
|
|
41294
|
+
constructor(stateObserver, dispatch, canDispatch) {
|
|
40565
41295
|
this.history = Object.assign(Object.create(stateObserver), {
|
|
40566
41296
|
update: stateObserver.addChange.bind(stateObserver, this),
|
|
40567
41297
|
selectCell: () => { },
|
|
40568
41298
|
});
|
|
40569
41299
|
this.dispatch = dispatch;
|
|
41300
|
+
this.canDispatch = canDispatch;
|
|
40570
41301
|
}
|
|
40571
41302
|
/**
|
|
40572
41303
|
* Export for excel should be available for all plugins, even for the UI.
|
|
@@ -40645,8 +41376,8 @@ class BasePlugin {
|
|
|
40645
41376
|
class CorePlugin extends BasePlugin {
|
|
40646
41377
|
getters;
|
|
40647
41378
|
uuidGenerator;
|
|
40648
|
-
constructor({ getters, stateObserver, range, dispatch, uuidGenerator }) {
|
|
40649
|
-
super(stateObserver, dispatch);
|
|
41379
|
+
constructor({ getters, stateObserver, range, dispatch, canDispatch, uuidGenerator, }) {
|
|
41380
|
+
super(stateObserver, dispatch, canDispatch);
|
|
40650
41381
|
range.addRangeProvider(this.adaptRanges.bind(this));
|
|
40651
41382
|
this.getters = getters;
|
|
40652
41383
|
this.uuidGenerator = uuidGenerator;
|
|
@@ -45224,7 +45955,7 @@ class SheetPlugin extends CorePlugin {
|
|
|
45224
45955
|
}
|
|
45225
45956
|
|
|
45226
45957
|
class TablePlugin extends CorePlugin {
|
|
45227
|
-
static getters = ["getCoreTable", "getCoreTables"];
|
|
45958
|
+
static getters = ["getCoreTable", "getCoreTables", "getCoreTableMatchingTopLeft"];
|
|
45228
45959
|
tables = {};
|
|
45229
45960
|
adaptRanges(applyChange, sheetId) {
|
|
45230
45961
|
const sheetIds = sheetId ? [sheetId] : this.getters.getSheetIds();
|
|
@@ -45245,7 +45976,7 @@ class TablePlugin extends CorePlugin {
|
|
|
45245
45976
|
? "TableOverlap" /* CommandResult.TableOverlap */
|
|
45246
45977
|
: "Success" /* CommandResult.Success */, (cmd) => this.checkTableConfigUpdateIsValid(cmd.config));
|
|
45247
45978
|
case "UPDATE_TABLE":
|
|
45248
|
-
const updatedTable = this.
|
|
45979
|
+
const updatedTable = this.getCoreTableMatchingTopLeft(cmd.sheetId, cmd.zone);
|
|
45249
45980
|
if (!updatedTable) {
|
|
45250
45981
|
return "TableNotFound" /* CommandResult.TableNotFound */;
|
|
45251
45982
|
}
|
|
@@ -45401,10 +46132,9 @@ class TablePlugin extends CorePlugin {
|
|
|
45401
46132
|
}
|
|
45402
46133
|
return direction;
|
|
45403
46134
|
}
|
|
45404
|
-
|
|
46135
|
+
getCoreTableMatchingTopLeft(sheetId, zone) {
|
|
45405
46136
|
for (const table of this.getCoreTables(sheetId)) {
|
|
45406
46137
|
const tableZone = table.range.zone;
|
|
45407
|
-
// Only check top left to match dynamic tables
|
|
45408
46138
|
if (tableZone.left === zone.left && tableZone.top === zone.top) {
|
|
45409
46139
|
return table;
|
|
45410
46140
|
}
|
|
@@ -45420,7 +46150,7 @@ class TablePlugin extends CorePlugin {
|
|
|
45420
46150
|
if (zoneIsInSheet !== "Success" /* CommandResult.Success */) {
|
|
45421
46151
|
return zoneIsInSheet;
|
|
45422
46152
|
}
|
|
45423
|
-
const updatedTable = this.
|
|
46153
|
+
const updatedTable = this.getCoreTableMatchingTopLeft(cmd.sheetId, cmd.zone);
|
|
45424
46154
|
if (!updatedTable) {
|
|
45425
46155
|
return "TableNotFound" /* CommandResult.TableNotFound */;
|
|
45426
46156
|
}
|
|
@@ -45470,7 +46200,7 @@ class TablePlugin extends CorePlugin {
|
|
|
45470
46200
|
};
|
|
45471
46201
|
}
|
|
45472
46202
|
updateTable(cmd) {
|
|
45473
|
-
const table = this.
|
|
46203
|
+
const table = this.getCoreTableMatchingTopLeft(cmd.sheetId, cmd.zone);
|
|
45474
46204
|
if (!table) {
|
|
45475
46205
|
return;
|
|
45476
46206
|
}
|
|
@@ -46143,8 +46873,8 @@ class UIPlugin extends BasePlugin {
|
|
|
46143
46873
|
getters;
|
|
46144
46874
|
ui;
|
|
46145
46875
|
selection;
|
|
46146
|
-
constructor({ getters, stateObserver, dispatch, uiActions, selection }) {
|
|
46147
|
-
super(stateObserver, dispatch);
|
|
46876
|
+
constructor({ getters, stateObserver, dispatch, canDispatch, uiActions, selection, }) {
|
|
46877
|
+
super(stateObserver, dispatch, canDispatch);
|
|
46148
46878
|
this.getters = getters;
|
|
46149
46879
|
this.ui = uiActions;
|
|
46150
46880
|
this.selection = selection;
|
|
@@ -47424,7 +48154,9 @@ class Evaluator {
|
|
|
47424
48154
|
this.blockedArrayFormulas = this.createEmptyPositionSet();
|
|
47425
48155
|
this.spreadingRelations = new SpreadingRelation(this.createEmptyPositionSet.bind(this));
|
|
47426
48156
|
this.formulaDependencies = lazy(() => {
|
|
47427
|
-
const dependencies = [...this.getAllCells()].flatMap((position) => this.getDirectDependencies(position)
|
|
48157
|
+
const dependencies = [...this.getAllCells()].flatMap((position) => this.getDirectDependencies(position)
|
|
48158
|
+
.filter((range) => !range.invalidSheetName && !range.invalidXc)
|
|
48159
|
+
.map((range) => ({
|
|
47428
48160
|
data: position,
|
|
47429
48161
|
boundingBox: {
|
|
47430
48162
|
zone: range.zone,
|
|
@@ -51508,6 +52240,8 @@ const invalidateTableStyleCommands = [
|
|
|
51508
52240
|
"CREATE_TABLE",
|
|
51509
52241
|
"UPDATE_TABLE",
|
|
51510
52242
|
"UPDATE_FILTER",
|
|
52243
|
+
"REMOVE_TABLE",
|
|
52244
|
+
"RESIZE_TABLE",
|
|
51511
52245
|
];
|
|
51512
52246
|
const invalidateTableStyleCommandsSet = new Set(invalidateTableStyleCommands);
|
|
51513
52247
|
function doesCommandInvalidatesTableStyle(cmd) {
|
|
@@ -51535,6 +52269,10 @@ class CellComputedStylePlugin extends UIPlugin {
|
|
|
51535
52269
|
this.styles = {};
|
|
51536
52270
|
return;
|
|
51537
52271
|
}
|
|
52272
|
+
if (invalidateBordersCommands.has(cmd.type)) {
|
|
52273
|
+
this.borders = {};
|
|
52274
|
+
return;
|
|
52275
|
+
}
|
|
51538
52276
|
}
|
|
51539
52277
|
getCellComputedBorder(position) {
|
|
51540
52278
|
const { sheetId, row, col } = position;
|
|
@@ -52138,7 +52876,10 @@ class TableAutofillPlugin extends UIPlugin {
|
|
|
52138
52876
|
const { col, row } = cmd;
|
|
52139
52877
|
const tableContentZone = getTableContentZone(table.range.zone, table.config);
|
|
52140
52878
|
if (tableContentZone && isInside(col, row, tableContentZone)) {
|
|
52141
|
-
|
|
52879
|
+
const top = cmd.autofillRowStart ?? tableContentZone.top;
|
|
52880
|
+
const bottom = cmd.autofillRowEnd ?? tableContentZone.bottom;
|
|
52881
|
+
const autofillZone = { ...tableContentZone, top, bottom };
|
|
52882
|
+
this.autofillTableZone(cmd, autofillZone);
|
|
52142
52883
|
}
|
|
52143
52884
|
break;
|
|
52144
52885
|
}
|
|
@@ -52172,6 +52913,50 @@ class TableAutofillPlugin extends UIPlugin {
|
|
|
52172
52913
|
}
|
|
52173
52914
|
}
|
|
52174
52915
|
|
|
52916
|
+
class TableResizeUI extends UIPlugin {
|
|
52917
|
+
allowDispatch(cmd) {
|
|
52918
|
+
switch (cmd.type) {
|
|
52919
|
+
case "RESIZE_TABLE":
|
|
52920
|
+
const table = this.getters.getCoreTableMatchingTopLeft(cmd.sheetId, cmd.zone);
|
|
52921
|
+
if (!table) {
|
|
52922
|
+
return "TableNotFound" /* CommandResult.TableNotFound */;
|
|
52923
|
+
}
|
|
52924
|
+
const oldTableZone = table.range.zone;
|
|
52925
|
+
const newTableZone = this.getters.getRangeFromRangeData(cmd.newTableRange).zone;
|
|
52926
|
+
if (newTableZone.top !== oldTableZone.top || newTableZone.left !== oldTableZone.left) {
|
|
52927
|
+
return "InvalidTableResize" /* CommandResult.InvalidTableResize */;
|
|
52928
|
+
}
|
|
52929
|
+
return this.canDispatch("UPDATE_TABLE", { ...cmd }).reasons;
|
|
52930
|
+
}
|
|
52931
|
+
return "Success" /* CommandResult.Success */;
|
|
52932
|
+
}
|
|
52933
|
+
handle(cmd) {
|
|
52934
|
+
switch (cmd.type) {
|
|
52935
|
+
case "RESIZE_TABLE": {
|
|
52936
|
+
const table = this.getters.getCoreTableMatchingTopLeft(cmd.sheetId, cmd.zone);
|
|
52937
|
+
this.dispatch("UPDATE_TABLE", { ...cmd });
|
|
52938
|
+
if (!table || !table.config.automaticAutofill)
|
|
52939
|
+
return;
|
|
52940
|
+
const oldTableZone = table.range.zone;
|
|
52941
|
+
const newTableZone = this.getters.getRangeFromRangeData(cmd.newTableRange).zone;
|
|
52942
|
+
if (newTableZone.bottom >= oldTableZone.bottom) {
|
|
52943
|
+
for (let col = newTableZone.left; col <= newTableZone.right; col++) {
|
|
52944
|
+
const autofillSource = { col, row: oldTableZone.bottom, sheetId: cmd.sheetId };
|
|
52945
|
+
if (this.getters.getCell(autofillSource)?.content.startsWith("=")) {
|
|
52946
|
+
this.dispatch("AUTOFILL_TABLE_COLUMN", {
|
|
52947
|
+
...autofillSource,
|
|
52948
|
+
autofillRowStart: oldTableZone.bottom,
|
|
52949
|
+
autofillRowEnd: newTableZone.bottom,
|
|
52950
|
+
});
|
|
52951
|
+
}
|
|
52952
|
+
}
|
|
52953
|
+
break;
|
|
52954
|
+
}
|
|
52955
|
+
}
|
|
52956
|
+
}
|
|
52957
|
+
}
|
|
52958
|
+
}
|
|
52959
|
+
|
|
52175
52960
|
/**
|
|
52176
52961
|
* Clipboard Plugin
|
|
52177
52962
|
*
|
|
@@ -53846,13 +54631,14 @@ class SheetViewPlugin extends UIPlugin {
|
|
|
53846
54631
|
}
|
|
53847
54632
|
}
|
|
53848
54633
|
handleEvent(event) {
|
|
54634
|
+
const sheetId = this.getters.getActiveSheetId();
|
|
53849
54635
|
if (event.options.scrollIntoView) {
|
|
53850
54636
|
let { col, row } = findCellInNewZone(event.previousAnchor.zone, event.anchor.zone);
|
|
53851
54637
|
if (event.mode === "updateAnchor") {
|
|
53852
54638
|
const oldZone = event.previousAnchor.zone;
|
|
53853
54639
|
const newZone = event.anchor.zone;
|
|
53854
54640
|
// altering a zone should not move the viewport in a dimension that wasn't changed
|
|
53855
|
-
const { top, bottom, left, right } = this.
|
|
54641
|
+
const { top, bottom, left, right } = this.getMainInternalViewport(sheetId);
|
|
53856
54642
|
if (oldZone.left === newZone.left && oldZone.right === newZone.right) {
|
|
53857
54643
|
col = left > col || col > right ? left : col;
|
|
53858
54644
|
}
|
|
@@ -53860,7 +54646,6 @@ class SheetViewPlugin extends UIPlugin {
|
|
|
53860
54646
|
row = top > row || row > bottom ? top : row;
|
|
53861
54647
|
}
|
|
53862
54648
|
}
|
|
53863
|
-
const sheetId = this.getters.getActiveSheetId();
|
|
53864
54649
|
col = Math.min(col, this.getters.getNumberCols(sheetId) - 1);
|
|
53865
54650
|
row = Math.min(row, this.getters.getNumberRows(sheetId) - 1);
|
|
53866
54651
|
if (!this.sheetsWithDirtyViewports.has(sheetId)) {
|
|
@@ -53897,16 +54682,16 @@ class SheetViewPlugin extends UIPlugin {
|
|
|
53897
54682
|
this.setSheetViewOffset(cmd.offsetX, cmd.offsetY);
|
|
53898
54683
|
break;
|
|
53899
54684
|
case "SHIFT_VIEWPORT_DOWN":
|
|
53900
|
-
const { top } = this.getActiveMainViewport();
|
|
53901
54685
|
const sheetId = this.getters.getActiveSheetId();
|
|
53902
|
-
const
|
|
53903
|
-
this.
|
|
54686
|
+
const { top, viewportHeight, offsetCorrectionY } = this.getMainInternalViewport(sheetId);
|
|
54687
|
+
const topRowDims = this.getters.getRowDimensions(sheetId, top);
|
|
54688
|
+
this.shiftVertically(topRowDims.start + viewportHeight - offsetCorrectionY);
|
|
53904
54689
|
break;
|
|
53905
54690
|
case "SHIFT_VIEWPORT_UP": {
|
|
53906
|
-
const { top } = this.getActiveMainViewport();
|
|
53907
54691
|
const sheetId = this.getters.getActiveSheetId();
|
|
53908
|
-
const
|
|
53909
|
-
this.
|
|
54692
|
+
const { top, viewportHeight, offsetCorrectionY } = this.getMainInternalViewport(sheetId);
|
|
54693
|
+
const topRowDims = this.getters.getRowDimensions(sheetId, top);
|
|
54694
|
+
this.shiftVertically(topRowDims.end - offsetCorrectionY - viewportHeight);
|
|
53910
54695
|
break;
|
|
53911
54696
|
}
|
|
53912
54697
|
case "REMOVE_TABLE":
|
|
@@ -54329,17 +55114,6 @@ class SheetViewPlugin extends UIPlugin {
|
|
|
54329
55114
|
const { maxOffsetX, maxOffsetY } = this.getMaximumSheetOffset();
|
|
54330
55115
|
Object.values(this.getSubViewports(sheetId)).forEach((viewport) => viewport.setViewportOffset(clip(offsetX, 0, maxOffsetX), clip(offsetY, 0, maxOffsetY)));
|
|
54331
55116
|
}
|
|
54332
|
-
/**
|
|
54333
|
-
* Clip the vertical offset within the allowed range.
|
|
54334
|
-
* Not above the sheet, nor below the sheet.
|
|
54335
|
-
*/
|
|
54336
|
-
clipOffsetY(offsetY) {
|
|
54337
|
-
const { height } = this.getMainViewportRect();
|
|
54338
|
-
const maxOffset = height - this.sheetViewHeight;
|
|
54339
|
-
offsetY = Math.min(offsetY, maxOffset);
|
|
54340
|
-
offsetY = Math.max(offsetY, 0);
|
|
54341
|
-
return offsetY;
|
|
54342
|
-
}
|
|
54343
55117
|
getViewportOffset(sheetId) {
|
|
54344
55118
|
return {
|
|
54345
55119
|
x: this.viewports[sheetId]?.bottomRight.offsetScrollbarX || 0,
|
|
@@ -54395,12 +55169,15 @@ class SheetViewPlugin extends UIPlugin {
|
|
|
54395
55169
|
* viewport top.
|
|
54396
55170
|
*/
|
|
54397
55171
|
shiftVertically(offset) {
|
|
54398
|
-
const
|
|
55172
|
+
const sheetId = this.getters.getActiveSheetId();
|
|
55173
|
+
const { top } = this.getMainInternalViewport(sheetId);
|
|
54399
55174
|
const { scrollX } = this.getActiveSheetScrollInfo();
|
|
54400
55175
|
this.setSheetViewOffset(scrollX, offset);
|
|
54401
55176
|
const { anchor } = this.getters.getSelection();
|
|
54402
|
-
|
|
54403
|
-
|
|
55177
|
+
if (anchor.cell.row >= this.getters.getPaneDivisions(sheetId).ySplit) {
|
|
55178
|
+
const deltaRow = this.getMainInternalViewport(sheetId).top - top;
|
|
55179
|
+
this.selection.selectCell(anchor.cell.col, anchor.cell.row + deltaRow);
|
|
55180
|
+
}
|
|
54404
55181
|
}
|
|
54405
55182
|
getVisibleFigures() {
|
|
54406
55183
|
const sheetId = this.getters.getActiveSheetId();
|
|
@@ -54589,7 +55366,8 @@ const featurePluginRegistry = new Registry()
|
|
|
54589
55366
|
.add("collaborative", CollaborativePlugin)
|
|
54590
55367
|
.add("history", HistoryPlugin)
|
|
54591
55368
|
.add("data_cleanup", DataCleanupPlugin)
|
|
54592
|
-
.add("table_autofill", TableAutofillPlugin)
|
|
55369
|
+
.add("table_autofill", TableAutofillPlugin)
|
|
55370
|
+
.add("table_ui_resize", TableResizeUI);
|
|
54593
55371
|
// Plugins which have a state, but which should not be shared in collaborative
|
|
54594
55372
|
const statefulUIPluginRegistry = new Registry()
|
|
54595
55373
|
.add("selection", GridSelectionPlugin)
|
|
@@ -54657,38 +55435,6 @@ class ImageProvider {
|
|
|
54657
55435
|
}
|
|
54658
55436
|
}
|
|
54659
55437
|
|
|
54660
|
-
class ArrayFormulaHighlight extends SpreadsheetStore {
|
|
54661
|
-
highlightStore = this.get(HighlightStore);
|
|
54662
|
-
constructor(get) {
|
|
54663
|
-
super(get);
|
|
54664
|
-
this.highlightStore.register(this);
|
|
54665
|
-
}
|
|
54666
|
-
get highlights() {
|
|
54667
|
-
const zone = this.getHighlightZone();
|
|
54668
|
-
if (!zone) {
|
|
54669
|
-
return [];
|
|
54670
|
-
}
|
|
54671
|
-
const sheetId = this.model.getters.getActiveSheetId();
|
|
54672
|
-
return [
|
|
54673
|
-
{
|
|
54674
|
-
sheetId,
|
|
54675
|
-
zone,
|
|
54676
|
-
color: "#17A2B8",
|
|
54677
|
-
noFill: true,
|
|
54678
|
-
thinLine: true,
|
|
54679
|
-
},
|
|
54680
|
-
];
|
|
54681
|
-
}
|
|
54682
|
-
getHighlightZone() {
|
|
54683
|
-
const position = this.model.getters.getActivePosition();
|
|
54684
|
-
const spreader = this.model.getters.getArrayFormulaSpreadingOn(position);
|
|
54685
|
-
const spreadZone = spreader
|
|
54686
|
-
? this.model.getters.getSpreadZone(spreader)
|
|
54687
|
-
: this.model.getters.getSpreadZone(position);
|
|
54688
|
-
return spreadZone;
|
|
54689
|
-
}
|
|
54690
|
-
}
|
|
54691
|
-
|
|
54692
55438
|
const RIPPLE_KEY_FRAMES = [
|
|
54693
55439
|
{ transform: "scale(0)" },
|
|
54694
55440
|
{ transform: "scale(0.8)", offset: 0.33 },
|
|
@@ -55475,7 +56221,6 @@ class SpreadsheetDashboard extends Component {
|
|
|
55475
56221
|
Popover,
|
|
55476
56222
|
VerticalScrollBar,
|
|
55477
56223
|
HorizontalScrollBar,
|
|
55478
|
-
FilterIconsOverlay,
|
|
55479
56224
|
};
|
|
55480
56225
|
cellPopovers;
|
|
55481
56226
|
onMouseWheel;
|
|
@@ -56362,6 +57107,13 @@ class TopBarComposer extends Component {
|
|
|
56362
57107
|
"border-color": SELECTION_BORDER_COLOR,
|
|
56363
57108
|
});
|
|
56364
57109
|
}
|
|
57110
|
+
get delimitation() {
|
|
57111
|
+
const { width, height } = this.env.model.getters.getSheetViewDimensionWithHeaders();
|
|
57112
|
+
return {
|
|
57113
|
+
width,
|
|
57114
|
+
height,
|
|
57115
|
+
};
|
|
57116
|
+
}
|
|
56365
57117
|
onFocus(selection) {
|
|
56366
57118
|
this.composerFocusStore.focusTopBarComposer(selection);
|
|
56367
57119
|
}
|
|
@@ -57016,7 +57768,6 @@ class Spreadsheet extends Component {
|
|
|
57016
57768
|
this.notificationStore = useStore(NotificationStore);
|
|
57017
57769
|
this.composerFocusStore = useStore(ComposerFocusStore);
|
|
57018
57770
|
this.sidePanel = useStore(SidePanelStore);
|
|
57019
|
-
useStore(ArrayFormulaHighlight);
|
|
57020
57771
|
this.keyDownMapping = {
|
|
57021
57772
|
"CTRL+H": () => this.sidePanel.toggle("FindAndReplace", {}),
|
|
57022
57773
|
"CTRL+F": () => this.sidePanel.toggle("FindAndReplace", {}),
|
|
@@ -58645,6 +59396,9 @@ function createChart(chart, chartSheetIndex, data) {
|
|
|
58645
59396
|
case "line":
|
|
58646
59397
|
plot = addLineChart(chart.data);
|
|
58647
59398
|
break;
|
|
59399
|
+
case "scatter":
|
|
59400
|
+
plot = addScatterChart(chart.data);
|
|
59401
|
+
break;
|
|
58648
59402
|
case "pie":
|
|
58649
59403
|
plot = addDoughnutChart(chart.data, chartSheetIndex, data, { holeSize: 0 });
|
|
58650
59404
|
break;
|
|
@@ -58922,6 +59676,52 @@ function addLineChart(chart) {
|
|
|
58922
59676
|
${addAx(axisPos, "c:valAx", valAxId, catAxId, { fontColor: chart.fontColor })}
|
|
58923
59677
|
`;
|
|
58924
59678
|
}
|
|
59679
|
+
function addScatterChart(chart) {
|
|
59680
|
+
const colors = new ChartColors();
|
|
59681
|
+
const dataSetsNodes = [];
|
|
59682
|
+
for (const [dsIndex, dataset] of Object.entries(chart.dataSets)) {
|
|
59683
|
+
dataSetsNodes.push(escapeXml /*xml*/ `
|
|
59684
|
+
<c:ser>
|
|
59685
|
+
<c:idx val="${dsIndex}"/>
|
|
59686
|
+
<c:order val="${dsIndex}"/>
|
|
59687
|
+
<c:smooth val="0"/>
|
|
59688
|
+
<c:spPr>
|
|
59689
|
+
<a:ln w="19050" cap="rnd">
|
|
59690
|
+
<a:noFill/>
|
|
59691
|
+
<a:round/>
|
|
59692
|
+
</a:ln>
|
|
59693
|
+
<a:effectLst/>
|
|
59694
|
+
</c:spPr>
|
|
59695
|
+
<c:marker>
|
|
59696
|
+
<c:symbol val="circle" />
|
|
59697
|
+
<c:size val="5"/>
|
|
59698
|
+
${shapeProperty({ backgroundColor: toXlsxHexColor(colors.next()) })}
|
|
59699
|
+
</c:marker>
|
|
59700
|
+
${chart.labelRange
|
|
59701
|
+
? escapeXml /*xml*/ `<c:xVal> <!-- x-coordinate values -->
|
|
59702
|
+
${numberRef(chart.labelRange)}
|
|
59703
|
+
</c:xVal>`
|
|
59704
|
+
: ""}
|
|
59705
|
+
<c:yVal> <!-- y-coordinate values -->
|
|
59706
|
+
${numberRef(dataset.range)}
|
|
59707
|
+
</c:yVal>
|
|
59708
|
+
</c:ser>
|
|
59709
|
+
`);
|
|
59710
|
+
}
|
|
59711
|
+
const axisPos = chart.verticalAxisPosition === "left" ? "l" : "r";
|
|
59712
|
+
return escapeXml /*xml*/ `
|
|
59713
|
+
<c:scatterChart>
|
|
59714
|
+
<!-- each data marker in the series does not have a different color -->
|
|
59715
|
+
<c:varyColors val="0"/>
|
|
59716
|
+
<c:scatterStyle val="lineMarker"/>
|
|
59717
|
+
${joinXmlNodes(dataSetsNodes)}
|
|
59718
|
+
<c:axId val="${catAxId}" />
|
|
59719
|
+
<c:axId val="${valAxId}" />
|
|
59720
|
+
</c:scatterChart>
|
|
59721
|
+
${addAx("b", "c:valAx", catAxId, valAxId, { fontColor: chart.fontColor })}
|
|
59722
|
+
${addAx(axisPos, "c:valAx", valAxId, catAxId, { fontColor: chart.fontColor })}
|
|
59723
|
+
`;
|
|
59724
|
+
}
|
|
58925
59725
|
function addDoughnutChart(chart, chartSheetIndex, data, { holeSize } = { holeSize: 50 }) {
|
|
58926
59726
|
const colors = new ChartColors();
|
|
58927
59727
|
const maxLength = largeMax(chart.dataSets.map((ds) => getRangeSize(ds.range, chartSheetIndex, data)));
|
|
@@ -59062,8 +59862,7 @@ function addContent(content, sharedStrings, forceString = false) {
|
|
|
59062
59862
|
attrs.push(["t", "b"]);
|
|
59063
59863
|
}
|
|
59064
59864
|
else if (forceString || !isNumber(value, DEFAULT_LOCALE)) {
|
|
59065
|
-
|
|
59066
|
-
value = id.toString();
|
|
59865
|
+
value = pushElement(content, sharedStrings);
|
|
59067
59866
|
attrs.push(["t", "s"]);
|
|
59068
59867
|
}
|
|
59069
59868
|
return { attrs, node: escapeXml /*xml*/ `<v>${value}</v>` };
|
|
@@ -59188,8 +59987,7 @@ function addCellIsRule(cf, rule, dxfs) {
|
|
|
59188
59987
|
if (rule.style.fillColor) {
|
|
59189
59988
|
dxf.fill = { fgColor: { rgb: rule.style.fillColor } };
|
|
59190
59989
|
}
|
|
59191
|
-
|
|
59192
|
-
ruleAttributes.push(["dxfId", id]);
|
|
59990
|
+
ruleAttributes.push(["dxfId", pushElement(dxf, dxfs)]);
|
|
59193
59991
|
return escapeXml /*xml*/ `
|
|
59194
59992
|
<conditionalFormatting sqref="${cf.ranges.join(" ")}">
|
|
59195
59993
|
<cfRule ${formatAttributes(ruleAttributes)}>
|
|
@@ -60019,8 +60817,9 @@ function addSheetViews(sheet) {
|
|
|
60019
60817
|
*/
|
|
60020
60818
|
function getXLSX(data) {
|
|
60021
60819
|
data = fixLengthySheetNames(data);
|
|
60820
|
+
data = purgeSingleRowTables(data);
|
|
60022
60821
|
const files = [];
|
|
60023
|
-
const construct = getDefaultXLSXStructure();
|
|
60822
|
+
const construct = getDefaultXLSXStructure(data);
|
|
60024
60823
|
files.push(createWorkbook(data, construct));
|
|
60025
60824
|
files.push(...createWorksheets(data, construct));
|
|
60026
60825
|
files.push(createStylesSheet(construct));
|
|
@@ -60300,6 +61099,16 @@ function fixLengthySheetNames(data) {
|
|
|
60300
61099
|
}
|
|
60301
61100
|
return JSON.parse(stringifiedData);
|
|
60302
61101
|
}
|
|
61102
|
+
/** Excel files do not support tables with a single row the defined range
|
|
61103
|
+
* Since those tables are not really useful (no filtering/limited styling)
|
|
61104
|
+
* This function filters out all tables with a single row.
|
|
61105
|
+
*/
|
|
61106
|
+
function purgeSingleRowTables(data) {
|
|
61107
|
+
for (const sheet of data.sheets) {
|
|
61108
|
+
sheet.tables = sheet.tables.filter((table) => zoneToDimension(toZone(table.range)).numberOfRows > 1);
|
|
61109
|
+
}
|
|
61110
|
+
return data;
|
|
61111
|
+
}
|
|
60303
61112
|
|
|
60304
61113
|
var Status;
|
|
60305
61114
|
(function (Status) {
|
|
@@ -60553,6 +61362,7 @@ class Model extends EventBus {
|
|
|
60553
61362
|
stateObserver: this.state,
|
|
60554
61363
|
range: this.range,
|
|
60555
61364
|
dispatch: this.dispatchFromCorePlugin,
|
|
61365
|
+
canDispatch: this.canDispatch,
|
|
60556
61366
|
uuidGenerator: this.uuidGenerator,
|
|
60557
61367
|
custom: this.config.custom,
|
|
60558
61368
|
external: this.config.external,
|
|
@@ -60563,6 +61373,7 @@ class Model extends EventBus {
|
|
|
60563
61373
|
getters: this.getters,
|
|
60564
61374
|
stateObserver: this.state,
|
|
60565
61375
|
dispatch: this.dispatch,
|
|
61376
|
+
canDispatch: this.canDispatch,
|
|
60566
61377
|
selection: this.selection,
|
|
60567
61378
|
moveClient: this.session.move.bind(this.session),
|
|
60568
61379
|
custom: this.config.custom,
|
|
@@ -60886,7 +61697,7 @@ const links = {
|
|
|
60886
61697
|
const components = {
|
|
60887
61698
|
Checkbox,
|
|
60888
61699
|
Section,
|
|
60889
|
-
|
|
61700
|
+
RoundColorPicker,
|
|
60890
61701
|
ChartDataSeries,
|
|
60891
61702
|
ChartErrorSection,
|
|
60892
61703
|
ChartLabelRange,
|
|
@@ -60898,9 +61709,9 @@ const components = {
|
|
|
60898
61709
|
GridOverlay,
|
|
60899
61710
|
ScorecardChart,
|
|
60900
61711
|
LineConfigPanel,
|
|
60901
|
-
|
|
61712
|
+
GenericChartDesignPanel,
|
|
60902
61713
|
BarConfigPanel,
|
|
60903
|
-
|
|
61714
|
+
GenericChartConfigPanel,
|
|
60904
61715
|
GaugeChartConfigPanel,
|
|
60905
61716
|
GaugeChartDesignPanel,
|
|
60906
61717
|
ScorecardChartConfigPanel,
|
|
@@ -60948,6 +61759,6 @@ const constants = {
|
|
|
60948
61759
|
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 };
|
|
60949
61760
|
|
|
60950
61761
|
|
|
60951
|
-
__info__.version = "17.3.0-alpha.
|
|
60952
|
-
__info__.date = "2024-04-
|
|
60953
|
-
__info__.hash = "
|
|
61762
|
+
__info__.version = "17.3.0-alpha.4";
|
|
61763
|
+
__info__.date = "2024-04-15T11:02:51.551Z";
|
|
61764
|
+
__info__.hash = "a32a1df";
|