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