@odoo/o-spreadsheet 17.3.0-alpha.2 → 17.3.0-alpha.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/o-spreadsheet.cjs.js +644 -346
- package/dist/o-spreadsheet.d.ts +118 -109
- package/dist/o-spreadsheet.esm.js +644 -346
- package/dist/o-spreadsheet.iife.js +644 -346
- package/dist/o-spreadsheet.iife.min.js +357 -309
- package/dist/o_spreadsheet.xml +140 -90
- package/package.json +1 -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.3
|
|
7
|
+
* @date 2024-04-10T12:28:23.658Z
|
|
8
|
+
* @hash 80b5056
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
11
|
'use strict';
|
|
@@ -190,7 +190,7 @@ const DEFAULT_GAUGE_LOWER_COLOR = "#cc0000";
|
|
|
190
190
|
const DEFAULT_GAUGE_MIDDLE_COLOR = "#f1c232";
|
|
191
191
|
const DEFAULT_GAUGE_UPPER_COLOR = "#6aa84f";
|
|
192
192
|
const DEFAULT_SCORECARD_BASELINE_MODE = "difference";
|
|
193
|
-
const DEFAULT_SCORECARD_BASELINE_COLOR_UP = "#
|
|
193
|
+
const DEFAULT_SCORECARD_BASELINE_COLOR_UP = "#6AA84F";
|
|
194
194
|
const DEFAULT_SCORECARD_BASELINE_COLOR_DOWN = "#E06666";
|
|
195
195
|
const LINE_FILL_TRANSPARENCY = 0.4;
|
|
196
196
|
// session
|
|
@@ -562,7 +562,7 @@ function deepEquals(o1, o2) {
|
|
|
562
562
|
if (typeof o1 !== typeof o2)
|
|
563
563
|
return false;
|
|
564
564
|
if (typeof o1 !== "object")
|
|
565
|
-
return
|
|
565
|
+
return false;
|
|
566
566
|
// Objects can have different keys if the values are undefined
|
|
567
567
|
for (const key in o2) {
|
|
568
568
|
if (!(key in o1) && o2[key] !== undefined) {
|
|
@@ -1886,6 +1886,11 @@ const invalidateCFEvaluationCommands = new Set([
|
|
|
1886
1886
|
"REMOVE_CONDITIONAL_FORMAT",
|
|
1887
1887
|
"CHANGE_CONDITIONAL_FORMAT_PRIORITY",
|
|
1888
1888
|
]);
|
|
1889
|
+
const invalidateBordersCommands = new Set([
|
|
1890
|
+
"AUTOFILL_CELL",
|
|
1891
|
+
"SET_BORDER",
|
|
1892
|
+
"SET_ZONE_BORDERS",
|
|
1893
|
+
]);
|
|
1889
1894
|
const readonlyAllowedCommands = new Set([
|
|
1890
1895
|
"START",
|
|
1891
1896
|
"ACTIVATE_SHEET",
|
|
@@ -2183,6 +2188,7 @@ const CellErrorType = {
|
|
|
2183
2188
|
BadExpression: "#BAD_EXPR",
|
|
2184
2189
|
CircularDependency: "#CYCLE",
|
|
2185
2190
|
UnknownFunction: "#NAME?",
|
|
2191
|
+
DivisionByZero: "#DIV/0!",
|
|
2186
2192
|
GenericError: "#ERROR",
|
|
2187
2193
|
};
|
|
2188
2194
|
const errorTypes = new Set(Object.values(CellErrorType));
|
|
@@ -2221,9 +2227,9 @@ class UnknownFunctionError extends EvaluationError {
|
|
|
2221
2227
|
|
|
2222
2228
|
// HELPERS
|
|
2223
2229
|
const SORT_TYPES_ORDER = ["number", "string", "boolean", "undefined"];
|
|
2224
|
-
function assert(condition, message) {
|
|
2230
|
+
function assert(condition, message, value) {
|
|
2225
2231
|
if (!condition()) {
|
|
2226
|
-
throw new EvaluationError(message);
|
|
2232
|
+
throw new EvaluationError(message, value);
|
|
2227
2233
|
}
|
|
2228
2234
|
}
|
|
2229
2235
|
function inferFormat(data) {
|
|
@@ -2301,6 +2307,9 @@ function strictToInteger(value, locale) {
|
|
|
2301
2307
|
function assertNumberGreaterThanOrEqualToOne(value) {
|
|
2302
2308
|
assert(() => value >= 1, _t("The function [[FUNCTION_NAME]] expects a number value to be greater than or equal to 1, but receives %s.", value.toString()));
|
|
2303
2309
|
}
|
|
2310
|
+
function assertNotZero(value) {
|
|
2311
|
+
assert(() => value !== 0, _t("Evaluation of function [[FUNCTION_NAME]] caused a divide by zero error."), CellErrorType.DivisionByZero);
|
|
2312
|
+
}
|
|
2304
2313
|
function toString(data) {
|
|
2305
2314
|
const value = toValue(data);
|
|
2306
2315
|
switch (typeof value) {
|
|
@@ -3066,6 +3075,9 @@ function applyInternalFormat(value, internalFormat, locale) {
|
|
|
3066
3075
|
return formattedValue;
|
|
3067
3076
|
}
|
|
3068
3077
|
function applyInternalNumberFormat(value, format, locale) {
|
|
3078
|
+
if (value === Infinity) {
|
|
3079
|
+
return "∞" + (format.isPercent ? "%" : "");
|
|
3080
|
+
}
|
|
3069
3081
|
if (format.isPercent) {
|
|
3070
3082
|
value = value * 100;
|
|
3071
3083
|
}
|
|
@@ -3409,6 +3421,46 @@ function roundFormat(format) {
|
|
|
3409
3421
|
});
|
|
3410
3422
|
return convertInternalFormatToFormat(roundedFormat);
|
|
3411
3423
|
}
|
|
3424
|
+
function humanizeNumber({ value, format }, locale) {
|
|
3425
|
+
const numberFormat = formatLargeNumber({
|
|
3426
|
+
value,
|
|
3427
|
+
format,
|
|
3428
|
+
}, undefined, locale);
|
|
3429
|
+
return formatValue(value, { format: numberFormat, locale });
|
|
3430
|
+
}
|
|
3431
|
+
function formatLargeNumber(arg, unit, locale) {
|
|
3432
|
+
let value = 0;
|
|
3433
|
+
try {
|
|
3434
|
+
value = Math.abs(toNumber(arg?.value, locale));
|
|
3435
|
+
}
|
|
3436
|
+
catch (e) {
|
|
3437
|
+
return "";
|
|
3438
|
+
}
|
|
3439
|
+
const format = arg?.format;
|
|
3440
|
+
if (unit !== undefined) {
|
|
3441
|
+
const postFix = unit?.value;
|
|
3442
|
+
switch (postFix) {
|
|
3443
|
+
case "k":
|
|
3444
|
+
return createLargeNumberFormat(format, 1e3, "k");
|
|
3445
|
+
case "m":
|
|
3446
|
+
return createLargeNumberFormat(format, 1e6, "m");
|
|
3447
|
+
case "b":
|
|
3448
|
+
return createLargeNumberFormat(format, 1e9, "b");
|
|
3449
|
+
default:
|
|
3450
|
+
throw new EvaluationError(_t("The formatting unit should be 'k', 'm' or 'b'."));
|
|
3451
|
+
}
|
|
3452
|
+
}
|
|
3453
|
+
if (value < 1e5) {
|
|
3454
|
+
return createLargeNumberFormat(format, 0, "");
|
|
3455
|
+
}
|
|
3456
|
+
else if (value < 1e8) {
|
|
3457
|
+
return createLargeNumberFormat(format, 1e3, "k");
|
|
3458
|
+
}
|
|
3459
|
+
else if (value < 1e11) {
|
|
3460
|
+
return createLargeNumberFormat(format, 1e6, "m");
|
|
3461
|
+
}
|
|
3462
|
+
return createLargeNumberFormat(format, 1e9, "b");
|
|
3463
|
+
}
|
|
3412
3464
|
function createLargeNumberFormat(format, magnitude, postFix, locale) {
|
|
3413
3465
|
const internalFormat = parseFormat(format || "#,##0");
|
|
3414
3466
|
const largeNumberFormat = [];
|
|
@@ -4601,14 +4653,19 @@ function getDefaultCellHeight(ctx, cell, colSize) {
|
|
|
4601
4653
|
const textWidthCache = {};
|
|
4602
4654
|
function computeTextWidth(context, text, style, fontUnit = "pt") {
|
|
4603
4655
|
const font = computeTextFont(style, fontUnit);
|
|
4656
|
+
context.save();
|
|
4657
|
+
context.font = font;
|
|
4658
|
+
const width = computeCachedTextWidth(context, text);
|
|
4659
|
+
context.restore();
|
|
4660
|
+
return width;
|
|
4661
|
+
}
|
|
4662
|
+
function computeCachedTextWidth(context, text) {
|
|
4663
|
+
const font = context.font;
|
|
4604
4664
|
if (!textWidthCache[font]) {
|
|
4605
4665
|
textWidthCache[font] = {};
|
|
4606
4666
|
}
|
|
4607
4667
|
if (textWidthCache[font][text] === undefined) {
|
|
4608
|
-
context.save();
|
|
4609
|
-
context.font = font;
|
|
4610
4668
|
const textWidth = context.measureText(text).width;
|
|
4611
|
-
context.restore();
|
|
4612
4669
|
textWidthCache[font][text] = textWidth;
|
|
4613
4670
|
}
|
|
4614
4671
|
return textWidthCache[font][text];
|
|
@@ -4753,6 +4810,42 @@ const pxRegex = /([0-9\.]*)px/;
|
|
|
4753
4810
|
function getContextFontSize(font) {
|
|
4754
4811
|
return Number(font.match(pxRegex)?.[1]);
|
|
4755
4812
|
}
|
|
4813
|
+
// Inspired from https://stackoverflow.com/a/10511598
|
|
4814
|
+
function clipTextWithEllipsis(ctx, text, maxWidth) {
|
|
4815
|
+
let width = computeCachedTextWidth(ctx, text);
|
|
4816
|
+
if (width <= maxWidth) {
|
|
4817
|
+
return text;
|
|
4818
|
+
}
|
|
4819
|
+
const ellipsis = "…";
|
|
4820
|
+
const ellipsisWidth = computeCachedTextWidth(ctx, text);
|
|
4821
|
+
if (width <= ellipsisWidth) {
|
|
4822
|
+
return text;
|
|
4823
|
+
}
|
|
4824
|
+
let len = text.length;
|
|
4825
|
+
while (width >= maxWidth - ellipsisWidth && len-- > 0) {
|
|
4826
|
+
text = text.substring(0, len);
|
|
4827
|
+
width = computeCachedTextWidth(ctx, text);
|
|
4828
|
+
}
|
|
4829
|
+
return text + ellipsis;
|
|
4830
|
+
}
|
|
4831
|
+
function splitTextInTwoLines(text) {
|
|
4832
|
+
let spaces = "";
|
|
4833
|
+
while (text[0] === " ") {
|
|
4834
|
+
spaces += " ";
|
|
4835
|
+
text = text.slice(1);
|
|
4836
|
+
}
|
|
4837
|
+
const length = text.length;
|
|
4838
|
+
const middle = Math.floor(length / 2);
|
|
4839
|
+
const leftSpace = text.substring(0, middle).lastIndexOf(" ");
|
|
4840
|
+
const rightSpace = text.substring(middle).indexOf(" ") + middle;
|
|
4841
|
+
if (leftSpace === -1 && rightSpace === middle - 1) {
|
|
4842
|
+
return [spaces + text, ""];
|
|
4843
|
+
}
|
|
4844
|
+
if (leftSpace > length - rightSpace || rightSpace === middle - 1) {
|
|
4845
|
+
return [spaces + text.slice(0, leftSpace), spaces + text.slice(leftSpace + 1)];
|
|
4846
|
+
}
|
|
4847
|
+
return [spaces + text.slice(0, rightSpace), spaces + text.slice(rightSpace + 1)];
|
|
4848
|
+
}
|
|
4756
4849
|
function drawDecoratedText(context, text, position, underline = false, strikethrough = false, strokeWidth = getContextFontSize(context.font) / 10 //This value is defined to get a good looking stroke
|
|
4757
4850
|
) {
|
|
4758
4851
|
context.fillText(text, position.x, position.y);
|
|
@@ -7396,6 +7489,7 @@ const CellIsOperators = {
|
|
|
7396
7489
|
};
|
|
7397
7490
|
const ChartTerms = {
|
|
7398
7491
|
Series: _t("Series"),
|
|
7492
|
+
BackgroundColor: _t("Background color"),
|
|
7399
7493
|
Errors: {
|
|
7400
7494
|
Unexpected: _t("The chart definition is invalid for an unknown reason"),
|
|
7401
7495
|
// BASIC CHART ERRORS (LINE | BAR | PIE)
|
|
@@ -9431,32 +9525,63 @@ function shouldRemoveFirstLabel(labelRange, dataset, dataSetsHaveTitle) {
|
|
|
9431
9525
|
}
|
|
9432
9526
|
return true;
|
|
9433
9527
|
}
|
|
9434
|
-
|
|
9435
|
-
|
|
9436
|
-
|
|
9437
|
-
|
|
9528
|
+
function getChartPositionAtCenterOfViewport(getters, chartSize) {
|
|
9529
|
+
const { x, y } = getters.getMainViewportCoordinates();
|
|
9530
|
+
const { scrollX, scrollY } = getters.getActiveSheetScrollInfo();
|
|
9531
|
+
const { width, height } = getters.getVisibleRect(getters.getActiveMainViewport());
|
|
9532
|
+
const position = {
|
|
9533
|
+
x: x + scrollX + Math.max(0, (width - chartSize.width) / 2),
|
|
9534
|
+
y: y + scrollY + Math.max(0, (height - chartSize.height) / 2),
|
|
9535
|
+
}; // Position at the center of the scrollable viewport
|
|
9536
|
+
return position;
|
|
9537
|
+
}
|
|
9538
|
+
|
|
9539
|
+
function getBaselineText(baseline, keyValue, baselineMode, humanize, locale) {
|
|
9438
9540
|
if (!baseline) {
|
|
9439
9541
|
return "";
|
|
9440
9542
|
}
|
|
9441
9543
|
else if (baselineMode === "text" ||
|
|
9442
9544
|
keyValue?.type !== CellValueType.number ||
|
|
9443
9545
|
baseline.type !== CellValueType.number) {
|
|
9546
|
+
if (humanize) {
|
|
9547
|
+
return humanizeNumber(baseline, locale);
|
|
9548
|
+
}
|
|
9444
9549
|
return baseline.formattedValue;
|
|
9445
9550
|
}
|
|
9551
|
+
let { value, format } = baseline;
|
|
9552
|
+
if (baselineMode === "progress") {
|
|
9553
|
+
value = keyValue.value / value;
|
|
9554
|
+
format = "0.0%";
|
|
9555
|
+
}
|
|
9446
9556
|
else {
|
|
9447
|
-
|
|
9448
|
-
if (baselineMode === "percentage" &&
|
|
9449
|
-
|
|
9557
|
+
value = Math.abs(keyValue.value - value);
|
|
9558
|
+
if (baselineMode === "percentage" && value !== 0) {
|
|
9559
|
+
value = value / baseline.value;
|
|
9450
9560
|
}
|
|
9451
|
-
if (baselineMode
|
|
9452
|
-
|
|
9561
|
+
if (baselineMode === "percentage") {
|
|
9562
|
+
format = "0.0%";
|
|
9453
9563
|
}
|
|
9454
|
-
|
|
9455
|
-
|
|
9564
|
+
if (!format) {
|
|
9565
|
+
value = Math.round(value * 100) / 100;
|
|
9566
|
+
}
|
|
9567
|
+
}
|
|
9568
|
+
if (humanize) {
|
|
9569
|
+
return humanizeNumber({ value, format }, locale);
|
|
9456
9570
|
}
|
|
9571
|
+
return formatValue(value, { format, locale });
|
|
9572
|
+
}
|
|
9573
|
+
function getKeyValueText(keyValueCell, humanize, locale) {
|
|
9574
|
+
if (!keyValueCell) {
|
|
9575
|
+
return "";
|
|
9576
|
+
}
|
|
9577
|
+
if (humanize) {
|
|
9578
|
+
return humanizeNumber(keyValueCell, locale);
|
|
9579
|
+
}
|
|
9580
|
+
return keyValueCell.formattedValue ?? String(keyValueCell.value ?? "");
|
|
9457
9581
|
}
|
|
9458
9582
|
function getBaselineColor(baseline, baselineMode, keyValue, colorUp, colorDown) {
|
|
9459
9583
|
if (baselineMode === "text" ||
|
|
9584
|
+
baselineMode === "progress" ||
|
|
9460
9585
|
baseline?.type !== CellValueType.number ||
|
|
9461
9586
|
keyValue?.type !== CellValueType.number) {
|
|
9462
9587
|
return undefined;
|
|
@@ -9485,17 +9610,6 @@ function getBaselineArrowDirection(baseline, keyValue, baselineMode) {
|
|
|
9485
9610
|
}
|
|
9486
9611
|
return "neutral";
|
|
9487
9612
|
}
|
|
9488
|
-
function getChartPositionAtCenterOfViewport(getters, chartSize) {
|
|
9489
|
-
const { x, y } = getters.getMainViewportCoordinates();
|
|
9490
|
-
const { scrollX, scrollY } = getters.getActiveSheetScrollInfo();
|
|
9491
|
-
const { width, height } = getters.getVisibleRect(getters.getActiveMainViewport());
|
|
9492
|
-
const position = {
|
|
9493
|
-
x: x + scrollX + Math.max(0, (width - chartSize.width) / 2),
|
|
9494
|
-
y: y + scrollY + Math.max(0, (height - chartSize.height) / 2),
|
|
9495
|
-
}; // Position at the center of the scrollable viewport
|
|
9496
|
-
return position;
|
|
9497
|
-
}
|
|
9498
|
-
|
|
9499
9613
|
function checkKeyValue(definition) {
|
|
9500
9614
|
return definition.keyValue && !rangeReference.test(definition.keyValue)
|
|
9501
9615
|
? "InvalidScorecardKeyValue" /* CommandResult.InvalidScorecardKeyValue */
|
|
@@ -9513,10 +9627,12 @@ let ScorecardChart$1 = class ScorecardChart extends AbstractChart {
|
|
|
9513
9627
|
baseline;
|
|
9514
9628
|
baselineMode;
|
|
9515
9629
|
baselineDescr;
|
|
9630
|
+
progressBar = false;
|
|
9516
9631
|
background;
|
|
9517
9632
|
baselineColorUp;
|
|
9518
9633
|
baselineColorDown;
|
|
9519
9634
|
fontColor;
|
|
9635
|
+
humanize;
|
|
9520
9636
|
type = "scorecard";
|
|
9521
9637
|
constructor(definition, sheetId, getters) {
|
|
9522
9638
|
super(definition, sheetId, getters);
|
|
@@ -9527,6 +9643,7 @@ let ScorecardChart$1 = class ScorecardChart extends AbstractChart {
|
|
|
9527
9643
|
this.background = definition.background;
|
|
9528
9644
|
this.baselineColorUp = definition.baselineColorUp;
|
|
9529
9645
|
this.baselineColorDown = definition.baselineColorDown;
|
|
9646
|
+
this.humanize = definition.humanize ?? false;
|
|
9530
9647
|
}
|
|
9531
9648
|
static validateChartDefinition(validator, definition) {
|
|
9532
9649
|
return validator.checkValidations(definition, checkKeyValue, checkBaseline);
|
|
@@ -9596,6 +9713,7 @@ let ScorecardChart$1 = class ScorecardChart extends AbstractChart {
|
|
|
9596
9713
|
keyValue: keyValue
|
|
9597
9714
|
? this.getters.getRangeString(keyValue, targetSheetId || this.sheetId)
|
|
9598
9715
|
: undefined,
|
|
9716
|
+
humanize: this.humanize,
|
|
9599
9717
|
};
|
|
9600
9718
|
}
|
|
9601
9719
|
getDefinitionForExcel() {
|
|
@@ -9621,7 +9739,7 @@ function drawScoreChart(structure, canvas) {
|
|
|
9621
9739
|
if (structure.title) {
|
|
9622
9740
|
ctx.font = structure.title.style.font;
|
|
9623
9741
|
ctx.fillStyle = structure.title.style.color;
|
|
9624
|
-
ctx.fillText(structure.title.text, structure.title.position.x, structure.title.position.y);
|
|
9742
|
+
ctx.fillText(clipTextWithEllipsis(ctx, structure.title.text, canvas.width - structure.title.position.x), structure.title.position.x, structure.title.position.y);
|
|
9625
9743
|
}
|
|
9626
9744
|
if (structure.baseline) {
|
|
9627
9745
|
ctx.font = structure.baseline.style.font;
|
|
@@ -9648,20 +9766,41 @@ function drawScoreChart(structure, canvas) {
|
|
|
9648
9766
|
ctx.restore();
|
|
9649
9767
|
}
|
|
9650
9768
|
if (structure.baselineDescr) {
|
|
9651
|
-
|
|
9652
|
-
ctx.
|
|
9653
|
-
ctx.
|
|
9769
|
+
const descr = structure.baselineDescr[0];
|
|
9770
|
+
ctx.font = descr.style.font;
|
|
9771
|
+
ctx.fillStyle = descr.style.color;
|
|
9772
|
+
for (const description of structure.baselineDescr) {
|
|
9773
|
+
ctx.fillText(clipTextWithEllipsis(ctx, description.text, canvas.width - description.position.x), description.position.x, description.position.y);
|
|
9774
|
+
}
|
|
9654
9775
|
}
|
|
9655
9776
|
if (structure.key) {
|
|
9656
9777
|
ctx.font = structure.key.style.font;
|
|
9657
9778
|
ctx.fillStyle = structure.key.style.color;
|
|
9658
9779
|
drawDecoratedText(ctx, structure.key.text, structure.key.position, structure.key.style.underline, structure.key.style.strikethrough);
|
|
9659
9780
|
}
|
|
9781
|
+
if (structure.progressBar) {
|
|
9782
|
+
ctx.fillStyle = structure.progressBar.style.backgroundColor;
|
|
9783
|
+
ctx.beginPath();
|
|
9784
|
+
ctx.roundRect(structure.progressBar.position.x, structure.progressBar.position.y, structure.progressBar.dimension.width, structure.progressBar.dimension.height, structure.progressBar.dimension.height / 2);
|
|
9785
|
+
ctx.fill();
|
|
9786
|
+
ctx.fillStyle = structure.progressBar.style.color;
|
|
9787
|
+
ctx.beginPath();
|
|
9788
|
+
if (structure.progressBar.value > 0) {
|
|
9789
|
+
ctx.roundRect(structure.progressBar.position.x, structure.progressBar.position.y, structure.progressBar.dimension.width *
|
|
9790
|
+
Math.max(0, Math.min(1.0, structure.progressBar.value)), structure.progressBar.dimension.height, structure.progressBar.dimension.height / 2);
|
|
9791
|
+
}
|
|
9792
|
+
else {
|
|
9793
|
+
const width = structure.progressBar.dimension.width *
|
|
9794
|
+
Math.max(0, Math.min(1.0, -structure.progressBar.value));
|
|
9795
|
+
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);
|
|
9796
|
+
}
|
|
9797
|
+
ctx.fill();
|
|
9798
|
+
}
|
|
9660
9799
|
}
|
|
9661
9800
|
function createScorecardChartRuntime(chart, getters) {
|
|
9662
|
-
let keyValue = "";
|
|
9663
9801
|
let formattedKeyValue = "";
|
|
9664
9802
|
let keyValueCell;
|
|
9803
|
+
const locale = getters.getLocale();
|
|
9665
9804
|
if (chart.keyValue) {
|
|
9666
9805
|
const keyValuePosition = {
|
|
9667
9806
|
sheetId: chart.keyValue.sheetId,
|
|
@@ -9669,31 +9808,33 @@ function createScorecardChartRuntime(chart, getters) {
|
|
|
9669
9808
|
row: chart.keyValue.zone.top,
|
|
9670
9809
|
};
|
|
9671
9810
|
keyValueCell = getters.getEvaluatedCell(keyValuePosition);
|
|
9672
|
-
|
|
9673
|
-
formattedKeyValue = keyValueCell.formattedValue;
|
|
9811
|
+
formattedKeyValue = getKeyValueText(keyValueCell, chart.humanize ?? false, locale);
|
|
9674
9812
|
}
|
|
9675
9813
|
let baselineCell;
|
|
9676
9814
|
const baseline = chart.baseline;
|
|
9677
9815
|
if (baseline) {
|
|
9678
9816
|
const baselinePosition = {
|
|
9679
|
-
sheetId:
|
|
9680
|
-
col:
|
|
9681
|
-
row:
|
|
9817
|
+
sheetId: baseline.sheetId,
|
|
9818
|
+
col: baseline.zone.left,
|
|
9819
|
+
row: baseline.zone.top,
|
|
9682
9820
|
};
|
|
9683
9821
|
baselineCell = getters.getEvaluatedCell(baselinePosition);
|
|
9684
9822
|
}
|
|
9685
9823
|
const { background, fontColor } = getters.getStyleOfSingleCellChart(chart.background, chart.keyValue);
|
|
9686
|
-
const
|
|
9824
|
+
const baselineDisplay = getBaselineText(baselineCell, keyValueCell, chart.baselineMode, chart.humanize ?? false, locale);
|
|
9825
|
+
const baselineValue = chart.baselineMode === "progress" && isNumber(baselineDisplay, locale)
|
|
9826
|
+
? toNumber(baselineDisplay, locale)
|
|
9827
|
+
: 0;
|
|
9687
9828
|
return {
|
|
9688
9829
|
title: _t(chart.title),
|
|
9689
|
-
keyValue: formattedKeyValue
|
|
9690
|
-
baselineDisplay
|
|
9830
|
+
keyValue: formattedKeyValue,
|
|
9831
|
+
baselineDisplay,
|
|
9691
9832
|
baselineArrow: getBaselineArrowDirection(baselineCell, keyValueCell, chart.baselineMode),
|
|
9692
9833
|
baselineColor: getBaselineColor(baselineCell, chart.baselineMode, keyValueCell, chart.baselineColorUp, chart.baselineColorDown),
|
|
9693
|
-
baselineDescr: chart.baselineDescr ? _t(chart.baselineDescr) : "",
|
|
9834
|
+
baselineDescr: chart.baselineMode !== "progress" && chart.baselineDescr ? _t(chart.baselineDescr) : "",
|
|
9694
9835
|
fontColor,
|
|
9695
9836
|
background,
|
|
9696
|
-
baselineStyle: chart.baselineMode !== "percentage" && baseline
|
|
9837
|
+
baselineStyle: chart.baselineMode !== "percentage" && chart.baselineMode !== "progress" && baseline
|
|
9697
9838
|
? getters.getCellStyle({
|
|
9698
9839
|
sheetId: baseline.sheetId,
|
|
9699
9840
|
col: baseline.zone.left,
|
|
@@ -9707,17 +9848,21 @@ function createScorecardChartRuntime(chart, getters) {
|
|
|
9707
9848
|
row: chart.keyValue.zone.top,
|
|
9708
9849
|
})
|
|
9709
9850
|
: undefined,
|
|
9851
|
+
progressBar: chart.baselineMode === "progress"
|
|
9852
|
+
? {
|
|
9853
|
+
value: baselineValue,
|
|
9854
|
+
color: baselineValue > 0 ? chart.baselineColorUp : chart.baselineColorDown,
|
|
9855
|
+
}
|
|
9856
|
+
: undefined,
|
|
9710
9857
|
};
|
|
9711
9858
|
}
|
|
9712
9859
|
|
|
9713
9860
|
/* Sizes of boxes containing the texts, in percentage of the Chart size */
|
|
9714
9861
|
const TITLE_FONT_SIZE = 18;
|
|
9715
|
-
const
|
|
9716
|
-
|
|
9717
|
-
|
|
9718
|
-
const
|
|
9719
|
-
/* Padding at the border of the chart, in percentage of the chart width */
|
|
9720
|
-
const CHART_PADDING_RATIO = 0.02;
|
|
9862
|
+
const KEY_BOX_HEIGHT_RATIO = 0.8;
|
|
9863
|
+
/* Padding at the border of the chart */
|
|
9864
|
+
const CHART_PADDING = 10;
|
|
9865
|
+
const BOTTOM_PADDING_RATIO = 0.05;
|
|
9721
9866
|
/**
|
|
9722
9867
|
* Line height (in em)
|
|
9723
9868
|
* Having a line heigh =1em (=font size) don't work, the font will overflow.
|
|
@@ -9757,31 +9902,37 @@ class ScorecardChartConfigBuilder {
|
|
|
9757
9902
|
},
|
|
9758
9903
|
};
|
|
9759
9904
|
const style = this.getTextStyles();
|
|
9760
|
-
|
|
9905
|
+
let titleHeight = 0;
|
|
9761
9906
|
if (this.title) {
|
|
9907
|
+
({ height: titleHeight } = this.getFullTextDimensions(this.title, style.title.font));
|
|
9762
9908
|
structure.title = {
|
|
9763
9909
|
text: this.title,
|
|
9764
9910
|
style: style.title,
|
|
9765
9911
|
position: {
|
|
9766
|
-
x:
|
|
9767
|
-
y:
|
|
9912
|
+
x: CHART_PADDING,
|
|
9913
|
+
y: CHART_PADDING / 2 + titleHeight,
|
|
9768
9914
|
},
|
|
9769
9915
|
};
|
|
9770
9916
|
}
|
|
9771
9917
|
const baselineArrowSize = style.baselineArrow?.size ?? 0;
|
|
9772
|
-
|
|
9773
|
-
|
|
9918
|
+
let { height: baselineHeight, width: baselineWidth } = this.getTextDimensions(this.baseline, style.baselineValue.font);
|
|
9919
|
+
if (!this.baseline) {
|
|
9920
|
+
baselineHeight = this.getTextDimensions(this.baselineDescr, style.baselineDescr.font).height;
|
|
9921
|
+
}
|
|
9922
|
+
const baselineDescrWidth = style.baselineDescr.isSplit
|
|
9923
|
+
? Math.max(...splitTextInTwoLines(this.baselineDescr).map((line) => this.getTextDimensions(line, style.baselineDescr.font).width))
|
|
9924
|
+
: this.getTextDimensions(this.baselineDescr, style.baselineDescr.font).width;
|
|
9774
9925
|
structure.baseline = {
|
|
9775
9926
|
text: this.baseline,
|
|
9776
9927
|
style: style.baselineValue,
|
|
9777
9928
|
position: {
|
|
9778
9929
|
x: (this.width - baselineWidth - baselineDescrWidth + baselineArrowSize) / 2,
|
|
9779
9930
|
y: this.keyValue
|
|
9780
|
-
? this.height -
|
|
9781
|
-
: this.height - (this.height - titleHeight - baselineHeight) / 2 -
|
|
9931
|
+
? this.height * (1 - BOTTOM_PADDING_RATIO * (this.runtime.progressBar ? 1 : 2))
|
|
9932
|
+
: this.height - (this.height - titleHeight - baselineHeight) / 2 - CHART_PADDING,
|
|
9782
9933
|
},
|
|
9783
9934
|
};
|
|
9784
|
-
if (style.baselineArrow) {
|
|
9935
|
+
if (style.baselineArrow && !this.runtime.progressBar) {
|
|
9785
9936
|
structure.baselineArrow = {
|
|
9786
9937
|
direction: this.baselineArrow,
|
|
9787
9938
|
style: style.baselineArrow,
|
|
@@ -9792,23 +9943,68 @@ class ScorecardChartConfigBuilder {
|
|
|
9792
9943
|
};
|
|
9793
9944
|
}
|
|
9794
9945
|
if (this.baselineDescr) {
|
|
9795
|
-
|
|
9796
|
-
|
|
9797
|
-
|
|
9946
|
+
const position = {
|
|
9947
|
+
x: structure.baseline.position.x + baselineWidth,
|
|
9948
|
+
y: structure.baseline.position.y,
|
|
9949
|
+
};
|
|
9950
|
+
if (style.baselineDescr.isSplit) {
|
|
9951
|
+
const description = splitTextInTwoLines(this.baselineDescr);
|
|
9952
|
+
const measure = this.context.measureText(description[1]);
|
|
9953
|
+
const deltaY = measure.fontBoundingBoxAscent + measure.fontBoundingBoxDescent;
|
|
9954
|
+
structure.baselineDescr = [
|
|
9955
|
+
{
|
|
9956
|
+
text: description[0],
|
|
9957
|
+
style: style.baselineDescr,
|
|
9958
|
+
position: {
|
|
9959
|
+
x: position.x,
|
|
9960
|
+
y: position.y - deltaY,
|
|
9961
|
+
},
|
|
9962
|
+
},
|
|
9963
|
+
{
|
|
9964
|
+
text: description[1],
|
|
9965
|
+
style: style.baselineDescr,
|
|
9966
|
+
position,
|
|
9967
|
+
},
|
|
9968
|
+
];
|
|
9969
|
+
}
|
|
9970
|
+
else {
|
|
9971
|
+
structure.baselineDescr = [
|
|
9972
|
+
{
|
|
9973
|
+
text: this.baselineDescr,
|
|
9974
|
+
style: style.baselineDescr,
|
|
9975
|
+
position,
|
|
9976
|
+
},
|
|
9977
|
+
];
|
|
9978
|
+
}
|
|
9979
|
+
}
|
|
9980
|
+
let progressBarHeight = 0;
|
|
9981
|
+
if (this.runtime.progressBar) {
|
|
9982
|
+
progressBarHeight = this.height * 0.05;
|
|
9983
|
+
structure.progressBar = {
|
|
9798
9984
|
position: {
|
|
9799
|
-
x:
|
|
9800
|
-
y:
|
|
9985
|
+
x: 2 * CHART_PADDING,
|
|
9986
|
+
y: this.height * (1 - 2 * BOTTOM_PADDING_RATIO) - baselineHeight - progressBarHeight,
|
|
9987
|
+
},
|
|
9988
|
+
dimension: {
|
|
9989
|
+
height: progressBarHeight,
|
|
9990
|
+
width: this.width - 4 * CHART_PADDING,
|
|
9991
|
+
},
|
|
9992
|
+
value: this.runtime.progressBar.value,
|
|
9993
|
+
style: {
|
|
9994
|
+
color: this.runtime.progressBar.color,
|
|
9995
|
+
backgroundColor: this.secondaryFontColor,
|
|
9801
9996
|
},
|
|
9802
9997
|
};
|
|
9803
9998
|
}
|
|
9804
|
-
const {
|
|
9999
|
+
const { width: keyWidth, height: keyHeight } = this.getFullTextDimensions(this.keyValue, style.keyValue.font);
|
|
9805
10000
|
if (this.keyValue) {
|
|
9806
10001
|
structure.key = {
|
|
9807
10002
|
text: this.keyValue,
|
|
9808
10003
|
style: style.keyValue,
|
|
9809
10004
|
position: {
|
|
9810
10005
|
x: (this.width - keyWidth) / 2,
|
|
9811
|
-
y:
|
|
10006
|
+
y: this.height * (0.5 - BOTTOM_PADDING_RATIO * 2) +
|
|
10007
|
+
(titleHeight + keyHeight / (this.baseline || this.baselineDescr ? 2 : 1.2)) / 2,
|
|
9812
10008
|
},
|
|
9813
10009
|
};
|
|
9814
10010
|
}
|
|
@@ -9835,9 +10031,6 @@ class ScorecardChartConfigBuilder {
|
|
|
9835
10031
|
get secondaryFontColor() {
|
|
9836
10032
|
return relativeLuminance(this.backgroundColor) > 0.3 ? "#525252" : "#C8C8C8";
|
|
9837
10033
|
}
|
|
9838
|
-
get chartPadding() {
|
|
9839
|
-
return this.width * CHART_PADDING_RATIO;
|
|
9840
|
-
}
|
|
9841
10034
|
getTextDimensions(text, font) {
|
|
9842
10035
|
this.context.font = font;
|
|
9843
10036
|
const measure = this.context.measureText(text);
|
|
@@ -9846,16 +10039,44 @@ class ScorecardChartConfigBuilder {
|
|
|
9846
10039
|
height: measure.actualBoundingBoxAscent + measure.actualBoundingBoxDescent,
|
|
9847
10040
|
};
|
|
9848
10041
|
}
|
|
10042
|
+
getFullTextDimensions(text, font) {
|
|
10043
|
+
this.context.font = font;
|
|
10044
|
+
const measure = this.context.measureText(text);
|
|
10045
|
+
return {
|
|
10046
|
+
width: measure.width,
|
|
10047
|
+
height: measure.fontBoundingBoxAscent + measure.fontBoundingBoxDescent,
|
|
10048
|
+
};
|
|
10049
|
+
}
|
|
9849
10050
|
getTextStyles() {
|
|
9850
10051
|
// If the widest text overflows horizontally, scale it down, and apply the same scaling factors to all the other fonts.
|
|
9851
|
-
const maxLineWidth = this.width
|
|
9852
|
-
const
|
|
9853
|
-
const baseFontSize = widestElement.getElementMaxFontSize(this.getDrawableHeight(), this);
|
|
9854
|
-
const fontSizeMatchingWidth = getFontSizeMatchingWidth(maxLineWidth, baseFontSize, (fontSize) => widestElement.getElementWidth(fontSize, this.context, this));
|
|
9855
|
-
let scalingFactor = fontSizeMatchingWidth / baseFontSize;
|
|
10052
|
+
const maxLineWidth = this.width - 2 * CHART_PADDING;
|
|
10053
|
+
const drawableHeight = this.getDrawableHeight();
|
|
9856
10054
|
// Fonts sizes in px
|
|
9857
|
-
const
|
|
9858
|
-
const
|
|
10055
|
+
const keyValueElement = new KeyValueElement(this.runtime.keyValueStyle);
|
|
10056
|
+
const heightFont = keyValueElement.getElementMaxFontSize(drawableHeight, this);
|
|
10057
|
+
const widthFont = getFontSizeMatchingWidth(maxLineWidth, 600, (fontSize) => keyValueElement.getElementWidth(fontSize, this.context, this));
|
|
10058
|
+
const keyFontSize = Math.min(heightFont, widthFont);
|
|
10059
|
+
let baselineValueFontSize = Math.floor(keyFontSize * 0.5);
|
|
10060
|
+
this.context.font = getDefaultContextFont(baselineValueFontSize, this.runtime.baselineStyle?.bold, this.runtime.baselineStyle?.italic);
|
|
10061
|
+
const baselineText = this.baselineArrow !== "neutral" ? "A " + this.baseline : this.baseline;
|
|
10062
|
+
const baselineValueWidth = computeCachedTextWidth(this.context, baselineText);
|
|
10063
|
+
const remainingWidth = maxLineWidth - baselineValueWidth;
|
|
10064
|
+
let baselineDescrFontSize = getFontSizeMatchingWidth(remainingWidth, baselineValueFontSize, (fontSize) => computeTextWidth(this.context, this.baselineDescr, { fontSize }));
|
|
10065
|
+
let isBaselineSplit = false;
|
|
10066
|
+
if (baselineDescrFontSize < baselineValueFontSize / 2.5) {
|
|
10067
|
+
isBaselineSplit = true;
|
|
10068
|
+
baselineDescrFontSize = Math.floor(baselineValueFontSize / 2.5);
|
|
10069
|
+
for (const line of splitTextInTwoLines(this.baselineDescr)) {
|
|
10070
|
+
const lineWidth = getFontSizeMatchingWidth(remainingWidth, baselineValueFontSize, (fontSize) => {
|
|
10071
|
+
this.context.font = getDefaultContextFont(fontSize);
|
|
10072
|
+
return this.context.measureText(line).width;
|
|
10073
|
+
});
|
|
10074
|
+
baselineDescrFontSize = Math.min(baselineDescrFontSize, lineWidth);
|
|
10075
|
+
}
|
|
10076
|
+
}
|
|
10077
|
+
if (this.runtime.progressBar) {
|
|
10078
|
+
baselineValueFontSize /= 1.5;
|
|
10079
|
+
}
|
|
9859
10080
|
return {
|
|
9860
10081
|
title: {
|
|
9861
10082
|
font: getDefaultContextFont(TITLE_FONT_SIZE),
|
|
@@ -9868,7 +10089,7 @@ class ScorecardChartConfigBuilder {
|
|
|
9868
10089
|
underline: this.runtime.keyValueStyle?.underline,
|
|
9869
10090
|
},
|
|
9870
10091
|
baselineValue: {
|
|
9871
|
-
font: getDefaultContextFont(
|
|
10092
|
+
font: getDefaultContextFont(baselineValueFontSize, this.runtime.baselineStyle?.bold, this.runtime.baselineStyle?.italic),
|
|
9872
10093
|
strikethrough: this.runtime.baselineStyle?.strikethrough,
|
|
9873
10094
|
underline: this.runtime.baselineStyle?.underline,
|
|
9874
10095
|
color: this.runtime.baselineStyle?.textColor ||
|
|
@@ -9876,33 +10097,25 @@ class ScorecardChartConfigBuilder {
|
|
|
9876
10097
|
this.secondaryFontColor,
|
|
9877
10098
|
},
|
|
9878
10099
|
baselineDescr: {
|
|
9879
|
-
font: getDefaultContextFont(
|
|
10100
|
+
font: getDefaultContextFont(baselineDescrFontSize),
|
|
10101
|
+
isSplit: isBaselineSplit,
|
|
9880
10102
|
color: this.secondaryFontColor,
|
|
9881
10103
|
},
|
|
9882
|
-
baselineArrow: this.baselineArrow === "neutral"
|
|
10104
|
+
baselineArrow: this.baselineArrow === "neutral" || this.runtime.progressBar
|
|
9883
10105
|
? undefined
|
|
9884
10106
|
: {
|
|
9885
|
-
size: this.keyValue ? 0.8 *
|
|
10107
|
+
size: this.keyValue ? 0.8 * baselineValueFontSize : 0,
|
|
9886
10108
|
color: this.runtime.baselineColor || this.secondaryFontColor,
|
|
9887
10109
|
},
|
|
9888
10110
|
};
|
|
9889
10111
|
}
|
|
9890
10112
|
/** Get the height of the chart minus all the vertical paddings */
|
|
9891
10113
|
getDrawableHeight() {
|
|
9892
|
-
const verticalPadding =
|
|
9893
|
-
let availableHeight = this.height - verticalPadding;
|
|
10114
|
+
const verticalPadding = CHART_PADDING + this.height * BOTTOM_PADDING_RATIO;
|
|
10115
|
+
let availableHeight = this.height - 2 * verticalPadding;
|
|
9894
10116
|
availableHeight -= this.title ? TITLE_FONT_SIZE * LINE_HEIGHT : 0;
|
|
9895
10117
|
return availableHeight;
|
|
9896
10118
|
}
|
|
9897
|
-
/** Return the element with he widest text in the chart */
|
|
9898
|
-
getWidestElement() {
|
|
9899
|
-
const baseline = new BaselineElement(this.runtime.baselineStyle);
|
|
9900
|
-
const keyValue = new KeyValueElement(this.runtime.keyValueStyle);
|
|
9901
|
-
return baseline.getElementWidth(BASELINE_BOX_HEIGHT_RATIO, this.context, this) >
|
|
9902
|
-
keyValue.getElementWidth(KEY_BOX_HEIGHT_RATIO, this.context, this)
|
|
9903
|
-
? baseline
|
|
9904
|
-
: keyValue;
|
|
9905
|
-
}
|
|
9906
10119
|
}
|
|
9907
10120
|
class ScorecardScalableElement {
|
|
9908
10121
|
style;
|
|
@@ -9911,29 +10124,7 @@ class ScorecardScalableElement {
|
|
|
9911
10124
|
}
|
|
9912
10125
|
measureTextWidth(ctx, text, fontSize) {
|
|
9913
10126
|
ctx.font = getDefaultContextFont(fontSize, this.style.bold, this.style.italic);
|
|
9914
|
-
return ctx
|
|
9915
|
-
}
|
|
9916
|
-
}
|
|
9917
|
-
class BaselineElement extends ScorecardScalableElement {
|
|
9918
|
-
getElementWidth(fontSize, ctx, chart) {
|
|
9919
|
-
if (!chart.runtime) {
|
|
9920
|
-
return 0;
|
|
9921
|
-
}
|
|
9922
|
-
const baselineStr = chart.baseline;
|
|
9923
|
-
// Put mock text to simulate the width of the up/down arrow
|
|
9924
|
-
const largeText = chart.baselineArrow !== "neutral" ? "A " + baselineStr : baselineStr;
|
|
9925
|
-
let textWidth = this.measureTextWidth(ctx, largeText, fontSize);
|
|
9926
|
-
// Baseline descr font size should be smaller than baseline font size
|
|
9927
|
-
textWidth += this.measureTextWidth(ctx, chart.baselineDescr, fontSize * BASELINE_DESCR_FONT_RATIO);
|
|
9928
|
-
return textWidth;
|
|
9929
|
-
}
|
|
9930
|
-
getElementMaxFontSize(availableHeight, chart) {
|
|
9931
|
-
if (!chart.runtime) {
|
|
9932
|
-
return 0;
|
|
9933
|
-
}
|
|
9934
|
-
const haveBaseline = chart.baseline !== "" || chart.baselineDescr;
|
|
9935
|
-
const maxHeight = haveBaseline ? BASELINE_BOX_HEIGHT_RATIO * availableHeight : 0;
|
|
9936
|
-
return maxHeight / LINE_HEIGHT;
|
|
10127
|
+
return computeCachedTextWidth(ctx, text);
|
|
9937
10128
|
}
|
|
9938
10129
|
}
|
|
9939
10130
|
class KeyValueElement extends ScorecardScalableElement {
|
|
@@ -10863,33 +11054,6 @@ var array = /*#__PURE__*/Object.freeze({
|
|
|
10863
11054
|
// -----------------------------------------------------------------------------
|
|
10864
11055
|
// FORMAT.LARGE.NUMBER
|
|
10865
11056
|
// -----------------------------------------------------------------------------
|
|
10866
|
-
function formatLargeNumber(arg, unit, locale) {
|
|
10867
|
-
const value = Math.abs(toNumber(arg?.value, locale));
|
|
10868
|
-
const format = arg?.format;
|
|
10869
|
-
if (unit !== undefined) {
|
|
10870
|
-
const postFix = unit?.value;
|
|
10871
|
-
switch (postFix) {
|
|
10872
|
-
case "k":
|
|
10873
|
-
return createLargeNumberFormat(format, 1e3, "k");
|
|
10874
|
-
case "m":
|
|
10875
|
-
return createLargeNumberFormat(format, 1e6, "m");
|
|
10876
|
-
case "b":
|
|
10877
|
-
return createLargeNumberFormat(format, 1e9, "b");
|
|
10878
|
-
default:
|
|
10879
|
-
throw new EvaluationError(_t("The formatting unit should be 'k', 'm' or 'b'."));
|
|
10880
|
-
}
|
|
10881
|
-
}
|
|
10882
|
-
if (value < 1e5) {
|
|
10883
|
-
return createLargeNumberFormat(format, 0, "");
|
|
10884
|
-
}
|
|
10885
|
-
else if (value < 1e8) {
|
|
10886
|
-
return createLargeNumberFormat(format, 1e3, "k");
|
|
10887
|
-
}
|
|
10888
|
-
else if (value < 1e11) {
|
|
10889
|
-
return createLargeNumberFormat(format, 1e6, "m");
|
|
10890
|
-
}
|
|
10891
|
-
return createLargeNumberFormat(format, 1e9, "b");
|
|
10892
|
-
}
|
|
10893
11057
|
const FORMAT_LARGE_NUMBER = {
|
|
10894
11058
|
description: _t("Apply a large number format"),
|
|
10895
11059
|
args: [
|
|
@@ -11051,7 +11215,7 @@ const ATAN2 = {
|
|
|
11051
11215
|
compute: function (x, y) {
|
|
11052
11216
|
const _x = toNumber(x, this.locale);
|
|
11053
11217
|
const _y = toNumber(y, this.locale);
|
|
11054
|
-
assert(() => _x !== 0 || _y !== 0, _t("Function [[FUNCTION_NAME]] caused a divide by zero error."));
|
|
11218
|
+
assert(() => _x !== 0 || _y !== 0, _t("Function [[FUNCTION_NAME]] caused a divide by zero error."), CellErrorType.DivisionByZero);
|
|
11055
11219
|
return Math.atan2(_y, _x);
|
|
11056
11220
|
},
|
|
11057
11221
|
isExported: true,
|
|
@@ -11181,7 +11345,7 @@ const COT = {
|
|
|
11181
11345
|
returns: ["NUMBER"],
|
|
11182
11346
|
compute: function (angle) {
|
|
11183
11347
|
const _angle = toNumber(angle, this.locale);
|
|
11184
|
-
|
|
11348
|
+
assertNotZero(_angle);
|
|
11185
11349
|
return 1 / Math.tan(_angle);
|
|
11186
11350
|
},
|
|
11187
11351
|
isExported: true,
|
|
@@ -11195,7 +11359,7 @@ const COTH = {
|
|
|
11195
11359
|
returns: ["NUMBER"],
|
|
11196
11360
|
compute: function (value) {
|
|
11197
11361
|
const _value = toNumber(value, this.locale);
|
|
11198
|
-
|
|
11362
|
+
assertNotZero(_value);
|
|
11199
11363
|
return 1 / Math.tanh(_value);
|
|
11200
11364
|
},
|
|
11201
11365
|
isExported: true,
|
|
@@ -11323,7 +11487,7 @@ const CSC = {
|
|
|
11323
11487
|
returns: ["NUMBER"],
|
|
11324
11488
|
compute: function (angle) {
|
|
11325
11489
|
const _angle = toNumber(angle, this.locale);
|
|
11326
|
-
|
|
11490
|
+
assertNotZero(_angle);
|
|
11327
11491
|
return 1 / Math.sin(_angle);
|
|
11328
11492
|
},
|
|
11329
11493
|
isExported: true,
|
|
@@ -11337,7 +11501,7 @@ const CSCH = {
|
|
|
11337
11501
|
returns: ["NUMBER"],
|
|
11338
11502
|
compute: function (value) {
|
|
11339
11503
|
const _value = toNumber(value, this.locale);
|
|
11340
|
-
|
|
11504
|
+
assertNotZero(_value);
|
|
11341
11505
|
return 1 / Math.sinh(_value);
|
|
11342
11506
|
},
|
|
11343
11507
|
isExported: true,
|
|
@@ -11536,7 +11700,7 @@ const LN = {
|
|
|
11536
11700
|
// MOD
|
|
11537
11701
|
// -----------------------------------------------------------------------------
|
|
11538
11702
|
function mod(dividend, divisor) {
|
|
11539
|
-
assert(() => divisor !== 0, _t("The divisor must be different from 0."));
|
|
11703
|
+
assert(() => divisor !== 0, _t("The divisor must be different from 0."), CellErrorType.DivisionByZero);
|
|
11540
11704
|
const modulus = dividend % divisor;
|
|
11541
11705
|
// -42 % 10 = -2 but we want 8, so need the code below
|
|
11542
11706
|
if ((modulus > 0 && divisor < 0) || (modulus < 0 && divisor > 0)) {
|
|
@@ -12105,7 +12269,7 @@ function average(values, locale) {
|
|
|
12105
12269
|
count += 1;
|
|
12106
12270
|
return acc + a;
|
|
12107
12271
|
}, 0, locale);
|
|
12108
|
-
|
|
12272
|
+
assertNotZero(count);
|
|
12109
12273
|
return sum / count;
|
|
12110
12274
|
}
|
|
12111
12275
|
function countNumbers(values, locale) {
|
|
@@ -12172,7 +12336,7 @@ function filterAndFlatData(dataY, dataX) {
|
|
|
12172
12336
|
function covariance(dataY, dataX, isSample) {
|
|
12173
12337
|
const { flatDataX, flatDataY } = filterAndFlatData(dataY, dataX);
|
|
12174
12338
|
const count = flatDataY.length;
|
|
12175
|
-
assert(() => count !== 0 && (!isSample || count !== 1), _t("Evaluation of function [[FUNCTION_NAME]] caused a divide by zero error."));
|
|
12339
|
+
assert(() => count !== 0 && (!isSample || count !== 1), _t("Evaluation of function [[FUNCTION_NAME]] caused a divide by zero error."), CellErrorType.DivisionByZero);
|
|
12176
12340
|
let sumY = 0;
|
|
12177
12341
|
let sumX = 0;
|
|
12178
12342
|
for (let i = 0; i < count; i++) {
|
|
@@ -12195,7 +12359,7 @@ function variance(args, isSample, textAs0, locale) {
|
|
|
12195
12359
|
count += 1;
|
|
12196
12360
|
return acc + a;
|
|
12197
12361
|
}, 0, locale);
|
|
12198
|
-
assert(() => count !== 0 && (!isSample || count !== 1), _t("Evaluation of function [[FUNCTION_NAME]] caused a divide by zero error."));
|
|
12362
|
+
assert(() => count !== 0 && (!isSample || count !== 1), _t("Evaluation of function [[FUNCTION_NAME]] caused a divide by zero error."), CellErrorType.DivisionByZero);
|
|
12199
12363
|
const average = sum / count;
|
|
12200
12364
|
return (reduceFunction(args, (acc, a) => acc + Math.pow(a - average, 2), 0, locale) /
|
|
12201
12365
|
(count - (isSample ? 1 : 0)));
|
|
@@ -12392,7 +12556,7 @@ const AVEDEV = {
|
|
|
12392
12556
|
count += 1;
|
|
12393
12557
|
return acc + a;
|
|
12394
12558
|
}, 0, this.locale);
|
|
12395
|
-
|
|
12559
|
+
assertNotZero(count);
|
|
12396
12560
|
const average = sum / count;
|
|
12397
12561
|
return reduceNumbers(values, (acc, a) => acc + Math.abs(average - a), 0, this.locale) / count;
|
|
12398
12562
|
},
|
|
@@ -12464,7 +12628,7 @@ const AVERAGE_WEIGHTED = {
|
|
|
12464
12628
|
}
|
|
12465
12629
|
}
|
|
12466
12630
|
}
|
|
12467
|
-
|
|
12631
|
+
assertNotZero(count);
|
|
12468
12632
|
return { value: sum / count, format: inferFormat(args[0]) };
|
|
12469
12633
|
},
|
|
12470
12634
|
};
|
|
@@ -12484,7 +12648,7 @@ const AVERAGEA = {
|
|
|
12484
12648
|
count += 1;
|
|
12485
12649
|
return acc + a;
|
|
12486
12650
|
}, 0, this.locale);
|
|
12487
|
-
|
|
12651
|
+
assertNotZero(count);
|
|
12488
12652
|
return {
|
|
12489
12653
|
value: sum / count,
|
|
12490
12654
|
format: inferFormat(args[0]),
|
|
@@ -12514,7 +12678,7 @@ const AVERAGEIF = {
|
|
|
12514
12678
|
sum += value;
|
|
12515
12679
|
}
|
|
12516
12680
|
}, this.locale);
|
|
12517
|
-
|
|
12681
|
+
assertNotZero(count);
|
|
12518
12682
|
return sum / count;
|
|
12519
12683
|
},
|
|
12520
12684
|
isExported: true,
|
|
@@ -12543,7 +12707,7 @@ const AVERAGEIFS = {
|
|
|
12543
12707
|
sum += value;
|
|
12544
12708
|
}
|
|
12545
12709
|
}, this.locale);
|
|
12546
|
-
|
|
12710
|
+
assertNotZero(count);
|
|
12547
12711
|
return sum / count;
|
|
12548
12712
|
},
|
|
12549
12713
|
isExported: true,
|
|
@@ -17968,7 +18132,7 @@ const DIVIDE = {
|
|
|
17968
18132
|
returns: ["NUMBER"],
|
|
17969
18133
|
compute: function (dividend, divisor) {
|
|
17970
18134
|
const _divisor = toNumber(divisor, this.locale);
|
|
17971
|
-
assert(() => _divisor !== 0, _t("The divisor must be different from zero."));
|
|
18135
|
+
assert(() => _divisor !== 0, _t("The divisor must be different from zero."), CellErrorType.DivisionByZero);
|
|
17972
18136
|
return {
|
|
17973
18137
|
value: toNumber(dividend, this.locale) / _divisor,
|
|
17974
18138
|
format: dividend?.format || divisor?.format,
|
|
@@ -21013,7 +21177,21 @@ class ScatterChart extends AbstractChart {
|
|
|
21013
21177
|
return new ScatterChart(definition, this.sheetId, this.getters);
|
|
21014
21178
|
}
|
|
21015
21179
|
getDefinitionForExcel() {
|
|
21016
|
-
|
|
21180
|
+
// Excel does not support aggregating labels
|
|
21181
|
+
if (this.aggregated) {
|
|
21182
|
+
return undefined;
|
|
21183
|
+
}
|
|
21184
|
+
const dataSets = this.dataSets
|
|
21185
|
+
.map((ds) => toExcelDataset(this.getters, ds))
|
|
21186
|
+
.filter((ds) => ds.range !== "");
|
|
21187
|
+
const labelRange = toExcelLabelRange(this.getters, this.labelRange, shouldRemoveFirstLabel(this.labelRange, this.dataSets[0], this.dataSetsHaveTitle));
|
|
21188
|
+
return {
|
|
21189
|
+
...this.getDefinition(),
|
|
21190
|
+
backgroundColor: toXlsxHexColor(this.background || BACKGROUND_CHART_COLOR),
|
|
21191
|
+
fontColor: toXlsxHexColor(chartFontColor(this.background)),
|
|
21192
|
+
dataSets,
|
|
21193
|
+
labelRange,
|
|
21194
|
+
};
|
|
21017
21195
|
}
|
|
21018
21196
|
copyForSheetId(sheetId) {
|
|
21019
21197
|
const dataSets = copyDataSetsWithNewSheetId(this.sheetId, sheetId, this.dataSets);
|
|
@@ -27441,13 +27619,30 @@ class ColorPickerWidget extends owl.Component {
|
|
|
27441
27619
|
}
|
|
27442
27620
|
}
|
|
27443
27621
|
|
|
27444
|
-
|
|
27445
|
-
|
|
27446
|
-
|
|
27622
|
+
const TRANSPARENT_BACKGROUND_SVG = /*xml*/ `
|
|
27623
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="10" height="10">
|
|
27624
|
+
<path fill="#d9d9d9" d="M5 5h5v5H5zH0V0h5"/>
|
|
27625
|
+
</svg>
|
|
27626
|
+
`;
|
|
27627
|
+
css /* scss */ `
|
|
27628
|
+
.o-round-color-picker-button {
|
|
27629
|
+
width: 15px;
|
|
27630
|
+
height: 15px;
|
|
27631
|
+
cursor: pointer;
|
|
27632
|
+
border: 1px solid #aaa;
|
|
27633
|
+
background-position: 1px 1px;
|
|
27634
|
+
background-image: url("data:image/svg+xml,${encodeURIComponent(TRANSPARENT_BACKGROUND_SVG)}");
|
|
27635
|
+
}
|
|
27636
|
+
`;
|
|
27637
|
+
class RoundColorPicker extends owl.Component {
|
|
27638
|
+
static template = "o-spreadsheet.RoundColorPicker";
|
|
27639
|
+
static components = { ColorPickerWidget, Section, ColorPicker };
|
|
27447
27640
|
static props = {
|
|
27448
27641
|
currentColor: { type: String, optional: true },
|
|
27642
|
+
title: { type: String, optional: true },
|
|
27449
27643
|
onColorPicked: Function,
|
|
27450
27644
|
};
|
|
27645
|
+
colorPickerButtonRef = owl.useRef("colorPickerButton");
|
|
27451
27646
|
state;
|
|
27452
27647
|
setup() {
|
|
27453
27648
|
this.state = owl.useState({ pickerOpened: false });
|
|
@@ -27459,6 +27654,19 @@ class ChartColor extends owl.Component {
|
|
|
27459
27654
|
togglePicker() {
|
|
27460
27655
|
this.state.pickerOpened = !this.state.pickerOpened;
|
|
27461
27656
|
}
|
|
27657
|
+
onColorPicked(color) {
|
|
27658
|
+
this.props.onColorPicked(color);
|
|
27659
|
+
this.state.pickerOpened = false;
|
|
27660
|
+
}
|
|
27661
|
+
get colorPickerAnchorRect() {
|
|
27662
|
+
const button = this.colorPickerButtonRef.el;
|
|
27663
|
+
return getBoundingRectAsPOJO(button);
|
|
27664
|
+
}
|
|
27665
|
+
get buttonStyle() {
|
|
27666
|
+
return cssPropertiesToCss({
|
|
27667
|
+
background: this.props.currentColor,
|
|
27668
|
+
});
|
|
27669
|
+
}
|
|
27462
27670
|
}
|
|
27463
27671
|
|
|
27464
27672
|
class ChartTitle extends owl.Component {
|
|
@@ -27472,7 +27680,7 @@ class ChartTitle extends owl.Component {
|
|
|
27472
27680
|
|
|
27473
27681
|
class LineBarPieDesignPanel extends owl.Component {
|
|
27474
27682
|
static template = "o-spreadsheet-LineBarPieDesignPanel";
|
|
27475
|
-
static components = {
|
|
27683
|
+
static components = { RoundColorPicker, ChartTitle, Section };
|
|
27476
27684
|
static props = {
|
|
27477
27685
|
figureId: String,
|
|
27478
27686
|
definition: Object,
|
|
@@ -27495,6 +27703,9 @@ class LineBarPieDesignPanel extends owl.Component {
|
|
|
27495
27703
|
[attr]: ev.target.value,
|
|
27496
27704
|
});
|
|
27497
27705
|
}
|
|
27706
|
+
get backgroundColorTitle() {
|
|
27707
|
+
return ChartTerms.BackgroundColor;
|
|
27708
|
+
}
|
|
27498
27709
|
}
|
|
27499
27710
|
|
|
27500
27711
|
class BarChartDesignPanel extends LineBarPieDesignPanel {
|
|
@@ -27564,6 +27775,10 @@ css /* scss */ `
|
|
|
27564
27775
|
line-height: 18px;
|
|
27565
27776
|
width: 100%;
|
|
27566
27777
|
}
|
|
27778
|
+
td {
|
|
27779
|
+
box-sizing: border-box;
|
|
27780
|
+
height: 30px;
|
|
27781
|
+
}
|
|
27567
27782
|
th.o-gauge-color-set-colorPicker {
|
|
27568
27783
|
width: 8%;
|
|
27569
27784
|
}
|
|
@@ -27586,7 +27801,12 @@ css /* scss */ `
|
|
|
27586
27801
|
`;
|
|
27587
27802
|
class GaugeChartDesignPanel extends owl.Component {
|
|
27588
27803
|
static template = "o-spreadsheet-GaugeChartDesignPanel";
|
|
27589
|
-
static components = {
|
|
27804
|
+
static components = {
|
|
27805
|
+
ChartErrorSection,
|
|
27806
|
+
RoundColorPicker,
|
|
27807
|
+
ChartTitle,
|
|
27808
|
+
Section,
|
|
27809
|
+
};
|
|
27590
27810
|
static props = {
|
|
27591
27811
|
figureId: String,
|
|
27592
27812
|
definition: Object,
|
|
@@ -27598,9 +27818,6 @@ class GaugeChartDesignPanel extends owl.Component {
|
|
|
27598
27818
|
sectionRuleDispatchResult: undefined,
|
|
27599
27819
|
sectionRule: deepCopy(this.props.definition.sectionRule),
|
|
27600
27820
|
});
|
|
27601
|
-
setup() {
|
|
27602
|
-
owl.useExternalListener(window, "click", this.closeMenus);
|
|
27603
|
-
}
|
|
27604
27821
|
get title() {
|
|
27605
27822
|
return _t(this.props.definition.title);
|
|
27606
27823
|
}
|
|
@@ -27641,27 +27858,22 @@ class GaugeChartDesignPanel extends owl.Component {
|
|
|
27641
27858
|
const sectionRule = deepCopy(this.state.sectionRule);
|
|
27642
27859
|
sectionRule.colors[target] = color;
|
|
27643
27860
|
this.updateSectionRule(sectionRule);
|
|
27644
|
-
this.closeMenus();
|
|
27645
|
-
}
|
|
27646
|
-
toggleMenu(menu) {
|
|
27647
|
-
const isSelected = this.state.openedMenu === menu;
|
|
27648
|
-
this.closeMenus();
|
|
27649
|
-
if (!isSelected) {
|
|
27650
|
-
this.state.openedMenu = menu;
|
|
27651
|
-
}
|
|
27652
27861
|
}
|
|
27653
27862
|
updateSectionRule(sectionRule) {
|
|
27654
27863
|
this.state.sectionRuleDispatchResult = this.props.updateChart(this.props.figureId, {
|
|
27655
27864
|
sectionRule,
|
|
27656
27865
|
});
|
|
27866
|
+
if (this.state.sectionRuleDispatchResult.isSuccessful) {
|
|
27867
|
+
this.state.sectionRule = deepCopy(sectionRule);
|
|
27868
|
+
}
|
|
27657
27869
|
}
|
|
27658
27870
|
canUpdateSectionRule(sectionRule) {
|
|
27659
27871
|
this.state.sectionRuleDispatchResult = this.props.canUpdateChart(this.props.figureId, {
|
|
27660
27872
|
sectionRule,
|
|
27661
27873
|
});
|
|
27662
27874
|
}
|
|
27663
|
-
|
|
27664
|
-
|
|
27875
|
+
get backgroundColorTitle() {
|
|
27876
|
+
return ChartTerms.BackgroundColor;
|
|
27665
27877
|
}
|
|
27666
27878
|
}
|
|
27667
27879
|
|
|
@@ -27809,39 +28021,36 @@ class ScorecardChartConfigPanel extends owl.Component {
|
|
|
27809
28021
|
|
|
27810
28022
|
class ScorecardChartDesignPanel extends owl.Component {
|
|
27811
28023
|
static template = "o-spreadsheet-ScorecardChartDesignPanel";
|
|
27812
|
-
static components = {
|
|
28024
|
+
static components = { RoundColorPicker, ChartTitle, Section, Checkbox };
|
|
27813
28025
|
static props = {
|
|
27814
28026
|
figureId: String,
|
|
27815
28027
|
definition: Object,
|
|
27816
28028
|
updateChart: Function,
|
|
27817
28029
|
canUpdateChart: Function,
|
|
27818
28030
|
};
|
|
27819
|
-
state = owl.useState({
|
|
27820
|
-
openedColorPicker: undefined,
|
|
27821
|
-
});
|
|
27822
|
-
setup() {
|
|
27823
|
-
owl.useExternalListener(window, "click", this.closeMenus);
|
|
27824
|
-
}
|
|
27825
28031
|
get title() {
|
|
27826
28032
|
return _t(this.props.definition.title);
|
|
27827
28033
|
}
|
|
28034
|
+
get colorsSectionTitle() {
|
|
28035
|
+
return this.props.definition.baselineMode === "progress"
|
|
28036
|
+
? _t("Progress bar colors")
|
|
28037
|
+
: _t("Baseline colors");
|
|
28038
|
+
}
|
|
28039
|
+
get humanizeNumbersLabel() {
|
|
28040
|
+
return _t("Humanize numbers");
|
|
28041
|
+
}
|
|
27828
28042
|
updateTitle(title) {
|
|
27829
28043
|
this.props.updateChart(this.props.figureId, { title });
|
|
27830
28044
|
}
|
|
28045
|
+
updateHumanizeNumbers(humanize) {
|
|
28046
|
+
this.props.updateChart(this.props.figureId, { humanize });
|
|
28047
|
+
}
|
|
27831
28048
|
translate(term) {
|
|
27832
28049
|
return _t(term);
|
|
27833
28050
|
}
|
|
27834
28051
|
updateBaselineDescr(ev) {
|
|
27835
28052
|
this.props.updateChart(this.props.figureId, { baselineDescr: ev.target.value });
|
|
27836
28053
|
}
|
|
27837
|
-
toggleColorPicker(colorPickerId) {
|
|
27838
|
-
if (this.state.openedColorPicker === colorPickerId) {
|
|
27839
|
-
this.state.openedColorPicker = undefined;
|
|
27840
|
-
}
|
|
27841
|
-
else {
|
|
27842
|
-
this.state.openedColorPicker = colorPickerId;
|
|
27843
|
-
}
|
|
27844
|
-
}
|
|
27845
28054
|
setColor(color, colorPickerId) {
|
|
27846
28055
|
switch (colorPickerId) {
|
|
27847
28056
|
case "backgroundColor":
|
|
@@ -27854,10 +28063,9 @@ class ScorecardChartDesignPanel extends owl.Component {
|
|
|
27854
28063
|
this.props.updateChart(this.props.figureId, { baselineColorUp: color });
|
|
27855
28064
|
break;
|
|
27856
28065
|
}
|
|
27857
|
-
this.closeMenus();
|
|
27858
28066
|
}
|
|
27859
|
-
|
|
27860
|
-
|
|
28067
|
+
get backgroundColorTitle() {
|
|
28068
|
+
return ChartTerms.BackgroundColor;
|
|
27861
28069
|
}
|
|
27862
28070
|
}
|
|
27863
28071
|
|
|
@@ -28806,6 +29014,7 @@ class ConditionalFormattingEditor extends owl.Component {
|
|
|
28806
29014
|
ColorPickerWidget,
|
|
28807
29015
|
ConditionalFormatPreviewList,
|
|
28808
29016
|
Section,
|
|
29017
|
+
RoundColorPicker,
|
|
28809
29018
|
};
|
|
28810
29019
|
icons = ICONS;
|
|
28811
29020
|
cellIsOperators = CellIsOperators;
|
|
@@ -32310,6 +32519,7 @@ class Composer extends owl.Component {
|
|
|
32310
32519
|
onComposerCellFocused: { type: Function, optional: true },
|
|
32311
32520
|
onComposerContentFocused: Function,
|
|
32312
32521
|
isDefaultFocus: { type: Boolean, optional: true },
|
|
32522
|
+
onInputContextMenu: { type: Function, optional: true },
|
|
32313
32523
|
};
|
|
32314
32524
|
static components = { TextValueProvider, FunctionDescriptionProvider };
|
|
32315
32525
|
static defaultProps = {
|
|
@@ -32360,6 +32570,9 @@ class Composer extends owl.Component {
|
|
|
32360
32570
|
assistantStyle.right = `0px`;
|
|
32361
32571
|
}
|
|
32362
32572
|
}
|
|
32573
|
+
else if (this.props.delimitation) {
|
|
32574
|
+
assistantStyle["max-height"] = `${this.props.delimitation.height}px`;
|
|
32575
|
+
}
|
|
32363
32576
|
return cssPropertiesToCss(assistantStyle);
|
|
32364
32577
|
}
|
|
32365
32578
|
// we can't allow input events to be triggered while we remove and add back the content of the composer in processContent
|
|
@@ -32646,6 +32859,11 @@ class Composer extends owl.Component {
|
|
|
32646
32859
|
}
|
|
32647
32860
|
}
|
|
32648
32861
|
}
|
|
32862
|
+
onContextMenu(ev) {
|
|
32863
|
+
if (this.composerStore.editionMode === "inactive") {
|
|
32864
|
+
this.props.onInputContextMenu?.(ev);
|
|
32865
|
+
}
|
|
32866
|
+
}
|
|
32649
32867
|
// ---------------------------------------------------------------------------
|
|
32650
32868
|
// Private
|
|
32651
32869
|
// ---------------------------------------------------------------------------
|
|
@@ -32867,6 +33085,7 @@ class GridComposer extends owl.Component {
|
|
|
32867
33085
|
static template = "o-spreadsheet-GridComposer";
|
|
32868
33086
|
static props = {
|
|
32869
33087
|
gridDims: Object,
|
|
33088
|
+
onInputContextMenu: Function,
|
|
32870
33089
|
};
|
|
32871
33090
|
static components = { Composer };
|
|
32872
33091
|
rect = this.defaultRect;
|
|
@@ -32914,6 +33133,7 @@ class GridComposer extends owl.Component {
|
|
|
32914
33133
|
isDefaultFocus: true,
|
|
32915
33134
|
onComposerContentFocused: () => this.composerFocusStore.focusGridComposerContent(),
|
|
32916
33135
|
onComposerCellFocused: (content) => this.composerFocusStore.focusGridComposerCell(content),
|
|
33136
|
+
onInputContextMenu: this.props.onInputContextMenu,
|
|
32917
33137
|
};
|
|
32918
33138
|
}
|
|
32919
33139
|
get containerStyle() {
|
|
@@ -33012,7 +33232,6 @@ class GridCellIcon extends owl.Component {
|
|
|
33012
33232
|
cellPosition: Object,
|
|
33013
33233
|
horizontalAlign: { type: String, optional: true },
|
|
33014
33234
|
verticalAlign: { type: String, optional: true },
|
|
33015
|
-
offset: { type: Object, optional: true },
|
|
33016
33235
|
slots: Object,
|
|
33017
33236
|
};
|
|
33018
33237
|
get iconStyle() {
|
|
@@ -33023,8 +33242,8 @@ class GridCellIcon extends owl.Component {
|
|
|
33023
33242
|
const x = this.getIconHorizontalPosition(rect, cellPosition);
|
|
33024
33243
|
const y = this.getIconVerticalPosition(rect, cellPosition);
|
|
33025
33244
|
return cssPropertiesToCss({
|
|
33026
|
-
top: `${y
|
|
33027
|
-
left: `${x
|
|
33245
|
+
top: `${y}px`,
|
|
33246
|
+
left: `${x}px`,
|
|
33028
33247
|
});
|
|
33029
33248
|
}
|
|
33030
33249
|
getIconVerticalPosition(rect, cellPosition) {
|
|
@@ -33064,83 +33283,6 @@ class GridCellIcon extends owl.Component {
|
|
|
33064
33283
|
}
|
|
33065
33284
|
}
|
|
33066
33285
|
|
|
33067
|
-
css /* scss */ `
|
|
33068
|
-
.o-filter-icon {
|
|
33069
|
-
color: ${FILTERS_COLOR};
|
|
33070
|
-
display: flex;
|
|
33071
|
-
align-items: center;
|
|
33072
|
-
justify-content: center;
|
|
33073
|
-
width: ${GRID_ICON_EDGE_LENGTH}px;
|
|
33074
|
-
height: ${GRID_ICON_EDGE_LENGTH}px;
|
|
33075
|
-
|
|
33076
|
-
&:hover {
|
|
33077
|
-
background: ${FILTERS_COLOR};
|
|
33078
|
-
color: #fff;
|
|
33079
|
-
}
|
|
33080
|
-
|
|
33081
|
-
&.o-high-contrast {
|
|
33082
|
-
color: #defade;
|
|
33083
|
-
}
|
|
33084
|
-
&.o-high-contrast:hover {
|
|
33085
|
-
color: ${FILTERS_COLOR};
|
|
33086
|
-
background: #fff;
|
|
33087
|
-
}
|
|
33088
|
-
}
|
|
33089
|
-
.o-filter-icon:hover {
|
|
33090
|
-
background: ${FILTERS_COLOR};
|
|
33091
|
-
color: #fff;
|
|
33092
|
-
}
|
|
33093
|
-
`;
|
|
33094
|
-
class FilterIcon extends owl.Component {
|
|
33095
|
-
static template = "o-spreadsheet-FilterIcon";
|
|
33096
|
-
static props = {
|
|
33097
|
-
cellPosition: Object,
|
|
33098
|
-
};
|
|
33099
|
-
cellPopovers;
|
|
33100
|
-
setup() {
|
|
33101
|
-
this.cellPopovers = useStore(CellPopoverStore);
|
|
33102
|
-
}
|
|
33103
|
-
onClick() {
|
|
33104
|
-
const position = this.props.cellPosition;
|
|
33105
|
-
const activePopover = this.cellPopovers.persistentCellPopover;
|
|
33106
|
-
const { col, row } = position;
|
|
33107
|
-
if (activePopover.isOpen &&
|
|
33108
|
-
activePopover.col === col &&
|
|
33109
|
-
activePopover.row === row &&
|
|
33110
|
-
activePopover.type === "FilterMenu") {
|
|
33111
|
-
this.cellPopovers.close();
|
|
33112
|
-
return;
|
|
33113
|
-
}
|
|
33114
|
-
this.cellPopovers.open({ col, row }, "FilterMenu");
|
|
33115
|
-
}
|
|
33116
|
-
get isFilterActive() {
|
|
33117
|
-
return this.env.model.getters.isFilterActive(this.props.cellPosition);
|
|
33118
|
-
}
|
|
33119
|
-
get iconClass() {
|
|
33120
|
-
const cellStyle = this.env.model.getters.getCellComputedStyle(this.props.cellPosition);
|
|
33121
|
-
const luminance = relativeLuminance(cellStyle.fillColor || "#fff");
|
|
33122
|
-
return luminance < 0.45 ? "o-high-contrast" : "";
|
|
33123
|
-
}
|
|
33124
|
-
}
|
|
33125
|
-
|
|
33126
|
-
class FilterIconsOverlay extends owl.Component {
|
|
33127
|
-
static template = "o-spreadsheet-FilterIconsOverlay";
|
|
33128
|
-
static props = {
|
|
33129
|
-
gridPosition: { type: Object, optional: true },
|
|
33130
|
-
};
|
|
33131
|
-
static components = {
|
|
33132
|
-
GridCellIcon,
|
|
33133
|
-
FilterIcon,
|
|
33134
|
-
};
|
|
33135
|
-
static defaultProps = {
|
|
33136
|
-
gridPosition: { x: 0, y: 0 },
|
|
33137
|
-
};
|
|
33138
|
-
getFilterHeadersPositions() {
|
|
33139
|
-
const sheetId = this.env.model.getters.getActiveSheetId();
|
|
33140
|
-
return this.env.model.getters.getFilterHeaders(sheetId);
|
|
33141
|
-
}
|
|
33142
|
-
}
|
|
33143
|
-
|
|
33144
33286
|
const CHECKBOX_WIDTH = 15;
|
|
33145
33287
|
const MARGIN = (GRID_ICON_EDGE_LENGTH - CHECKBOX_WIDTH) / 2;
|
|
33146
33288
|
css /* scss */ `
|
|
@@ -33808,6 +33950,80 @@ class FiguresContainer extends owl.Component {
|
|
|
33808
33950
|
}
|
|
33809
33951
|
}
|
|
33810
33952
|
|
|
33953
|
+
css /* scss */ `
|
|
33954
|
+
.o-filter-icon {
|
|
33955
|
+
color: ${FILTERS_COLOR};
|
|
33956
|
+
display: flex;
|
|
33957
|
+
align-items: center;
|
|
33958
|
+
justify-content: center;
|
|
33959
|
+
width: ${GRID_ICON_EDGE_LENGTH}px;
|
|
33960
|
+
height: ${GRID_ICON_EDGE_LENGTH}px;
|
|
33961
|
+
|
|
33962
|
+
&:hover {
|
|
33963
|
+
background: ${FILTERS_COLOR};
|
|
33964
|
+
color: #fff;
|
|
33965
|
+
}
|
|
33966
|
+
|
|
33967
|
+
&.o-high-contrast {
|
|
33968
|
+
color: #defade;
|
|
33969
|
+
}
|
|
33970
|
+
&.o-high-contrast:hover {
|
|
33971
|
+
color: ${FILTERS_COLOR};
|
|
33972
|
+
background: #fff;
|
|
33973
|
+
}
|
|
33974
|
+
}
|
|
33975
|
+
.o-filter-icon:hover {
|
|
33976
|
+
background: ${FILTERS_COLOR};
|
|
33977
|
+
color: #fff;
|
|
33978
|
+
}
|
|
33979
|
+
`;
|
|
33980
|
+
class FilterIcon extends owl.Component {
|
|
33981
|
+
static template = "o-spreadsheet-FilterIcon";
|
|
33982
|
+
static props = {
|
|
33983
|
+
cellPosition: Object,
|
|
33984
|
+
};
|
|
33985
|
+
cellPopovers;
|
|
33986
|
+
setup() {
|
|
33987
|
+
this.cellPopovers = useStore(CellPopoverStore);
|
|
33988
|
+
}
|
|
33989
|
+
onClick() {
|
|
33990
|
+
const position = this.props.cellPosition;
|
|
33991
|
+
const activePopover = this.cellPopovers.persistentCellPopover;
|
|
33992
|
+
const { col, row } = position;
|
|
33993
|
+
if (activePopover.isOpen &&
|
|
33994
|
+
activePopover.col === col &&
|
|
33995
|
+
activePopover.row === row &&
|
|
33996
|
+
activePopover.type === "FilterMenu") {
|
|
33997
|
+
this.cellPopovers.close();
|
|
33998
|
+
return;
|
|
33999
|
+
}
|
|
34000
|
+
this.cellPopovers.open({ col, row }, "FilterMenu");
|
|
34001
|
+
}
|
|
34002
|
+
get isFilterActive() {
|
|
34003
|
+
return this.env.model.getters.isFilterActive(this.props.cellPosition);
|
|
34004
|
+
}
|
|
34005
|
+
get iconClass() {
|
|
34006
|
+
const cellStyle = this.env.model.getters.getCellComputedStyle(this.props.cellPosition);
|
|
34007
|
+
const luminance = relativeLuminance(cellStyle.fillColor || "#fff");
|
|
34008
|
+
return luminance < 0.45 ? "o-high-contrast" : "";
|
|
34009
|
+
}
|
|
34010
|
+
}
|
|
34011
|
+
|
|
34012
|
+
class FilterIconsOverlay extends owl.Component {
|
|
34013
|
+
static template = "o-spreadsheet-FilterIconsOverlay";
|
|
34014
|
+
static props = {
|
|
34015
|
+
onMouseDown: Function,
|
|
34016
|
+
};
|
|
34017
|
+
static components = {
|
|
34018
|
+
GridCellIcon,
|
|
34019
|
+
FilterIcon,
|
|
34020
|
+
};
|
|
34021
|
+
getFilterHeadersPositions() {
|
|
34022
|
+
const sheetId = this.env.model.getters.getActiveSheetId();
|
|
34023
|
+
return this.env.model.getters.getFilterHeaders(sheetId);
|
|
34024
|
+
}
|
|
34025
|
+
}
|
|
34026
|
+
|
|
33811
34027
|
css /* scss */ `
|
|
33812
34028
|
.o-grid-add-rows {
|
|
33813
34029
|
input {
|
|
@@ -34021,7 +34237,12 @@ class GridOverlay extends owl.Component {
|
|
|
34021
34237
|
onGridMoved: Function,
|
|
34022
34238
|
gridOverlayDimensions: String,
|
|
34023
34239
|
};
|
|
34024
|
-
static components = {
|
|
34240
|
+
static components = {
|
|
34241
|
+
FiguresContainer,
|
|
34242
|
+
DataValidationOverlay,
|
|
34243
|
+
GridAddRowsFooter,
|
|
34244
|
+
FilterIconsOverlay,
|
|
34245
|
+
};
|
|
34025
34246
|
static defaultProps = {
|
|
34026
34247
|
onCellHovered: () => { },
|
|
34027
34248
|
onCellDoubleClicked: () => { },
|
|
@@ -34066,7 +34287,7 @@ class GridOverlay extends owl.Component {
|
|
|
34066
34287
|
get isPaintingFormat() {
|
|
34067
34288
|
return this.env.model.getters.isPaintingFormat();
|
|
34068
34289
|
}
|
|
34069
|
-
onMouseDown(ev) {
|
|
34290
|
+
onMouseDown(ev, modifiers) {
|
|
34070
34291
|
if (ev.button > 0) {
|
|
34071
34292
|
// not main button, probably a context menu
|
|
34072
34293
|
return;
|
|
@@ -34075,6 +34296,7 @@ class GridOverlay extends owl.Component {
|
|
|
34075
34296
|
this.props.onCellClicked(col, row, {
|
|
34076
34297
|
expandZone: ev.shiftKey,
|
|
34077
34298
|
addZone: isCtrlKey(ev),
|
|
34299
|
+
closePopover: modifiers?.closePopover ?? true,
|
|
34078
34300
|
});
|
|
34079
34301
|
}
|
|
34080
34302
|
onDoubleClick(ev) {
|
|
@@ -35840,7 +36062,6 @@ class Grid extends owl.Component {
|
|
|
35840
36062
|
Popover,
|
|
35841
36063
|
VerticalScrollBar,
|
|
35842
36064
|
HorizontalScrollBar,
|
|
35843
|
-
FilterIconsOverlay,
|
|
35844
36065
|
};
|
|
35845
36066
|
HEADER_HEIGHT = HEADER_HEIGHT;
|
|
35846
36067
|
HEADER_WIDTH = HEADER_WIDTH;
|
|
@@ -36141,17 +36362,17 @@ class Grid extends owl.Component {
|
|
|
36141
36362
|
// ---------------------------------------------------------------------------
|
|
36142
36363
|
// Zone selection with mouse
|
|
36143
36364
|
// ---------------------------------------------------------------------------
|
|
36144
|
-
onCellClicked(col, row,
|
|
36145
|
-
if (this.cellPopovers.isOpen) {
|
|
36365
|
+
onCellClicked(col, row, modifiers) {
|
|
36366
|
+
if (modifiers.closePopover && this.cellPopovers.isOpen) {
|
|
36146
36367
|
this.cellPopovers.close();
|
|
36147
36368
|
}
|
|
36148
36369
|
if (this.composerStore.editionMode === "editing") {
|
|
36149
36370
|
this.composerStore.stopEdition();
|
|
36150
36371
|
}
|
|
36151
|
-
if (expandZone) {
|
|
36372
|
+
if (modifiers.expandZone) {
|
|
36152
36373
|
this.env.model.selection.setAnchorCorner(col, row);
|
|
36153
36374
|
}
|
|
36154
|
-
else if (addZone) {
|
|
36375
|
+
else if (modifiers.addZone) {
|
|
36155
36376
|
this.env.model.selection.addCellToSelection(col, row);
|
|
36156
36377
|
}
|
|
36157
36378
|
else {
|
|
@@ -36873,7 +37094,7 @@ const CHART_TYPE_CONVERSION_MAP = {
|
|
|
36873
37094
|
line3DChart: undefined,
|
|
36874
37095
|
stockChart: undefined,
|
|
36875
37096
|
radarChart: undefined,
|
|
36876
|
-
scatterChart:
|
|
37097
|
+
scatterChart: "scatter",
|
|
36877
37098
|
pieChart: "pie",
|
|
36878
37099
|
pie3DChart: undefined,
|
|
36879
37100
|
doughnutChart: "pie",
|
|
@@ -37637,29 +37858,12 @@ function convertWidthFromExcel(width) {
|
|
|
37637
37858
|
return width;
|
|
37638
37859
|
return Math.round((width / WIDTH_FACTOR) * 100) / 100;
|
|
37639
37860
|
}
|
|
37640
|
-
function convertBorderDescr(descr) {
|
|
37641
|
-
if (!descr) {
|
|
37642
|
-
return undefined;
|
|
37643
|
-
}
|
|
37644
|
-
return {
|
|
37645
|
-
style: descr.style,
|
|
37646
|
-
color: { rgb: descr.color },
|
|
37647
|
-
};
|
|
37648
|
-
}
|
|
37649
37861
|
function extractStyle(cell, data) {
|
|
37650
37862
|
let style = {};
|
|
37651
37863
|
if (cell.style) {
|
|
37652
37864
|
style = data.styles[cell.style];
|
|
37653
37865
|
}
|
|
37654
37866
|
const format = extractFormat(cell, data);
|
|
37655
|
-
const exportedBorder = {};
|
|
37656
|
-
if (cell.border) {
|
|
37657
|
-
const border = data.borders[cell.border];
|
|
37658
|
-
exportedBorder.left = convertBorderDescr(border.left);
|
|
37659
|
-
exportedBorder.right = convertBorderDescr(border.right);
|
|
37660
|
-
exportedBorder.bottom = convertBorderDescr(border.bottom);
|
|
37661
|
-
exportedBorder.top = convertBorderDescr(border.top);
|
|
37662
|
-
}
|
|
37663
37867
|
const styles = {
|
|
37664
37868
|
font: {
|
|
37665
37869
|
size: style?.fontSize || DEFAULT_FONT_SIZE,
|
|
@@ -37673,7 +37877,7 @@ function extractStyle(cell, data) {
|
|
|
37673
37877
|
}
|
|
37674
37878
|
: { reservedAttribute: "none" },
|
|
37675
37879
|
numFmt: format ? { format: format, id: 0 /* id not used for export */ } : undefined,
|
|
37676
|
-
border:
|
|
37880
|
+
border: cell.border || 0,
|
|
37677
37881
|
alignment: {
|
|
37678
37882
|
horizontal: style.align,
|
|
37679
37883
|
vertical: style.verticalAlign
|
|
@@ -37695,15 +37899,12 @@ function extractFormat(cell, data) {
|
|
|
37695
37899
|
return undefined;
|
|
37696
37900
|
}
|
|
37697
37901
|
function normalizeStyle(construct, styles) {
|
|
37698
|
-
const { id: fontId } = pushElement(styles["font"], construct.fonts);
|
|
37699
|
-
const { id: fillId } = pushElement(styles["fill"], construct.fills);
|
|
37700
|
-
const { id: borderId } = pushElement(styles["border"], construct.borders);
|
|
37701
37902
|
// Normalize this
|
|
37702
37903
|
const numFmtId = convertFormat(styles["numFmt"], construct.numFmts);
|
|
37703
37904
|
const style = {
|
|
37704
|
-
fontId,
|
|
37705
|
-
fillId,
|
|
37706
|
-
borderId,
|
|
37905
|
+
fontId: pushElement(styles.font, construct.fonts),
|
|
37906
|
+
fillId: pushElement(styles.fill, construct.fills),
|
|
37907
|
+
borderId: styles.border,
|
|
37707
37908
|
numFmtId,
|
|
37708
37909
|
alignment: {
|
|
37709
37910
|
vertical: styles.alignment.vertical,
|
|
@@ -37711,8 +37912,7 @@ function normalizeStyle(construct, styles) {
|
|
|
37711
37912
|
wrapText: styles.alignment.wrapText,
|
|
37712
37913
|
},
|
|
37713
37914
|
};
|
|
37714
|
-
|
|
37715
|
-
return id;
|
|
37915
|
+
return pushElement(style, construct.styles);
|
|
37716
37916
|
}
|
|
37717
37917
|
function convertFormat(format, numFmtStructure) {
|
|
37718
37918
|
if (!format) {
|
|
@@ -37720,8 +37920,7 @@ function convertFormat(format, numFmtStructure) {
|
|
|
37720
37920
|
}
|
|
37721
37921
|
let formatId = XLSX_FORMAT_MAP[format.format];
|
|
37722
37922
|
if (!formatId) {
|
|
37723
|
-
|
|
37724
|
-
formatId = id + FIRST_NUMFMT_ID;
|
|
37923
|
+
formatId = pushElement(format, numFmtStructure) + FIRST_NUMFMT_ID;
|
|
37725
37924
|
}
|
|
37726
37925
|
return formatId;
|
|
37727
37926
|
}
|
|
@@ -37746,20 +37945,15 @@ function addRelsToFile(relsFiles, path, rel) {
|
|
|
37746
37945
|
return id;
|
|
37747
37946
|
}
|
|
37748
37947
|
function pushElement(property, propertyList) {
|
|
37749
|
-
|
|
37750
|
-
|
|
37751
|
-
|
|
37948
|
+
let len = propertyList.length;
|
|
37949
|
+
const operator = typeof property === "object" ? deepEquals : (a, b) => a === b;
|
|
37950
|
+
for (let i = 0; i < len; i++) {
|
|
37951
|
+
if (operator(property, propertyList[i])) {
|
|
37952
|
+
return i;
|
|
37752
37953
|
}
|
|
37753
37954
|
}
|
|
37754
|
-
|
|
37755
|
-
|
|
37756
|
-
propertyList.push(property);
|
|
37757
|
-
elemId = propertyList.length - 1;
|
|
37758
|
-
}
|
|
37759
|
-
return {
|
|
37760
|
-
id: elemId,
|
|
37761
|
-
list: propertyList,
|
|
37762
|
-
};
|
|
37955
|
+
propertyList[propertyList.length] = property;
|
|
37956
|
+
return propertyList.length - 1;
|
|
37763
37957
|
}
|
|
37764
37958
|
const chartIds = [];
|
|
37765
37959
|
/**
|
|
@@ -38554,7 +38748,25 @@ function parseXML(xmlString, mimeType = "text/xml") {
|
|
|
38554
38748
|
}
|
|
38555
38749
|
return document;
|
|
38556
38750
|
}
|
|
38557
|
-
function
|
|
38751
|
+
function convertBorderDescr(descr) {
|
|
38752
|
+
if (!descr) {
|
|
38753
|
+
return undefined;
|
|
38754
|
+
}
|
|
38755
|
+
return {
|
|
38756
|
+
style: descr.style,
|
|
38757
|
+
color: { rgb: descr.color },
|
|
38758
|
+
};
|
|
38759
|
+
}
|
|
38760
|
+
function getDefaultXLSXStructure(data) {
|
|
38761
|
+
const xlsxBorders = Object.values(data.borders).map((border) => {
|
|
38762
|
+
return {
|
|
38763
|
+
left: convertBorderDescr(border.left),
|
|
38764
|
+
right: convertBorderDescr(border.right),
|
|
38765
|
+
bottom: convertBorderDescr(border.bottom),
|
|
38766
|
+
top: convertBorderDescr(border.top),
|
|
38767
|
+
};
|
|
38768
|
+
});
|
|
38769
|
+
const borders = [{}, ...xlsxBorders];
|
|
38558
38770
|
return {
|
|
38559
38771
|
relsFiles: [],
|
|
38560
38772
|
sharedStrings: [],
|
|
@@ -38577,7 +38789,7 @@ function getDefaultXLSXStructure() {
|
|
|
38577
38789
|
},
|
|
38578
38790
|
],
|
|
38579
38791
|
fills: [{ reservedAttribute: "none" }, { reservedAttribute: "gray125" }],
|
|
38580
|
-
borders
|
|
38792
|
+
borders,
|
|
38581
38793
|
numFmts: [],
|
|
38582
38794
|
dxfs: [],
|
|
38583
38795
|
};
|
|
@@ -39113,8 +39325,8 @@ class XlsxChartExtractor extends XlsxBaseExtractor {
|
|
|
39113
39325
|
return {
|
|
39114
39326
|
title: chartTitle,
|
|
39115
39327
|
type: CHART_TYPE_CONVERSION_MAP[chartType],
|
|
39116
|
-
dataSets: this.extractChartDatasets(this.querySelector(rootChartElement, `c:${chartType}`)),
|
|
39117
|
-
labelRange: this.extractChildTextContent(rootChartElement,
|
|
39328
|
+
dataSets: this.extractChartDatasets(this.querySelector(rootChartElement, `c:${chartType}`), chartType),
|
|
39329
|
+
labelRange: this.extractChildTextContent(rootChartElement, `c:ser ${chartType === "scatterChart" ? "c:numRef" : "c:cat"} c:f`),
|
|
39118
39330
|
backgroundColor: this.extractChildAttr(rootChartElement, "c:chartSpace > c:spPr a:srgbClr", "val", {
|
|
39119
39331
|
default: "ffffff",
|
|
39120
39332
|
}).asString(),
|
|
@@ -39143,8 +39355,8 @@ class XlsxChartExtractor extends XlsxBaseExtractor {
|
|
|
39143
39355
|
title: chartTitle,
|
|
39144
39356
|
type: "combo",
|
|
39145
39357
|
dataSets: [
|
|
39146
|
-
...this.extractChartDatasets(this.querySelector(chartElement, `c:barChart`)),
|
|
39147
|
-
...this.extractChartDatasets(this.querySelector(chartElement, `c:lineChart`)),
|
|
39358
|
+
...this.extractChartDatasets(this.querySelector(chartElement, `c:barChart`), "comboChart"),
|
|
39359
|
+
...this.extractChartDatasets(this.querySelector(chartElement, `c:lineChart`), "comboChart"),
|
|
39148
39360
|
],
|
|
39149
39361
|
labelRange: this.extractChildTextContent(chartElement, "c:ser c:cat c:f"),
|
|
39150
39362
|
backgroundColor: this.extractChildAttr(chartElement, "c:chartSpace > c:spPr a:srgbClr", "val", {
|
|
@@ -39162,7 +39374,10 @@ class XlsxChartExtractor extends XlsxBaseExtractor {
|
|
|
39162
39374
|
fontColor: "000000",
|
|
39163
39375
|
};
|
|
39164
39376
|
}
|
|
39165
|
-
extractChartDatasets(chartElement) {
|
|
39377
|
+
extractChartDatasets(chartElement, chartType) {
|
|
39378
|
+
if (chartType === "scatterChart") {
|
|
39379
|
+
return this.extractScatterChartDatasets(chartElement);
|
|
39380
|
+
}
|
|
39166
39381
|
return this.mapOnElements({ parent: chartElement, query: "c:ser" }, (chartDataElement) => {
|
|
39167
39382
|
return {
|
|
39168
39383
|
label: this.extractChildTextContent(chartDataElement, "c:tx c:f"),
|
|
@@ -39170,6 +39385,14 @@ class XlsxChartExtractor extends XlsxBaseExtractor {
|
|
|
39170
39385
|
};
|
|
39171
39386
|
});
|
|
39172
39387
|
}
|
|
39388
|
+
extractScatterChartDatasets(chartElement) {
|
|
39389
|
+
return this.mapOnElements({ parent: chartElement, query: "c:ser" }, (chartDataElement) => {
|
|
39390
|
+
return {
|
|
39391
|
+
label: this.extractChildTextContent(chartDataElement, "c:xVal c:f", { required: false }),
|
|
39392
|
+
range: this.extractChildTextContent(chartDataElement, "c:yVal c:f", { required: true }),
|
|
39393
|
+
};
|
|
39394
|
+
});
|
|
39395
|
+
}
|
|
39173
39396
|
/**
|
|
39174
39397
|
* The chart type in the XML isn't explicitly defined, but there is an XML element that define the
|
|
39175
39398
|
* chart, and this element tag name tells us which type of chart it is. We just need to find this XML element.
|
|
@@ -40563,12 +40786,14 @@ class BasePlugin {
|
|
|
40563
40786
|
static getters = [];
|
|
40564
40787
|
history;
|
|
40565
40788
|
dispatch;
|
|
40566
|
-
|
|
40789
|
+
canDispatch;
|
|
40790
|
+
constructor(stateObserver, dispatch, canDispatch) {
|
|
40567
40791
|
this.history = Object.assign(Object.create(stateObserver), {
|
|
40568
40792
|
update: stateObserver.addChange.bind(stateObserver, this),
|
|
40569
40793
|
selectCell: () => { },
|
|
40570
40794
|
});
|
|
40571
40795
|
this.dispatch = dispatch;
|
|
40796
|
+
this.canDispatch = canDispatch;
|
|
40572
40797
|
}
|
|
40573
40798
|
/**
|
|
40574
40799
|
* Export for excel should be available for all plugins, even for the UI.
|
|
@@ -40647,8 +40872,8 @@ class BasePlugin {
|
|
|
40647
40872
|
class CorePlugin extends BasePlugin {
|
|
40648
40873
|
getters;
|
|
40649
40874
|
uuidGenerator;
|
|
40650
|
-
constructor({ getters, stateObserver, range, dispatch, uuidGenerator }) {
|
|
40651
|
-
super(stateObserver, dispatch);
|
|
40875
|
+
constructor({ getters, stateObserver, range, dispatch, canDispatch, uuidGenerator, }) {
|
|
40876
|
+
super(stateObserver, dispatch, canDispatch);
|
|
40652
40877
|
range.addRangeProvider(this.adaptRanges.bind(this));
|
|
40653
40878
|
this.getters = getters;
|
|
40654
40879
|
this.uuidGenerator = uuidGenerator;
|
|
@@ -46145,8 +46370,8 @@ class UIPlugin extends BasePlugin {
|
|
|
46145
46370
|
getters;
|
|
46146
46371
|
ui;
|
|
46147
46372
|
selection;
|
|
46148
|
-
constructor({ getters, stateObserver, dispatch, uiActions, selection }) {
|
|
46149
|
-
super(stateObserver, dispatch);
|
|
46373
|
+
constructor({ getters, stateObserver, dispatch, canDispatch, uiActions, selection, }) {
|
|
46374
|
+
super(stateObserver, dispatch, canDispatch);
|
|
46150
46375
|
this.getters = getters;
|
|
46151
46376
|
this.ui = uiActions;
|
|
46152
46377
|
this.selection = selection;
|
|
@@ -47426,7 +47651,9 @@ class Evaluator {
|
|
|
47426
47651
|
this.blockedArrayFormulas = this.createEmptyPositionSet();
|
|
47427
47652
|
this.spreadingRelations = new SpreadingRelation(this.createEmptyPositionSet.bind(this));
|
|
47428
47653
|
this.formulaDependencies = lazy(() => {
|
|
47429
|
-
const dependencies = [...this.getAllCells()].flatMap((position) => this.getDirectDependencies(position)
|
|
47654
|
+
const dependencies = [...this.getAllCells()].flatMap((position) => this.getDirectDependencies(position)
|
|
47655
|
+
.filter((range) => !range.invalidSheetName && !range.invalidXc)
|
|
47656
|
+
.map((range) => ({
|
|
47430
47657
|
data: position,
|
|
47431
47658
|
boundingBox: {
|
|
47432
47659
|
zone: range.zone,
|
|
@@ -51510,6 +51737,7 @@ const invalidateTableStyleCommands = [
|
|
|
51510
51737
|
"CREATE_TABLE",
|
|
51511
51738
|
"UPDATE_TABLE",
|
|
51512
51739
|
"UPDATE_FILTER",
|
|
51740
|
+
"REMOVE_TABLE",
|
|
51513
51741
|
];
|
|
51514
51742
|
const invalidateTableStyleCommandsSet = new Set(invalidateTableStyleCommands);
|
|
51515
51743
|
function doesCommandInvalidatesTableStyle(cmd) {
|
|
@@ -51537,6 +51765,10 @@ class CellComputedStylePlugin extends UIPlugin {
|
|
|
51537
51765
|
this.styles = {};
|
|
51538
51766
|
return;
|
|
51539
51767
|
}
|
|
51768
|
+
if (invalidateBordersCommands.has(cmd.type)) {
|
|
51769
|
+
this.borders = {};
|
|
51770
|
+
return;
|
|
51771
|
+
}
|
|
51540
51772
|
}
|
|
51541
51773
|
getCellComputedBorder(position) {
|
|
51542
51774
|
const { sheetId, row, col } = position;
|
|
@@ -55477,7 +55709,6 @@ class SpreadsheetDashboard extends owl.Component {
|
|
|
55477
55709
|
Popover,
|
|
55478
55710
|
VerticalScrollBar,
|
|
55479
55711
|
HorizontalScrollBar,
|
|
55480
|
-
FilterIconsOverlay,
|
|
55481
55712
|
};
|
|
55482
55713
|
cellPopovers;
|
|
55483
55714
|
onMouseWheel;
|
|
@@ -56364,6 +56595,13 @@ class TopBarComposer extends owl.Component {
|
|
|
56364
56595
|
"border-color": SELECTION_BORDER_COLOR,
|
|
56365
56596
|
});
|
|
56366
56597
|
}
|
|
56598
|
+
get delimitation() {
|
|
56599
|
+
const { width, height } = this.env.model.getters.getSheetViewDimensionWithHeaders();
|
|
56600
|
+
return {
|
|
56601
|
+
width,
|
|
56602
|
+
height,
|
|
56603
|
+
};
|
|
56604
|
+
}
|
|
56367
56605
|
onFocus(selection) {
|
|
56368
56606
|
this.composerFocusStore.focusTopBarComposer(selection);
|
|
56369
56607
|
}
|
|
@@ -58647,6 +58885,9 @@ function createChart(chart, chartSheetIndex, data) {
|
|
|
58647
58885
|
case "line":
|
|
58648
58886
|
plot = addLineChart(chart.data);
|
|
58649
58887
|
break;
|
|
58888
|
+
case "scatter":
|
|
58889
|
+
plot = addScatterChart(chart.data);
|
|
58890
|
+
break;
|
|
58650
58891
|
case "pie":
|
|
58651
58892
|
plot = addDoughnutChart(chart.data, chartSheetIndex, data, { holeSize: 0 });
|
|
58652
58893
|
break;
|
|
@@ -58924,6 +59165,52 @@ function addLineChart(chart) {
|
|
|
58924
59165
|
${addAx(axisPos, "c:valAx", valAxId, catAxId, { fontColor: chart.fontColor })}
|
|
58925
59166
|
`;
|
|
58926
59167
|
}
|
|
59168
|
+
function addScatterChart(chart) {
|
|
59169
|
+
const colors = new ChartColors();
|
|
59170
|
+
const dataSetsNodes = [];
|
|
59171
|
+
for (const [dsIndex, dataset] of Object.entries(chart.dataSets)) {
|
|
59172
|
+
dataSetsNodes.push(escapeXml /*xml*/ `
|
|
59173
|
+
<c:ser>
|
|
59174
|
+
<c:idx val="${dsIndex}"/>
|
|
59175
|
+
<c:order val="${dsIndex}"/>
|
|
59176
|
+
<c:smooth val="0"/>
|
|
59177
|
+
<c:spPr>
|
|
59178
|
+
<a:ln w="19050" cap="rnd">
|
|
59179
|
+
<a:noFill/>
|
|
59180
|
+
<a:round/>
|
|
59181
|
+
</a:ln>
|
|
59182
|
+
<a:effectLst/>
|
|
59183
|
+
</c:spPr>
|
|
59184
|
+
<c:marker>
|
|
59185
|
+
<c:symbol val="circle" />
|
|
59186
|
+
<c:size val="5"/>
|
|
59187
|
+
${shapeProperty({ backgroundColor: toXlsxHexColor(colors.next()) })}
|
|
59188
|
+
</c:marker>
|
|
59189
|
+
${chart.labelRange
|
|
59190
|
+
? escapeXml /*xml*/ `<c:xVal> <!-- x-coordinate values -->
|
|
59191
|
+
${numberRef(chart.labelRange)}
|
|
59192
|
+
</c:xVal>`
|
|
59193
|
+
: ""}
|
|
59194
|
+
<c:yVal> <!-- y-coordinate values -->
|
|
59195
|
+
${numberRef(dataset.range)}
|
|
59196
|
+
</c:yVal>
|
|
59197
|
+
</c:ser>
|
|
59198
|
+
`);
|
|
59199
|
+
}
|
|
59200
|
+
const axisPos = chart.verticalAxisPosition === "left" ? "l" : "r";
|
|
59201
|
+
return escapeXml /*xml*/ `
|
|
59202
|
+
<c:scatterChart>
|
|
59203
|
+
<!-- each data marker in the series does not have a different color -->
|
|
59204
|
+
<c:varyColors val="0"/>
|
|
59205
|
+
<c:scatterStyle val="lineMarker"/>
|
|
59206
|
+
${joinXmlNodes(dataSetsNodes)}
|
|
59207
|
+
<c:axId val="${catAxId}" />
|
|
59208
|
+
<c:axId val="${valAxId}" />
|
|
59209
|
+
</c:scatterChart>
|
|
59210
|
+
${addAx("b", "c:valAx", catAxId, valAxId, { fontColor: chart.fontColor })}
|
|
59211
|
+
${addAx(axisPos, "c:valAx", valAxId, catAxId, { fontColor: chart.fontColor })}
|
|
59212
|
+
`;
|
|
59213
|
+
}
|
|
58927
59214
|
function addDoughnutChart(chart, chartSheetIndex, data, { holeSize } = { holeSize: 50 }) {
|
|
58928
59215
|
const colors = new ChartColors();
|
|
58929
59216
|
const maxLength = largeMax(chart.dataSets.map((ds) => getRangeSize(ds.range, chartSheetIndex, data)));
|
|
@@ -59064,8 +59351,7 @@ function addContent(content, sharedStrings, forceString = false) {
|
|
|
59064
59351
|
attrs.push(["t", "b"]);
|
|
59065
59352
|
}
|
|
59066
59353
|
else if (forceString || !isNumber(value, DEFAULT_LOCALE)) {
|
|
59067
|
-
|
|
59068
|
-
value = id.toString();
|
|
59354
|
+
value = pushElement(content, sharedStrings);
|
|
59069
59355
|
attrs.push(["t", "s"]);
|
|
59070
59356
|
}
|
|
59071
59357
|
return { attrs, node: escapeXml /*xml*/ `<v>${value}</v>` };
|
|
@@ -59190,8 +59476,7 @@ function addCellIsRule(cf, rule, dxfs) {
|
|
|
59190
59476
|
if (rule.style.fillColor) {
|
|
59191
59477
|
dxf.fill = { fgColor: { rgb: rule.style.fillColor } };
|
|
59192
59478
|
}
|
|
59193
|
-
|
|
59194
|
-
ruleAttributes.push(["dxfId", id]);
|
|
59479
|
+
ruleAttributes.push(["dxfId", pushElement(dxf, dxfs)]);
|
|
59195
59480
|
return escapeXml /*xml*/ `
|
|
59196
59481
|
<conditionalFormatting sqref="${cf.ranges.join(" ")}">
|
|
59197
59482
|
<cfRule ${formatAttributes(ruleAttributes)}>
|
|
@@ -60021,8 +60306,9 @@ function addSheetViews(sheet) {
|
|
|
60021
60306
|
*/
|
|
60022
60307
|
function getXLSX(data) {
|
|
60023
60308
|
data = fixLengthySheetNames(data);
|
|
60309
|
+
data = purgeSingleRowTables(data);
|
|
60024
60310
|
const files = [];
|
|
60025
|
-
const construct = getDefaultXLSXStructure();
|
|
60311
|
+
const construct = getDefaultXLSXStructure(data);
|
|
60026
60312
|
files.push(createWorkbook(data, construct));
|
|
60027
60313
|
files.push(...createWorksheets(data, construct));
|
|
60028
60314
|
files.push(createStylesSheet(construct));
|
|
@@ -60302,6 +60588,16 @@ function fixLengthySheetNames(data) {
|
|
|
60302
60588
|
}
|
|
60303
60589
|
return JSON.parse(stringifiedData);
|
|
60304
60590
|
}
|
|
60591
|
+
/** Excel files do not support tables with a single row the defined range
|
|
60592
|
+
* Since those tables are not really useful (no filtering/limited styling)
|
|
60593
|
+
* This function filters out all tables with a single row.
|
|
60594
|
+
*/
|
|
60595
|
+
function purgeSingleRowTables(data) {
|
|
60596
|
+
for (const sheet of data.sheets) {
|
|
60597
|
+
sheet.tables = sheet.tables.filter((table) => zoneToDimension(toZone(table.range)).numberOfRows > 1);
|
|
60598
|
+
}
|
|
60599
|
+
return data;
|
|
60600
|
+
}
|
|
60305
60601
|
|
|
60306
60602
|
var Status;
|
|
60307
60603
|
(function (Status) {
|
|
@@ -60555,6 +60851,7 @@ class Model extends EventBus {
|
|
|
60555
60851
|
stateObserver: this.state,
|
|
60556
60852
|
range: this.range,
|
|
60557
60853
|
dispatch: this.dispatchFromCorePlugin,
|
|
60854
|
+
canDispatch: this.canDispatch,
|
|
60558
60855
|
uuidGenerator: this.uuidGenerator,
|
|
60559
60856
|
custom: this.config.custom,
|
|
60560
60857
|
external: this.config.external,
|
|
@@ -60565,6 +60862,7 @@ class Model extends EventBus {
|
|
|
60565
60862
|
getters: this.getters,
|
|
60566
60863
|
stateObserver: this.state,
|
|
60567
60864
|
dispatch: this.dispatch,
|
|
60865
|
+
canDispatch: this.canDispatch,
|
|
60568
60866
|
selection: this.selection,
|
|
60569
60867
|
moveClient: this.session.move.bind(this.session),
|
|
60570
60868
|
custom: this.config.custom,
|
|
@@ -60888,7 +61186,7 @@ const links = {
|
|
|
60888
61186
|
const components = {
|
|
60889
61187
|
Checkbox,
|
|
60890
61188
|
Section,
|
|
60891
|
-
|
|
61189
|
+
RoundColorPicker,
|
|
60892
61190
|
ChartDataSeries,
|
|
60893
61191
|
ChartErrorSection,
|
|
60894
61192
|
ChartLabelRange,
|
|
@@ -60991,6 +61289,6 @@ exports.tokenColors = tokenColors;
|
|
|
60991
61289
|
exports.tokenize = tokenize;
|
|
60992
61290
|
|
|
60993
61291
|
|
|
60994
|
-
__info__.version = "17.3.0-alpha.
|
|
60995
|
-
__info__.date = "2024-04-
|
|
60996
|
-
__info__.hash = "
|
|
61292
|
+
__info__.version = "17.3.0-alpha.3";
|
|
61293
|
+
__info__.date = "2024-04-10T12:28:23.658Z";
|
|
61294
|
+
__info__.hash = "80b5056";
|