@odoo/o-spreadsheet 18.2.0-alpha.6 → 18.2.0-alpha.7
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 +1855 -1712
- package/dist/o-spreadsheet.d.ts +408 -384
- package/dist/o-spreadsheet.esm.js +1855 -1712
- package/dist/o-spreadsheet.iife.js +1852 -1709
- package/dist/o-spreadsheet.iife.min.js +328 -328
- package/dist/o_spreadsheet.xml +28 -28
- package/package.json +1 -1
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* This file is generated by o-spreadsheet build tools. Do not edit it.
|
|
4
4
|
* @see https://github.com/odoo/o-spreadsheet
|
|
5
|
-
* @version 18.2.0-alpha.
|
|
6
|
-
* @date 2025-02-
|
|
7
|
-
* @hash
|
|
5
|
+
* @version 18.2.0-alpha.7
|
|
6
|
+
* @date 2025-02-10T09:01:19.353Z
|
|
7
|
+
* @hash 0432f17
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
(function (exports, owl) {
|
|
@@ -770,9 +770,16 @@
|
|
|
770
770
|
}
|
|
771
771
|
return true;
|
|
772
772
|
}
|
|
773
|
-
/**
|
|
773
|
+
/**
|
|
774
|
+
* Check if the given array contains all the values of the other array.
|
|
775
|
+
* It makes the assumption that both array do not contain duplicates.
|
|
776
|
+
*/
|
|
774
777
|
function includesAll(arr, values) {
|
|
775
|
-
|
|
778
|
+
if (arr.length < values.length) {
|
|
779
|
+
return false;
|
|
780
|
+
}
|
|
781
|
+
const set = new Set(arr);
|
|
782
|
+
return values.every((value) => set.has(value));
|
|
776
783
|
}
|
|
777
784
|
/**
|
|
778
785
|
* Return an object with all the keys in the object that have a falsy value removed.
|
|
@@ -3646,10 +3653,8 @@
|
|
|
3646
3653
|
CommandResult["GaugeRangeMinNaN"] = "GaugeRangeMinNaN";
|
|
3647
3654
|
CommandResult["EmptyGaugeRangeMax"] = "EmptyGaugeRangeMax";
|
|
3648
3655
|
CommandResult["GaugeRangeMaxNaN"] = "GaugeRangeMaxNaN";
|
|
3649
|
-
CommandResult["GaugeRangeMinBiggerThanRangeMax"] = "GaugeRangeMinBiggerThanRangeMax";
|
|
3650
3656
|
CommandResult["GaugeLowerInflectionPointNaN"] = "GaugeLowerInflectionPointNaN";
|
|
3651
3657
|
CommandResult["GaugeUpperInflectionPointNaN"] = "GaugeUpperInflectionPointNaN";
|
|
3652
|
-
CommandResult["GaugeLowerBiggerThanUpper"] = "GaugeLowerBiggerThanUpper";
|
|
3653
3658
|
CommandResult["InvalidAutofillSelection"] = "InvalidAutofillSelection";
|
|
3654
3659
|
CommandResult["MinBiggerThanMax"] = "MinBiggerThanMax";
|
|
3655
3660
|
CommandResult["LowerBiggerThanUpper"] = "LowerBiggerThanUpper";
|
|
@@ -27949,7 +27954,6 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
27949
27954
|
["GaugeRangeMinNaN" /* CommandResult.GaugeRangeMinNaN */]: _t("The minimum range limit value must be a number"),
|
|
27950
27955
|
["EmptyGaugeRangeMax" /* CommandResult.EmptyGaugeRangeMax */]: _t("A maximum range limit value is needed"),
|
|
27951
27956
|
["GaugeRangeMaxNaN" /* CommandResult.GaugeRangeMaxNaN */]: _t("The maximum range limit value must be a number"),
|
|
27952
|
-
["GaugeRangeMinBiggerThanRangeMax" /* CommandResult.GaugeRangeMinBiggerThanRangeMax */]: _t("Minimum range limit must be smaller than maximum range limit"),
|
|
27953
27957
|
["GaugeLowerInflectionPointNaN" /* CommandResult.GaugeLowerInflectionPointNaN */]: _t("The lower inflection point value must be a number"),
|
|
27954
27958
|
["GaugeUpperInflectionPointNaN" /* CommandResult.GaugeUpperInflectionPointNaN */]: _t("The upper inflection point value must be a number"),
|
|
27955
27959
|
},
|
|
@@ -29771,6 +29775,8 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
29771
29775
|
const { locale, axisFormats } = args;
|
|
29772
29776
|
const format = axisFormats?.y || axisFormats?.y1;
|
|
29773
29777
|
return {
|
|
29778
|
+
enabled: false,
|
|
29779
|
+
external: customTooltipHandler,
|
|
29774
29780
|
filter: function (tooltipItem) {
|
|
29775
29781
|
return tooltipItem.raw.value !== undefined;
|
|
29776
29782
|
},
|
|
@@ -30224,14 +30230,6 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
30224
30230
|
return "Success" /* CommandResult.Success */;
|
|
30225
30231
|
});
|
|
30226
30232
|
}
|
|
30227
|
-
function checkRangeMinBiggerThanRangeMax(definition) {
|
|
30228
|
-
if (definition.sectionRule) {
|
|
30229
|
-
if (Number(definition.sectionRule.rangeMin) >= Number(definition.sectionRule.rangeMax)) {
|
|
30230
|
-
return "GaugeRangeMinBiggerThanRangeMax" /* CommandResult.GaugeRangeMinBiggerThanRangeMax */;
|
|
30231
|
-
}
|
|
30232
|
-
}
|
|
30233
|
-
return "Success" /* CommandResult.Success */;
|
|
30234
|
-
}
|
|
30235
30233
|
function checkEmpty(value, valueName) {
|
|
30236
30234
|
if (value === "") {
|
|
30237
30235
|
switch (valueName) {
|
|
@@ -30243,7 +30241,10 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
30243
30241
|
}
|
|
30244
30242
|
return "Success" /* CommandResult.Success */;
|
|
30245
30243
|
}
|
|
30246
|
-
function
|
|
30244
|
+
function checkValueIsNumberOrFormula(value, valueName) {
|
|
30245
|
+
if (value.startsWith("=")) {
|
|
30246
|
+
return "Success" /* CommandResult.Success */;
|
|
30247
|
+
}
|
|
30247
30248
|
if (isNaN(value)) {
|
|
30248
30249
|
switch (valueName) {
|
|
30249
30250
|
case "rangeMin":
|
|
@@ -30270,7 +30271,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
30270
30271
|
this.background = definition.background;
|
|
30271
30272
|
}
|
|
30272
30273
|
static validateChartDefinition(validator, definition) {
|
|
30273
|
-
return validator.checkValidations(definition, isDataRangeValid, validator.chainValidations(checkRangeLimits(checkEmpty, validator.batchValidations), checkRangeLimits(
|
|
30274
|
+
return validator.checkValidations(definition, isDataRangeValid, validator.chainValidations(checkRangeLimits(checkEmpty, validator.batchValidations), checkRangeLimits(checkValueIsNumberOrFormula, validator.batchValidations)), validator.chainValidations(checkInflectionPointsValue(checkValueIsNumberOrFormula, validator.batchValidations)));
|
|
30274
30275
|
}
|
|
30275
30276
|
static transformDefinition(definition, executed) {
|
|
30276
30277
|
let dataRangeZone;
|
|
@@ -30311,20 +30312,24 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
30311
30312
|
}
|
|
30312
30313
|
duplicateInDuplicatedSheet(newSheetId) {
|
|
30313
30314
|
const dataRange = duplicateLabelRangeInDuplicatedSheet(this.sheetId, newSheetId, this.dataRange);
|
|
30314
|
-
const
|
|
30315
|
+
const adaptFormula = (formula) => this.getters.copyFormulaStringForSheet(this.sheetId, newSheetId, formula, "moveReference");
|
|
30316
|
+
const sectionRule = adaptSectionRuleFormulas(this.sectionRule, adaptFormula);
|
|
30317
|
+
const definition = this.getDefinitionWithSpecificRanges(dataRange, sectionRule, newSheetId);
|
|
30315
30318
|
return new GaugeChart(definition, newSheetId, this.getters);
|
|
30316
30319
|
}
|
|
30317
30320
|
copyInSheetId(sheetId) {
|
|
30318
|
-
const
|
|
30321
|
+
const adaptFormula = (formula) => this.getters.copyFormulaStringForSheet(this.sheetId, sheetId, formula, "keepSameReference");
|
|
30322
|
+
const sectionRule = adaptSectionRuleFormulas(this.sectionRule, adaptFormula);
|
|
30323
|
+
const definition = this.getDefinitionWithSpecificRanges(this.dataRange, sectionRule, sheetId);
|
|
30319
30324
|
return new GaugeChart(definition, sheetId, this.getters);
|
|
30320
30325
|
}
|
|
30321
30326
|
getDefinition() {
|
|
30322
|
-
return this.getDefinitionWithSpecificRanges(this.dataRange);
|
|
30327
|
+
return this.getDefinitionWithSpecificRanges(this.dataRange, this.sectionRule);
|
|
30323
30328
|
}
|
|
30324
|
-
getDefinitionWithSpecificRanges(dataRange, targetSheetId) {
|
|
30329
|
+
getDefinitionWithSpecificRanges(dataRange, sectionRule, targetSheetId) {
|
|
30325
30330
|
return {
|
|
30326
30331
|
background: this.background,
|
|
30327
|
-
sectionRule:
|
|
30332
|
+
sectionRule: sectionRule,
|
|
30328
30333
|
title: this.title,
|
|
30329
30334
|
type: "gauge",
|
|
30330
30335
|
dataRange: dataRange
|
|
@@ -30345,11 +30350,10 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
30345
30350
|
};
|
|
30346
30351
|
}
|
|
30347
30352
|
updateRanges(applyChange) {
|
|
30348
|
-
const
|
|
30349
|
-
|
|
30350
|
-
|
|
30351
|
-
|
|
30352
|
-
const definition = this.getDefinitionWithSpecificRanges(range);
|
|
30353
|
+
const dataRange = adaptChartRange(this.dataRange, applyChange);
|
|
30354
|
+
const adaptFormula = (formula) => this.getters.adaptFormulaStringDependencies(this.sheetId, formula, applyChange);
|
|
30355
|
+
const sectionRule = adaptSectionRuleFormulas(this.sectionRule, adaptFormula);
|
|
30356
|
+
const definition = this.getDefinitionWithSpecificRanges(dataRange, sectionRule);
|
|
30353
30357
|
return new GaugeChart(definition, this.sheetId, this.getters);
|
|
30354
30358
|
}
|
|
30355
30359
|
}
|
|
@@ -30372,12 +30376,18 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
30372
30376
|
format = cell.format;
|
|
30373
30377
|
}
|
|
30374
30378
|
}
|
|
30375
|
-
|
|
30376
|
-
|
|
30379
|
+
let minValue = getFormulaNumberValue(chart.sheetId, chart.sectionRule.rangeMin, getters);
|
|
30380
|
+
let maxValue = getFormulaNumberValue(chart.sheetId, chart.sectionRule.rangeMax, getters);
|
|
30381
|
+
if (minValue === undefined || maxValue === undefined) {
|
|
30382
|
+
return getInvalidGaugeRuntime(chart, getters);
|
|
30383
|
+
}
|
|
30384
|
+
if (maxValue < minValue) {
|
|
30385
|
+
[minValue, maxValue] = [maxValue, minValue];
|
|
30386
|
+
}
|
|
30377
30387
|
const lowerPoint = chart.sectionRule.lowerInflectionPoint;
|
|
30378
30388
|
const upperPoint = chart.sectionRule.upperInflectionPoint;
|
|
30379
|
-
const lowerPointValue = getSectionThresholdValue(
|
|
30380
|
-
const upperPointValue = getSectionThresholdValue(
|
|
30389
|
+
const lowerPointValue = getSectionThresholdValue(chart.sheetId, chart.sectionRule.lowerInflectionPoint, minValue, maxValue, getters);
|
|
30390
|
+
const upperPointValue = getSectionThresholdValue(chart.sheetId, chart.sectionRule.upperInflectionPoint, minValue, maxValue, getters);
|
|
30381
30391
|
const inflectionValues = [];
|
|
30382
30392
|
const colors = [];
|
|
30383
30393
|
if (lowerPointValue !== undefined) {
|
|
@@ -30425,16 +30435,46 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
30425
30435
|
colors,
|
|
30426
30436
|
};
|
|
30427
30437
|
}
|
|
30428
|
-
function getSectionThresholdValue(threshold, minValue, maxValue) {
|
|
30429
|
-
|
|
30438
|
+
function getSectionThresholdValue(sheetId, threshold, minValue, maxValue, getters) {
|
|
30439
|
+
const numberValue = getFormulaNumberValue(sheetId, threshold.value, getters);
|
|
30440
|
+
if (numberValue === undefined) {
|
|
30430
30441
|
return undefined;
|
|
30431
30442
|
}
|
|
30432
|
-
const numberValue = Number(threshold.value);
|
|
30433
30443
|
const value = threshold.type === "number"
|
|
30434
30444
|
? numberValue
|
|
30435
30445
|
: minValue + ((maxValue - minValue) * numberValue) / 100;
|
|
30436
30446
|
return clip(value, minValue, maxValue);
|
|
30437
30447
|
}
|
|
30448
|
+
function getFormulaNumberValue(sheetId, formula, getters) {
|
|
30449
|
+
const value = getters.evaluateFormula(sheetId, formula);
|
|
30450
|
+
return isMatrix(value) ? undefined : tryToNumber(value, getters.getLocale());
|
|
30451
|
+
}
|
|
30452
|
+
function getInvalidGaugeRuntime(chart, getters) {
|
|
30453
|
+
return {
|
|
30454
|
+
background: getters.getStyleOfSingleCellChart(chart.background, chart.dataRange).background,
|
|
30455
|
+
title: chart.title ?? { text: "" },
|
|
30456
|
+
minValue: { value: 0, label: "" },
|
|
30457
|
+
maxValue: { value: 100, label: "" },
|
|
30458
|
+
gaugeValue: { value: 0, label: CellErrorType.GenericError },
|
|
30459
|
+
inflectionValues: [],
|
|
30460
|
+
colors: [],
|
|
30461
|
+
};
|
|
30462
|
+
}
|
|
30463
|
+
function adaptSectionRuleFormulas(sectionRule, adaptCallback) {
|
|
30464
|
+
return {
|
|
30465
|
+
...sectionRule,
|
|
30466
|
+
rangeMin: adaptCallback(sectionRule.rangeMin),
|
|
30467
|
+
rangeMax: adaptCallback(sectionRule.rangeMax),
|
|
30468
|
+
lowerInflectionPoint: {
|
|
30469
|
+
...sectionRule.lowerInflectionPoint,
|
|
30470
|
+
value: adaptCallback(sectionRule.lowerInflectionPoint.value),
|
|
30471
|
+
},
|
|
30472
|
+
upperInflectionPoint: {
|
|
30473
|
+
...sectionRule.upperInflectionPoint,
|
|
30474
|
+
value: adaptCallback(sectionRule.upperInflectionPoint.value),
|
|
30475
|
+
},
|
|
30476
|
+
};
|
|
30477
|
+
}
|
|
30438
30478
|
|
|
30439
30479
|
class GeoChart extends AbstractChart {
|
|
30440
30480
|
dataSets;
|
|
@@ -37322,8 +37362,8 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
37322
37362
|
document.body.style.cursor = "move";
|
|
37323
37363
|
state.draggedItemId = args.draggedItemId;
|
|
37324
37364
|
const container = direction === "horizontal"
|
|
37325
|
-
? new HorizontalContainer(args.
|
|
37326
|
-
: new VerticalContainer(args.
|
|
37365
|
+
? new HorizontalContainer(args.scrollableContainerEl)
|
|
37366
|
+
: new VerticalContainer(args.scrollableContainerEl);
|
|
37327
37367
|
dndHelper = new DOMDndHelper({
|
|
37328
37368
|
...args,
|
|
37329
37369
|
container,
|
|
@@ -37334,8 +37374,8 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
37334
37374
|
const stopListening = startDnd(dndHelper.onMouseMove.bind(dndHelper), dndHelper.onMouseUp.bind(dndHelper));
|
|
37335
37375
|
cleanupFns.push(stopListening);
|
|
37336
37376
|
const onScroll = dndHelper.onScroll.bind(dndHelper);
|
|
37337
|
-
args.
|
|
37338
|
-
cleanupFns.push(() => args.
|
|
37377
|
+
args.scrollableContainerEl.addEventListener("scroll", onScroll);
|
|
37378
|
+
cleanupFns.push(() => args.scrollableContainerEl.removeEventListener("scroll", onScroll));
|
|
37339
37379
|
cleanupFns.push(dndHelper.destroy.bind(dndHelper));
|
|
37340
37380
|
};
|
|
37341
37381
|
owl.onWillUnmount(() => {
|
|
@@ -38046,7 +38086,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
38046
38086
|
draggedItemId: rangeId.toString(),
|
|
38047
38087
|
initialMousePosition: event.clientY,
|
|
38048
38088
|
items: draggableItems,
|
|
38049
|
-
|
|
38089
|
+
scrollableContainerEl: this.selectionRef.el,
|
|
38050
38090
|
onDragEnd: (dimensionName, finalIndex) => {
|
|
38051
38091
|
const originalIndex = draggableIds.findIndex((id) => id === rangeId);
|
|
38052
38092
|
if (originalIndex === finalIndex) {
|
|
@@ -39615,1897 +39655,1954 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
39615
39655
|
}
|
|
39616
39656
|
}
|
|
39617
39657
|
|
|
39658
|
+
class DOMFocusableElementStore {
|
|
39659
|
+
mutators = ["setFocusableElement"];
|
|
39660
|
+
focusableElement = undefined;
|
|
39661
|
+
setFocusableElement(element) {
|
|
39662
|
+
this.focusableElement = element;
|
|
39663
|
+
}
|
|
39664
|
+
}
|
|
39665
|
+
|
|
39618
39666
|
css /* scss */ `
|
|
39619
|
-
.o-
|
|
39620
|
-
|
|
39621
|
-
|
|
39622
|
-
|
|
39623
|
-
|
|
39624
|
-
|
|
39625
|
-
font-size: 12px;
|
|
39626
|
-
line-height: 18px;
|
|
39627
|
-
width: 100%;
|
|
39628
|
-
font-size: 12px;
|
|
39629
|
-
}
|
|
39667
|
+
.o-autocomplete-dropdown {
|
|
39668
|
+
pointer-events: auto;
|
|
39669
|
+
cursor: pointer;
|
|
39670
|
+
background-color: #fff;
|
|
39671
|
+
max-width: 400px;
|
|
39672
|
+
z-index: 1;
|
|
39630
39673
|
|
|
39631
|
-
|
|
39632
|
-
|
|
39633
|
-
height: 30px;
|
|
39634
|
-
padding: 6px 0;
|
|
39635
|
-
}
|
|
39636
|
-
th.o-gauge-color-set-colorPicker {
|
|
39637
|
-
width: 8%;
|
|
39638
|
-
}
|
|
39639
|
-
th.o-gauge-color-set-text {
|
|
39640
|
-
width: 25%;
|
|
39641
|
-
}
|
|
39642
|
-
th.o-gauge-color-set-operator {
|
|
39643
|
-
width: 10%;
|
|
39644
|
-
}
|
|
39645
|
-
th.o-gauge-color-set-value {
|
|
39646
|
-
width: 22%;
|
|
39647
|
-
}
|
|
39648
|
-
th.o-gauge-color-set-type {
|
|
39649
|
-
width: 30%;
|
|
39674
|
+
.o-autocomplete-value-focus {
|
|
39675
|
+
background-color: #f2f2f2;
|
|
39650
39676
|
}
|
|
39651
|
-
|
|
39652
|
-
|
|
39653
|
-
|
|
39654
|
-
|
|
39655
|
-
|
|
39677
|
+
|
|
39678
|
+
& > div {
|
|
39679
|
+
padding: 1px 5px 5px 5px;
|
|
39680
|
+
.o-autocomplete-description {
|
|
39681
|
+
padding-left: 5px;
|
|
39682
|
+
font-size: 11px;
|
|
39683
|
+
}
|
|
39656
39684
|
}
|
|
39657
39685
|
}
|
|
39658
39686
|
`;
|
|
39659
|
-
class
|
|
39660
|
-
static template = "o-spreadsheet-
|
|
39661
|
-
static components = {
|
|
39662
|
-
SidePanelCollapsible,
|
|
39663
|
-
Section,
|
|
39664
|
-
RoundColorPicker,
|
|
39665
|
-
GeneralDesignEditor,
|
|
39666
|
-
ChartErrorSection,
|
|
39667
|
-
};
|
|
39687
|
+
class TextValueProvider extends owl.Component {
|
|
39688
|
+
static template = "o-spreadsheet-TextValueProvider";
|
|
39668
39689
|
static props = {
|
|
39669
|
-
|
|
39670
|
-
|
|
39671
|
-
|
|
39672
|
-
|
|
39690
|
+
proposals: Array,
|
|
39691
|
+
selectedIndex: { type: Number, optional: true },
|
|
39692
|
+
onValueSelected: Function,
|
|
39693
|
+
onValueHovered: Function,
|
|
39673
39694
|
};
|
|
39674
|
-
|
|
39695
|
+
autoCompleteListRef = owl.useRef("autoCompleteList");
|
|
39675
39696
|
setup() {
|
|
39676
|
-
|
|
39677
|
-
|
|
39678
|
-
|
|
39679
|
-
|
|
39680
|
-
|
|
39681
|
-
|
|
39682
|
-
|
|
39683
|
-
|
|
39684
|
-
}
|
|
39685
|
-
isRangeMinInvalid() {
|
|
39686
|
-
return !!(this.state.sectionRuleDispatchResult?.isCancelledBecause("EmptyGaugeRangeMin" /* CommandResult.EmptyGaugeRangeMin */) ||
|
|
39687
|
-
this.state.sectionRuleDispatchResult?.isCancelledBecause("GaugeRangeMinNaN" /* CommandResult.GaugeRangeMinNaN */) ||
|
|
39688
|
-
this.state.sectionRuleDispatchResult?.isCancelledBecause("GaugeRangeMinBiggerThanRangeMax" /* CommandResult.GaugeRangeMinBiggerThanRangeMax */));
|
|
39689
|
-
}
|
|
39690
|
-
isRangeMaxInvalid() {
|
|
39691
|
-
return !!(this.state.sectionRuleDispatchResult?.isCancelledBecause("EmptyGaugeRangeMax" /* CommandResult.EmptyGaugeRangeMax */) ||
|
|
39692
|
-
this.state.sectionRuleDispatchResult?.isCancelledBecause("GaugeRangeMaxNaN" /* CommandResult.GaugeRangeMaxNaN */) ||
|
|
39693
|
-
this.state.sectionRuleDispatchResult?.isCancelledBecause("GaugeRangeMinBiggerThanRangeMax" /* CommandResult.GaugeRangeMinBiggerThanRangeMax */));
|
|
39694
|
-
}
|
|
39695
|
-
// ---------------------------------------------------------------------------
|
|
39696
|
-
// COLOR_SECTION_TEMPLATE
|
|
39697
|
-
// ---------------------------------------------------------------------------
|
|
39698
|
-
get isLowerInflectionPointInvalid() {
|
|
39699
|
-
return !!(this.state.sectionRuleDispatchResult?.isCancelledBecause("GaugeLowerInflectionPointNaN" /* CommandResult.GaugeLowerInflectionPointNaN */) ||
|
|
39700
|
-
this.state.sectionRuleDispatchResult?.isCancelledBecause("GaugeLowerBiggerThanUpper" /* CommandResult.GaugeLowerBiggerThanUpper */));
|
|
39701
|
-
}
|
|
39702
|
-
get isUpperInflectionPointInvalid() {
|
|
39703
|
-
return !!(this.state.sectionRuleDispatchResult?.isCancelledBecause("GaugeUpperInflectionPointNaN" /* CommandResult.GaugeUpperInflectionPointNaN */) ||
|
|
39704
|
-
this.state.sectionRuleDispatchResult?.isCancelledBecause("GaugeLowerBiggerThanUpper" /* CommandResult.GaugeLowerBiggerThanUpper */));
|
|
39705
|
-
}
|
|
39706
|
-
updateSectionColor(target, color) {
|
|
39707
|
-
const sectionRule = deepCopy(this.state.sectionRule);
|
|
39708
|
-
sectionRule.colors[target] = color;
|
|
39709
|
-
this.updateSectionRule(sectionRule);
|
|
39710
|
-
}
|
|
39711
|
-
updateSectionRule(sectionRule) {
|
|
39712
|
-
this.state.sectionRuleDispatchResult = this.props.updateChart(this.props.figureId, {
|
|
39713
|
-
sectionRule,
|
|
39714
|
-
});
|
|
39715
|
-
if (this.state.sectionRuleDispatchResult.isSuccessful) {
|
|
39716
|
-
this.state.sectionRule = deepCopy(sectionRule);
|
|
39717
|
-
}
|
|
39718
|
-
}
|
|
39719
|
-
canUpdateSectionRule(sectionRule) {
|
|
39720
|
-
this.state.sectionRuleDispatchResult = this.props.canUpdateChart(this.props.figureId, {
|
|
39721
|
-
sectionRule,
|
|
39722
|
-
});
|
|
39697
|
+
owl.useEffect(() => {
|
|
39698
|
+
const selectedIndex = this.props.selectedIndex;
|
|
39699
|
+
if (selectedIndex === undefined) {
|
|
39700
|
+
return;
|
|
39701
|
+
}
|
|
39702
|
+
const selectedElement = this.autoCompleteListRef.el?.children[selectedIndex];
|
|
39703
|
+
selectedElement?.scrollIntoView?.({ block: "nearest" });
|
|
39704
|
+
}, () => [this.props.selectedIndex, this.autoCompleteListRef.el]);
|
|
39723
39705
|
}
|
|
39724
39706
|
}
|
|
39725
39707
|
|
|
39726
|
-
class
|
|
39727
|
-
|
|
39728
|
-
|
|
39729
|
-
|
|
39730
|
-
|
|
39731
|
-
|
|
39732
|
-
|
|
39733
|
-
|
|
39734
|
-
|
|
39735
|
-
const value = ev.target.value;
|
|
39736
|
-
this.props.updateChart(this.props.figureId, { region: value });
|
|
39737
|
-
}
|
|
39738
|
-
get availableRegions() {
|
|
39739
|
-
return this.env.model.getters.getGeoChartAvailableRegions();
|
|
39708
|
+
class AutoCompleteStore extends SpreadsheetStore {
|
|
39709
|
+
mutators = ["useProvider", "moveSelection", "hide", "selectIndex"];
|
|
39710
|
+
selectedIndex = undefined;
|
|
39711
|
+
provider;
|
|
39712
|
+
get selectedProposal() {
|
|
39713
|
+
if (this.selectedIndex === undefined || this.provider === undefined) {
|
|
39714
|
+
return undefined;
|
|
39715
|
+
}
|
|
39716
|
+
return this.provider.proposals[this.selectedIndex];
|
|
39740
39717
|
}
|
|
39741
|
-
|
|
39742
|
-
|
|
39718
|
+
useProvider(provider) {
|
|
39719
|
+
this.provider = provider;
|
|
39720
|
+
this.selectedIndex = provider.autoSelectFirstProposal ? 0 : undefined;
|
|
39743
39721
|
}
|
|
39744
|
-
|
|
39745
|
-
|
|
39746
|
-
|
|
39747
|
-
static template = "o-spreadsheet-GeoChartConfigPanel";
|
|
39748
|
-
static components = { ...GenericChartConfigPanel.components, GeoChartRegionSelectSection };
|
|
39749
|
-
get dataRanges() {
|
|
39750
|
-
return this.getDataSeriesRanges().slice(0, 1);
|
|
39722
|
+
hide() {
|
|
39723
|
+
this.provider = undefined;
|
|
39724
|
+
this.selectedIndex = undefined;
|
|
39751
39725
|
}
|
|
39752
|
-
|
|
39753
|
-
this.
|
|
39754
|
-
this.state.datasetDispatchResult = this.props.updateChart(this.props.figureId, {
|
|
39755
|
-
dataSets: this.dataSets,
|
|
39756
|
-
});
|
|
39726
|
+
selectIndex(index) {
|
|
39727
|
+
this.selectedIndex = index;
|
|
39757
39728
|
}
|
|
39758
|
-
|
|
39759
|
-
|
|
39760
|
-
|
|
39761
|
-
|
|
39762
|
-
|
|
39763
|
-
|
|
39764
|
-
|
|
39765
|
-
|
|
39766
|
-
|
|
39729
|
+
moveSelection(direction) {
|
|
39730
|
+
if (!this.provider) {
|
|
39731
|
+
return;
|
|
39732
|
+
}
|
|
39733
|
+
if (this.selectedIndex === undefined) {
|
|
39734
|
+
this.selectedIndex = 0;
|
|
39735
|
+
return;
|
|
39736
|
+
}
|
|
39737
|
+
if (direction === "previous") {
|
|
39738
|
+
this.selectedIndex--;
|
|
39739
|
+
if (this.selectedIndex < 0) {
|
|
39740
|
+
this.selectedIndex = this.provider.proposals.length - 1;
|
|
39741
|
+
}
|
|
39742
|
+
}
|
|
39743
|
+
else {
|
|
39744
|
+
this.selectedIndex = (this.selectedIndex + 1) % this.provider.proposals.length;
|
|
39745
|
+
}
|
|
39767
39746
|
}
|
|
39768
39747
|
}
|
|
39769
39748
|
|
|
39770
|
-
|
|
39771
|
-
|
|
39772
|
-
|
|
39773
|
-
|
|
39774
|
-
|
|
39775
|
-
class GeoChartDesignPanel extends ChartWithAxisDesignPanel {
|
|
39776
|
-
static template = "o-spreadsheet-GeoChartDesignPanel";
|
|
39777
|
-
static components = { ...ChartWithAxisDesignPanel.components, RoundColorPicker };
|
|
39778
|
-
colorScalesChoices = ChartTerms.GeoChart.ColorScales;
|
|
39779
|
-
updateColorScaleType(ev) {
|
|
39780
|
-
const value = ev.target.value;
|
|
39781
|
-
value === "custom"
|
|
39782
|
-
? this.updateColorScale(DEFAULT_CUSTOM_COLOR_SCALE)
|
|
39783
|
-
: this.updateColorScale(value);
|
|
39784
|
-
}
|
|
39785
|
-
updateColorScale(colorScale) {
|
|
39786
|
-
this.props.updateChart(this.props.figureId, { colorScale });
|
|
39787
|
-
}
|
|
39788
|
-
updateMissingValueColor(color) {
|
|
39789
|
-
this.props.updateChart(this.props.figureId, { missingValueColor: color });
|
|
39790
|
-
}
|
|
39791
|
-
updateLegendPosition(ev) {
|
|
39792
|
-
const value = ev.target.value;
|
|
39793
|
-
this.props.updateChart(this.props.figureId, { legendPosition: value });
|
|
39794
|
-
}
|
|
39795
|
-
get selectedColorScale() {
|
|
39796
|
-
return typeof this.props.definition.colorScale === "object"
|
|
39797
|
-
? "custom"
|
|
39798
|
-
: this.props.definition.colorScale || "oranges";
|
|
39749
|
+
class ContentEditableHelper {
|
|
39750
|
+
// todo make el private and expose dedicated methods
|
|
39751
|
+
el;
|
|
39752
|
+
constructor(el) {
|
|
39753
|
+
this.el = el;
|
|
39799
39754
|
}
|
|
39800
|
-
|
|
39801
|
-
|
|
39755
|
+
updateEl(el) {
|
|
39756
|
+
this.el = el;
|
|
39802
39757
|
}
|
|
39803
|
-
|
|
39804
|
-
|
|
39805
|
-
|
|
39758
|
+
/**
|
|
39759
|
+
* select the text at position start to end, no matter the children
|
|
39760
|
+
*/
|
|
39761
|
+
selectRange(start, end) {
|
|
39762
|
+
let selection = window.getSelection();
|
|
39763
|
+
const { start: currentStart, end: currentEnd } = this.getCurrentSelection();
|
|
39764
|
+
if (currentStart === start && currentEnd === end) {
|
|
39765
|
+
return;
|
|
39766
|
+
}
|
|
39767
|
+
if (selection.rangeCount === 0) {
|
|
39768
|
+
const range = document.createRange();
|
|
39769
|
+
selection.addRange(range);
|
|
39770
|
+
}
|
|
39771
|
+
const currentRange = selection.getRangeAt(0);
|
|
39772
|
+
let range;
|
|
39773
|
+
if (this.el.contains(currentRange.startContainer)) {
|
|
39774
|
+
range = currentRange;
|
|
39775
|
+
}
|
|
39776
|
+
else {
|
|
39777
|
+
range = document.createRange();
|
|
39778
|
+
selection.removeAllRanges();
|
|
39779
|
+
selection.addRange(range);
|
|
39780
|
+
}
|
|
39781
|
+
if (start === end && start === 0) {
|
|
39782
|
+
range.setStart(this.el, 0);
|
|
39783
|
+
range.setEnd(this.el, 0);
|
|
39784
|
+
}
|
|
39785
|
+
else {
|
|
39786
|
+
const textLength = this.getText().length;
|
|
39787
|
+
if (start < 0 || end > textLength) {
|
|
39788
|
+
console.warn(`wrong selection asked start ${start}, end ${end}, text content length ${textLength}`);
|
|
39789
|
+
if (start < 0)
|
|
39790
|
+
start = 0;
|
|
39791
|
+
if (end > textLength)
|
|
39792
|
+
end = textLength;
|
|
39793
|
+
if (start > textLength)
|
|
39794
|
+
start = textLength;
|
|
39795
|
+
}
|
|
39796
|
+
let startNode = this.findChildAtCharacterIndex(start);
|
|
39797
|
+
let endNode = this.findChildAtCharacterIndex(end);
|
|
39798
|
+
range.setStart(startNode.node, startNode.offset);
|
|
39799
|
+
range.setEnd(endNode.node, endNode.offset);
|
|
39806
39800
|
}
|
|
39807
|
-
return undefined;
|
|
39808
|
-
}
|
|
39809
|
-
getCustomColorScaleColor(color) {
|
|
39810
|
-
return this.customColorScale?.[color] ?? "";
|
|
39811
39801
|
}
|
|
39812
|
-
|
|
39813
|
-
|
|
39814
|
-
|
|
39802
|
+
/**
|
|
39803
|
+
* finds the dom element that contains the character at `offset`
|
|
39804
|
+
*/
|
|
39805
|
+
findChildAtCharacterIndex(offset) {
|
|
39806
|
+
let it = iterateChildren(this.el);
|
|
39807
|
+
let current, previous;
|
|
39808
|
+
let usedCharacters = offset;
|
|
39809
|
+
let isFirstParagraph = true;
|
|
39810
|
+
do {
|
|
39811
|
+
current = it.next();
|
|
39812
|
+
if (!current.done && !current.value.hasChildNodes()) {
|
|
39813
|
+
if (current.value.textContent && current.value.textContent.length < usedCharacters) {
|
|
39814
|
+
usedCharacters -= current.value.textContent.length;
|
|
39815
|
+
}
|
|
39816
|
+
else if (current.value.textContent &&
|
|
39817
|
+
current.value.textContent.length >= usedCharacters) {
|
|
39818
|
+
it.return(current.value);
|
|
39819
|
+
}
|
|
39820
|
+
previous = current.value;
|
|
39821
|
+
}
|
|
39822
|
+
// One new paragraph = one new line character, except for the first paragraph
|
|
39823
|
+
if (!current.done && current.value.nodeName === "P") {
|
|
39824
|
+
if (isFirstParagraph) {
|
|
39825
|
+
isFirstParagraph = false;
|
|
39826
|
+
}
|
|
39827
|
+
else {
|
|
39828
|
+
usedCharacters--;
|
|
39829
|
+
}
|
|
39830
|
+
}
|
|
39831
|
+
} while (!current.done && usedCharacters);
|
|
39832
|
+
if (current.value) {
|
|
39833
|
+
return { node: current.value, offset: usedCharacters };
|
|
39815
39834
|
}
|
|
39816
|
-
|
|
39817
|
-
|
|
39835
|
+
return { node: previous, offset: usedCharacters };
|
|
39836
|
+
}
|
|
39837
|
+
/**
|
|
39838
|
+
* Sets (or Replaces all) the text inside the root element in the form of distinctive paragraphs and
|
|
39839
|
+
* span for each element provided in `contents`.
|
|
39840
|
+
*
|
|
39841
|
+
* The function will apply the diff between the current content and the new content to avoid the systematic
|
|
39842
|
+
* destruction of DOM elements which interferes with IME[1]
|
|
39843
|
+
*
|
|
39844
|
+
* Each line of text will be encapsulated in a paragraph element.
|
|
39845
|
+
* Each span will have its own fontcolor and specific class if provided in the HtmlContent object.
|
|
39846
|
+
*
|
|
39847
|
+
* [1] https://developer.mozilla.org/en-US/docs/Glossary/Input_method_editor
|
|
39848
|
+
*/
|
|
39849
|
+
setText(contents) {
|
|
39850
|
+
if (contents.length === 0) {
|
|
39851
|
+
this.removeAll();
|
|
39818
39852
|
return;
|
|
39819
39853
|
}
|
|
39820
|
-
|
|
39821
|
-
|
|
39822
|
-
|
|
39823
|
-
|
|
39824
|
-
|
|
39825
|
-
|
|
39826
|
-
|
|
39827
|
-
|
|
39828
|
-
|
|
39829
|
-
|
|
39854
|
+
const childElements = Array.from(this.el.childNodes);
|
|
39855
|
+
const contentLength = contents.length;
|
|
39856
|
+
for (let i = 0; i < contentLength; i++) {
|
|
39857
|
+
const line = contents[i];
|
|
39858
|
+
const childElement = childElements[i];
|
|
39859
|
+
let newChild = false;
|
|
39860
|
+
let p;
|
|
39861
|
+
if (childElement && childElement.nodeName === "P") {
|
|
39862
|
+
p = childElement;
|
|
39863
|
+
}
|
|
39864
|
+
else {
|
|
39865
|
+
newChild = true;
|
|
39866
|
+
p = document.createElement("p");
|
|
39867
|
+
}
|
|
39868
|
+
const lineLength = line.length;
|
|
39869
|
+
const existingChildren = Array.from(p.childNodes);
|
|
39870
|
+
for (let j = 0; j < lineLength; j++) {
|
|
39871
|
+
const content = line[j];
|
|
39872
|
+
const child = existingChildren[j];
|
|
39873
|
+
// child nodes can be multiple types of nodes: Span, Text, Div, etc...
|
|
39874
|
+
// We can only modify a node in place if it has the same type as the content
|
|
39875
|
+
// that we would insert, which are spans.
|
|
39876
|
+
// Otherwise, it means that the node has been input by the user, through the keyboard or a copy/paste
|
|
39877
|
+
// @ts-ignore (somehow required because jest does not like child.tagName despite the prior check)
|
|
39878
|
+
const childIsSpan = child && "tagName" in child && child.tagName === "SPAN";
|
|
39879
|
+
if (childIsSpan && compareContentToSpanElement(content, child)) {
|
|
39880
|
+
continue;
|
|
39881
|
+
}
|
|
39882
|
+
// this is an empty line in the content
|
|
39883
|
+
if (!content.value && !content.class) {
|
|
39884
|
+
if (child)
|
|
39885
|
+
p.removeChild(child);
|
|
39886
|
+
continue;
|
|
39887
|
+
}
|
|
39888
|
+
const span = document.createElement("span");
|
|
39889
|
+
span.innerText = content.value;
|
|
39890
|
+
span.style.color = content.color || "";
|
|
39891
|
+
if (content.class) {
|
|
39892
|
+
span.classList.add(content.class);
|
|
39893
|
+
}
|
|
39894
|
+
if (child) {
|
|
39895
|
+
p.replaceChild(span, child);
|
|
39896
|
+
}
|
|
39897
|
+
else {
|
|
39898
|
+
p.appendChild(span);
|
|
39899
|
+
}
|
|
39900
|
+
}
|
|
39901
|
+
if (existingChildren.length > lineLength) {
|
|
39902
|
+
for (let i = lineLength; i < existingChildren.length; i++) {
|
|
39903
|
+
p.removeChild(existingChildren[i]);
|
|
39904
|
+
}
|
|
39905
|
+
}
|
|
39906
|
+
// Empty line
|
|
39907
|
+
if (!p.hasChildNodes()) {
|
|
39908
|
+
const span = document.createElement("span");
|
|
39909
|
+
span.appendChild(document.createElement("br"));
|
|
39910
|
+
p.appendChild(span);
|
|
39911
|
+
}
|
|
39912
|
+
// replace p if necessary
|
|
39913
|
+
if (newChild) {
|
|
39914
|
+
if (childElement) {
|
|
39915
|
+
this.el.replaceChild(p, childElement);
|
|
39916
|
+
}
|
|
39917
|
+
else {
|
|
39918
|
+
this.el.appendChild(p);
|
|
39919
|
+
}
|
|
39920
|
+
}
|
|
39921
|
+
}
|
|
39922
|
+
if (childElements.length > contentLength) {
|
|
39923
|
+
for (let i = contentLength; i < childElements.length; i++) {
|
|
39924
|
+
this.el.removeChild(childElements[i]);
|
|
39925
|
+
}
|
|
39830
39926
|
}
|
|
39831
|
-
return false;
|
|
39832
39927
|
}
|
|
39833
|
-
|
|
39834
|
-
const
|
|
39835
|
-
|
|
39836
|
-
|
|
39837
|
-
|
|
39928
|
+
scrollSelectionIntoView() {
|
|
39929
|
+
const focusedNode = document.getSelection()?.focusNode;
|
|
39930
|
+
if (!focusedNode || !this.el.contains(focusedNode))
|
|
39931
|
+
return;
|
|
39932
|
+
const element = focusedNode instanceof HTMLElement ? focusedNode : focusedNode.parentElement;
|
|
39933
|
+
element?.scrollIntoView?.({ block: "nearest" });
|
|
39838
39934
|
}
|
|
39839
|
-
|
|
39840
|
-
|
|
39841
|
-
|
|
39842
|
-
|
|
39843
|
-
|
|
39844
|
-
|
|
39845
|
-
label: this.chartTerms.TreatLabelsAsText,
|
|
39846
|
-
onChange: this.onUpdateLabelsAsText.bind(this),
|
|
39847
|
-
});
|
|
39848
|
-
}
|
|
39849
|
-
return options;
|
|
39935
|
+
/**
|
|
39936
|
+
* remove the current selection of the user
|
|
39937
|
+
* */
|
|
39938
|
+
removeSelection() {
|
|
39939
|
+
let selection = window.getSelection();
|
|
39940
|
+
selection.removeAllRanges();
|
|
39850
39941
|
}
|
|
39851
|
-
|
|
39852
|
-
|
|
39853
|
-
|
|
39854
|
-
|
|
39942
|
+
removeAll() {
|
|
39943
|
+
if (this.el) {
|
|
39944
|
+
while (this.el.firstChild) {
|
|
39945
|
+
this.el.removeChild(this.el.firstChild);
|
|
39946
|
+
}
|
|
39947
|
+
}
|
|
39855
39948
|
}
|
|
39856
|
-
|
|
39857
|
-
|
|
39858
|
-
|
|
39859
|
-
|
|
39949
|
+
/**
|
|
39950
|
+
* finds the indexes of the current selection.
|
|
39951
|
+
* */
|
|
39952
|
+
getCurrentSelection() {
|
|
39953
|
+
return getCurrentSelection(this.el);
|
|
39860
39954
|
}
|
|
39861
|
-
|
|
39862
|
-
|
|
39863
|
-
|
|
39864
|
-
|
|
39955
|
+
getText() {
|
|
39956
|
+
let text = "";
|
|
39957
|
+
let it = iterateChildren(this.el);
|
|
39958
|
+
let current = it.next();
|
|
39959
|
+
let isFirstParagraph = true;
|
|
39960
|
+
while (!current.done) {
|
|
39961
|
+
if (!current.value.hasChildNodes()) {
|
|
39962
|
+
text += current.value.textContent;
|
|
39963
|
+
}
|
|
39964
|
+
if (current.value.nodeName === "P" ||
|
|
39965
|
+
(current.value.nodeName === "DIV" && current.value !== this.el) // On paste, the HTML may contain <div> instead of <p>
|
|
39966
|
+
) {
|
|
39967
|
+
if (isFirstParagraph) {
|
|
39968
|
+
isFirstParagraph = false;
|
|
39969
|
+
}
|
|
39970
|
+
else {
|
|
39971
|
+
text += NEWLINE;
|
|
39972
|
+
}
|
|
39973
|
+
}
|
|
39974
|
+
current = it.next();
|
|
39975
|
+
}
|
|
39976
|
+
return text;
|
|
39865
39977
|
}
|
|
39866
39978
|
}
|
|
39867
|
-
|
|
39868
|
-
|
|
39869
|
-
|
|
39870
|
-
|
|
39871
|
-
|
|
39872
|
-
|
|
39873
|
-
|
|
39874
|
-
ChartLegend,
|
|
39875
|
-
};
|
|
39876
|
-
static props = {
|
|
39877
|
-
figureId: String,
|
|
39878
|
-
definition: Object,
|
|
39879
|
-
updateChart: Function,
|
|
39880
|
-
canUpdateChart: { type: Function, optional: true },
|
|
39881
|
-
};
|
|
39882
|
-
}
|
|
39883
|
-
|
|
39884
|
-
class RadarChartDesignPanel extends owl.Component {
|
|
39885
|
-
static template = "o-spreadsheet-RadarChartDesignPanel";
|
|
39886
|
-
static components = {
|
|
39887
|
-
GeneralDesignEditor,
|
|
39888
|
-
SeriesDesignEditor,
|
|
39889
|
-
Section,
|
|
39890
|
-
Checkbox,
|
|
39891
|
-
ChartLegend,
|
|
39892
|
-
};
|
|
39893
|
-
static props = {
|
|
39894
|
-
figureId: String,
|
|
39895
|
-
definition: Object,
|
|
39896
|
-
canUpdateChart: Function,
|
|
39897
|
-
updateChart: Function,
|
|
39898
|
-
};
|
|
39979
|
+
function compareContentToSpanElement(content, node) {
|
|
39980
|
+
const contentColor = content.color ? toHex(content.color) : "";
|
|
39981
|
+
const nodeColor = node.style?.color ? toHex(node.style.color) : "";
|
|
39982
|
+
const sameColor = contentColor === nodeColor;
|
|
39983
|
+
const sameClass = deepEquals([content.class], [...node.classList]);
|
|
39984
|
+
const sameContent = node.innerText === content.value;
|
|
39985
|
+
return sameColor && sameClass && sameContent;
|
|
39899
39986
|
}
|
|
39900
39987
|
|
|
39901
|
-
|
|
39902
|
-
|
|
39903
|
-
|
|
39904
|
-
|
|
39905
|
-
|
|
39906
|
-
|
|
39907
|
-
|
|
39908
|
-
|
|
39909
|
-
|
|
39910
|
-
|
|
39911
|
-
|
|
39912
|
-
|
|
39913
|
-
|
|
39988
|
+
// -----------------------------------------------------------------------------
|
|
39989
|
+
// Formula Assistant component
|
|
39990
|
+
// -----------------------------------------------------------------------------
|
|
39991
|
+
css /* scss */ `
|
|
39992
|
+
.o-formula-assistant {
|
|
39993
|
+
background: #ffffff;
|
|
39994
|
+
.o-formula-assistant-head {
|
|
39995
|
+
background-color: #f2f2f2;
|
|
39996
|
+
padding: 10px;
|
|
39997
|
+
}
|
|
39998
|
+
.collapsed {
|
|
39999
|
+
transform: rotate(180deg);
|
|
40000
|
+
}
|
|
40001
|
+
.o-formula-assistant-core {
|
|
40002
|
+
border-bottom: 1px solid gray;
|
|
40003
|
+
}
|
|
40004
|
+
.o-formula-assistant-arg-description {
|
|
40005
|
+
font-size: 85%;
|
|
40006
|
+
}
|
|
40007
|
+
.o-formula-assistant-focus {
|
|
40008
|
+
div:first-child,
|
|
40009
|
+
span {
|
|
40010
|
+
color: ${COMPOSER_ASSISTANT_COLOR};
|
|
40011
|
+
text-shadow: 0px 0px 1px ${COMPOSER_ASSISTANT_COLOR};
|
|
39914
40012
|
}
|
|
39915
|
-
|
|
39916
|
-
|
|
39917
|
-
if (this.canTreatLabelsAsText) {
|
|
39918
|
-
options.push({
|
|
39919
|
-
name: "labelsAsText",
|
|
39920
|
-
value: this.props.definition.labelsAsText,
|
|
39921
|
-
label: this.chartTerms.TreatLabelsAsText,
|
|
39922
|
-
onChange: this.onUpdateLabelsAsText.bind(this),
|
|
39923
|
-
});
|
|
39924
|
-
}
|
|
39925
|
-
return options;
|
|
40013
|
+
div:last-child {
|
|
40014
|
+
color: black;
|
|
39926
40015
|
}
|
|
40016
|
+
}
|
|
40017
|
+
.o-formula-assistant-gray {
|
|
40018
|
+
color: gray;
|
|
40019
|
+
}
|
|
39927
40020
|
}
|
|
39928
|
-
|
|
39929
|
-
class
|
|
39930
|
-
static template = "o-spreadsheet-
|
|
39931
|
-
static components = { SelectionInput, ChartErrorSection, Section };
|
|
40021
|
+
`;
|
|
40022
|
+
class FunctionDescriptionProvider extends owl.Component {
|
|
40023
|
+
static template = "o-spreadsheet-FunctionDescriptionProvider";
|
|
39932
40024
|
static props = {
|
|
39933
|
-
|
|
39934
|
-
|
|
39935
|
-
|
|
39936
|
-
canUpdateChart: Function,
|
|
40025
|
+
functionName: String,
|
|
40026
|
+
functionDescription: Object,
|
|
40027
|
+
argToFocus: Number,
|
|
39937
40028
|
};
|
|
39938
|
-
|
|
39939
|
-
|
|
39940
|
-
baselineDispatchResult: undefined,
|
|
39941
|
-
});
|
|
39942
|
-
keyValue = this.props.definition.keyValue;
|
|
39943
|
-
baseline = this.props.definition.baseline;
|
|
39944
|
-
get errorMessages() {
|
|
39945
|
-
const cancelledReasons = [
|
|
39946
|
-
...(this.state.keyValueDispatchResult?.reasons || []),
|
|
39947
|
-
...(this.state.baselineDispatchResult?.reasons || []),
|
|
39948
|
-
];
|
|
39949
|
-
return cancelledReasons.map((error) => ChartTerms.Errors[error] || ChartTerms.Errors.Unexpected);
|
|
39950
|
-
}
|
|
39951
|
-
get isKeyValueInvalid() {
|
|
39952
|
-
return !!this.state.keyValueDispatchResult?.isCancelledBecause("InvalidScorecardKeyValue" /* CommandResult.InvalidScorecardKeyValue */);
|
|
39953
|
-
}
|
|
39954
|
-
get isBaselineInvalid() {
|
|
39955
|
-
return !!this.state.keyValueDispatchResult?.isCancelledBecause("InvalidScorecardBaseline" /* CommandResult.InvalidScorecardBaseline */);
|
|
39956
|
-
}
|
|
39957
|
-
onKeyValueRangeChanged(ranges) {
|
|
39958
|
-
this.keyValue = ranges[0];
|
|
39959
|
-
this.state.keyValueDispatchResult = this.props.canUpdateChart(this.props.figureId, {
|
|
39960
|
-
keyValue: this.keyValue,
|
|
39961
|
-
});
|
|
39962
|
-
}
|
|
39963
|
-
updateKeyValueRange() {
|
|
39964
|
-
this.state.keyValueDispatchResult = this.props.updateChart(this.props.figureId, {
|
|
39965
|
-
keyValue: this.keyValue,
|
|
39966
|
-
});
|
|
39967
|
-
}
|
|
39968
|
-
getKeyValueRange() {
|
|
39969
|
-
return this.keyValue || "";
|
|
39970
|
-
}
|
|
39971
|
-
onBaselineRangeChanged(ranges) {
|
|
39972
|
-
this.baseline = ranges[0];
|
|
39973
|
-
this.state.baselineDispatchResult = this.props.canUpdateChart(this.props.figureId, {
|
|
39974
|
-
baseline: this.baseline,
|
|
39975
|
-
});
|
|
39976
|
-
}
|
|
39977
|
-
updateBaselineRange() {
|
|
39978
|
-
this.state.baselineDispatchResult = this.props.updateChart(this.props.figureId, {
|
|
39979
|
-
baseline: this.baseline,
|
|
39980
|
-
});
|
|
39981
|
-
}
|
|
39982
|
-
getBaselineRange() {
|
|
39983
|
-
return this.baseline || "";
|
|
40029
|
+
getContext() {
|
|
40030
|
+
return this.props;
|
|
39984
40031
|
}
|
|
39985
|
-
|
|
39986
|
-
this.
|
|
40032
|
+
get formulaArgSeparator() {
|
|
40033
|
+
return this.env.model.getters.getLocale().formulaArgSeparator + " ";
|
|
39987
40034
|
}
|
|
39988
40035
|
}
|
|
39989
40036
|
|
|
39990
|
-
|
|
39991
|
-
|
|
39992
|
-
|
|
39993
|
-
|
|
39994
|
-
|
|
39995
|
-
|
|
39996
|
-
|
|
39997
|
-
|
|
39998
|
-
|
|
39999
|
-
|
|
40000
|
-
|
|
40001
|
-
|
|
40002
|
-
|
|
40003
|
-
|
|
40004
|
-
|
|
40005
|
-
|
|
40006
|
-
|
|
40007
|
-
|
|
40008
|
-
|
|
40009
|
-
|
|
40010
|
-
|
|
40011
|
-
|
|
40012
|
-
|
|
40013
|
-
|
|
40014
|
-
|
|
40015
|
-
|
|
40016
|
-
|
|
40017
|
-
|
|
40018
|
-
|
|
40019
|
-
|
|
40020
|
-
|
|
40021
|
-
}
|
|
40022
|
-
updateBaselineDescr(ev) {
|
|
40023
|
-
this.props.updateChart(this.props.figureId, { baselineDescr: ev.target.value });
|
|
40024
|
-
}
|
|
40025
|
-
setColor(color, colorPickerId) {
|
|
40026
|
-
switch (colorPickerId) {
|
|
40027
|
-
case "backgroundColor":
|
|
40028
|
-
this.props.updateChart(this.props.figureId, { background: color });
|
|
40029
|
-
break;
|
|
40030
|
-
case "baselineColorDown":
|
|
40031
|
-
this.props.updateChart(this.props.figureId, { baselineColorDown: color });
|
|
40032
|
-
break;
|
|
40033
|
-
case "baselineColorUp":
|
|
40034
|
-
this.props.updateChart(this.props.figureId, { baselineColorUp: color });
|
|
40035
|
-
break;
|
|
40037
|
+
const functions = functionRegistry.content;
|
|
40038
|
+
const ASSISTANT_WIDTH = 300;
|
|
40039
|
+
const CLOSE_ICON_RADIUS = 9;
|
|
40040
|
+
const selectionIndicatorClass = "selector-flag";
|
|
40041
|
+
const backgroundClass = "background-flag";
|
|
40042
|
+
const selectionIndicatorColor = "#a9a9a9";
|
|
40043
|
+
const selectionIndicator = "␣";
|
|
40044
|
+
css /* scss */ `
|
|
40045
|
+
.o-composer-container {
|
|
40046
|
+
.o-composer {
|
|
40047
|
+
overflow-y: auto;
|
|
40048
|
+
overflow-x: hidden;
|
|
40049
|
+
word-break: break-all;
|
|
40050
|
+
padding-right: 2px;
|
|
40051
|
+
|
|
40052
|
+
box-sizing: border-box;
|
|
40053
|
+
|
|
40054
|
+
caret-color: black;
|
|
40055
|
+
padding-left: 3px;
|
|
40056
|
+
padding-right: 3px;
|
|
40057
|
+
outline: none;
|
|
40058
|
+
|
|
40059
|
+
p {
|
|
40060
|
+
margin-bottom: 0px;
|
|
40061
|
+
|
|
40062
|
+
span {
|
|
40063
|
+
white-space: pre-wrap;
|
|
40064
|
+
|
|
40065
|
+
&.${selectionIndicatorClass}:after {
|
|
40066
|
+
content: "${selectionIndicator}";
|
|
40067
|
+
color: ${selectionIndicatorColor};
|
|
40036
40068
|
}
|
|
40037
|
-
}
|
|
40038
|
-
}
|
|
40039
40069
|
|
|
40040
|
-
|
|
40041
|
-
|
|
40042
|
-
|
|
40043
|
-
|
|
40044
|
-
|
|
40045
|
-
|
|
40046
|
-
Section,
|
|
40047
|
-
RoundColorPicker,
|
|
40048
|
-
AxisDesignEditor,
|
|
40049
|
-
RadioSelection,
|
|
40050
|
-
ChartLegend,
|
|
40051
|
-
};
|
|
40052
|
-
static props = {
|
|
40053
|
-
figureId: String,
|
|
40054
|
-
definition: Object,
|
|
40055
|
-
updateChart: Function,
|
|
40056
|
-
canUpdateChart: { type: Function, optional: true },
|
|
40057
|
-
};
|
|
40058
|
-
axisChoices = CHART_AXIS_CHOICES;
|
|
40059
|
-
onUpdateShowSubTotals(showSubTotals) {
|
|
40060
|
-
this.props.updateChart(this.props.figureId, { showSubTotals });
|
|
40061
|
-
}
|
|
40062
|
-
onUpdateShowConnectorLines(showConnectorLines) {
|
|
40063
|
-
this.props.updateChart(this.props.figureId, { showConnectorLines });
|
|
40064
|
-
}
|
|
40065
|
-
onUpdateFirstValueAsSubtotal(firstValueAsSubtotal) {
|
|
40066
|
-
this.props.updateChart(this.props.figureId, { firstValueAsSubtotal });
|
|
40067
|
-
}
|
|
40068
|
-
updateColor(colorName, color) {
|
|
40069
|
-
this.props.updateChart(this.props.figureId, { [colorName]: color });
|
|
40070
|
-
}
|
|
40071
|
-
get axesList() {
|
|
40072
|
-
return [
|
|
40073
|
-
{ id: "x", name: _t("Horizontal axis") },
|
|
40074
|
-
{ id: "y", name: _t("Vertical axis") },
|
|
40075
|
-
];
|
|
40076
|
-
}
|
|
40077
|
-
get positiveValuesColor() {
|
|
40078
|
-
return (this.props.definition.positiveValuesColor ||
|
|
40079
|
-
CHART_WATERFALL_POSITIVE_COLOR);
|
|
40080
|
-
}
|
|
40081
|
-
get negativeValuesColor() {
|
|
40082
|
-
return (this.props.definition.negativeValuesColor ||
|
|
40083
|
-
CHART_WATERFALL_NEGATIVE_COLOR);
|
|
40084
|
-
}
|
|
40085
|
-
get subTotalValuesColor() {
|
|
40086
|
-
return (this.props.definition.subTotalValuesColor ||
|
|
40087
|
-
CHART_WATERFALL_SUBTOTAL_COLOR);
|
|
40088
|
-
}
|
|
40089
|
-
updateVerticalAxisPosition(value) {
|
|
40090
|
-
this.props.updateChart(this.props.figureId, {
|
|
40091
|
-
verticalAxisPosition: value,
|
|
40092
|
-
});
|
|
40070
|
+
&.${backgroundClass} {
|
|
40071
|
+
border-radius: 5px;
|
|
40072
|
+
background-color: lightgray;
|
|
40073
|
+
padding: 0px 1.5px 1.5px 1.5px;
|
|
40074
|
+
}
|
|
40075
|
+
}
|
|
40093
40076
|
}
|
|
40094
|
-
|
|
40095
|
-
|
|
40096
|
-
|
|
40097
|
-
|
|
40098
|
-
|
|
40099
|
-
|
|
40100
|
-
|
|
40101
|
-
|
|
40102
|
-
.add("scatter", {
|
|
40103
|
-
configuration: ScatterConfigPanel,
|
|
40104
|
-
design: ChartWithAxisDesignPanel,
|
|
40105
|
-
})
|
|
40106
|
-
.add("bar", {
|
|
40107
|
-
configuration: BarConfigPanel,
|
|
40108
|
-
design: ChartWithAxisDesignPanel,
|
|
40109
|
-
})
|
|
40110
|
-
.add("combo", {
|
|
40111
|
-
configuration: GenericChartConfigPanel,
|
|
40112
|
-
design: ComboChartDesignPanel,
|
|
40113
|
-
})
|
|
40114
|
-
.add("pie", {
|
|
40115
|
-
configuration: GenericChartConfigPanel,
|
|
40116
|
-
design: PieChartDesignPanel,
|
|
40117
|
-
})
|
|
40118
|
-
.add("gauge", {
|
|
40119
|
-
configuration: GaugeChartConfigPanel,
|
|
40120
|
-
design: GaugeChartDesignPanel,
|
|
40121
|
-
})
|
|
40122
|
-
.add("scorecard", {
|
|
40123
|
-
configuration: ScorecardChartConfigPanel,
|
|
40124
|
-
design: ScorecardChartDesignPanel,
|
|
40125
|
-
})
|
|
40126
|
-
.add("waterfall", {
|
|
40127
|
-
configuration: GenericChartConfigPanel,
|
|
40128
|
-
design: WaterfallChartDesignPanel,
|
|
40129
|
-
})
|
|
40130
|
-
.add("pyramid", {
|
|
40131
|
-
configuration: GenericChartConfigPanel,
|
|
40132
|
-
design: ChartWithAxisDesignPanel,
|
|
40133
|
-
})
|
|
40134
|
-
.add("radar", {
|
|
40135
|
-
configuration: GenericChartConfigPanel,
|
|
40136
|
-
design: RadarChartDesignPanel,
|
|
40137
|
-
})
|
|
40138
|
-
.add("geo", {
|
|
40139
|
-
configuration: GeoChartConfigPanel,
|
|
40140
|
-
design: GeoChartDesignPanel,
|
|
40141
|
-
});
|
|
40077
|
+
}
|
|
40078
|
+
.o-composer[placeholder]:empty:not(:focus):not(.active)::before {
|
|
40079
|
+
content: attr(placeholder);
|
|
40080
|
+
color: #bdbdbd;
|
|
40081
|
+
position: relative;
|
|
40082
|
+
top: 0%;
|
|
40083
|
+
pointer-events: none;
|
|
40084
|
+
}
|
|
40142
40085
|
|
|
40143
|
-
|
|
40144
|
-
|
|
40145
|
-
|
|
40146
|
-
|
|
40147
|
-
|
|
40148
|
-
}
|
|
40149
|
-
.o-type-selector-preview {
|
|
40150
|
-
left: 5px;
|
|
40151
|
-
top: 3px;
|
|
40152
|
-
.o-chart-preview {
|
|
40153
|
-
width: 24px;
|
|
40154
|
-
height: 24px;
|
|
40086
|
+
.fa-stack {
|
|
40087
|
+
/* reset stack size which is doubled by default */
|
|
40088
|
+
width: ${CLOSE_ICON_RADIUS * 2}px;
|
|
40089
|
+
height: ${CLOSE_ICON_RADIUS * 2}px;
|
|
40090
|
+
line-height: ${CLOSE_ICON_RADIUS * 2}px;
|
|
40155
40091
|
}
|
|
40156
|
-
}
|
|
40157
40092
|
|
|
40158
|
-
|
|
40159
|
-
|
|
40160
|
-
|
|
40161
|
-
|
|
40162
|
-
|
|
40163
|
-
|
|
40164
|
-
margin: 1px 2px;
|
|
40165
|
-
&.selected,
|
|
40166
|
-
&:hover {
|
|
40167
|
-
border: 1px solid ${ACTION_COLOR};
|
|
40168
|
-
background: ${BADGE_SELECTED_COLOR};
|
|
40169
|
-
padding: 2px 5px;
|
|
40093
|
+
.force-open-assistant {
|
|
40094
|
+
left: -1px;
|
|
40095
|
+
top: -1px;
|
|
40096
|
+
|
|
40097
|
+
.fa-question-circle {
|
|
40098
|
+
color: ${PRIMARY_BUTTON_BG};
|
|
40170
40099
|
}
|
|
40171
|
-
|
|
40172
|
-
|
|
40173
|
-
|
|
40100
|
+
}
|
|
40101
|
+
|
|
40102
|
+
.o-composer-assistant {
|
|
40103
|
+
position: absolute;
|
|
40104
|
+
margin: 1px 4px;
|
|
40105
|
+
|
|
40106
|
+
.o-semi-bold {
|
|
40107
|
+
/* FIXME: to remove in favor of Bootstrap
|
|
40108
|
+
* 'fw-semibold' when we upgrade to Bootstrap 5.2
|
|
40109
|
+
*/
|
|
40110
|
+
font-weight: 600 !important;
|
|
40174
40111
|
}
|
|
40175
40112
|
}
|
|
40176
40113
|
}
|
|
40177
40114
|
`;
|
|
40178
|
-
class
|
|
40179
|
-
static template = "o-spreadsheet-
|
|
40180
|
-
static
|
|
40181
|
-
|
|
40182
|
-
|
|
40183
|
-
|
|
40184
|
-
|
|
40185
|
-
|
|
40186
|
-
|
|
40187
|
-
|
|
40188
|
-
|
|
40189
|
-
|
|
40190
|
-
|
|
40191
|
-
|
|
40115
|
+
class Composer extends owl.Component {
|
|
40116
|
+
static template = "o-spreadsheet-Composer";
|
|
40117
|
+
static props = {
|
|
40118
|
+
focus: {
|
|
40119
|
+
validate: (value) => ["inactive", "cellFocus", "contentFocus"].includes(value),
|
|
40120
|
+
},
|
|
40121
|
+
inputStyle: { type: String, optional: true },
|
|
40122
|
+
rect: { type: Object, optional: true },
|
|
40123
|
+
delimitation: { type: Object, optional: true },
|
|
40124
|
+
onComposerCellFocused: { type: Function, optional: true },
|
|
40125
|
+
onComposerContentFocused: Function,
|
|
40126
|
+
isDefaultFocus: { type: Boolean, optional: true },
|
|
40127
|
+
onInputContextMenu: { type: Function, optional: true },
|
|
40128
|
+
composerStore: Object,
|
|
40129
|
+
placeholder: { type: String, optional: true },
|
|
40130
|
+
};
|
|
40131
|
+
static components = { TextValueProvider, FunctionDescriptionProvider };
|
|
40132
|
+
static defaultProps = {
|
|
40133
|
+
inputStyle: "",
|
|
40134
|
+
isDefaultFocus: false,
|
|
40135
|
+
};
|
|
40136
|
+
DOMFocusableElementStore;
|
|
40137
|
+
composerRef = owl.useRef("o_composer");
|
|
40138
|
+
contentHelper = new ContentEditableHelper(this.composerRef.el);
|
|
40139
|
+
composerState = owl.useState({
|
|
40140
|
+
positionStart: 0,
|
|
40141
|
+
positionEnd: 0,
|
|
40142
|
+
});
|
|
40143
|
+
autoCompleteState;
|
|
40144
|
+
functionDescriptionState = owl.useState({
|
|
40145
|
+
showDescription: false,
|
|
40146
|
+
functionName: "",
|
|
40147
|
+
functionDescription: {},
|
|
40148
|
+
argToFocus: 0,
|
|
40149
|
+
});
|
|
40150
|
+
assistant = owl.useState({
|
|
40151
|
+
forcedClosed: false,
|
|
40152
|
+
});
|
|
40153
|
+
compositionActive = false;
|
|
40154
|
+
spreadsheetRect = useSpreadsheetRect();
|
|
40155
|
+
get assistantStyle() {
|
|
40156
|
+
const composerRect = this.composerRef.el.getBoundingClientRect();
|
|
40157
|
+
const assistantStyle = {};
|
|
40158
|
+
assistantStyle["min-width"] = `${this.props.rect?.width || ASSISTANT_WIDTH}px`;
|
|
40159
|
+
const proposals = this.autoCompleteState.provider?.proposals;
|
|
40160
|
+
const proposalsHaveDescription = proposals?.some((proposal) => proposal.description);
|
|
40161
|
+
if (this.functionDescriptionState.showDescription || proposalsHaveDescription) {
|
|
40162
|
+
assistantStyle.width = `${ASSISTANT_WIDTH}px`;
|
|
40163
|
+
}
|
|
40164
|
+
if (this.props.delimitation && this.props.rect) {
|
|
40165
|
+
const { x: cellX, y: cellY, height: cellHeight } = this.props.rect;
|
|
40166
|
+
const remainingHeight = this.props.delimitation.height - (cellY + cellHeight);
|
|
40167
|
+
assistantStyle["max-height"] = `${remainingHeight}px`;
|
|
40168
|
+
if (cellY > remainingHeight) {
|
|
40169
|
+
const availableSpaceAbove = cellY;
|
|
40170
|
+
assistantStyle["max-height"] = `${availableSpaceAbove - CLOSE_ICON_RADIUS}px`;
|
|
40171
|
+
// render top
|
|
40172
|
+
// We compensate 2 px of margin on the assistant style + 1px for design reasons
|
|
40173
|
+
assistantStyle.top = `-3px`;
|
|
40174
|
+
assistantStyle.transform = `translate(0, -100%)`;
|
|
40192
40175
|
}
|
|
40193
|
-
|
|
40194
|
-
|
|
40176
|
+
if (cellX + ASSISTANT_WIDTH > this.props.delimitation.width) {
|
|
40177
|
+
// render left
|
|
40178
|
+
assistantStyle.right = `0px`;
|
|
40195
40179
|
}
|
|
40196
40180
|
}
|
|
40197
|
-
|
|
40198
|
-
|
|
40199
|
-
|
|
40200
|
-
|
|
40201
|
-
|
|
40181
|
+
else {
|
|
40182
|
+
assistantStyle["max-height"] = `${this.spreadsheetRect.height - composerRect.bottom}px`;
|
|
40183
|
+
if (composerRect.left + ASSISTANT_WIDTH + SCROLLBAR_WIDTH + CLOSE_ICON_RADIUS >
|
|
40184
|
+
this.spreadsheetRect.width) {
|
|
40185
|
+
assistantStyle.right = `${CLOSE_ICON_RADIUS}px`;
|
|
40186
|
+
}
|
|
40202
40187
|
}
|
|
40203
|
-
|
|
40204
|
-
}
|
|
40205
|
-
onTypeChange(type) {
|
|
40206
|
-
this.props.chartPanelStore.changeChartType(this.props.figureId, type);
|
|
40207
|
-
this.closePopover();
|
|
40208
|
-
}
|
|
40209
|
-
getChartDefinition(figureId) {
|
|
40210
|
-
return this.env.model.getters.getChartDefinition(figureId);
|
|
40188
|
+
return cssPropertiesToCss(assistantStyle);
|
|
40211
40189
|
}
|
|
40212
|
-
|
|
40213
|
-
|
|
40214
|
-
|
|
40215
|
-
|
|
40216
|
-
|
|
40217
|
-
|
|
40190
|
+
// we can't allow input events to be triggered while we remove and add back the content of the composer in processContent
|
|
40191
|
+
shouldProcessInputEvents = false;
|
|
40192
|
+
tokens = [];
|
|
40193
|
+
keyMapping = {
|
|
40194
|
+
Enter: (ev) => this.processEnterKey(ev, "down"),
|
|
40195
|
+
"Shift+Enter": (ev) => this.processEnterKey(ev, "up"),
|
|
40196
|
+
"Alt+Enter": this.processNewLineEvent,
|
|
40197
|
+
"Ctrl+Enter": this.processNewLineEvent,
|
|
40198
|
+
Escape: this.processEscapeKey,
|
|
40199
|
+
F2: (ev) => this.toggleEditionMode(ev),
|
|
40200
|
+
F4: (ev) => this.processF4Key(ev),
|
|
40201
|
+
Tab: (ev) => this.processTabKey(ev, "right"),
|
|
40202
|
+
"Shift+Tab": (ev) => this.processTabKey(ev, "left"),
|
|
40203
|
+
};
|
|
40204
|
+
keyCodeMapping = {
|
|
40205
|
+
NumpadDecimal: this.processNumpadDecimal,
|
|
40206
|
+
};
|
|
40207
|
+
setup() {
|
|
40208
|
+
this.DOMFocusableElementStore = useStore(DOMFocusableElementStore);
|
|
40209
|
+
this.autoCompleteState = useLocalStore(AutoCompleteStore);
|
|
40210
|
+
owl.onMounted(() => {
|
|
40211
|
+
const el = this.composerRef.el;
|
|
40212
|
+
if (this.props.isDefaultFocus) {
|
|
40213
|
+
this.DOMFocusableElementStore.setFocusableElement(el);
|
|
40214
|
+
}
|
|
40215
|
+
this.contentHelper.updateEl(el);
|
|
40216
|
+
});
|
|
40217
|
+
this.env.model.selection.observe(this, {
|
|
40218
|
+
handleEvent: () => this.autoCompleteState.hide(),
|
|
40219
|
+
});
|
|
40220
|
+
owl.onWillUnmount(() => {
|
|
40221
|
+
this.env.model.selection.detachObserver(this);
|
|
40222
|
+
});
|
|
40223
|
+
owl.useEffect(() => {
|
|
40224
|
+
this.processContent();
|
|
40225
|
+
if (document.activeElement === this.contentHelper.el &&
|
|
40226
|
+
this.props.composerStore.editionMode === "inactive" &&
|
|
40227
|
+
!this.props.isDefaultFocus) {
|
|
40228
|
+
this.DOMFocusableElementStore.focusableElement?.focus();
|
|
40229
|
+
}
|
|
40230
|
+
});
|
|
40231
|
+
owl.useEffect(() => {
|
|
40232
|
+
this.processTokenAtCursor();
|
|
40233
|
+
}, () => [this.props.composerStore.editionMode !== "inactive"]);
|
|
40218
40234
|
}
|
|
40219
|
-
|
|
40220
|
-
|
|
40221
|
-
|
|
40235
|
+
// ---------------------------------------------------------------------------
|
|
40236
|
+
// Handlers
|
|
40237
|
+
// ---------------------------------------------------------------------------
|
|
40238
|
+
processArrowKeys(ev) {
|
|
40239
|
+
const tokenAtCursor = this.props.composerStore.tokenAtCursor;
|
|
40240
|
+
if ((this.props.composerStore.isSelectingRange ||
|
|
40241
|
+
this.props.composerStore.editionMode === "inactive") &&
|
|
40242
|
+
!(["ArrowUp", "ArrowDown"].includes(ev.key) &&
|
|
40243
|
+
this.autoCompleteState.provider &&
|
|
40244
|
+
tokenAtCursor?.type !== "REFERENCE")) {
|
|
40245
|
+
this.functionDescriptionState.showDescription = false;
|
|
40246
|
+
this.autoCompleteState.hide();
|
|
40247
|
+
// Prevent the default content editable behavior which moves the cursor
|
|
40248
|
+
ev.preventDefault();
|
|
40249
|
+
ev.stopPropagation();
|
|
40250
|
+
updateSelectionWithArrowKeys(ev, this.env.model.selection);
|
|
40222
40251
|
return;
|
|
40223
40252
|
}
|
|
40224
|
-
const
|
|
40225
|
-
|
|
40226
|
-
|
|
40227
|
-
|
|
40228
|
-
|
|
40229
|
-
|
|
40230
|
-
}
|
|
40231
|
-
|
|
40232
|
-
|
|
40233
|
-
|
|
40234
|
-
this.
|
|
40253
|
+
const content = this.props.composerStore.currentContent;
|
|
40254
|
+
if (this.props.focus === "cellFocus" &&
|
|
40255
|
+
!this.autoCompleteState.provider &&
|
|
40256
|
+
!content.startsWith("=")) {
|
|
40257
|
+
this.props.composerStore.stopEdition();
|
|
40258
|
+
return;
|
|
40259
|
+
}
|
|
40260
|
+
// All arrow keys are processed: up and down should move autocomplete, left
|
|
40261
|
+
// and right should move the cursor.
|
|
40262
|
+
ev.stopPropagation();
|
|
40263
|
+
this.handleArrowKeysForAutocomplete(ev);
|
|
40235
40264
|
}
|
|
40236
|
-
|
|
40237
|
-
|
|
40238
|
-
|
|
40239
|
-
|
|
40240
|
-
|
|
40241
|
-
|
|
40242
|
-
activatePanel(panel) {
|
|
40243
|
-
this.panel = panel;
|
|
40265
|
+
handleArrowKeysForAutocomplete(ev) {
|
|
40266
|
+
// only for arrow up and down
|
|
40267
|
+
if (["ArrowUp", "ArrowDown"].includes(ev.key) && this.autoCompleteState.provider) {
|
|
40268
|
+
ev.preventDefault();
|
|
40269
|
+
this.autoCompleteState.moveSelection(ev.key === "ArrowDown" ? "next" : "previous");
|
|
40270
|
+
}
|
|
40244
40271
|
}
|
|
40245
|
-
|
|
40246
|
-
|
|
40247
|
-
|
|
40248
|
-
|
|
40249
|
-
|
|
40250
|
-
|
|
40272
|
+
processTabKey(ev, direction) {
|
|
40273
|
+
ev.preventDefault();
|
|
40274
|
+
ev.stopPropagation();
|
|
40275
|
+
if (this.props.composerStore.editionMode !== "inactive") {
|
|
40276
|
+
const state = this.autoCompleteState;
|
|
40277
|
+
if (state.provider && state.selectedIndex !== undefined) {
|
|
40278
|
+
const autoCompleteValue = state.provider.proposals[state.selectedIndex]?.text;
|
|
40279
|
+
if (autoCompleteValue) {
|
|
40280
|
+
this.autoComplete(autoCompleteValue);
|
|
40281
|
+
return;
|
|
40282
|
+
}
|
|
40283
|
+
}
|
|
40284
|
+
this.props.composerStore.stopEdition(direction);
|
|
40251
40285
|
}
|
|
40252
|
-
|
|
40253
|
-
|
|
40254
|
-
|
|
40255
|
-
|
|
40256
|
-
|
|
40257
|
-
|
|
40258
|
-
|
|
40259
|
-
|
|
40286
|
+
}
|
|
40287
|
+
processEnterKey(ev, direction) {
|
|
40288
|
+
ev.preventDefault();
|
|
40289
|
+
ev.stopPropagation();
|
|
40290
|
+
const state = this.autoCompleteState;
|
|
40291
|
+
if (state.provider && state.selectedIndex !== undefined) {
|
|
40292
|
+
const autoCompleteValue = state.provider.proposals[state.selectedIndex]?.text;
|
|
40293
|
+
if (autoCompleteValue) {
|
|
40294
|
+
this.autoComplete(autoCompleteValue);
|
|
40295
|
+
return;
|
|
40296
|
+
}
|
|
40260
40297
|
}
|
|
40261
|
-
|
|
40262
|
-
|
|
40263
|
-
|
|
40264
|
-
|
|
40265
|
-
|
|
40298
|
+
this.props.composerStore.stopEdition(direction);
|
|
40299
|
+
}
|
|
40300
|
+
processNewLineEvent(ev) {
|
|
40301
|
+
ev.preventDefault();
|
|
40302
|
+
ev.stopPropagation();
|
|
40303
|
+
const content = this.contentHelper.getText();
|
|
40304
|
+
const selection = this.contentHelper.getCurrentSelection();
|
|
40305
|
+
const start = Math.min(selection.start, selection.end);
|
|
40306
|
+
const end = Math.max(selection.start, selection.end);
|
|
40307
|
+
this.props.composerStore.stopComposerRangeSelection();
|
|
40308
|
+
this.props.composerStore.setCurrentContent(content.slice(0, start) + NEWLINE + content.slice(end), {
|
|
40309
|
+
start: start + 1,
|
|
40310
|
+
end: start + 1,
|
|
40266
40311
|
});
|
|
40312
|
+
this.processContent();
|
|
40267
40313
|
}
|
|
40268
|
-
|
|
40269
|
-
|
|
40270
|
-
|
|
40271
|
-
|
|
40272
|
-
...ChartClass.getChartDefinitionFromContextCreation(this.creationContexts[figureId]),
|
|
40273
|
-
...newChartInfo.subtypeDefinition,
|
|
40274
|
-
};
|
|
40314
|
+
processEscapeKey(ev) {
|
|
40315
|
+
this.props.composerStore.cancelEdition();
|
|
40316
|
+
ev.stopPropagation();
|
|
40317
|
+
ev.preventDefault();
|
|
40275
40318
|
}
|
|
40276
|
-
|
|
40277
|
-
|
|
40278
|
-
|
|
40279
|
-
|
|
40280
|
-
.o-panel {
|
|
40281
|
-
display: flex;
|
|
40282
|
-
.o-panel-element {
|
|
40283
|
-
flex: 1 0 auto;
|
|
40284
|
-
padding: 8px 0px;
|
|
40285
|
-
text-align: center;
|
|
40286
|
-
cursor: pointer;
|
|
40287
|
-
border-right: 1px solid ${GRAY_300};
|
|
40288
|
-
|
|
40289
|
-
&.inactive {
|
|
40290
|
-
color: ${TEXT_BODY};
|
|
40291
|
-
background-color: ${GRAY_100};
|
|
40292
|
-
border-bottom: 1px solid ${GRAY_300};
|
|
40293
|
-
}
|
|
40294
|
-
|
|
40295
|
-
&:not(.inactive) {
|
|
40296
|
-
color: ${TEXT_HEADING};
|
|
40297
|
-
border-bottom: 1px solid #fff;
|
|
40298
|
-
}
|
|
40299
|
-
|
|
40300
|
-
.fa {
|
|
40301
|
-
margin-right: 4px;
|
|
40302
|
-
}
|
|
40319
|
+
processF4Key(ev) {
|
|
40320
|
+
ev.stopPropagation();
|
|
40321
|
+
this.props.composerStore.cycleReferences();
|
|
40322
|
+
this.processContent();
|
|
40303
40323
|
}
|
|
40304
|
-
|
|
40305
|
-
|
|
40324
|
+
toggleEditionMode(ev) {
|
|
40325
|
+
ev.stopPropagation();
|
|
40326
|
+
this.props.composerStore.toggleEditionMode();
|
|
40327
|
+
this.processContent();
|
|
40306
40328
|
}
|
|
40307
|
-
|
|
40308
|
-
|
|
40309
|
-
|
|
40310
|
-
|
|
40311
|
-
|
|
40312
|
-
|
|
40313
|
-
|
|
40314
|
-
|
|
40315
|
-
|
|
40316
|
-
|
|
40329
|
+
processNumpadDecimal(ev) {
|
|
40330
|
+
ev.stopPropagation();
|
|
40331
|
+
ev.preventDefault();
|
|
40332
|
+
const locale = this.env.model.getters.getLocale();
|
|
40333
|
+
const selection = this.contentHelper.getCurrentSelection();
|
|
40334
|
+
const currentContent = this.props.composerStore.currentContent;
|
|
40335
|
+
const content = currentContent.slice(0, selection.start) +
|
|
40336
|
+
locale.decimalSeparator +
|
|
40337
|
+
currentContent.slice(selection.end);
|
|
40338
|
+
// Update composer even by hand rather than dispatching an InputEvent because untrusted inputs
|
|
40339
|
+
// events aren't handled natively by contentEditable
|
|
40340
|
+
this.props.composerStore.setCurrentContent(content, {
|
|
40341
|
+
start: selection.start + 1,
|
|
40342
|
+
end: selection.start + 1,
|
|
40343
|
+
});
|
|
40344
|
+
// We need to do the process content here in case there is no render between the keyDown and the
|
|
40345
|
+
// keyUp event
|
|
40346
|
+
this.processContent();
|
|
40317
40347
|
}
|
|
40318
|
-
|
|
40319
|
-
this.
|
|
40348
|
+
onCompositionStart() {
|
|
40349
|
+
this.compositionActive = true;
|
|
40320
40350
|
}
|
|
40321
|
-
|
|
40322
|
-
|
|
40351
|
+
onCompositionEnd() {
|
|
40352
|
+
this.compositionActive = false;
|
|
40353
|
+
}
|
|
40354
|
+
onKeydown(ev) {
|
|
40355
|
+
if (this.props.composerStore.editionMode === "inactive") {
|
|
40323
40356
|
return;
|
|
40324
40357
|
}
|
|
40325
|
-
|
|
40326
|
-
|
|
40327
|
-
|
|
40328
|
-
}
|
|
40329
|
-
|
|
40330
|
-
|
|
40331
|
-
|
|
40332
|
-
|
|
40333
|
-
}
|
|
40358
|
+
if (ev.key.startsWith("Arrow")) {
|
|
40359
|
+
this.processArrowKeys(ev);
|
|
40360
|
+
return;
|
|
40361
|
+
}
|
|
40362
|
+
let handler = this.keyMapping[keyboardEventToShortcutString(ev)] ||
|
|
40363
|
+
this.keyCodeMapping[keyboardEventToShortcutString(ev, "code")];
|
|
40364
|
+
if (handler) {
|
|
40365
|
+
handler.call(this, ev);
|
|
40366
|
+
}
|
|
40367
|
+
else {
|
|
40368
|
+
ev.stopPropagation();
|
|
40369
|
+
}
|
|
40334
40370
|
}
|
|
40335
|
-
|
|
40336
|
-
if (
|
|
40337
|
-
|
|
40371
|
+
onPaste(ev) {
|
|
40372
|
+
if (this.props.composerStore.editionMode !== "inactive") {
|
|
40373
|
+
// let the browser clipboard work
|
|
40374
|
+
ev.stopPropagation();
|
|
40375
|
+
}
|
|
40376
|
+
else {
|
|
40377
|
+
// the user meant to paste in the sheet, not open the composer with the pasted content
|
|
40378
|
+
// While we're not editing, we still have the focus and should therefore prevent
|
|
40379
|
+
// the native "paste" to occur.
|
|
40380
|
+
ev.preventDefault();
|
|
40338
40381
|
}
|
|
40339
|
-
const definition = {
|
|
40340
|
-
...this.getChartDefinition(this.figureId),
|
|
40341
|
-
...updateDefinition,
|
|
40342
|
-
};
|
|
40343
|
-
return this.env.model.canDispatch("UPDATE_CHART", {
|
|
40344
|
-
definition,
|
|
40345
|
-
id: figureId,
|
|
40346
|
-
sheetId: this.env.model.getters.getFigureSheetId(figureId),
|
|
40347
|
-
});
|
|
40348
40382
|
}
|
|
40349
|
-
|
|
40350
|
-
|
|
40383
|
+
/*
|
|
40384
|
+
* Triggered automatically by the content-editable between the keydown and key up
|
|
40385
|
+
* */
|
|
40386
|
+
onInput(ev) {
|
|
40387
|
+
if (!this.shouldProcessInputEvents) {
|
|
40351
40388
|
return;
|
|
40352
40389
|
}
|
|
40353
|
-
|
|
40354
|
-
|
|
40355
|
-
|
|
40356
|
-
|
|
40357
|
-
throw new Error("Chart not defined.");
|
|
40390
|
+
ev.stopPropagation();
|
|
40391
|
+
let content;
|
|
40392
|
+
if (this.props.composerStore.editionMode === "inactive") {
|
|
40393
|
+
content = ev.data || "";
|
|
40358
40394
|
}
|
|
40359
|
-
|
|
40360
|
-
|
|
40361
|
-
throw new Error("Chart not defined.");
|
|
40395
|
+
else {
|
|
40396
|
+
content = this.contentHelper.getText();
|
|
40362
40397
|
}
|
|
40363
|
-
|
|
40364
|
-
|
|
40365
|
-
throw new Error(`Component is not defined for type ${type}`);
|
|
40398
|
+
if (this.props.focus === "inactive") {
|
|
40399
|
+
return this.props.onComposerCellFocused?.(content);
|
|
40366
40400
|
}
|
|
40367
|
-
|
|
40368
|
-
|
|
40369
|
-
|
|
40370
|
-
|
|
40371
|
-
}
|
|
40372
|
-
}
|
|
40373
|
-
|
|
40374
|
-
class DOMFocusableElementStore {
|
|
40375
|
-
mutators = ["setFocusableElement"];
|
|
40376
|
-
focusableElement = undefined;
|
|
40377
|
-
setFocusableElement(element) {
|
|
40378
|
-
this.focusableElement = element;
|
|
40379
|
-
}
|
|
40380
|
-
}
|
|
40381
|
-
|
|
40382
|
-
css /* scss */ `
|
|
40383
|
-
.o-autocomplete-dropdown {
|
|
40384
|
-
pointer-events: auto;
|
|
40385
|
-
cursor: pointer;
|
|
40386
|
-
background-color: #fff;
|
|
40387
|
-
max-width: 400px;
|
|
40388
|
-
z-index: 1;
|
|
40389
|
-
|
|
40390
|
-
.o-autocomplete-value-focus {
|
|
40391
|
-
background-color: #f2f2f2;
|
|
40392
|
-
}
|
|
40393
|
-
|
|
40394
|
-
& > div {
|
|
40395
|
-
padding: 1px 5px 5px 5px;
|
|
40396
|
-
.o-autocomplete-description {
|
|
40397
|
-
padding-left: 5px;
|
|
40398
|
-
font-size: 11px;
|
|
40401
|
+
let selection = this.contentHelper.getCurrentSelection();
|
|
40402
|
+
this.props.composerStore.stopComposerRangeSelection();
|
|
40403
|
+
this.props.composerStore.setCurrentContent(content, selection);
|
|
40404
|
+
this.processTokenAtCursor();
|
|
40399
40405
|
}
|
|
40400
|
-
|
|
40401
|
-
|
|
40402
|
-
|
|
40403
|
-
class TextValueProvider extends owl.Component {
|
|
40404
|
-
static template = "o-spreadsheet-TextValueProvider";
|
|
40405
|
-
static props = {
|
|
40406
|
-
proposals: Array,
|
|
40407
|
-
selectedIndex: { type: Number, optional: true },
|
|
40408
|
-
onValueSelected: Function,
|
|
40409
|
-
onValueHovered: Function,
|
|
40410
|
-
};
|
|
40411
|
-
autoCompleteListRef = owl.useRef("autoCompleteList");
|
|
40412
|
-
setup() {
|
|
40413
|
-
owl.useEffect(() => {
|
|
40414
|
-
const selectedIndex = this.props.selectedIndex;
|
|
40415
|
-
if (selectedIndex === undefined) {
|
|
40406
|
+
onKeyup(ev) {
|
|
40407
|
+
if (this.contentHelper.el === document.activeElement) {
|
|
40408
|
+
if (this.autoCompleteState.provider && ["ArrowUp", "ArrowDown"].includes(ev.key)) {
|
|
40416
40409
|
return;
|
|
40417
40410
|
}
|
|
40418
|
-
|
|
40419
|
-
|
|
40420
|
-
|
|
40421
|
-
|
|
40422
|
-
|
|
40423
|
-
|
|
40424
|
-
|
|
40425
|
-
|
|
40426
|
-
|
|
40427
|
-
provider;
|
|
40428
|
-
get selectedProposal() {
|
|
40429
|
-
if (this.selectedIndex === undefined || this.provider === undefined) {
|
|
40430
|
-
return undefined;
|
|
40411
|
+
if (this.props.composerStore.isSelectingRange && ev.key?.startsWith("Arrow")) {
|
|
40412
|
+
return;
|
|
40413
|
+
}
|
|
40414
|
+
const { start: oldStart, end: oldEnd } = this.props.composerStore.composerSelection;
|
|
40415
|
+
const { start, end } = this.contentHelper.getCurrentSelection();
|
|
40416
|
+
if (start !== oldStart || end !== oldEnd) {
|
|
40417
|
+
this.props.composerStore.changeComposerCursorSelection(start, end);
|
|
40418
|
+
}
|
|
40419
|
+
this.processTokenAtCursor();
|
|
40431
40420
|
}
|
|
40432
|
-
return this.provider.proposals[this.selectedIndex];
|
|
40433
|
-
}
|
|
40434
|
-
useProvider(provider) {
|
|
40435
|
-
this.provider = provider;
|
|
40436
|
-
this.selectedIndex = provider.autoSelectFirstProposal ? 0 : undefined;
|
|
40437
|
-
}
|
|
40438
|
-
hide() {
|
|
40439
|
-
this.provider = undefined;
|
|
40440
|
-
this.selectedIndex = undefined;
|
|
40441
|
-
}
|
|
40442
|
-
selectIndex(index) {
|
|
40443
|
-
this.selectedIndex = index;
|
|
40444
40421
|
}
|
|
40445
|
-
|
|
40446
|
-
if (
|
|
40422
|
+
onBlur(ev) {
|
|
40423
|
+
if (this.props.composerStore.editionMode === "inactive") {
|
|
40447
40424
|
return;
|
|
40448
40425
|
}
|
|
40449
|
-
|
|
40450
|
-
|
|
40426
|
+
const target = ev.relatedTarget;
|
|
40427
|
+
if (!target || !(target instanceof HTMLElement)) {
|
|
40428
|
+
this.props.composerStore.stopEdition();
|
|
40451
40429
|
return;
|
|
40452
40430
|
}
|
|
40453
|
-
if (
|
|
40454
|
-
this.
|
|
40455
|
-
|
|
40456
|
-
this.selectedIndex = this.provider.proposals.length - 1;
|
|
40457
|
-
}
|
|
40431
|
+
if (target.attributes.getNamedItem("composerFocusableElement")) {
|
|
40432
|
+
this.contentHelper.el.focus();
|
|
40433
|
+
return;
|
|
40458
40434
|
}
|
|
40459
|
-
|
|
40460
|
-
|
|
40435
|
+
if (target.classList.contains("o-composer")) {
|
|
40436
|
+
return;
|
|
40461
40437
|
}
|
|
40438
|
+
this.props.composerStore.stopEdition();
|
|
40462
40439
|
}
|
|
40463
|
-
|
|
40464
|
-
|
|
40465
|
-
class ContentEditableHelper {
|
|
40466
|
-
// todo make el private and expose dedicated methods
|
|
40467
|
-
el;
|
|
40468
|
-
constructor(el) {
|
|
40469
|
-
this.el = el;
|
|
40470
|
-
}
|
|
40471
|
-
updateEl(el) {
|
|
40472
|
-
this.el = el;
|
|
40440
|
+
updateAutoCompleteIndex(index) {
|
|
40441
|
+
this.autoCompleteState.selectIndex(clip(0, index, 10));
|
|
40473
40442
|
}
|
|
40474
40443
|
/**
|
|
40475
|
-
*
|
|
40444
|
+
* This is required to ensure the content helper selection is
|
|
40445
|
+
* properly updated on "onclick" events. Depending on the browser,
|
|
40446
|
+
* the callback onClick from the composer will be executed before
|
|
40447
|
+
* the selection was updated in the dom, which means we capture an
|
|
40448
|
+
* wrong selection which is then forced upon the content helper on
|
|
40449
|
+
* processContent.
|
|
40476
40450
|
*/
|
|
40477
|
-
|
|
40478
|
-
|
|
40479
|
-
|
|
40480
|
-
if (currentStart === start && currentEnd === end) {
|
|
40451
|
+
onMousedown(ev) {
|
|
40452
|
+
if (ev.button > 0) {
|
|
40453
|
+
// not main button, probably a context menu
|
|
40481
40454
|
return;
|
|
40482
40455
|
}
|
|
40483
|
-
|
|
40484
|
-
|
|
40485
|
-
|
|
40456
|
+
this.contentHelper.removeSelection();
|
|
40457
|
+
}
|
|
40458
|
+
onClick() {
|
|
40459
|
+
if (this.env.model.getters.isReadonly()) {
|
|
40460
|
+
return;
|
|
40486
40461
|
}
|
|
40487
|
-
const
|
|
40488
|
-
|
|
40489
|
-
|
|
40490
|
-
|
|
40462
|
+
const newSelection = this.contentHelper.getCurrentSelection();
|
|
40463
|
+
this.props.composerStore.stopComposerRangeSelection();
|
|
40464
|
+
this.props.onComposerContentFocused();
|
|
40465
|
+
this.props.composerStore.changeComposerCursorSelection(newSelection.start, newSelection.end);
|
|
40466
|
+
this.processTokenAtCursor();
|
|
40467
|
+
}
|
|
40468
|
+
onDblClick() {
|
|
40469
|
+
if (this.env.model.getters.isReadonly()) {
|
|
40470
|
+
return;
|
|
40491
40471
|
}
|
|
40492
|
-
|
|
40493
|
-
|
|
40494
|
-
|
|
40495
|
-
|
|
40472
|
+
const composerContent = this.props.composerStore.currentContent;
|
|
40473
|
+
const isValidFormula = composerContent.startsWith("=");
|
|
40474
|
+
if (isValidFormula) {
|
|
40475
|
+
const tokens = this.props.composerStore.currentTokens;
|
|
40476
|
+
const currentSelection = this.contentHelper.getCurrentSelection();
|
|
40477
|
+
if (currentSelection.start === currentSelection.end)
|
|
40478
|
+
return;
|
|
40479
|
+
const currentSelectedText = composerContent.substring(currentSelection.start, currentSelection.end);
|
|
40480
|
+
const token = tokens.filter((token) => token.value.includes(currentSelectedText) &&
|
|
40481
|
+
token.start <= currentSelection.start &&
|
|
40482
|
+
token.end >= currentSelection.end)[0];
|
|
40483
|
+
if (!token) {
|
|
40484
|
+
return;
|
|
40485
|
+
}
|
|
40486
|
+
if (token.type === "REFERENCE") {
|
|
40487
|
+
this.props.composerStore.changeComposerCursorSelection(token.start, token.end);
|
|
40488
|
+
}
|
|
40496
40489
|
}
|
|
40497
|
-
|
|
40498
|
-
|
|
40499
|
-
|
|
40490
|
+
}
|
|
40491
|
+
onContextMenu(ev) {
|
|
40492
|
+
if (this.props.composerStore.editionMode === "inactive") {
|
|
40493
|
+
this.props.onInputContextMenu?.(ev);
|
|
40500
40494
|
}
|
|
40501
|
-
|
|
40502
|
-
|
|
40503
|
-
|
|
40504
|
-
|
|
40505
|
-
|
|
40506
|
-
|
|
40507
|
-
|
|
40508
|
-
|
|
40509
|
-
|
|
40510
|
-
|
|
40495
|
+
}
|
|
40496
|
+
closeAssistant() {
|
|
40497
|
+
this.assistant.forcedClosed = true;
|
|
40498
|
+
}
|
|
40499
|
+
openAssistant() {
|
|
40500
|
+
this.assistant.forcedClosed = false;
|
|
40501
|
+
}
|
|
40502
|
+
// ---------------------------------------------------------------------------
|
|
40503
|
+
// Private
|
|
40504
|
+
// ---------------------------------------------------------------------------
|
|
40505
|
+
processContent() {
|
|
40506
|
+
if (this.compositionActive) {
|
|
40507
|
+
return;
|
|
40508
|
+
}
|
|
40509
|
+
this.shouldProcessInputEvents = false;
|
|
40510
|
+
if (this.props.focus !== "inactive" && document.activeElement !== this.contentHelper.el) {
|
|
40511
|
+
this.contentHelper.el.focus();
|
|
40512
|
+
}
|
|
40513
|
+
const content = this.getContentLines();
|
|
40514
|
+
this.contentHelper.setText(content);
|
|
40515
|
+
if (content.length !== 0 && content.length[0] !== 0) {
|
|
40516
|
+
if (this.props.focus !== "inactive") {
|
|
40517
|
+
// Put the cursor back where it was before the rendering
|
|
40518
|
+
const { start, end } = this.props.composerStore.composerSelection;
|
|
40519
|
+
this.contentHelper.selectRange(start, end);
|
|
40511
40520
|
}
|
|
40512
|
-
|
|
40513
|
-
let endNode = this.findChildAtCharacterIndex(end);
|
|
40514
|
-
range.setStart(startNode.node, startNode.offset);
|
|
40515
|
-
range.setEnd(endNode.node, endNode.offset);
|
|
40521
|
+
this.contentHelper.scrollSelectionIntoView();
|
|
40516
40522
|
}
|
|
40523
|
+
this.shouldProcessInputEvents = true;
|
|
40517
40524
|
}
|
|
40518
40525
|
/**
|
|
40519
|
-
*
|
|
40526
|
+
* Get the HTML content corresponding to the current composer token, divided by lines.
|
|
40520
40527
|
*/
|
|
40521
|
-
|
|
40522
|
-
let
|
|
40523
|
-
|
|
40524
|
-
|
|
40525
|
-
|
|
40526
|
-
|
|
40527
|
-
|
|
40528
|
-
|
|
40529
|
-
|
|
40530
|
-
|
|
40531
|
-
|
|
40532
|
-
|
|
40533
|
-
|
|
40534
|
-
|
|
40535
|
-
|
|
40536
|
-
|
|
40528
|
+
getContentLines() {
|
|
40529
|
+
let value = this.props.composerStore.currentContent;
|
|
40530
|
+
const isValidFormula = value.startsWith("=");
|
|
40531
|
+
if (value === "") {
|
|
40532
|
+
return [];
|
|
40533
|
+
}
|
|
40534
|
+
else if (isValidFormula && this.props.focus !== "inactive") {
|
|
40535
|
+
return this.splitHtmlContentIntoLines(this.getHtmlContentFromTokens());
|
|
40536
|
+
}
|
|
40537
|
+
return this.splitHtmlContentIntoLines([{ value }]);
|
|
40538
|
+
}
|
|
40539
|
+
getHtmlContentFromTokens() {
|
|
40540
|
+
const tokens = this.props.composerStore.currentTokens;
|
|
40541
|
+
const result = [];
|
|
40542
|
+
const { end, start } = this.props.composerStore.composerSelection;
|
|
40543
|
+
for (const token of tokens) {
|
|
40544
|
+
let color = token.color || DEFAULT_TOKEN_COLOR;
|
|
40545
|
+
if (token.isBlurred) {
|
|
40546
|
+
color = setColorAlpha(color, 0.5);
|
|
40537
40547
|
}
|
|
40538
|
-
|
|
40539
|
-
if (
|
|
40540
|
-
|
|
40541
|
-
|
|
40542
|
-
|
|
40543
|
-
|
|
40544
|
-
|
|
40545
|
-
|
|
40548
|
+
result.push({ value: token.value, color });
|
|
40549
|
+
if (token.type === "REFERENCE" &&
|
|
40550
|
+
this.props.composerStore.tokenAtCursor === token &&
|
|
40551
|
+
this.props.composerStore.editionMode === "selecting") {
|
|
40552
|
+
result[result.length - 1].class = "text-decoration-underline";
|
|
40553
|
+
}
|
|
40554
|
+
if (end === start && token.isParenthesisLinkedToCursor) {
|
|
40555
|
+
result[result.length - 1].class = backgroundClass;
|
|
40556
|
+
}
|
|
40557
|
+
if (this.props.composerStore.showSelectionIndicator && end === start && end === token.end) {
|
|
40558
|
+
result[result.length - 1].class = selectionIndicatorClass;
|
|
40546
40559
|
}
|
|
40547
|
-
} while (!current.done && usedCharacters);
|
|
40548
|
-
if (current.value) {
|
|
40549
|
-
return { node: current.value, offset: usedCharacters };
|
|
40550
40560
|
}
|
|
40551
|
-
return
|
|
40561
|
+
return result;
|
|
40552
40562
|
}
|
|
40553
40563
|
/**
|
|
40554
|
-
*
|
|
40555
|
-
*
|
|
40556
|
-
*
|
|
40557
|
-
* The function will apply the diff between the current content and the new content to avoid the systematic
|
|
40558
|
-
* destruction of DOM elements which interferes with IME[1]
|
|
40559
|
-
*
|
|
40560
|
-
* Each line of text will be encapsulated in a paragraph element.
|
|
40561
|
-
* Each span will have its own fontcolor and specific class if provided in the HtmlContent object.
|
|
40562
|
-
*
|
|
40563
|
-
* [1] https://developer.mozilla.org/en-US/docs/Glossary/Input_method_editor
|
|
40564
|
+
* Split an array of HTMLContents into lines. Each NEWLINE character encountered will create a new
|
|
40565
|
+
* line. Contents can be split into multiple parts if they contain multiple NEWLINE characters.
|
|
40564
40566
|
*/
|
|
40565
|
-
|
|
40566
|
-
|
|
40567
|
-
|
|
40568
|
-
|
|
40569
|
-
|
|
40570
|
-
|
|
40571
|
-
|
|
40572
|
-
|
|
40573
|
-
|
|
40574
|
-
|
|
40575
|
-
|
|
40576
|
-
let p;
|
|
40577
|
-
if (childElement && childElement.nodeName === "P") {
|
|
40578
|
-
p = childElement;
|
|
40579
|
-
}
|
|
40580
|
-
else {
|
|
40581
|
-
newChild = true;
|
|
40582
|
-
p = document.createElement("p");
|
|
40583
|
-
}
|
|
40584
|
-
const lineLength = line.length;
|
|
40585
|
-
const existingChildren = Array.from(p.childNodes);
|
|
40586
|
-
for (let j = 0; j < lineLength; j++) {
|
|
40587
|
-
const content = line[j];
|
|
40588
|
-
const child = existingChildren[j];
|
|
40589
|
-
// child nodes can be multiple types of nodes: Span, Text, Div, etc...
|
|
40590
|
-
// We can only modify a node in place if it has the same type as the content
|
|
40591
|
-
// that we would insert, which are spans.
|
|
40592
|
-
// Otherwise, it means that the node has been input by the user, through the keyboard or a copy/paste
|
|
40593
|
-
// @ts-ignore (somehow required because jest does not like child.tagName despite the prior check)
|
|
40594
|
-
const childIsSpan = child && "tagName" in child && child.tagName === "SPAN";
|
|
40595
|
-
if (childIsSpan && compareContentToSpanElement(content, child)) {
|
|
40596
|
-
continue;
|
|
40597
|
-
}
|
|
40598
|
-
// this is an empty line in the content
|
|
40599
|
-
if (!content.value && !content.class) {
|
|
40600
|
-
if (child)
|
|
40601
|
-
p.removeChild(child);
|
|
40602
|
-
continue;
|
|
40603
|
-
}
|
|
40604
|
-
const span = document.createElement("span");
|
|
40605
|
-
span.innerText = content.value;
|
|
40606
|
-
span.style.color = content.color || "";
|
|
40607
|
-
if (content.class) {
|
|
40608
|
-
span.classList.add(content.class);
|
|
40609
|
-
}
|
|
40610
|
-
if (child) {
|
|
40611
|
-
p.replaceChild(span, child);
|
|
40612
|
-
}
|
|
40613
|
-
else {
|
|
40614
|
-
p.appendChild(span);
|
|
40567
|
+
splitHtmlContentIntoLines(contents) {
|
|
40568
|
+
const contentSplitInLines = [];
|
|
40569
|
+
let currentLine = [];
|
|
40570
|
+
for (const content of contents) {
|
|
40571
|
+
if (content.value.includes(NEWLINE)) {
|
|
40572
|
+
const lines = content.value.split(NEWLINE);
|
|
40573
|
+
const lastLine = lines.pop();
|
|
40574
|
+
for (const line of lines) {
|
|
40575
|
+
currentLine.push({ color: content.color, value: line }); // don't copy class, only last line should keep it
|
|
40576
|
+
contentSplitInLines.push(currentLine);
|
|
40577
|
+
currentLine = [];
|
|
40615
40578
|
}
|
|
40579
|
+
currentLine.push({ ...content, value: lastLine });
|
|
40616
40580
|
}
|
|
40617
|
-
|
|
40618
|
-
|
|
40619
|
-
p.removeChild(existingChildren[i]);
|
|
40620
|
-
}
|
|
40581
|
+
else {
|
|
40582
|
+
currentLine.push(content);
|
|
40621
40583
|
}
|
|
40622
|
-
|
|
40623
|
-
|
|
40624
|
-
|
|
40625
|
-
|
|
40626
|
-
|
|
40584
|
+
}
|
|
40585
|
+
if (currentLine.length) {
|
|
40586
|
+
contentSplitInLines.push(currentLine);
|
|
40587
|
+
}
|
|
40588
|
+
// Remove useless empty contents
|
|
40589
|
+
const filteredLines = [];
|
|
40590
|
+
for (const line of contentSplitInLines) {
|
|
40591
|
+
if (line.every(this.isContentEmpty)) {
|
|
40592
|
+
filteredLines.push([line[0]]);
|
|
40627
40593
|
}
|
|
40628
|
-
|
|
40629
|
-
|
|
40630
|
-
if (childElement) {
|
|
40631
|
-
this.el.replaceChild(p, childElement);
|
|
40632
|
-
}
|
|
40633
|
-
else {
|
|
40634
|
-
this.el.appendChild(p);
|
|
40635
|
-
}
|
|
40594
|
+
else {
|
|
40595
|
+
filteredLines.push(line.filter((content) => !this.isContentEmpty(content)));
|
|
40636
40596
|
}
|
|
40637
40597
|
}
|
|
40638
|
-
|
|
40639
|
-
|
|
40640
|
-
|
|
40598
|
+
return filteredLines;
|
|
40599
|
+
}
|
|
40600
|
+
isContentEmpty(content) {
|
|
40601
|
+
return !(content.value || content.class);
|
|
40602
|
+
}
|
|
40603
|
+
/**
|
|
40604
|
+
* Compute the state of the composer from the tokenAtCursor.
|
|
40605
|
+
* If the token is a function or symbol (that isn't a cell/range reference) we have to initialize
|
|
40606
|
+
* the autocomplete engine otherwise we initialize the formula assistant.
|
|
40607
|
+
*/
|
|
40608
|
+
processTokenAtCursor() {
|
|
40609
|
+
let content = this.props.composerStore.currentContent;
|
|
40610
|
+
if (this.autoCompleteState.provider) {
|
|
40611
|
+
this.autoCompleteState.hide();
|
|
40612
|
+
}
|
|
40613
|
+
this.functionDescriptionState.showDescription = false;
|
|
40614
|
+
const autoCompleteProvider = this.props.composerStore.autocompleteProvider;
|
|
40615
|
+
if (autoCompleteProvider) {
|
|
40616
|
+
this.autoCompleteState.useProvider(autoCompleteProvider);
|
|
40617
|
+
}
|
|
40618
|
+
const token = this.props.composerStore.tokenAtCursor;
|
|
40619
|
+
if (content.startsWith("=") && token && token.type !== "SYMBOL") {
|
|
40620
|
+
const tokenContext = token.functionContext;
|
|
40621
|
+
const parentFunction = tokenContext?.parent.toUpperCase();
|
|
40622
|
+
if (tokenContext &&
|
|
40623
|
+
parentFunction &&
|
|
40624
|
+
parentFunction in functions &&
|
|
40625
|
+
token.type !== "UNKNOWN") {
|
|
40626
|
+
// initialize Formula Assistant
|
|
40627
|
+
const description = functions[parentFunction];
|
|
40628
|
+
const argPosition = tokenContext.argPosition;
|
|
40629
|
+
this.functionDescriptionState.functionName = parentFunction;
|
|
40630
|
+
this.functionDescriptionState.functionDescription = description;
|
|
40631
|
+
this.functionDescriptionState.argToFocus = description.getArgToFocus(argPosition + 1) - 1;
|
|
40632
|
+
this.functionDescriptionState.showDescription = true;
|
|
40641
40633
|
}
|
|
40642
40634
|
}
|
|
40643
40635
|
}
|
|
40644
|
-
|
|
40645
|
-
|
|
40646
|
-
if (!focusedNode || !this.el.contains(focusedNode))
|
|
40636
|
+
autoComplete(value) {
|
|
40637
|
+
if (!value || this.assistant.forcedClosed) {
|
|
40647
40638
|
return;
|
|
40648
|
-
|
|
40649
|
-
|
|
40639
|
+
}
|
|
40640
|
+
this.autoCompleteState.provider?.selectProposal(value);
|
|
40641
|
+
this.processTokenAtCursor();
|
|
40650
40642
|
}
|
|
40651
|
-
|
|
40652
|
-
|
|
40653
|
-
|
|
40654
|
-
|
|
40655
|
-
|
|
40656
|
-
|
|
40643
|
+
}
|
|
40644
|
+
|
|
40645
|
+
class StandaloneComposerStore extends AbstractComposerStore {
|
|
40646
|
+
args;
|
|
40647
|
+
constructor(get, args) {
|
|
40648
|
+
super(get);
|
|
40649
|
+
this.args = args;
|
|
40650
|
+
this._currentContent = this.getComposerContent();
|
|
40657
40651
|
}
|
|
40658
|
-
|
|
40659
|
-
|
|
40660
|
-
|
|
40661
|
-
|
|
40662
|
-
|
|
40652
|
+
getAutoCompleteProviders() {
|
|
40653
|
+
const providersDefinitions = super.getAutoCompleteProviders();
|
|
40654
|
+
const contextualAutocomplete = this.args().contextualAutocomplete;
|
|
40655
|
+
if (contextualAutocomplete) {
|
|
40656
|
+
providersDefinitions.push(contextualAutocomplete);
|
|
40663
40657
|
}
|
|
40658
|
+
return providersDefinitions;
|
|
40664
40659
|
}
|
|
40665
40660
|
/**
|
|
40666
|
-
*
|
|
40661
|
+
* Replace the current reference selected by the new one.
|
|
40667
40662
|
* */
|
|
40668
|
-
|
|
40669
|
-
|
|
40663
|
+
getZoneReference(zone) {
|
|
40664
|
+
const res = super.getZoneReference(zone);
|
|
40665
|
+
if (this.args().defaultStatic) {
|
|
40666
|
+
return setXcToFixedReferenceType(res, "colrow");
|
|
40667
|
+
}
|
|
40668
|
+
return res;
|
|
40670
40669
|
}
|
|
40671
|
-
|
|
40672
|
-
|
|
40673
|
-
|
|
40674
|
-
|
|
40675
|
-
|
|
40676
|
-
|
|
40677
|
-
|
|
40678
|
-
|
|
40679
|
-
|
|
40680
|
-
|
|
40681
|
-
|
|
40682
|
-
) {
|
|
40683
|
-
if (isFirstParagraph) {
|
|
40684
|
-
isFirstParagraph = false;
|
|
40685
|
-
}
|
|
40686
|
-
else {
|
|
40687
|
-
text += NEWLINE;
|
|
40670
|
+
getComposerContent() {
|
|
40671
|
+
if (this.editionMode === "inactive") {
|
|
40672
|
+
// References in the content might not be linked to the current active sheet
|
|
40673
|
+
// We here force the sheet name prefix for all references that are not in
|
|
40674
|
+
// the current active sheet
|
|
40675
|
+
const defaultRangeSheetId = this.args().defaultRangeSheetId;
|
|
40676
|
+
return rangeTokenize(this.args().content)
|
|
40677
|
+
.map((token) => {
|
|
40678
|
+
if (token.type === "REFERENCE") {
|
|
40679
|
+
const range = this.getters.getRangeFromSheetXC(defaultRangeSheetId, token.value);
|
|
40680
|
+
return this.getters.getRangeString(range, this.getters.getActiveSheetId());
|
|
40688
40681
|
}
|
|
40682
|
+
return token.value;
|
|
40683
|
+
})
|
|
40684
|
+
.join("");
|
|
40685
|
+
}
|
|
40686
|
+
return this._currentContent;
|
|
40687
|
+
}
|
|
40688
|
+
stopEdition() {
|
|
40689
|
+
this._stopEdition();
|
|
40690
|
+
}
|
|
40691
|
+
confirmEdition(content) {
|
|
40692
|
+
this.args().onConfirm(content);
|
|
40693
|
+
}
|
|
40694
|
+
getTokenColor(token) {
|
|
40695
|
+
if (token.type === "SYMBOL") {
|
|
40696
|
+
const matchedColor = this.args().getContextualColoredSymbolToken?.(token);
|
|
40697
|
+
if (matchedColor) {
|
|
40698
|
+
return matchedColor;
|
|
40689
40699
|
}
|
|
40690
|
-
current = it.next();
|
|
40691
40700
|
}
|
|
40692
|
-
return
|
|
40701
|
+
return super.getTokenColor(token);
|
|
40693
40702
|
}
|
|
40694
40703
|
}
|
|
40695
|
-
function compareContentToSpanElement(content, node) {
|
|
40696
|
-
const contentColor = content.color ? toHex(content.color) : "";
|
|
40697
|
-
const nodeColor = node.style?.color ? toHex(node.style.color) : "";
|
|
40698
|
-
const sameColor = contentColor === nodeColor;
|
|
40699
|
-
const sameClass = deepEquals([content.class], [...node.classList]);
|
|
40700
|
-
const sameContent = node.innerText === content.value;
|
|
40701
|
-
return sameColor && sameClass && sameContent;
|
|
40702
|
-
}
|
|
40703
40704
|
|
|
40704
|
-
// -----------------------------------------------------------------------------
|
|
40705
|
-
// Formula Assistant component
|
|
40706
|
-
// -----------------------------------------------------------------------------
|
|
40707
40705
|
css /* scss */ `
|
|
40708
|
-
.o-
|
|
40709
|
-
|
|
40710
|
-
|
|
40711
|
-
|
|
40712
|
-
|
|
40713
|
-
|
|
40714
|
-
|
|
40715
|
-
|
|
40716
|
-
|
|
40717
|
-
|
|
40718
|
-
border-bottom: 1px solid gray;
|
|
40719
|
-
}
|
|
40720
|
-
.o-formula-assistant-arg-description {
|
|
40721
|
-
font-size: 85%;
|
|
40722
|
-
}
|
|
40723
|
-
.o-formula-assistant-focus {
|
|
40724
|
-
div:first-child,
|
|
40725
|
-
span {
|
|
40726
|
-
color: ${COMPOSER_ASSISTANT_COLOR};
|
|
40727
|
-
text-shadow: 0px 0px 1px ${COMPOSER_ASSISTANT_COLOR};
|
|
40706
|
+
.o-spreadsheet {
|
|
40707
|
+
.o-standalone-composer {
|
|
40708
|
+
min-height: 24px;
|
|
40709
|
+
box-sizing: border-box;
|
|
40710
|
+
|
|
40711
|
+
border-bottom: 1px solid;
|
|
40712
|
+
border-color: ${GRAY_300};
|
|
40713
|
+
|
|
40714
|
+
&.active {
|
|
40715
|
+
border-color: ${ACTION_COLOR};
|
|
40728
40716
|
}
|
|
40729
|
-
|
|
40730
|
-
|
|
40717
|
+
|
|
40718
|
+
&.o-invalid {
|
|
40719
|
+
border-bottom: 2px solid red;
|
|
40720
|
+
}
|
|
40721
|
+
|
|
40722
|
+
/* As the standalone composer is potentially very small (eg. in a side panel), we remove the scrollbar display */
|
|
40723
|
+
scrollbar-width: none; /* Firefox */
|
|
40724
|
+
&::-webkit-scrollbar {
|
|
40725
|
+
display: none;
|
|
40731
40726
|
}
|
|
40732
|
-
}
|
|
40733
|
-
.o-formula-assistant-gray {
|
|
40734
|
-
color: gray;
|
|
40735
40727
|
}
|
|
40736
40728
|
}
|
|
40737
40729
|
`;
|
|
40738
|
-
class
|
|
40739
|
-
static template = "o-spreadsheet-
|
|
40730
|
+
class StandaloneComposer extends owl.Component {
|
|
40731
|
+
static template = "o-spreadsheet-StandaloneComposer";
|
|
40740
40732
|
static props = {
|
|
40741
|
-
|
|
40742
|
-
|
|
40743
|
-
|
|
40733
|
+
composerContent: { type: String, optional: true },
|
|
40734
|
+
defaultRangeSheetId: { type: String, optional: true },
|
|
40735
|
+
defaultStatic: { type: Boolean, optional: true },
|
|
40736
|
+
onConfirm: Function,
|
|
40737
|
+
contextualAutocomplete: { type: Object, optional: true },
|
|
40738
|
+
placeholder: { type: String, optional: true },
|
|
40739
|
+
title: { type: String, optional: true },
|
|
40740
|
+
class: { type: String, optional: true },
|
|
40741
|
+
invalid: { type: Boolean, optional: true },
|
|
40742
|
+
getContextualColoredSymbolToken: { type: Function, optional: true },
|
|
40744
40743
|
};
|
|
40745
|
-
|
|
40746
|
-
|
|
40744
|
+
static components = { Composer };
|
|
40745
|
+
static defaultProps = {
|
|
40746
|
+
composerContent: "",
|
|
40747
|
+
defaultStatic: false,
|
|
40748
|
+
};
|
|
40749
|
+
composerFocusStore;
|
|
40750
|
+
standaloneComposerStore;
|
|
40751
|
+
composerInterface;
|
|
40752
|
+
spreadsheetRect = useSpreadsheetRect();
|
|
40753
|
+
setup() {
|
|
40754
|
+
this.composerFocusStore = useStore(ComposerFocusStore);
|
|
40755
|
+
const standaloneComposerStore = useLocalStore(StandaloneComposerStore, () => ({
|
|
40756
|
+
onConfirm: this.props.onConfirm,
|
|
40757
|
+
content: this.props.composerContent,
|
|
40758
|
+
defaultStatic: this.props.defaultStatic ?? false,
|
|
40759
|
+
contextualAutocomplete: this.props.contextualAutocomplete,
|
|
40760
|
+
defaultRangeSheetId: this.props.defaultRangeSheetId,
|
|
40761
|
+
getContextualColoredSymbolToken: this.props.getContextualColoredSymbolToken,
|
|
40762
|
+
}));
|
|
40763
|
+
this.standaloneComposerStore = standaloneComposerStore;
|
|
40764
|
+
this.composerInterface = {
|
|
40765
|
+
id: "standaloneComposer",
|
|
40766
|
+
get editionMode() {
|
|
40767
|
+
return standaloneComposerStore.editionMode;
|
|
40768
|
+
},
|
|
40769
|
+
startEdition: this.standaloneComposerStore.startEdition,
|
|
40770
|
+
setCurrentContent: this.standaloneComposerStore.setCurrentContent,
|
|
40771
|
+
stopEdition: this.standaloneComposerStore.stopEdition,
|
|
40772
|
+
};
|
|
40747
40773
|
}
|
|
40748
|
-
get
|
|
40749
|
-
return this.
|
|
40774
|
+
get focus() {
|
|
40775
|
+
return this.composerFocusStore.activeComposer === this.composerInterface
|
|
40776
|
+
? this.composerFocusStore.focusMode
|
|
40777
|
+
: "inactive";
|
|
40778
|
+
}
|
|
40779
|
+
get composerStyle() {
|
|
40780
|
+
return this.props.invalid
|
|
40781
|
+
? cssPropertiesToCss({ padding: "1px 0px 0px 0px" })
|
|
40782
|
+
: cssPropertiesToCss({ padding: "1px 0px" });
|
|
40783
|
+
}
|
|
40784
|
+
get containerClass() {
|
|
40785
|
+
const classes = [
|
|
40786
|
+
this.focus === "inactive" ? "" : "active",
|
|
40787
|
+
this.props.invalid ? "o-invalid" : "",
|
|
40788
|
+
this.props.class || "",
|
|
40789
|
+
];
|
|
40790
|
+
return classes.join(" ");
|
|
40791
|
+
}
|
|
40792
|
+
onFocus(selection) {
|
|
40793
|
+
this.composerFocusStore.focusComposer(this.composerInterface, { selection });
|
|
40750
40794
|
}
|
|
40751
40795
|
}
|
|
40752
40796
|
|
|
40753
|
-
const functions = functionRegistry.content;
|
|
40754
|
-
const ASSISTANT_WIDTH = 300;
|
|
40755
|
-
const CLOSE_ICON_RADIUS = 9;
|
|
40756
|
-
const selectionIndicatorClass = "selector-flag";
|
|
40757
|
-
const backgroundClass = "background-flag";
|
|
40758
|
-
const selectionIndicatorColor = "#a9a9a9";
|
|
40759
|
-
const selectionIndicator = "␣";
|
|
40760
40797
|
css /* scss */ `
|
|
40761
|
-
.o-
|
|
40762
|
-
|
|
40763
|
-
|
|
40764
|
-
|
|
40765
|
-
|
|
40766
|
-
|
|
40798
|
+
.o-gauge-color-set {
|
|
40799
|
+
table {
|
|
40800
|
+
table-layout: fixed;
|
|
40801
|
+
margin-top: 2%;
|
|
40802
|
+
display: table;
|
|
40803
|
+
text-align: left;
|
|
40804
|
+
font-size: 12px;
|
|
40805
|
+
line-height: 18px;
|
|
40806
|
+
width: 100%;
|
|
40807
|
+
font-size: 12px;
|
|
40808
|
+
}
|
|
40767
40809
|
|
|
40810
|
+
td {
|
|
40768
40811
|
box-sizing: border-box;
|
|
40769
|
-
|
|
40770
|
-
|
|
40771
|
-
padding-left: 3px;
|
|
40772
|
-
padding-right: 3px;
|
|
40773
|
-
outline: none;
|
|
40774
|
-
|
|
40775
|
-
p {
|
|
40776
|
-
margin-bottom: 0px;
|
|
40777
|
-
|
|
40778
|
-
span {
|
|
40779
|
-
white-space: pre-wrap;
|
|
40780
|
-
|
|
40781
|
-
&.${selectionIndicatorClass}:after {
|
|
40782
|
-
content: "${selectionIndicator}";
|
|
40783
|
-
color: ${selectionIndicatorColor};
|
|
40784
|
-
}
|
|
40785
|
-
|
|
40786
|
-
&.${backgroundClass} {
|
|
40787
|
-
border-radius: 5px;
|
|
40788
|
-
background-color: lightgray;
|
|
40789
|
-
padding: 0px 1.5px 1.5px 1.5px;
|
|
40790
|
-
}
|
|
40791
|
-
}
|
|
40792
|
-
}
|
|
40812
|
+
height: 30px;
|
|
40813
|
+
padding: 6px 0;
|
|
40793
40814
|
}
|
|
40794
|
-
.o-
|
|
40795
|
-
|
|
40796
|
-
color: #bdbdbd;
|
|
40797
|
-
position: relative;
|
|
40798
|
-
top: 0%;
|
|
40799
|
-
pointer-events: none;
|
|
40815
|
+
th.o-gauge-color-set-colorPicker {
|
|
40816
|
+
width: 8%;
|
|
40800
40817
|
}
|
|
40801
|
-
|
|
40802
|
-
|
|
40803
|
-
/* reset stack size which is doubled by default */
|
|
40804
|
-
width: ${CLOSE_ICON_RADIUS * 2}px;
|
|
40805
|
-
height: ${CLOSE_ICON_RADIUS * 2}px;
|
|
40806
|
-
line-height: ${CLOSE_ICON_RADIUS * 2}px;
|
|
40818
|
+
th.o-gauge-color-set-text {
|
|
40819
|
+
width: 25%;
|
|
40807
40820
|
}
|
|
40808
|
-
|
|
40809
|
-
|
|
40810
|
-
left: -1px;
|
|
40811
|
-
top: -1px;
|
|
40812
|
-
|
|
40813
|
-
.fa-question-circle {
|
|
40814
|
-
color: ${PRIMARY_BUTTON_BG};
|
|
40815
|
-
}
|
|
40821
|
+
th.o-gauge-color-set-operator {
|
|
40822
|
+
width: 10%;
|
|
40816
40823
|
}
|
|
40817
|
-
|
|
40818
|
-
|
|
40819
|
-
|
|
40820
|
-
|
|
40821
|
-
|
|
40822
|
-
|
|
40823
|
-
|
|
40824
|
-
|
|
40825
|
-
|
|
40826
|
-
|
|
40827
|
-
|
|
40824
|
+
th.o-gauge-color-set-value {
|
|
40825
|
+
width: 22%;
|
|
40826
|
+
}
|
|
40827
|
+
th.o-gauge-color-set-type {
|
|
40828
|
+
width: 30%;
|
|
40829
|
+
}
|
|
40830
|
+
input,
|
|
40831
|
+
select {
|
|
40832
|
+
width: 100%;
|
|
40833
|
+
height: 100%;
|
|
40834
|
+
box-sizing: border-box;
|
|
40828
40835
|
}
|
|
40829
40836
|
}
|
|
40830
40837
|
`;
|
|
40831
|
-
class
|
|
40832
|
-
static template = "o-spreadsheet-
|
|
40838
|
+
class GaugeChartDesignPanel extends owl.Component {
|
|
40839
|
+
static template = "o-spreadsheet-GaugeChartDesignPanel";
|
|
40840
|
+
static components = {
|
|
40841
|
+
SidePanelCollapsible,
|
|
40842
|
+
Section,
|
|
40843
|
+
RoundColorPicker,
|
|
40844
|
+
GeneralDesignEditor,
|
|
40845
|
+
ChartErrorSection,
|
|
40846
|
+
StandaloneComposer,
|
|
40847
|
+
};
|
|
40833
40848
|
static props = {
|
|
40834
|
-
|
|
40835
|
-
|
|
40836
|
-
|
|
40837
|
-
|
|
40838
|
-
rect: { type: Object, optional: true },
|
|
40839
|
-
delimitation: { type: Object, optional: true },
|
|
40840
|
-
onComposerCellFocused: { type: Function, optional: true },
|
|
40841
|
-
onComposerContentFocused: Function,
|
|
40842
|
-
isDefaultFocus: { type: Boolean, optional: true },
|
|
40843
|
-
onInputContextMenu: { type: Function, optional: true },
|
|
40844
|
-
composerStore: Object,
|
|
40845
|
-
placeholder: { type: String, optional: true },
|
|
40849
|
+
figureId: String,
|
|
40850
|
+
definition: Object,
|
|
40851
|
+
updateChart: Function,
|
|
40852
|
+
canUpdateChart: { type: Function, optional: true },
|
|
40846
40853
|
};
|
|
40847
|
-
|
|
40848
|
-
|
|
40849
|
-
|
|
40850
|
-
|
|
40854
|
+
state;
|
|
40855
|
+
setup() {
|
|
40856
|
+
this.state = owl.useState({
|
|
40857
|
+
sectionRuleCancelledReasons: this.checkSectionRuleFormulasAreValid(this.props.definition.sectionRule),
|
|
40858
|
+
sectionRule: deepCopy(this.props.definition.sectionRule),
|
|
40859
|
+
});
|
|
40860
|
+
}
|
|
40861
|
+
get designErrorMessages() {
|
|
40862
|
+
const cancelledReasons = [...(this.state.sectionRuleCancelledReasons || [])];
|
|
40863
|
+
return cancelledReasons.map((error) => ChartTerms.Errors[error] || ChartTerms.Errors.Unexpected);
|
|
40864
|
+
}
|
|
40865
|
+
get isRangeMinInvalid() {
|
|
40866
|
+
return !!(this.state.sectionRuleCancelledReasons?.includes("EmptyGaugeRangeMin" /* CommandResult.EmptyGaugeRangeMin */) ||
|
|
40867
|
+
this.state.sectionRuleCancelledReasons?.includes("GaugeRangeMinNaN" /* CommandResult.GaugeRangeMinNaN */));
|
|
40868
|
+
}
|
|
40869
|
+
get isRangeMaxInvalid() {
|
|
40870
|
+
return !!(this.state.sectionRuleCancelledReasons?.includes("EmptyGaugeRangeMax" /* CommandResult.EmptyGaugeRangeMax */) ||
|
|
40871
|
+
this.state.sectionRuleCancelledReasons?.includes("GaugeRangeMaxNaN" /* CommandResult.GaugeRangeMaxNaN */));
|
|
40872
|
+
}
|
|
40873
|
+
// ---------------------------------------------------------------------------
|
|
40874
|
+
// COLOR_SECTION_TEMPLATE
|
|
40875
|
+
// ---------------------------------------------------------------------------
|
|
40876
|
+
get isLowerInflectionPointInvalid() {
|
|
40877
|
+
return !!this.state.sectionRuleCancelledReasons?.includes("GaugeLowerInflectionPointNaN" /* CommandResult.GaugeLowerInflectionPointNaN */);
|
|
40878
|
+
}
|
|
40879
|
+
get isUpperInflectionPointInvalid() {
|
|
40880
|
+
return !!this.state.sectionRuleCancelledReasons?.includes("GaugeUpperInflectionPointNaN" /* CommandResult.GaugeUpperInflectionPointNaN */);
|
|
40881
|
+
}
|
|
40882
|
+
updateSectionColor(target, color) {
|
|
40883
|
+
const sectionRule = deepCopy(this.state.sectionRule);
|
|
40884
|
+
sectionRule.colors[target] = color;
|
|
40885
|
+
this.updateSectionRule(sectionRule);
|
|
40886
|
+
}
|
|
40887
|
+
updateSectionRule(sectionRule) {
|
|
40888
|
+
this.state.sectionRuleCancelledReasons = [];
|
|
40889
|
+
this.state.sectionRuleCancelledReasons.push(...this.checkSectionRuleFormulasAreValid(this.state.sectionRule));
|
|
40890
|
+
const dispatchResult = this.props.updateChart(this.props.figureId, {
|
|
40891
|
+
sectionRule,
|
|
40892
|
+
});
|
|
40893
|
+
if (dispatchResult.isSuccessful) {
|
|
40894
|
+
this.state.sectionRule = deepCopy(sectionRule);
|
|
40895
|
+
}
|
|
40896
|
+
else {
|
|
40897
|
+
this.state.sectionRuleCancelledReasons.push(...dispatchResult.reasons);
|
|
40898
|
+
}
|
|
40899
|
+
}
|
|
40900
|
+
onConfirmGaugeRange(editedRange, content) {
|
|
40901
|
+
this.state.sectionRule = { ...this.state.sectionRule, [editedRange]: content };
|
|
40902
|
+
this.updateSectionRule(this.state.sectionRule);
|
|
40903
|
+
}
|
|
40904
|
+
getGaugeInflectionComposerProps(sectionType) {
|
|
40905
|
+
const inflectionPointName = sectionType === "lowerColor" ? "lowerInflectionPoint" : "upperInflectionPoint";
|
|
40906
|
+
const inflectionPoint = this.state.sectionRule[inflectionPointName];
|
|
40907
|
+
return {
|
|
40908
|
+
onConfirm: (str) => {
|
|
40909
|
+
this.state.sectionRule = {
|
|
40910
|
+
...this.state.sectionRule,
|
|
40911
|
+
[inflectionPointName]: { ...inflectionPoint, value: str },
|
|
40912
|
+
};
|
|
40913
|
+
this.updateSectionRule(this.state.sectionRule);
|
|
40914
|
+
},
|
|
40915
|
+
composerContent: inflectionPoint.value,
|
|
40916
|
+
invalid: sectionType === "lowerColor"
|
|
40917
|
+
? this.isLowerInflectionPointInvalid
|
|
40918
|
+
: this.isUpperInflectionPointInvalid,
|
|
40919
|
+
defaultRangeSheetId: this.sheetId,
|
|
40920
|
+
class: inflectionPointName,
|
|
40921
|
+
placeholder: _t("Value"),
|
|
40922
|
+
title: _t("Value or formula"),
|
|
40923
|
+
};
|
|
40924
|
+
}
|
|
40925
|
+
checkSectionRuleFormulasAreValid(sectionRule) {
|
|
40926
|
+
const reasons = [];
|
|
40927
|
+
if (!this.valueIsValidNumber(sectionRule.rangeMin)) {
|
|
40928
|
+
reasons.push("GaugeRangeMinNaN" /* CommandResult.GaugeRangeMinNaN */);
|
|
40929
|
+
}
|
|
40930
|
+
if (!this.valueIsValidNumber(sectionRule.rangeMax)) {
|
|
40931
|
+
reasons.push("GaugeRangeMaxNaN" /* CommandResult.GaugeRangeMaxNaN */);
|
|
40932
|
+
}
|
|
40933
|
+
if (!this.valueIsValidNumber(sectionRule.lowerInflectionPoint.value)) {
|
|
40934
|
+
reasons.push("GaugeLowerInflectionPointNaN" /* CommandResult.GaugeLowerInflectionPointNaN */);
|
|
40935
|
+
}
|
|
40936
|
+
if (!this.valueIsValidNumber(sectionRule.upperInflectionPoint.value)) {
|
|
40937
|
+
reasons.push("GaugeUpperInflectionPointNaN" /* CommandResult.GaugeUpperInflectionPointNaN */);
|
|
40938
|
+
}
|
|
40939
|
+
return reasons;
|
|
40940
|
+
}
|
|
40941
|
+
valueIsValidNumber(value) {
|
|
40942
|
+
const locale = this.env.model.getters.getLocale();
|
|
40943
|
+
if (!value.startsWith("=")) {
|
|
40944
|
+
return tryToNumber(value, locale) !== undefined;
|
|
40945
|
+
}
|
|
40946
|
+
const evaluatedValue = this.env.model.getters.evaluateFormula(this.sheetId, value);
|
|
40947
|
+
if (isMatrix(evaluatedValue)) {
|
|
40948
|
+
return false;
|
|
40949
|
+
}
|
|
40950
|
+
return tryToNumber(evaluatedValue, locale) !== undefined;
|
|
40951
|
+
}
|
|
40952
|
+
get sheetId() {
|
|
40953
|
+
const chart = this.env.model.getters.getChart(this.props.figureId);
|
|
40954
|
+
if (!chart) {
|
|
40955
|
+
throw new Error("Chart not found with id " + this.props.figureId);
|
|
40956
|
+
}
|
|
40957
|
+
return chart.sheetId;
|
|
40958
|
+
}
|
|
40959
|
+
}
|
|
40960
|
+
|
|
40961
|
+
class GeoChartRegionSelectSection extends owl.Component {
|
|
40962
|
+
static template = "o-spreadsheet-GeoChartRegionSelectSection";
|
|
40963
|
+
static components = { Section };
|
|
40964
|
+
static props = {
|
|
40965
|
+
figureId: String,
|
|
40966
|
+
definition: Object,
|
|
40967
|
+
updateChart: Function,
|
|
40851
40968
|
};
|
|
40852
|
-
|
|
40853
|
-
|
|
40854
|
-
|
|
40855
|
-
|
|
40856
|
-
|
|
40857
|
-
|
|
40858
|
-
}
|
|
40859
|
-
|
|
40860
|
-
|
|
40861
|
-
|
|
40862
|
-
|
|
40863
|
-
|
|
40864
|
-
|
|
40865
|
-
|
|
40866
|
-
|
|
40867
|
-
|
|
40868
|
-
|
|
40869
|
-
|
|
40870
|
-
|
|
40871
|
-
|
|
40872
|
-
|
|
40873
|
-
|
|
40874
|
-
|
|
40875
|
-
|
|
40876
|
-
|
|
40877
|
-
|
|
40878
|
-
|
|
40969
|
+
updateSelectedRegion(ev) {
|
|
40970
|
+
const value = ev.target.value;
|
|
40971
|
+
this.props.updateChart(this.props.figureId, { region: value });
|
|
40972
|
+
}
|
|
40973
|
+
get availableRegions() {
|
|
40974
|
+
return this.env.model.getters.getGeoChartAvailableRegions();
|
|
40975
|
+
}
|
|
40976
|
+
get selectedRegion() {
|
|
40977
|
+
return this.props.definition.region || this.availableRegions[0]?.id;
|
|
40978
|
+
}
|
|
40979
|
+
}
|
|
40980
|
+
|
|
40981
|
+
class GeoChartConfigPanel extends GenericChartConfigPanel {
|
|
40982
|
+
static template = "o-spreadsheet-GeoChartConfigPanel";
|
|
40983
|
+
static components = { ...GenericChartConfigPanel.components, GeoChartRegionSelectSection };
|
|
40984
|
+
get dataRanges() {
|
|
40985
|
+
return this.getDataSeriesRanges().slice(0, 1);
|
|
40986
|
+
}
|
|
40987
|
+
onDataSeriesConfirmed() {
|
|
40988
|
+
this.dataSets = spreadRange(this.env.model.getters, this.dataSets).slice(0, 1);
|
|
40989
|
+
this.state.datasetDispatchResult = this.props.updateChart(this.props.figureId, {
|
|
40990
|
+
dataSets: this.dataSets,
|
|
40991
|
+
});
|
|
40992
|
+
}
|
|
40993
|
+
getLabelRangeOptions() {
|
|
40994
|
+
return [
|
|
40995
|
+
{
|
|
40996
|
+
name: "dataSetsHaveTitle",
|
|
40997
|
+
label: this.dataSetsHaveTitleLabel,
|
|
40998
|
+
value: this.props.definition.dataSetsHaveTitle,
|
|
40999
|
+
onChange: this.onUpdateDataSetsHaveTitle.bind(this),
|
|
41000
|
+
},
|
|
41001
|
+
];
|
|
41002
|
+
}
|
|
41003
|
+
}
|
|
41004
|
+
|
|
41005
|
+
const DEFAULT_CUSTOM_COLOR_SCALE = {
|
|
41006
|
+
minColor: "#FFF5EB",
|
|
41007
|
+
midColor: "#FD8D3C",
|
|
41008
|
+
maxColor: "#7F2704",
|
|
41009
|
+
};
|
|
41010
|
+
class GeoChartDesignPanel extends ChartWithAxisDesignPanel {
|
|
41011
|
+
static template = "o-spreadsheet-GeoChartDesignPanel";
|
|
41012
|
+
static components = { ...ChartWithAxisDesignPanel.components, RoundColorPicker };
|
|
41013
|
+
colorScalesChoices = ChartTerms.GeoChart.ColorScales;
|
|
41014
|
+
updateColorScaleType(ev) {
|
|
41015
|
+
const value = ev.target.value;
|
|
41016
|
+
value === "custom"
|
|
41017
|
+
? this.updateColorScale(DEFAULT_CUSTOM_COLOR_SCALE)
|
|
41018
|
+
: this.updateColorScale(value);
|
|
41019
|
+
}
|
|
41020
|
+
updateColorScale(colorScale) {
|
|
41021
|
+
this.props.updateChart(this.props.figureId, { colorScale });
|
|
41022
|
+
}
|
|
41023
|
+
updateMissingValueColor(color) {
|
|
41024
|
+
this.props.updateChart(this.props.figureId, { missingValueColor: color });
|
|
41025
|
+
}
|
|
41026
|
+
updateLegendPosition(ev) {
|
|
41027
|
+
const value = ev.target.value;
|
|
41028
|
+
this.props.updateChart(this.props.figureId, { legendPosition: value });
|
|
41029
|
+
}
|
|
41030
|
+
get selectedColorScale() {
|
|
41031
|
+
return typeof this.props.definition.colorScale === "object"
|
|
41032
|
+
? "custom"
|
|
41033
|
+
: this.props.definition.colorScale || "oranges";
|
|
41034
|
+
}
|
|
41035
|
+
get selectedMissingValueColor() {
|
|
41036
|
+
return this.props.definition.missingValueColor || "#ffffff";
|
|
41037
|
+
}
|
|
41038
|
+
get customColorScale() {
|
|
41039
|
+
if (typeof this.props.definition.colorScale === "object") {
|
|
41040
|
+
return this.props.definition.colorScale;
|
|
40879
41041
|
}
|
|
40880
|
-
|
|
40881
|
-
|
|
40882
|
-
|
|
40883
|
-
|
|
40884
|
-
|
|
40885
|
-
|
|
40886
|
-
|
|
40887
|
-
|
|
40888
|
-
|
|
40889
|
-
|
|
40890
|
-
|
|
40891
|
-
|
|
40892
|
-
|
|
40893
|
-
|
|
40894
|
-
|
|
40895
|
-
|
|
41042
|
+
return undefined;
|
|
41043
|
+
}
|
|
41044
|
+
getCustomColorScaleColor(color) {
|
|
41045
|
+
return this.customColorScale?.[color] ?? "";
|
|
41046
|
+
}
|
|
41047
|
+
setCustomColorScaleColor(colorType, color) {
|
|
41048
|
+
if (!color && colorType !== "midColor") {
|
|
41049
|
+
color = "#fff";
|
|
41050
|
+
}
|
|
41051
|
+
const customColorScale = this.customColorScale;
|
|
41052
|
+
if (!customColorScale) {
|
|
41053
|
+
return;
|
|
41054
|
+
}
|
|
41055
|
+
this.updateColorScale({ ...customColorScale, [colorType]: color });
|
|
41056
|
+
}
|
|
41057
|
+
}
|
|
41058
|
+
|
|
41059
|
+
class LineConfigPanel extends GenericChartConfigPanel {
|
|
41060
|
+
static template = "o-spreadsheet-LineConfigPanel";
|
|
41061
|
+
get canTreatLabelsAsText() {
|
|
41062
|
+
const chart = this.env.model.getters.getChart(this.props.figureId);
|
|
41063
|
+
if (chart && chart instanceof LineChart) {
|
|
41064
|
+
return canChartParseLabels(chart.labelRange, this.env.model.getters);
|
|
40896
41065
|
}
|
|
40897
|
-
|
|
40898
|
-
|
|
40899
|
-
|
|
40900
|
-
|
|
40901
|
-
|
|
40902
|
-
|
|
41066
|
+
return false;
|
|
41067
|
+
}
|
|
41068
|
+
get stackedLabel() {
|
|
41069
|
+
const definition = this.props.definition;
|
|
41070
|
+
return definition.fillArea
|
|
41071
|
+
? this.chartTerms.StackedAreaChart
|
|
41072
|
+
: this.chartTerms.StackedLineChart;
|
|
41073
|
+
}
|
|
41074
|
+
getLabelRangeOptions() {
|
|
41075
|
+
const options = super.getLabelRangeOptions();
|
|
41076
|
+
if (this.canTreatLabelsAsText) {
|
|
41077
|
+
options.push({
|
|
41078
|
+
name: "labelsAsText",
|
|
41079
|
+
value: this.props.definition.labelsAsText,
|
|
41080
|
+
label: this.chartTerms.TreatLabelsAsText,
|
|
41081
|
+
onChange: this.onUpdateLabelsAsText.bind(this),
|
|
41082
|
+
});
|
|
40903
41083
|
}
|
|
40904
|
-
return
|
|
41084
|
+
return options;
|
|
40905
41085
|
}
|
|
40906
|
-
|
|
40907
|
-
|
|
40908
|
-
|
|
40909
|
-
keyMapping = {
|
|
40910
|
-
Enter: (ev) => this.processEnterKey(ev, "down"),
|
|
40911
|
-
"Shift+Enter": (ev) => this.processEnterKey(ev, "up"),
|
|
40912
|
-
"Alt+Enter": this.processNewLineEvent,
|
|
40913
|
-
"Ctrl+Enter": this.processNewLineEvent,
|
|
40914
|
-
Escape: this.processEscapeKey,
|
|
40915
|
-
F2: (ev) => this.toggleEditionMode(ev),
|
|
40916
|
-
F4: (ev) => this.processF4Key(ev),
|
|
40917
|
-
Tab: (ev) => this.processTabKey(ev, "right"),
|
|
40918
|
-
"Shift+Tab": (ev) => this.processTabKey(ev, "left"),
|
|
40919
|
-
};
|
|
40920
|
-
keyCodeMapping = {
|
|
40921
|
-
NumpadDecimal: this.processNumpadDecimal,
|
|
40922
|
-
};
|
|
40923
|
-
setup() {
|
|
40924
|
-
this.DOMFocusableElementStore = useStore(DOMFocusableElementStore);
|
|
40925
|
-
this.autoCompleteState = useLocalStore(AutoCompleteStore);
|
|
40926
|
-
owl.onMounted(() => {
|
|
40927
|
-
const el = this.composerRef.el;
|
|
40928
|
-
if (this.props.isDefaultFocus) {
|
|
40929
|
-
this.DOMFocusableElementStore.setFocusableElement(el);
|
|
40930
|
-
}
|
|
40931
|
-
this.contentHelper.updateEl(el);
|
|
40932
|
-
});
|
|
40933
|
-
this.env.model.selection.observe(this, {
|
|
40934
|
-
handleEvent: () => this.autoCompleteState.hide(),
|
|
41086
|
+
onUpdateLabelsAsText(labelsAsText) {
|
|
41087
|
+
this.props.updateChart(this.props.figureId, {
|
|
41088
|
+
labelsAsText,
|
|
40935
41089
|
});
|
|
40936
|
-
|
|
40937
|
-
|
|
41090
|
+
}
|
|
41091
|
+
onUpdateStacked(stacked) {
|
|
41092
|
+
this.props.updateChart(this.props.figureId, {
|
|
41093
|
+
stacked,
|
|
40938
41094
|
});
|
|
40939
|
-
|
|
40940
|
-
|
|
40941
|
-
|
|
40942
|
-
|
|
40943
|
-
!this.props.isDefaultFocus) {
|
|
40944
|
-
this.DOMFocusableElementStore.focusableElement?.focus();
|
|
40945
|
-
}
|
|
41095
|
+
}
|
|
41096
|
+
onUpdateCumulative(cumulative) {
|
|
41097
|
+
this.props.updateChart(this.props.figureId, {
|
|
41098
|
+
cumulative,
|
|
40946
41099
|
});
|
|
40947
|
-
owl.useEffect(() => {
|
|
40948
|
-
this.processTokenAtCursor();
|
|
40949
|
-
}, () => [this.props.composerStore.editionMode !== "inactive"]);
|
|
40950
41100
|
}
|
|
40951
|
-
|
|
40952
|
-
|
|
40953
|
-
|
|
40954
|
-
|
|
40955
|
-
|
|
40956
|
-
|
|
40957
|
-
|
|
40958
|
-
|
|
40959
|
-
|
|
40960
|
-
|
|
40961
|
-
|
|
40962
|
-
|
|
40963
|
-
|
|
40964
|
-
|
|
40965
|
-
|
|
40966
|
-
|
|
40967
|
-
|
|
40968
|
-
|
|
40969
|
-
|
|
40970
|
-
|
|
40971
|
-
|
|
40972
|
-
|
|
40973
|
-
|
|
40974
|
-
|
|
41101
|
+
}
|
|
41102
|
+
|
|
41103
|
+
class PieChartDesignPanel extends owl.Component {
|
|
41104
|
+
static template = "o-spreadsheet-PieChartDesignPanel";
|
|
41105
|
+
static components = {
|
|
41106
|
+
GeneralDesignEditor,
|
|
41107
|
+
Section,
|
|
41108
|
+
Checkbox,
|
|
41109
|
+
ChartLegend,
|
|
41110
|
+
};
|
|
41111
|
+
static props = {
|
|
41112
|
+
figureId: String,
|
|
41113
|
+
definition: Object,
|
|
41114
|
+
updateChart: Function,
|
|
41115
|
+
canUpdateChart: { type: Function, optional: true },
|
|
41116
|
+
};
|
|
41117
|
+
}
|
|
41118
|
+
|
|
41119
|
+
class RadarChartDesignPanel extends owl.Component {
|
|
41120
|
+
static template = "o-spreadsheet-RadarChartDesignPanel";
|
|
41121
|
+
static components = {
|
|
41122
|
+
GeneralDesignEditor,
|
|
41123
|
+
SeriesDesignEditor,
|
|
41124
|
+
Section,
|
|
41125
|
+
Checkbox,
|
|
41126
|
+
ChartLegend,
|
|
41127
|
+
};
|
|
41128
|
+
static props = {
|
|
41129
|
+
figureId: String,
|
|
41130
|
+
definition: Object,
|
|
41131
|
+
canUpdateChart: Function,
|
|
41132
|
+
updateChart: Function,
|
|
41133
|
+
};
|
|
41134
|
+
}
|
|
41135
|
+
|
|
41136
|
+
class ScatterConfigPanel extends GenericChartConfigPanel {
|
|
41137
|
+
static template = "o-spreadsheet-ScatterConfigPanel";
|
|
41138
|
+
get canTreatLabelsAsText() {
|
|
41139
|
+
const chart = this.env.model.getters.getChart(this.props.figureId);
|
|
41140
|
+
if (chart && chart instanceof ScatterChart) {
|
|
41141
|
+
return canChartParseLabels(chart.labelRange, this.env.model.getters);
|
|
40975
41142
|
}
|
|
40976
|
-
|
|
40977
|
-
// and right should move the cursor.
|
|
40978
|
-
ev.stopPropagation();
|
|
40979
|
-
this.handleArrowKeysForAutocomplete(ev);
|
|
41143
|
+
return false;
|
|
40980
41144
|
}
|
|
40981
|
-
|
|
40982
|
-
|
|
40983
|
-
|
|
40984
|
-
|
|
40985
|
-
this.autoCompleteState.moveSelection(ev.key === "ArrowDown" ? "next" : "previous");
|
|
40986
|
-
}
|
|
41145
|
+
onUpdateLabelsAsText(labelsAsText) {
|
|
41146
|
+
this.props.updateChart(this.props.figureId, {
|
|
41147
|
+
labelsAsText,
|
|
41148
|
+
});
|
|
40987
41149
|
}
|
|
40988
|
-
|
|
40989
|
-
|
|
40990
|
-
|
|
40991
|
-
|
|
40992
|
-
|
|
40993
|
-
|
|
40994
|
-
|
|
40995
|
-
|
|
40996
|
-
|
|
40997
|
-
return;
|
|
40998
|
-
}
|
|
40999
|
-
}
|
|
41000
|
-
this.props.composerStore.stopEdition(direction);
|
|
41150
|
+
getLabelRangeOptions() {
|
|
41151
|
+
const options = super.getLabelRangeOptions();
|
|
41152
|
+
if (this.canTreatLabelsAsText) {
|
|
41153
|
+
options.push({
|
|
41154
|
+
name: "labelsAsText",
|
|
41155
|
+
value: this.props.definition.labelsAsText,
|
|
41156
|
+
label: this.chartTerms.TreatLabelsAsText,
|
|
41157
|
+
onChange: this.onUpdateLabelsAsText.bind(this),
|
|
41158
|
+
});
|
|
41001
41159
|
}
|
|
41160
|
+
return options;
|
|
41002
41161
|
}
|
|
41003
|
-
|
|
41004
|
-
|
|
41005
|
-
|
|
41006
|
-
|
|
41007
|
-
|
|
41008
|
-
|
|
41009
|
-
|
|
41010
|
-
|
|
41011
|
-
|
|
41012
|
-
|
|
41013
|
-
|
|
41014
|
-
|
|
41162
|
+
}
|
|
41163
|
+
|
|
41164
|
+
class ScorecardChartConfigPanel extends owl.Component {
|
|
41165
|
+
static template = "o-spreadsheet-ScorecardChartConfigPanel";
|
|
41166
|
+
static components = { SelectionInput, ChartErrorSection, Section };
|
|
41167
|
+
static props = {
|
|
41168
|
+
figureId: String,
|
|
41169
|
+
definition: Object,
|
|
41170
|
+
updateChart: Function,
|
|
41171
|
+
canUpdateChart: Function,
|
|
41172
|
+
};
|
|
41173
|
+
state = owl.useState({
|
|
41174
|
+
keyValueDispatchResult: undefined,
|
|
41175
|
+
baselineDispatchResult: undefined,
|
|
41176
|
+
});
|
|
41177
|
+
keyValue = this.props.definition.keyValue;
|
|
41178
|
+
baseline = this.props.definition.baseline;
|
|
41179
|
+
get errorMessages() {
|
|
41180
|
+
const cancelledReasons = [
|
|
41181
|
+
...(this.state.keyValueDispatchResult?.reasons || []),
|
|
41182
|
+
...(this.state.baselineDispatchResult?.reasons || []),
|
|
41183
|
+
];
|
|
41184
|
+
return cancelledReasons.map((error) => ChartTerms.Errors[error] || ChartTerms.Errors.Unexpected);
|
|
41015
41185
|
}
|
|
41016
|
-
|
|
41017
|
-
|
|
41018
|
-
|
|
41019
|
-
|
|
41020
|
-
|
|
41021
|
-
|
|
41022
|
-
|
|
41023
|
-
this.
|
|
41024
|
-
this.
|
|
41025
|
-
|
|
41026
|
-
end: start + 1,
|
|
41186
|
+
get isKeyValueInvalid() {
|
|
41187
|
+
return !!this.state.keyValueDispatchResult?.isCancelledBecause("InvalidScorecardKeyValue" /* CommandResult.InvalidScorecardKeyValue */);
|
|
41188
|
+
}
|
|
41189
|
+
get isBaselineInvalid() {
|
|
41190
|
+
return !!this.state.keyValueDispatchResult?.isCancelledBecause("InvalidScorecardBaseline" /* CommandResult.InvalidScorecardBaseline */);
|
|
41191
|
+
}
|
|
41192
|
+
onKeyValueRangeChanged(ranges) {
|
|
41193
|
+
this.keyValue = ranges[0];
|
|
41194
|
+
this.state.keyValueDispatchResult = this.props.canUpdateChart(this.props.figureId, {
|
|
41195
|
+
keyValue: this.keyValue,
|
|
41027
41196
|
});
|
|
41028
|
-
this.processContent();
|
|
41029
41197
|
}
|
|
41030
|
-
|
|
41031
|
-
this.props.
|
|
41032
|
-
|
|
41033
|
-
|
|
41198
|
+
updateKeyValueRange() {
|
|
41199
|
+
this.state.keyValueDispatchResult = this.props.updateChart(this.props.figureId, {
|
|
41200
|
+
keyValue: this.keyValue,
|
|
41201
|
+
});
|
|
41034
41202
|
}
|
|
41035
|
-
|
|
41036
|
-
|
|
41037
|
-
this.props.composerStore.cycleReferences();
|
|
41038
|
-
this.processContent();
|
|
41203
|
+
getKeyValueRange() {
|
|
41204
|
+
return this.keyValue || "";
|
|
41039
41205
|
}
|
|
41040
|
-
|
|
41041
|
-
|
|
41042
|
-
this.props.
|
|
41043
|
-
|
|
41206
|
+
onBaselineRangeChanged(ranges) {
|
|
41207
|
+
this.baseline = ranges[0];
|
|
41208
|
+
this.state.baselineDispatchResult = this.props.canUpdateChart(this.props.figureId, {
|
|
41209
|
+
baseline: this.baseline,
|
|
41210
|
+
});
|
|
41044
41211
|
}
|
|
41045
|
-
|
|
41046
|
-
|
|
41047
|
-
|
|
41048
|
-
const locale = this.env.model.getters.getLocale();
|
|
41049
|
-
const selection = this.contentHelper.getCurrentSelection();
|
|
41050
|
-
const currentContent = this.props.composerStore.currentContent;
|
|
41051
|
-
const content = currentContent.slice(0, selection.start) +
|
|
41052
|
-
locale.decimalSeparator +
|
|
41053
|
-
currentContent.slice(selection.end);
|
|
41054
|
-
// Update composer even by hand rather than dispatching an InputEvent because untrusted inputs
|
|
41055
|
-
// events aren't handled natively by contentEditable
|
|
41056
|
-
this.props.composerStore.setCurrentContent(content, {
|
|
41057
|
-
start: selection.start + 1,
|
|
41058
|
-
end: selection.start + 1,
|
|
41212
|
+
updateBaselineRange() {
|
|
41213
|
+
this.state.baselineDispatchResult = this.props.updateChart(this.props.figureId, {
|
|
41214
|
+
baseline: this.baseline,
|
|
41059
41215
|
});
|
|
41060
|
-
// We need to do the process content here in case there is no render between the keyDown and the
|
|
41061
|
-
// keyUp event
|
|
41062
|
-
this.processContent();
|
|
41063
41216
|
}
|
|
41064
|
-
|
|
41065
|
-
this.
|
|
41217
|
+
getBaselineRange() {
|
|
41218
|
+
return this.baseline || "";
|
|
41066
41219
|
}
|
|
41067
|
-
|
|
41068
|
-
this.
|
|
41220
|
+
updateBaselineMode(ev) {
|
|
41221
|
+
this.props.updateChart(this.props.figureId, { baselineMode: ev.target.value });
|
|
41069
41222
|
}
|
|
41070
|
-
|
|
41071
|
-
|
|
41072
|
-
|
|
41073
|
-
|
|
41074
|
-
|
|
41075
|
-
|
|
41076
|
-
|
|
41077
|
-
|
|
41078
|
-
|
|
41079
|
-
|
|
41080
|
-
|
|
41081
|
-
|
|
41082
|
-
|
|
41083
|
-
|
|
41084
|
-
|
|
41085
|
-
}
|
|
41223
|
+
}
|
|
41224
|
+
|
|
41225
|
+
class ScorecardChartDesignPanel extends owl.Component {
|
|
41226
|
+
static template = "o-spreadsheet-ScorecardChartDesignPanel";
|
|
41227
|
+
static components = {
|
|
41228
|
+
GeneralDesignEditor,
|
|
41229
|
+
RoundColorPicker,
|
|
41230
|
+
SidePanelCollapsible,
|
|
41231
|
+
Section,
|
|
41232
|
+
Checkbox,
|
|
41233
|
+
};
|
|
41234
|
+
static props = {
|
|
41235
|
+
figureId: String,
|
|
41236
|
+
definition: Object,
|
|
41237
|
+
updateChart: Function,
|
|
41238
|
+
canUpdateChart: { type: Function, optional: true },
|
|
41239
|
+
};
|
|
41240
|
+
get colorsSectionTitle() {
|
|
41241
|
+
return this.props.definition.baselineMode === "progress"
|
|
41242
|
+
? _t("Progress bar colors")
|
|
41243
|
+
: _t("Baseline colors");
|
|
41086
41244
|
}
|
|
41087
|
-
|
|
41088
|
-
|
|
41089
|
-
// let the browser clipboard work
|
|
41090
|
-
ev.stopPropagation();
|
|
41091
|
-
}
|
|
41092
|
-
else {
|
|
41093
|
-
// the user meant to paste in the sheet, not open the composer with the pasted content
|
|
41094
|
-
// While we're not editing, we still have the focus and should therefore prevent
|
|
41095
|
-
// the native "paste" to occur.
|
|
41096
|
-
ev.preventDefault();
|
|
41097
|
-
}
|
|
41245
|
+
get humanizeNumbersLabel() {
|
|
41246
|
+
return _t("Humanize numbers");
|
|
41098
41247
|
}
|
|
41099
|
-
|
|
41100
|
-
|
|
41101
|
-
* */
|
|
41102
|
-
onInput(ev) {
|
|
41103
|
-
if (!this.shouldProcessInputEvents) {
|
|
41104
|
-
return;
|
|
41105
|
-
}
|
|
41106
|
-
ev.stopPropagation();
|
|
41107
|
-
let content;
|
|
41108
|
-
if (this.props.composerStore.editionMode === "inactive") {
|
|
41109
|
-
content = ev.data || "";
|
|
41110
|
-
}
|
|
41111
|
-
else {
|
|
41112
|
-
content = this.contentHelper.getText();
|
|
41113
|
-
}
|
|
41114
|
-
if (this.props.focus === "inactive") {
|
|
41115
|
-
return this.props.onComposerCellFocused?.(content);
|
|
41116
|
-
}
|
|
41117
|
-
let selection = this.contentHelper.getCurrentSelection();
|
|
41118
|
-
this.props.composerStore.stopComposerRangeSelection();
|
|
41119
|
-
this.props.composerStore.setCurrentContent(content, selection);
|
|
41120
|
-
this.processTokenAtCursor();
|
|
41248
|
+
get defaultScorecardTitleFontSize() {
|
|
41249
|
+
return SCORECARD_CHART_TITLE_FONT_SIZE;
|
|
41121
41250
|
}
|
|
41122
|
-
|
|
41123
|
-
|
|
41124
|
-
if (this.autoCompleteState.provider && ["ArrowUp", "ArrowDown"].includes(ev.key)) {
|
|
41125
|
-
return;
|
|
41126
|
-
}
|
|
41127
|
-
if (this.props.composerStore.isSelectingRange && ev.key?.startsWith("Arrow")) {
|
|
41128
|
-
return;
|
|
41129
|
-
}
|
|
41130
|
-
const { start: oldStart, end: oldEnd } = this.props.composerStore.composerSelection;
|
|
41131
|
-
const { start, end } = this.contentHelper.getCurrentSelection();
|
|
41132
|
-
if (start !== oldStart || end !== oldEnd) {
|
|
41133
|
-
this.props.composerStore.changeComposerCursorSelection(start, end);
|
|
41134
|
-
}
|
|
41135
|
-
this.processTokenAtCursor();
|
|
41136
|
-
}
|
|
41251
|
+
updateHumanizeNumbers(humanize) {
|
|
41252
|
+
this.props.updateChart(this.props.figureId, { humanize });
|
|
41137
41253
|
}
|
|
41138
|
-
|
|
41139
|
-
|
|
41140
|
-
return;
|
|
41141
|
-
}
|
|
41142
|
-
const target = ev.relatedTarget;
|
|
41143
|
-
if (!target || !(target instanceof HTMLElement)) {
|
|
41144
|
-
this.props.composerStore.stopEdition();
|
|
41145
|
-
return;
|
|
41146
|
-
}
|
|
41147
|
-
if (target.attributes.getNamedItem("composerFocusableElement")) {
|
|
41148
|
-
this.contentHelper.el.focus();
|
|
41149
|
-
return;
|
|
41150
|
-
}
|
|
41151
|
-
if (target.classList.contains("o-composer")) {
|
|
41152
|
-
return;
|
|
41153
|
-
}
|
|
41154
|
-
this.props.composerStore.stopEdition();
|
|
41254
|
+
translate(term) {
|
|
41255
|
+
return _t(term);
|
|
41155
41256
|
}
|
|
41156
|
-
|
|
41157
|
-
this.
|
|
41257
|
+
updateBaselineDescr(ev) {
|
|
41258
|
+
this.props.updateChart(this.props.figureId, { baselineDescr: ev.target.value });
|
|
41158
41259
|
}
|
|
41159
|
-
|
|
41160
|
-
|
|
41161
|
-
|
|
41162
|
-
|
|
41163
|
-
|
|
41164
|
-
|
|
41165
|
-
|
|
41166
|
-
|
|
41167
|
-
|
|
41168
|
-
|
|
41169
|
-
|
|
41170
|
-
return;
|
|
41260
|
+
setColor(color, colorPickerId) {
|
|
41261
|
+
switch (colorPickerId) {
|
|
41262
|
+
case "backgroundColor":
|
|
41263
|
+
this.props.updateChart(this.props.figureId, { background: color });
|
|
41264
|
+
break;
|
|
41265
|
+
case "baselineColorDown":
|
|
41266
|
+
this.props.updateChart(this.props.figureId, { baselineColorDown: color });
|
|
41267
|
+
break;
|
|
41268
|
+
case "baselineColorUp":
|
|
41269
|
+
this.props.updateChart(this.props.figureId, { baselineColorUp: color });
|
|
41270
|
+
break;
|
|
41171
41271
|
}
|
|
41172
|
-
this.contentHelper.removeSelection();
|
|
41173
41272
|
}
|
|
41174
|
-
|
|
41175
|
-
|
|
41176
|
-
|
|
41177
|
-
|
|
41178
|
-
|
|
41179
|
-
|
|
41180
|
-
|
|
41181
|
-
|
|
41182
|
-
|
|
41273
|
+
}
|
|
41274
|
+
|
|
41275
|
+
class WaterfallChartDesignPanel extends owl.Component {
|
|
41276
|
+
static template = "o-spreadsheet-WaterfallChartDesignPanel";
|
|
41277
|
+
static components = {
|
|
41278
|
+
GeneralDesignEditor,
|
|
41279
|
+
Checkbox,
|
|
41280
|
+
SidePanelCollapsible,
|
|
41281
|
+
Section,
|
|
41282
|
+
RoundColorPicker,
|
|
41283
|
+
AxisDesignEditor,
|
|
41284
|
+
RadioSelection,
|
|
41285
|
+
ChartLegend,
|
|
41286
|
+
};
|
|
41287
|
+
static props = {
|
|
41288
|
+
figureId: String,
|
|
41289
|
+
definition: Object,
|
|
41290
|
+
updateChart: Function,
|
|
41291
|
+
canUpdateChart: { type: Function, optional: true },
|
|
41292
|
+
};
|
|
41293
|
+
axisChoices = CHART_AXIS_CHOICES;
|
|
41294
|
+
onUpdateShowSubTotals(showSubTotals) {
|
|
41295
|
+
this.props.updateChart(this.props.figureId, { showSubTotals });
|
|
41183
41296
|
}
|
|
41184
|
-
|
|
41185
|
-
|
|
41186
|
-
return;
|
|
41187
|
-
}
|
|
41188
|
-
const composerContent = this.props.composerStore.currentContent;
|
|
41189
|
-
const isValidFormula = composerContent.startsWith("=");
|
|
41190
|
-
if (isValidFormula) {
|
|
41191
|
-
const tokens = this.props.composerStore.currentTokens;
|
|
41192
|
-
const currentSelection = this.contentHelper.getCurrentSelection();
|
|
41193
|
-
if (currentSelection.start === currentSelection.end)
|
|
41194
|
-
return;
|
|
41195
|
-
const currentSelectedText = composerContent.substring(currentSelection.start, currentSelection.end);
|
|
41196
|
-
const token = tokens.filter((token) => token.value.includes(currentSelectedText) &&
|
|
41197
|
-
token.start <= currentSelection.start &&
|
|
41198
|
-
token.end >= currentSelection.end)[0];
|
|
41199
|
-
if (!token) {
|
|
41200
|
-
return;
|
|
41201
|
-
}
|
|
41202
|
-
if (token.type === "REFERENCE") {
|
|
41203
|
-
this.props.composerStore.changeComposerCursorSelection(token.start, token.end);
|
|
41204
|
-
}
|
|
41205
|
-
}
|
|
41297
|
+
onUpdateShowConnectorLines(showConnectorLines) {
|
|
41298
|
+
this.props.updateChart(this.props.figureId, { showConnectorLines });
|
|
41206
41299
|
}
|
|
41207
|
-
|
|
41208
|
-
|
|
41209
|
-
this.props.onInputContextMenu?.(ev);
|
|
41210
|
-
}
|
|
41300
|
+
onUpdateFirstValueAsSubtotal(firstValueAsSubtotal) {
|
|
41301
|
+
this.props.updateChart(this.props.figureId, { firstValueAsSubtotal });
|
|
41211
41302
|
}
|
|
41212
|
-
|
|
41213
|
-
this.
|
|
41303
|
+
updateColor(colorName, color) {
|
|
41304
|
+
this.props.updateChart(this.props.figureId, { [colorName]: color });
|
|
41214
41305
|
}
|
|
41215
|
-
|
|
41216
|
-
|
|
41306
|
+
get axesList() {
|
|
41307
|
+
return [
|
|
41308
|
+
{ id: "x", name: _t("Horizontal axis") },
|
|
41309
|
+
{ id: "y", name: _t("Vertical axis") },
|
|
41310
|
+
];
|
|
41311
|
+
}
|
|
41312
|
+
get positiveValuesColor() {
|
|
41313
|
+
return (this.props.definition.positiveValuesColor ||
|
|
41314
|
+
CHART_WATERFALL_POSITIVE_COLOR);
|
|
41315
|
+
}
|
|
41316
|
+
get negativeValuesColor() {
|
|
41317
|
+
return (this.props.definition.negativeValuesColor ||
|
|
41318
|
+
CHART_WATERFALL_NEGATIVE_COLOR);
|
|
41319
|
+
}
|
|
41320
|
+
get subTotalValuesColor() {
|
|
41321
|
+
return (this.props.definition.subTotalValuesColor ||
|
|
41322
|
+
CHART_WATERFALL_SUBTOTAL_COLOR);
|
|
41217
41323
|
}
|
|
41218
|
-
|
|
41219
|
-
|
|
41220
|
-
|
|
41221
|
-
|
|
41222
|
-
if (this.compositionActive) {
|
|
41223
|
-
return;
|
|
41224
|
-
}
|
|
41225
|
-
this.shouldProcessInputEvents = false;
|
|
41226
|
-
if (this.props.focus !== "inactive" && document.activeElement !== this.contentHelper.el) {
|
|
41227
|
-
this.contentHelper.el.focus();
|
|
41228
|
-
}
|
|
41229
|
-
const content = this.getContentLines();
|
|
41230
|
-
this.contentHelper.setText(content);
|
|
41231
|
-
if (content.length !== 0 && content.length[0] !== 0) {
|
|
41232
|
-
if (this.props.focus !== "inactive") {
|
|
41233
|
-
// Put the cursor back where it was before the rendering
|
|
41234
|
-
const { start, end } = this.props.composerStore.composerSelection;
|
|
41235
|
-
this.contentHelper.selectRange(start, end);
|
|
41236
|
-
}
|
|
41237
|
-
this.contentHelper.scrollSelectionIntoView();
|
|
41238
|
-
}
|
|
41239
|
-
this.shouldProcessInputEvents = true;
|
|
41324
|
+
updateVerticalAxisPosition(value) {
|
|
41325
|
+
this.props.updateChart(this.props.figureId, {
|
|
41326
|
+
verticalAxisPosition: value,
|
|
41327
|
+
});
|
|
41240
41328
|
}
|
|
41241
|
-
|
|
41242
|
-
|
|
41243
|
-
|
|
41244
|
-
|
|
41245
|
-
|
|
41246
|
-
|
|
41247
|
-
|
|
41248
|
-
|
|
41249
|
-
|
|
41250
|
-
|
|
41251
|
-
|
|
41252
|
-
|
|
41253
|
-
|
|
41329
|
+
}
|
|
41330
|
+
|
|
41331
|
+
const chartSidePanelComponentRegistry = new Registry();
|
|
41332
|
+
chartSidePanelComponentRegistry
|
|
41333
|
+
.add("line", {
|
|
41334
|
+
configuration: LineConfigPanel,
|
|
41335
|
+
design: ChartWithAxisDesignPanel,
|
|
41336
|
+
})
|
|
41337
|
+
.add("scatter", {
|
|
41338
|
+
configuration: ScatterConfigPanel,
|
|
41339
|
+
design: ChartWithAxisDesignPanel,
|
|
41340
|
+
})
|
|
41341
|
+
.add("bar", {
|
|
41342
|
+
configuration: BarConfigPanel,
|
|
41343
|
+
design: ChartWithAxisDesignPanel,
|
|
41344
|
+
})
|
|
41345
|
+
.add("combo", {
|
|
41346
|
+
configuration: GenericChartConfigPanel,
|
|
41347
|
+
design: ComboChartDesignPanel,
|
|
41348
|
+
})
|
|
41349
|
+
.add("pie", {
|
|
41350
|
+
configuration: GenericChartConfigPanel,
|
|
41351
|
+
design: PieChartDesignPanel,
|
|
41352
|
+
})
|
|
41353
|
+
.add("gauge", {
|
|
41354
|
+
configuration: GaugeChartConfigPanel,
|
|
41355
|
+
design: GaugeChartDesignPanel,
|
|
41356
|
+
})
|
|
41357
|
+
.add("scorecard", {
|
|
41358
|
+
configuration: ScorecardChartConfigPanel,
|
|
41359
|
+
design: ScorecardChartDesignPanel,
|
|
41360
|
+
})
|
|
41361
|
+
.add("waterfall", {
|
|
41362
|
+
configuration: GenericChartConfigPanel,
|
|
41363
|
+
design: WaterfallChartDesignPanel,
|
|
41364
|
+
})
|
|
41365
|
+
.add("pyramid", {
|
|
41366
|
+
configuration: GenericChartConfigPanel,
|
|
41367
|
+
design: ChartWithAxisDesignPanel,
|
|
41368
|
+
})
|
|
41369
|
+
.add("radar", {
|
|
41370
|
+
configuration: GenericChartConfigPanel,
|
|
41371
|
+
design: RadarChartDesignPanel,
|
|
41372
|
+
})
|
|
41373
|
+
.add("geo", {
|
|
41374
|
+
configuration: GeoChartConfigPanel,
|
|
41375
|
+
design: GeoChartDesignPanel,
|
|
41376
|
+
});
|
|
41377
|
+
|
|
41378
|
+
css /* scss */ `
|
|
41379
|
+
.o-section .o-input.o-type-selector {
|
|
41380
|
+
height: 30px;
|
|
41381
|
+
padding-left: 35px;
|
|
41382
|
+
padding-top: 5px;
|
|
41383
|
+
}
|
|
41384
|
+
.o-type-selector-preview {
|
|
41385
|
+
left: 5px;
|
|
41386
|
+
top: 3px;
|
|
41387
|
+
.o-chart-preview {
|
|
41388
|
+
width: 24px;
|
|
41389
|
+
height: 24px;
|
|
41390
|
+
}
|
|
41391
|
+
}
|
|
41392
|
+
|
|
41393
|
+
.o-popover .o-chart-select-popover {
|
|
41394
|
+
box-sizing: border-box;
|
|
41395
|
+
background: #fff;
|
|
41396
|
+
.o-chart-type-item {
|
|
41397
|
+
cursor: pointer;
|
|
41398
|
+
padding: 3px 6px;
|
|
41399
|
+
margin: 1px 2px;
|
|
41400
|
+
&.selected,
|
|
41401
|
+
&:hover {
|
|
41402
|
+
border: 1px solid ${ACTION_COLOR};
|
|
41403
|
+
background: ${BADGE_SELECTED_COLOR};
|
|
41404
|
+
padding: 2px 5px;
|
|
41254
41405
|
}
|
|
41255
|
-
|
|
41256
|
-
|
|
41257
|
-
|
|
41258
|
-
const { end, start } = this.props.composerStore.composerSelection;
|
|
41259
|
-
for (const token of tokens) {
|
|
41260
|
-
let color = token.color || DEFAULT_TOKEN_COLOR;
|
|
41261
|
-
if (token.isBlurred) {
|
|
41262
|
-
color = setColorAlpha(color, 0.5);
|
|
41263
|
-
}
|
|
41264
|
-
result.push({ value: token.value, color });
|
|
41265
|
-
if (token.type === "REFERENCE" &&
|
|
41266
|
-
this.props.composerStore.tokenAtCursor === token &&
|
|
41267
|
-
this.props.composerStore.editionMode === "selecting") {
|
|
41268
|
-
result[result.length - 1].class = "text-decoration-underline";
|
|
41269
|
-
}
|
|
41270
|
-
if (end === start && token.isParenthesisLinkedToCursor) {
|
|
41271
|
-
result[result.length - 1].class = backgroundClass;
|
|
41272
|
-
}
|
|
41273
|
-
if (this.props.composerStore.showSelectionIndicator && end === start && end === token.end) {
|
|
41274
|
-
result[result.length - 1].class = selectionIndicatorClass;
|
|
41275
|
-
}
|
|
41276
|
-
}
|
|
41277
|
-
return result;
|
|
41406
|
+
.o-chart-preview {
|
|
41407
|
+
width: 48px;
|
|
41408
|
+
height: 48px;
|
|
41278
41409
|
}
|
|
41279
|
-
|
|
41280
|
-
|
|
41281
|
-
|
|
41282
|
-
|
|
41283
|
-
|
|
41284
|
-
|
|
41285
|
-
|
|
41286
|
-
|
|
41287
|
-
|
|
41288
|
-
|
|
41289
|
-
|
|
41290
|
-
|
|
41291
|
-
|
|
41292
|
-
|
|
41293
|
-
|
|
41294
|
-
|
|
41295
|
-
|
|
41410
|
+
}
|
|
41411
|
+
}
|
|
41412
|
+
`;
|
|
41413
|
+
class ChartTypePicker extends owl.Component {
|
|
41414
|
+
static template = "o-spreadsheet-ChartTypePicker";
|
|
41415
|
+
static components = { Section, Popover };
|
|
41416
|
+
static props = { figureId: String, chartPanelStore: Object };
|
|
41417
|
+
categories = chartCategories;
|
|
41418
|
+
chartTypeByCategories = {};
|
|
41419
|
+
popoverRef = owl.useRef("popoverRef");
|
|
41420
|
+
selectRef = owl.useRef("selectRef");
|
|
41421
|
+
state = owl.useState({ popoverProps: undefined, popoverStyle: "" });
|
|
41422
|
+
setup() {
|
|
41423
|
+
owl.useExternalListener(window, "pointerdown", this.onExternalClick, { capture: true });
|
|
41424
|
+
for (const subtypeProperties of chartSubtypeRegistry.getAll()) {
|
|
41425
|
+
if (this.chartTypeByCategories[subtypeProperties.category]) {
|
|
41426
|
+
this.chartTypeByCategories[subtypeProperties.category].push(subtypeProperties);
|
|
41296
41427
|
}
|
|
41297
41428
|
else {
|
|
41298
|
-
|
|
41429
|
+
this.chartTypeByCategories[subtypeProperties.category] = [subtypeProperties];
|
|
41299
41430
|
}
|
|
41300
41431
|
}
|
|
41301
|
-
|
|
41302
|
-
|
|
41303
|
-
|
|
41304
|
-
|
|
41305
|
-
|
|
41306
|
-
for (const line of contentSplitInLines) {
|
|
41307
|
-
if (line.every(this.isContentEmpty)) {
|
|
41308
|
-
filteredLines.push([line[0]]);
|
|
41309
|
-
}
|
|
41310
|
-
else {
|
|
41311
|
-
filteredLines.push(line.filter((content) => !this.isContentEmpty(content)));
|
|
41312
|
-
}
|
|
41432
|
+
}
|
|
41433
|
+
onExternalClick(ev) {
|
|
41434
|
+
if (isChildEvent(this.popoverRef.el?.parentElement, ev) ||
|
|
41435
|
+
isChildEvent(this.selectRef.el, ev)) {
|
|
41436
|
+
return;
|
|
41313
41437
|
}
|
|
41314
|
-
|
|
41438
|
+
this.closePopover();
|
|
41315
41439
|
}
|
|
41316
|
-
|
|
41317
|
-
|
|
41440
|
+
onTypeChange(type) {
|
|
41441
|
+
this.props.chartPanelStore.changeChartType(this.props.figureId, type);
|
|
41442
|
+
this.closePopover();
|
|
41318
41443
|
}
|
|
41319
|
-
|
|
41320
|
-
|
|
41321
|
-
* If the token is a function or symbol (that isn't a cell/range reference) we have to initialize
|
|
41322
|
-
* the autocomplete engine otherwise we initialize the formula assistant.
|
|
41323
|
-
*/
|
|
41324
|
-
processTokenAtCursor() {
|
|
41325
|
-
let content = this.props.composerStore.currentContent;
|
|
41326
|
-
if (this.autoCompleteState.provider) {
|
|
41327
|
-
this.autoCompleteState.hide();
|
|
41328
|
-
}
|
|
41329
|
-
this.functionDescriptionState.showDescription = false;
|
|
41330
|
-
const autoCompleteProvider = this.props.composerStore.autocompleteProvider;
|
|
41331
|
-
if (autoCompleteProvider) {
|
|
41332
|
-
this.autoCompleteState.useProvider(autoCompleteProvider);
|
|
41333
|
-
}
|
|
41334
|
-
const token = this.props.composerStore.tokenAtCursor;
|
|
41335
|
-
if (content.startsWith("=") && token && token.type !== "SYMBOL") {
|
|
41336
|
-
const tokenContext = token.functionContext;
|
|
41337
|
-
const parentFunction = tokenContext?.parent.toUpperCase();
|
|
41338
|
-
if (tokenContext &&
|
|
41339
|
-
parentFunction &&
|
|
41340
|
-
parentFunction in functions &&
|
|
41341
|
-
token.type !== "UNKNOWN") {
|
|
41342
|
-
// initialize Formula Assistant
|
|
41343
|
-
const description = functions[parentFunction];
|
|
41344
|
-
const argPosition = tokenContext.argPosition;
|
|
41345
|
-
this.functionDescriptionState.functionName = parentFunction;
|
|
41346
|
-
this.functionDescriptionState.functionDescription = description;
|
|
41347
|
-
this.functionDescriptionState.argToFocus = description.getArgToFocus(argPosition + 1) - 1;
|
|
41348
|
-
this.functionDescriptionState.showDescription = true;
|
|
41349
|
-
}
|
|
41350
|
-
}
|
|
41444
|
+
getChartDefinition(figureId) {
|
|
41445
|
+
return this.env.model.getters.getChartDefinition(figureId);
|
|
41351
41446
|
}
|
|
41352
|
-
|
|
41353
|
-
|
|
41447
|
+
getSelectedChartSubtypeProperties() {
|
|
41448
|
+
const definition = this.getChartDefinition(this.props.figureId);
|
|
41449
|
+
const matchedChart = chartSubtypeRegistry
|
|
41450
|
+
.getAll()
|
|
41451
|
+
.find((c) => c.matcher?.(definition) || false);
|
|
41452
|
+
return matchedChart || chartSubtypeRegistry.get(definition.type);
|
|
41453
|
+
}
|
|
41454
|
+
onPointerDown(ev) {
|
|
41455
|
+
if (this.state.popoverProps) {
|
|
41456
|
+
this.closePopover();
|
|
41354
41457
|
return;
|
|
41355
41458
|
}
|
|
41356
|
-
|
|
41357
|
-
|
|
41459
|
+
const target = ev.currentTarget;
|
|
41460
|
+
const { bottom, right, width } = target.getBoundingClientRect();
|
|
41461
|
+
this.state.popoverProps = {
|
|
41462
|
+
anchorRect: { x: right, y: bottom, width: 0, height: 0 },
|
|
41463
|
+
positioning: "TopRight",
|
|
41464
|
+
verticalOffset: 0,
|
|
41465
|
+
};
|
|
41466
|
+
this.state.popoverStyle = cssPropertiesToCss({ width: `${width}px` });
|
|
41467
|
+
}
|
|
41468
|
+
closePopover() {
|
|
41469
|
+
this.state.popoverProps = undefined;
|
|
41358
41470
|
}
|
|
41359
41471
|
}
|
|
41360
41472
|
|
|
41361
|
-
class
|
|
41362
|
-
|
|
41363
|
-
|
|
41364
|
-
|
|
41365
|
-
|
|
41366
|
-
this.
|
|
41367
|
-
}
|
|
41368
|
-
getAutoCompleteProviders() {
|
|
41369
|
-
const providersDefinitions = super.getAutoCompleteProviders();
|
|
41370
|
-
const contextualAutocomplete = this.args().contextualAutocomplete;
|
|
41371
|
-
if (contextualAutocomplete) {
|
|
41372
|
-
providersDefinitions.push(contextualAutocomplete);
|
|
41373
|
-
}
|
|
41374
|
-
return providersDefinitions;
|
|
41473
|
+
class MainChartPanelStore extends SpreadsheetStore {
|
|
41474
|
+
mutators = ["activatePanel", "changeChartType"];
|
|
41475
|
+
panel = "configuration";
|
|
41476
|
+
creationContexts = {};
|
|
41477
|
+
activatePanel(panel) {
|
|
41478
|
+
this.panel = panel;
|
|
41375
41479
|
}
|
|
41376
|
-
|
|
41377
|
-
|
|
41378
|
-
|
|
41379
|
-
|
|
41380
|
-
|
|
41381
|
-
|
|
41382
|
-
return setXcToFixedReferenceType(res, "colrow");
|
|
41480
|
+
changeChartType(figureId, newDisplayType) {
|
|
41481
|
+
const currentCreationContext = this.getters.getContextCreationChart(figureId);
|
|
41482
|
+
const savedCreationContext = this.creationContexts[figureId] || {};
|
|
41483
|
+
let newRanges = currentCreationContext?.range;
|
|
41484
|
+
if (newRanges?.every((range, i) => deepEquals(range, savedCreationContext.range?.[i]))) {
|
|
41485
|
+
newRanges = Object.assign([], savedCreationContext.range, currentCreationContext?.range);
|
|
41383
41486
|
}
|
|
41384
|
-
|
|
41385
|
-
|
|
41386
|
-
|
|
41387
|
-
|
|
41388
|
-
|
|
41389
|
-
|
|
41390
|
-
|
|
41391
|
-
|
|
41392
|
-
return rangeTokenize(this.args().content)
|
|
41393
|
-
.map((token) => {
|
|
41394
|
-
if (token.type === "REFERENCE") {
|
|
41395
|
-
const range = this.getters.getRangeFromSheetXC(defaultRangeSheetId, token.value);
|
|
41396
|
-
return this.getters.getRangeString(range, this.getters.getActiveSheetId());
|
|
41397
|
-
}
|
|
41398
|
-
return token.value;
|
|
41399
|
-
})
|
|
41400
|
-
.join("");
|
|
41487
|
+
this.creationContexts[figureId] = {
|
|
41488
|
+
...savedCreationContext,
|
|
41489
|
+
...currentCreationContext,
|
|
41490
|
+
range: newRanges,
|
|
41491
|
+
};
|
|
41492
|
+
const sheetId = this.getters.getFigureSheetId(figureId);
|
|
41493
|
+
if (!sheetId) {
|
|
41494
|
+
return;
|
|
41401
41495
|
}
|
|
41402
|
-
|
|
41403
|
-
|
|
41404
|
-
|
|
41405
|
-
|
|
41406
|
-
|
|
41407
|
-
|
|
41408
|
-
this.args().onConfirm(content);
|
|
41496
|
+
const definition = this.getChartDefinitionFromContextCreation(figureId, newDisplayType);
|
|
41497
|
+
this.model.dispatch("UPDATE_CHART", {
|
|
41498
|
+
definition,
|
|
41499
|
+
id: figureId,
|
|
41500
|
+
sheetId,
|
|
41501
|
+
});
|
|
41409
41502
|
}
|
|
41410
|
-
|
|
41411
|
-
|
|
41412
|
-
|
|
41413
|
-
|
|
41414
|
-
|
|
41415
|
-
|
|
41416
|
-
}
|
|
41417
|
-
return super.getTokenColor(token);
|
|
41503
|
+
getChartDefinitionFromContextCreation(figureId, newDisplayType) {
|
|
41504
|
+
const newChartInfo = chartSubtypeRegistry.get(newDisplayType);
|
|
41505
|
+
const ChartClass = chartRegistry.get(newChartInfo.chartType);
|
|
41506
|
+
return {
|
|
41507
|
+
...ChartClass.getChartDefinitionFromContextCreation(this.creationContexts[figureId]),
|
|
41508
|
+
...newChartInfo.subtypeDefinition,
|
|
41509
|
+
};
|
|
41418
41510
|
}
|
|
41419
41511
|
}
|
|
41420
41512
|
|
|
41421
41513
|
css /* scss */ `
|
|
41422
|
-
.o-
|
|
41423
|
-
.o-
|
|
41424
|
-
|
|
41425
|
-
|
|
41514
|
+
.o-chart {
|
|
41515
|
+
.o-panel {
|
|
41516
|
+
display: flex;
|
|
41517
|
+
.o-panel-element {
|
|
41518
|
+
flex: 1 0 auto;
|
|
41519
|
+
padding: 8px 0px;
|
|
41520
|
+
text-align: center;
|
|
41521
|
+
cursor: pointer;
|
|
41522
|
+
border-right: 1px solid ${GRAY_300};
|
|
41426
41523
|
|
|
41427
|
-
|
|
41428
|
-
|
|
41524
|
+
&.inactive {
|
|
41525
|
+
color: ${TEXT_BODY};
|
|
41526
|
+
background-color: ${GRAY_100};
|
|
41527
|
+
border-bottom: 1px solid ${GRAY_300};
|
|
41528
|
+
}
|
|
41429
41529
|
|
|
41430
|
-
|
|
41431
|
-
|
|
41432
|
-
|
|
41530
|
+
&:not(.inactive) {
|
|
41531
|
+
color: ${TEXT_HEADING};
|
|
41532
|
+
border-bottom: 1px solid #fff;
|
|
41533
|
+
}
|
|
41433
41534
|
|
|
41434
|
-
|
|
41435
|
-
|
|
41535
|
+
.fa {
|
|
41536
|
+
margin-right: 4px;
|
|
41537
|
+
}
|
|
41436
41538
|
}
|
|
41437
|
-
|
|
41438
|
-
|
|
41439
|
-
scrollbar-width: none; /* Firefox */
|
|
41440
|
-
&::-webkit-scrollbar {
|
|
41441
|
-
display: none;
|
|
41539
|
+
.o-panel-element:last-child {
|
|
41540
|
+
border-right: none;
|
|
41442
41541
|
}
|
|
41443
41542
|
}
|
|
41444
41543
|
}
|
|
41445
41544
|
`;
|
|
41446
|
-
class
|
|
41447
|
-
static template = "o-spreadsheet-
|
|
41448
|
-
static
|
|
41449
|
-
|
|
41450
|
-
|
|
41451
|
-
|
|
41452
|
-
|
|
41453
|
-
|
|
41454
|
-
placeholder: { type: String, optional: true },
|
|
41455
|
-
class: { type: String, optional: true },
|
|
41456
|
-
invalid: { type: Boolean, optional: true },
|
|
41457
|
-
getContextualColoredSymbolToken: { type: Function, optional: true },
|
|
41458
|
-
};
|
|
41459
|
-
static components = { Composer };
|
|
41460
|
-
static defaultProps = {
|
|
41461
|
-
composerContent: "",
|
|
41462
|
-
defaultStatic: false,
|
|
41463
|
-
};
|
|
41464
|
-
composerFocusStore;
|
|
41465
|
-
standaloneComposerStore;
|
|
41466
|
-
composerInterface;
|
|
41467
|
-
spreadsheetRect = useSpreadsheetRect();
|
|
41545
|
+
class ChartPanel extends owl.Component {
|
|
41546
|
+
static template = "o-spreadsheet-ChartPanel";
|
|
41547
|
+
static components = { Section, ChartTypePicker };
|
|
41548
|
+
static props = { onCloseSidePanel: Function, figureId: String };
|
|
41549
|
+
store;
|
|
41550
|
+
get figureId() {
|
|
41551
|
+
return this.props.figureId;
|
|
41552
|
+
}
|
|
41468
41553
|
setup() {
|
|
41469
|
-
this.
|
|
41470
|
-
|
|
41471
|
-
|
|
41472
|
-
|
|
41473
|
-
|
|
41474
|
-
|
|
41475
|
-
|
|
41476
|
-
|
|
41477
|
-
|
|
41478
|
-
this.standaloneComposerStore = standaloneComposerStore;
|
|
41479
|
-
this.composerInterface = {
|
|
41480
|
-
id: "standaloneComposer",
|
|
41481
|
-
get editionMode() {
|
|
41482
|
-
return standaloneComposerStore.editionMode;
|
|
41483
|
-
},
|
|
41484
|
-
startEdition: this.standaloneComposerStore.startEdition,
|
|
41485
|
-
setCurrentContent: this.standaloneComposerStore.setCurrentContent,
|
|
41486
|
-
stopEdition: this.standaloneComposerStore.stopEdition,
|
|
41554
|
+
this.store = useLocalStore(MainChartPanelStore);
|
|
41555
|
+
}
|
|
41556
|
+
updateChart(figureId, updateDefinition) {
|
|
41557
|
+
if (figureId !== this.figureId) {
|
|
41558
|
+
return;
|
|
41559
|
+
}
|
|
41560
|
+
const definition = {
|
|
41561
|
+
...this.getChartDefinition(this.figureId),
|
|
41562
|
+
...updateDefinition,
|
|
41487
41563
|
};
|
|
41564
|
+
return this.env.model.dispatch("UPDATE_CHART", {
|
|
41565
|
+
definition,
|
|
41566
|
+
id: figureId,
|
|
41567
|
+
sheetId: this.env.model.getters.getFigureSheetId(figureId),
|
|
41568
|
+
});
|
|
41488
41569
|
}
|
|
41489
|
-
|
|
41490
|
-
|
|
41491
|
-
|
|
41492
|
-
|
|
41570
|
+
canUpdateChart(figureId, updateDefinition) {
|
|
41571
|
+
if (figureId !== this.figureId || !this.env.model.getters.isChartDefined(figureId)) {
|
|
41572
|
+
return;
|
|
41573
|
+
}
|
|
41574
|
+
const definition = {
|
|
41575
|
+
...this.getChartDefinition(this.figureId),
|
|
41576
|
+
...updateDefinition,
|
|
41577
|
+
};
|
|
41578
|
+
return this.env.model.canDispatch("UPDATE_CHART", {
|
|
41579
|
+
definition,
|
|
41580
|
+
id: figureId,
|
|
41581
|
+
sheetId: this.env.model.getters.getFigureSheetId(figureId),
|
|
41582
|
+
});
|
|
41493
41583
|
}
|
|
41494
|
-
|
|
41495
|
-
|
|
41496
|
-
|
|
41497
|
-
|
|
41584
|
+
onTypeChange(type) {
|
|
41585
|
+
if (!this.figureId) {
|
|
41586
|
+
return;
|
|
41587
|
+
}
|
|
41588
|
+
this.store.changeChartType(this.figureId, type);
|
|
41498
41589
|
}
|
|
41499
|
-
get
|
|
41500
|
-
|
|
41501
|
-
|
|
41502
|
-
|
|
41503
|
-
|
|
41504
|
-
|
|
41505
|
-
|
|
41590
|
+
get chartPanel() {
|
|
41591
|
+
if (!this.figureId) {
|
|
41592
|
+
throw new Error("Chart not defined.");
|
|
41593
|
+
}
|
|
41594
|
+
const type = this.env.model.getters.getChartType(this.figureId);
|
|
41595
|
+
if (!type) {
|
|
41596
|
+
throw new Error("Chart not defined.");
|
|
41597
|
+
}
|
|
41598
|
+
const chartPanel = chartSidePanelComponentRegistry.get(type);
|
|
41599
|
+
if (!chartPanel) {
|
|
41600
|
+
throw new Error(`Component is not defined for type ${type}`);
|
|
41601
|
+
}
|
|
41602
|
+
return chartPanel;
|
|
41506
41603
|
}
|
|
41507
|
-
|
|
41508
|
-
this.
|
|
41604
|
+
getChartDefinition(figureId) {
|
|
41605
|
+
return this.env.model.getters.getChartDefinition(figureId);
|
|
41509
41606
|
}
|
|
41510
41607
|
}
|
|
41511
41608
|
|
|
@@ -41765,7 +41862,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
41765
41862
|
draggedItemId: cf.id,
|
|
41766
41863
|
initialMousePosition: event.clientY,
|
|
41767
41864
|
items: items,
|
|
41768
|
-
|
|
41865
|
+
scrollableContainerEl: this.cfListRef.el,
|
|
41769
41866
|
onDragEnd: (cfId, finalIndex) => this.onDragEnd(cfId, finalIndex),
|
|
41770
41867
|
});
|
|
41771
41868
|
}
|
|
@@ -44950,6 +45047,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
44950
45047
|
unusedGranularities: Object,
|
|
44951
45048
|
dateGranularities: Array,
|
|
44952
45049
|
datetimeGranularities: Array,
|
|
45050
|
+
getScrollableContainerEl: { type: Function, optional: true },
|
|
44953
45051
|
pivotId: String,
|
|
44954
45052
|
};
|
|
44955
45053
|
dimensionsRef = owl.useRef("pivot-dimensions");
|
|
@@ -44983,7 +45081,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
44983
45081
|
draggedItemId: dimension.nameWithGranularity,
|
|
44984
45082
|
initialMousePosition: event.clientY,
|
|
44985
45083
|
items: draggableItems,
|
|
44986
|
-
|
|
45084
|
+
scrollableContainerEl: this.props.getScrollableContainerEl?.() || this.dimensionsRef.el,
|
|
44987
45085
|
onDragEnd: (dimensionName, finalIndex) => {
|
|
44988
45086
|
const originalIndex = draggableIds.findIndex((id) => id === dimensionName);
|
|
44989
45087
|
if (originalIndex === finalIndex) {
|
|
@@ -45032,7 +45130,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
45032
45130
|
draggedItemId: measure.id,
|
|
45033
45131
|
initialMousePosition: event.clientY,
|
|
45034
45132
|
items: draggableItems,
|
|
45035
|
-
|
|
45133
|
+
scrollableContainerEl: this.props.getScrollableContainerEl?.() || this.dimensionsRef.el,
|
|
45036
45134
|
onDragEnd: (measureName, finalIndex) => {
|
|
45037
45135
|
const originalIndex = draggableIds.findIndex((id) => id === measureName);
|
|
45038
45136
|
if (originalIndex === finalIndex) {
|
|
@@ -45665,7 +45763,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
45665
45763
|
rowTreeToRows(tree, parentRow) {
|
|
45666
45764
|
return tree.flatMap((node) => {
|
|
45667
45765
|
const row = {
|
|
45668
|
-
indent: parentRow ? parentRow.indent + 1 :
|
|
45766
|
+
indent: parentRow ? parentRow.indent + 1 : 1,
|
|
45669
45767
|
fields: [...(parentRow?.fields || []), node.field],
|
|
45670
45768
|
values: [...(parentRow?.values || []), node.value],
|
|
45671
45769
|
};
|
|
@@ -45721,7 +45819,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
45721
45819
|
pivotTableRows.push({
|
|
45722
45820
|
fields: _fields,
|
|
45723
45821
|
values: _values,
|
|
45724
|
-
indent: index,
|
|
45822
|
+
indent: index + 1,
|
|
45725
45823
|
});
|
|
45726
45824
|
const record = groups[value];
|
|
45727
45825
|
if (record) {
|
|
@@ -46717,6 +46815,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
46717
46815
|
};
|
|
46718
46816
|
store;
|
|
46719
46817
|
state;
|
|
46818
|
+
pivotSidePanelRef = owl.useRef("pivotSidePanel");
|
|
46720
46819
|
setup() {
|
|
46721
46820
|
this.store = useLocalStore(PivotSidePanelStore, this.props.pivotId);
|
|
46722
46821
|
this.state = owl.useState({
|
|
@@ -46745,6 +46844,9 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
46745
46844
|
get definition() {
|
|
46746
46845
|
return this.store.definition;
|
|
46747
46846
|
}
|
|
46847
|
+
getScrollableContainerEl() {
|
|
46848
|
+
return this.pivotSidePanelRef.el;
|
|
46849
|
+
}
|
|
46748
46850
|
onSelectionChanged(ranges) {
|
|
46749
46851
|
this.state.rangeHasChanged = true;
|
|
46750
46852
|
this.state.range = ranges[0];
|
|
@@ -55923,6 +56025,8 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
55923
56025
|
this.getters = getters;
|
|
55924
56026
|
}
|
|
55925
56027
|
static getters = [
|
|
56028
|
+
"adaptFormulaStringDependencies",
|
|
56029
|
+
"copyFormulaStringForSheet",
|
|
55926
56030
|
"extendRange",
|
|
55927
56031
|
"getRangeString",
|
|
55928
56032
|
"getRangeFromSheetXC",
|
|
@@ -56315,6 +56419,38 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
56315
56419
|
const unionOfZones = unionUnboundedZones(...zones);
|
|
56316
56420
|
return this.getRangeFromZone(ranges[0].sheetId, unionOfZones);
|
|
56317
56421
|
}
|
|
56422
|
+
adaptFormulaStringDependencies(sheetId, formula, applyChange) {
|
|
56423
|
+
if (!formula.startsWith("=")) {
|
|
56424
|
+
return formula;
|
|
56425
|
+
}
|
|
56426
|
+
const compiledFormula = compile(formula);
|
|
56427
|
+
const updatedDependencies = compiledFormula.dependencies.map((dep) => {
|
|
56428
|
+
const range = this.getters.getRangeFromSheetXC(sheetId, dep);
|
|
56429
|
+
const changedRange = applyChange(range);
|
|
56430
|
+
return changedRange.changeType === "NONE" ? range : changedRange.range;
|
|
56431
|
+
});
|
|
56432
|
+
return this.getters.getFormulaString(sheetId, compiledFormula.tokens, updatedDependencies);
|
|
56433
|
+
}
|
|
56434
|
+
/**
|
|
56435
|
+
* Copy a formula string to another sheet.
|
|
56436
|
+
*
|
|
56437
|
+
* @param mode
|
|
56438
|
+
* `keepSameReference` will make the formula reference the exact same ranges,
|
|
56439
|
+
* `moveReference` will change all the references to `sheetIdFrom` into references to `sheetIdTo`.
|
|
56440
|
+
*/
|
|
56441
|
+
copyFormulaStringForSheet(sheetIdFrom, sheetIdTo, formula, mode) {
|
|
56442
|
+
if (!formula.startsWith("=")) {
|
|
56443
|
+
return formula;
|
|
56444
|
+
}
|
|
56445
|
+
const compiledFormula = compile(formula);
|
|
56446
|
+
const updatedDependencies = compiledFormula.dependencies.map((dep) => {
|
|
56447
|
+
const range = this.getters.getRangeFromSheetXC(sheetIdFrom, dep);
|
|
56448
|
+
return mode === "keepSameReference"
|
|
56449
|
+
? range
|
|
56450
|
+
: duplicateRangeInDuplicatedSheet(sheetIdFrom, sheetIdTo, range);
|
|
56451
|
+
});
|
|
56452
|
+
return this.getters.getFormulaString(sheetIdTo, compiledFormula.tokens, updatedDependencies);
|
|
56453
|
+
}
|
|
56318
56454
|
// ---------------------------------------------------------------------------
|
|
56319
56455
|
// Private
|
|
56320
56456
|
// ---------------------------------------------------------------------------
|
|
@@ -69212,7 +69348,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
69212
69348
|
draggedItemId: sheetId,
|
|
69213
69349
|
initialMousePosition: event.clientX,
|
|
69214
69350
|
items: sheets,
|
|
69215
|
-
|
|
69351
|
+
scrollableContainerEl: this.sheetListRef.el,
|
|
69216
69352
|
onDragEnd: (sheetId, finalIndex) => this.onDragEnd(sheetId, finalIndex),
|
|
69217
69353
|
});
|
|
69218
69354
|
}
|
|
@@ -69267,7 +69403,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
69267
69403
|
this._registryItems = owl.markRaw(clickableCellRegistry.getAll().sort((a, b) => a.sequence - b.sequence));
|
|
69268
69404
|
}
|
|
69269
69405
|
}
|
|
69270
|
-
|
|
69406
|
+
getClickableItem(position) {
|
|
69271
69407
|
const { sheetId, col, row } = position;
|
|
69272
69408
|
const clickableCells = this._clickableCells;
|
|
69273
69409
|
const xc = toXC(col, row);
|
|
@@ -69275,33 +69411,37 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
69275
69411
|
clickableCells[sheetId] = {};
|
|
69276
69412
|
}
|
|
69277
69413
|
if (!(xc in clickableCells[sheetId])) {
|
|
69278
|
-
|
|
69414
|
+
const clickableCell = this.findClickableItem(position);
|
|
69415
|
+
if (clickableCell) {
|
|
69416
|
+
clickableCells[sheetId][xc] = clickableCell;
|
|
69417
|
+
}
|
|
69279
69418
|
}
|
|
69280
69419
|
return clickableCells[sheetId][xc];
|
|
69281
69420
|
}
|
|
69282
|
-
|
|
69421
|
+
findClickableItem(position) {
|
|
69283
69422
|
const getters = this.getters;
|
|
69284
69423
|
for (const item of this._registryItems) {
|
|
69285
69424
|
if (item.condition(position, getters)) {
|
|
69286
|
-
return item
|
|
69425
|
+
return item;
|
|
69287
69426
|
}
|
|
69288
69427
|
}
|
|
69289
|
-
return
|
|
69428
|
+
return undefined;
|
|
69290
69429
|
}
|
|
69291
69430
|
get clickableCells() {
|
|
69292
69431
|
const cells = [];
|
|
69293
69432
|
const getters = this.getters;
|
|
69294
69433
|
const sheetId = getters.getActiveSheetId();
|
|
69295
69434
|
for (const position of this.getters.getVisibleCellPositions()) {
|
|
69296
|
-
const
|
|
69297
|
-
if (!
|
|
69435
|
+
const item = this.getClickableItem(position);
|
|
69436
|
+
if (!item) {
|
|
69298
69437
|
continue;
|
|
69299
69438
|
}
|
|
69300
69439
|
const zone = getters.expandZone(sheetId, positionToZone(position));
|
|
69301
69440
|
cells.push({
|
|
69302
69441
|
coordinates: getters.getVisibleRect(zone),
|
|
69303
69442
|
position,
|
|
69304
|
-
action,
|
|
69443
|
+
action: item.execute,
|
|
69444
|
+
title: item.title || "",
|
|
69305
69445
|
});
|
|
69306
69446
|
}
|
|
69307
69447
|
return cells;
|
|
@@ -74677,6 +74817,9 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
74677
74817
|
this.coreGetters.extendRange = this.range.extendRange.bind(this.range);
|
|
74678
74818
|
this.coreGetters.getRangesUnion = this.range.getRangesUnion.bind(this.range);
|
|
74679
74819
|
this.coreGetters.removeRangesSheetPrefix = this.range.removeRangesSheetPrefix.bind(this.range);
|
|
74820
|
+
this.coreGetters.adaptFormulaStringDependencies =
|
|
74821
|
+
this.range.adaptFormulaStringDependencies.bind(this.range);
|
|
74822
|
+
this.coreGetters.copyFormulaStringForSheet = this.range.copyFormulaStringForSheet.bind(this.range);
|
|
74680
74823
|
this.getters = {
|
|
74681
74824
|
isReadonly: () => this.config.mode === "readonly" || this.config.mode === "dashboard",
|
|
74682
74825
|
isDashboard: () => this.config.mode === "dashboard",
|
|
@@ -75378,9 +75521,9 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
75378
75521
|
exports.tokenize = tokenize;
|
|
75379
75522
|
|
|
75380
75523
|
|
|
75381
|
-
__info__.version = "18.2.0-alpha.
|
|
75382
|
-
__info__.date = "2025-02-
|
|
75383
|
-
__info__.hash = "
|
|
75524
|
+
__info__.version = "18.2.0-alpha.7";
|
|
75525
|
+
__info__.date = "2025-02-10T09:01:19.353Z";
|
|
75526
|
+
__info__.hash = "0432f17";
|
|
75384
75527
|
|
|
75385
75528
|
|
|
75386
75529
|
})(this.o_spreadsheet = this.o_spreadsheet || {}, owl);
|