@odoo/o-spreadsheet 16.5.0-alpha.9 → 17.0.0
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 +2001 -1298
- package/dist/o-spreadsheet.d.ts +141 -103
- package/dist/o-spreadsheet.esm.js +2002 -1299
- package/dist/o-spreadsheet.iife.js +2000 -1297
- package/dist/o-spreadsheet.iife.min.js +331 -358
- package/package.json +2 -2
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* This file is generated by o-spreadsheet build tools. Do not edit it.
|
|
4
4
|
* @see https://github.com/odoo/o-spreadsheet
|
|
5
|
-
* @version
|
|
6
|
-
* @date 2023-10-
|
|
7
|
-
* @hash
|
|
5
|
+
* @version 17.0.0
|
|
6
|
+
* @date 2023-10-27T06:53:09.229Z
|
|
7
|
+
* @hash c0a34c2
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
'use strict';
|
|
@@ -132,6 +132,7 @@ const HEADER_GROUPING_BORDER_COLOR = "#999";
|
|
|
132
132
|
const GRID_BORDER_COLOR = "#E2E3E3";
|
|
133
133
|
const FROZEN_PANE_HEADER_BORDER_COLOR = "#BCBCBC";
|
|
134
134
|
const FROZEN_PANE_BORDER_COLOR = "#DADFE8";
|
|
135
|
+
const COMPOSER_ASSISTANT_COLOR = "#9B359B";
|
|
135
136
|
// Color picker defaults as upper case HEX to match `toHex`helper
|
|
136
137
|
const COLOR_PICKER_DEFAULTS = [
|
|
137
138
|
"#000000",
|
|
@@ -1243,7 +1244,17 @@ function valueToDateNumber(value, locale) {
|
|
|
1243
1244
|
function isDateTime(str, locale) {
|
|
1244
1245
|
return parseDateTime(str, locale) !== null;
|
|
1245
1246
|
}
|
|
1247
|
+
const CACHE = new Map();
|
|
1246
1248
|
function parseDateTime(str, locale) {
|
|
1249
|
+
if (!CACHE.has(locale)) {
|
|
1250
|
+
CACHE.set(locale, new Map());
|
|
1251
|
+
}
|
|
1252
|
+
if (!CACHE.get(locale).has(str)) {
|
|
1253
|
+
CACHE.get(locale).set(str, _parseDateTime(str, locale));
|
|
1254
|
+
}
|
|
1255
|
+
return CACHE.get(locale).get(str);
|
|
1256
|
+
}
|
|
1257
|
+
function _parseDateTime(str, locale) {
|
|
1247
1258
|
str = str.trim();
|
|
1248
1259
|
let time = null;
|
|
1249
1260
|
const timeMatch = str.match(timeRegexp);
|
|
@@ -2049,6 +2060,7 @@ exports.CommandResult = void 0;
|
|
|
2049
2060
|
CommandResult["InvalidDataValidationCriterionValue"] = "InvalidDataValidationCriterionValue";
|
|
2050
2061
|
CommandResult["InvalidNumberOfCriterionValues"] = "InvalidNumberOfCriterionValues";
|
|
2051
2062
|
CommandResult["BlockingValidationRule"] = "BlockingValidationRule";
|
|
2063
|
+
CommandResult["InvalidCopyPasteSelection"] = "InvalidCopyPasteSelection";
|
|
2052
2064
|
})(exports.CommandResult || (exports.CommandResult = {}));
|
|
2053
2065
|
|
|
2054
2066
|
const DEFAULT_LOCALES = [
|
|
@@ -2101,7 +2113,7 @@ var LAYERS;
|
|
|
2101
2113
|
const SORT_TYPES_ORDER = ["number", "string", "boolean", "undefined"];
|
|
2102
2114
|
function assert(condition, message) {
|
|
2103
2115
|
if (!condition()) {
|
|
2104
|
-
throw new
|
|
2116
|
+
throw new EvaluationError(CellErrorType.GenericError, message);
|
|
2105
2117
|
}
|
|
2106
2118
|
}
|
|
2107
2119
|
// -----------------------------------------------------------------------------
|
|
@@ -2507,7 +2519,7 @@ function evaluatePredicate(value, criterion) {
|
|
|
2507
2519
|
function visitMatchingRanges(args, cb, locale, isQuery = false) {
|
|
2508
2520
|
const countArg = args.length;
|
|
2509
2521
|
if (countArg % 2 === 1) {
|
|
2510
|
-
throw new Error(_t(
|
|
2522
|
+
throw new Error(_t("Function [[FUNCTION_NAME]] expects criteria_range and criterion to be in pairs."));
|
|
2511
2523
|
}
|
|
2512
2524
|
const dimRow = args[0].length;
|
|
2513
2525
|
const dimCol = args[0][0].length;
|
|
@@ -2517,7 +2529,7 @@ function visitMatchingRanges(args, cb, locale, isQuery = false) {
|
|
|
2517
2529
|
if (!isMatrix(criteriaRange) ||
|
|
2518
2530
|
criteriaRange.length !== dimRow ||
|
|
2519
2531
|
criteriaRange[0].length !== dimCol) {
|
|
2520
|
-
throw new Error(_t(
|
|
2532
|
+
throw new Error(_t("Function [[FUNCTION_NAME]] expects criteria_range to have the same dimension"));
|
|
2521
2533
|
}
|
|
2522
2534
|
const description = toString(args[i + 1]);
|
|
2523
2535
|
predicates.push(getPredicate(description, isQuery, locale));
|
|
@@ -4063,8 +4075,9 @@ class RangeImpl {
|
|
|
4063
4075
|
}
|
|
4064
4076
|
get zone() {
|
|
4065
4077
|
const { left, top, bottom, right } = this._zone;
|
|
4066
|
-
if (right !== undefined && bottom !== undefined)
|
|
4067
|
-
return
|
|
4078
|
+
if (right !== undefined && bottom !== undefined) {
|
|
4079
|
+
return this._zone;
|
|
4080
|
+
}
|
|
4068
4081
|
else if (bottom === undefined && right !== undefined) {
|
|
4069
4082
|
return { right, top, left, bottom: this.getSheetSize(this.sheetId).numberOfRows - 1 };
|
|
4070
4083
|
}
|
|
@@ -4489,6 +4502,69 @@ function computeIconWidth(style) {
|
|
|
4489
4502
|
function toLowerCase(str) {
|
|
4490
4503
|
return str ? str.toLowerCase() : "";
|
|
4491
4504
|
}
|
|
4505
|
+
/**
|
|
4506
|
+
* Extract the fontSize from a context font string
|
|
4507
|
+
* @param font The (context) font string to parse
|
|
4508
|
+
* @returns The fontSize in pixels
|
|
4509
|
+
*/
|
|
4510
|
+
const pxRegex = /([0-9\.]*)px/;
|
|
4511
|
+
function getContextFontSize(font) {
|
|
4512
|
+
return Number(font.match(pxRegex)?.[1]);
|
|
4513
|
+
}
|
|
4514
|
+
function drawDecoratedText(context, text, position, underline = false, strikethrough = false, strokeWidth = getContextFontSize(context.font) / 10 //This value is defined to get a good looking stroke
|
|
4515
|
+
) {
|
|
4516
|
+
context.fillText(text, position.x, position.y);
|
|
4517
|
+
if (!underline && !strikethrough) {
|
|
4518
|
+
return;
|
|
4519
|
+
}
|
|
4520
|
+
const measure = context.measureText(text);
|
|
4521
|
+
const textWidth = measure.width;
|
|
4522
|
+
const textHeight = measure.actualBoundingBoxAscent + measure.actualBoundingBoxDescent;
|
|
4523
|
+
const boxHeight = measure.fontBoundingBoxAscent + measure.fontBoundingBoxDescent;
|
|
4524
|
+
let { x, y } = position;
|
|
4525
|
+
let strikeY = y, underlineY = y;
|
|
4526
|
+
switch (context.textAlign) {
|
|
4527
|
+
case "center":
|
|
4528
|
+
x -= textWidth / 2;
|
|
4529
|
+
break;
|
|
4530
|
+
case "right":
|
|
4531
|
+
x -= textWidth;
|
|
4532
|
+
break;
|
|
4533
|
+
}
|
|
4534
|
+
switch (context.textBaseline) {
|
|
4535
|
+
case "top":
|
|
4536
|
+
underlineY += boxHeight - 2 * strokeWidth;
|
|
4537
|
+
strikeY += boxHeight - textHeight;
|
|
4538
|
+
break;
|
|
4539
|
+
case "middle":
|
|
4540
|
+
underlineY += boxHeight / 2 - strokeWidth;
|
|
4541
|
+
break;
|
|
4542
|
+
case "alphabetic":
|
|
4543
|
+
underlineY += 2 * strokeWidth;
|
|
4544
|
+
strikeY -= textHeight / 2 - strokeWidth / 2;
|
|
4545
|
+
break;
|
|
4546
|
+
case "bottom":
|
|
4547
|
+
underlineY = y;
|
|
4548
|
+
strikeY -= textHeight / 2 - strokeWidth / 2;
|
|
4549
|
+
break;
|
|
4550
|
+
}
|
|
4551
|
+
if (underline) {
|
|
4552
|
+
context.lineWidth = strokeWidth;
|
|
4553
|
+
context.strokeStyle = context.fillStyle;
|
|
4554
|
+
context.beginPath();
|
|
4555
|
+
context.moveTo(x, underlineY);
|
|
4556
|
+
context.lineTo(x + textWidth, underlineY);
|
|
4557
|
+
context.stroke();
|
|
4558
|
+
}
|
|
4559
|
+
if (strikethrough) {
|
|
4560
|
+
context.lineWidth = strokeWidth;
|
|
4561
|
+
context.strokeStyle = context.fillStyle;
|
|
4562
|
+
context.beginPath();
|
|
4563
|
+
context.moveTo(x, strikeY);
|
|
4564
|
+
context.lineTo(x + textWidth, strikeY);
|
|
4565
|
+
context.stroke();
|
|
4566
|
+
}
|
|
4567
|
+
}
|
|
4492
4568
|
|
|
4493
4569
|
/*
|
|
4494
4570
|
* https://stackoverflow.com/questions/105034/create-guid-uuid-in-javascript
|
|
@@ -4552,6 +4628,29 @@ function createAction(item) {
|
|
|
4552
4628
|
};
|
|
4553
4629
|
}
|
|
4554
4630
|
|
|
4631
|
+
function transformZone(zone, executed) {
|
|
4632
|
+
if (executed.type === "REMOVE_COLUMNS_ROWS") {
|
|
4633
|
+
return reduceZoneOnDeletion(zone, executed.dimension === "COL" ? "left" : "top", executed.elements);
|
|
4634
|
+
}
|
|
4635
|
+
if (executed.type === "ADD_COLUMNS_ROWS") {
|
|
4636
|
+
return expandZoneOnInsertion(zone, executed.dimension === "COL" ? "left" : "top", executed.base, executed.position, executed.quantity);
|
|
4637
|
+
}
|
|
4638
|
+
return { ...zone };
|
|
4639
|
+
}
|
|
4640
|
+
function transformRangeData(range, executed) {
|
|
4641
|
+
const deletedSheet = executed.type === "DELETE_SHEET" && executed.sheetId;
|
|
4642
|
+
if ("sheetId" in executed && range._sheetId !== executed.sheetId) {
|
|
4643
|
+
return range;
|
|
4644
|
+
}
|
|
4645
|
+
else {
|
|
4646
|
+
const newZone = transformZone(range._zone, executed);
|
|
4647
|
+
if (newZone && deletedSheet !== range._sheetId) {
|
|
4648
|
+
return { ...range, _zone: newZone };
|
|
4649
|
+
}
|
|
4650
|
+
}
|
|
4651
|
+
return undefined;
|
|
4652
|
+
}
|
|
4653
|
+
|
|
4555
4654
|
class ChartJsComponent extends owl.Component {
|
|
4556
4655
|
static template = "o-spreadsheet-ChartJsComponent";
|
|
4557
4656
|
canvas = owl.useRef("graphContainer");
|
|
@@ -4574,15 +4673,7 @@ class ChartJsComponent extends owl.Component {
|
|
|
4574
4673
|
const runtime = this.chartRuntime;
|
|
4575
4674
|
this.createChart(runtime.chartJsConfig);
|
|
4576
4675
|
});
|
|
4577
|
-
|
|
4578
|
-
owl.onPatched(() => {
|
|
4579
|
-
const chartRuntime = this.chartRuntime;
|
|
4580
|
-
if (deepEquals(previousRuntime, chartRuntime)) {
|
|
4581
|
-
return;
|
|
4582
|
-
}
|
|
4583
|
-
this.updateChartJs(chartRuntime);
|
|
4584
|
-
previousRuntime = chartRuntime;
|
|
4585
|
-
});
|
|
4676
|
+
owl.useEffect(() => this.updateChartJs(this.chartRuntime), () => [this.chartRuntime]);
|
|
4586
4677
|
}
|
|
4587
4678
|
createChart(chartData) {
|
|
4588
4679
|
const canvas = this.canvas.el;
|
|
@@ -4619,145 +4710,606 @@ ChartJsComponent.props = {
|
|
|
4619
4710
|
};
|
|
4620
4711
|
|
|
4621
4712
|
/**
|
|
4622
|
-
*
|
|
4623
|
-
*
|
|
4624
|
-
* So, the solution was to import the behavior of owl 1 directly in our
|
|
4625
|
-
* codebase, with one difference: the css is added to the sheet as soon as the
|
|
4626
|
-
* css tag is executed. In owl 1, the css was added as soon as a Component was
|
|
4627
|
-
* created for the first time.
|
|
4713
|
+
* AbstractChart is the class from which every Chart should inherit.
|
|
4714
|
+
* The role of this class is to maintain the state of each chart.
|
|
4628
4715
|
*/
|
|
4629
|
-
|
|
4630
|
-
|
|
4716
|
+
class AbstractChart {
|
|
4717
|
+
sheetId;
|
|
4718
|
+
title;
|
|
4719
|
+
getters;
|
|
4720
|
+
constructor(definition, sheetId, getters) {
|
|
4721
|
+
this.title = definition.title;
|
|
4722
|
+
this.sheetId = sheetId;
|
|
4723
|
+
this.getters = getters;
|
|
4724
|
+
}
|
|
4725
|
+
/**
|
|
4726
|
+
* Validate the chart definition given as arguments. This function will be
|
|
4727
|
+
* called from allowDispatch function
|
|
4728
|
+
*/
|
|
4729
|
+
static validateChartDefinition(validator, definition) {
|
|
4730
|
+
throw new Error("This method should be implemented by sub class");
|
|
4731
|
+
}
|
|
4732
|
+
/**
|
|
4733
|
+
* Get a new chart definition transformed with the executed command. This
|
|
4734
|
+
* functions will be called during operational transform process
|
|
4735
|
+
*/
|
|
4736
|
+
static transformDefinition(definition, executed) {
|
|
4737
|
+
throw new Error("This method should be implemented by sub class");
|
|
4738
|
+
}
|
|
4739
|
+
/**
|
|
4740
|
+
* Get an empty definition based on the given context
|
|
4741
|
+
*/
|
|
4742
|
+
static getDefinitionFromContextCreation(context) {
|
|
4743
|
+
throw new Error("This method should be implemented by sub class");
|
|
4744
|
+
}
|
|
4745
|
+
}
|
|
4746
|
+
|
|
4631
4747
|
/**
|
|
4632
|
-
*
|
|
4633
|
-
*
|
|
4634
|
-
* ```js
|
|
4635
|
-
* css`.component-a { color: red; }`;
|
|
4636
|
-
* ```
|
|
4748
|
+
* This file contains helpers that are common to different charts (mainly
|
|
4749
|
+
* line, bar and pie charts)
|
|
4637
4750
|
*/
|
|
4638
|
-
|
|
4639
|
-
|
|
4640
|
-
|
|
4641
|
-
|
|
4642
|
-
|
|
4643
|
-
|
|
4644
|
-
|
|
4645
|
-
|
|
4646
|
-
|
|
4647
|
-
|
|
4648
|
-
|
|
4649
|
-
|
|
4650
|
-
|
|
4651
|
-
|
|
4652
|
-
|
|
4653
|
-
|
|
4654
|
-
if (part.includes("&")) {
|
|
4655
|
-
part = selector.replace(/&/g, parentSelector || "");
|
|
4656
|
-
}
|
|
4657
|
-
if (stackIndex < selectorStack.length - 1) {
|
|
4658
|
-
part = generateSelector(stackIndex + 1, part);
|
|
4751
|
+
/**
|
|
4752
|
+
* Adapt ranges of a chart which support DataSet (dataSets and LabelRange).
|
|
4753
|
+
*/
|
|
4754
|
+
function updateChartRangesWithDataSets(getters, applyChange, chartDataSets, chartLabelRange) {
|
|
4755
|
+
let isStale = false;
|
|
4756
|
+
const dataSetsWithUndefined = [];
|
|
4757
|
+
for (let index in chartDataSets) {
|
|
4758
|
+
let ds = chartDataSets[index];
|
|
4759
|
+
if (ds.labelCell) {
|
|
4760
|
+
const labelCell = adaptChartRange(ds.labelCell, applyChange);
|
|
4761
|
+
if (ds.labelCell !== labelCell) {
|
|
4762
|
+
isStale = true;
|
|
4763
|
+
ds = {
|
|
4764
|
+
...ds,
|
|
4765
|
+
labelCell: labelCell,
|
|
4766
|
+
};
|
|
4659
4767
|
}
|
|
4660
|
-
parts.push(part);
|
|
4661
4768
|
}
|
|
4662
|
-
|
|
4663
|
-
|
|
4664
|
-
|
|
4665
|
-
|
|
4666
|
-
|
|
4667
|
-
|
|
4668
|
-
|
|
4669
|
-
|
|
4769
|
+
const dataRange = adaptChartRange(ds.dataRange, applyChange);
|
|
4770
|
+
if (dataRange === undefined ||
|
|
4771
|
+
getters.getRangeString(dataRange, dataRange.sheetId) === INCORRECT_RANGE_STRING) {
|
|
4772
|
+
isStale = true;
|
|
4773
|
+
ds = undefined;
|
|
4774
|
+
}
|
|
4775
|
+
else if (dataRange !== ds.dataRange) {
|
|
4776
|
+
isStale = true;
|
|
4777
|
+
ds = {
|
|
4778
|
+
...ds,
|
|
4779
|
+
dataRange,
|
|
4780
|
+
};
|
|
4670
4781
|
}
|
|
4782
|
+
dataSetsWithUndefined[index] = ds;
|
|
4671
4783
|
}
|
|
4672
|
-
|
|
4673
|
-
|
|
4674
|
-
|
|
4675
|
-
|
|
4676
|
-
|
|
4784
|
+
let labelRange = chartLabelRange;
|
|
4785
|
+
const range = adaptChartRange(labelRange, applyChange);
|
|
4786
|
+
if (range !== labelRange) {
|
|
4787
|
+
isStale = true;
|
|
4788
|
+
labelRange = range;
|
|
4789
|
+
}
|
|
4790
|
+
const dataSets = dataSetsWithUndefined.filter(isDefined$1);
|
|
4791
|
+
return {
|
|
4792
|
+
isStale,
|
|
4793
|
+
dataSets,
|
|
4794
|
+
labelRange,
|
|
4795
|
+
};
|
|
4796
|
+
}
|
|
4797
|
+
/**
|
|
4798
|
+
* Copy the dataSets given. All the ranges which are on sheetIdFrom will target
|
|
4799
|
+
* sheetIdTo.
|
|
4800
|
+
*/
|
|
4801
|
+
function copyDataSetsWithNewSheetId(sheetIdFrom, sheetIdTo, dataSets) {
|
|
4802
|
+
return dataSets.map((ds) => {
|
|
4803
|
+
return {
|
|
4804
|
+
dataRange: copyRangeWithNewSheetId(sheetIdFrom, sheetIdTo, ds.dataRange),
|
|
4805
|
+
labelCell: ds.labelCell
|
|
4806
|
+
? copyRangeWithNewSheetId(sheetIdFrom, sheetIdTo, ds.labelCell)
|
|
4807
|
+
: undefined,
|
|
4808
|
+
};
|
|
4809
|
+
});
|
|
4810
|
+
}
|
|
4811
|
+
/**
|
|
4812
|
+
* Copy a range. If the range is on the sheetIdFrom, the range will target
|
|
4813
|
+
* sheetIdTo.
|
|
4814
|
+
*/
|
|
4815
|
+
function copyLabelRangeWithNewSheetId(sheetIdFrom, sheetIdTo, range) {
|
|
4816
|
+
return range ? copyRangeWithNewSheetId(sheetIdFrom, sheetIdTo, range) : undefined;
|
|
4817
|
+
}
|
|
4818
|
+
/**
|
|
4819
|
+
* Adapt a single range of a chart
|
|
4820
|
+
*/
|
|
4821
|
+
function adaptChartRange(range, applyChange) {
|
|
4822
|
+
if (!range) {
|
|
4823
|
+
return undefined;
|
|
4824
|
+
}
|
|
4825
|
+
const change = applyChange(range);
|
|
4826
|
+
switch (change.changeType) {
|
|
4827
|
+
case "NONE":
|
|
4828
|
+
return range;
|
|
4829
|
+
case "REMOVE":
|
|
4830
|
+
return undefined;
|
|
4831
|
+
default:
|
|
4832
|
+
return change.range;
|
|
4833
|
+
}
|
|
4834
|
+
}
|
|
4835
|
+
/**
|
|
4836
|
+
* Create the dataSet objects from xcs
|
|
4837
|
+
*/
|
|
4838
|
+
function createDataSets(getters, dataSetsString, sheetId, dataSetsHaveTitle) {
|
|
4839
|
+
const dataSets = [];
|
|
4840
|
+
for (const sheetXC of dataSetsString) {
|
|
4841
|
+
const dataRange = getters.getRangeFromSheetXC(sheetId, sheetXC);
|
|
4842
|
+
const { unboundedZone: zone, sheetId: dataSetSheetId, invalidSheetName, invalidXc } = dataRange;
|
|
4843
|
+
if (invalidSheetName || invalidXc) {
|
|
4844
|
+
continue;
|
|
4677
4845
|
}
|
|
4678
|
-
|
|
4679
|
-
|
|
4680
|
-
|
|
4681
|
-
|
|
4682
|
-
|
|
4846
|
+
// It's a rectangle. We treat all columns (arbitrary) as different data series.
|
|
4847
|
+
if (zone.left !== zone.right && zone.top !== zone.bottom) {
|
|
4848
|
+
if (zone.right === undefined) {
|
|
4849
|
+
// Should never happens because of the allowDispatch of charts, but just making sure
|
|
4850
|
+
continue;
|
|
4683
4851
|
}
|
|
4684
|
-
|
|
4685
|
-
|
|
4852
|
+
for (let column = zone.left; column <= zone.right; column++) {
|
|
4853
|
+
const columnZone = {
|
|
4854
|
+
...zone,
|
|
4855
|
+
left: column,
|
|
4856
|
+
right: column,
|
|
4857
|
+
};
|
|
4858
|
+
dataSets.push(createDataSet(getters, dataSetSheetId, columnZone, dataSetsHaveTitle
|
|
4859
|
+
? {
|
|
4860
|
+
top: columnZone.top,
|
|
4861
|
+
bottom: columnZone.top,
|
|
4862
|
+
left: columnZone.left,
|
|
4863
|
+
right: columnZone.left,
|
|
4864
|
+
}
|
|
4865
|
+
: undefined));
|
|
4866
|
+
}
|
|
4867
|
+
}
|
|
4868
|
+
else if (zone.left === zone.right && zone.top === zone.bottom) {
|
|
4869
|
+
// A single cell. If it's only the title, the dataset is not added.
|
|
4870
|
+
if (!dataSetsHaveTitle) {
|
|
4871
|
+
dataSets.push(createDataSet(getters, dataSetSheetId, zone, undefined));
|
|
4686
4872
|
}
|
|
4687
4873
|
}
|
|
4874
|
+
else {
|
|
4875
|
+
/* 1 row or 1 column */
|
|
4876
|
+
dataSets.push(createDataSet(getters, dataSetSheetId, zone, dataSetsHaveTitle
|
|
4877
|
+
? {
|
|
4878
|
+
top: zone.top,
|
|
4879
|
+
bottom: zone.top,
|
|
4880
|
+
left: zone.left,
|
|
4881
|
+
right: zone.left,
|
|
4882
|
+
}
|
|
4883
|
+
: undefined));
|
|
4884
|
+
}
|
|
4688
4885
|
}
|
|
4689
|
-
return
|
|
4886
|
+
return dataSets;
|
|
4690
4887
|
}
|
|
4691
|
-
function
|
|
4692
|
-
|
|
4693
|
-
|
|
4694
|
-
|
|
4888
|
+
function createDataSet(getters, sheetId, fullZone, titleZone) {
|
|
4889
|
+
if (fullZone.left !== fullZone.right && fullZone.top !== fullZone.bottom) {
|
|
4890
|
+
throw new Error(`Zone should be a single column or row: ${zoneToXc(fullZone)}`);
|
|
4891
|
+
}
|
|
4892
|
+
if (titleZone) {
|
|
4893
|
+
const dataXC = zoneToXc(fullZone);
|
|
4894
|
+
const labelCellXC = zoneToXc(titleZone);
|
|
4895
|
+
return {
|
|
4896
|
+
labelCell: getters.getRangeFromSheetXC(sheetId, labelCellXC),
|
|
4897
|
+
dataRange: getters.getRangeFromSheetXC(sheetId, dataXC),
|
|
4898
|
+
};
|
|
4899
|
+
}
|
|
4900
|
+
else {
|
|
4901
|
+
return {
|
|
4902
|
+
labelCell: undefined,
|
|
4903
|
+
dataRange: getters.getRangeFromSheetXC(sheetId, zoneToXc(fullZone)),
|
|
4904
|
+
};
|
|
4905
|
+
}
|
|
4695
4906
|
}
|
|
4696
|
-
|
|
4697
|
-
|
|
4698
|
-
|
|
4699
|
-
|
|
4907
|
+
/**
|
|
4908
|
+
* Transform a dataSet to a ExcelDataSet
|
|
4909
|
+
*/
|
|
4910
|
+
function toExcelDataset(getters, ds) {
|
|
4911
|
+
const labelZone = ds.labelCell?.zone;
|
|
4912
|
+
let dataZone = ds.dataRange.zone;
|
|
4913
|
+
if (labelZone) {
|
|
4914
|
+
const { numberOfRows, numberOfCols } = zoneToDimension(dataZone);
|
|
4915
|
+
if (numberOfRows === 1) {
|
|
4916
|
+
dataZone = { ...dataZone, left: dataZone.left + 1 };
|
|
4917
|
+
}
|
|
4918
|
+
else if (numberOfCols === 1) {
|
|
4919
|
+
dataZone = { ...dataZone, top: dataZone.top + 1 };
|
|
4920
|
+
}
|
|
4921
|
+
}
|
|
4922
|
+
const dataRange = ds.dataRange.clone({ zone: dataZone });
|
|
4923
|
+
return {
|
|
4924
|
+
label: ds.labelCell ? getters.getRangeString(ds.labelCell, "forceSheetReference") : undefined,
|
|
4925
|
+
range: getters.getRangeString(dataRange, "forceSheetReference"),
|
|
4926
|
+
};
|
|
4700
4927
|
}
|
|
4701
|
-
function
|
|
4702
|
-
if (!
|
|
4703
|
-
return
|
|
4928
|
+
function toExcelLabelRange(getters, labelRange, shouldRemoveFirstLabel) {
|
|
4929
|
+
if (!labelRange)
|
|
4930
|
+
return undefined;
|
|
4931
|
+
let zone = {
|
|
4932
|
+
...labelRange.zone,
|
|
4933
|
+
};
|
|
4934
|
+
if (shouldRemoveFirstLabel && labelRange.zone.bottom > labelRange.zone.top) {
|
|
4935
|
+
zone.top = zone.top + 1;
|
|
4704
4936
|
}
|
|
4705
|
-
|
|
4937
|
+
const range = labelRange.clone({ zone });
|
|
4938
|
+
return getters.getRangeString(range, "forceSheetReference");
|
|
4706
4939
|
}
|
|
4707
4940
|
/**
|
|
4708
|
-
*
|
|
4941
|
+
* Transform a chart definition which supports dataSets (dataSets and LabelRange)
|
|
4942
|
+
* with an executed command
|
|
4709
4943
|
*/
|
|
4710
|
-
function
|
|
4711
|
-
|
|
4712
|
-
if (
|
|
4713
|
-
|
|
4714
|
-
|
|
4715
|
-
|
|
4944
|
+
function transformChartDefinitionWithDataSetsWithZone(definition, executed) {
|
|
4945
|
+
let labelRange;
|
|
4946
|
+
if (definition.labelRange) {
|
|
4947
|
+
const labelZone = transformZone(toUnboundedZone(definition.labelRange), executed);
|
|
4948
|
+
labelRange = labelZone ? zoneToXc(labelZone) : undefined;
|
|
4949
|
+
}
|
|
4950
|
+
const dataSets = definition.dataSets
|
|
4951
|
+
.map(toUnboundedZone)
|
|
4952
|
+
.map((zone) => transformZone(zone, executed))
|
|
4953
|
+
.filter(isDefined$1)
|
|
4954
|
+
.map(zoneToXc);
|
|
4955
|
+
return {
|
|
4956
|
+
...definition,
|
|
4957
|
+
labelRange,
|
|
4958
|
+
dataSets,
|
|
4959
|
+
};
|
|
4960
|
+
}
|
|
4961
|
+
const GraphColors = [
|
|
4962
|
+
// the same colors as those used in odoo reporting
|
|
4963
|
+
"rgb(31,119,180)",
|
|
4964
|
+
"rgb(255,127,14)",
|
|
4965
|
+
"rgb(174,199,232)",
|
|
4966
|
+
"rgb(255,187,120)",
|
|
4967
|
+
"rgb(44,160,44)",
|
|
4968
|
+
"rgb(152,223,138)",
|
|
4969
|
+
"rgb(214,39,40)",
|
|
4970
|
+
"rgb(255,152,150)",
|
|
4971
|
+
"rgb(148,103,189)",
|
|
4972
|
+
"rgb(197,176,213)",
|
|
4973
|
+
"rgb(140,86,75)",
|
|
4974
|
+
"rgb(196,156,148)",
|
|
4975
|
+
"rgb(227,119,194)",
|
|
4976
|
+
"rgb(247,182,210)",
|
|
4977
|
+
"rgb(127,127,127)",
|
|
4978
|
+
"rgb(199,199,199)",
|
|
4979
|
+
"rgb(188,189,34)",
|
|
4980
|
+
"rgb(219,219,141)",
|
|
4981
|
+
"rgb(23,190,207)",
|
|
4982
|
+
"rgb(158,218,229)",
|
|
4983
|
+
];
|
|
4984
|
+
class ChartColors {
|
|
4985
|
+
graphColorIndex = 0;
|
|
4986
|
+
next() {
|
|
4987
|
+
return GraphColors[this.graphColorIndex++ % GraphColors.length];
|
|
4716
4988
|
}
|
|
4717
|
-
return attributes;
|
|
4718
4989
|
}
|
|
4719
4990
|
/**
|
|
4720
|
-
*
|
|
4991
|
+
* Choose a font color based on a background color.
|
|
4992
|
+
* The font is white with a dark background.
|
|
4721
4993
|
*/
|
|
4722
|
-
function
|
|
4723
|
-
|
|
4724
|
-
|
|
4725
|
-
return attributes;
|
|
4726
|
-
if (style.bold) {
|
|
4727
|
-
attributes["font-weight"] = "bold";
|
|
4994
|
+
function chartFontColor(backgroundColor) {
|
|
4995
|
+
if (!backgroundColor) {
|
|
4996
|
+
return "#000000";
|
|
4728
4997
|
}
|
|
4729
|
-
|
|
4730
|
-
|
|
4998
|
+
return relativeLuminance(backgroundColor) < 0.3 ? "#FFFFFF" : "#000000";
|
|
4999
|
+
}
|
|
5000
|
+
function checkDataset(definition) {
|
|
5001
|
+
if (definition.dataSets) {
|
|
5002
|
+
const invalidRanges = definition.dataSets.find((range) => !rangeReference.test(range)) !== undefined;
|
|
5003
|
+
if (invalidRanges) {
|
|
5004
|
+
return "InvalidDataSet" /* CommandResult.InvalidDataSet */;
|
|
5005
|
+
}
|
|
5006
|
+
const zones = definition.dataSets.map(toUnboundedZone);
|
|
5007
|
+
if (zones.some((zone) => zone.top !== zone.bottom && isFullRow(zone))) {
|
|
5008
|
+
return "InvalidDataSet" /* CommandResult.InvalidDataSet */;
|
|
5009
|
+
}
|
|
4731
5010
|
}
|
|
4732
|
-
|
|
4733
|
-
|
|
4734
|
-
|
|
4735
|
-
|
|
5011
|
+
return "Success" /* CommandResult.Success */;
|
|
5012
|
+
}
|
|
5013
|
+
function checkLabelRange(definition) {
|
|
5014
|
+
if (definition.labelRange) {
|
|
5015
|
+
const invalidLabels = !rangeReference.test(definition.labelRange || "");
|
|
5016
|
+
if (invalidLabels) {
|
|
5017
|
+
return "InvalidLabelRange" /* CommandResult.InvalidLabelRange */;
|
|
5018
|
+
}
|
|
4736
5019
|
}
|
|
4737
|
-
|
|
4738
|
-
|
|
5020
|
+
return "Success" /* CommandResult.Success */;
|
|
5021
|
+
}
|
|
5022
|
+
function shouldRemoveFirstLabel(labelRange, dataset, dataSetsHaveTitle) {
|
|
5023
|
+
if (!dataSetsHaveTitle)
|
|
5024
|
+
return false;
|
|
5025
|
+
if (!labelRange)
|
|
5026
|
+
return false;
|
|
5027
|
+
if (!dataset)
|
|
5028
|
+
return true;
|
|
5029
|
+
const datasetLength = getZoneArea(dataset.dataRange.zone);
|
|
5030
|
+
const labelLength = getZoneArea(labelRange.zone);
|
|
5031
|
+
if (labelLength < datasetLength) {
|
|
5032
|
+
return false;
|
|
4739
5033
|
}
|
|
4740
|
-
return
|
|
5034
|
+
return true;
|
|
4741
5035
|
}
|
|
4742
|
-
|
|
4743
|
-
|
|
4744
|
-
|
|
4745
|
-
function
|
|
4746
|
-
|
|
4747
|
-
|
|
4748
|
-
|
|
4749
|
-
|
|
4750
|
-
|
|
5036
|
+
// ---------------------------------------------------------------------------
|
|
5037
|
+
// Scorecard
|
|
5038
|
+
// ---------------------------------------------------------------------------
|
|
5039
|
+
function getBaselineText(baseline, keyValue, baselineMode, locale) {
|
|
5040
|
+
if (!baseline) {
|
|
5041
|
+
return "";
|
|
5042
|
+
}
|
|
5043
|
+
else if (baselineMode === "text" ||
|
|
5044
|
+
keyValue?.type !== CellValueType.number ||
|
|
5045
|
+
baseline.type !== CellValueType.number) {
|
|
5046
|
+
return baseline.formattedValue;
|
|
5047
|
+
}
|
|
5048
|
+
else {
|
|
5049
|
+
let diff = keyValue.value - baseline.value;
|
|
5050
|
+
if (baselineMode === "percentage" && diff !== 0) {
|
|
5051
|
+
diff = (diff / baseline.value) * 100;
|
|
5052
|
+
}
|
|
5053
|
+
if (baselineMode !== "percentage" && baseline.format) {
|
|
5054
|
+
return formatValue(diff, { format: baseline.format, locale });
|
|
5055
|
+
}
|
|
5056
|
+
const baselineStr = Math.abs(parseFloat(diff.toFixed(2))).toLocaleString();
|
|
5057
|
+
return baselineMode === "percentage" ? baselineStr + "%" : baselineStr;
|
|
5058
|
+
}
|
|
4751
5059
|
}
|
|
4752
|
-
function
|
|
4753
|
-
|
|
4754
|
-
|
|
4755
|
-
|
|
4756
|
-
|
|
4757
|
-
|
|
4758
|
-
|
|
5060
|
+
function getBaselineColor(baseline, baselineMode, keyValue, colorUp, colorDown) {
|
|
5061
|
+
if (baselineMode === "text" ||
|
|
5062
|
+
baseline?.type !== CellValueType.number ||
|
|
5063
|
+
keyValue?.type !== CellValueType.number) {
|
|
5064
|
+
return undefined;
|
|
5065
|
+
}
|
|
5066
|
+
const diff = keyValue.value - baseline.value;
|
|
5067
|
+
if (diff > 0) {
|
|
5068
|
+
return colorUp;
|
|
5069
|
+
}
|
|
5070
|
+
else if (diff < 0) {
|
|
5071
|
+
return colorDown;
|
|
5072
|
+
}
|
|
5073
|
+
return undefined;
|
|
5074
|
+
}
|
|
5075
|
+
function getBaselineArrowDirection(baseline, keyValue, baselineMode) {
|
|
5076
|
+
if (baselineMode === "text" ||
|
|
5077
|
+
baseline?.type !== CellValueType.number ||
|
|
5078
|
+
keyValue?.type !== CellValueType.number) {
|
|
5079
|
+
return "neutral";
|
|
5080
|
+
}
|
|
5081
|
+
const diff = keyValue.value - baseline.value;
|
|
5082
|
+
if (diff > 0) {
|
|
5083
|
+
return "up";
|
|
5084
|
+
}
|
|
5085
|
+
else if (diff < 0) {
|
|
5086
|
+
return "down";
|
|
5087
|
+
}
|
|
5088
|
+
return "neutral";
|
|
5089
|
+
}
|
|
5090
|
+
function getChartPositionAtCenterOfViewport(getters, chartSize) {
|
|
5091
|
+
const { x, y } = getters.getMainViewportCoordinates();
|
|
5092
|
+
const { scrollX, scrollY } = getters.getActiveSheetScrollInfo();
|
|
5093
|
+
const { width, height } = getters.getVisibleRect(getters.getActiveMainViewport());
|
|
5094
|
+
const position = {
|
|
5095
|
+
x: x + scrollX + Math.max(0, (width - chartSize.width) / 2),
|
|
5096
|
+
y: y + scrollY + Math.max(0, (height - chartSize.height) / 2),
|
|
5097
|
+
}; // Position at the center of the scrollable viewport
|
|
5098
|
+
return position;
|
|
5099
|
+
}
|
|
5100
|
+
|
|
5101
|
+
function checkKeyValue(definition) {
|
|
5102
|
+
return definition.keyValue && !rangeReference.test(definition.keyValue)
|
|
5103
|
+
? "InvalidScorecardKeyValue" /* CommandResult.InvalidScorecardKeyValue */
|
|
5104
|
+
: "Success" /* CommandResult.Success */;
|
|
5105
|
+
}
|
|
5106
|
+
function checkBaseline(definition) {
|
|
5107
|
+
return definition.baseline && !rangeReference.test(definition.baseline)
|
|
5108
|
+
? "InvalidScorecardBaseline" /* CommandResult.InvalidScorecardBaseline */
|
|
5109
|
+
: "Success" /* CommandResult.Success */;
|
|
5110
|
+
}
|
|
5111
|
+
const arrowDownPath = new window.Path2D("M8.6 4.8a.5.5 0 0 1 0 .75l-3.9 3.9a.5 .5 0 0 1 -.75 0l-3.8 -3.9a.5 .5 0 0 1 0 -.75l.4-.4a.5.5 0 0 1 .75 0l2.3 2.4v-5.7c0-.25.25-.5.5-.5h.6c.25 0 .5.25.5.5v5.8l2.3 -2.4a.5.5 0 0 1 .75 0z");
|
|
5112
|
+
const arrowUpPath = new window.Path2D("M8.7 5.5a.5.5 0 0 0 0-.75l-3.8-4a.5.5 0 0 0-.75 0l-3.8 4a.5.5 0 0 0 0 .75l.4.4a.5.5 0 0 0 .75 0l2.3-2.4v5.8c0 .25.25.5.5.5h.6c.25 0 .5-.25.5-.5v-5.8l2.2 2.4a.5.5 0 0 0 .75 0z");
|
|
5113
|
+
let ScorecardChart$1 = class ScorecardChart extends AbstractChart {
|
|
5114
|
+
keyValue;
|
|
5115
|
+
baseline;
|
|
5116
|
+
baselineMode;
|
|
5117
|
+
baselineDescr;
|
|
5118
|
+
background;
|
|
5119
|
+
baselineColorUp;
|
|
5120
|
+
baselineColorDown;
|
|
5121
|
+
fontColor;
|
|
5122
|
+
type = "scorecard";
|
|
5123
|
+
constructor(definition, sheetId, getters) {
|
|
5124
|
+
super(definition, sheetId, getters);
|
|
5125
|
+
this.keyValue = createRange(getters, sheetId, definition.keyValue);
|
|
5126
|
+
this.baseline = createRange(getters, sheetId, definition.baseline);
|
|
5127
|
+
this.baselineMode = definition.baselineMode;
|
|
5128
|
+
this.baselineDescr = definition.baselineDescr;
|
|
5129
|
+
this.background = definition.background;
|
|
5130
|
+
this.baselineColorUp = definition.baselineColorUp;
|
|
5131
|
+
this.baselineColorDown = definition.baselineColorDown;
|
|
5132
|
+
}
|
|
5133
|
+
static validateChartDefinition(validator, definition) {
|
|
5134
|
+
return validator.checkValidations(definition, checkKeyValue, checkBaseline);
|
|
5135
|
+
}
|
|
5136
|
+
static getDefinitionFromContextCreation(context) {
|
|
5137
|
+
return {
|
|
5138
|
+
background: context.background,
|
|
5139
|
+
type: "scorecard",
|
|
5140
|
+
keyValue: context.range ? context.range[0] : undefined,
|
|
5141
|
+
title: context.title || "",
|
|
5142
|
+
baselineMode: DEFAULT_SCORECARD_BASELINE_MODE,
|
|
5143
|
+
baselineColorUp: DEFAULT_SCORECARD_BASELINE_COLOR_UP,
|
|
5144
|
+
baselineColorDown: DEFAULT_SCORECARD_BASELINE_COLOR_DOWN,
|
|
5145
|
+
baseline: context.auxiliaryRange || "",
|
|
5146
|
+
};
|
|
5147
|
+
}
|
|
5148
|
+
static transformDefinition(definition, executed) {
|
|
5149
|
+
let baselineZone;
|
|
5150
|
+
let keyValueZone;
|
|
5151
|
+
if (definition.baseline) {
|
|
5152
|
+
baselineZone = transformZone(toUnboundedZone(definition.baseline), executed);
|
|
5153
|
+
}
|
|
5154
|
+
if (definition.keyValue) {
|
|
5155
|
+
keyValueZone = transformZone(toUnboundedZone(definition.keyValue), executed);
|
|
5156
|
+
}
|
|
5157
|
+
return {
|
|
5158
|
+
...definition,
|
|
5159
|
+
baseline: baselineZone ? zoneToXc(baselineZone) : undefined,
|
|
5160
|
+
keyValue: keyValueZone ? zoneToXc(keyValueZone) : undefined,
|
|
5161
|
+
};
|
|
5162
|
+
}
|
|
5163
|
+
copyForSheetId(sheetId) {
|
|
5164
|
+
const baseline = copyLabelRangeWithNewSheetId(this.sheetId, sheetId, this.baseline);
|
|
5165
|
+
const keyValue = copyLabelRangeWithNewSheetId(this.sheetId, sheetId, this.keyValue);
|
|
5166
|
+
const definition = this.getDefinitionWithSpecificRanges(baseline, keyValue, sheetId);
|
|
5167
|
+
return new ScorecardChart(definition, sheetId, this.getters);
|
|
5168
|
+
}
|
|
5169
|
+
copyInSheetId(sheetId) {
|
|
5170
|
+
const definition = this.getDefinitionWithSpecificRanges(this.baseline, this.keyValue, sheetId);
|
|
5171
|
+
return new ScorecardChart(definition, sheetId, this.getters);
|
|
5172
|
+
}
|
|
5173
|
+
getDefinition() {
|
|
5174
|
+
return this.getDefinitionWithSpecificRanges(this.baseline, this.keyValue);
|
|
5175
|
+
}
|
|
5176
|
+
getContextCreation() {
|
|
5177
|
+
return {
|
|
5178
|
+
background: this.background,
|
|
5179
|
+
title: this.title,
|
|
5180
|
+
range: this.keyValue ? [this.getters.getRangeString(this.keyValue, this.sheetId)] : undefined,
|
|
5181
|
+
auxiliaryRange: this.baseline
|
|
5182
|
+
? this.getters.getRangeString(this.baseline, this.sheetId)
|
|
5183
|
+
: undefined,
|
|
5184
|
+
};
|
|
5185
|
+
}
|
|
5186
|
+
getDefinitionWithSpecificRanges(baseline, keyValue, targetSheetId) {
|
|
5187
|
+
return {
|
|
5188
|
+
baselineColorDown: this.baselineColorDown,
|
|
5189
|
+
baselineColorUp: this.baselineColorUp,
|
|
5190
|
+
baselineMode: this.baselineMode,
|
|
5191
|
+
title: this.title,
|
|
5192
|
+
type: "scorecard",
|
|
5193
|
+
background: this.background,
|
|
5194
|
+
baseline: baseline
|
|
5195
|
+
? this.getters.getRangeString(baseline, targetSheetId || this.sheetId)
|
|
5196
|
+
: undefined,
|
|
5197
|
+
baselineDescr: this.baselineDescr,
|
|
5198
|
+
keyValue: keyValue
|
|
5199
|
+
? this.getters.getRangeString(keyValue, targetSheetId || this.sheetId)
|
|
5200
|
+
: undefined,
|
|
5201
|
+
};
|
|
5202
|
+
}
|
|
5203
|
+
getDefinitionForExcel() {
|
|
5204
|
+
// This kind of graph is not exportable in Excel
|
|
5205
|
+
return undefined;
|
|
5206
|
+
}
|
|
5207
|
+
updateRanges(applyChange) {
|
|
5208
|
+
const baseline = adaptChartRange(this.baseline, applyChange);
|
|
5209
|
+
const keyValue = adaptChartRange(this.keyValue, applyChange);
|
|
5210
|
+
if (this.baseline === baseline && this.keyValue === keyValue) {
|
|
5211
|
+
return this;
|
|
5212
|
+
}
|
|
5213
|
+
const definition = this.getDefinitionWithSpecificRanges(baseline, keyValue);
|
|
5214
|
+
return new ScorecardChart(definition, this.sheetId, this.getters);
|
|
5215
|
+
}
|
|
5216
|
+
};
|
|
5217
|
+
function drawScoreChart(structure, canvas) {
|
|
5218
|
+
const ctx = canvas.getContext("2d");
|
|
5219
|
+
canvas.width = structure.canvas.width;
|
|
5220
|
+
canvas.height = structure.canvas.height;
|
|
5221
|
+
ctx.fillStyle = structure.canvas.backgroundColor;
|
|
5222
|
+
ctx.fillRect(0, 0, structure.canvas.width, structure.canvas.height);
|
|
5223
|
+
if (structure.title) {
|
|
5224
|
+
ctx.font = structure.title.style.font;
|
|
5225
|
+
ctx.fillStyle = structure.title.style.color;
|
|
5226
|
+
ctx.fillText(structure.title.text, structure.title.position.x, structure.title.position.y);
|
|
5227
|
+
}
|
|
5228
|
+
if (structure.baseline) {
|
|
5229
|
+
ctx.font = structure.baseline.style.font;
|
|
5230
|
+
ctx.fillStyle = structure.baseline.style.color;
|
|
5231
|
+
drawDecoratedText(ctx, structure.baseline.text, structure.baseline.position, structure.baseline.style.underline, structure.baseline.style.strikethrough);
|
|
5232
|
+
}
|
|
5233
|
+
if (structure.baselineArrow && structure.baselineArrow.style.size > 0) {
|
|
5234
|
+
ctx.save();
|
|
5235
|
+
ctx.fillStyle = structure.baselineArrow.style.color;
|
|
5236
|
+
ctx.translate(structure.baselineArrow.position.x, structure.baselineArrow.position.y);
|
|
5237
|
+
// This ratio is computed according to the original svg size and the final size we want
|
|
5238
|
+
const ratio = structure.baselineArrow.style.size / 10;
|
|
5239
|
+
ctx.scale(ratio, ratio);
|
|
5240
|
+
switch (structure.baselineArrow.direction) {
|
|
5241
|
+
case "down": {
|
|
5242
|
+
ctx.fill(arrowDownPath);
|
|
5243
|
+
break;
|
|
5244
|
+
}
|
|
5245
|
+
case "up": {
|
|
5246
|
+
ctx.fill(arrowUpPath);
|
|
5247
|
+
break;
|
|
5248
|
+
}
|
|
5249
|
+
}
|
|
5250
|
+
ctx.restore();
|
|
5251
|
+
}
|
|
5252
|
+
if (structure.baselineDescr) {
|
|
5253
|
+
ctx.font = structure.baselineDescr.style.font;
|
|
5254
|
+
ctx.fillStyle = structure.baselineDescr.style.color;
|
|
5255
|
+
ctx.fillText(structure.baselineDescr.text, structure.baselineDescr.position.x, structure.baselineDescr.position.y);
|
|
5256
|
+
}
|
|
5257
|
+
if (structure.key) {
|
|
5258
|
+
ctx.font = structure.key.style.font;
|
|
5259
|
+
ctx.fillStyle = structure.key.style.color;
|
|
5260
|
+
drawDecoratedText(ctx, structure.key.text, structure.key.position, structure.key.style.underline, structure.key.style.strikethrough);
|
|
5261
|
+
}
|
|
5262
|
+
}
|
|
5263
|
+
function createScorecardChartRuntime(chart, getters) {
|
|
5264
|
+
let keyValue = "";
|
|
5265
|
+
let formattedKeyValue = "";
|
|
5266
|
+
let keyValueCell;
|
|
5267
|
+
if (chart.keyValue) {
|
|
5268
|
+
const keyValuePosition = {
|
|
5269
|
+
sheetId: chart.keyValue.sheetId,
|
|
5270
|
+
col: chart.keyValue.zone.left,
|
|
5271
|
+
row: chart.keyValue.zone.top,
|
|
5272
|
+
};
|
|
5273
|
+
keyValueCell = getters.getEvaluatedCell(keyValuePosition);
|
|
5274
|
+
keyValue = String(keyValueCell.value);
|
|
5275
|
+
formattedKeyValue = keyValueCell.formattedValue;
|
|
5276
|
+
}
|
|
5277
|
+
let baselineCell;
|
|
5278
|
+
const baseline = chart.baseline;
|
|
5279
|
+
if (baseline) {
|
|
5280
|
+
const baselinePosition = {
|
|
5281
|
+
sheetId: chart.baseline.sheetId,
|
|
5282
|
+
col: chart.baseline.zone.left,
|
|
5283
|
+
row: chart.baseline.zone.top,
|
|
5284
|
+
};
|
|
5285
|
+
baselineCell = getters.getEvaluatedCell(baselinePosition);
|
|
5286
|
+
}
|
|
5287
|
+
const background = getters.getBackgroundOfSingleCellChart(chart.background, chart.keyValue);
|
|
5288
|
+
const locale = getters.getLocale();
|
|
5289
|
+
return {
|
|
5290
|
+
title: _t(chart.title),
|
|
5291
|
+
keyValue: formattedKeyValue || keyValue,
|
|
5292
|
+
baselineDisplay: getBaselineText(baselineCell, keyValueCell, chart.baselineMode, locale),
|
|
5293
|
+
baselineArrow: getBaselineArrowDirection(baselineCell, keyValueCell, chart.baselineMode),
|
|
5294
|
+
baselineColor: getBaselineColor(baselineCell, chart.baselineMode, keyValueCell, chart.baselineColorUp, chart.baselineColorDown),
|
|
5295
|
+
baselineDescr: chart.baselineDescr ? _t(chart.baselineDescr) : "",
|
|
5296
|
+
fontColor: chartFontColor(background),
|
|
5297
|
+
background,
|
|
5298
|
+
baselineStyle: chart.baselineMode !== "percentage" && baseline
|
|
5299
|
+
? getters.getCellStyle({
|
|
5300
|
+
sheetId: baseline.sheetId,
|
|
5301
|
+
col: baseline.zone.left,
|
|
5302
|
+
row: baseline.zone.top,
|
|
5303
|
+
})
|
|
5304
|
+
: undefined,
|
|
5305
|
+
keyValueStyle: chart.keyValue
|
|
5306
|
+
? getters.getCellStyle({
|
|
5307
|
+
sheetId: chart.keyValue.sheetId,
|
|
5308
|
+
col: chart.keyValue.zone.left,
|
|
5309
|
+
row: chart.keyValue.zone.top,
|
|
5310
|
+
})
|
|
5311
|
+
: undefined,
|
|
4759
5312
|
};
|
|
4760
|
-
return margins;
|
|
4761
5313
|
}
|
|
4762
5314
|
|
|
4763
5315
|
/* Sizes of boxes containing the texts, in percentage of the Chart size */
|
|
@@ -4773,64 +5325,96 @@ const CHART_PADDING_RATIO = 0.02;
|
|
|
4773
5325
|
* Having a line heigh =1em (=font size) don't work, the font will overflow.
|
|
4774
5326
|
*/
|
|
4775
5327
|
const LINE_HEIGHT = 1.2;
|
|
4776
|
-
|
|
4777
|
-
|
|
4778
|
-
|
|
4779
|
-
|
|
4780
|
-
|
|
4781
|
-
|
|
4782
|
-
|
|
4783
|
-
|
|
4784
|
-
|
|
4785
|
-
|
|
4786
|
-
|
|
4787
|
-
|
|
4788
|
-
|
|
4789
|
-
|
|
4790
|
-
|
|
4791
|
-
|
|
4792
|
-
|
|
4793
|
-
|
|
4794
|
-
|
|
4795
|
-
|
|
4796
|
-
|
|
4797
|
-
|
|
4798
|
-
|
|
4799
|
-
|
|
4800
|
-
|
|
4801
|
-
|
|
4802
|
-
|
|
4803
|
-
|
|
4804
|
-
|
|
4805
|
-
|
|
4806
|
-
|
|
4807
|
-
|
|
4808
|
-
|
|
4809
|
-
|
|
4810
|
-
|
|
4811
|
-
|
|
4812
|
-
|
|
4813
|
-
|
|
4814
|
-
|
|
4815
|
-
|
|
4816
|
-
|
|
4817
|
-
|
|
4818
|
-
|
|
4819
|
-
|
|
4820
|
-
|
|
4821
|
-
|
|
4822
|
-
|
|
4823
|
-
|
|
4824
|
-
|
|
4825
|
-
|
|
4826
|
-
|
|
4827
|
-
|
|
4828
|
-
|
|
4829
|
-
|
|
4830
|
-
|
|
4831
|
-
|
|
4832
|
-
|
|
4833
|
-
|
|
5328
|
+
function formatBaselineDescr(baselineDescr, baseline) {
|
|
5329
|
+
const _baselineDescr = baselineDescr || "";
|
|
5330
|
+
return baseline && _baselineDescr ? " " + _baselineDescr : _baselineDescr;
|
|
5331
|
+
}
|
|
5332
|
+
function getDefaultContextFont(fontSize, bold = false, italic = false) {
|
|
5333
|
+
const italicStr = italic ? "italic" : "";
|
|
5334
|
+
const weight = bold ? "bold" : "";
|
|
5335
|
+
return `${italicStr} ${weight} ${fontSize}px ${DEFAULT_FONT}`;
|
|
5336
|
+
}
|
|
5337
|
+
function getScorecardConfiguration({ width, height }, runtime) {
|
|
5338
|
+
const designer = new ScorecardChartConfigBuilder({ width, height }, runtime);
|
|
5339
|
+
return designer.computeDesign();
|
|
5340
|
+
}
|
|
5341
|
+
class ScorecardChartConfigBuilder {
|
|
5342
|
+
runtime;
|
|
5343
|
+
context;
|
|
5344
|
+
width;
|
|
5345
|
+
height;
|
|
5346
|
+
constructor({ width, height }, runtime) {
|
|
5347
|
+
this.runtime = runtime;
|
|
5348
|
+
const canvas = document.createElement("canvas");
|
|
5349
|
+
this.width = canvas.width = width;
|
|
5350
|
+
this.height = canvas.height = height;
|
|
5351
|
+
this.context = canvas.getContext("2d");
|
|
5352
|
+
}
|
|
5353
|
+
computeDesign() {
|
|
5354
|
+
const structure = {
|
|
5355
|
+
canvas: {
|
|
5356
|
+
width: this.width,
|
|
5357
|
+
height: this.height,
|
|
5358
|
+
backgroundColor: this.backgroundColor,
|
|
5359
|
+
},
|
|
5360
|
+
};
|
|
5361
|
+
const style = this.getTextStyles();
|
|
5362
|
+
const { height: titleHeight } = this.getTextDimensions(this.title, style.title.font);
|
|
5363
|
+
if (this.title) {
|
|
5364
|
+
structure.title = {
|
|
5365
|
+
text: this.title,
|
|
5366
|
+
style: style.title,
|
|
5367
|
+
position: {
|
|
5368
|
+
x: this.chartPadding,
|
|
5369
|
+
y: this.chartPadding + titleHeight,
|
|
5370
|
+
},
|
|
5371
|
+
};
|
|
5372
|
+
}
|
|
5373
|
+
const baselineArrowSize = style.baselineArrow?.size ?? 0;
|
|
5374
|
+
const { height: baselineHeight, width: baselineWidth } = this.getTextDimensions(this.baseline, style.baselineValue.font);
|
|
5375
|
+
const { width: baselineDescrWidth } = this.getTextDimensions(this.baselineDescr, style.baselineDescr.font);
|
|
5376
|
+
structure.baseline = {
|
|
5377
|
+
text: this.baseline,
|
|
5378
|
+
style: style.baselineValue,
|
|
5379
|
+
position: {
|
|
5380
|
+
x: (this.width - baselineWidth - baselineDescrWidth + baselineArrowSize) / 2,
|
|
5381
|
+
y: this.keyValue
|
|
5382
|
+
? this.height - 2 * this.chartPadding
|
|
5383
|
+
: this.height - (this.height - titleHeight - baselineHeight) / 2 - this.chartPadding,
|
|
5384
|
+
},
|
|
5385
|
+
};
|
|
5386
|
+
if (style.baselineArrow) {
|
|
5387
|
+
structure.baselineArrow = {
|
|
5388
|
+
direction: this.baselineArrow,
|
|
5389
|
+
style: style.baselineArrow,
|
|
5390
|
+
position: {
|
|
5391
|
+
x: structure.baseline.position.x - baselineArrowSize,
|
|
5392
|
+
y: structure.baseline.position.y - (baselineHeight + baselineArrowSize) / 2,
|
|
5393
|
+
},
|
|
5394
|
+
};
|
|
5395
|
+
}
|
|
5396
|
+
if (this.baselineDescr) {
|
|
5397
|
+
structure.baselineDescr = {
|
|
5398
|
+
text: this.baselineDescr,
|
|
5399
|
+
style: style.baselineDescr,
|
|
5400
|
+
position: {
|
|
5401
|
+
x: structure.baseline.position.x + baselineWidth,
|
|
5402
|
+
y: structure.baseline.position.y,
|
|
5403
|
+
},
|
|
5404
|
+
};
|
|
5405
|
+
}
|
|
5406
|
+
const { height: keyHeight, width: keyWidth } = this.getTextDimensions(this.keyValue, style.keyValue.font);
|
|
5407
|
+
if (this.keyValue) {
|
|
5408
|
+
structure.key = {
|
|
5409
|
+
text: this.keyValue,
|
|
5410
|
+
style: style.keyValue,
|
|
5411
|
+
position: {
|
|
5412
|
+
x: (this.width - keyWidth) / 2,
|
|
5413
|
+
y: (this.height - baselineHeight + titleHeight + keyHeight) / 2 - this.chartPadding,
|
|
5414
|
+
},
|
|
5415
|
+
};
|
|
5416
|
+
}
|
|
5417
|
+
return structure;
|
|
4834
5418
|
}
|
|
4835
5419
|
get title() {
|
|
4836
5420
|
return this.runtime.title;
|
|
@@ -4842,87 +5426,73 @@ let ScorecardChart$1 = class ScorecardChart extends owl.Component {
|
|
|
4842
5426
|
return this.runtime.baselineDisplay;
|
|
4843
5427
|
}
|
|
4844
5428
|
get baselineDescr() {
|
|
4845
|
-
|
|
4846
|
-
return this.baseline && baselineDescr ? " " + baselineDescr : baselineDescr;
|
|
5429
|
+
return formatBaselineDescr(this.runtime.baselineDescr, this.baseline);
|
|
4847
5430
|
}
|
|
4848
|
-
get
|
|
5431
|
+
get baselineArrow() {
|
|
4849
5432
|
return this.runtime.baselineArrow;
|
|
4850
5433
|
}
|
|
4851
5434
|
get backgroundColor() {
|
|
4852
5435
|
return this.runtime.background;
|
|
4853
5436
|
}
|
|
4854
|
-
get primaryFontColor() {
|
|
4855
|
-
return this.runtime.fontColor;
|
|
4856
|
-
}
|
|
4857
5437
|
get secondaryFontColor() {
|
|
4858
5438
|
return relativeLuminance(this.backgroundColor) > 0.3 ? "#525252" : "#C8C8C8";
|
|
4859
5439
|
}
|
|
4860
|
-
get figure() {
|
|
4861
|
-
return this.props.figure;
|
|
4862
|
-
}
|
|
4863
|
-
get chartStyle() {
|
|
4864
|
-
return cssPropertiesToCss({
|
|
4865
|
-
padding: `${this.chartPadding}px`,
|
|
4866
|
-
background: this.backgroundColor,
|
|
4867
|
-
});
|
|
4868
|
-
}
|
|
4869
|
-
get chartContentStyle() {
|
|
4870
|
-
return cssPropertiesToCss({ height: `${this.getDrawableHeight()}px` });
|
|
4871
|
-
}
|
|
4872
5440
|
get chartPadding() {
|
|
4873
|
-
return this.
|
|
5441
|
+
return this.width * CHART_PADDING_RATIO;
|
|
4874
5442
|
}
|
|
4875
|
-
|
|
4876
|
-
|
|
5443
|
+
getTextDimensions(text, font) {
|
|
5444
|
+
this.context.font = font;
|
|
5445
|
+
const measure = this.context.measureText(text);
|
|
5446
|
+
return {
|
|
5447
|
+
width: measure.width,
|
|
5448
|
+
height: measure.actualBoundingBoxAscent + measure.actualBoundingBoxDescent,
|
|
5449
|
+
};
|
|
4877
5450
|
}
|
|
4878
5451
|
getTextStyles() {
|
|
4879
5452
|
// If the widest text overflows horizontally, scale it down, and apply the same scaling factors to all the other fonts.
|
|
4880
|
-
const maxLineWidth = this.
|
|
5453
|
+
const maxLineWidth = this.width * (1 - 2 * CHART_PADDING_RATIO);
|
|
4881
5454
|
const widestElement = this.getWidestElement();
|
|
4882
5455
|
const baseFontSize = widestElement.getElementMaxFontSize(this.getDrawableHeight(), this);
|
|
4883
|
-
const fontSizeMatchingWidth = getFontSizeMatchingWidth(maxLineWidth, baseFontSize, (fontSize) => widestElement.getElementWidth(fontSize, this.
|
|
5456
|
+
const fontSizeMatchingWidth = getFontSizeMatchingWidth(maxLineWidth, baseFontSize, (fontSize) => widestElement.getElementWidth(fontSize, this.context, this));
|
|
4884
5457
|
let scalingFactor = fontSizeMatchingWidth / baseFontSize;
|
|
4885
5458
|
// Fonts sizes in px
|
|
4886
5459
|
const keyFontSize = new KeyValueElement(this.runtime.keyValueStyle).getElementMaxFontSize(this.getDrawableHeight(), this) * scalingFactor;
|
|
4887
5460
|
const baselineFontSize = new BaselineElement(this.runtime.baselineStyle).getElementMaxFontSize(this.getDrawableHeight(), this) * scalingFactor;
|
|
4888
5461
|
return {
|
|
4889
|
-
|
|
4890
|
-
|
|
5462
|
+
title: {
|
|
5463
|
+
font: getDefaultContextFont(TITLE_FONT_SIZE),
|
|
4891
5464
|
color: this.secondaryFontColor,
|
|
4892
|
-
}
|
|
4893
|
-
|
|
4894
|
-
|
|
4895
|
-
|
|
4896
|
-
|
|
4897
|
-
|
|
4898
|
-
|
|
4899
|
-
|
|
4900
|
-
|
|
4901
|
-
|
|
4902
|
-
|
|
4903
|
-
|
|
4904
|
-
|
|
4905
|
-
|
|
4906
|
-
|
|
4907
|
-
|
|
5465
|
+
},
|
|
5466
|
+
keyValue: {
|
|
5467
|
+
color: this.runtime.keyValueStyle?.textColor || this.runtime.fontColor,
|
|
5468
|
+
font: getDefaultContextFont(keyFontSize, this.runtime.keyValueStyle?.bold, this.runtime.keyValueStyle?.italic),
|
|
5469
|
+
strikethrough: this.runtime.keyValueStyle?.strikethrough,
|
|
5470
|
+
underline: this.runtime.keyValueStyle?.underline,
|
|
5471
|
+
},
|
|
5472
|
+
baselineValue: {
|
|
5473
|
+
font: getDefaultContextFont(baselineFontSize, this.runtime.baselineStyle?.bold, this.runtime.baselineStyle?.italic),
|
|
5474
|
+
strikethrough: this.runtime.baselineStyle?.strikethrough,
|
|
5475
|
+
underline: this.runtime.baselineStyle?.underline,
|
|
5476
|
+
color: this.runtime.baselineStyle?.textColor ||
|
|
5477
|
+
this.runtime.baselineColor ||
|
|
5478
|
+
this.secondaryFontColor,
|
|
5479
|
+
},
|
|
5480
|
+
baselineDescr: {
|
|
5481
|
+
font: getDefaultContextFont(baselineFontSize * BASELINE_DESCR_FONT_RATIO),
|
|
4908
5482
|
color: this.secondaryFontColor,
|
|
4909
|
-
}
|
|
5483
|
+
},
|
|
5484
|
+
baselineArrow: this.baselineArrow === "neutral"
|
|
5485
|
+
? undefined
|
|
5486
|
+
: {
|
|
5487
|
+
size: this.keyValue ? 0.8 * baselineFontSize : 0,
|
|
5488
|
+
color: this.runtime.baselineColor || this.secondaryFontColor,
|
|
5489
|
+
},
|
|
4910
5490
|
};
|
|
4911
5491
|
}
|
|
4912
|
-
/** Return an CSS style string corresponding to the given arguments */
|
|
4913
|
-
getTextStyle(args) {
|
|
4914
|
-
const cssAttributes = cellTextStyleToCss(args.cellStyle);
|
|
4915
|
-
cssAttributes["font-size"] = `${args.fontSize}px`;
|
|
4916
|
-
cssAttributes["display"] = "inline-block";
|
|
4917
|
-
if (!cssAttributes["color"] && args.color) {
|
|
4918
|
-
cssAttributes["color"] = args.color;
|
|
4919
|
-
}
|
|
4920
|
-
return cssPropertiesToCss(cssAttributes);
|
|
4921
|
-
}
|
|
4922
5492
|
/** Get the height of the chart minus all the vertical paddings */
|
|
4923
5493
|
getDrawableHeight() {
|
|
4924
5494
|
const verticalPadding = 2 * this.chartPadding;
|
|
4925
|
-
let availableHeight = this.
|
|
5495
|
+
let availableHeight = this.height - verticalPadding;
|
|
4926
5496
|
availableHeight -= this.title ? TITLE_FONT_SIZE * LINE_HEIGHT : 0;
|
|
4927
5497
|
return availableHeight;
|
|
4928
5498
|
}
|
|
@@ -4930,39 +5500,39 @@ let ScorecardChart$1 = class ScorecardChart extends owl.Component {
|
|
|
4930
5500
|
getWidestElement() {
|
|
4931
5501
|
const baseline = new BaselineElement(this.runtime.baselineStyle);
|
|
4932
5502
|
const keyValue = new KeyValueElement(this.runtime.keyValueStyle);
|
|
4933
|
-
return baseline.getElementWidth(BASELINE_BOX_HEIGHT_RATIO, this.
|
|
4934
|
-
keyValue.getElementWidth(KEY_BOX_HEIGHT_RATIO, this.
|
|
5503
|
+
return baseline.getElementWidth(BASELINE_BOX_HEIGHT_RATIO, this.context, this) >
|
|
5504
|
+
keyValue.getElementWidth(KEY_BOX_HEIGHT_RATIO, this.context, this)
|
|
4935
5505
|
? baseline
|
|
4936
5506
|
: keyValue;
|
|
4937
5507
|
}
|
|
4938
|
-
}
|
|
5508
|
+
}
|
|
4939
5509
|
class ScorecardScalableElement {
|
|
4940
5510
|
style;
|
|
4941
5511
|
constructor(style = {}) {
|
|
4942
5512
|
this.style = style;
|
|
4943
5513
|
}
|
|
4944
5514
|
measureTextWidth(ctx, text, fontSize) {
|
|
4945
|
-
|
|
4946
|
-
const weight = this.style.bold ? "bold" : "";
|
|
4947
|
-
ctx.font = `${italic} ${weight} ${fontSize}px ${DEFAULT_FONT}`;
|
|
5515
|
+
ctx.font = getDefaultContextFont(fontSize, this.style.bold, this.style.italic);
|
|
4948
5516
|
return ctx.measureText(text).width;
|
|
4949
5517
|
}
|
|
4950
5518
|
}
|
|
4951
5519
|
class BaselineElement extends ScorecardScalableElement {
|
|
4952
5520
|
getElementWidth(fontSize, ctx, chart) {
|
|
4953
|
-
if (!chart.runtime)
|
|
5521
|
+
if (!chart.runtime) {
|
|
4954
5522
|
return 0;
|
|
5523
|
+
}
|
|
4955
5524
|
const baselineStr = chart.baseline;
|
|
4956
5525
|
// Put mock text to simulate the width of the up/down arrow
|
|
4957
|
-
const largeText = chart.
|
|
5526
|
+
const largeText = chart.baselineArrow !== "neutral" ? "A " + baselineStr : baselineStr;
|
|
4958
5527
|
let textWidth = this.measureTextWidth(ctx, largeText, fontSize);
|
|
4959
5528
|
// Baseline descr font size should be smaller than baseline font size
|
|
4960
5529
|
textWidth += this.measureTextWidth(ctx, chart.baselineDescr, fontSize * BASELINE_DESCR_FONT_RATIO);
|
|
4961
5530
|
return textWidth;
|
|
4962
5531
|
}
|
|
4963
5532
|
getElementMaxFontSize(availableHeight, chart) {
|
|
4964
|
-
if (!chart.runtime)
|
|
5533
|
+
if (!chart.runtime) {
|
|
4965
5534
|
return 0;
|
|
5535
|
+
}
|
|
4966
5536
|
const haveBaseline = chart.baseline !== "" || chart.baselineDescr;
|
|
4967
5537
|
const maxHeight = haveBaseline ? BASELINE_BOX_HEIGHT_RATIO * availableHeight : 0;
|
|
4968
5538
|
return maxHeight / LINE_HEIGHT;
|
|
@@ -4970,20 +5540,42 @@ class BaselineElement extends ScorecardScalableElement {
|
|
|
4970
5540
|
}
|
|
4971
5541
|
class KeyValueElement extends ScorecardScalableElement {
|
|
4972
5542
|
getElementWidth(fontSize, ctx, chart) {
|
|
4973
|
-
if (!chart.runtime)
|
|
5543
|
+
if (!chart.runtime) {
|
|
4974
5544
|
return 0;
|
|
5545
|
+
}
|
|
4975
5546
|
const str = chart.keyValue || "";
|
|
4976
5547
|
return this.measureTextWidth(ctx, str, fontSize);
|
|
4977
5548
|
}
|
|
4978
5549
|
getElementMaxFontSize(availableHeight, chart) {
|
|
4979
|
-
if (!chart.runtime)
|
|
5550
|
+
if (!chart.runtime) {
|
|
4980
5551
|
return 0;
|
|
5552
|
+
}
|
|
4981
5553
|
const haveBaseline = chart.baseline !== "" || chart.baselineDescr;
|
|
4982
5554
|
const maxHeight = haveBaseline ? KEY_BOX_HEIGHT_RATIO * availableHeight : availableHeight;
|
|
4983
5555
|
return maxHeight / LINE_HEIGHT;
|
|
4984
5556
|
}
|
|
4985
5557
|
}
|
|
4986
|
-
|
|
5558
|
+
|
|
5559
|
+
class ScorecardChart extends owl.Component {
|
|
5560
|
+
static template = "o-spreadsheet-ScorecardChart";
|
|
5561
|
+
canvas = owl.useRef("chartContainer");
|
|
5562
|
+
get runtime() {
|
|
5563
|
+
return this.env.model.getters.getChartRuntime(this.props.figure.id);
|
|
5564
|
+
}
|
|
5565
|
+
setup() {
|
|
5566
|
+
owl.useEffect(this.createChart.bind(this), () => {
|
|
5567
|
+
const canvas = this.canvas.el;
|
|
5568
|
+
const rect = canvas.getBoundingClientRect();
|
|
5569
|
+
return [rect.width, rect.height, this.runtime, this.canvas.el];
|
|
5570
|
+
});
|
|
5571
|
+
}
|
|
5572
|
+
createChart() {
|
|
5573
|
+
const canvas = this.canvas.el;
|
|
5574
|
+
const config = getScorecardConfiguration(canvas.getBoundingClientRect(), this.runtime);
|
|
5575
|
+
drawScoreChart(config, canvas);
|
|
5576
|
+
}
|
|
5577
|
+
}
|
|
5578
|
+
ScorecardChart.props = {
|
|
4987
5579
|
figure: Object,
|
|
4988
5580
|
};
|
|
4989
5581
|
|
|
@@ -5170,7 +5762,7 @@ function _createEvaluatedCell(value, localeFormat) {
|
|
|
5170
5762
|
return textCell((value || "").toString(), localeFormat);
|
|
5171
5763
|
}
|
|
5172
5764
|
catch (error) {
|
|
5173
|
-
return errorCell(
|
|
5765
|
+
return errorCell(new EvaluationError(CellErrorType.GenericError, error.message || DEFAULT_ERROR_MESSAGE));
|
|
5174
5766
|
}
|
|
5175
5767
|
}
|
|
5176
5768
|
function textCell(value, localeFormat) {
|
|
@@ -5237,7 +5829,7 @@ function booleanCell(value, localeFormat) {
|
|
|
5237
5829
|
formattedValue,
|
|
5238
5830
|
};
|
|
5239
5831
|
}
|
|
5240
|
-
function errorCell(
|
|
5832
|
+
function errorCell(error) {
|
|
5241
5833
|
return {
|
|
5242
5834
|
type: CellValueType.error,
|
|
5243
5835
|
value: error.errorType,
|
|
@@ -5496,6 +6088,148 @@ autofillRulesRegistry
|
|
|
5496
6088
|
sequence: 40,
|
|
5497
6089
|
});
|
|
5498
6090
|
|
|
6091
|
+
/**
|
|
6092
|
+
* This file is largely inspired by owl 1.
|
|
6093
|
+
* `css` tag has been removed from owl 2 without workaround to manage css.
|
|
6094
|
+
* So, the solution was to import the behavior of owl 1 directly in our
|
|
6095
|
+
* codebase, with one difference: the css is added to the sheet as soon as the
|
|
6096
|
+
* css tag is executed. In owl 1, the css was added as soon as a Component was
|
|
6097
|
+
* created for the first time.
|
|
6098
|
+
*/
|
|
6099
|
+
const STYLESHEETS = {};
|
|
6100
|
+
let nextId = 0;
|
|
6101
|
+
/**
|
|
6102
|
+
* CSS tag helper for defining inline stylesheets. With this, one can simply define
|
|
6103
|
+
* an inline stylesheet with just the following code:
|
|
6104
|
+
* ```js
|
|
6105
|
+
* css`.component-a { color: red; }`;
|
|
6106
|
+
* ```
|
|
6107
|
+
*/
|
|
6108
|
+
function css(strings, ...args) {
|
|
6109
|
+
const name = `__sheet__${nextId++}`;
|
|
6110
|
+
const value = String.raw(strings, ...args);
|
|
6111
|
+
registerSheet(name, value);
|
|
6112
|
+
activateSheet(name);
|
|
6113
|
+
return name;
|
|
6114
|
+
}
|
|
6115
|
+
function processSheet(str) {
|
|
6116
|
+
const tokens = str.split(/(\{|\}|;)/).map((s) => s.trim());
|
|
6117
|
+
const selectorStack = [];
|
|
6118
|
+
const parts = [];
|
|
6119
|
+
let rules = [];
|
|
6120
|
+
function generateSelector(stackIndex, parentSelector) {
|
|
6121
|
+
const parts = [];
|
|
6122
|
+
for (const selector of selectorStack[stackIndex]) {
|
|
6123
|
+
let part = (parentSelector && parentSelector + " " + selector) || selector;
|
|
6124
|
+
if (part.includes("&")) {
|
|
6125
|
+
part = selector.replace(/&/g, parentSelector || "");
|
|
6126
|
+
}
|
|
6127
|
+
if (stackIndex < selectorStack.length - 1) {
|
|
6128
|
+
part = generateSelector(stackIndex + 1, part);
|
|
6129
|
+
}
|
|
6130
|
+
parts.push(part);
|
|
6131
|
+
}
|
|
6132
|
+
return parts.join(", ");
|
|
6133
|
+
}
|
|
6134
|
+
function generateRules() {
|
|
6135
|
+
if (rules.length) {
|
|
6136
|
+
parts.push(generateSelector(0) + " {");
|
|
6137
|
+
parts.push(...rules);
|
|
6138
|
+
parts.push("}");
|
|
6139
|
+
rules = [];
|
|
6140
|
+
}
|
|
6141
|
+
}
|
|
6142
|
+
while (tokens.length) {
|
|
6143
|
+
let token = tokens.shift();
|
|
6144
|
+
if (token === "}") {
|
|
6145
|
+
generateRules();
|
|
6146
|
+
selectorStack.pop();
|
|
6147
|
+
}
|
|
6148
|
+
else {
|
|
6149
|
+
if (tokens[0] === "{") {
|
|
6150
|
+
generateRules();
|
|
6151
|
+
selectorStack.push(token.split(/\s*,\s*/));
|
|
6152
|
+
tokens.shift();
|
|
6153
|
+
}
|
|
6154
|
+
if (tokens[0] === ";") {
|
|
6155
|
+
rules.push(" " + token + ";");
|
|
6156
|
+
}
|
|
6157
|
+
}
|
|
6158
|
+
}
|
|
6159
|
+
return parts.join("\n");
|
|
6160
|
+
}
|
|
6161
|
+
function registerSheet(id, css) {
|
|
6162
|
+
const sheet = document.createElement("style");
|
|
6163
|
+
sheet.textContent = processSheet(css);
|
|
6164
|
+
STYLESHEETS[id] = sheet;
|
|
6165
|
+
}
|
|
6166
|
+
function activateSheet(id) {
|
|
6167
|
+
const sheet = STYLESHEETS[id];
|
|
6168
|
+
sheet.setAttribute("component", id);
|
|
6169
|
+
document.head.appendChild(sheet);
|
|
6170
|
+
}
|
|
6171
|
+
function getTextDecoration({ strikethrough, underline, }) {
|
|
6172
|
+
if (!strikethrough && !underline) {
|
|
6173
|
+
return "none";
|
|
6174
|
+
}
|
|
6175
|
+
return `${strikethrough ? "line-through" : ""} ${underline ? "underline" : ""}`;
|
|
6176
|
+
}
|
|
6177
|
+
/**
|
|
6178
|
+
* Convert the cell style to CSS properties.
|
|
6179
|
+
*/
|
|
6180
|
+
function cellStyleToCss(style) {
|
|
6181
|
+
const attributes = cellTextStyleToCss(style);
|
|
6182
|
+
if (!style)
|
|
6183
|
+
return attributes;
|
|
6184
|
+
if (style.fillColor) {
|
|
6185
|
+
attributes["background"] = style.fillColor;
|
|
6186
|
+
}
|
|
6187
|
+
return attributes;
|
|
6188
|
+
}
|
|
6189
|
+
/**
|
|
6190
|
+
* Convert the cell text style to CSS properties.
|
|
6191
|
+
*/
|
|
6192
|
+
function cellTextStyleToCss(style) {
|
|
6193
|
+
const attributes = {};
|
|
6194
|
+
if (!style)
|
|
6195
|
+
return attributes;
|
|
6196
|
+
if (style.bold) {
|
|
6197
|
+
attributes["font-weight"] = "bold";
|
|
6198
|
+
}
|
|
6199
|
+
if (style.italic) {
|
|
6200
|
+
attributes["font-style"] = "italic";
|
|
6201
|
+
}
|
|
6202
|
+
if (style.strikethrough || style.underline) {
|
|
6203
|
+
let decoration = style.strikethrough ? "line-through" : "";
|
|
6204
|
+
decoration = style.underline ? decoration + " underline" : decoration;
|
|
6205
|
+
attributes["text-decoration"] = decoration;
|
|
6206
|
+
}
|
|
6207
|
+
if (style.textColor) {
|
|
6208
|
+
attributes["color"] = style.textColor;
|
|
6209
|
+
}
|
|
6210
|
+
return attributes;
|
|
6211
|
+
}
|
|
6212
|
+
/**
|
|
6213
|
+
* Transform CSS properties into a CSS string.
|
|
6214
|
+
*/
|
|
6215
|
+
function cssPropertiesToCss(attributes) {
|
|
6216
|
+
const str = Object.entries(attributes)
|
|
6217
|
+
.filter(([attName, attValue]) => attValue !== undefined)
|
|
6218
|
+
.map(([attName, attValue]) => `${attName}:${attValue};`)
|
|
6219
|
+
.join(" ");
|
|
6220
|
+
return str;
|
|
6221
|
+
}
|
|
6222
|
+
function getElementMargins(el) {
|
|
6223
|
+
const style = window.getComputedStyle(el);
|
|
6224
|
+
const margins = {
|
|
6225
|
+
top: parseInt(style.marginTop, 10) || 0,
|
|
6226
|
+
bottom: parseInt(style.marginBottom, 10) || 0,
|
|
6227
|
+
left: parseInt(style.marginLeft, 10) || 0,
|
|
6228
|
+
right: parseInt(style.marginRight, 10) || 0,
|
|
6229
|
+
};
|
|
6230
|
+
return margins;
|
|
6231
|
+
}
|
|
6232
|
+
|
|
5499
6233
|
const ERROR_TOOLTIP_MAX_HEIGHT = 80;
|
|
5500
6234
|
const ERROR_TOOLTIP_WIDTH = 180;
|
|
5501
6235
|
css /* scss */ `
|
|
@@ -6229,6 +6963,10 @@ css /* scss */ `
|
|
|
6229
6963
|
cursor: pointer;
|
|
6230
6964
|
user-select: none;
|
|
6231
6965
|
|
|
6966
|
+
.o-menu-item-name {
|
|
6967
|
+
min-width: 40%;
|
|
6968
|
+
}
|
|
6969
|
+
|
|
6232
6970
|
&.o-menu-root {
|
|
6233
6971
|
display: flex;
|
|
6234
6972
|
justify-content: space-between;
|
|
@@ -6847,6 +7585,18 @@ function multiplyMatrices(matrix1, matrix2) {
|
|
|
6847
7585
|
}
|
|
6848
7586
|
return result;
|
|
6849
7587
|
}
|
|
7588
|
+
/**
|
|
7589
|
+
* Return the input if it's a scalar or the first element of the input if it's a matrix.
|
|
7590
|
+
*/
|
|
7591
|
+
function toScalar(matrix) {
|
|
7592
|
+
if (!isMatrix(matrix)) {
|
|
7593
|
+
return matrix;
|
|
7594
|
+
}
|
|
7595
|
+
if (matrix.length !== 1 || matrix[0].length !== 1) {
|
|
7596
|
+
throw new Error("toScalar: matrix should be a scalar or a 1x1 matrix");
|
|
7597
|
+
}
|
|
7598
|
+
return matrix[0][0];
|
|
7599
|
+
}
|
|
6850
7600
|
|
|
6851
7601
|
// -----------------------------------------------------------------------------
|
|
6852
7602
|
// ARRAY_CONSTRAIN
|
|
@@ -7377,7 +8127,7 @@ var array = /*#__PURE__*/Object.freeze({
|
|
|
7377
8127
|
// FORMAT.LARGE.NUMBER
|
|
7378
8128
|
// -----------------------------------------------------------------------------
|
|
7379
8129
|
const FORMAT_LARGE_NUMBER = {
|
|
7380
|
-
description: _t(
|
|
8130
|
+
description: _t("Apply a large number format"),
|
|
7381
8131
|
args: [
|
|
7382
8132
|
arg("value (number)", _t("The number.")),
|
|
7383
8133
|
arg("unit (string, optional)", _t("The formatting unit. Use 'k', 'm', or 'b' to force the unit")),
|
|
@@ -7557,7 +8307,7 @@ const ATAN2 = {
|
|
|
7557
8307
|
compute: function (x, y) {
|
|
7558
8308
|
const _x = toNumber(x, this.locale);
|
|
7559
8309
|
const _y = toNumber(y, this.locale);
|
|
7560
|
-
assert(() => _x !== 0 || _y !== 0, _t(
|
|
8310
|
+
assert(() => _x !== 0 || _y !== 0, _t("Function [[FUNCTION_NAME]] caused a divide by zero error."));
|
|
7561
8311
|
return Math.atan2(_y, _x);
|
|
7562
8312
|
},
|
|
7563
8313
|
isExported: true,
|
|
@@ -7582,7 +8332,7 @@ const ATANH = {
|
|
|
7582
8332
|
// CEILING
|
|
7583
8333
|
// -----------------------------------------------------------------------------
|
|
7584
8334
|
const CEILING = {
|
|
7585
|
-
description: _t(
|
|
8335
|
+
description: _t("Rounds number up to nearest multiple of factor."),
|
|
7586
8336
|
args: [
|
|
7587
8337
|
arg("value (number)", _t("The value to round up to the nearest integer multiple of factor.")),
|
|
7588
8338
|
arg(`factor (number, default=${DEFAULT_FACTOR})`, _t("The number to whose multiples value will be rounded.")),
|
|
@@ -7601,7 +8351,7 @@ const CEILING = {
|
|
|
7601
8351
|
// CEILING.MATH
|
|
7602
8352
|
// -----------------------------------------------------------------------------
|
|
7603
8353
|
const CEILING_MATH = {
|
|
7604
|
-
description: _t(
|
|
8354
|
+
description: _t("Rounds number up to nearest multiple of factor."),
|
|
7605
8355
|
args: [
|
|
7606
8356
|
arg("number (number)", _t("The value to round up to the nearest integer multiple of significance.")),
|
|
7607
8357
|
arg(`significance (number, default=${DEFAULT_SIGNIFICANCE})`, _t("The number to whose multiples number will be rounded. The sign of significance will be ignored.")),
|
|
@@ -7631,7 +8381,7 @@ const CEILING_MATH = {
|
|
|
7631
8381
|
// CEILING.PRECISE
|
|
7632
8382
|
// -----------------------------------------------------------------------------
|
|
7633
8383
|
const CEILING_PRECISE = {
|
|
7634
|
-
description: _t(
|
|
8384
|
+
description: _t("Rounds number up to nearest multiple of factor."),
|
|
7635
8385
|
args: [
|
|
7636
8386
|
arg("number (number)", _t("The value to round up to the nearest integer multiple of significance.")),
|
|
7637
8387
|
arg(`significance (number, default=${DEFAULT_SIGNIFICANCE})`, _t("The number to whose multiples number will be rounded.")),
|
|
@@ -7676,7 +8426,7 @@ const COT = {
|
|
|
7676
8426
|
returns: ["NUMBER"],
|
|
7677
8427
|
compute: function (angle) {
|
|
7678
8428
|
const _angle = toNumber(angle, this.locale);
|
|
7679
|
-
assert(() => _angle !== 0, _t(
|
|
8429
|
+
assert(() => _angle !== 0, _t("Evaluation of function [[FUNCTION_NAME]] caused a divide by zero error."));
|
|
7680
8430
|
return 1 / Math.tan(_angle);
|
|
7681
8431
|
},
|
|
7682
8432
|
isExported: true,
|
|
@@ -7690,7 +8440,7 @@ const COTH = {
|
|
|
7690
8440
|
returns: ["NUMBER"],
|
|
7691
8441
|
compute: function (value) {
|
|
7692
8442
|
const _value = toNumber(value, this.locale);
|
|
7693
|
-
assert(() => _value !== 0, _t(
|
|
8443
|
+
assert(() => _value !== 0, _t("Evaluation of function [[FUNCTION_NAME]] caused a divide by zero error."));
|
|
7694
8444
|
return 1 / Math.tanh(_value);
|
|
7695
8445
|
},
|
|
7696
8446
|
isExported: true,
|
|
@@ -7809,7 +8559,7 @@ const CSC = {
|
|
|
7809
8559
|
returns: ["NUMBER"],
|
|
7810
8560
|
compute: function (angle) {
|
|
7811
8561
|
const _angle = toNumber(angle, this.locale);
|
|
7812
|
-
assert(() => _angle !== 0, _t(
|
|
8562
|
+
assert(() => _angle !== 0, _t("Evaluation of function [[FUNCTION_NAME]] caused a divide by zero error."));
|
|
7813
8563
|
return 1 / Math.sin(_angle);
|
|
7814
8564
|
},
|
|
7815
8565
|
isExported: true,
|
|
@@ -7823,7 +8573,7 @@ const CSCH = {
|
|
|
7823
8573
|
returns: ["NUMBER"],
|
|
7824
8574
|
compute: function (value) {
|
|
7825
8575
|
const _value = toNumber(value, this.locale);
|
|
7826
|
-
assert(() => _value !== 0, _t(
|
|
8576
|
+
assert(() => _value !== 0, _t("Evaluation of function [[FUNCTION_NAME]] caused a divide by zero error."));
|
|
7827
8577
|
return 1 / Math.sinh(_value);
|
|
7828
8578
|
},
|
|
7829
8579
|
isExported: true,
|
|
@@ -7862,7 +8612,7 @@ const DECIMAL = {
|
|
|
7862
8612
|
// DEGREES
|
|
7863
8613
|
// -----------------------------------------------------------------------------
|
|
7864
8614
|
const DEGREES = {
|
|
7865
|
-
description: _t(
|
|
8615
|
+
description: _t("Converts an angle value in radians to degrees."),
|
|
7866
8616
|
args: [arg("angle (number)", _t("The angle to convert from radians to degrees."))],
|
|
7867
8617
|
returns: ["NUMBER"],
|
|
7868
8618
|
compute: function (angle) {
|
|
@@ -7874,7 +8624,7 @@ const DEGREES = {
|
|
|
7874
8624
|
// EXP
|
|
7875
8625
|
// -----------------------------------------------------------------------------
|
|
7876
8626
|
const EXP = {
|
|
7877
|
-
description: _t(
|
|
8627
|
+
description: _t("Euler's number, e (~2.718) raised to a power."),
|
|
7878
8628
|
args: [arg("value (number)", _t("The exponent to raise e."))],
|
|
7879
8629
|
returns: ["NUMBER"],
|
|
7880
8630
|
compute: function (value) {
|
|
@@ -7886,7 +8636,7 @@ const EXP = {
|
|
|
7886
8636
|
// FLOOR
|
|
7887
8637
|
// -----------------------------------------------------------------------------
|
|
7888
8638
|
const FLOOR = {
|
|
7889
|
-
description: _t(
|
|
8639
|
+
description: _t("Rounds number down to nearest multiple of factor."),
|
|
7890
8640
|
args: [
|
|
7891
8641
|
arg("value (number)", _t("The value to round down to the nearest integer multiple of factor.")),
|
|
7892
8642
|
arg(`factor (number, default=${DEFAULT_FACTOR})`, _t("The number to whose multiples value will be rounded.")),
|
|
@@ -7905,7 +8655,7 @@ const FLOOR = {
|
|
|
7905
8655
|
// FLOOR.MATH
|
|
7906
8656
|
// -----------------------------------------------------------------------------
|
|
7907
8657
|
const FLOOR_MATH = {
|
|
7908
|
-
description: _t(
|
|
8658
|
+
description: _t("Rounds number down to nearest multiple of factor."),
|
|
7909
8659
|
args: [
|
|
7910
8660
|
arg("number (number)", _t("The value to round down to the nearest integer multiple of significance.")),
|
|
7911
8661
|
arg(`significance (number, default=${DEFAULT_SIGNIFICANCE})`, _t("The number to whose multiples number will be rounded. The sign of significance will be ignored.")),
|
|
@@ -7935,7 +8685,7 @@ const FLOOR_MATH = {
|
|
|
7935
8685
|
// FLOOR.PRECISE
|
|
7936
8686
|
// -----------------------------------------------------------------------------
|
|
7937
8687
|
const FLOOR_PRECISE = {
|
|
7938
|
-
description: _t(
|
|
8688
|
+
description: _t("Rounds number down to nearest multiple of factor."),
|
|
7939
8689
|
args: [
|
|
7940
8690
|
arg("number (number)", _t("The value to round down to the nearest integer multiple of significance.")),
|
|
7941
8691
|
arg(`significance (number, default=${DEFAULT_SIGNIFICANCE})`, _t("The number to whose multiples number will be rounded.")),
|
|
@@ -7951,7 +8701,7 @@ const FLOOR_PRECISE = {
|
|
|
7951
8701
|
// ISEVEN
|
|
7952
8702
|
// -----------------------------------------------------------------------------
|
|
7953
8703
|
const ISEVEN = {
|
|
7954
|
-
description: _t(
|
|
8704
|
+
description: _t("Whether the provided value is even."),
|
|
7955
8705
|
args: [arg("value (number)", _t("The value to be verified as even."))],
|
|
7956
8706
|
returns: ["BOOLEAN"],
|
|
7957
8707
|
compute: function (value) {
|
|
@@ -7964,7 +8714,7 @@ const ISEVEN = {
|
|
|
7964
8714
|
// ISO.CEILING
|
|
7965
8715
|
// -----------------------------------------------------------------------------
|
|
7966
8716
|
const ISO_CEILING = {
|
|
7967
|
-
description: _t(
|
|
8717
|
+
description: _t("Rounds number up to nearest multiple of factor."),
|
|
7968
8718
|
args: [
|
|
7969
8719
|
arg("number (number)", _t("The value to round up to the nearest integer multiple of significance.")),
|
|
7970
8720
|
arg(`significance (number, default=${DEFAULT_SIGNIFICANCE})`, _t("The number to whose multiples number will be rounded.")),
|
|
@@ -7980,7 +8730,7 @@ const ISO_CEILING = {
|
|
|
7980
8730
|
// ISODD
|
|
7981
8731
|
// -----------------------------------------------------------------------------
|
|
7982
8732
|
const ISODD = {
|
|
7983
|
-
description: _t(
|
|
8733
|
+
description: _t("Whether the provided value is even."),
|
|
7984
8734
|
args: [arg("value (number)", _t("The value to be verified as even."))],
|
|
7985
8735
|
returns: ["BOOLEAN"],
|
|
7986
8736
|
compute: function (value) {
|
|
@@ -7993,7 +8743,7 @@ const ISODD = {
|
|
|
7993
8743
|
// LN
|
|
7994
8744
|
// -----------------------------------------------------------------------------
|
|
7995
8745
|
const LN = {
|
|
7996
|
-
description: _t(
|
|
8746
|
+
description: _t("The logarithm of a number, base e (euler's number)."),
|
|
7997
8747
|
args: [arg("value (number)", _t("The value for which to calculate the logarithm, base e."))],
|
|
7998
8748
|
returns: ["NUMBER"],
|
|
7999
8749
|
compute: function (value) {
|
|
@@ -8007,7 +8757,7 @@ const LN = {
|
|
|
8007
8757
|
// MOD
|
|
8008
8758
|
// -----------------------------------------------------------------------------
|
|
8009
8759
|
const MOD = {
|
|
8010
|
-
description: _t(
|
|
8760
|
+
description: _t("Modulo (remainder) operator."),
|
|
8011
8761
|
args: [
|
|
8012
8762
|
arg("dividend (number)", _t("The number to be divided to find the remainder.")),
|
|
8013
8763
|
arg("divisor (number)", _t("The number to divide by.")),
|
|
@@ -8047,7 +8797,7 @@ const MUNIT = {
|
|
|
8047
8797
|
// ODD
|
|
8048
8798
|
// -----------------------------------------------------------------------------
|
|
8049
8799
|
const ODD = {
|
|
8050
|
-
description: _t(
|
|
8800
|
+
description: _t("Rounds a number up to the nearest odd integer."),
|
|
8051
8801
|
args: [arg("value (number)", _t("The value to round to the next greatest odd number."))],
|
|
8052
8802
|
returns: ["NUMBER"],
|
|
8053
8803
|
computeFormat: (number) => number?.format,
|
|
@@ -8063,7 +8813,7 @@ const ODD = {
|
|
|
8063
8813
|
// PI
|
|
8064
8814
|
// -----------------------------------------------------------------------------
|
|
8065
8815
|
const PI = {
|
|
8066
|
-
description: _t(
|
|
8816
|
+
description: _t("The number pi."),
|
|
8067
8817
|
args: [],
|
|
8068
8818
|
returns: ["NUMBER"],
|
|
8069
8819
|
compute: function () {
|
|
@@ -8075,7 +8825,7 @@ const PI = {
|
|
|
8075
8825
|
// POWER
|
|
8076
8826
|
// -----------------------------------------------------------------------------
|
|
8077
8827
|
const POWER = {
|
|
8078
|
-
description: _t(
|
|
8828
|
+
description: _t("A number raised to a power."),
|
|
8079
8829
|
args: [
|
|
8080
8830
|
arg("base (number)", _t("The number to raise to the exponent power.")),
|
|
8081
8831
|
arg("exponent (number)", _t("The exponent to raise base to.")),
|
|
@@ -8240,7 +8990,7 @@ const ROUND = {
|
|
|
8240
8990
|
// ROUNDDOWN
|
|
8241
8991
|
// -----------------------------------------------------------------------------
|
|
8242
8992
|
const ROUNDDOWN = {
|
|
8243
|
-
description: _t(
|
|
8993
|
+
description: _t("Rounds down a number."),
|
|
8244
8994
|
args: [
|
|
8245
8995
|
arg("value (number)", _t("The value to round to places number of places, always rounding down.")),
|
|
8246
8996
|
arg(`places (number, default=${DEFAULT_PLACES})`, _t("The number of decimal places to which to round.")),
|
|
@@ -8269,7 +9019,7 @@ const ROUNDDOWN = {
|
|
|
8269
9019
|
// ROUNDUP
|
|
8270
9020
|
// -----------------------------------------------------------------------------
|
|
8271
9021
|
const ROUNDUP = {
|
|
8272
|
-
description: _t(
|
|
9022
|
+
description: _t("Rounds up a number."),
|
|
8273
9023
|
args: [
|
|
8274
9024
|
arg("value (number)", _t("The value to round to places number of places, always rounding up.")),
|
|
8275
9025
|
arg(`places (number, default=${DEFAULT_PLACES})`, _t("The number of decimal places to which to round.")),
|
|
@@ -8571,7 +9321,7 @@ function filterAndFlatData(dataY, dataX) {
|
|
|
8571
9321
|
function covariance(dataY, dataX, isSample) {
|
|
8572
9322
|
const { flatDataX, flatDataY } = filterAndFlatData(dataY, dataX);
|
|
8573
9323
|
const count = flatDataY.length;
|
|
8574
|
-
assert(() => count !== 0 && (!isSample || count !== 1), _t(
|
|
9324
|
+
assert(() => count !== 0 && (!isSample || count !== 1), _t("Evaluation of function [[FUNCTION_NAME]] caused a divide by zero error."));
|
|
8575
9325
|
let sumY = 0;
|
|
8576
9326
|
let sumX = 0;
|
|
8577
9327
|
for (let i = 0; i < count; i++) {
|
|
@@ -8594,14 +9344,14 @@ function variance(args, isSample, textAs0, locale) {
|
|
|
8594
9344
|
count += 1;
|
|
8595
9345
|
return acc + a;
|
|
8596
9346
|
}, 0, locale);
|
|
8597
|
-
assert(() => count !== 0 && (!isSample || count !== 1), _t(
|
|
9347
|
+
assert(() => count !== 0 && (!isSample || count !== 1), _t("Evaluation of function [[FUNCTION_NAME]] caused a divide by zero error."));
|
|
8598
9348
|
const average = sum / count;
|
|
8599
9349
|
return (reduceFunction(args, (acc, a) => acc + Math.pow(a - average, 2), 0, locale) /
|
|
8600
9350
|
(count - (isSample ? 1 : 0)));
|
|
8601
9351
|
}
|
|
8602
9352
|
function centile(data, percent, isInclusive, locale) {
|
|
8603
9353
|
const _percent = toNumber(percent, locale);
|
|
8604
|
-
assert(() => (isInclusive ? 0 <= _percent && _percent <= 1 : 0 < _percent && _percent < 1), _t(
|
|
9354
|
+
assert(() => (isInclusive ? 0 <= _percent && _percent <= 1 : 0 < _percent && _percent < 1), _t("Function [[FUNCTION_NAME]] parameter 2 value is out of range."));
|
|
8605
9355
|
let sortedArray = [];
|
|
8606
9356
|
let index;
|
|
8607
9357
|
let count = 0;
|
|
@@ -8612,10 +9362,10 @@ function centile(data, percent, isInclusive, locale) {
|
|
|
8612
9362
|
count++;
|
|
8613
9363
|
}
|
|
8614
9364
|
});
|
|
8615
|
-
assert(() => count !== 0, _t(
|
|
9365
|
+
assert(() => count !== 0, _t("[[FUNCTION_NAME]] has no valid input data."));
|
|
8616
9366
|
if (!isInclusive) {
|
|
8617
9367
|
// 2nd argument must be between 1/(n+1) and n/(n+1) with n the number of data
|
|
8618
|
-
assert(() => 1 / (count + 1) <= _percent && _percent <= count / (count + 1), _t(
|
|
9368
|
+
assert(() => 1 / (count + 1) <= _percent && _percent <= count / (count + 1), _t("Function [[FUNCTION_NAME]] parameter 2 value is out of range."));
|
|
8619
9369
|
}
|
|
8620
9370
|
return percentile(sortedArray, _percent, isInclusive);
|
|
8621
9371
|
}
|
|
@@ -8729,7 +9479,7 @@ function fullLinearRegression(X, Y, computeIntercept = true, verbose = false) {
|
|
|
8729
9479
|
*/
|
|
8730
9480
|
function polynomialRegression(flatY, flatX, order, intercept) {
|
|
8731
9481
|
assertSameNumberOfElements(flatX, flatY);
|
|
8732
|
-
assert(() => order >= 1, _t(
|
|
9482
|
+
assert(() => order >= 1, _t("Function [[FUNCTION_NAME]] A regression of order less than 1 cannot be possible."));
|
|
8733
9483
|
const yMatrix = [flatY];
|
|
8734
9484
|
const xMatrix = flatX.map((x) => range(0, order).map((i) => Math.pow(x, order - i)));
|
|
8735
9485
|
if (intercept) {
|
|
@@ -8790,7 +9540,7 @@ const AVEDEV = {
|
|
|
8790
9540
|
count += 1;
|
|
8791
9541
|
return acc + a;
|
|
8792
9542
|
}, 0, this.locale);
|
|
8793
|
-
assert(() => count !== 0, _t(
|
|
9543
|
+
assert(() => count !== 0, _t("Evaluation of function [[FUNCTION_NAME]] caused a divide by zero error."));
|
|
8794
9544
|
const average = sum / count;
|
|
8795
9545
|
return reduceNumbers(values, (acc, a) => acc + Math.abs(average - a), 0, this.locale) / count;
|
|
8796
9546
|
},
|
|
@@ -8800,7 +9550,7 @@ const AVEDEV = {
|
|
|
8800
9550
|
// AVERAGE
|
|
8801
9551
|
// -----------------------------------------------------------------------------
|
|
8802
9552
|
const AVERAGE = {
|
|
8803
|
-
description: _t(
|
|
9553
|
+
description: _t("Numerical average value in a dataset, ignoring text."),
|
|
8804
9554
|
args: [
|
|
8805
9555
|
arg("value1 (number, range<number>)", _t("The first value or range to consider when calculating the average value.")),
|
|
8806
9556
|
arg("value2 (number, range<number>, repeating)", _t("Additional values or ranges to consider when calculating the average value.")),
|
|
@@ -8815,7 +9565,7 @@ const AVERAGE = {
|
|
|
8815
9565
|
count += 1;
|
|
8816
9566
|
return acc + a;
|
|
8817
9567
|
}, 0, this.locale);
|
|
8818
|
-
assert(() => count !== 0, _t(
|
|
9568
|
+
assert(() => count !== 0, _t("Evaluation of function [[FUNCTION_NAME]] caused a divide by zero error."));
|
|
8819
9569
|
return sum / count;
|
|
8820
9570
|
},
|
|
8821
9571
|
isExported: true,
|
|
@@ -8823,10 +9573,10 @@ const AVERAGE = {
|
|
|
8823
9573
|
// -----------------------------------------------------------------------------
|
|
8824
9574
|
// AVERAGE.WEIGHTED
|
|
8825
9575
|
// -----------------------------------------------------------------------------
|
|
8826
|
-
const rangeError = _t(
|
|
8827
|
-
const negativeWeightError = _t(
|
|
9576
|
+
const rangeError = _t("[[FUNCTION_NAME]] has mismatched range sizes.");
|
|
9577
|
+
const negativeWeightError = _t("[[FUNCTION_NAME]] expects the weight to be positive or equal to 0.");
|
|
8828
9578
|
const AVERAGE_WEIGHTED = {
|
|
8829
|
-
description: _t(
|
|
9579
|
+
description: _t("Weighted average."),
|
|
8830
9580
|
args: [
|
|
8831
9581
|
arg("values (number, range<number>)", _t("Values to average.")),
|
|
8832
9582
|
arg("weights (number, range<number>)", _t("Weights for each corresponding value.")),
|
|
@@ -8842,7 +9592,7 @@ const AVERAGE_WEIGHTED = {
|
|
|
8842
9592
|
let count = 0;
|
|
8843
9593
|
let value;
|
|
8844
9594
|
let weight;
|
|
8845
|
-
assert(() => values.length % 2 === 0, _t(
|
|
9595
|
+
assert(() => values.length % 2 === 0, _t("Wrong number of Argument[]. Expected an even number of Argument[]."));
|
|
8846
9596
|
for (let n = 0; n < values.length - 1; n += 2) {
|
|
8847
9597
|
value = values[n];
|
|
8848
9598
|
weight = values[n + 1];
|
|
@@ -8861,7 +9611,7 @@ const AVERAGE_WEIGHTED = {
|
|
|
8861
9611
|
let subValueIsNumber = typeof subValue === "number";
|
|
8862
9612
|
let subWeightIsNumber = typeof subWeight === "number";
|
|
8863
9613
|
// typeof subValue or subWeight can be 'number' or 'undefined'
|
|
8864
|
-
assert(() => subValueIsNumber === subWeightIsNumber, _t(
|
|
9614
|
+
assert(() => subValueIsNumber === subWeightIsNumber, _t("[[FUNCTION_NAME]] expects number values."));
|
|
8865
9615
|
if (subWeightIsNumber) {
|
|
8866
9616
|
assert(() => subWeight >= 0, negativeWeightError);
|
|
8867
9617
|
sum += subValue * subWeight;
|
|
@@ -8878,7 +9628,7 @@ const AVERAGE_WEIGHTED = {
|
|
|
8878
9628
|
count += weight;
|
|
8879
9629
|
}
|
|
8880
9630
|
}
|
|
8881
|
-
assert(() => count !== 0, _t(
|
|
9631
|
+
assert(() => count !== 0, _t("Evaluation of function [[FUNCTION_NAME]] caused a divide by zero error."));
|
|
8882
9632
|
return sum / count;
|
|
8883
9633
|
},
|
|
8884
9634
|
};
|
|
@@ -8886,7 +9636,7 @@ const AVERAGE_WEIGHTED = {
|
|
|
8886
9636
|
// AVERAGEA
|
|
8887
9637
|
// -----------------------------------------------------------------------------
|
|
8888
9638
|
const AVERAGEA = {
|
|
8889
|
-
description: _t(
|
|
9639
|
+
description: _t("Numerical average value in a dataset."),
|
|
8890
9640
|
args: [
|
|
8891
9641
|
arg("value1 (number, range<number>)", _t("The first value or range to consider when calculating the average value.")),
|
|
8892
9642
|
arg("value2 (number, range<number>, repeating)", _t("Additional values or ranges to consider when calculating the average value.")),
|
|
@@ -8901,7 +9651,7 @@ const AVERAGEA = {
|
|
|
8901
9651
|
count += 1;
|
|
8902
9652
|
return acc + a;
|
|
8903
9653
|
}, 0, this.locale);
|
|
8904
|
-
assert(() => count !== 0, _t(
|
|
9654
|
+
assert(() => count !== 0, _t("Evaluation of function [[FUNCTION_NAME]] caused a divide by zero error."));
|
|
8905
9655
|
return sum / count;
|
|
8906
9656
|
},
|
|
8907
9657
|
isExported: true,
|
|
@@ -8910,7 +9660,7 @@ const AVERAGEA = {
|
|
|
8910
9660
|
// AVERAGEIF
|
|
8911
9661
|
// -----------------------------------------------------------------------------
|
|
8912
9662
|
const AVERAGEIF = {
|
|
8913
|
-
description: _t(
|
|
9663
|
+
description: _t("Average of values depending on criteria."),
|
|
8914
9664
|
args: [
|
|
8915
9665
|
arg("criteria_range (number, range<number>)", _t("The range to check against criterion.")),
|
|
8916
9666
|
arg("criterion (string)", _t("The pattern or test to apply to criteria_range.")),
|
|
@@ -8929,7 +9679,7 @@ const AVERAGEIF = {
|
|
|
8929
9679
|
sum += value;
|
|
8930
9680
|
}
|
|
8931
9681
|
}, this.locale);
|
|
8932
|
-
assert(() => count !== 0, _t(
|
|
9682
|
+
assert(() => count !== 0, _t("Evaluation of function [[FUNCTION_NAME]] caused a divide by zero error."));
|
|
8933
9683
|
return sum / count;
|
|
8934
9684
|
},
|
|
8935
9685
|
isExported: true,
|
|
@@ -8938,7 +9688,7 @@ const AVERAGEIF = {
|
|
|
8938
9688
|
// AVERAGEIFS
|
|
8939
9689
|
// -----------------------------------------------------------------------------
|
|
8940
9690
|
const AVERAGEIFS = {
|
|
8941
|
-
description: _t(
|
|
9691
|
+
description: _t("Average of values depending on multiple criteria."),
|
|
8942
9692
|
args: [
|
|
8943
9693
|
arg("average_range (range)", _t("The range to average.")),
|
|
8944
9694
|
arg("criteria_range1 (range)", _t("The range to check against criterion1.")),
|
|
@@ -8958,7 +9708,7 @@ const AVERAGEIFS = {
|
|
|
8958
9708
|
sum += value;
|
|
8959
9709
|
}
|
|
8960
9710
|
}, this.locale);
|
|
8961
|
-
assert(() => count !== 0, _t(
|
|
9711
|
+
assert(() => count !== 0, _t("Evaluation of function [[FUNCTION_NAME]] caused a divide by zero error."));
|
|
8962
9712
|
return sum / count;
|
|
8963
9713
|
},
|
|
8964
9714
|
isExported: true,
|
|
@@ -8967,7 +9717,7 @@ const AVERAGEIFS = {
|
|
|
8967
9717
|
// COUNT
|
|
8968
9718
|
// -----------------------------------------------------------------------------
|
|
8969
9719
|
const COUNT = {
|
|
8970
|
-
description: _t(
|
|
9720
|
+
description: _t("The number of numeric values in dataset."),
|
|
8971
9721
|
args: [
|
|
8972
9722
|
arg("value1 (number, range<number>)", _t("The first value or range to consider when counting.")),
|
|
8973
9723
|
arg("value2 (number, range<number>, repeating)", _t("Additional values or ranges to consider when counting.")),
|
|
@@ -8999,7 +9749,7 @@ const COUNT = {
|
|
|
8999
9749
|
// COUNTA
|
|
9000
9750
|
// -----------------------------------------------------------------------------
|
|
9001
9751
|
const COUNTA = {
|
|
9002
|
-
description: _t(
|
|
9752
|
+
description: _t("The number of values in a dataset."),
|
|
9003
9753
|
args: [
|
|
9004
9754
|
arg("value1 (any, range)", _t("The first value or range to consider when counting.")),
|
|
9005
9755
|
arg("value2 (any, range, repeating)", _t("Additional values or ranges to consider when counting.")),
|
|
@@ -9016,7 +9766,7 @@ const COUNTA = {
|
|
|
9016
9766
|
// Note: Unlike the VAR function which corresponds to the variance over a sample (VAR.S),
|
|
9017
9767
|
// the COVAR function corresponds to the covariance over an entire population (COVAR.P)
|
|
9018
9768
|
const COVAR = {
|
|
9019
|
-
description: _t(
|
|
9769
|
+
description: _t("The covariance of a dataset."),
|
|
9020
9770
|
args: [
|
|
9021
9771
|
arg("data_y (any, range)", _t("The range representing the array or matrix of dependent data.")),
|
|
9022
9772
|
arg("data_x (any, range)", _t("The range representing the array or matrix of independent data.")),
|
|
@@ -9031,7 +9781,7 @@ const COVAR = {
|
|
|
9031
9781
|
// COVARIANCE.P
|
|
9032
9782
|
// -----------------------------------------------------------------------------
|
|
9033
9783
|
const COVARIANCE_P = {
|
|
9034
|
-
description: _t(
|
|
9784
|
+
description: _t("The covariance of a dataset."),
|
|
9035
9785
|
args: [
|
|
9036
9786
|
arg("data_y (any, range)", _t("The range representing the array or matrix of dependent data.")),
|
|
9037
9787
|
arg("data_x (any, range)", _t("The range representing the array or matrix of independent data.")),
|
|
@@ -9046,7 +9796,7 @@ const COVARIANCE_P = {
|
|
|
9046
9796
|
// COVARIANCE.S
|
|
9047
9797
|
// -----------------------------------------------------------------------------
|
|
9048
9798
|
const COVARIANCE_S = {
|
|
9049
|
-
description: _t(
|
|
9799
|
+
description: _t("The sample covariance of a dataset."),
|
|
9050
9800
|
args: [
|
|
9051
9801
|
arg("data_y (any, range)", _t("The range representing the array or matrix of dependent data.")),
|
|
9052
9802
|
arg("data_x (any, range)", _t("The range representing the array or matrix of independent data.")),
|
|
@@ -9134,7 +9884,7 @@ const LARGE = {
|
|
|
9134
9884
|
}
|
|
9135
9885
|
});
|
|
9136
9886
|
const result = largests.shift();
|
|
9137
|
-
assert(() => result !== undefined, _t(
|
|
9887
|
+
assert(() => result !== undefined, _t("[[FUNCTION_NAME]] has no valid input data."));
|
|
9138
9888
|
assert(() => count >= _n, _t("Function [[FUNCTION_NAME]] parameter 2 value (%s) is out of range.", _n));
|
|
9139
9889
|
return result;
|
|
9140
9890
|
},
|
|
@@ -9560,6 +10310,42 @@ const QUARTILE_INC = {
|
|
|
9560
10310
|
},
|
|
9561
10311
|
isExported: true,
|
|
9562
10312
|
};
|
|
10313
|
+
// RANK
|
|
10314
|
+
// -----------------------------------------------------------------------------
|
|
10315
|
+
const RANK = {
|
|
10316
|
+
description: _t("Returns the rank of a specified value in a dataset."),
|
|
10317
|
+
args: [
|
|
10318
|
+
arg("value (number)", _t("The value whose rank will be determined.")),
|
|
10319
|
+
arg("data (range)", _t("The range containing the dataset to consider.")),
|
|
10320
|
+
arg("is_ascending (boolean, default=FALSE)", _t("Whether to consider the values in data in descending or ascending order.")),
|
|
10321
|
+
],
|
|
10322
|
+
returns: ["ANY"],
|
|
10323
|
+
compute: function (value, data, isAscending = false) {
|
|
10324
|
+
const _isAscending = toBoolean(isAscending);
|
|
10325
|
+
const _value = toNumber(value, this.locale);
|
|
10326
|
+
let rank = 1;
|
|
10327
|
+
let found = false;
|
|
10328
|
+
for (const row of data) {
|
|
10329
|
+
for (const cell of row) {
|
|
10330
|
+
if (typeof cell !== "number") {
|
|
10331
|
+
continue;
|
|
10332
|
+
}
|
|
10333
|
+
const _cell = toNumber(cell, this.locale);
|
|
10334
|
+
if (_cell === _value) {
|
|
10335
|
+
found = true;
|
|
10336
|
+
}
|
|
10337
|
+
else if (_cell > _value !== _isAscending) {
|
|
10338
|
+
rank++;
|
|
10339
|
+
}
|
|
10340
|
+
}
|
|
10341
|
+
}
|
|
10342
|
+
if (!found) {
|
|
10343
|
+
throw new NotAvailableError(_t("Value not found in the given data."));
|
|
10344
|
+
}
|
|
10345
|
+
return rank;
|
|
10346
|
+
},
|
|
10347
|
+
isExported: true,
|
|
10348
|
+
};
|
|
9563
10349
|
// -----------------------------------------------------------------------------
|
|
9564
10350
|
// RSQ
|
|
9565
10351
|
// -----------------------------------------------------------------------------
|
|
@@ -9620,7 +10406,7 @@ const SMALL = {
|
|
|
9620
10406
|
}
|
|
9621
10407
|
});
|
|
9622
10408
|
const result = largests.pop();
|
|
9623
|
-
assert(() => result !== undefined, _t(
|
|
10409
|
+
assert(() => result !== undefined, _t("[[FUNCTION_NAME]] has no valid input data."));
|
|
9624
10410
|
assert(() => count >= _n, _t("Function [[FUNCTION_NAME]] parameter 2 value (%s) is out of range.", _n));
|
|
9625
10411
|
return result;
|
|
9626
10412
|
},
|
|
@@ -9904,6 +10690,7 @@ var statistical = /*#__PURE__*/Object.freeze({
|
|
|
9904
10690
|
QUARTILE: QUARTILE,
|
|
9905
10691
|
QUARTILE_EXC: QUARTILE_EXC,
|
|
9906
10692
|
QUARTILE_INC: QUARTILE_INC,
|
|
10693
|
+
RANK: RANK,
|
|
9907
10694
|
RSQ: RSQ,
|
|
9908
10695
|
SLOPE: SLOPE,
|
|
9909
10696
|
SMALL: SMALL,
|
|
@@ -10226,7 +11013,7 @@ const DATE = {
|
|
|
10226
11013
|
}
|
|
10227
11014
|
const jsDate = new Date(_year, _month - 1, _day);
|
|
10228
11015
|
const result = jsDateToRoundNumber(jsDate);
|
|
10229
|
-
assert(() => result >= 0, _t(
|
|
11016
|
+
assert(() => result >= 0, _t("The function [[FUNCTION_NAME]] result must be greater than or equal 01/01/1900."));
|
|
10230
11017
|
return result;
|
|
10231
11018
|
},
|
|
10232
11019
|
isExported: true,
|
|
@@ -10239,7 +11026,7 @@ const DATEDIF = {
|
|
|
10239
11026
|
args: [
|
|
10240
11027
|
arg("start_date (date)", _t("The start date to consider in the calculation. Must be a reference to a cell containing a DATE, a function returning a DATE type, or a number.")),
|
|
10241
11028
|
arg("end_date (date)", _t("The end date to consider in the calculation. Must be a reference to a cell containing a DATE, a function returning a DATE type, or a number.")),
|
|
10242
|
-
arg("unit (string)", _t(
|
|
11029
|
+
arg("unit (string)", _t('A text abbreviation for unit of time. Accepted values are "Y" (the number of whole years between start_date and end_date), "M" (the number of whole months between start_date and end_date), "D" (the number of days between start_date and end_date), "MD" (the number of days between start_date and end_date after subtracting whole months), "YM" (the number of whole months between start_date and end_date after subtracting whole years), "YD" (the number of days between start_date and end_date, assuming start_date and end_date were no more than one year apart).')),
|
|
10243
11030
|
],
|
|
10244
11031
|
returns: ["NUMBER"],
|
|
10245
11032
|
compute: function (startDate, endDate, unit) {
|
|
@@ -10681,7 +11468,7 @@ const TIME = {
|
|
|
10681
11468
|
_hour += Math.floor(_minute / 60);
|
|
10682
11469
|
_minute = (_minute % 60) + (_minute < 0 ? 60 : 0);
|
|
10683
11470
|
_hour %= 24;
|
|
10684
|
-
assert(() => _hour >= 0, _t(
|
|
11471
|
+
assert(() => _hour >= 0, _t("The function [[FUNCTION_NAME]] result cannot be negative"));
|
|
10685
11472
|
return _hour / 24 + _minute / (24 * 60) + _second / (24 * 60 * 60);
|
|
10686
11473
|
},
|
|
10687
11474
|
isExported: true,
|
|
@@ -11050,6 +11837,172 @@ var engineering = /*#__PURE__*/Object.freeze({
|
|
|
11050
11837
|
DELTA: DELTA
|
|
11051
11838
|
});
|
|
11052
11839
|
|
|
11840
|
+
const SORT_TYPES = [
|
|
11841
|
+
CellValueType.number,
|
|
11842
|
+
CellValueType.error,
|
|
11843
|
+
CellValueType.text,
|
|
11844
|
+
CellValueType.boolean,
|
|
11845
|
+
];
|
|
11846
|
+
function cellsSortingCriterion(sortingOrder) {
|
|
11847
|
+
const inverse = sortingOrder === "ascending" ? 1 : -1;
|
|
11848
|
+
return (left, right) => {
|
|
11849
|
+
if (left.type === CellValueType.empty) {
|
|
11850
|
+
return right.type === CellValueType.empty ? 0 : 1;
|
|
11851
|
+
}
|
|
11852
|
+
else if (right.type === CellValueType.empty) {
|
|
11853
|
+
return -1;
|
|
11854
|
+
}
|
|
11855
|
+
let typeOrder = SORT_TYPES.indexOf(left.type) - SORT_TYPES.indexOf(right.type);
|
|
11856
|
+
if (typeOrder === 0) {
|
|
11857
|
+
if (left.type === CellValueType.text || left.type === CellValueType.error) {
|
|
11858
|
+
typeOrder = left.value.localeCompare(right.value);
|
|
11859
|
+
}
|
|
11860
|
+
else {
|
|
11861
|
+
typeOrder = left.value - right.value;
|
|
11862
|
+
}
|
|
11863
|
+
}
|
|
11864
|
+
return inverse * typeOrder;
|
|
11865
|
+
};
|
|
11866
|
+
}
|
|
11867
|
+
function sortCells(cells, sortDirection, emptyCellAsZero) {
|
|
11868
|
+
const cellsWithIndex = cells.map((cell, index) => ({
|
|
11869
|
+
index,
|
|
11870
|
+
type: cell.type,
|
|
11871
|
+
value: cell.value,
|
|
11872
|
+
}));
|
|
11873
|
+
const cellsToSort = emptyCellAsZero
|
|
11874
|
+
? cellsWithIndex.map((cell) => cell.type === CellValueType.empty ? { ...cell, type: CellValueType.number, value: 0 } : cell)
|
|
11875
|
+
: cellsWithIndex;
|
|
11876
|
+
return cellsToSort.sort(cellsSortingCriterion(sortDirection));
|
|
11877
|
+
}
|
|
11878
|
+
function interactiveSortSelection(env, sheetId, anchor, zone, sortDirection) {
|
|
11879
|
+
let result = DispatchResult.Success;
|
|
11880
|
+
//several columns => bypass the contiguity check
|
|
11881
|
+
let multiColumns = zone.right > zone.left;
|
|
11882
|
+
if (env.model.getters.doesIntersectMerge(sheetId, zone)) {
|
|
11883
|
+
multiColumns = false;
|
|
11884
|
+
let table;
|
|
11885
|
+
for (let row = zone.top; row <= zone.bottom; row++) {
|
|
11886
|
+
table = [];
|
|
11887
|
+
for (let col = zone.left; col <= zone.right; col++) {
|
|
11888
|
+
let merge = env.model.getters.getMerge({ sheetId, col, row });
|
|
11889
|
+
if (merge && !table.includes(merge.id.toString())) {
|
|
11890
|
+
table.push(merge.id.toString());
|
|
11891
|
+
}
|
|
11892
|
+
}
|
|
11893
|
+
if (table.length >= 2) {
|
|
11894
|
+
multiColumns = true;
|
|
11895
|
+
break;
|
|
11896
|
+
}
|
|
11897
|
+
}
|
|
11898
|
+
}
|
|
11899
|
+
const { col, row } = anchor;
|
|
11900
|
+
if (multiColumns) {
|
|
11901
|
+
result = env.model.dispatch("SORT_CELLS", { sheetId, col, row, zone, sortDirection });
|
|
11902
|
+
}
|
|
11903
|
+
else {
|
|
11904
|
+
// check contiguity
|
|
11905
|
+
const contiguousZone = env.model.getters.getContiguousZone(sheetId, zone);
|
|
11906
|
+
if (isEqual(contiguousZone, zone)) {
|
|
11907
|
+
// merge as it is
|
|
11908
|
+
result = env.model.dispatch("SORT_CELLS", {
|
|
11909
|
+
sheetId,
|
|
11910
|
+
col,
|
|
11911
|
+
row,
|
|
11912
|
+
zone,
|
|
11913
|
+
sortDirection,
|
|
11914
|
+
});
|
|
11915
|
+
}
|
|
11916
|
+
else {
|
|
11917
|
+
env.askConfirmation(_t("We found data next to your selection. Since this data was not selected, it will not be sorted. Do you want to extend your selection?"), () => {
|
|
11918
|
+
zone = contiguousZone;
|
|
11919
|
+
result = env.model.dispatch("SORT_CELLS", {
|
|
11920
|
+
sheetId,
|
|
11921
|
+
col,
|
|
11922
|
+
row,
|
|
11923
|
+
zone,
|
|
11924
|
+
sortDirection,
|
|
11925
|
+
});
|
|
11926
|
+
}, () => {
|
|
11927
|
+
result = env.model.dispatch("SORT_CELLS", {
|
|
11928
|
+
sheetId,
|
|
11929
|
+
col,
|
|
11930
|
+
row,
|
|
11931
|
+
zone,
|
|
11932
|
+
sortDirection,
|
|
11933
|
+
});
|
|
11934
|
+
});
|
|
11935
|
+
}
|
|
11936
|
+
}
|
|
11937
|
+
if (result.isCancelledBecause("InvalidSortZone" /* CommandResult.InvalidSortZone */)) {
|
|
11938
|
+
const { col, row } = anchor;
|
|
11939
|
+
env.model.selection.selectZone({ cell: { col, row }, zone });
|
|
11940
|
+
env.raiseError(_t("Cannot sort. To sort, select only cells or only merges that have the same size."));
|
|
11941
|
+
}
|
|
11942
|
+
}
|
|
11943
|
+
|
|
11944
|
+
function sortMatrix(matrix, locale, ...criteria) {
|
|
11945
|
+
for (const [i, value] of criteria.entries()) {
|
|
11946
|
+
assert(() => value !== undefined, _t("Value for parameter %d is missing, while the function [[FUNCTION_NAME]] expect a number or a range.", i + 1));
|
|
11947
|
+
}
|
|
11948
|
+
const sortingOrders = [];
|
|
11949
|
+
const sortColumns = [];
|
|
11950
|
+
const nRows = matrix.length;
|
|
11951
|
+
for (let i = 0; i < criteria.length; i += 2) {
|
|
11952
|
+
sortingOrders.push(toBoolean(toScalar(criteria[i + 1])?.value) ? "ascending" : "descending");
|
|
11953
|
+
const sortColumn = criteria[i];
|
|
11954
|
+
if (isMatrix(sortColumn) && (sortColumn.length > 1 || sortColumn[0].length > 1)) {
|
|
11955
|
+
assert(() => sortColumn.length === 1 && sortColumn[0].length === nRows, _t("Wrong size for %s. Expected a range of size 1x%s. Got %sx%s.", `sort_column${i + 1}`, nRows, sortColumn.length, sortColumn[0].length));
|
|
11956
|
+
sortColumns.push(sortColumn.flat().map((c) => c.value));
|
|
11957
|
+
}
|
|
11958
|
+
else {
|
|
11959
|
+
const colIndex = toNumber(toScalar(sortColumn)?.value, locale);
|
|
11960
|
+
if (colIndex < 1 || colIndex > matrix[0].length) {
|
|
11961
|
+
return matrix;
|
|
11962
|
+
}
|
|
11963
|
+
sortColumns.push(matrix.map((row) => row[colIndex - 1].value));
|
|
11964
|
+
}
|
|
11965
|
+
}
|
|
11966
|
+
if (sortColumns.length === 0) {
|
|
11967
|
+
for (let i = 0; i < matrix[0].length; i++) {
|
|
11968
|
+
sortColumns.push(matrix.map((row) => row[i].value));
|
|
11969
|
+
sortingOrders.push("ascending");
|
|
11970
|
+
}
|
|
11971
|
+
}
|
|
11972
|
+
const sortingCriteria = {
|
|
11973
|
+
descending: cellsSortingCriterion("descending"),
|
|
11974
|
+
ascending: cellsSortingCriterion("ascending"),
|
|
11975
|
+
};
|
|
11976
|
+
const indexes = range(0, matrix.length);
|
|
11977
|
+
indexes.sort((a, b) => {
|
|
11978
|
+
for (const [i, sortColumn] of sortColumns.entries()) {
|
|
11979
|
+
const left = sortColumn[a];
|
|
11980
|
+
const right = sortColumn[b];
|
|
11981
|
+
const leftCell = {
|
|
11982
|
+
value: left,
|
|
11983
|
+
type: left === null
|
|
11984
|
+
? CellValueType.empty
|
|
11985
|
+
: typeof left === "string"
|
|
11986
|
+
? CellValueType.text
|
|
11987
|
+
: typeof left,
|
|
11988
|
+
};
|
|
11989
|
+
const rightCell = {
|
|
11990
|
+
value: right,
|
|
11991
|
+
type: right === null
|
|
11992
|
+
? CellValueType.empty
|
|
11993
|
+
: typeof right === "string"
|
|
11994
|
+
? CellValueType.text
|
|
11995
|
+
: typeof right,
|
|
11996
|
+
};
|
|
11997
|
+
const result = sortingCriteria[sortingOrders[i]](leftCell, rightCell);
|
|
11998
|
+
if (result !== 0) {
|
|
11999
|
+
return result;
|
|
12000
|
+
}
|
|
12001
|
+
}
|
|
12002
|
+
return 0;
|
|
12003
|
+
});
|
|
12004
|
+
return indexes.map((i) => matrix[i]);
|
|
12005
|
+
}
|
|
11053
12006
|
// -----------------------------------------------------------------------------
|
|
11054
12007
|
// FILTER
|
|
11055
12008
|
// -----------------------------------------------------------------------------
|
|
@@ -11070,7 +12023,7 @@ const FILTER = {
|
|
|
11070
12023
|
const _conditions = _conditionsMatrices.map((c) => c.flat());
|
|
11071
12024
|
const mode = _conditionsMatrices[0].length === 1 ? "row" : "col";
|
|
11072
12025
|
_array = mode === "row" ? transposeMatrix(_array) : _array;
|
|
11073
|
-
assert(() => _conditions.every((cond) => cond.length === _array.length), _t(
|
|
12026
|
+
assert(() => _conditions.every((cond) => cond.length === _array.length), _t("FILTER has mismatched sizes on the range and conditions."));
|
|
11074
12027
|
const result = [];
|
|
11075
12028
|
for (let i = 0; i < _array.length; i++) {
|
|
11076
12029
|
const row = _array[i];
|
|
@@ -11086,6 +12039,93 @@ const FILTER = {
|
|
|
11086
12039
|
isExported: true,
|
|
11087
12040
|
};
|
|
11088
12041
|
// -----------------------------------------------------------------------------
|
|
12042
|
+
// SORT
|
|
12043
|
+
// -----------------------------------------------------------------------------
|
|
12044
|
+
const SORT = {
|
|
12045
|
+
description: _t("Sorts the rows of a given array or range by the values in one or more columns."),
|
|
12046
|
+
args: [
|
|
12047
|
+
arg("range (range)", _t("The data to be sorted.")),
|
|
12048
|
+
arg("sort_column (any, range<number>, repeating)", _t("The index of the column in range or a range outside of range containing the values by which to sort.")),
|
|
12049
|
+
arg("is_ascending (boolean, repeating)", _t("TRUE or FALSE indicating whether to sort sort_column in ascending order. FALSE sorts in descending order.")),
|
|
12050
|
+
],
|
|
12051
|
+
returns: ["RANGE"],
|
|
12052
|
+
computeValueAndFormat: function (range, ...sortingCriteria) {
|
|
12053
|
+
const _range = transposeMatrix(range);
|
|
12054
|
+
return transposeMatrix(sortMatrix(_range, this.locale, ...sortingCriteria));
|
|
12055
|
+
},
|
|
12056
|
+
isExported: true,
|
|
12057
|
+
};
|
|
12058
|
+
// -----------------------------------------------------------------------------
|
|
12059
|
+
// SORTN
|
|
12060
|
+
// -----------------------------------------------------------------------------
|
|
12061
|
+
const SORTN = {
|
|
12062
|
+
description: _t("Returns the first n items in a data set after performing a sort."),
|
|
12063
|
+
args: [
|
|
12064
|
+
arg("range (range)", _t("The data to be sorted.")),
|
|
12065
|
+
arg("n (number, default=1)", _t("The number of items to return.")),
|
|
12066
|
+
arg("display_ties_mode (number, default=0)", _t("A number representing the way to display ties.")),
|
|
12067
|
+
arg("sort_column (number, range<number>, repeating)", _t("The index of the column in range or a range outside of range containing the values by which to sort.")),
|
|
12068
|
+
arg("is_ascending (boolean, repeating)", _t("TRUE or FALSE indicating whether to sort sort_column in ascending order. FALSE sorts in descending order.")),
|
|
12069
|
+
],
|
|
12070
|
+
returns: ["RANGE"],
|
|
12071
|
+
computeValueAndFormat: function (range, n, displayTiesMode, ...sortingCriteria) {
|
|
12072
|
+
const _n = toNumber(n?.value ?? 1, this.locale);
|
|
12073
|
+
assert(() => _n >= 0, _t("Wrong value of 'n'. Expected a positive number. Got %s.", _n));
|
|
12074
|
+
const _displayTiesMode = toNumber(displayTiesMode?.value ?? 0, this.locale);
|
|
12075
|
+
assert(() => _displayTiesMode >= 0 && _displayTiesMode <= 3, _t("Wrong value of 'display_ties_mode'. Expected a positive number between 0 and 3. Got %s.", _displayTiesMode));
|
|
12076
|
+
const sortedData = sortMatrix(transposeMatrix(range), this.locale, ...sortingCriteria);
|
|
12077
|
+
const sameRows = (i, j) => JSON.stringify(sortedData[i].map((c) => c.value)) ===
|
|
12078
|
+
JSON.stringify(sortedData[j].map((c) => c.value));
|
|
12079
|
+
/*
|
|
12080
|
+
* displayTiesMode determine how ties (equal values) are dealt with:
|
|
12081
|
+
* 0 - ignore ties and show first n rows only
|
|
12082
|
+
* 1 - show first n rows plus any additional ties with nth row
|
|
12083
|
+
* 2 - show n rows but remove duplicates
|
|
12084
|
+
* 3 - show first n unique rows and all duplicates of these rows
|
|
12085
|
+
*/
|
|
12086
|
+
switch (_displayTiesMode) {
|
|
12087
|
+
case 0:
|
|
12088
|
+
return transposeMatrix(sortedData.slice(0, _n));
|
|
12089
|
+
case 1:
|
|
12090
|
+
for (let i = _n; i < sortedData.length; i++) {
|
|
12091
|
+
if (!sameRows(i, _n - 1)) {
|
|
12092
|
+
return transposeMatrix(sortedData.slice(0, i));
|
|
12093
|
+
}
|
|
12094
|
+
}
|
|
12095
|
+
return transposeMatrix(sortedData);
|
|
12096
|
+
case 2: {
|
|
12097
|
+
const uniques = [sortedData[0]];
|
|
12098
|
+
for (let i = 1; i < sortedData.length; i++) {
|
|
12099
|
+
for (let j = 0; j < i; j++) {
|
|
12100
|
+
if (sameRows(i, j)) {
|
|
12101
|
+
break;
|
|
12102
|
+
}
|
|
12103
|
+
if (j === i - 1) {
|
|
12104
|
+
uniques.push(sortedData[i]);
|
|
12105
|
+
}
|
|
12106
|
+
}
|
|
12107
|
+
}
|
|
12108
|
+
return transposeMatrix(uniques.slice(0, _n));
|
|
12109
|
+
}
|
|
12110
|
+
case 3: {
|
|
12111
|
+
const uniques = [sortedData[0]];
|
|
12112
|
+
let counter = 1;
|
|
12113
|
+
for (let i = 1; i < sortedData.length; i++) {
|
|
12114
|
+
if (!sameRows(i, i - 1)) {
|
|
12115
|
+
counter++;
|
|
12116
|
+
}
|
|
12117
|
+
if (counter > _n) {
|
|
12118
|
+
break;
|
|
12119
|
+
}
|
|
12120
|
+
uniques.push(sortedData[i]);
|
|
12121
|
+
}
|
|
12122
|
+
return transposeMatrix(uniques);
|
|
12123
|
+
}
|
|
12124
|
+
}
|
|
12125
|
+
},
|
|
12126
|
+
isExported: true,
|
|
12127
|
+
};
|
|
12128
|
+
// -----------------------------------------------------------------------------
|
|
11089
12129
|
// UNIQUE
|
|
11090
12130
|
// -----------------------------------------------------------------------------
|
|
11091
12131
|
const UNIQUE = {
|
|
@@ -11133,6 +12173,8 @@ const UNIQUE = {
|
|
|
11133
12173
|
var filter = /*#__PURE__*/Object.freeze({
|
|
11134
12174
|
__proto__: null,
|
|
11135
12175
|
FILTER: FILTER,
|
|
12176
|
+
SORT: SORT,
|
|
12177
|
+
SORTN: SORTN,
|
|
11136
12178
|
UNIQUE: UNIQUE
|
|
11137
12179
|
});
|
|
11138
12180
|
|
|
@@ -11285,7 +12327,7 @@ function newtonMethod(func, derivFunc, startValue, maxIterations, epsMax = 1e-10
|
|
|
11285
12327
|
do {
|
|
11286
12328
|
y = func(x);
|
|
11287
12329
|
if (isNaN(y)) {
|
|
11288
|
-
assert(() => count < maxIterations && nanFallback !== undefined, _t(
|
|
12330
|
+
assert(() => count < maxIterations && nanFallback !== undefined, _t("Function [[FUNCTION_NAME]] didn't find any result."));
|
|
11289
12331
|
count++;
|
|
11290
12332
|
x = nanFallback(previousFallback);
|
|
11291
12333
|
previousFallback = x;
|
|
@@ -11295,7 +12337,7 @@ function newtonMethod(func, derivFunc, startValue, maxIterations, epsMax = 1e-10
|
|
|
11295
12337
|
xDelta = Math.abs(newX - x);
|
|
11296
12338
|
x = newX;
|
|
11297
12339
|
yEqual0 = xDelta < epsMax || Math.abs(y) < epsMax;
|
|
11298
|
-
assert(() => count < maxIterations, _t(
|
|
12340
|
+
assert(() => count < maxIterations, _t("Function [[FUNCTION_NAME]] didn't find any result."));
|
|
11299
12341
|
count++;
|
|
11300
12342
|
} while (!yEqual0);
|
|
11301
12343
|
return x;
|
|
@@ -13388,7 +14430,7 @@ const AND = {
|
|
|
13388
14430
|
acc = acc && arg;
|
|
13389
14431
|
return acc;
|
|
13390
14432
|
});
|
|
13391
|
-
assert(() => foundBoolean, _t(
|
|
14433
|
+
assert(() => foundBoolean, _t("[[FUNCTION_NAME]] has no valid input data."));
|
|
13392
14434
|
return acc;
|
|
13393
14435
|
},
|
|
13394
14436
|
isExported: true,
|
|
@@ -13502,7 +14544,7 @@ const IFS = {
|
|
|
13502
14544
|
],
|
|
13503
14545
|
returns: ["ANY"],
|
|
13504
14546
|
computeValueAndFormat: function (...values) {
|
|
13505
|
-
assert(() => values.length % 2 === 0, _t(
|
|
14547
|
+
assert(() => values.length % 2 === 0, _t("Wrong number of arguments. Expected an even number of arguments."));
|
|
13506
14548
|
for (let n = 0; n < values.length - 1; n += 2) {
|
|
13507
14549
|
if (toBoolean(values[n]()?.value)) {
|
|
13508
14550
|
const result = values[n + 1]();
|
|
@@ -13515,7 +14557,7 @@ const IFS = {
|
|
|
13515
14557
|
return result;
|
|
13516
14558
|
}
|
|
13517
14559
|
}
|
|
13518
|
-
throw new Error(_t(
|
|
14560
|
+
throw new Error(_t("No match."));
|
|
13519
14561
|
},
|
|
13520
14562
|
isExported: true,
|
|
13521
14563
|
};
|
|
@@ -13551,7 +14593,7 @@ const OR = {
|
|
|
13551
14593
|
acc = acc || arg;
|
|
13552
14594
|
return !acc;
|
|
13553
14595
|
});
|
|
13554
|
-
assert(() => foundBoolean, _t(
|
|
14596
|
+
assert(() => foundBoolean, _t("[[FUNCTION_NAME]] has no valid input data."));
|
|
13555
14597
|
return acc;
|
|
13556
14598
|
},
|
|
13557
14599
|
isExported: true,
|
|
@@ -13586,7 +14628,7 @@ const XOR = {
|
|
|
13586
14628
|
acc = acc ? !arg : arg;
|
|
13587
14629
|
return true; // no stop condition
|
|
13588
14630
|
});
|
|
13589
|
-
assert(() => foundBoolean, _t(
|
|
14631
|
+
assert(() => foundBoolean, _t("[[FUNCTION_NAME]] has no valid input data."));
|
|
13590
14632
|
return acc;
|
|
13591
14633
|
},
|
|
13592
14634
|
isExported: true,
|
|
@@ -13690,7 +14732,7 @@ const COLUMNS = {
|
|
|
13690
14732
|
// HLOOKUP
|
|
13691
14733
|
// -----------------------------------------------------------------------------
|
|
13692
14734
|
const HLOOKUP = {
|
|
13693
|
-
description: _t(
|
|
14735
|
+
description: _t("Horizontal lookup"),
|
|
13694
14736
|
args: [
|
|
13695
14737
|
arg("search_key (any)", _t("The value to search for. For example, 42, 'Cats', or I24.")),
|
|
13696
14738
|
arg("range (range)", _t("The range to consider for the search. The first row in the range is searched for the key specified in search_key.")),
|
|
@@ -13748,7 +14790,7 @@ const INDEX = {
|
|
|
13748
14790
|
// LOOKUP
|
|
13749
14791
|
// -----------------------------------------------------------------------------
|
|
13750
14792
|
const LOOKUP = {
|
|
13751
|
-
description: _t(
|
|
14793
|
+
description: _t("Look up a value."),
|
|
13752
14794
|
args: [
|
|
13753
14795
|
arg("search_key (any)", _t("The value to search for. For example, 42, 'Cats', or I24.")),
|
|
13754
14796
|
arg("search_array (range)", _t("One method of using this function is to provide a single sorted row or column search_array to look through for the search_key with a second argument result_range. The other way is to combine these two arguments into one search_array where the first row or column is searched and a value is returned from the last row or column in the array. If search_key is not found, a non-exact match may be returned.")),
|
|
@@ -13789,7 +14831,7 @@ const LOOKUP = {
|
|
|
13789
14831
|
// -----------------------------------------------------------------------------
|
|
13790
14832
|
const DEFAULT_SEARCH_TYPE = 1;
|
|
13791
14833
|
const MATCH = {
|
|
13792
|
-
description: _t(
|
|
14834
|
+
description: _t("Position of item in range that matches value."),
|
|
13793
14835
|
args: [
|
|
13794
14836
|
arg("search_key (any)", _t("The value to search for. For example, 42, 'Cats', or I24.")),
|
|
13795
14837
|
arg("range (any, range)", _t("The one-dimensional array to be searched.")),
|
|
@@ -13857,7 +14899,7 @@ const ROWS = {
|
|
|
13857
14899
|
// VLOOKUP
|
|
13858
14900
|
// -----------------------------------------------------------------------------
|
|
13859
14901
|
const VLOOKUP = {
|
|
13860
|
-
description: _t(
|
|
14902
|
+
description: _t("Vertical lookup."),
|
|
13861
14903
|
args: [
|
|
13862
14904
|
arg("search_key (any)", _t("The value to search for. For example, 42, 'Cats', or I24.")),
|
|
13863
14905
|
arg("range (any, range)", _t("The range to consider for the search. The first column in the range is searched for the key specified in search_key.")),
|
|
@@ -13883,7 +14925,7 @@ const VLOOKUP = {
|
|
|
13883
14925
|
// XLOOKUP
|
|
13884
14926
|
// -----------------------------------------------------------------------------
|
|
13885
14927
|
const XLOOKUP = {
|
|
13886
|
-
description: _t(
|
|
14928
|
+
description: _t("Search a range for a match and return the corresponding item from a second range."),
|
|
13887
14929
|
args: [
|
|
13888
14930
|
arg("search_key (any)", _t("The value to search for.")),
|
|
13889
14931
|
arg("lookup_range (any, range)", _t("The range to consider for the search. Should be a single column or a single row.")),
|
|
@@ -13947,7 +14989,7 @@ var lookup = /*#__PURE__*/Object.freeze({
|
|
|
13947
14989
|
// ADD
|
|
13948
14990
|
// -----------------------------------------------------------------------------
|
|
13949
14991
|
const ADD = {
|
|
13950
|
-
description: _t(
|
|
14992
|
+
description: _t("Sum of two numbers."),
|
|
13951
14993
|
args: [
|
|
13952
14994
|
arg("value1 (number)", _t("The first addend.")),
|
|
13953
14995
|
arg("value2 (number)", _t("The second addend.")),
|
|
@@ -13962,7 +15004,7 @@ const ADD = {
|
|
|
13962
15004
|
// CONCAT
|
|
13963
15005
|
// -----------------------------------------------------------------------------
|
|
13964
15006
|
const CONCAT = {
|
|
13965
|
-
description: _t(
|
|
15007
|
+
description: _t("Concatenation of two values."),
|
|
13966
15008
|
args: [
|
|
13967
15009
|
arg("value1 (string)", _t("The value to which value2 will be appended.")),
|
|
13968
15010
|
arg("value2 (string)", _t("The value to append to value1.")),
|
|
@@ -13977,7 +15019,7 @@ const CONCAT = {
|
|
|
13977
15019
|
// DIVIDE
|
|
13978
15020
|
// -----------------------------------------------------------------------------
|
|
13979
15021
|
const DIVIDE = {
|
|
13980
|
-
description: _t(
|
|
15022
|
+
description: _t("One number divided by another."),
|
|
13981
15023
|
args: [
|
|
13982
15024
|
arg("dividend (number)", _t("The number to be divided.")),
|
|
13983
15025
|
arg("divisor (number)", _t("The number to divide by.")),
|
|
@@ -13998,7 +15040,7 @@ function isEmpty(value) {
|
|
|
13998
15040
|
}
|
|
13999
15041
|
const getNeutral = { number: 0, string: "", boolean: false };
|
|
14000
15042
|
const EQ = {
|
|
14001
|
-
description: _t(
|
|
15043
|
+
description: _t("Equal."),
|
|
14002
15044
|
args: [
|
|
14003
15045
|
arg("value1 (any)", _t("The first value.")),
|
|
14004
15046
|
arg("value2 (any)", _t("The value to test against value1 for equality.")),
|
|
@@ -14039,7 +15081,7 @@ function applyRelationalOperator(value1, value2, cb) {
|
|
|
14039
15081
|
return cb(value1, value2);
|
|
14040
15082
|
}
|
|
14041
15083
|
const GT = {
|
|
14042
|
-
description: _t(
|
|
15084
|
+
description: _t("Strictly greater than."),
|
|
14043
15085
|
args: [
|
|
14044
15086
|
arg("value1 (any)", _t("The value to test as being greater than value2.")),
|
|
14045
15087
|
arg("value2 (any)", _t("The second value.")),
|
|
@@ -14055,7 +15097,7 @@ const GT = {
|
|
|
14055
15097
|
// GTE
|
|
14056
15098
|
// -----------------------------------------------------------------------------
|
|
14057
15099
|
const GTE = {
|
|
14058
|
-
description: _t(
|
|
15100
|
+
description: _t("Greater than or equal to."),
|
|
14059
15101
|
args: [
|
|
14060
15102
|
arg("value1 (any)", _t("The value to test as being greater than or equal to value2.")),
|
|
14061
15103
|
arg("value2 (any)", _t("The second value.")),
|
|
@@ -14071,7 +15113,7 @@ const GTE = {
|
|
|
14071
15113
|
// LT
|
|
14072
15114
|
// -----------------------------------------------------------------------------
|
|
14073
15115
|
const LT = {
|
|
14074
|
-
description: _t(
|
|
15116
|
+
description: _t("Less than."),
|
|
14075
15117
|
args: [
|
|
14076
15118
|
arg("value1 (any)", _t("The value to test as being less than value2.")),
|
|
14077
15119
|
arg("value2 (any)", _t("The second value.")),
|
|
@@ -14085,7 +15127,7 @@ const LT = {
|
|
|
14085
15127
|
// LTE
|
|
14086
15128
|
// -----------------------------------------------------------------------------
|
|
14087
15129
|
const LTE = {
|
|
14088
|
-
description: _t(
|
|
15130
|
+
description: _t("Less than or equal to."),
|
|
14089
15131
|
args: [
|
|
14090
15132
|
arg("value1 (any)", _t("The value to test as being less than or equal to value2.")),
|
|
14091
15133
|
arg("value2 (any)", _t("The second value.")),
|
|
@@ -14099,7 +15141,7 @@ const LTE = {
|
|
|
14099
15141
|
// MINUS
|
|
14100
15142
|
// -----------------------------------------------------------------------------
|
|
14101
15143
|
const MINUS = {
|
|
14102
|
-
description: _t(
|
|
15144
|
+
description: _t("Difference of two numbers."),
|
|
14103
15145
|
args: [
|
|
14104
15146
|
arg("value1 (number)", _t("The minuend, or number to be subtracted from.")),
|
|
14105
15147
|
arg("value2 (number)", _t("The subtrahend, or number to subtract from value1.")),
|
|
@@ -14114,7 +15156,7 @@ const MINUS = {
|
|
|
14114
15156
|
// MULTIPLY
|
|
14115
15157
|
// -----------------------------------------------------------------------------
|
|
14116
15158
|
const MULTIPLY = {
|
|
14117
|
-
description: _t(
|
|
15159
|
+
description: _t("Product of two numbers"),
|
|
14118
15160
|
args: [
|
|
14119
15161
|
arg("factor1 (number)", _t("The first multiplicand.")),
|
|
14120
15162
|
arg("factor2 (number)", _t("The second multiplicand.")),
|
|
@@ -14129,7 +15171,7 @@ const MULTIPLY = {
|
|
|
14129
15171
|
// NE
|
|
14130
15172
|
// -----------------------------------------------------------------------------
|
|
14131
15173
|
const NE = {
|
|
14132
|
-
description: _t(
|
|
15174
|
+
description: _t("Not equal."),
|
|
14133
15175
|
args: [
|
|
14134
15176
|
arg("value1 (any)", _t("The first value.")),
|
|
14135
15177
|
arg("value2 (any)", _t("The value to test against value1 for inequality.")),
|
|
@@ -14143,7 +15185,7 @@ const NE = {
|
|
|
14143
15185
|
// POW
|
|
14144
15186
|
// -----------------------------------------------------------------------------
|
|
14145
15187
|
const POW = {
|
|
14146
|
-
description: _t(
|
|
15188
|
+
description: _t("A number raised to a power."),
|
|
14147
15189
|
args: [
|
|
14148
15190
|
arg("base (number)", _t("The number to raise to the exponent power.")),
|
|
14149
15191
|
arg("exponent (number)", _t("The exponent to raise base to.")),
|
|
@@ -14157,7 +15199,7 @@ const POW = {
|
|
|
14157
15199
|
// UMINUS
|
|
14158
15200
|
// -----------------------------------------------------------------------------
|
|
14159
15201
|
const UMINUS = {
|
|
14160
|
-
description: _t(
|
|
15202
|
+
description: _t("A number with the sign reversed."),
|
|
14161
15203
|
args: [
|
|
14162
15204
|
arg("value (number)", _t("The number to have its sign reversed. Equivalently, the number to multiply by -1.")),
|
|
14163
15205
|
],
|
|
@@ -14171,7 +15213,7 @@ const UMINUS = {
|
|
|
14171
15213
|
// UNARY_PERCENT
|
|
14172
15214
|
// -----------------------------------------------------------------------------
|
|
14173
15215
|
const UNARY_PERCENT = {
|
|
14174
|
-
description: _t(
|
|
15216
|
+
description: _t("Value interpreted as a percentage."),
|
|
14175
15217
|
args: [arg("percentage (number)", _t("The value to interpret as a percentage."))],
|
|
14176
15218
|
returns: ["NUMBER"],
|
|
14177
15219
|
compute: function (percentage) {
|
|
@@ -14182,7 +15224,7 @@ const UNARY_PERCENT = {
|
|
|
14182
15224
|
// UPLUS
|
|
14183
15225
|
// -----------------------------------------------------------------------------
|
|
14184
15226
|
const UPLUS = {
|
|
14185
|
-
description: _t(
|
|
15227
|
+
description: _t("A specified number, unchanged."),
|
|
14186
15228
|
args: [arg("value (any)", _t("The number to return."))],
|
|
14187
15229
|
returns: ["ANY"],
|
|
14188
15230
|
computeFormat: (value) => value?.format,
|
|
@@ -14293,7 +15335,7 @@ const FIND = {
|
|
|
14293
15335
|
const _searchFor = toString(searchFor);
|
|
14294
15336
|
const _textToSearch = toString(textToSearch);
|
|
14295
15337
|
const _startingAt = toNumber(startingAt, this.locale);
|
|
14296
|
-
assert(() => _textToSearch !== "", _t(
|
|
15338
|
+
assert(() => _textToSearch !== "", _t("The text_to_search must be non-empty."));
|
|
14297
15339
|
assert(() => _startingAt >= 1, _t("The starting_at (%s) must be greater than or equal to 1.", _startingAt.toString()));
|
|
14298
15340
|
const result = _textToSearch.indexOf(_searchFor, _startingAt - 1);
|
|
14299
15341
|
assert(() => result >= 0, _t("In [[FUNCTION_NAME]] evaluation, cannot find '%s' within '%s'.", _searchFor.toString(), _textToSearch));
|
|
@@ -14452,7 +15494,7 @@ const SEARCH = {
|
|
|
14452
15494
|
const _searchFor = toString(searchFor).toLowerCase();
|
|
14453
15495
|
const _textToSearch = toString(textToSearch).toLowerCase();
|
|
14454
15496
|
const _startingAt = toNumber(startingAt, this.locale);
|
|
14455
|
-
assert(() => _textToSearch !== "", _t(
|
|
15497
|
+
assert(() => _textToSearch !== "", _t("The text_to_search must be non-empty."));
|
|
14456
15498
|
assert(() => _startingAt >= 1, _t("The starting_at (%s) must be greater than or equal to 1.", _startingAt.toString()));
|
|
14457
15499
|
const result = _textToSearch.indexOf(_searchFor, _startingAt - 1);
|
|
14458
15500
|
assert(() => result >= 0, _t("In [[FUNCTION_NAME]] evaluation, cannot find '%s' within '%s'.", _searchFor, _textToSearch));
|
|
@@ -15560,7 +16602,7 @@ function compile(formula) {
|
|
|
15560
16602
|
const code = new FunctionCodeBuilder(scope);
|
|
15561
16603
|
if (ast.type !== "REFERENCE" && !(ast.type === "BIN_OPERATION" && ast.value === ":")) {
|
|
15562
16604
|
if (isMeta) {
|
|
15563
|
-
throw new BadExpressionError(_t(
|
|
16605
|
+
throw new BadExpressionError(_t("Argument must be a reference to a cell or range."));
|
|
15564
16606
|
}
|
|
15565
16607
|
}
|
|
15566
16608
|
if (ast.debug) {
|
|
@@ -16364,410 +17406,11 @@ function toXlsxHexColor(color) {
|
|
|
16364
17406
|
return color;
|
|
16365
17407
|
}
|
|
16366
17408
|
|
|
16367
|
-
/**
|
|
16368
|
-
* AbstractChart is the class from which every Chart should inherit.
|
|
16369
|
-
* The role of this class is to maintain the state of each chart.
|
|
16370
|
-
*/
|
|
16371
|
-
class AbstractChart {
|
|
16372
|
-
sheetId;
|
|
16373
|
-
title;
|
|
16374
|
-
getters;
|
|
16375
|
-
constructor(definition, sheetId, getters) {
|
|
16376
|
-
this.title = definition.title;
|
|
16377
|
-
this.sheetId = sheetId;
|
|
16378
|
-
this.getters = getters;
|
|
16379
|
-
}
|
|
16380
|
-
/**
|
|
16381
|
-
* Validate the chart definition given as arguments. This function will be
|
|
16382
|
-
* called from allowDispatch function
|
|
16383
|
-
*/
|
|
16384
|
-
static validateChartDefinition(validator, definition) {
|
|
16385
|
-
throw new Error("This method should be implemented by sub class");
|
|
16386
|
-
}
|
|
16387
|
-
/**
|
|
16388
|
-
* Get a new chart definition transformed with the executed command. This
|
|
16389
|
-
* functions will be called during operational transform process
|
|
16390
|
-
*/
|
|
16391
|
-
static transformDefinition(definition, executed) {
|
|
16392
|
-
throw new Error("This method should be implemented by sub class");
|
|
16393
|
-
}
|
|
16394
|
-
/**
|
|
16395
|
-
* Get an empty definition based on the given context
|
|
16396
|
-
*/
|
|
16397
|
-
static getDefinitionFromContextCreation(context) {
|
|
16398
|
-
throw new Error("This method should be implemented by sub class");
|
|
16399
|
-
}
|
|
16400
|
-
}
|
|
16401
|
-
|
|
16402
|
-
function transformZone(zone, executed) {
|
|
16403
|
-
if (executed.type === "REMOVE_COLUMNS_ROWS") {
|
|
16404
|
-
return reduceZoneOnDeletion(zone, executed.dimension === "COL" ? "left" : "top", executed.elements);
|
|
16405
|
-
}
|
|
16406
|
-
if (executed.type === "ADD_COLUMNS_ROWS") {
|
|
16407
|
-
return expandZoneOnInsertion(zone, executed.dimension === "COL" ? "left" : "top", executed.base, executed.position, executed.quantity);
|
|
16408
|
-
}
|
|
16409
|
-
return { ...zone };
|
|
16410
|
-
}
|
|
16411
|
-
|
|
16412
|
-
/**
|
|
16413
|
-
* This file contains helpers that are common to different charts (mainly
|
|
16414
|
-
* line, bar and pie charts)
|
|
16415
|
-
*/
|
|
16416
|
-
/**
|
|
16417
|
-
* Adapt ranges of a chart which support DataSet (dataSets and LabelRange).
|
|
16418
|
-
*/
|
|
16419
|
-
function updateChartRangesWithDataSets(getters, applyChange, chartDataSets, chartLabelRange) {
|
|
16420
|
-
let isStale = false;
|
|
16421
|
-
const dataSetsWithUndefined = [];
|
|
16422
|
-
for (let index in chartDataSets) {
|
|
16423
|
-
let ds = chartDataSets[index];
|
|
16424
|
-
if (ds.labelCell) {
|
|
16425
|
-
const labelCell = adaptChartRange(ds.labelCell, applyChange);
|
|
16426
|
-
if (ds.labelCell !== labelCell) {
|
|
16427
|
-
isStale = true;
|
|
16428
|
-
ds = {
|
|
16429
|
-
...ds,
|
|
16430
|
-
labelCell: labelCell,
|
|
16431
|
-
};
|
|
16432
|
-
}
|
|
16433
|
-
}
|
|
16434
|
-
const dataRange = adaptChartRange(ds.dataRange, applyChange);
|
|
16435
|
-
if (dataRange === undefined ||
|
|
16436
|
-
getters.getRangeString(dataRange, dataRange.sheetId) === INCORRECT_RANGE_STRING) {
|
|
16437
|
-
isStale = true;
|
|
16438
|
-
ds = undefined;
|
|
16439
|
-
}
|
|
16440
|
-
else if (dataRange !== ds.dataRange) {
|
|
16441
|
-
isStale = true;
|
|
16442
|
-
ds = {
|
|
16443
|
-
...ds,
|
|
16444
|
-
dataRange,
|
|
16445
|
-
};
|
|
16446
|
-
}
|
|
16447
|
-
dataSetsWithUndefined[index] = ds;
|
|
16448
|
-
}
|
|
16449
|
-
let labelRange = chartLabelRange;
|
|
16450
|
-
const range = adaptChartRange(labelRange, applyChange);
|
|
16451
|
-
if (range !== labelRange) {
|
|
16452
|
-
isStale = true;
|
|
16453
|
-
labelRange = range;
|
|
16454
|
-
}
|
|
16455
|
-
const dataSets = dataSetsWithUndefined.filter(isDefined$1);
|
|
16456
|
-
return {
|
|
16457
|
-
isStale,
|
|
16458
|
-
dataSets,
|
|
16459
|
-
labelRange,
|
|
16460
|
-
};
|
|
16461
|
-
}
|
|
16462
|
-
/**
|
|
16463
|
-
* Copy the dataSets given. All the ranges which are on sheetIdFrom will target
|
|
16464
|
-
* sheetIdTo.
|
|
16465
|
-
*/
|
|
16466
|
-
function copyDataSetsWithNewSheetId(sheetIdFrom, sheetIdTo, dataSets) {
|
|
16467
|
-
return dataSets.map((ds) => {
|
|
16468
|
-
return {
|
|
16469
|
-
dataRange: copyRangeWithNewSheetId(sheetIdFrom, sheetIdTo, ds.dataRange),
|
|
16470
|
-
labelCell: ds.labelCell
|
|
16471
|
-
? copyRangeWithNewSheetId(sheetIdFrom, sheetIdTo, ds.labelCell)
|
|
16472
|
-
: undefined,
|
|
16473
|
-
};
|
|
16474
|
-
});
|
|
16475
|
-
}
|
|
16476
|
-
/**
|
|
16477
|
-
* Copy a range. If the range is on the sheetIdFrom, the range will target
|
|
16478
|
-
* sheetIdTo.
|
|
16479
|
-
*/
|
|
16480
|
-
function copyLabelRangeWithNewSheetId(sheetIdFrom, sheetIdTo, range) {
|
|
16481
|
-
return range ? copyRangeWithNewSheetId(sheetIdFrom, sheetIdTo, range) : undefined;
|
|
16482
|
-
}
|
|
16483
|
-
/**
|
|
16484
|
-
* Adapt a single range of a chart
|
|
16485
|
-
*/
|
|
16486
|
-
function adaptChartRange(range, applyChange) {
|
|
16487
|
-
if (!range) {
|
|
16488
|
-
return undefined;
|
|
16489
|
-
}
|
|
16490
|
-
const change = applyChange(range);
|
|
16491
|
-
switch (change.changeType) {
|
|
16492
|
-
case "NONE":
|
|
16493
|
-
return range;
|
|
16494
|
-
case "REMOVE":
|
|
16495
|
-
return undefined;
|
|
16496
|
-
default:
|
|
16497
|
-
return change.range;
|
|
16498
|
-
}
|
|
16499
|
-
}
|
|
16500
|
-
/**
|
|
16501
|
-
* Create the dataSet objects from xcs
|
|
16502
|
-
*/
|
|
16503
|
-
function createDataSets(getters, dataSetsString, sheetId, dataSetsHaveTitle) {
|
|
16504
|
-
const dataSets = [];
|
|
16505
|
-
for (const sheetXC of dataSetsString) {
|
|
16506
|
-
const dataRange = getters.getRangeFromSheetXC(sheetId, sheetXC);
|
|
16507
|
-
const { unboundedZone: zone, sheetId: dataSetSheetId, invalidSheetName, invalidXc } = dataRange;
|
|
16508
|
-
if (invalidSheetName || invalidXc) {
|
|
16509
|
-
continue;
|
|
16510
|
-
}
|
|
16511
|
-
// It's a rectangle. We treat all columns (arbitrary) as different data series.
|
|
16512
|
-
if (zone.left !== zone.right && zone.top !== zone.bottom) {
|
|
16513
|
-
if (zone.right === undefined) {
|
|
16514
|
-
// Should never happens because of the allowDispatch of charts, but just making sure
|
|
16515
|
-
continue;
|
|
16516
|
-
}
|
|
16517
|
-
for (let column = zone.left; column <= zone.right; column++) {
|
|
16518
|
-
const columnZone = {
|
|
16519
|
-
...zone,
|
|
16520
|
-
left: column,
|
|
16521
|
-
right: column,
|
|
16522
|
-
};
|
|
16523
|
-
dataSets.push(createDataSet(getters, dataSetSheetId, columnZone, dataSetsHaveTitle
|
|
16524
|
-
? {
|
|
16525
|
-
top: columnZone.top,
|
|
16526
|
-
bottom: columnZone.top,
|
|
16527
|
-
left: columnZone.left,
|
|
16528
|
-
right: columnZone.left,
|
|
16529
|
-
}
|
|
16530
|
-
: undefined));
|
|
16531
|
-
}
|
|
16532
|
-
}
|
|
16533
|
-
else if (zone.left === zone.right && zone.top === zone.bottom) {
|
|
16534
|
-
// A single cell. If it's only the title, the dataset is not added.
|
|
16535
|
-
if (!dataSetsHaveTitle) {
|
|
16536
|
-
dataSets.push(createDataSet(getters, dataSetSheetId, zone, undefined));
|
|
16537
|
-
}
|
|
16538
|
-
}
|
|
16539
|
-
else {
|
|
16540
|
-
/* 1 row or 1 column */
|
|
16541
|
-
dataSets.push(createDataSet(getters, dataSetSheetId, zone, dataSetsHaveTitle
|
|
16542
|
-
? {
|
|
16543
|
-
top: zone.top,
|
|
16544
|
-
bottom: zone.top,
|
|
16545
|
-
left: zone.left,
|
|
16546
|
-
right: zone.left,
|
|
16547
|
-
}
|
|
16548
|
-
: undefined));
|
|
16549
|
-
}
|
|
16550
|
-
}
|
|
16551
|
-
return dataSets;
|
|
16552
|
-
}
|
|
16553
|
-
function createDataSet(getters, sheetId, fullZone, titleZone) {
|
|
16554
|
-
if (fullZone.left !== fullZone.right && fullZone.top !== fullZone.bottom) {
|
|
16555
|
-
throw new Error(`Zone should be a single column or row: ${zoneToXc(fullZone)}`);
|
|
16556
|
-
}
|
|
16557
|
-
if (titleZone) {
|
|
16558
|
-
const dataXC = zoneToXc(fullZone);
|
|
16559
|
-
const labelCellXC = zoneToXc(titleZone);
|
|
16560
|
-
return {
|
|
16561
|
-
labelCell: getters.getRangeFromSheetXC(sheetId, labelCellXC),
|
|
16562
|
-
dataRange: getters.getRangeFromSheetXC(sheetId, dataXC),
|
|
16563
|
-
};
|
|
16564
|
-
}
|
|
16565
|
-
else {
|
|
16566
|
-
return {
|
|
16567
|
-
labelCell: undefined,
|
|
16568
|
-
dataRange: getters.getRangeFromSheetXC(sheetId, zoneToXc(fullZone)),
|
|
16569
|
-
};
|
|
16570
|
-
}
|
|
16571
|
-
}
|
|
16572
|
-
/**
|
|
16573
|
-
* Transform a dataSet to a ExcelDataSet
|
|
16574
|
-
*/
|
|
16575
|
-
function toExcelDataset(getters, ds) {
|
|
16576
|
-
const labelZone = ds.labelCell?.zone;
|
|
16577
|
-
let dataZone = ds.dataRange.zone;
|
|
16578
|
-
if (labelZone) {
|
|
16579
|
-
const { numberOfRows, numberOfCols } = zoneToDimension(dataZone);
|
|
16580
|
-
if (numberOfRows === 1) {
|
|
16581
|
-
dataZone = { ...dataZone, left: dataZone.left + 1 };
|
|
16582
|
-
}
|
|
16583
|
-
else if (numberOfCols === 1) {
|
|
16584
|
-
dataZone = { ...dataZone, top: dataZone.top + 1 };
|
|
16585
|
-
}
|
|
16586
|
-
}
|
|
16587
|
-
const dataRange = ds.dataRange.clone({ zone: dataZone });
|
|
16588
|
-
return {
|
|
16589
|
-
label: ds.labelCell ? getters.getRangeString(ds.labelCell, "forceSheetReference") : undefined,
|
|
16590
|
-
range: getters.getRangeString(dataRange, "forceSheetReference"),
|
|
16591
|
-
};
|
|
16592
|
-
}
|
|
16593
|
-
function toExcelLabelRange(getters, labelRange, shouldRemoveFirstLabel) {
|
|
16594
|
-
if (!labelRange)
|
|
16595
|
-
return undefined;
|
|
16596
|
-
let zone = {
|
|
16597
|
-
...labelRange.zone,
|
|
16598
|
-
};
|
|
16599
|
-
if (shouldRemoveFirstLabel && labelRange.zone.bottom > labelRange.zone.top) {
|
|
16600
|
-
zone.top = zone.top + 1;
|
|
16601
|
-
}
|
|
16602
|
-
const range = labelRange.clone({ zone });
|
|
16603
|
-
return getters.getRangeString(range, "forceSheetReference");
|
|
16604
|
-
}
|
|
16605
|
-
/**
|
|
16606
|
-
* Transform a chart definition which supports dataSets (dataSets and LabelRange)
|
|
16607
|
-
* with an executed command
|
|
16608
|
-
*/
|
|
16609
|
-
function transformChartDefinitionWithDataSetsWithZone(definition, executed) {
|
|
16610
|
-
let labelRange;
|
|
16611
|
-
if (definition.labelRange) {
|
|
16612
|
-
const labelZone = transformZone(toUnboundedZone(definition.labelRange), executed);
|
|
16613
|
-
labelRange = labelZone ? zoneToXc(labelZone) : undefined;
|
|
16614
|
-
}
|
|
16615
|
-
const dataSets = definition.dataSets
|
|
16616
|
-
.map(toUnboundedZone)
|
|
16617
|
-
.map((zone) => transformZone(zone, executed))
|
|
16618
|
-
.filter(isDefined$1)
|
|
16619
|
-
.map(zoneToXc);
|
|
16620
|
-
return {
|
|
16621
|
-
...definition,
|
|
16622
|
-
labelRange,
|
|
16623
|
-
dataSets,
|
|
16624
|
-
};
|
|
16625
|
-
}
|
|
16626
|
-
const GraphColors = [
|
|
16627
|
-
// the same colors as those used in odoo reporting
|
|
16628
|
-
"rgb(31,119,180)",
|
|
16629
|
-
"rgb(255,127,14)",
|
|
16630
|
-
"rgb(174,199,232)",
|
|
16631
|
-
"rgb(255,187,120)",
|
|
16632
|
-
"rgb(44,160,44)",
|
|
16633
|
-
"rgb(152,223,138)",
|
|
16634
|
-
"rgb(214,39,40)",
|
|
16635
|
-
"rgb(255,152,150)",
|
|
16636
|
-
"rgb(148,103,189)",
|
|
16637
|
-
"rgb(197,176,213)",
|
|
16638
|
-
"rgb(140,86,75)",
|
|
16639
|
-
"rgb(196,156,148)",
|
|
16640
|
-
"rgb(227,119,194)",
|
|
16641
|
-
"rgb(247,182,210)",
|
|
16642
|
-
"rgb(127,127,127)",
|
|
16643
|
-
"rgb(199,199,199)",
|
|
16644
|
-
"rgb(188,189,34)",
|
|
16645
|
-
"rgb(219,219,141)",
|
|
16646
|
-
"rgb(23,190,207)",
|
|
16647
|
-
"rgb(158,218,229)",
|
|
16648
|
-
];
|
|
16649
|
-
class ChartColors {
|
|
16650
|
-
graphColorIndex = 0;
|
|
16651
|
-
next() {
|
|
16652
|
-
return GraphColors[this.graphColorIndex++ % GraphColors.length];
|
|
16653
|
-
}
|
|
16654
|
-
}
|
|
16655
|
-
/**
|
|
16656
|
-
* Choose a font color based on a background color.
|
|
16657
|
-
* The font is white with a dark background.
|
|
16658
|
-
*/
|
|
16659
|
-
function chartFontColor(backgroundColor) {
|
|
16660
|
-
if (!backgroundColor) {
|
|
16661
|
-
return "#000000";
|
|
16662
|
-
}
|
|
16663
|
-
return relativeLuminance(backgroundColor) < 0.3 ? "#FFFFFF" : "#000000";
|
|
16664
|
-
}
|
|
16665
|
-
function checkDataset(definition) {
|
|
16666
|
-
if (definition.dataSets) {
|
|
16667
|
-
const invalidRanges = definition.dataSets.find((range) => !rangeReference.test(range)) !== undefined;
|
|
16668
|
-
if (invalidRanges) {
|
|
16669
|
-
return "InvalidDataSet" /* CommandResult.InvalidDataSet */;
|
|
16670
|
-
}
|
|
16671
|
-
const zones = definition.dataSets.map(toUnboundedZone);
|
|
16672
|
-
if (zones.some((zone) => zone.top !== zone.bottom && isFullRow(zone))) {
|
|
16673
|
-
return "InvalidDataSet" /* CommandResult.InvalidDataSet */;
|
|
16674
|
-
}
|
|
16675
|
-
}
|
|
16676
|
-
return "Success" /* CommandResult.Success */;
|
|
16677
|
-
}
|
|
16678
|
-
function checkLabelRange(definition) {
|
|
16679
|
-
if (definition.labelRange) {
|
|
16680
|
-
const invalidLabels = !rangeReference.test(definition.labelRange || "");
|
|
16681
|
-
if (invalidLabels) {
|
|
16682
|
-
return "InvalidLabelRange" /* CommandResult.InvalidLabelRange */;
|
|
16683
|
-
}
|
|
16684
|
-
}
|
|
16685
|
-
return "Success" /* CommandResult.Success */;
|
|
16686
|
-
}
|
|
16687
|
-
function shouldRemoveFirstLabel(labelRange, dataset, dataSetsHaveTitle) {
|
|
16688
|
-
if (!dataSetsHaveTitle)
|
|
16689
|
-
return false;
|
|
16690
|
-
if (!labelRange)
|
|
16691
|
-
return false;
|
|
16692
|
-
if (!dataset)
|
|
16693
|
-
return true;
|
|
16694
|
-
const datasetLength = getZoneArea(dataset.dataRange.zone);
|
|
16695
|
-
const labelLength = getZoneArea(labelRange.zone);
|
|
16696
|
-
if (labelLength < datasetLength) {
|
|
16697
|
-
return false;
|
|
16698
|
-
}
|
|
16699
|
-
return true;
|
|
16700
|
-
}
|
|
16701
|
-
// ---------------------------------------------------------------------------
|
|
16702
|
-
// Scorecard
|
|
16703
|
-
// ---------------------------------------------------------------------------
|
|
16704
|
-
function getBaselineText(baseline, keyValue, baselineMode, locale) {
|
|
16705
|
-
if (!baseline) {
|
|
16706
|
-
return "";
|
|
16707
|
-
}
|
|
16708
|
-
else if (baselineMode === "text" ||
|
|
16709
|
-
keyValue?.type !== CellValueType.number ||
|
|
16710
|
-
baseline.type !== CellValueType.number) {
|
|
16711
|
-
return baseline.formattedValue;
|
|
16712
|
-
}
|
|
16713
|
-
else {
|
|
16714
|
-
let diff = keyValue.value - baseline.value;
|
|
16715
|
-
if (baselineMode === "percentage" && diff !== 0) {
|
|
16716
|
-
diff = (diff / baseline.value) * 100;
|
|
16717
|
-
}
|
|
16718
|
-
if (baselineMode !== "percentage" && baseline.format) {
|
|
16719
|
-
return formatValue(diff, { format: baseline.format, locale });
|
|
16720
|
-
}
|
|
16721
|
-
const baselineStr = Math.abs(parseFloat(diff.toFixed(2))).toLocaleString();
|
|
16722
|
-
return baselineMode === "percentage" ? baselineStr + "%" : baselineStr;
|
|
16723
|
-
}
|
|
16724
|
-
}
|
|
16725
|
-
function getBaselineColor(baseline, baselineMode, keyValue, colorUp, colorDown) {
|
|
16726
|
-
if (baselineMode === "text" ||
|
|
16727
|
-
baseline?.type !== CellValueType.number ||
|
|
16728
|
-
keyValue?.type !== CellValueType.number) {
|
|
16729
|
-
return undefined;
|
|
16730
|
-
}
|
|
16731
|
-
const diff = keyValue.value - baseline.value;
|
|
16732
|
-
if (diff > 0) {
|
|
16733
|
-
return colorUp;
|
|
16734
|
-
}
|
|
16735
|
-
else if (diff < 0) {
|
|
16736
|
-
return colorDown;
|
|
16737
|
-
}
|
|
16738
|
-
return undefined;
|
|
16739
|
-
}
|
|
16740
|
-
function getBaselineArrowDirection(baseline, keyValue, baselineMode) {
|
|
16741
|
-
if (baselineMode === "text" ||
|
|
16742
|
-
baseline?.type !== CellValueType.number ||
|
|
16743
|
-
keyValue?.type !== CellValueType.number) {
|
|
16744
|
-
return "neutral";
|
|
16745
|
-
}
|
|
16746
|
-
const diff = keyValue.value - baseline.value;
|
|
16747
|
-
if (diff > 0) {
|
|
16748
|
-
return "up";
|
|
16749
|
-
}
|
|
16750
|
-
else if (diff < 0) {
|
|
16751
|
-
return "down";
|
|
16752
|
-
}
|
|
16753
|
-
return "neutral";
|
|
16754
|
-
}
|
|
16755
|
-
function getChartPositionAtCenterOfViewport(getters, chartSize) {
|
|
16756
|
-
const { x, y } = getters.getMainViewportCoordinates();
|
|
16757
|
-
const { scrollX, scrollY } = getters.getActiveSheetScrollInfo();
|
|
16758
|
-
const { width, height } = getters.getVisibleRect(getters.getActiveMainViewport());
|
|
16759
|
-
const position = {
|
|
16760
|
-
x: x + scrollX + Math.max(0, (width - chartSize.width) / 2),
|
|
16761
|
-
y: y + scrollY + Math.max(0, (height - chartSize.height) / 2),
|
|
16762
|
-
}; // Position at the center of the scrollable viewport
|
|
16763
|
-
return position;
|
|
16764
|
-
}
|
|
16765
|
-
|
|
16766
17409
|
const PasteInteractiveContent = {
|
|
16767
17410
|
wrongPasteSelection: _t("This operation is not allowed with multiple selections."),
|
|
16768
17411
|
willRemoveExistingMerge: _t("This operation is not possible due to a merge. Please remove the merges first than try again."),
|
|
16769
17412
|
wrongFigurePasteOption: _t("Cannot do a special paste of a figure."),
|
|
16770
|
-
frozenPaneOverlap: _t("
|
|
17413
|
+
frozenPaneOverlap: _t("This operation is not allowed due to an overlapping frozen pane."),
|
|
16771
17414
|
};
|
|
16772
17415
|
function handlePasteResult(env, result) {
|
|
16773
17416
|
if (!result.isSuccessful) {
|
|
@@ -17110,6 +17753,51 @@ function getFillingMode(index) {
|
|
|
17110
17753
|
return index - 1;
|
|
17111
17754
|
}
|
|
17112
17755
|
}
|
|
17756
|
+
function chartToImage(runtime, figure, type) {
|
|
17757
|
+
// wrap the canvas in a div with a fixed size because chart.js would
|
|
17758
|
+
// fill the whole page otherwise
|
|
17759
|
+
const div = document.createElement("div");
|
|
17760
|
+
div.style.width = `${figure.width}px`;
|
|
17761
|
+
div.style.height = `${figure.height}px`;
|
|
17762
|
+
const canvas = document.createElement("canvas");
|
|
17763
|
+
div.append(canvas);
|
|
17764
|
+
canvas.setAttribute("width", figure.width.toString());
|
|
17765
|
+
canvas.setAttribute("height", figure.height.toString());
|
|
17766
|
+
// we have to add the canvas to the DOM otherwise it won't be rendered
|
|
17767
|
+
document.body.append(div);
|
|
17768
|
+
if ("chartJsConfig" in runtime) {
|
|
17769
|
+
runtime.chartJsConfig.plugins = [backgroundColorChartJSPlugin];
|
|
17770
|
+
// @ts-ignore
|
|
17771
|
+
const chart = new window.Chart(canvas, runtime.chartJsConfig);
|
|
17772
|
+
const imgContent = chart.toBase64Image();
|
|
17773
|
+
chart.destroy();
|
|
17774
|
+
div.remove();
|
|
17775
|
+
return imgContent;
|
|
17776
|
+
}
|
|
17777
|
+
else if (type === "scorecard") {
|
|
17778
|
+
const design = getScorecardConfiguration(figure, runtime);
|
|
17779
|
+
drawScoreChart(design, canvas);
|
|
17780
|
+
const imgContent = canvas.toDataURL();
|
|
17781
|
+
div.remove();
|
|
17782
|
+
return imgContent;
|
|
17783
|
+
}
|
|
17784
|
+
return "";
|
|
17785
|
+
}
|
|
17786
|
+
/**
|
|
17787
|
+
* Custom chart.js plugin to set the background color of the canvas
|
|
17788
|
+
* https://github.com/chartjs/Chart.js/blob/8fdf76f8f02d31684d34704341a5d9217e977491/docs/configuration/canvas-background.md
|
|
17789
|
+
*/
|
|
17790
|
+
const backgroundColorChartJSPlugin = {
|
|
17791
|
+
id: "customCanvasBackgroundColor",
|
|
17792
|
+
beforeDraw: (chart) => {
|
|
17793
|
+
const { ctx } = chart;
|
|
17794
|
+
ctx.save();
|
|
17795
|
+
ctx.globalCompositeOperation = "destination-over";
|
|
17796
|
+
ctx.fillStyle = "#ffffff";
|
|
17797
|
+
ctx.fillRect(0, 0, chart.width, chart.height);
|
|
17798
|
+
ctx.restore();
|
|
17799
|
+
},
|
|
17800
|
+
};
|
|
17113
17801
|
|
|
17114
17802
|
class BarChart extends AbstractChart {
|
|
17115
17803
|
dataSets;
|
|
@@ -18180,172 +18868,6 @@ function createPieChartRuntime(chart, getters) {
|
|
|
18180
18868
|
return { chartJsConfig: config, background: chart.background || BACKGROUND_CHART_COLOR };
|
|
18181
18869
|
}
|
|
18182
18870
|
|
|
18183
|
-
function checkKeyValue(definition) {
|
|
18184
|
-
return definition.keyValue && !rangeReference.test(definition.keyValue)
|
|
18185
|
-
? "InvalidScorecardKeyValue" /* CommandResult.InvalidScorecardKeyValue */
|
|
18186
|
-
: "Success" /* CommandResult.Success */;
|
|
18187
|
-
}
|
|
18188
|
-
function checkBaseline(definition) {
|
|
18189
|
-
return definition.baseline && !rangeReference.test(definition.baseline)
|
|
18190
|
-
? "InvalidScorecardBaseline" /* CommandResult.InvalidScorecardBaseline */
|
|
18191
|
-
: "Success" /* CommandResult.Success */;
|
|
18192
|
-
}
|
|
18193
|
-
class ScorecardChart extends AbstractChart {
|
|
18194
|
-
keyValue;
|
|
18195
|
-
baseline;
|
|
18196
|
-
baselineMode;
|
|
18197
|
-
baselineDescr;
|
|
18198
|
-
background;
|
|
18199
|
-
baselineColorUp;
|
|
18200
|
-
baselineColorDown;
|
|
18201
|
-
fontColor;
|
|
18202
|
-
type = "scorecard";
|
|
18203
|
-
constructor(definition, sheetId, getters) {
|
|
18204
|
-
super(definition, sheetId, getters);
|
|
18205
|
-
this.keyValue = createRange(getters, sheetId, definition.keyValue);
|
|
18206
|
-
this.baseline = createRange(getters, sheetId, definition.baseline);
|
|
18207
|
-
this.baselineMode = definition.baselineMode;
|
|
18208
|
-
this.baselineDescr = definition.baselineDescr;
|
|
18209
|
-
this.background = definition.background;
|
|
18210
|
-
this.baselineColorUp = definition.baselineColorUp;
|
|
18211
|
-
this.baselineColorDown = definition.baselineColorDown;
|
|
18212
|
-
}
|
|
18213
|
-
static validateChartDefinition(validator, definition) {
|
|
18214
|
-
return validator.checkValidations(definition, checkKeyValue, checkBaseline);
|
|
18215
|
-
}
|
|
18216
|
-
static getDefinitionFromContextCreation(context) {
|
|
18217
|
-
return {
|
|
18218
|
-
background: context.background,
|
|
18219
|
-
type: "scorecard",
|
|
18220
|
-
keyValue: context.range ? context.range[0] : undefined,
|
|
18221
|
-
title: context.title || "",
|
|
18222
|
-
baselineMode: DEFAULT_SCORECARD_BASELINE_MODE,
|
|
18223
|
-
baselineColorUp: DEFAULT_SCORECARD_BASELINE_COLOR_UP,
|
|
18224
|
-
baselineColorDown: DEFAULT_SCORECARD_BASELINE_COLOR_DOWN,
|
|
18225
|
-
baseline: context.auxiliaryRange || "",
|
|
18226
|
-
};
|
|
18227
|
-
}
|
|
18228
|
-
static transformDefinition(definition, executed) {
|
|
18229
|
-
let baselineZone;
|
|
18230
|
-
let keyValueZone;
|
|
18231
|
-
if (definition.baseline) {
|
|
18232
|
-
baselineZone = transformZone(toUnboundedZone(definition.baseline), executed);
|
|
18233
|
-
}
|
|
18234
|
-
if (definition.keyValue) {
|
|
18235
|
-
keyValueZone = transformZone(toUnboundedZone(definition.keyValue), executed);
|
|
18236
|
-
}
|
|
18237
|
-
return {
|
|
18238
|
-
...definition,
|
|
18239
|
-
baseline: baselineZone ? zoneToXc(baselineZone) : undefined,
|
|
18240
|
-
keyValue: keyValueZone ? zoneToXc(keyValueZone) : undefined,
|
|
18241
|
-
};
|
|
18242
|
-
}
|
|
18243
|
-
copyForSheetId(sheetId) {
|
|
18244
|
-
const baseline = copyLabelRangeWithNewSheetId(this.sheetId, sheetId, this.baseline);
|
|
18245
|
-
const keyValue = copyLabelRangeWithNewSheetId(this.sheetId, sheetId, this.keyValue);
|
|
18246
|
-
const definition = this.getDefinitionWithSpecificRanges(baseline, keyValue, sheetId);
|
|
18247
|
-
return new ScorecardChart(definition, sheetId, this.getters);
|
|
18248
|
-
}
|
|
18249
|
-
copyInSheetId(sheetId) {
|
|
18250
|
-
const definition = this.getDefinitionWithSpecificRanges(this.baseline, this.keyValue, sheetId);
|
|
18251
|
-
return new ScorecardChart(definition, sheetId, this.getters);
|
|
18252
|
-
}
|
|
18253
|
-
getDefinition() {
|
|
18254
|
-
return this.getDefinitionWithSpecificRanges(this.baseline, this.keyValue);
|
|
18255
|
-
}
|
|
18256
|
-
getContextCreation() {
|
|
18257
|
-
return {
|
|
18258
|
-
background: this.background,
|
|
18259
|
-
title: this.title,
|
|
18260
|
-
range: this.keyValue ? [this.getters.getRangeString(this.keyValue, this.sheetId)] : undefined,
|
|
18261
|
-
auxiliaryRange: this.baseline
|
|
18262
|
-
? this.getters.getRangeString(this.baseline, this.sheetId)
|
|
18263
|
-
: undefined,
|
|
18264
|
-
};
|
|
18265
|
-
}
|
|
18266
|
-
getDefinitionWithSpecificRanges(baseline, keyValue, targetSheetId) {
|
|
18267
|
-
return {
|
|
18268
|
-
baselineColorDown: this.baselineColorDown,
|
|
18269
|
-
baselineColorUp: this.baselineColorUp,
|
|
18270
|
-
baselineMode: this.baselineMode,
|
|
18271
|
-
title: this.title,
|
|
18272
|
-
type: "scorecard",
|
|
18273
|
-
background: this.background,
|
|
18274
|
-
baseline: baseline
|
|
18275
|
-
? this.getters.getRangeString(baseline, targetSheetId || this.sheetId)
|
|
18276
|
-
: undefined,
|
|
18277
|
-
baselineDescr: this.baselineDescr,
|
|
18278
|
-
keyValue: keyValue
|
|
18279
|
-
? this.getters.getRangeString(keyValue, targetSheetId || this.sheetId)
|
|
18280
|
-
: undefined,
|
|
18281
|
-
};
|
|
18282
|
-
}
|
|
18283
|
-
getDefinitionForExcel() {
|
|
18284
|
-
// This kind of graph is not exportable in Excel
|
|
18285
|
-
return undefined;
|
|
18286
|
-
}
|
|
18287
|
-
updateRanges(applyChange) {
|
|
18288
|
-
const baseline = adaptChartRange(this.baseline, applyChange);
|
|
18289
|
-
const keyValue = adaptChartRange(this.keyValue, applyChange);
|
|
18290
|
-
if (this.baseline === baseline && this.keyValue === keyValue) {
|
|
18291
|
-
return this;
|
|
18292
|
-
}
|
|
18293
|
-
const definition = this.getDefinitionWithSpecificRanges(baseline, keyValue);
|
|
18294
|
-
return new ScorecardChart(definition, this.sheetId, this.getters);
|
|
18295
|
-
}
|
|
18296
|
-
}
|
|
18297
|
-
function createScorecardChartRuntime(chart, getters) {
|
|
18298
|
-
let keyValue = "";
|
|
18299
|
-
let formattedKeyValue = "";
|
|
18300
|
-
let keyValueCell;
|
|
18301
|
-
if (chart.keyValue) {
|
|
18302
|
-
const keyValuePosition = {
|
|
18303
|
-
sheetId: chart.keyValue.sheetId,
|
|
18304
|
-
col: chart.keyValue.zone.left,
|
|
18305
|
-
row: chart.keyValue.zone.top,
|
|
18306
|
-
};
|
|
18307
|
-
keyValueCell = getters.getEvaluatedCell(keyValuePosition);
|
|
18308
|
-
keyValue = String(keyValueCell.value);
|
|
18309
|
-
formattedKeyValue = keyValueCell.formattedValue;
|
|
18310
|
-
}
|
|
18311
|
-
let baselineCell;
|
|
18312
|
-
const baseline = chart.baseline;
|
|
18313
|
-
if (baseline) {
|
|
18314
|
-
const baselinePosition = {
|
|
18315
|
-
sheetId: chart.baseline.sheetId,
|
|
18316
|
-
col: chart.baseline.zone.left,
|
|
18317
|
-
row: chart.baseline.zone.top,
|
|
18318
|
-
};
|
|
18319
|
-
baselineCell = getters.getEvaluatedCell(baselinePosition);
|
|
18320
|
-
}
|
|
18321
|
-
const background = getters.getBackgroundOfSingleCellChart(chart.background, chart.keyValue);
|
|
18322
|
-
const locale = getters.getLocale();
|
|
18323
|
-
return {
|
|
18324
|
-
title: _t(chart.title),
|
|
18325
|
-
keyValue: formattedKeyValue || keyValue,
|
|
18326
|
-
baselineDisplay: getBaselineText(baselineCell, keyValueCell, chart.baselineMode, locale),
|
|
18327
|
-
baselineArrow: getBaselineArrowDirection(baselineCell, keyValueCell, chart.baselineMode),
|
|
18328
|
-
baselineColor: getBaselineColor(baselineCell, chart.baselineMode, keyValueCell, chart.baselineColorUp, chart.baselineColorDown),
|
|
18329
|
-
baselineDescr: chart.baselineDescr ? _t(chart.baselineDescr) : "",
|
|
18330
|
-
fontColor: chartFontColor(background),
|
|
18331
|
-
background,
|
|
18332
|
-
baselineStyle: chart.baselineMode !== "percentage" && baseline
|
|
18333
|
-
? getters.getCellStyle({
|
|
18334
|
-
sheetId: baseline.sheetId,
|
|
18335
|
-
col: baseline.zone.left,
|
|
18336
|
-
row: baseline.zone.top,
|
|
18337
|
-
})
|
|
18338
|
-
: undefined,
|
|
18339
|
-
keyValueStyle: chart.keyValue
|
|
18340
|
-
? getters.getCellStyle({
|
|
18341
|
-
sheetId: chart.keyValue.sheetId,
|
|
18342
|
-
col: chart.keyValue.zone.left,
|
|
18343
|
-
row: chart.keyValue.zone.top,
|
|
18344
|
-
})
|
|
18345
|
-
: undefined,
|
|
18346
|
-
};
|
|
18347
|
-
}
|
|
18348
|
-
|
|
18349
18871
|
/**
|
|
18350
18872
|
* This registry is intended to map a cell content (raw string) to
|
|
18351
18873
|
* an instance of a cell.
|
|
@@ -18383,11 +18905,11 @@ chartRegistry.add("pie", {
|
|
|
18383
18905
|
});
|
|
18384
18906
|
chartRegistry.add("scorecard", {
|
|
18385
18907
|
match: (type) => type === "scorecard",
|
|
18386
|
-
createChart: (definition, sheetId, getters) => new ScorecardChart(definition, sheetId, getters),
|
|
18908
|
+
createChart: (definition, sheetId, getters) => new ScorecardChart$1(definition, sheetId, getters),
|
|
18387
18909
|
getChartRuntime: createScorecardChartRuntime,
|
|
18388
|
-
validateChartDefinition: (validator, definition) => ScorecardChart.validateChartDefinition(validator, definition),
|
|
18389
|
-
transformDefinition: (definition, executed) => ScorecardChart.transformDefinition(definition, executed),
|
|
18390
|
-
getChartDefinitionFromContextCreation: (context) => ScorecardChart.getDefinitionFromContextCreation(context),
|
|
18910
|
+
validateChartDefinition: (validator, definition) => ScorecardChart$1.validateChartDefinition(validator, definition),
|
|
18911
|
+
transformDefinition: (definition, executed) => ScorecardChart$1.transformDefinition(definition, executed),
|
|
18912
|
+
getChartDefinitionFromContextCreation: (context) => ScorecardChart$1.getDefinitionFromContextCreation(context),
|
|
18391
18913
|
name: _t("Scorecard"),
|
|
18392
18914
|
sequence: 40,
|
|
18393
18915
|
});
|
|
@@ -18406,7 +18928,7 @@ chartComponentRegistry.add("line", ChartJsComponent);
|
|
|
18406
18928
|
chartComponentRegistry.add("bar", ChartJsComponent);
|
|
18407
18929
|
chartComponentRegistry.add("pie", ChartJsComponent);
|
|
18408
18930
|
chartComponentRegistry.add("gauge", ChartJsComponent);
|
|
18409
|
-
chartComponentRegistry.add("scorecard", ScorecardChart
|
|
18931
|
+
chartComponentRegistry.add("scorecard", ScorecardChart);
|
|
18410
18932
|
|
|
18411
18933
|
/**
|
|
18412
18934
|
* Registry intended to support usual currencies. It is mainly used to create
|
|
@@ -19148,6 +19670,7 @@ if (window.Chart) {
|
|
|
19148
19670
|
// Helpers
|
|
19149
19671
|
//------------------------------------------------------------------------------
|
|
19150
19672
|
function setFormatter(env, format) {
|
|
19673
|
+
env.model.dispatch("CANCEL_EDITION");
|
|
19151
19674
|
env.model.dispatch("SET_FORMATTING", {
|
|
19152
19675
|
sheetId: env.model.getters.getActiveSheetId(),
|
|
19153
19676
|
target: env.model.getters.getSelectedZones(),
|
|
@@ -20066,105 +20589,6 @@ cellMenuRegistry
|
|
|
20066
20589
|
separator: true,
|
|
20067
20590
|
});
|
|
20068
20591
|
|
|
20069
|
-
const SORT_TYPES = [
|
|
20070
|
-
CellValueType.number,
|
|
20071
|
-
CellValueType.error,
|
|
20072
|
-
CellValueType.text,
|
|
20073
|
-
CellValueType.boolean,
|
|
20074
|
-
];
|
|
20075
|
-
function sortCells(cells, sortDirection, emptyCellAsZero) {
|
|
20076
|
-
const cellsWithIndex = cells.map((cell, index) => ({
|
|
20077
|
-
index,
|
|
20078
|
-
type: cell.type,
|
|
20079
|
-
value: cell.value,
|
|
20080
|
-
}));
|
|
20081
|
-
let emptyCells = cellsWithIndex.filter((x) => x.type === CellValueType.empty);
|
|
20082
|
-
let nonEmptyCells = cellsWithIndex.filter((x) => x.type !== CellValueType.empty);
|
|
20083
|
-
if (emptyCellAsZero) {
|
|
20084
|
-
nonEmptyCells.push(...emptyCells.map((emptyCell) => ({ ...emptyCell, type: CellValueType.number, value: 0 })));
|
|
20085
|
-
emptyCells = [];
|
|
20086
|
-
}
|
|
20087
|
-
const inverse = sortDirection === "descending" ? -1 : 1;
|
|
20088
|
-
return nonEmptyCells
|
|
20089
|
-
.sort((left, right) => {
|
|
20090
|
-
let typeOrder = SORT_TYPES.indexOf(left.type) - SORT_TYPES.indexOf(right.type);
|
|
20091
|
-
if (typeOrder === 0) {
|
|
20092
|
-
if (left.type === CellValueType.text || left.type === CellValueType.error) {
|
|
20093
|
-
typeOrder = left.value.localeCompare(right.value);
|
|
20094
|
-
}
|
|
20095
|
-
else
|
|
20096
|
-
typeOrder = left.value - right.value;
|
|
20097
|
-
}
|
|
20098
|
-
return inverse * typeOrder;
|
|
20099
|
-
})
|
|
20100
|
-
.concat(emptyCells);
|
|
20101
|
-
}
|
|
20102
|
-
function interactiveSortSelection(env, sheetId, anchor, zone, sortDirection) {
|
|
20103
|
-
let result = DispatchResult.Success;
|
|
20104
|
-
//several columns => bypass the contiguity check
|
|
20105
|
-
let multiColumns = zone.right > zone.left;
|
|
20106
|
-
if (env.model.getters.doesIntersectMerge(sheetId, zone)) {
|
|
20107
|
-
multiColumns = false;
|
|
20108
|
-
let table;
|
|
20109
|
-
for (let row = zone.top; row <= zone.bottom; row++) {
|
|
20110
|
-
table = [];
|
|
20111
|
-
for (let col = zone.left; col <= zone.right; col++) {
|
|
20112
|
-
let merge = env.model.getters.getMerge({ sheetId, col, row });
|
|
20113
|
-
if (merge && !table.includes(merge.id.toString())) {
|
|
20114
|
-
table.push(merge.id.toString());
|
|
20115
|
-
}
|
|
20116
|
-
}
|
|
20117
|
-
if (table.length >= 2) {
|
|
20118
|
-
multiColumns = true;
|
|
20119
|
-
break;
|
|
20120
|
-
}
|
|
20121
|
-
}
|
|
20122
|
-
}
|
|
20123
|
-
const { col, row } = anchor;
|
|
20124
|
-
if (multiColumns) {
|
|
20125
|
-
result = env.model.dispatch("SORT_CELLS", { sheetId, col, row, zone, sortDirection });
|
|
20126
|
-
}
|
|
20127
|
-
else {
|
|
20128
|
-
// check contiguity
|
|
20129
|
-
const contiguousZone = env.model.getters.getContiguousZone(sheetId, zone);
|
|
20130
|
-
if (isEqual(contiguousZone, zone)) {
|
|
20131
|
-
// merge as it is
|
|
20132
|
-
result = env.model.dispatch("SORT_CELLS", {
|
|
20133
|
-
sheetId,
|
|
20134
|
-
col,
|
|
20135
|
-
row,
|
|
20136
|
-
zone,
|
|
20137
|
-
sortDirection,
|
|
20138
|
-
});
|
|
20139
|
-
}
|
|
20140
|
-
else {
|
|
20141
|
-
env.askConfirmation(_t("We found data next to your selection. Since this data was not selected, it will not be sorted. Do you want to extend your selection?"), () => {
|
|
20142
|
-
zone = contiguousZone;
|
|
20143
|
-
result = env.model.dispatch("SORT_CELLS", {
|
|
20144
|
-
sheetId,
|
|
20145
|
-
col,
|
|
20146
|
-
row,
|
|
20147
|
-
zone,
|
|
20148
|
-
sortDirection,
|
|
20149
|
-
});
|
|
20150
|
-
}, () => {
|
|
20151
|
-
result = env.model.dispatch("SORT_CELLS", {
|
|
20152
|
-
sheetId,
|
|
20153
|
-
col,
|
|
20154
|
-
row,
|
|
20155
|
-
zone,
|
|
20156
|
-
sortDirection,
|
|
20157
|
-
});
|
|
20158
|
-
});
|
|
20159
|
-
}
|
|
20160
|
-
}
|
|
20161
|
-
if (result.isCancelledBecause("InvalidSortZone" /* CommandResult.InvalidSortZone */)) {
|
|
20162
|
-
const { col, row } = anchor;
|
|
20163
|
-
env.model.selection.selectZone({ cell: { col, row }, zone });
|
|
20164
|
-
env.raiseError(_t("Cannot sort. To sort, select only cells or only merges that have the same size."));
|
|
20165
|
-
}
|
|
20166
|
-
}
|
|
20167
|
-
|
|
20168
20592
|
const AddFilterInteractiveContent = {
|
|
20169
20593
|
filterOverlap: _t("You cannot create overlapping filters."),
|
|
20170
20594
|
nonContinuousTargets: _t("A filter can only be created on a continuous selection."),
|
|
@@ -20259,109 +20683,126 @@ const splitToColumns = {
|
|
|
20259
20683
|
icon: "o-spreadsheet-Icon.SPLIT_TEXT",
|
|
20260
20684
|
};
|
|
20261
20685
|
|
|
20686
|
+
/**
|
|
20687
|
+
* Create a format action specification for a given format.
|
|
20688
|
+
* The format can be dynamically computed from the environment.
|
|
20689
|
+
*/
|
|
20690
|
+
function createFormatActionSpec({ name, format, descriptionValue, }) {
|
|
20691
|
+
const formatCallback = typeof format === "function" ? format : () => format;
|
|
20692
|
+
return {
|
|
20693
|
+
name,
|
|
20694
|
+
description: (env) => formatValue(descriptionValue, {
|
|
20695
|
+
format: formatCallback(env),
|
|
20696
|
+
locale: env.model.getters.getLocale(),
|
|
20697
|
+
}),
|
|
20698
|
+
execute: (env) => setFormatter(env, formatCallback(env)),
|
|
20699
|
+
isActive: (env) => isFormatSelected(env, formatCallback(env)),
|
|
20700
|
+
};
|
|
20701
|
+
}
|
|
20262
20702
|
const formatNumberAutomatic = {
|
|
20263
20703
|
name: _t("Automatic"),
|
|
20264
20704
|
execute: (env) => setFormatter(env, ""),
|
|
20265
20705
|
isActive: (env) => isAutomaticFormatSelected(env),
|
|
20266
20706
|
};
|
|
20267
|
-
const formatNumberNumber = {
|
|
20707
|
+
const formatNumberNumber = createFormatActionSpec({
|
|
20268
20708
|
name: _t("Number"),
|
|
20269
|
-
|
|
20270
|
-
|
|
20271
|
-
|
|
20272
|
-
}),
|
|
20273
|
-
execute: (env) => setFormatter(env, "#,##0.00"),
|
|
20274
|
-
isActive: (env) => isFormatSelected(env, "#,##0.00"),
|
|
20275
|
-
};
|
|
20709
|
+
descriptionValue: 1000.12,
|
|
20710
|
+
format: "#,##0.00",
|
|
20711
|
+
});
|
|
20276
20712
|
const formatPercent = {
|
|
20277
20713
|
name: _t("Format as percent"),
|
|
20278
20714
|
execute: FORMAT_PERCENT_ACTION,
|
|
20279
20715
|
icon: "o-spreadsheet-Icon.PERCENT",
|
|
20280
20716
|
};
|
|
20281
|
-
const formatNumberPercent = {
|
|
20717
|
+
const formatNumberPercent = createFormatActionSpec({
|
|
20282
20718
|
name: _t("Percent"),
|
|
20283
|
-
|
|
20284
|
-
|
|
20285
|
-
|
|
20286
|
-
|
|
20287
|
-
execute: FORMAT_PERCENT_ACTION,
|
|
20288
|
-
isActive: (env) => isFormatSelected(env, "0.00%"),
|
|
20289
|
-
};
|
|
20290
|
-
const formatNumberCurrency = {
|
|
20719
|
+
descriptionValue: 0.1012,
|
|
20720
|
+
format: "0.00%",
|
|
20721
|
+
});
|
|
20722
|
+
const formatNumberCurrency = createFormatActionSpec({
|
|
20291
20723
|
name: _t("Currency"),
|
|
20292
|
-
|
|
20293
|
-
|
|
20294
|
-
|
|
20295
|
-
}),
|
|
20296
|
-
execute: (env) => setFormatter(env, env.model.config.defaultCurrencyFormat),
|
|
20297
|
-
isActive: (env) => isFormatSelected(env, env.model.config.defaultCurrencyFormat),
|
|
20298
|
-
};
|
|
20724
|
+
descriptionValue: 1000.12,
|
|
20725
|
+
format: (env) => env.model.config.defaultCurrencyFormat,
|
|
20726
|
+
});
|
|
20299
20727
|
const formatNumberCurrencyRounded = {
|
|
20300
|
-
|
|
20301
|
-
|
|
20302
|
-
|
|
20303
|
-
|
|
20728
|
+
...createFormatActionSpec({
|
|
20729
|
+
name: _t("Currency rounded"),
|
|
20730
|
+
descriptionValue: 1000,
|
|
20731
|
+
format: (env) => roundFormat(env.model.config.defaultCurrencyFormat),
|
|
20304
20732
|
}),
|
|
20305
|
-
execute: (env) => setFormatter(env, roundFormat(env.model.config.defaultCurrencyFormat)),
|
|
20306
|
-
isActive: (env) => isFormatSelected(env, roundFormat(env.model.config.defaultCurrencyFormat)),
|
|
20307
20733
|
isVisible: (env) => {
|
|
20308
20734
|
const currencyFormat = env.model.config.defaultCurrencyFormat;
|
|
20309
20735
|
return currencyFormat !== roundFormat(currencyFormat);
|
|
20310
20736
|
},
|
|
20311
20737
|
};
|
|
20738
|
+
const EXAMPLE_DATE = parseLiteral("2023/09/26 10:43:00 PM", DEFAULT_LOCALE);
|
|
20312
20739
|
const formatCustomCurrency = {
|
|
20313
20740
|
name: _t("Custom currency"),
|
|
20314
20741
|
isVisible: (env) => env.loadCurrencies !== undefined,
|
|
20315
20742
|
execute: (env) => env.openSidePanel("CustomCurrency", {}),
|
|
20316
20743
|
};
|
|
20317
|
-
const formatNumberDate = {
|
|
20744
|
+
const formatNumberDate = createFormatActionSpec({
|
|
20318
20745
|
name: _t("Date"),
|
|
20319
|
-
|
|
20320
|
-
|
|
20321
|
-
|
|
20322
|
-
|
|
20323
|
-
locale,
|
|
20324
|
-
});
|
|
20325
|
-
},
|
|
20326
|
-
execute: (env) => setFormatter(env, env.model.getters.getLocale().dateFormat),
|
|
20327
|
-
isActive: (env) => isFormatSelected(env, env.model.getters.getLocale().dateFormat),
|
|
20328
|
-
};
|
|
20329
|
-
const formatNumberTime = {
|
|
20746
|
+
descriptionValue: EXAMPLE_DATE,
|
|
20747
|
+
format: (env) => env.model.getters.getLocale().dateFormat,
|
|
20748
|
+
});
|
|
20749
|
+
const formatNumberTime = createFormatActionSpec({
|
|
20330
20750
|
name: _t("Time"),
|
|
20331
|
-
|
|
20332
|
-
|
|
20333
|
-
|
|
20334
|
-
|
|
20335
|
-
locale,
|
|
20336
|
-
});
|
|
20337
|
-
},
|
|
20338
|
-
execute: (env) => setFormatter(env, env.model.getters.getLocale().timeFormat),
|
|
20339
|
-
isActive: (env) => isFormatSelected(env, env.model.getters.getLocale().timeFormat),
|
|
20340
|
-
};
|
|
20341
|
-
const formatNumberDateTime = {
|
|
20751
|
+
descriptionValue: EXAMPLE_DATE,
|
|
20752
|
+
format: (env) => env.model.getters.getLocale().timeFormat,
|
|
20753
|
+
});
|
|
20754
|
+
const formatNumberDateTime = createFormatActionSpec({
|
|
20342
20755
|
name: _t("Date time"),
|
|
20343
|
-
|
|
20344
|
-
|
|
20345
|
-
return formatValue(parseLiteral("9/26/2023 22:43:00", DEFAULT_LOCALE), {
|
|
20346
|
-
format: locale.dateFormat + " " + locale.timeFormat,
|
|
20347
|
-
locale,
|
|
20348
|
-
});
|
|
20349
|
-
},
|
|
20350
|
-
execute: (env) => {
|
|
20351
|
-
const locale = env.model.getters.getLocale();
|
|
20352
|
-
setFormatter(env, locale.dateFormat + " " + locale.timeFormat);
|
|
20353
|
-
},
|
|
20354
|
-
isActive: (env) => {
|
|
20756
|
+
descriptionValue: EXAMPLE_DATE,
|
|
20757
|
+
format: (env) => {
|
|
20355
20758
|
const locale = env.model.getters.getLocale();
|
|
20356
|
-
return
|
|
20759
|
+
return locale.dateFormat + " " + locale.timeFormat;
|
|
20357
20760
|
},
|
|
20358
|
-
};
|
|
20359
|
-
const formatNumberDuration = {
|
|
20761
|
+
});
|
|
20762
|
+
const formatNumberDuration = createFormatActionSpec({
|
|
20360
20763
|
name: _t("Duration"),
|
|
20361
|
-
|
|
20362
|
-
|
|
20363
|
-
|
|
20364
|
-
|
|
20764
|
+
descriptionValue: "27:51:38",
|
|
20765
|
+
format: "hhhh:mm:ss",
|
|
20766
|
+
});
|
|
20767
|
+
const moreFormats = {
|
|
20768
|
+
name: _t("More date formats"),
|
|
20769
|
+
execute: (env) => env.openSidePanel("MoreFormats", {}),
|
|
20770
|
+
};
|
|
20771
|
+
const formatNumberFullDateTime = createFormatActionSpec({
|
|
20772
|
+
name: _t("Full date time"),
|
|
20773
|
+
format: "dddd d mmmm yyyy hh:mm:ss a",
|
|
20774
|
+
descriptionValue: EXAMPLE_DATE,
|
|
20775
|
+
});
|
|
20776
|
+
const formatNumberFullWeekDayAndMonth = createFormatActionSpec({
|
|
20777
|
+
name: _t("Full week day and month"),
|
|
20778
|
+
format: "dddd d mmmm yyyy",
|
|
20779
|
+
descriptionValue: EXAMPLE_DATE,
|
|
20780
|
+
});
|
|
20781
|
+
const formatNumberDayAndFullMonth = createFormatActionSpec({
|
|
20782
|
+
name: _t("Day and full month"),
|
|
20783
|
+
format: "d mmmm yyyy",
|
|
20784
|
+
descriptionValue: EXAMPLE_DATE,
|
|
20785
|
+
});
|
|
20786
|
+
const formatNumberShortWeekDay = createFormatActionSpec({
|
|
20787
|
+
name: _t("Short week day"),
|
|
20788
|
+
format: "ddd d mmm yyyy",
|
|
20789
|
+
descriptionValue: EXAMPLE_DATE,
|
|
20790
|
+
});
|
|
20791
|
+
const formatNumberDayAndShortMonth = createFormatActionSpec({
|
|
20792
|
+
name: _t("Day and short month"),
|
|
20793
|
+
format: "d mmm yyyy",
|
|
20794
|
+
descriptionValue: EXAMPLE_DATE,
|
|
20795
|
+
});
|
|
20796
|
+
const formatNumberFullMonth = createFormatActionSpec({
|
|
20797
|
+
name: _t("Full month"),
|
|
20798
|
+
format: "mmmm yyyy",
|
|
20799
|
+
descriptionValue: EXAMPLE_DATE,
|
|
20800
|
+
});
|
|
20801
|
+
const formatNumberShortMonth = createFormatActionSpec({
|
|
20802
|
+
name: _t("Short month"),
|
|
20803
|
+
format: "mmm yyyy",
|
|
20804
|
+
descriptionValue: EXAMPLE_DATE,
|
|
20805
|
+
});
|
|
20365
20806
|
const incraseDecimalPlaces = {
|
|
20366
20807
|
name: _t("Increase decimal places"),
|
|
20367
20808
|
icon: "o-spreadsheet-Icon.INCREASE_DECIMAL",
|
|
@@ -20614,9 +21055,16 @@ var ACTION_FORMAT = /*#__PURE__*/Object.freeze({
|
|
|
20614
21055
|
formatNumberCurrencyRounded: formatNumberCurrencyRounded,
|
|
20615
21056
|
formatNumberDate: formatNumberDate,
|
|
20616
21057
|
formatNumberDateTime: formatNumberDateTime,
|
|
21058
|
+
formatNumberDayAndFullMonth: formatNumberDayAndFullMonth,
|
|
21059
|
+
formatNumberDayAndShortMonth: formatNumberDayAndShortMonth,
|
|
20617
21060
|
formatNumberDuration: formatNumberDuration,
|
|
21061
|
+
formatNumberFullDateTime: formatNumberFullDateTime,
|
|
21062
|
+
formatNumberFullMonth: formatNumberFullMonth,
|
|
21063
|
+
formatNumberFullWeekDayAndMonth: formatNumberFullWeekDayAndMonth,
|
|
20618
21064
|
formatNumberNumber: formatNumberNumber,
|
|
20619
21065
|
formatNumberPercent: formatNumberPercent,
|
|
21066
|
+
formatNumberShortMonth: formatNumberShortMonth,
|
|
21067
|
+
formatNumberShortWeekDay: formatNumberShortWeekDay,
|
|
20620
21068
|
formatNumberTime: formatNumberTime,
|
|
20621
21069
|
formatPercent: formatPercent,
|
|
20622
21070
|
formatStrikethrough: formatStrikethrough,
|
|
@@ -20627,6 +21075,7 @@ var ACTION_FORMAT = /*#__PURE__*/Object.freeze({
|
|
|
20627
21075
|
formatWrappingOverflow: formatWrappingOverflow,
|
|
20628
21076
|
formatWrappingWrap: formatWrappingWrap,
|
|
20629
21077
|
incraseDecimalPlaces: incraseDecimalPlaces,
|
|
21078
|
+
moreFormats: moreFormats,
|
|
20630
21079
|
textColor: textColor
|
|
20631
21080
|
});
|
|
20632
21081
|
|
|
@@ -21084,6 +21533,10 @@ numberFormatMenuRegistry
|
|
|
21084
21533
|
...formatNumberDuration,
|
|
21085
21534
|
sequence: 100,
|
|
21086
21535
|
separator: true,
|
|
21536
|
+
})
|
|
21537
|
+
.add("more_formats", {
|
|
21538
|
+
...moreFormats,
|
|
21539
|
+
sequence: 110,
|
|
21087
21540
|
});
|
|
21088
21541
|
const formatNumberMenuItemSpec = {
|
|
21089
21542
|
name: _t("More formats"),
|
|
@@ -22985,7 +23438,7 @@ class ChartPanel extends owl.Component {
|
|
|
22985
23438
|
});
|
|
22986
23439
|
}
|
|
22987
23440
|
canUpdateChart(figureId, updateDefinition) {
|
|
22988
|
-
if (figureId !== this.figureId) {
|
|
23441
|
+
if (figureId !== this.figureId || !this.env.model.getters.isChartDefined(figureId)) {
|
|
22989
23442
|
return;
|
|
22990
23443
|
}
|
|
22991
23444
|
const definition = {
|
|
@@ -24352,6 +24805,44 @@ FindAndReplacePanel.props = {
|
|
|
24352
24805
|
onCloseSidePanel: Function,
|
|
24353
24806
|
};
|
|
24354
24807
|
|
|
24808
|
+
css /* scss */ `
|
|
24809
|
+
.o-more-formats-panel {
|
|
24810
|
+
.format-preview {
|
|
24811
|
+
height: 48px;
|
|
24812
|
+
background-color: white;
|
|
24813
|
+
|
|
24814
|
+
&:hover {
|
|
24815
|
+
background-color: rgba(0, 0, 0, 0.08);
|
|
24816
|
+
}
|
|
24817
|
+
}
|
|
24818
|
+
.check-icon {
|
|
24819
|
+
width: 24px;
|
|
24820
|
+
}
|
|
24821
|
+
}
|
|
24822
|
+
`;
|
|
24823
|
+
const DATE_FORMAT_ACTIONS = createActions([
|
|
24824
|
+
formatNumberFullDateTime,
|
|
24825
|
+
formatNumberFullWeekDayAndMonth,
|
|
24826
|
+
formatNumberDayAndFullMonth,
|
|
24827
|
+
formatNumberShortWeekDay,
|
|
24828
|
+
formatNumberDayAndShortMonth,
|
|
24829
|
+
formatNumberFullMonth,
|
|
24830
|
+
formatNumberShortMonth,
|
|
24831
|
+
formatNumberDate,
|
|
24832
|
+
formatNumberTime,
|
|
24833
|
+
formatNumberDateTime,
|
|
24834
|
+
formatNumberDuration,
|
|
24835
|
+
]);
|
|
24836
|
+
class MoreFormatsPanel extends owl.Component {
|
|
24837
|
+
static template = "o-spreadsheet-MoreFormatsPanel";
|
|
24838
|
+
get dateFormatsActions() {
|
|
24839
|
+
return DATE_FORMAT_ACTIONS;
|
|
24840
|
+
}
|
|
24841
|
+
}
|
|
24842
|
+
MoreFormatsPanel.props = {
|
|
24843
|
+
onCloseSidePanel: Function,
|
|
24844
|
+
};
|
|
24845
|
+
|
|
24355
24846
|
css /* scss */ `
|
|
24356
24847
|
.o-checkbox-selection {
|
|
24357
24848
|
height: 150px;
|
|
@@ -25809,6 +26300,10 @@ sidePanelRegistry.add("DataValidation", {
|
|
|
25809
26300
|
title: _t("Data validation"),
|
|
25810
26301
|
Body: DataValidationPanel,
|
|
25811
26302
|
});
|
|
26303
|
+
sidePanelRegistry.add("MoreFormats", {
|
|
26304
|
+
title: _t("More date formats"),
|
|
26305
|
+
Body: MoreFormatsPanel,
|
|
26306
|
+
});
|
|
25812
26307
|
|
|
25813
26308
|
class TopBarComponentRegistry extends Registry {
|
|
25814
26309
|
mapping = {};
|
|
@@ -26276,7 +26771,6 @@ css /* scss */ `
|
|
|
26276
26771
|
border-top-right-radius: 4px;
|
|
26277
26772
|
font-size: ${DEFAULT_FONT_SIZE};
|
|
26278
26773
|
color: white;
|
|
26279
|
-
opacity: 0;
|
|
26280
26774
|
pointer-events: none;
|
|
26281
26775
|
}
|
|
26282
26776
|
`;
|
|
@@ -26296,7 +26790,6 @@ class ClientTag extends owl.Component {
|
|
|
26296
26790
|
left: `${x - 1}px`,
|
|
26297
26791
|
border: `1px solid ${color}`,
|
|
26298
26792
|
"background-color": color,
|
|
26299
|
-
opacity: this.props.active ? "opacity:1 !important" : undefined,
|
|
26300
26793
|
});
|
|
26301
26794
|
}
|
|
26302
26795
|
}
|
|
@@ -26308,16 +26801,31 @@ ClientTag.props = {
|
|
|
26308
26801
|
row: Number,
|
|
26309
26802
|
};
|
|
26310
26803
|
|
|
26804
|
+
function getHtmlContentFromPattern(pattern, value, highlightColor, className) {
|
|
26805
|
+
const pendingHtmlContent = [];
|
|
26806
|
+
pattern = pattern.toLowerCase();
|
|
26807
|
+
for (const patternChar of pattern) {
|
|
26808
|
+
const index = value.toLocaleLowerCase().indexOf(patternChar);
|
|
26809
|
+
if (index === -1) {
|
|
26810
|
+
continue;
|
|
26811
|
+
}
|
|
26812
|
+
pendingHtmlContent.push({ value: value.slice(0, index), color: "" }, { value: value[index], color: highlightColor, class: className });
|
|
26813
|
+
value = value.slice(index + 1);
|
|
26814
|
+
}
|
|
26815
|
+
pendingHtmlContent.push({ value });
|
|
26816
|
+
const htmlContent = pendingHtmlContent.filter((content) => content.value);
|
|
26817
|
+
return htmlContent;
|
|
26818
|
+
}
|
|
26819
|
+
|
|
26311
26820
|
css /* scss */ `
|
|
26312
26821
|
.o-autocomplete-dropdown {
|
|
26313
26822
|
pointer-events: auto;
|
|
26823
|
+
cursor: pointer;
|
|
26314
26824
|
background-color: #fff;
|
|
26315
26825
|
max-width: 400px;
|
|
26316
|
-
|
|
26317
|
-
background-color: #f2f2f2;
|
|
26318
|
-
}
|
|
26826
|
+
|
|
26319
26827
|
.o-autocomplete-value-focus {
|
|
26320
|
-
background-color:
|
|
26828
|
+
background-color: #f2f2f2;
|
|
26321
26829
|
}
|
|
26322
26830
|
|
|
26323
26831
|
& > div {
|
|
@@ -26335,7 +26843,9 @@ class TextValueProvider extends owl.Component {
|
|
|
26335
26843
|
TextValueProvider.props = {
|
|
26336
26844
|
values: Array,
|
|
26337
26845
|
selectedIndex: { type: Number, optional: true },
|
|
26846
|
+
getHtmlContent: Function,
|
|
26338
26847
|
onValueSelected: Function,
|
|
26848
|
+
onValueHovered: Function,
|
|
26339
26849
|
};
|
|
26340
26850
|
|
|
26341
26851
|
class ContentEditableHelper {
|
|
@@ -26684,8 +27194,8 @@ css /* scss */ `
|
|
|
26684
27194
|
.o-formula-assistant-focus {
|
|
26685
27195
|
div:first-child,
|
|
26686
27196
|
span {
|
|
26687
|
-
color:
|
|
26688
|
-
text-shadow: 0px 0px 1px
|
|
27197
|
+
color: ${COMPOSER_ASSISTANT_COLOR};
|
|
27198
|
+
text-shadow: 0px 0px 1px ${COMPOSER_ASSISTANT_COLOR};
|
|
26689
27199
|
}
|
|
26690
27200
|
div:last-child {
|
|
26691
27201
|
color: black;
|
|
@@ -26730,6 +27240,7 @@ FunctionDescriptionProvider.props = {
|
|
|
26730
27240
|
|
|
26731
27241
|
const functions = functionRegistry.content;
|
|
26732
27242
|
const ASSISTANT_WIDTH = 300;
|
|
27243
|
+
const AUTOCOMPLETE_ENTRIES = 10;
|
|
26733
27244
|
const selectionIndicatorClass = "selector-flag";
|
|
26734
27245
|
const selectionIndicatorColor = "#a9a9a9";
|
|
26735
27246
|
const selectionIndicator = "␣";
|
|
@@ -26779,6 +27290,13 @@ css /* scss */ `
|
|
|
26779
27290
|
position: absolute;
|
|
26780
27291
|
margin: 1px 4px;
|
|
26781
27292
|
pointer-events: none;
|
|
27293
|
+
|
|
27294
|
+
.o-semi-bold {
|
|
27295
|
+
/** FIXME: to remove in favor of Bootstrap
|
|
27296
|
+
* 'fw-semibold' when we upgrade to Bootstrap 5.2
|
|
27297
|
+
*/
|
|
27298
|
+
font-weight: 600 !important;
|
|
27299
|
+
}
|
|
26782
27300
|
}
|
|
26783
27301
|
}
|
|
26784
27302
|
`;
|
|
@@ -26799,6 +27317,7 @@ class Composer extends owl.Component {
|
|
|
26799
27317
|
values: [],
|
|
26800
27318
|
selectedIndex: undefined,
|
|
26801
27319
|
type: "function",
|
|
27320
|
+
getHtmlContent: () => [],
|
|
26802
27321
|
});
|
|
26803
27322
|
functionDescriptionState = owl.useState({
|
|
26804
27323
|
showDescription: false,
|
|
@@ -27041,24 +27560,27 @@ class Composer extends owl.Component {
|
|
|
27041
27560
|
text,
|
|
27042
27561
|
description,
|
|
27043
27562
|
};
|
|
27563
|
+
})
|
|
27564
|
+
.sort((a, b) => {
|
|
27565
|
+
return a.text.length - b.text.length || a.text.localeCompare(b.text);
|
|
27044
27566
|
});
|
|
27045
27567
|
if (searchTerm) {
|
|
27046
|
-
values = fuzzyLookup(searchTerm, values, (t) => t.text);
|
|
27047
|
-
}
|
|
27048
|
-
else {
|
|
27049
|
-
// alphabetical order
|
|
27050
|
-
values = values.sort((a, b) => a.text.localeCompare(b.text));
|
|
27568
|
+
values = fuzzyLookup(searchTerm, values, (t) => t.text).slice(0, AUTOCOMPLETE_ENTRIES);
|
|
27051
27569
|
}
|
|
27052
|
-
this.autoCompleteState.values = values.slice(0,
|
|
27570
|
+
this.autoCompleteState.values = values.slice(0, AUTOCOMPLETE_ENTRIES);
|
|
27571
|
+
this.autoCompleteState.getHtmlContent = (value) => getHtmlContentFromPattern(searchTerm, value, COMPOSER_ASSISTANT_COLOR, "o-semi-bold");
|
|
27053
27572
|
this.autoCompleteState.selectedIndex = 0;
|
|
27054
27573
|
}
|
|
27574
|
+
updateAutoCompleteIndex(index) {
|
|
27575
|
+
this.autoCompleteState.selectedIndex = clip(0, index, 10);
|
|
27576
|
+
}
|
|
27055
27577
|
/**
|
|
27056
27578
|
* This is required to ensure the content helper selection is
|
|
27057
27579
|
* properly updated on "onclick" events. Depending on the browser,
|
|
27058
27580
|
* the callback onClick from the composer will be executed before
|
|
27059
27581
|
* the selection was updated in the dom, which means we capture an
|
|
27060
27582
|
* wrong selection which is then forced upon the content helper on
|
|
27061
|
-
*
|
|
27583
|
+
* processContent.
|
|
27062
27584
|
*/
|
|
27063
27585
|
onMousedown(ev) {
|
|
27064
27586
|
if (ev.button > 0) {
|
|
@@ -27328,6 +27850,7 @@ class Composer extends owl.Component {
|
|
|
27328
27850
|
this.autoCompleteState.type = "dataValidation";
|
|
27329
27851
|
this.autoCompleteState.selectedIndex = undefined;
|
|
27330
27852
|
this.autoCompleteState.values = values.map((value) => ({ text: value, description: "" }));
|
|
27853
|
+
this.autoCompleteState.getHtmlContent = (value) => [{ value }];
|
|
27331
27854
|
}
|
|
27332
27855
|
}
|
|
27333
27856
|
Composer.props = {
|
|
@@ -28724,7 +29247,14 @@ class AbstractResizer extends owl.Component {
|
|
|
28724
29247
|
return;
|
|
28725
29248
|
}
|
|
28726
29249
|
if (this.state.waitingForMove === true) {
|
|
28727
|
-
this.
|
|
29250
|
+
if (!this.env.model.getters.isGridSelectionActive()) {
|
|
29251
|
+
this._selectElement(index, false);
|
|
29252
|
+
}
|
|
29253
|
+
else {
|
|
29254
|
+
// FIXME: Consider reintroducing this feature for all type of selection if we find
|
|
29255
|
+
// a way to have the grid selection follow the other selections evolution
|
|
29256
|
+
this.startMovement(ev);
|
|
29257
|
+
}
|
|
28728
29258
|
return;
|
|
28729
29259
|
}
|
|
28730
29260
|
if (this.env.model.getters.getEditionMode() === "editing") {
|
|
@@ -29805,6 +30335,8 @@ class Grid extends owl.Component {
|
|
|
29805
30335
|
const position = this.env.model.getters.getActivePosition();
|
|
29806
30336
|
this.env.model.selection.selectZone({ cell: position, zone: newZone });
|
|
29807
30337
|
},
|
|
30338
|
+
"CTRL+D": async () => this.env.model.dispatch("COPY_PASTE_CELLS_ABOVE"),
|
|
30339
|
+
"CTRL+R": async () => this.env.model.dispatch("COPY_PASTE_CELLS_ON_LEFT"),
|
|
29808
30340
|
"CTRL+SHIFT+E": () => this.setHorizontalAlign("center"),
|
|
29809
30341
|
"CTRL+SHIFT+L": () => this.setHorizontalAlign("left"),
|
|
29810
30342
|
"CTRL+SHIFT+R": () => this.setHorizontalAlign("right"),
|
|
@@ -32239,12 +32771,15 @@ function createXMLFile(doc, path, contentType) {
|
|
|
32239
32771
|
};
|
|
32240
32772
|
}
|
|
32241
32773
|
function xmlEscape(str) {
|
|
32242
|
-
return String(str)
|
|
32774
|
+
return (String(str)
|
|
32243
32775
|
.replace(/\&/g, "&")
|
|
32244
32776
|
.replace(/\</g, "<")
|
|
32245
32777
|
.replace(/\>/g, ">")
|
|
32246
32778
|
.replace(/\"/g, """)
|
|
32247
|
-
.replace(/\'/g, "'")
|
|
32779
|
+
.replace(/\'/g, "'")
|
|
32780
|
+
// Delete all ASCII control characters except for TAB (\x09), LF (\x0A) and CR (\x0D)
|
|
32781
|
+
// They are not valid at all in XML 1.0 (even escaped)
|
|
32782
|
+
.replace(/[\x00-\x08\x0B\x0C\x0E-\x1F]/g, ""));
|
|
32248
32783
|
}
|
|
32249
32784
|
function formatAttributes(attrs) {
|
|
32250
32785
|
return new XMLString(attrs.map(([key, val]) => `${key}="${xmlEscape(val)}"`).join(" "));
|
|
@@ -34972,10 +35507,11 @@ class CellPlugin extends CorePlugin {
|
|
|
34972
35507
|
* Reconstructs the original formula string based on a normalized form and its dependencies
|
|
34973
35508
|
*/
|
|
34974
35509
|
getFormulaCellContent(sheetId, cell, dependencies) {
|
|
34975
|
-
const ranges = dependencies ||
|
|
35510
|
+
const ranges = dependencies || cell.dependencies;
|
|
35511
|
+
let rangeIndex = 0;
|
|
34976
35512
|
return concat(cell.compiledFormula.tokens.map((token) => {
|
|
34977
35513
|
if (token.type === "REFERENCE") {
|
|
34978
|
-
const range = ranges
|
|
35514
|
+
const range = ranges[rangeIndex++];
|
|
34979
35515
|
return this.getters.getRangeString(range, sheetId);
|
|
34980
35516
|
}
|
|
34981
35517
|
return token.value;
|
|
@@ -35353,7 +35889,7 @@ class ChartPlugin extends CorePlugin {
|
|
|
35353
35889
|
export(data) {
|
|
35354
35890
|
if (data.sheets) {
|
|
35355
35891
|
for (let sheet of data.sheets) {
|
|
35356
|
-
// TODO This code is false, if two plugins want
|
|
35892
|
+
// TODO This code is false, if two plugins want to insert figures on the sheet, it will crash !
|
|
35357
35893
|
const sheetFigures = this.getters.getFigures(sheet.id);
|
|
35358
35894
|
const figures = [];
|
|
35359
35895
|
for (let sheetFigure of sheetFigures) {
|
|
@@ -35373,24 +35909,6 @@ class ChartPlugin extends CorePlugin {
|
|
|
35373
35909
|
}
|
|
35374
35910
|
}
|
|
35375
35911
|
}
|
|
35376
|
-
exportForExcel(data) {
|
|
35377
|
-
for (let sheet of data.sheets) {
|
|
35378
|
-
const sheetFigures = this.getters.getFigures(sheet.id);
|
|
35379
|
-
const figures = [];
|
|
35380
|
-
for (let figure of sheetFigures) {
|
|
35381
|
-
if (figure && figure.tag === "chart") {
|
|
35382
|
-
const figureData = this.charts[figure.id]?.getDefinitionForExcel();
|
|
35383
|
-
if (figureData) {
|
|
35384
|
-
figures.push({
|
|
35385
|
-
...figure,
|
|
35386
|
-
data: figureData,
|
|
35387
|
-
});
|
|
35388
|
-
}
|
|
35389
|
-
}
|
|
35390
|
-
}
|
|
35391
|
-
sheet.charts = figures;
|
|
35392
|
-
}
|
|
35393
|
-
}
|
|
35394
35912
|
// ---------------------------------------------------------------------------
|
|
35395
35913
|
// Private
|
|
35396
35914
|
// ---------------------------------------------------------------------------
|
|
@@ -36912,6 +37430,9 @@ class ImagePlugin extends CorePlugin {
|
|
|
36912
37430
|
}
|
|
36913
37431
|
exportForExcel(data) {
|
|
36914
37432
|
for (const sheet of data.sheets) {
|
|
37433
|
+
if (!sheet.images) {
|
|
37434
|
+
sheet.images = [];
|
|
37435
|
+
}
|
|
36915
37436
|
const figures = this.getters.getFigures(sheet.id);
|
|
36916
37437
|
const images = [];
|
|
36917
37438
|
for (const figure of figures) {
|
|
@@ -36925,7 +37446,7 @@ class ImagePlugin extends CorePlugin {
|
|
|
36925
37446
|
}
|
|
36926
37447
|
}
|
|
36927
37448
|
}
|
|
36928
|
-
sheet.images = images;
|
|
37449
|
+
sheet.images = [...sheet.images, ...images];
|
|
36929
37450
|
}
|
|
36930
37451
|
}
|
|
36931
37452
|
getAllImages() {
|
|
@@ -39189,7 +39710,7 @@ class CompilationParametersBuilder {
|
|
|
39189
39710
|
const evaluatedCell = this.getEvaluatedCell(position);
|
|
39190
39711
|
if (evaluatedCell.type === CellValueType.empty) {
|
|
39191
39712
|
const cell = this.getters.getCell(position);
|
|
39192
|
-
if (!cell || cell.content === "") {
|
|
39713
|
+
if (!cell || (!cell.isFormula && cell.content === "")) {
|
|
39193
39714
|
return undefined;
|
|
39194
39715
|
}
|
|
39195
39716
|
}
|
|
@@ -39295,9 +39816,9 @@ class FormulaDependencyGraph {
|
|
|
39295
39816
|
*/
|
|
39296
39817
|
getCellsDependingOn(positionIds) {
|
|
39297
39818
|
const visited = new JetSet();
|
|
39298
|
-
const queue = Array.from(positionIds);
|
|
39819
|
+
const queue = Array.from(positionIds).reverse();
|
|
39299
39820
|
while (queue.length > 0) {
|
|
39300
|
-
const node = queue.
|
|
39821
|
+
const node = queue.pop();
|
|
39301
39822
|
visited.add(node);
|
|
39302
39823
|
const adjacentNodes = this.inverseDependencies.get(node) || new Set();
|
|
39303
39824
|
for (const adjacentNode of adjacentNodes) {
|
|
@@ -39402,7 +39923,7 @@ class Evaluator {
|
|
|
39402
39923
|
constructor(context, getters) {
|
|
39403
39924
|
this.context = context;
|
|
39404
39925
|
this.getters = getters;
|
|
39405
|
-
this.compilationParams = buildCompilationParameters(context, getters,
|
|
39926
|
+
this.compilationParams = buildCompilationParameters(this.context, this.getters, this.computeAndSave.bind(this));
|
|
39406
39927
|
}
|
|
39407
39928
|
getEvaluatedCell(position) {
|
|
39408
39929
|
return (this.evaluatedCells.get(this.encodePosition(position)) ||
|
|
@@ -39430,7 +39951,7 @@ class Evaluator {
|
|
|
39430
39951
|
this.formulaDependencies().addDependencies(positionId, dependencies);
|
|
39431
39952
|
}
|
|
39432
39953
|
updateCompilationParameters() {
|
|
39433
|
-
this.compilationParams = buildCompilationParameters(this.context, this.getters,
|
|
39954
|
+
this.compilationParams = buildCompilationParameters(this.context, this.getters, this.computeAndSave.bind(this));
|
|
39434
39955
|
}
|
|
39435
39956
|
evaluateCells(positions) {
|
|
39436
39957
|
const cells = positions.map(this.encodePosition.bind(this));
|
|
@@ -39546,15 +40067,21 @@ class Evaluator {
|
|
|
39546
40067
|
this.cellsBeingComputed.delete(cellId);
|
|
39547
40068
|
}
|
|
39548
40069
|
}
|
|
40070
|
+
computeAndSave(position) {
|
|
40071
|
+
const positionId = this.encodePosition(position);
|
|
40072
|
+
const evaluatedCell = this.computeCell(positionId);
|
|
40073
|
+
if (!this.evaluatedCells.has(positionId)) {
|
|
40074
|
+
this.setEvaluatedCell(positionId, evaluatedCell);
|
|
40075
|
+
}
|
|
40076
|
+
return evaluatedCell;
|
|
40077
|
+
}
|
|
39549
40078
|
handleError(e, cell) {
|
|
39550
|
-
if (!(e instanceof
|
|
39551
|
-
e = new
|
|
40079
|
+
if (!(e instanceof EvaluationError)) {
|
|
40080
|
+
e = new EvaluationError(CellErrorType.GenericError, e.message);
|
|
39552
40081
|
}
|
|
39553
|
-
const msg = e?.errorType || CellErrorType.GenericError;
|
|
39554
|
-
// apply function name
|
|
39555
40082
|
const __lastFnCalled = this.compilationParams[2].__lastFnCalled || "";
|
|
39556
|
-
|
|
39557
|
-
return errorCell(
|
|
40083
|
+
e.message = e.message.replace("[[FUNCTION_NAME]]", __lastFnCalled);
|
|
40084
|
+
return errorCell(e);
|
|
39558
40085
|
}
|
|
39559
40086
|
computeFormulaCell(cellData) {
|
|
39560
40087
|
const cellId = cellData.id;
|
|
@@ -39862,6 +40389,7 @@ class EvaluationPlugin extends UIPlugin {
|
|
|
39862
40389
|
config;
|
|
39863
40390
|
static getters = [
|
|
39864
40391
|
"evaluateFormula",
|
|
40392
|
+
"getCorrespondingFormulaCell",
|
|
39865
40393
|
"getRangeFormattedValues",
|
|
39866
40394
|
"getRangeValues",
|
|
39867
40395
|
"getRangeFormats",
|
|
@@ -40258,6 +40786,47 @@ class EvaluationChartPlugin extends UIPlugin {
|
|
|
40258
40786
|
const style = this.getters.getCellComputedStyle({ sheetId, col, row });
|
|
40259
40787
|
return style.fillColor || BACKGROUND_CHART_COLOR;
|
|
40260
40788
|
}
|
|
40789
|
+
exportForExcel(data) {
|
|
40790
|
+
for (const sheet of data.sheets) {
|
|
40791
|
+
if (!sheet.images) {
|
|
40792
|
+
sheet.images = [];
|
|
40793
|
+
}
|
|
40794
|
+
const sheetFigures = this.getters.getFigures(sheet.id);
|
|
40795
|
+
const figures = [];
|
|
40796
|
+
for (const figure of sheetFigures) {
|
|
40797
|
+
if (!figure || figure.tag !== "chart") {
|
|
40798
|
+
continue;
|
|
40799
|
+
}
|
|
40800
|
+
const figureId = figure.id;
|
|
40801
|
+
const figureData = this.getters.getChart(figureId)?.getDefinitionForExcel();
|
|
40802
|
+
if (figureData) {
|
|
40803
|
+
figures.push({
|
|
40804
|
+
...figure,
|
|
40805
|
+
data: figureData,
|
|
40806
|
+
});
|
|
40807
|
+
}
|
|
40808
|
+
else {
|
|
40809
|
+
const chart = this.getters.getChart(figureId);
|
|
40810
|
+
if (!chart) {
|
|
40811
|
+
continue;
|
|
40812
|
+
}
|
|
40813
|
+
const type = this.getters.getChartType(figureId);
|
|
40814
|
+
const runtime = this.getters.getChartRuntime(figureId);
|
|
40815
|
+
const img = chartToImage(runtime, figure, type);
|
|
40816
|
+
sheet.images.push({
|
|
40817
|
+
...figure,
|
|
40818
|
+
tag: "image",
|
|
40819
|
+
data: {
|
|
40820
|
+
mimetype: "image/png",
|
|
40821
|
+
path: img,
|
|
40822
|
+
size: { width: figure.width, height: figure.height },
|
|
40823
|
+
},
|
|
40824
|
+
});
|
|
40825
|
+
}
|
|
40826
|
+
}
|
|
40827
|
+
sheet.charts = figures;
|
|
40828
|
+
}
|
|
40829
|
+
}
|
|
40261
40830
|
}
|
|
40262
40831
|
|
|
40263
40832
|
class EvaluationConditionalFormatPlugin extends UIPlugin {
|
|
@@ -40397,7 +40966,7 @@ class EvaluationConditionalFormatPlugin extends UIPlugin {
|
|
|
40397
40966
|
return percentile(rangeValues, Number(threshold.value) / 100, true);
|
|
40398
40967
|
case "formula":
|
|
40399
40968
|
const value = threshold.value && this.getters.evaluateFormula(sheetId, threshold.value);
|
|
40400
|
-
return
|
|
40969
|
+
return typeof value === "number" ? value : null;
|
|
40401
40970
|
default:
|
|
40402
40971
|
return null;
|
|
40403
40972
|
}
|
|
@@ -40523,10 +41092,13 @@ class EvaluationConditionalFormatPlugin extends UIPlugin {
|
|
|
40523
41092
|
}
|
|
40524
41093
|
const [value0, value1] = rule.values.map((val) => {
|
|
40525
41094
|
if (val.startsWith("=")) {
|
|
40526
|
-
return this.getters.evaluateFormula(target.sheetId, val);
|
|
41095
|
+
return this.getters.evaluateFormula(target.sheetId, val) ?? "";
|
|
40527
41096
|
}
|
|
40528
41097
|
return parseLiteral(val, DEFAULT_LOCALE);
|
|
40529
41098
|
});
|
|
41099
|
+
if (isMatrix(value0) || isMatrix(value1)) {
|
|
41100
|
+
return false;
|
|
41101
|
+
}
|
|
40530
41102
|
switch (rule.operator) {
|
|
40531
41103
|
case "IsEmpty":
|
|
40532
41104
|
return cell.value.toString().trim() === "";
|
|
@@ -40715,7 +41287,7 @@ class EvaluationDataValidationPlugin extends UIPlugin {
|
|
|
40715
41287
|
const formula = compile(value);
|
|
40716
41288
|
const translatedFormula = this.getters.getTranslatedCellFormula(sheetId, offset.col, offset.row, formula, formula.dependencies.map((d) => this.getters.getRangeFromSheetXC(sheetId, d)));
|
|
40717
41289
|
const evaluated = this.getters.evaluateFormula(sheetId, translatedFormula);
|
|
40718
|
-
return evaluated ? evaluated.toString() : "";
|
|
41290
|
+
return evaluated && !isMatrix(evaluated) ? evaluated.toString() : "";
|
|
40719
41291
|
}
|
|
40720
41292
|
catch (e) {
|
|
40721
41293
|
return e instanceof EvaluationError ? e.errorType : CellErrorType.GenericError;
|
|
@@ -40724,8 +41296,8 @@ class EvaluationDataValidationPlugin extends UIPlugin {
|
|
|
40724
41296
|
}
|
|
40725
41297
|
}
|
|
40726
41298
|
|
|
40727
|
-
class
|
|
40728
|
-
static getters = ["getRowSize"];
|
|
41299
|
+
class HeaderSizeUIPlugin extends UIPlugin {
|
|
41300
|
+
static getters = ["getRowSize", "getHeaderSize"];
|
|
40729
41301
|
tallestCellInRow = {};
|
|
40730
41302
|
ctx = document.createElement("canvas").getContext("2d");
|
|
40731
41303
|
handle(cmd) {
|
|
@@ -40804,6 +41376,11 @@ class UIRowSizePlugin extends UIPlugin {
|
|
|
40804
41376
|
this.tallestCellInRow[sheetId][row]?.size ??
|
|
40805
41377
|
DEFAULT_CELL_HEIGHT);
|
|
40806
41378
|
}
|
|
41379
|
+
getHeaderSize(sheetId, dimension, index) {
|
|
41380
|
+
return dimension === "ROW"
|
|
41381
|
+
? this.getRowSize(sheetId, index)
|
|
41382
|
+
: this.getters.getColSize(sheetId, index);
|
|
41383
|
+
}
|
|
40807
41384
|
updateRowSizeForCellChange(sheetId, row, col) {
|
|
40808
41385
|
const tallestCellInRow = this.tallestCellInRow[sheetId]?.[row];
|
|
40809
41386
|
if (tallestCellInRow?.cell.col === col) {
|
|
@@ -41874,7 +42451,7 @@ const transformations = [
|
|
|
41874
42451
|
{ match: isZoneDependent, fn: transformZoneDependentCommand },
|
|
41875
42452
|
{ match: isPositionDependent, fn: transformPosition },
|
|
41876
42453
|
{ match: isHeadersDependant, fn: transformHeaders },
|
|
41877
|
-
{ match: isRangeDependant, fn:
|
|
42454
|
+
{ match: isRangeDependant, fn: transformRangesDependentCommand },
|
|
41878
42455
|
];
|
|
41879
42456
|
/**
|
|
41880
42457
|
* Get the result of applying the operation transformations on the given command
|
|
@@ -41970,23 +42547,13 @@ function transformZoneDependentCommand(cmd, executed) {
|
|
|
41970
42547
|
}
|
|
41971
42548
|
return "IGNORE_COMMAND";
|
|
41972
42549
|
}
|
|
41973
|
-
function
|
|
42550
|
+
function transformRangesDependentCommand(toTransform, executed) {
|
|
41974
42551
|
if (!("sheetId" in executed)) {
|
|
41975
42552
|
return toTransform;
|
|
41976
42553
|
}
|
|
41977
|
-
const ranges =
|
|
41978
|
-
|
|
41979
|
-
|
|
41980
|
-
if (range._sheetId !== executed.sheetId) {
|
|
41981
|
-
ranges.push({ ...range, _zone: range._zone });
|
|
41982
|
-
}
|
|
41983
|
-
else {
|
|
41984
|
-
const newZone = transformZone(range._zone, executed);
|
|
41985
|
-
if (newZone && deletedSheet !== range._sheetId) {
|
|
41986
|
-
ranges.push({ ...range, _zone: newZone });
|
|
41987
|
-
}
|
|
41988
|
-
}
|
|
41989
|
-
}
|
|
42554
|
+
const ranges = toTransform.ranges
|
|
42555
|
+
.map((range) => transformRangeData(range, executed))
|
|
42556
|
+
.filter(isDefined$1);
|
|
41990
42557
|
if (!ranges.length) {
|
|
41991
42558
|
return "IGNORE_COMMAND";
|
|
41992
42559
|
}
|
|
@@ -43131,6 +43698,10 @@ class ClipboardCellsState extends ClipboardCellsAbstractState {
|
|
|
43131
43698
|
if (!newRangesXC) {
|
|
43132
43699
|
return;
|
|
43133
43700
|
}
|
|
43701
|
+
if (newRangesXC.length === 0) {
|
|
43702
|
+
this.dispatch("REMOVE_CONDITIONAL_FORMAT", { id: cf.id, sheetId });
|
|
43703
|
+
return;
|
|
43704
|
+
}
|
|
43134
43705
|
this.dispatch("ADD_CONDITIONAL_FORMAT", {
|
|
43135
43706
|
cf: {
|
|
43136
43707
|
id: cf.id,
|
|
@@ -44046,7 +44617,7 @@ class RendererPlugin extends UIPlugin {
|
|
|
44046
44617
|
}
|
|
44047
44618
|
}
|
|
44048
44619
|
drawTexts(renderingContext) {
|
|
44049
|
-
const { ctx
|
|
44620
|
+
const { ctx } = renderingContext;
|
|
44050
44621
|
ctx.textBaseline = "top";
|
|
44051
44622
|
let currentFont;
|
|
44052
44623
|
for (let box of this.boxes) {
|
|
@@ -44091,23 +44662,7 @@ class RendererPlugin extends UIPlugin {
|
|
|
44091
44662
|
// use the horizontal and the vertical start points to:
|
|
44092
44663
|
// fill text / fill strikethrough / fill underline
|
|
44093
44664
|
for (let brokenLine of box.content.textLines) {
|
|
44094
|
-
ctx
|
|
44095
|
-
if (style.strikethrough || style.underline) {
|
|
44096
|
-
const lineWidth = computeTextWidth(ctx, brokenLine, style);
|
|
44097
|
-
let _x = x;
|
|
44098
|
-
if (align === "right") {
|
|
44099
|
-
_x -= lineWidth;
|
|
44100
|
-
}
|
|
44101
|
-
else if (align === "center") {
|
|
44102
|
-
_x -= lineWidth / 2;
|
|
44103
|
-
}
|
|
44104
|
-
if (style.strikethrough) {
|
|
44105
|
-
ctx.fillRect(_x, y + textLineHeight / 2, lineWidth, 2.6 * thinLineWidth);
|
|
44106
|
-
}
|
|
44107
|
-
if (style.underline) {
|
|
44108
|
-
ctx.fillRect(_x, y + textLineHeight + 1, lineWidth, 1.3 * thinLineWidth);
|
|
44109
|
-
}
|
|
44110
|
-
}
|
|
44665
|
+
drawDecoratedText(ctx, brokenLine, { x: Math.round(x), y: Math.round(y) }, style.underline, style.strikethrough);
|
|
44111
44666
|
y += MIN_CELL_TEXT_MARGIN + textLineHeight;
|
|
44112
44667
|
}
|
|
44113
44668
|
if (box.clipRect) {
|
|
@@ -45231,9 +45786,6 @@ class SheetUIPlugin extends UIPlugin {
|
|
|
45231
45786
|
"getTextWidth",
|
|
45232
45787
|
"getCellText",
|
|
45233
45788
|
"getCellMultiLineText",
|
|
45234
|
-
"getColDimensions",
|
|
45235
|
-
"getRowDimensions",
|
|
45236
|
-
"getColRowOffset",
|
|
45237
45789
|
];
|
|
45238
45790
|
ctx = document.createElement("canvas").getContext("2d");
|
|
45239
45791
|
// ---------------------------------------------------------------------------
|
|
@@ -45318,55 +45870,6 @@ class SheetUIPlugin extends UIPlugin {
|
|
|
45318
45870
|
const text = this.getters.getCellText(position, this.getters.shouldShowFormulas());
|
|
45319
45871
|
return splitTextToWidth(this.ctx, text, style, width);
|
|
45320
45872
|
}
|
|
45321
|
-
/**
|
|
45322
|
-
* Returns the size, start and end coordinates of a column on an unfolded sheet
|
|
45323
|
-
*/
|
|
45324
|
-
getColDimensions(sheetId, col) {
|
|
45325
|
-
const start = this.getColRowOffset("COL", 0, col, sheetId);
|
|
45326
|
-
const size = this.getters.getColSize(sheetId, col);
|
|
45327
|
-
const isColHidden = this.getters.isColHidden(sheetId, col);
|
|
45328
|
-
return {
|
|
45329
|
-
start,
|
|
45330
|
-
size,
|
|
45331
|
-
end: start + (isColHidden ? 0 : size),
|
|
45332
|
-
};
|
|
45333
|
-
}
|
|
45334
|
-
/**
|
|
45335
|
-
* Returns the size, start and end coordinates of a row an unfolded sheet
|
|
45336
|
-
*/
|
|
45337
|
-
getRowDimensions(sheetId, row) {
|
|
45338
|
-
const start = this.getColRowOffset("ROW", 0, row, sheetId);
|
|
45339
|
-
const size = this.getters.getRowSize(sheetId, row);
|
|
45340
|
-
const isRowHidden = this.getters.isRowHidden(sheetId, row);
|
|
45341
|
-
return {
|
|
45342
|
-
start,
|
|
45343
|
-
size: size,
|
|
45344
|
-
end: start + (isRowHidden ? 0 : size),
|
|
45345
|
-
};
|
|
45346
|
-
}
|
|
45347
|
-
/**
|
|
45348
|
-
* Returns the offset of a header (determined by the dimension) at the given index
|
|
45349
|
-
* based on the referenceIndex given. If start === 0, this method will return
|
|
45350
|
-
* the start attribute of the header.
|
|
45351
|
-
*
|
|
45352
|
-
* i.e. The size from A to B is the distance between A.start and B.end
|
|
45353
|
-
*/
|
|
45354
|
-
getColRowOffset(dimension, referenceIndex, index, sheetId = this.getters.getActiveSheetId()) {
|
|
45355
|
-
if (index < referenceIndex) {
|
|
45356
|
-
return -this.getColRowOffset(dimension, index, referenceIndex);
|
|
45357
|
-
}
|
|
45358
|
-
let offset = 0;
|
|
45359
|
-
for (let i = referenceIndex; i < index; i++) {
|
|
45360
|
-
if (this.getters.isHeaderHidden(sheetId, dimension, i)) {
|
|
45361
|
-
continue;
|
|
45362
|
-
}
|
|
45363
|
-
offset +=
|
|
45364
|
-
dimension === "COL"
|
|
45365
|
-
? this.getters.getColSize(sheetId, i)
|
|
45366
|
-
: this.getters.getRowSize(sheetId, i);
|
|
45367
|
-
}
|
|
45368
|
-
return offset;
|
|
45369
|
-
}
|
|
45370
45873
|
doesCellHaveGridIcon(position) {
|
|
45371
45874
|
const isFilterHeader = this.getters.isFilterHeader(position);
|
|
45372
45875
|
const hasListIcon = !this.getters.isReadonly() && this.getters.cellHasListDataValidationIcon(position);
|
|
@@ -46192,6 +46695,20 @@ class ClipboardPlugin extends UIPlugin {
|
|
|
46192
46695
|
const state = new ClipboardOsState(cmd.text, this.getters, this.dispatch, this.selection);
|
|
46193
46696
|
return state.isPasteAllowed(cmd.target, { pasteOption: cmd.pasteOption });
|
|
46194
46697
|
}
|
|
46698
|
+
case "COPY_PASTE_CELLS_ABOVE": {
|
|
46699
|
+
const zones = this.getters.getSelectedZones();
|
|
46700
|
+
if (zones.length > 1 || (zones[0].top === 0 && zones[0].bottom === 0)) {
|
|
46701
|
+
return "InvalidCopyPasteSelection" /* CommandResult.InvalidCopyPasteSelection */;
|
|
46702
|
+
}
|
|
46703
|
+
break;
|
|
46704
|
+
}
|
|
46705
|
+
case "COPY_PASTE_CELLS_ON_LEFT": {
|
|
46706
|
+
const zones = this.getters.getSelectedZones();
|
|
46707
|
+
if (zones.length > 1 || (zones[0].left === 0 && zones[0].right === 0)) {
|
|
46708
|
+
return "InvalidCopyPasteSelection" /* CommandResult.InvalidCopyPasteSelection */;
|
|
46709
|
+
}
|
|
46710
|
+
break;
|
|
46711
|
+
}
|
|
46195
46712
|
case "INSERT_CELL": {
|
|
46196
46713
|
const { cut, paste } = this.getInsertCellsTargets(cmd.zone, cmd.shiftDimension);
|
|
46197
46714
|
const state = this.getClipboardStateForCopyCells(cut, "CUT");
|
|
@@ -46231,6 +46748,40 @@ class ClipboardPlugin extends UIPlugin {
|
|
|
46231
46748
|
this.status = "invisible";
|
|
46232
46749
|
}
|
|
46233
46750
|
break;
|
|
46751
|
+
case "COPY_PASTE_CELLS_ABOVE":
|
|
46752
|
+
{
|
|
46753
|
+
const zone = this.getters.getSelectedZone();
|
|
46754
|
+
const multipleRowsInSelection = zone.top !== zone.bottom;
|
|
46755
|
+
const copyTarget = {
|
|
46756
|
+
...zone,
|
|
46757
|
+
bottom: multipleRowsInSelection ? zone.top : zone.top - 1,
|
|
46758
|
+
top: multipleRowsInSelection ? zone.top : zone.top - 1,
|
|
46759
|
+
};
|
|
46760
|
+
const state = this.getClipboardStateForCopyCells([copyTarget], "COPY");
|
|
46761
|
+
state.paste([zone], {
|
|
46762
|
+
pasteOption: undefined,
|
|
46763
|
+
shouldPasteCF: true,
|
|
46764
|
+
selectTarget: true,
|
|
46765
|
+
});
|
|
46766
|
+
}
|
|
46767
|
+
break;
|
|
46768
|
+
case "COPY_PASTE_CELLS_ON_LEFT":
|
|
46769
|
+
{
|
|
46770
|
+
const zone = this.getters.getSelectedZone();
|
|
46771
|
+
const multipleColsInSelection = zone.left !== zone.right;
|
|
46772
|
+
const copyTarget = {
|
|
46773
|
+
...zone,
|
|
46774
|
+
right: multipleColsInSelection ? zone.left : zone.left - 1,
|
|
46775
|
+
left: multipleColsInSelection ? zone.left : zone.left - 1,
|
|
46776
|
+
};
|
|
46777
|
+
const state = this.getClipboardStateForCopyCells([copyTarget], "COPY");
|
|
46778
|
+
state.paste([zone], {
|
|
46779
|
+
pasteOption: undefined,
|
|
46780
|
+
shouldPasteCF: true,
|
|
46781
|
+
selectTarget: true,
|
|
46782
|
+
});
|
|
46783
|
+
}
|
|
46784
|
+
break;
|
|
46234
46785
|
case "CLEAN_CLIPBOARD_HIGHLIGHT":
|
|
46235
46786
|
this.status = "invisible";
|
|
46236
46787
|
break;
|
|
@@ -46910,7 +47461,15 @@ class EditionPlugin extends UIPlugin {
|
|
|
46910
47461
|
return cell?.content || "";
|
|
46911
47462
|
case CellValueType.number:
|
|
46912
47463
|
if (format && isDateTimeFormat(format)) {
|
|
46913
|
-
|
|
47464
|
+
if (parseDateTime(formattedValue, locale) !== null) {
|
|
47465
|
+
// formatted string can be parsed again
|
|
47466
|
+
return formattedValue;
|
|
47467
|
+
}
|
|
47468
|
+
// display a simplified and parsable string otherwise
|
|
47469
|
+
const timeFormat = Number.isInteger(value)
|
|
47470
|
+
? locale.dateFormat
|
|
47471
|
+
: locale.dateFormat + " " + locale.timeFormat;
|
|
47472
|
+
return formatValue(value, { locale, format: timeFormat });
|
|
46914
47473
|
}
|
|
46915
47474
|
return this.numberComposerContent(value, format, locale);
|
|
46916
47475
|
}
|
|
@@ -47176,6 +47735,9 @@ class EditionPlugin extends UIPlugin {
|
|
|
47176
47735
|
const cellValue = content.startsWith("=")
|
|
47177
47736
|
? this.getters.evaluateFormula(this.sheetId, content)
|
|
47178
47737
|
: parseLiteral(content, this.getters.getLocale());
|
|
47738
|
+
if (isMatrix(cellValue)) {
|
|
47739
|
+
return "Success" /* CommandResult.Success */;
|
|
47740
|
+
}
|
|
47179
47741
|
const validationResult = this.getters.getValidationResultForCellValue(cellValue, cellPosition);
|
|
47180
47742
|
if (!validationResult.isValid && validationResult.rule.isBlocking) {
|
|
47181
47743
|
return "BlockingValidationRule" /* CommandResult.BlockingValidationRule */;
|
|
@@ -47465,6 +48027,7 @@ class GridSelectionPlugin extends UIPlugin {
|
|
|
47465
48027
|
"isSingleColSelected",
|
|
47466
48028
|
"getElementsFromSelection",
|
|
47467
48029
|
"tryGetActiveSheetId",
|
|
48030
|
+
"isGridSelectionActive",
|
|
47468
48031
|
];
|
|
47469
48032
|
gridSelection = {
|
|
47470
48033
|
anchor: {
|
|
@@ -47550,10 +48113,7 @@ class GridSelectionPlugin extends UIPlugin {
|
|
|
47550
48113
|
switch (cmd.type) {
|
|
47551
48114
|
case "START":
|
|
47552
48115
|
const firstSheetId = this.getters.getVisibleSheetIds()[0];
|
|
47553
|
-
this.
|
|
47554
|
-
sheetIdTo: firstSheetId,
|
|
47555
|
-
sheetIdFrom: firstSheetId,
|
|
47556
|
-
});
|
|
48116
|
+
this.activateSheet(firstSheetId, firstSheetId);
|
|
47557
48117
|
const { col, row } = this.getters.getNextVisibleCellPosition({
|
|
47558
48118
|
sheetId: firstSheetId,
|
|
47559
48119
|
col: 0,
|
|
@@ -47566,25 +48126,7 @@ class GridSelectionPlugin extends UIPlugin {
|
|
|
47566
48126
|
this.moveClient({ sheetId: firstSheetId, col: 0, row: 0 });
|
|
47567
48127
|
break;
|
|
47568
48128
|
case "ACTIVATE_SHEET": {
|
|
47569
|
-
|
|
47570
|
-
this.dispatch("SHOW_SHEET", { sheetId: cmd.sheetIdTo });
|
|
47571
|
-
}
|
|
47572
|
-
this.setActiveSheet(cmd.sheetIdTo);
|
|
47573
|
-
this.sheetsData[cmd.sheetIdFrom] = {
|
|
47574
|
-
gridSelection: deepCopy(this.gridSelection),
|
|
47575
|
-
};
|
|
47576
|
-
if (cmd.sheetIdTo in this.sheetsData) {
|
|
47577
|
-
Object.assign(this, this.sheetsData[cmd.sheetIdTo]);
|
|
47578
|
-
this.selection.resetDefaultAnchor(this, deepCopy(this.gridSelection.anchor));
|
|
47579
|
-
}
|
|
47580
|
-
else {
|
|
47581
|
-
const { col, row } = this.getters.getNextVisibleCellPosition({
|
|
47582
|
-
sheetId: cmd.sheetIdTo,
|
|
47583
|
-
col: 0,
|
|
47584
|
-
row: 0,
|
|
47585
|
-
});
|
|
47586
|
-
this.selectCell(col, row);
|
|
47587
|
-
}
|
|
48129
|
+
this.activateSheet(cmd.sheetIdFrom, cmd.sheetIdTo);
|
|
47588
48130
|
break;
|
|
47589
48131
|
}
|
|
47590
48132
|
case "REMOVE_COLUMNS_ROWS": {
|
|
@@ -47674,6 +48216,9 @@ class GridSelectionPlugin extends UIPlugin {
|
|
|
47674
48216
|
// ---------------------------------------------------------------------------
|
|
47675
48217
|
// Getters
|
|
47676
48218
|
// ---------------------------------------------------------------------------
|
|
48219
|
+
isGridSelectionActive() {
|
|
48220
|
+
return this.selection.isListening(this);
|
|
48221
|
+
}
|
|
47677
48222
|
getActiveSheet() {
|
|
47678
48223
|
return this.activeSheet;
|
|
47679
48224
|
}
|
|
@@ -47758,11 +48303,19 @@ class GridSelectionPlugin extends UIPlugin {
|
|
|
47758
48303
|
}
|
|
47759
48304
|
}
|
|
47760
48305
|
getStatisticFnResults() {
|
|
47761
|
-
|
|
47762
|
-
const cells = new Set(
|
|
47763
|
-
|
|
47764
|
-
|
|
47765
|
-
|
|
48306
|
+
const sheetId = this.getters.getActiveSheetId();
|
|
48307
|
+
const cells = new Set();
|
|
48308
|
+
for (const zone of this.gridSelection.zones) {
|
|
48309
|
+
for (const { col, row } of positions(zone)) {
|
|
48310
|
+
if (this.getters.isRowHidden(sheetId, row) || this.getters.isColHidden(sheetId, col)) {
|
|
48311
|
+
continue; // Skip hidden cells
|
|
48312
|
+
}
|
|
48313
|
+
const evaluatedCell = this.getters.getEvaluatedCell({ sheetId, col, row });
|
|
48314
|
+
if (evaluatedCell.type !== CellValueType.empty) {
|
|
48315
|
+
cells.add(evaluatedCell);
|
|
48316
|
+
}
|
|
48317
|
+
}
|
|
48318
|
+
}
|
|
47766
48319
|
let cellsTypes = new Set();
|
|
47767
48320
|
let cellsValues = [];
|
|
47768
48321
|
for (let cell of cells) {
|
|
@@ -47840,6 +48393,27 @@ class GridSelectionPlugin extends UIPlugin {
|
|
|
47840
48393
|
// ---------------------------------------------------------------------------
|
|
47841
48394
|
// Other
|
|
47842
48395
|
// ---------------------------------------------------------------------------
|
|
48396
|
+
activateSheet(sheetIdFrom, sheetIdTo) {
|
|
48397
|
+
if (!this.getters.isSheetVisible(sheetIdTo)) {
|
|
48398
|
+
this.dispatch("SHOW_SHEET", { sheetId: sheetIdTo });
|
|
48399
|
+
}
|
|
48400
|
+
this.setActiveSheet(sheetIdTo);
|
|
48401
|
+
this.sheetsData[sheetIdFrom] = {
|
|
48402
|
+
gridSelection: deepCopy(this.gridSelection),
|
|
48403
|
+
};
|
|
48404
|
+
if (sheetIdTo in this.sheetsData) {
|
|
48405
|
+
Object.assign(this, this.sheetsData[sheetIdTo]);
|
|
48406
|
+
this.selection.resetDefaultAnchor(this, deepCopy(this.gridSelection.anchor));
|
|
48407
|
+
}
|
|
48408
|
+
else {
|
|
48409
|
+
const { col, row } = this.getters.getNextVisibleCellPosition({
|
|
48410
|
+
sheetId: sheetIdTo,
|
|
48411
|
+
col: 0,
|
|
48412
|
+
row: 0,
|
|
48413
|
+
});
|
|
48414
|
+
this.selectCell(col, row);
|
|
48415
|
+
}
|
|
48416
|
+
}
|
|
47843
48417
|
/**
|
|
47844
48418
|
* Ensure selections are not outside sheet boundaries.
|
|
47845
48419
|
* They are clipped to fit inside the sheet if needed.
|
|
@@ -47951,9 +48525,7 @@ class GridSelectionPlugin extends UIPlugin {
|
|
|
47951
48525
|
const toRemove = isBasedBefore ? cmd.elements.map((el) => el + thickness) : cmd.elements;
|
|
47952
48526
|
let currentIndex = cmd.base;
|
|
47953
48527
|
for (const element of toRemove) {
|
|
47954
|
-
const size = cmd.dimension
|
|
47955
|
-
? this.getters.getColSize(cmd.sheetId, element)
|
|
47956
|
-
: this.getters.getRowSize(cmd.sheetId, element);
|
|
48528
|
+
const size = this.getters.getHeaderSize(cmd.sheetId, cmd.dimension, element);
|
|
47957
48529
|
this.dispatch("RESIZE_COLUMNS_ROWS", {
|
|
47958
48530
|
dimension: cmd.dimension,
|
|
47959
48531
|
sheetId: cmd.sheetId,
|
|
@@ -48134,22 +48706,22 @@ class InternalViewport {
|
|
|
48134
48706
|
* visible cell.
|
|
48135
48707
|
* It returns -1 if no column is found.
|
|
48136
48708
|
*/
|
|
48137
|
-
getColIndex(x
|
|
48709
|
+
getColIndex(x) {
|
|
48138
48710
|
if (x < this.offsetCorrectionX || x > this.offsetCorrectionX + this.viewportWidth) {
|
|
48139
48711
|
return -1;
|
|
48140
48712
|
}
|
|
48141
|
-
return this.searchHeaderIndex("COL", x - this.offsetCorrectionX, this.left
|
|
48713
|
+
return this.searchHeaderIndex("COL", x - this.offsetCorrectionX, this.left);
|
|
48142
48714
|
}
|
|
48143
48715
|
/**
|
|
48144
48716
|
* Return the index of a row given an offset y, based on the pane top
|
|
48145
48717
|
* visible cell.
|
|
48146
48718
|
* It returns -1 if no row is found.
|
|
48147
48719
|
*/
|
|
48148
|
-
getRowIndex(y
|
|
48720
|
+
getRowIndex(y) {
|
|
48149
48721
|
if (y < this.offsetCorrectionY || y > this.offsetCorrectionY + this.viewportHeight) {
|
|
48150
48722
|
return -1;
|
|
48151
48723
|
}
|
|
48152
|
-
return this.searchHeaderIndex("ROW", y - this.offsetCorrectionY, this.top
|
|
48724
|
+
return this.searchHeaderIndex("ROW", y - this.offsetCorrectionY, this.top);
|
|
48153
48725
|
}
|
|
48154
48726
|
/**
|
|
48155
48727
|
* This function will make sure that the provided cell position (or current selected position) is part of
|
|
@@ -48180,7 +48752,7 @@ class InternalViewport {
|
|
|
48180
48752
|
finalTarget++;
|
|
48181
48753
|
}
|
|
48182
48754
|
const finalTargetEnd = this.getters.getColDimensions(sheetId, finalTarget).end;
|
|
48183
|
-
const startIndex = this.searchHeaderIndex("COL", finalTargetEnd - this.viewportWidth - this.offsetCorrectionX, this.boundaries.left
|
|
48755
|
+
const startIndex = this.searchHeaderIndex("COL", finalTargetEnd - this.viewportWidth - this.offsetCorrectionX, this.boundaries.left);
|
|
48184
48756
|
this.offsetX =
|
|
48185
48757
|
this.getters.getColDimensions(sheetId, startIndex).end - this.offsetCorrectionX;
|
|
48186
48758
|
this.offsetScrollbarX = this.offsetX;
|
|
@@ -48207,7 +48779,7 @@ class InternalViewport {
|
|
|
48207
48779
|
finalTarget++;
|
|
48208
48780
|
}
|
|
48209
48781
|
const finalTargetEnd = this.getters.getRowDimensions(sheetId, finalTarget).end;
|
|
48210
|
-
const startIndex = this.searchHeaderIndex("ROW", finalTargetEnd - this.viewportHeight - this.offsetCorrectionY, this.boundaries.top
|
|
48782
|
+
const startIndex = this.searchHeaderIndex("ROW", finalTargetEnd - this.viewportHeight - this.offsetCorrectionY, this.boundaries.top);
|
|
48211
48783
|
this.offsetY =
|
|
48212
48784
|
this.getters.getRowDimensions(sheetId, startIndex).end - this.offsetCorrectionY;
|
|
48213
48785
|
this.offsetScrollbarY = this.offsetY;
|
|
@@ -48263,21 +48835,15 @@ class InternalViewport {
|
|
|
48263
48835
|
!this.getters.isRowHidden(this.sheetId, row));
|
|
48264
48836
|
}
|
|
48265
48837
|
// PRIVATE
|
|
48266
|
-
searchHeaderIndex(dimension, position, startIndex = 0
|
|
48838
|
+
searchHeaderIndex(dimension, position, startIndex = 0) {
|
|
48267
48839
|
let size = 0;
|
|
48268
48840
|
const sheetId = this.sheetId;
|
|
48269
48841
|
const headers = this.getters.getNumberHeaders(sheetId, dimension);
|
|
48270
48842
|
for (let i = startIndex; i <= headers - 1; i++) {
|
|
48271
|
-
|
|
48272
|
-
? i < this.left && i > this.right
|
|
48273
|
-
: i < this.top && i > this.bottom;
|
|
48274
|
-
if (this.getters.isHeaderHidden(sheetId, dimension, i) || isHiddenInViewport) {
|
|
48843
|
+
if (this.getters.isHeaderHidden(sheetId, dimension, i)) {
|
|
48275
48844
|
continue;
|
|
48276
48845
|
}
|
|
48277
|
-
size +=
|
|
48278
|
-
dimension === "COL"
|
|
48279
|
-
? this.getters.getColSize(sheetId, i)
|
|
48280
|
-
: this.getters.getRowSize(sheetId, i);
|
|
48846
|
+
size += this.getters.getHeaderSize(sheetId, dimension, i);
|
|
48281
48847
|
if (size > position) {
|
|
48282
48848
|
return i;
|
|
48283
48849
|
}
|
|
@@ -48311,8 +48877,8 @@ class InternalViewport {
|
|
|
48311
48877
|
this.offsetScrollbarX = Math.max(0, viewportWidth - this.viewportWidth);
|
|
48312
48878
|
}
|
|
48313
48879
|
}
|
|
48314
|
-
this.left = this.getColIndex(this.offsetScrollbarX
|
|
48315
|
-
this.right = this.getColIndex(this.offsetScrollbarX + this.viewportWidth
|
|
48880
|
+
this.left = this.getColIndex(this.offsetScrollbarX);
|
|
48881
|
+
this.right = this.getColIndex(this.offsetScrollbarX + this.viewportWidth);
|
|
48316
48882
|
if (this.right === -1) {
|
|
48317
48883
|
this.right = this.boundaries.right;
|
|
48318
48884
|
}
|
|
@@ -48328,8 +48894,8 @@ class InternalViewport {
|
|
|
48328
48894
|
this.offsetScrollbarY = Math.max(0, paneHeight - this.viewportHeight);
|
|
48329
48895
|
}
|
|
48330
48896
|
}
|
|
48331
|
-
this.top = this.getRowIndex(this.offsetScrollbarY
|
|
48332
|
-
this.bottom = this.getRowIndex(this.offsetScrollbarY + this.viewportHeight
|
|
48897
|
+
this.top = this.getRowIndex(this.offsetScrollbarY);
|
|
48898
|
+
this.bottom = this.getRowIndex(this.offsetScrollbarY + this.viewportHeight);
|
|
48333
48899
|
if (this.bottom === -1) {
|
|
48334
48900
|
this.bottom = this.boundaries.bottom;
|
|
48335
48901
|
}
|
|
@@ -48444,6 +49010,7 @@ class SheetViewPlugin extends UIPlugin {
|
|
|
48444
49010
|
gridOffsetX = 0;
|
|
48445
49011
|
gridOffsetY = 0;
|
|
48446
49012
|
sheetsWithDirtyViewports = new Set();
|
|
49013
|
+
shouldAdjustViewports = false;
|
|
48447
49014
|
// ---------------------------------------------------------------------------
|
|
48448
49015
|
// Command Handling
|
|
48449
49016
|
// ---------------------------------------------------------------------------
|
|
@@ -48475,11 +49042,19 @@ class SheetViewPlugin extends UIPlugin {
|
|
|
48475
49042
|
const sheetId = this.getters.getActiveSheetId();
|
|
48476
49043
|
col = Math.min(col, this.getters.getNumberCols(sheetId) - 1);
|
|
48477
49044
|
row = Math.min(row, this.getters.getNumberRows(sheetId) - 1);
|
|
48478
|
-
|
|
49045
|
+
if (!this.sheetsWithDirtyViewports.has(sheetId)) {
|
|
49046
|
+
this.refreshViewport(this.getters.getActiveSheetId(), { col, row });
|
|
49047
|
+
}
|
|
48479
49048
|
}
|
|
48480
49049
|
}
|
|
48481
49050
|
handle(cmd) {
|
|
48482
49051
|
this.cleanViewports();
|
|
49052
|
+
// changing the evaluation can hide/show rows because of data filters
|
|
49053
|
+
if (invalidateEvaluationCommands.has(cmd.type)) {
|
|
49054
|
+
for (const sheetId of this.getters.getSheetIds()) {
|
|
49055
|
+
this.sheetsWithDirtyViewports.add(sheetId);
|
|
49056
|
+
}
|
|
49057
|
+
}
|
|
48483
49058
|
switch (cmd.type) {
|
|
48484
49059
|
case "START":
|
|
48485
49060
|
this.selection.observe(this, {
|
|
@@ -48489,7 +49064,10 @@ class SheetViewPlugin extends UIPlugin {
|
|
|
48489
49064
|
break;
|
|
48490
49065
|
case "UNDO":
|
|
48491
49066
|
case "REDO":
|
|
48492
|
-
this.
|
|
49067
|
+
for (const sheetId of this.getters.getSheetIds()) {
|
|
49068
|
+
this.sheetsWithDirtyViewports.add(sheetId);
|
|
49069
|
+
}
|
|
49070
|
+
this.shouldAdjustViewports = true;
|
|
48493
49071
|
break;
|
|
48494
49072
|
case "RESIZE_SHEETVIEW":
|
|
48495
49073
|
this.resizeSheetView(cmd.height, cmd.width, cmd.gridOffsetX, cmd.gridOffsetY);
|
|
@@ -48510,12 +49088,17 @@ class SheetViewPlugin extends UIPlugin {
|
|
|
48510
49088
|
this.shiftVertically(shiftedOffsetY);
|
|
48511
49089
|
break;
|
|
48512
49090
|
}
|
|
49091
|
+
case "REMOVE_FILTER_TABLE":
|
|
49092
|
+
case "UPDATE_FILTER":
|
|
49093
|
+
this.sheetsWithDirtyViewports.add(cmd.sheetId);
|
|
49094
|
+
break;
|
|
48513
49095
|
case "REMOVE_COLUMNS_ROWS":
|
|
48514
49096
|
case "RESIZE_COLUMNS_ROWS":
|
|
48515
49097
|
case "HIDE_COLUMNS_ROWS":
|
|
48516
49098
|
case "ADD_COLUMNS_ROWS":
|
|
48517
49099
|
case "UNHIDE_COLUMNS_ROWS":
|
|
48518
|
-
case "
|
|
49100
|
+
case "UNGROUP_HEADERS":
|
|
49101
|
+
case "GROUP_HEADERS":
|
|
48519
49102
|
case "FOLD_HEADER_GROUP":
|
|
48520
49103
|
case "UNFOLD_HEADER_GROUP":
|
|
48521
49104
|
case "FOLD_HEADER_GROUPS_IN_ZONE":
|
|
@@ -48529,12 +49112,13 @@ class SheetViewPlugin extends UIPlugin {
|
|
|
48529
49112
|
case "UPDATE_CELL":
|
|
48530
49113
|
// update cell content or format can change hidden rows because of data filters
|
|
48531
49114
|
if ("content" in cmd || "format" in cmd || cmd.style?.fontSize !== undefined) {
|
|
48532
|
-
this.
|
|
49115
|
+
for (const sheetId of this.getters.getSheetIds()) {
|
|
49116
|
+
this.sheetsWithDirtyViewports.add(sheetId);
|
|
49117
|
+
}
|
|
48533
49118
|
}
|
|
48534
49119
|
break;
|
|
48535
49120
|
case "ACTIVATE_SHEET":
|
|
48536
|
-
this.
|
|
48537
|
-
this.refreshViewport(cmd.sheetIdTo);
|
|
49121
|
+
this.sheetsWithDirtyViewports.add(cmd.sheetIdTo);
|
|
48538
49122
|
break;
|
|
48539
49123
|
case "UNFREEZE_ROWS":
|
|
48540
49124
|
case "UNFREEZE_COLUMNS":
|
|
@@ -48555,8 +49139,16 @@ class SheetViewPlugin extends UIPlugin {
|
|
|
48555
49139
|
finalize() {
|
|
48556
49140
|
for (const sheetId of this.sheetsWithDirtyViewports) {
|
|
48557
49141
|
this.resetViewports(sheetId);
|
|
49142
|
+
if (this.shouldAdjustViewports) {
|
|
49143
|
+
const position = this.getters.getSheetPosition(sheetId);
|
|
49144
|
+
const viewports = this.getSubViewports(sheetId);
|
|
49145
|
+
Object.values(viewports).forEach((viewport) => {
|
|
49146
|
+
viewport.adjustPosition(position);
|
|
49147
|
+
});
|
|
49148
|
+
}
|
|
48558
49149
|
}
|
|
48559
49150
|
this.sheetsWithDirtyViewports = new Set();
|
|
49151
|
+
this.shouldAdjustViewports = false;
|
|
48560
49152
|
this.setViewports();
|
|
48561
49153
|
}
|
|
48562
49154
|
setViewports() {
|
|
@@ -48674,10 +49266,7 @@ class SheetViewPlugin extends UIPlugin {
|
|
|
48674
49266
|
if (!visibleIndexes.includes(i)) {
|
|
48675
49267
|
continue;
|
|
48676
49268
|
}
|
|
48677
|
-
offset +=
|
|
48678
|
-
dimension === "COL"
|
|
48679
|
-
? this.getters.getColSize(sheetId, i)
|
|
48680
|
-
: this.getters.getRowSize(sheetId, i);
|
|
49269
|
+
offset += this.getters.getHeaderSize(sheetId, dimension, i);
|
|
48681
49270
|
}
|
|
48682
49271
|
return offset;
|
|
48683
49272
|
}
|
|
@@ -48850,16 +49439,6 @@ class SheetViewPlugin extends UIPlugin {
|
|
|
48850
49439
|
}
|
|
48851
49440
|
}
|
|
48852
49441
|
}
|
|
48853
|
-
resetSheetViews() {
|
|
48854
|
-
for (let sheetId of Object.keys(this.viewports)) {
|
|
48855
|
-
const position = this.getters.getSheetPosition(sheetId);
|
|
48856
|
-
this.resetViewports(sheetId);
|
|
48857
|
-
const viewports = this.getSubViewports(sheetId);
|
|
48858
|
-
Object.values(viewports).forEach((viewport) => {
|
|
48859
|
-
viewport.adjustPosition(position);
|
|
48860
|
-
});
|
|
48861
|
-
}
|
|
48862
|
-
}
|
|
48863
49442
|
resizeSheetView(height, width, gridOffsetX = 0, gridOffsetY = 0) {
|
|
48864
49443
|
this.sheetViewHeight = height;
|
|
48865
49444
|
this.sheetViewWidth = width;
|
|
@@ -48996,6 +49575,120 @@ class SheetViewPlugin extends UIPlugin {
|
|
|
48996
49575
|
}
|
|
48997
49576
|
}
|
|
48998
49577
|
|
|
49578
|
+
class HeaderPositionsUIPlugin extends UIPlugin {
|
|
49579
|
+
static getters = ["getColDimensions", "getRowDimensions", "getColRowOffset"];
|
|
49580
|
+
headerPositions = {};
|
|
49581
|
+
isDirty = true;
|
|
49582
|
+
handle(cmd) {
|
|
49583
|
+
if (invalidateEvaluationCommands.has(cmd.type)) {
|
|
49584
|
+
this.headerPositions = {};
|
|
49585
|
+
this.isDirty = true;
|
|
49586
|
+
}
|
|
49587
|
+
switch (cmd.type) {
|
|
49588
|
+
case "START":
|
|
49589
|
+
for (const sheetId of this.getters.getSheetIds()) {
|
|
49590
|
+
this.headerPositions[sheetId] = this.computeHeaderPositionsOfSheet(sheetId);
|
|
49591
|
+
}
|
|
49592
|
+
break;
|
|
49593
|
+
case "UPDATE_CELL":
|
|
49594
|
+
if ("content" in cmd || "format" in cmd || cmd.style?.fontSize !== undefined) {
|
|
49595
|
+
this.headerPositions = {};
|
|
49596
|
+
this.isDirty = true;
|
|
49597
|
+
}
|
|
49598
|
+
break;
|
|
49599
|
+
case "UPDATE_FILTER":
|
|
49600
|
+
case "REMOVE_FILTER_TABLE":
|
|
49601
|
+
this.headerPositions = {};
|
|
49602
|
+
this.isDirty = true;
|
|
49603
|
+
break;
|
|
49604
|
+
case "REMOVE_COLUMNS_ROWS":
|
|
49605
|
+
case "RESIZE_COLUMNS_ROWS":
|
|
49606
|
+
case "HIDE_COLUMNS_ROWS":
|
|
49607
|
+
case "ADD_COLUMNS_ROWS":
|
|
49608
|
+
case "UNHIDE_COLUMNS_ROWS":
|
|
49609
|
+
case "FOLD_HEADER_GROUP":
|
|
49610
|
+
case "UNFOLD_HEADER_GROUP":
|
|
49611
|
+
case "FOLD_HEADER_GROUPS_IN_ZONE":
|
|
49612
|
+
case "UNFOLD_HEADER_GROUPS_IN_ZONE":
|
|
49613
|
+
case "UNFOLD_ALL_HEADER_GROUPS":
|
|
49614
|
+
case "FOLD_ALL_HEADER_GROUPS":
|
|
49615
|
+
case "UNGROUP_HEADERS":
|
|
49616
|
+
case "GROUP_HEADERS":
|
|
49617
|
+
case "CREATE_SHEET":
|
|
49618
|
+
this.headerPositions[cmd.sheetId] = this.computeHeaderPositionsOfSheet(cmd.sheetId);
|
|
49619
|
+
break;
|
|
49620
|
+
case "DUPLICATE_SHEET":
|
|
49621
|
+
this.headerPositions[cmd.sheetIdTo] = deepCopy(this.headerPositions[cmd.sheetId]);
|
|
49622
|
+
break;
|
|
49623
|
+
}
|
|
49624
|
+
}
|
|
49625
|
+
finalize() {
|
|
49626
|
+
if (this.isDirty) {
|
|
49627
|
+
for (const sheetId of this.getters.getSheetIds()) {
|
|
49628
|
+
this.headerPositions[sheetId] = this.computeHeaderPositionsOfSheet(sheetId);
|
|
49629
|
+
}
|
|
49630
|
+
this.isDirty = false;
|
|
49631
|
+
}
|
|
49632
|
+
}
|
|
49633
|
+
/**
|
|
49634
|
+
* Returns the size, start and end coordinates of a column on an unfolded sheet
|
|
49635
|
+
*/
|
|
49636
|
+
getColDimensions(sheetId, col) {
|
|
49637
|
+
const start = this.headerPositions[sheetId]["COL"][col];
|
|
49638
|
+
const size = this.getters.getColSize(sheetId, col);
|
|
49639
|
+
const isColHidden = this.getters.isColHidden(sheetId, col);
|
|
49640
|
+
return {
|
|
49641
|
+
start,
|
|
49642
|
+
size,
|
|
49643
|
+
end: start + (isColHidden ? 0 : size),
|
|
49644
|
+
};
|
|
49645
|
+
}
|
|
49646
|
+
/**
|
|
49647
|
+
* Returns the size, start and end coordinates of a row an unfolded sheet
|
|
49648
|
+
*/
|
|
49649
|
+
getRowDimensions(sheetId, row) {
|
|
49650
|
+
const start = this.headerPositions[sheetId]["ROW"][row];
|
|
49651
|
+
const size = this.getters.getRowSize(sheetId, row);
|
|
49652
|
+
const isRowHidden = this.getters.isRowHidden(sheetId, row);
|
|
49653
|
+
return {
|
|
49654
|
+
start,
|
|
49655
|
+
size: size,
|
|
49656
|
+
end: start + (isRowHidden ? 0 : size),
|
|
49657
|
+
};
|
|
49658
|
+
}
|
|
49659
|
+
/**
|
|
49660
|
+
* Returns the offset of a header (determined by the dimension) at the given index
|
|
49661
|
+
* based on the referenceIndex given. If start === 0, this method will return
|
|
49662
|
+
* the start attribute of the header.
|
|
49663
|
+
*
|
|
49664
|
+
* i.e. The size from A to B is the distance between A.start and B.end
|
|
49665
|
+
*/
|
|
49666
|
+
getColRowOffset(dimension, referenceIndex, index, sheetId = this.getters.getActiveSheetId()) {
|
|
49667
|
+
const referencePosition = this.headerPositions[sheetId][dimension][referenceIndex];
|
|
49668
|
+
const position = this.headerPositions[sheetId][dimension][index];
|
|
49669
|
+
return position - referencePosition;
|
|
49670
|
+
}
|
|
49671
|
+
computeHeaderPositionsOfSheet(sheetId) {
|
|
49672
|
+
return {
|
|
49673
|
+
COL: this.computePositions(sheetId, "COL"),
|
|
49674
|
+
ROW: this.computePositions(sheetId, "ROW"),
|
|
49675
|
+
};
|
|
49676
|
+
}
|
|
49677
|
+
computePositions(sheetId, dimension) {
|
|
49678
|
+
const positions = {};
|
|
49679
|
+
let offset = 0;
|
|
49680
|
+
// loop on number of headers +1 so the position of (last header + 1) is the end of the sheet
|
|
49681
|
+
for (let i = 0; i < this.getters.getNumberHeaders(sheetId, dimension) + 1; i++) {
|
|
49682
|
+
positions[i] = offset;
|
|
49683
|
+
if (this.getters.isHeaderHidden(sheetId, dimension, i)) {
|
|
49684
|
+
continue;
|
|
49685
|
+
}
|
|
49686
|
+
offset += this.getters.getHeaderSize(sheetId, dimension, i);
|
|
49687
|
+
}
|
|
49688
|
+
return positions;
|
|
49689
|
+
}
|
|
49690
|
+
}
|
|
49691
|
+
|
|
48999
49692
|
const corePluginRegistry = new Registry()
|
|
49000
49693
|
.add("settings", SettingsPlugin)
|
|
49001
49694
|
.add("sheet", SheetPlugin)
|
|
@@ -49014,7 +49707,6 @@ const corePluginRegistry = new Registry()
|
|
|
49014
49707
|
// Plugins which handle a specific feature, without handling any core commands
|
|
49015
49708
|
const featurePluginRegistry = new Registry()
|
|
49016
49709
|
.add("ui_sheet", SheetUIPlugin)
|
|
49017
|
-
.add("header_visibility_ui", HeaderVisibilityUIPlugin)
|
|
49018
49710
|
.add("ui_options", UIOptionsPlugin)
|
|
49019
49711
|
.add("selectionInputManager", SelectionInputsManagerPlugin)
|
|
49020
49712
|
.add("highlight", HighlightPlugin)
|
|
@@ -49033,6 +49725,8 @@ const featurePluginRegistry = new Registry()
|
|
|
49033
49725
|
const statefulUIPluginRegistry = new Registry()
|
|
49034
49726
|
.add("selection", GridSelectionPlugin)
|
|
49035
49727
|
.add("evaluation_filter", FilterEvaluationPlugin)
|
|
49728
|
+
.add("header_visibility_ui", HeaderVisibilityUIPlugin)
|
|
49729
|
+
.add("header_positions", HeaderPositionsUIPlugin)
|
|
49036
49730
|
.add("viewport", SheetViewPlugin)
|
|
49037
49731
|
.add("clipboard", ClipboardPlugin)
|
|
49038
49732
|
.add("edition", EditionPlugin);
|
|
@@ -49041,7 +49735,7 @@ const coreViewsPluginRegistry = new Registry()
|
|
|
49041
49735
|
.add("evaluation", EvaluationPlugin)
|
|
49042
49736
|
.add("evaluation_chart", EvaluationChartPlugin)
|
|
49043
49737
|
.add("evaluation_cf", EvaluationConditionalFormatPlugin)
|
|
49044
|
-
.add("row_size",
|
|
49738
|
+
.add("row_size", HeaderSizeUIPlugin)
|
|
49045
49739
|
.add("custom_colors", CustomColorsPlugin)
|
|
49046
49740
|
.add("data_validation_ui", EvaluationDataValidationPlugin);
|
|
49047
49741
|
|
|
@@ -50403,6 +51097,7 @@ const BORDER_POSITIONS = [
|
|
|
50403
51097
|
css /* scss */ `
|
|
50404
51098
|
.o-border-selector {
|
|
50405
51099
|
padding: 4px;
|
|
51100
|
+
background-color: white;
|
|
50406
51101
|
}
|
|
50407
51102
|
.o-divider {
|
|
50408
51103
|
border-right: 1px solid #e0e2e4;
|
|
@@ -50763,6 +51458,7 @@ css /* scss */ `
|
|
|
50763
51458
|
line-height: 1.2;
|
|
50764
51459
|
font-size: 13px;
|
|
50765
51460
|
font-weight: 500;
|
|
51461
|
+
background-color: #fff;
|
|
50766
51462
|
|
|
50767
51463
|
.o-topbar-top {
|
|
50768
51464
|
border-bottom: 1px solid ${SEPARATOR_COLOR};
|
|
@@ -52625,6 +53321,9 @@ class SelectionStreamProcessorImpl {
|
|
|
52625
53321
|
},
|
|
52626
53322
|
});
|
|
52627
53323
|
}
|
|
53324
|
+
isListening(owner) {
|
|
53325
|
+
return this.stream.isListening(owner);
|
|
53326
|
+
}
|
|
52628
53327
|
/**
|
|
52629
53328
|
* Process a new anchor selection event. If the new anchor is inside
|
|
52630
53329
|
* the sheet boundaries, the event is pushed to the event stream to
|
|
@@ -55013,8 +55712,10 @@ const helpers = {
|
|
|
55013
55712
|
toString,
|
|
55014
55713
|
toXC,
|
|
55015
55714
|
toZone,
|
|
55715
|
+
toUnboundedZone,
|
|
55016
55716
|
toCartesian,
|
|
55017
55717
|
numberToLetters,
|
|
55718
|
+
lettersToNumber,
|
|
55018
55719
|
UuidGenerator,
|
|
55019
55720
|
formatValue,
|
|
55020
55721
|
createCurrencyFormat,
|
|
@@ -55036,6 +55737,7 @@ const helpers = {
|
|
|
55036
55737
|
genericRepeat,
|
|
55037
55738
|
createAction,
|
|
55038
55739
|
createActions,
|
|
55740
|
+
transformRangeData,
|
|
55039
55741
|
};
|
|
55040
55742
|
const links = {
|
|
55041
55743
|
isMarkdownLink,
|
|
@@ -55050,7 +55752,7 @@ const components = {
|
|
|
55050
55752
|
ChartJsComponent,
|
|
55051
55753
|
Grid,
|
|
55052
55754
|
GridOverlay,
|
|
55053
|
-
ScorecardChart
|
|
55755
|
+
ScorecardChart,
|
|
55054
55756
|
LineConfigPanel,
|
|
55055
55757
|
LineBarPieDesignPanel,
|
|
55056
55758
|
BarConfigPanel,
|
|
@@ -55061,6 +55763,7 @@ const components = {
|
|
|
55061
55763
|
ScorecardChartDesignPanel,
|
|
55062
55764
|
FigureComponent,
|
|
55063
55765
|
Menu,
|
|
55766
|
+
SelectionInput,
|
|
55064
55767
|
};
|
|
55065
55768
|
const hooks = {
|
|
55066
55769
|
useDragAndDropListItems,
|
|
@@ -55111,6 +55814,6 @@ exports.setTranslationMethod = setTranslationMethod;
|
|
|
55111
55814
|
exports.tokenize = tokenize;
|
|
55112
55815
|
|
|
55113
55816
|
|
|
55114
|
-
__info__.version = "
|
|
55115
|
-
__info__.date = "2023-10-
|
|
55116
|
-
__info__.hash = "
|
|
55817
|
+
__info__.version = "17.0.0";
|
|
55818
|
+
__info__.date = "2023-10-27T06:53:09.229Z";
|
|
55819
|
+
__info__.hash = "c0a34c2";
|