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