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