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