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