@odoo/o-spreadsheet 19.2.0-alpha.1 → 19.2.0-alpha.2
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-engine.d.ts +11 -6
- package/dist/o-spreadsheet-engine.esm.js +183 -32
- package/dist/o-spreadsheet-engine.iife.js +183 -32
- package/dist/o-spreadsheet-engine.min.iife.js +313 -313
- package/dist/o-spreadsheet.d.ts +23 -18
- package/dist/o_spreadsheet.css +20 -10
- package/dist/o_spreadsheet.esm.js +741 -620
- package/dist/o_spreadsheet.iife.js +741 -620
- package/dist/o_spreadsheet.min.iife.js +9 -9
- package/dist/o_spreadsheet.xml +98 -106
- 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 19.2.0-alpha.
|
|
6
|
-
* @date
|
|
7
|
-
* @hash
|
|
5
|
+
* @version 19.2.0-alpha.2
|
|
6
|
+
* @date 2026-01-07T16:21:35.251Z
|
|
7
|
+
* @hash ac2fa3e
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
import { useEnv, useSubEnv, onWillUnmount, useComponent, status, Component, useRef, onMounted, useEffect, App, blockDom, useState, onPatched, useExternalListener, onWillUpdateProps, onWillStart, onWillPatch, xml, useChildSubEnv, markRaw, toRaw } from '@odoo/owl';
|
|
@@ -10482,6 +10482,7 @@ function changeCFRuleLocale(rule, changeContentLocale) {
|
|
|
10482
10482
|
case "dateIsAfter":
|
|
10483
10483
|
case "dateIsOnOrAfter":
|
|
10484
10484
|
case "dateIsOnOrBefore":
|
|
10485
|
+
case "top10":
|
|
10485
10486
|
rule.values = rule.values.map((v) => changeContentLocale(v));
|
|
10486
10487
|
return rule;
|
|
10487
10488
|
case "beginsWithText":
|
|
@@ -14631,6 +14632,7 @@ const DVTerms = {
|
|
|
14631
14632
|
dateValue: _t("The value must be a date"),
|
|
14632
14633
|
validRange: _t("The value must be a valid range"),
|
|
14633
14634
|
validFormula: _t("The formula must be valid"),
|
|
14635
|
+
positiveNumber: _t("The value must be a positive number"),
|
|
14634
14636
|
},
|
|
14635
14637
|
Errors: {
|
|
14636
14638
|
["InvalidRange" /* CommandResult.InvalidRange */]: _t("The range is invalid."),
|
|
@@ -19608,17 +19610,41 @@ function toCriterionDateNumber(dateValue) {
|
|
|
19608
19610
|
const today = DateTime.now();
|
|
19609
19611
|
switch (dateValue) {
|
|
19610
19612
|
case "today":
|
|
19611
|
-
return jsDateToNumber(today);
|
|
19612
|
-
case "yesterday":
|
|
19613
|
-
|
|
19614
|
-
|
|
19615
|
-
|
|
19613
|
+
return Math.floor(jsDateToNumber(today));
|
|
19614
|
+
case "yesterday": {
|
|
19615
|
+
today.setDate(today.getDate() - 1);
|
|
19616
|
+
return Math.floor(jsDateToNumber(today));
|
|
19617
|
+
}
|
|
19618
|
+
case "tomorrow": {
|
|
19619
|
+
today.setDate(today.getDate() + 1);
|
|
19620
|
+
return Math.floor(jsDateToNumber(today));
|
|
19621
|
+
}
|
|
19616
19622
|
case "lastWeek":
|
|
19617
|
-
|
|
19618
|
-
|
|
19619
|
-
|
|
19623
|
+
today.setDate(today.getDate() - 6);
|
|
19624
|
+
return Math.floor(jsDateToNumber(today));
|
|
19625
|
+
case "lastMonth": {
|
|
19626
|
+
const lastMonth = today.getMonth() === 0 ? 11 : today.getMonth() - 1;
|
|
19627
|
+
const dateInLastMonth = new DateTime(today.getFullYear(), lastMonth, 1);
|
|
19628
|
+
if (today.getDate() > getDaysInMonth(dateInLastMonth)) {
|
|
19629
|
+
today.setDate(1);
|
|
19630
|
+
}
|
|
19631
|
+
else {
|
|
19632
|
+
today.setDate(today.getDate() + 1);
|
|
19633
|
+
today.setMonth(today.getMonth() - 1);
|
|
19634
|
+
}
|
|
19635
|
+
return Math.floor(jsDateToNumber(today));
|
|
19636
|
+
}
|
|
19620
19637
|
case "lastYear":
|
|
19621
|
-
|
|
19638
|
+
// Handle leap year case
|
|
19639
|
+
if (today.getMonth() === 1 && today.getDate() === 29) {
|
|
19640
|
+
today.setDate(28);
|
|
19641
|
+
today.setFullYear(today.getFullYear() - 1);
|
|
19642
|
+
}
|
|
19643
|
+
else {
|
|
19644
|
+
today.setDate(today.getDate() + 1);
|
|
19645
|
+
today.setFullYear(today.getFullYear() - 1);
|
|
19646
|
+
}
|
|
19647
|
+
return Math.floor(jsDateToNumber(today));
|
|
19622
19648
|
}
|
|
19623
19649
|
}
|
|
19624
19650
|
/** Get all the dates values of a criterion converted to numbers, converting date values such as "today" to actual dates */
|
|
@@ -31115,7 +31141,7 @@ class ReadonlyTransportFilter {
|
|
|
31115
31141
|
if (message.type === "CLIENT_JOINED" ||
|
|
31116
31142
|
message.type === "CLIENT_LEFT" ||
|
|
31117
31143
|
message.type === "CLIENT_MOVED") {
|
|
31118
|
-
this.transportService.sendMessage(message);
|
|
31144
|
+
await this.transportService.sendMessage(message);
|
|
31119
31145
|
}
|
|
31120
31146
|
// ignore all other messages
|
|
31121
31147
|
}
|
|
@@ -32289,7 +32315,7 @@ class Session extends EventBus {
|
|
|
32289
32315
|
}
|
|
32290
32316
|
delete this.clients[this.clientId];
|
|
32291
32317
|
this.transportService.leave(this.clientId);
|
|
32292
|
-
this.
|
|
32318
|
+
this.sendToTransport({
|
|
32293
32319
|
type: "CLIENT_LEFT",
|
|
32294
32320
|
clientId: this.clientId,
|
|
32295
32321
|
version: MESSAGE_VERSION,
|
|
@@ -32303,7 +32329,7 @@ class Session extends EventBus {
|
|
|
32303
32329
|
return;
|
|
32304
32330
|
}
|
|
32305
32331
|
const snapshotId = this.uuidGenerator.uuidv4();
|
|
32306
|
-
await this.
|
|
32332
|
+
await this.sendToTransport({
|
|
32307
32333
|
type: "SNAPSHOT",
|
|
32308
32334
|
nextRevisionId: snapshotId,
|
|
32309
32335
|
serverRevisionId: this.serverRevisionId,
|
|
@@ -32351,10 +32377,14 @@ class Session extends EventBus {
|
|
|
32351
32377
|
const type = currentPosition ? "CLIENT_MOVED" : "CLIENT_JOINED";
|
|
32352
32378
|
const client = this.getCurrentClient();
|
|
32353
32379
|
this.clients[this.clientId] = { ...client, position };
|
|
32354
|
-
this.
|
|
32380
|
+
this.sendToTransport({
|
|
32355
32381
|
type,
|
|
32356
32382
|
version: MESSAGE_VERSION,
|
|
32357
32383
|
client: { ...client, position },
|
|
32384
|
+
}).then(() => {
|
|
32385
|
+
if (this.pendingMessages.length > 0 && !this.waitingAck) {
|
|
32386
|
+
this.sendPendingMessage();
|
|
32387
|
+
}
|
|
32358
32388
|
});
|
|
32359
32389
|
}
|
|
32360
32390
|
/**
|
|
@@ -32435,7 +32465,7 @@ class Session extends EventBus {
|
|
|
32435
32465
|
if (client) {
|
|
32436
32466
|
const { position } = client;
|
|
32437
32467
|
if (position) {
|
|
32438
|
-
this.
|
|
32468
|
+
this.sendToTransport({
|
|
32439
32469
|
type: "CLIENT_MOVED",
|
|
32440
32470
|
version: MESSAGE_VERSION,
|
|
32441
32471
|
client: { ...client, position },
|
|
@@ -32456,6 +32486,10 @@ class Session extends EventBus {
|
|
|
32456
32486
|
}
|
|
32457
32487
|
this.sendPendingMessage();
|
|
32458
32488
|
}
|
|
32489
|
+
async sendToTransport(message) {
|
|
32490
|
+
// wrap in an async function to ensure it returns a promise
|
|
32491
|
+
return this.transportService.sendMessage(message);
|
|
32492
|
+
}
|
|
32459
32493
|
/**
|
|
32460
32494
|
* Send the next pending message
|
|
32461
32495
|
*/
|
|
@@ -32484,9 +32518,14 @@ class Session extends EventBus {
|
|
|
32484
32518
|
${JSON.stringify(message)}`);
|
|
32485
32519
|
}
|
|
32486
32520
|
this.waitingAck = true;
|
|
32487
|
-
this.
|
|
32521
|
+
this.sendToTransport({
|
|
32488
32522
|
...message,
|
|
32489
32523
|
serverRevisionId: this.serverRevisionId,
|
|
32524
|
+
}).catch((e) => {
|
|
32525
|
+
if (!(e instanceof ClientDisconnectedError)) {
|
|
32526
|
+
throw e.cause || e;
|
|
32527
|
+
}
|
|
32528
|
+
this.waitingAck = false;
|
|
32490
32529
|
});
|
|
32491
32530
|
}
|
|
32492
32531
|
acknowledge(message) {
|
|
@@ -33982,6 +34021,7 @@ const CF_OPERATOR_TYPE_CONVERSION_MAP = {
|
|
|
33982
34021
|
greaterThanOrEqual: "isGreaterOrEqualTo",
|
|
33983
34022
|
lessThan: "isLessThan",
|
|
33984
34023
|
lessThanOrEqual: "isLessOrEqualTo",
|
|
34024
|
+
top10: "top10",
|
|
33985
34025
|
};
|
|
33986
34026
|
/** Conversion map CF types in XLSX <=> Cf types in o_spreadsheet */
|
|
33987
34027
|
const CF_TYPE_CONVERSION_MAP = {
|
|
@@ -34522,6 +34562,7 @@ function convertConditionalFormats(xlsxCfs, dxfs, warningManager) {
|
|
|
34522
34562
|
const rule = cf.cfRules[0];
|
|
34523
34563
|
let operator;
|
|
34524
34564
|
const values = [];
|
|
34565
|
+
const cfAdditionalProperties = {};
|
|
34525
34566
|
if (rule.dxfId === undefined &&
|
|
34526
34567
|
!(rule.type === "colorScale" || rule.type === "iconSet" || rule.type === "dataBar"))
|
|
34527
34568
|
continue;
|
|
@@ -34530,7 +34571,6 @@ function convertConditionalFormats(xlsxCfs, dxfs, warningManager) {
|
|
|
34530
34571
|
case "containsErrors":
|
|
34531
34572
|
case "notContainsErrors":
|
|
34532
34573
|
case "duplicateValues":
|
|
34533
|
-
case "top10":
|
|
34534
34574
|
case "uniqueValues":
|
|
34535
34575
|
case "timePeriod":
|
|
34536
34576
|
// Not supported
|
|
@@ -34581,6 +34621,18 @@ function convertConditionalFormats(xlsxCfs, dxfs, warningManager) {
|
|
|
34581
34621
|
values.push(prefixFormulaWithEqual(rule.formula[1]));
|
|
34582
34622
|
}
|
|
34583
34623
|
break;
|
|
34624
|
+
case "top10":
|
|
34625
|
+
if (rule.rank === undefined)
|
|
34626
|
+
continue;
|
|
34627
|
+
operator = CF_OPERATOR_TYPE_CONVERSION_MAP[rule.type];
|
|
34628
|
+
values.push(rule.rank.toString());
|
|
34629
|
+
if (rule.percent) {
|
|
34630
|
+
cfAdditionalProperties.isPercent = true;
|
|
34631
|
+
}
|
|
34632
|
+
if (rule.bottom) {
|
|
34633
|
+
cfAdditionalProperties.isBottom = true;
|
|
34634
|
+
}
|
|
34635
|
+
break;
|
|
34584
34636
|
}
|
|
34585
34637
|
if (operator && rule.dxfId !== undefined) {
|
|
34586
34638
|
cfs.push({
|
|
@@ -34591,6 +34643,7 @@ function convertConditionalFormats(xlsxCfs, dxfs, warningManager) {
|
|
|
34591
34643
|
type: "CellIsRule",
|
|
34592
34644
|
operator: operator,
|
|
34593
34645
|
values: values,
|
|
34646
|
+
...cfAdditionalProperties,
|
|
34594
34647
|
style: convertStyle({ fontStyle: dxfs[rule.dxfId].font, fillStyle: dxfs[rule.dxfId].fill }, warningManager),
|
|
34595
34648
|
},
|
|
34596
34649
|
});
|
|
@@ -34809,6 +34862,8 @@ function convertOperator(operator) {
|
|
|
34809
34862
|
return "greaterThanOrEqual";
|
|
34810
34863
|
case "dateIsOnOrBefore":
|
|
34811
34864
|
return "lessThanOrEqual";
|
|
34865
|
+
case "top10":
|
|
34866
|
+
return "top10";
|
|
34812
34867
|
}
|
|
34813
34868
|
}
|
|
34814
34869
|
// -------------------------------------
|
|
@@ -40210,7 +40265,7 @@ criterionEvaluatorRegistry.add("dateIs", {
|
|
|
40210
40265
|
return false;
|
|
40211
40266
|
}
|
|
40212
40267
|
if (["lastWeek", "lastMonth", "lastYear"].includes(criterion.dateValue)) {
|
|
40213
|
-
const today =
|
|
40268
|
+
const today = Math.floor(jsDateToNumber(DateTime.now()));
|
|
40214
40269
|
return isDateBetween(dateValue, today, criterionValue);
|
|
40215
40270
|
}
|
|
40216
40271
|
return areDatesSameDay(dateValue, criterionValue);
|
|
@@ -40592,15 +40647,20 @@ criterionEvaluatorRegistry.add("isValueInList", {
|
|
|
40592
40647
|
getPreview: (criterion) => _t("Value one of: %s", criterion.values.join(", ")),
|
|
40593
40648
|
});
|
|
40594
40649
|
criterionEvaluatorRegistry.add("isValueInRange", {
|
|
40595
|
-
type: "
|
|
40596
|
-
|
|
40650
|
+
type: "isValueInRange",
|
|
40651
|
+
preComputeCriterion: (criterion, criterionRanges, getters) => {
|
|
40652
|
+
if (criterionRanges.length === 0) {
|
|
40653
|
+
return new Set();
|
|
40654
|
+
}
|
|
40655
|
+
const sheetId = criterionRanges[0].sheetId;
|
|
40656
|
+
const criterionValues = getters.getDataValidationRangeValues(sheetId, criterion);
|
|
40657
|
+
return new Set(criterionValues.map((value) => value.value.toString().toLowerCase()));
|
|
40658
|
+
},
|
|
40659
|
+
isValueValid: (value, criterion, valuesSet) => {
|
|
40597
40660
|
if (!value) {
|
|
40598
40661
|
return false;
|
|
40599
40662
|
}
|
|
40600
|
-
|
|
40601
|
-
return criterionValues
|
|
40602
|
-
.map((value) => value.value.toLowerCase())
|
|
40603
|
-
.includes(value.toString().toLowerCase());
|
|
40663
|
+
return valuesSet.has(value.toString().toLowerCase());
|
|
40604
40664
|
},
|
|
40605
40665
|
getErrorString: (criterion) => _t("The value must be a value in the range %s", String(criterion.values[0])),
|
|
40606
40666
|
isCriterionValueValid: (value) => rangeReference.test(value),
|
|
@@ -40677,6 +40737,67 @@ criterionEvaluatorRegistry.add("isNotEmpty", {
|
|
|
40677
40737
|
name: _t("Is not empty"),
|
|
40678
40738
|
getPreview: () => _t("Is not empty"),
|
|
40679
40739
|
});
|
|
40740
|
+
criterionEvaluatorRegistry.add("top10", {
|
|
40741
|
+
type: "top10",
|
|
40742
|
+
preComputeCriterion: (criterion, criterionRanges, getters) => {
|
|
40743
|
+
let value = tryToNumber(criterion.values[0], DEFAULT_LOCALE);
|
|
40744
|
+
if (value === undefined || value <= 0) {
|
|
40745
|
+
return undefined;
|
|
40746
|
+
}
|
|
40747
|
+
const numberValues = [];
|
|
40748
|
+
for (const range of criterionRanges) {
|
|
40749
|
+
for (const value of getters.getRangeValues(range)) {
|
|
40750
|
+
if (typeof value === "number") {
|
|
40751
|
+
numberValues.push(value);
|
|
40752
|
+
}
|
|
40753
|
+
}
|
|
40754
|
+
}
|
|
40755
|
+
const sortedValues = numberValues.sort((a, b) => a - b);
|
|
40756
|
+
if (criterion.isPercent) {
|
|
40757
|
+
value = clip(value, 1, 100);
|
|
40758
|
+
}
|
|
40759
|
+
let index = 0;
|
|
40760
|
+
if (criterion.isBottom && !criterion.isPercent) {
|
|
40761
|
+
index = value - 1;
|
|
40762
|
+
}
|
|
40763
|
+
else if (criterion.isBottom && criterion.isPercent) {
|
|
40764
|
+
index = Math.floor((sortedValues.length * value) / 100) - 1;
|
|
40765
|
+
}
|
|
40766
|
+
else if (!criterion.isBottom && criterion.isPercent) {
|
|
40767
|
+
index = sortedValues.length - Math.floor((sortedValues.length * value) / 100);
|
|
40768
|
+
}
|
|
40769
|
+
else {
|
|
40770
|
+
index = sortedValues.length - value;
|
|
40771
|
+
}
|
|
40772
|
+
index = clip(index, 0, sortedValues.length - 1);
|
|
40773
|
+
return sortedValues[index];
|
|
40774
|
+
},
|
|
40775
|
+
isValueValid: (value, criterion, threshold) => {
|
|
40776
|
+
if (typeof value !== "number" || threshold === undefined) {
|
|
40777
|
+
return false;
|
|
40778
|
+
}
|
|
40779
|
+
return criterion.isBottom ? value <= threshold : value >= threshold;
|
|
40780
|
+
},
|
|
40781
|
+
getErrorString: (criterion) => {
|
|
40782
|
+
const args = {
|
|
40783
|
+
value: String(criterion.values[0]),
|
|
40784
|
+
percentSymbol: criterion.isPercent ? "%" : "",
|
|
40785
|
+
};
|
|
40786
|
+
return criterion.isBottom
|
|
40787
|
+
? _t("The value must be in bottom %(value)s%(percentSymbol)s", args)
|
|
40788
|
+
: _t("The value must be in top %(value)s%(percentSymbol)s", args);
|
|
40789
|
+
},
|
|
40790
|
+
isCriterionValueValid: (value) => checkValueIsPositiveNumber(value),
|
|
40791
|
+
criterionValueErrorString: DVTerms.CriterionError.positiveNumber,
|
|
40792
|
+
numberOfValues: () => 1,
|
|
40793
|
+
name: _t("Is in Top/Bottom ranking"),
|
|
40794
|
+
getPreview: (criterion) => {
|
|
40795
|
+
const args = { value: criterion.values[0], percentSymbol: criterion.isPercent ? "%" : "" };
|
|
40796
|
+
return criterion.isBottom
|
|
40797
|
+
? _t("Value is in bottom %(value)s%(percentSymbol)s", args)
|
|
40798
|
+
: _t("Value is in top %(value)s%(percentSymbol)s", args);
|
|
40799
|
+
},
|
|
40800
|
+
});
|
|
40680
40801
|
function getNumberCriterionlocalizedValues(criterion, locale) {
|
|
40681
40802
|
return criterion.values.map((value) => {
|
|
40682
40803
|
return value !== undefined
|
|
@@ -40698,6 +40819,10 @@ function checkValueIsNumber(value) {
|
|
|
40698
40819
|
const valueAsNumber = tryToNumber(value, DEFAULT_LOCALE);
|
|
40699
40820
|
return valueAsNumber !== undefined;
|
|
40700
40821
|
}
|
|
40822
|
+
function checkValueIsPositiveNumber(value) {
|
|
40823
|
+
const valueAsNumber = tryToNumber(value, DEFAULT_LOCALE);
|
|
40824
|
+
return valueAsNumber !== undefined && valueAsNumber > 0;
|
|
40825
|
+
}
|
|
40701
40826
|
|
|
40702
40827
|
// -----------------------------------------------------------------------------
|
|
40703
40828
|
// Constants
|
|
@@ -51072,6 +51197,10 @@ class EvaluationConditionalFormatPlugin extends CoreViewPlugin {
|
|
|
51072
51197
|
break;
|
|
51073
51198
|
case "CellIsRule":
|
|
51074
51199
|
const formulas = cf.rule.values.map((value) => value.startsWith("=") ? compile(value) : undefined);
|
|
51200
|
+
const evaluator = criterionEvaluatorRegistry.get(cf.rule.operator);
|
|
51201
|
+
const criterion = { ...cf.rule, type: cf.rule.operator };
|
|
51202
|
+
const ranges = cf.ranges.map((xc) => this.getters.getRangeFromSheetXC(sheetId, xc));
|
|
51203
|
+
const preComputedCriterion = evaluator.preComputeCriterion?.(criterion, ranges, this.getters);
|
|
51075
51204
|
for (const ref of cf.ranges) {
|
|
51076
51205
|
const zone = this.getters.getRangeFromSheetXC(sheetId, ref).zone;
|
|
51077
51206
|
for (let row = zone.top; row <= zone.bottom; row++) {
|
|
@@ -51084,7 +51213,7 @@ class EvaluationConditionalFormatPlugin extends CoreViewPlugin {
|
|
|
51084
51213
|
}
|
|
51085
51214
|
return value;
|
|
51086
51215
|
});
|
|
51087
|
-
if (this.getRuleResultForTarget(target, { ...cf.rule, values })) {
|
|
51216
|
+
if (this.getRuleResultForTarget(target, { ...cf.rule, values }, preComputedCriterion)) {
|
|
51088
51217
|
if (!computedStyle[col])
|
|
51089
51218
|
computedStyle[col] = [];
|
|
51090
51219
|
// we must combine all the properties of all the CF rules applied to the given cell
|
|
@@ -51247,7 +51376,7 @@ class EvaluationConditionalFormatPlugin extends CoreViewPlugin {
|
|
|
51247
51376
|
}
|
|
51248
51377
|
}
|
|
51249
51378
|
}
|
|
51250
|
-
getRuleResultForTarget(target, rule) {
|
|
51379
|
+
getRuleResultForTarget(target, rule, preComputedCriterion) {
|
|
51251
51380
|
const cell = this.getters.getEvaluatedCell(target);
|
|
51252
51381
|
if (cell.type === CellValueType.error) {
|
|
51253
51382
|
return false;
|
|
@@ -51264,11 +51393,12 @@ class EvaluationConditionalFormatPlugin extends CoreViewPlugin {
|
|
|
51264
51393
|
return false;
|
|
51265
51394
|
}
|
|
51266
51395
|
const evaluatedCriterion = {
|
|
51396
|
+
...rule,
|
|
51267
51397
|
type: rule.operator,
|
|
51268
51398
|
values: evaluatedCriterionValues.map(toScalar),
|
|
51269
51399
|
dateValue: rule.dateValue || "exactDate",
|
|
51270
51400
|
};
|
|
51271
|
-
return evaluator.isValueValid(cell.value ?? "", evaluatedCriterion,
|
|
51401
|
+
return evaluator.isValueValid(cell.value ?? "", evaluatedCriterion, preComputedCriterion);
|
|
51272
51402
|
}
|
|
51273
51403
|
}
|
|
51274
51404
|
|
|
@@ -51285,17 +51415,20 @@ class EvaluationDataValidationPlugin extends CoreViewPlugin {
|
|
|
51285
51415
|
"isDataValidationInvalid",
|
|
51286
51416
|
];
|
|
51287
51417
|
validationResults = {};
|
|
51418
|
+
criterionPreComputeResult = {};
|
|
51288
51419
|
handle(cmd) {
|
|
51289
51420
|
if (invalidateEvaluationCommands.has(cmd.type) ||
|
|
51290
51421
|
cmd.type === "EVALUATE_CELLS" ||
|
|
51291
51422
|
(cmd.type === "UPDATE_CELL" && ("content" in cmd || "format" in cmd))) {
|
|
51292
51423
|
this.validationResults = {};
|
|
51424
|
+
this.criterionPreComputeResult = {};
|
|
51293
51425
|
return;
|
|
51294
51426
|
}
|
|
51295
51427
|
switch (cmd.type) {
|
|
51296
51428
|
case "ADD_DATA_VALIDATION_RULE":
|
|
51297
51429
|
case "REMOVE_DATA_VALIDATION_RULE":
|
|
51298
51430
|
delete this.validationResults[cmd.sheetId];
|
|
51431
|
+
delete this.criterionPreComputeResult[cmd.sheetId];
|
|
51299
51432
|
break;
|
|
51300
51433
|
}
|
|
51301
51434
|
}
|
|
@@ -51438,7 +51571,15 @@ class EvaluationDataValidationPlugin extends CoreViewPlugin {
|
|
|
51438
51571
|
return undefined;
|
|
51439
51572
|
}
|
|
51440
51573
|
const evaluatedCriterion = { ...criterion, values: evaluatedCriterionValues.map(toScalar) };
|
|
51441
|
-
if (
|
|
51574
|
+
if (!this.criterionPreComputeResult[sheetId]) {
|
|
51575
|
+
this.criterionPreComputeResult[sheetId] = {};
|
|
51576
|
+
}
|
|
51577
|
+
let preComputedCriterion = this.criterionPreComputeResult[sheetId][rule.id];
|
|
51578
|
+
if (preComputedCriterion === undefined) {
|
|
51579
|
+
preComputedCriterion = evaluator.preComputeCriterion?.(rule.criterion, rule.ranges, this.getters);
|
|
51580
|
+
this.criterionPreComputeResult[sheetId][rule.id] = preComputedCriterion;
|
|
51581
|
+
}
|
|
51582
|
+
if (evaluator.isValueValid(cellValue, evaluatedCriterion, preComputedCriterion)) {
|
|
51442
51583
|
return undefined;
|
|
51443
51584
|
}
|
|
51444
51585
|
return evaluator.getErrorString(evaluatedCriterion, this.getters, sheetId);
|
|
@@ -57133,6 +57274,7 @@ class FilterEvaluationPlugin extends UIPlugin {
|
|
|
57133
57274
|
if (filterValue.type === "none")
|
|
57134
57275
|
continue;
|
|
57135
57276
|
const evaluator = criterionEvaluatorRegistry.get(filterValue.type);
|
|
57277
|
+
const preComputedCriterion = evaluator.preComputeCriterion?.(filterValue, [filter.filteredRange], this.getters);
|
|
57136
57278
|
const evaluatedCriterionValues = filterValue.values.map((value) => {
|
|
57137
57279
|
if (!value.startsWith("=")) {
|
|
57138
57280
|
return parseLiteral(value, DEFAULT_LOCALE);
|
|
@@ -57150,7 +57292,7 @@ class FilterEvaluationPlugin extends UIPlugin {
|
|
|
57150
57292
|
for (let row = filteredZone.top; row <= filteredZone.bottom; row++) {
|
|
57151
57293
|
const position = { sheetId, col: filter.col, row };
|
|
57152
57294
|
const value = this.getters.getEvaluatedCell(position).value ?? "";
|
|
57153
|
-
if (!evaluator.isValueValid(value, evaluatedCriterion,
|
|
57295
|
+
if (!evaluator.isValueValid(value, evaluatedCriterion, preComputedCriterion)) {
|
|
57154
57296
|
hiddenRows.add(row);
|
|
57155
57297
|
}
|
|
57156
57298
|
}
|
|
@@ -61277,6 +61419,8 @@ function cellRuleFormula(ranges, rule) {
|
|
|
61277
61419
|
case undefined:
|
|
61278
61420
|
throw new Error("dateValue should be defined");
|
|
61279
61421
|
}
|
|
61422
|
+
case "top10":
|
|
61423
|
+
return [];
|
|
61280
61424
|
}
|
|
61281
61425
|
}
|
|
61282
61426
|
function cellRuleTypeAttributes(rule) {
|
|
@@ -61309,6 +61453,14 @@ function cellRuleTypeAttributes(rule) {
|
|
|
61309
61453
|
case "dateIs":
|
|
61310
61454
|
case "customFormula":
|
|
61311
61455
|
return [["type", "expression"]];
|
|
61456
|
+
case "top10": {
|
|
61457
|
+
return [
|
|
61458
|
+
["type", "top10"],
|
|
61459
|
+
["rank", rule.values[0]],
|
|
61460
|
+
["percent", rule.isPercent ? "1" : "0"],
|
|
61461
|
+
["bottom", rule.isBottom ? "1" : "0"],
|
|
61462
|
+
];
|
|
61463
|
+
}
|
|
61312
61464
|
}
|
|
61313
61465
|
}
|
|
61314
61466
|
function addDataBarRule(cf, rule) {
|
|
@@ -65029,6 +65181,7 @@ const cfOperators = [
|
|
|
65029
65181
|
"dateIsAfter",
|
|
65030
65182
|
"dateIsOnOrBefore",
|
|
65031
65183
|
"dateIsOnOrAfter",
|
|
65184
|
+
"top10",
|
|
65032
65185
|
];
|
|
65033
65186
|
const availableConditionalFormatOperators = new Set(cfOperators);
|
|
65034
65187
|
|
|
@@ -73000,6 +73153,26 @@ class SingleInputCriterionForm extends CriterionForm {
|
|
|
73000
73153
|
}
|
|
73001
73154
|
}
|
|
73002
73155
|
|
|
73156
|
+
class Top10CriterionForm extends CriterionForm {
|
|
73157
|
+
static template = "o-spreadsheet-Top10CriterionForm";
|
|
73158
|
+
static components = { CriterionInput };
|
|
73159
|
+
onValueChanged(value) {
|
|
73160
|
+
const criterion = deepCopy(this.props.criterion);
|
|
73161
|
+
criterion.values[0] = value;
|
|
73162
|
+
this.updateCriterion(criterion);
|
|
73163
|
+
}
|
|
73164
|
+
updateIsBottom(ev) {
|
|
73165
|
+
const criterion = deepCopy(this.props.criterion);
|
|
73166
|
+
criterion.isBottom = ev.target.value === "bottom";
|
|
73167
|
+
this.updateCriterion(criterion);
|
|
73168
|
+
}
|
|
73169
|
+
updateIsPercent(ev) {
|
|
73170
|
+
const criterion = deepCopy(this.props.criterion);
|
|
73171
|
+
criterion.isPercent = ev.target.value === "percent";
|
|
73172
|
+
this.updateCriterion(criterion);
|
|
73173
|
+
}
|
|
73174
|
+
}
|
|
73175
|
+
|
|
73003
73176
|
/**
|
|
73004
73177
|
* Start listening to pointer events and apply the given callbacks.
|
|
73005
73178
|
*
|
|
@@ -74169,6 +74342,7 @@ const criterionCategoriesSequences = {
|
|
|
74169
74342
|
text: 20,
|
|
74170
74343
|
number: 30,
|
|
74171
74344
|
date: 40,
|
|
74345
|
+
relative: 45,
|
|
74172
74346
|
misc: 50,
|
|
74173
74347
|
};
|
|
74174
74348
|
const criterionComponentRegistry = new Registry$1();
|
|
@@ -74346,6 +74520,12 @@ criterionComponentRegistry.add("isNotEmpty", {
|
|
|
74346
74520
|
category: "misc",
|
|
74347
74521
|
sequence: 6,
|
|
74348
74522
|
});
|
|
74523
|
+
criterionComponentRegistry.add("top10", {
|
|
74524
|
+
type: "top10",
|
|
74525
|
+
component: Top10CriterionForm,
|
|
74526
|
+
category: "relative",
|
|
74527
|
+
sequence: 7,
|
|
74528
|
+
});
|
|
74349
74529
|
function getCriterionMenuItems(callback, availableTypes) {
|
|
74350
74530
|
const items = criterionComponentRegistry
|
|
74351
74531
|
.getAll()
|
|
@@ -75976,7 +76156,17 @@ const FORMAT_PERCENT_ACTION = (env) => setFormatter(env, "0.00%");
|
|
|
75976
76156
|
// Side panel
|
|
75977
76157
|
//------------------------------------------------------------------------------
|
|
75978
76158
|
const OPEN_CF_SIDEPANEL_ACTION = (env) => {
|
|
75979
|
-
|
|
76159
|
+
const sheetId = env.model.getters.getActiveSheetId();
|
|
76160
|
+
const zones = env.model.getters.getSelectedZones();
|
|
76161
|
+
const rules = env.model.getters.getConditionalFormats(sheetId);
|
|
76162
|
+
const ruleIds = env.model.getters.getRulesSelection(sheetId, zones);
|
|
76163
|
+
if (ruleIds.length === 1) {
|
|
76164
|
+
return env.openSidePanel("ConditionalFormattingEditor", {
|
|
76165
|
+
cf: rules.find((r) => r.id === ruleIds[0]),
|
|
76166
|
+
isNewCf: false,
|
|
76167
|
+
});
|
|
76168
|
+
}
|
|
76169
|
+
return env.openSidePanel("ConditionalFormatting");
|
|
75980
76170
|
};
|
|
75981
76171
|
const INSERT_LINK = (env) => {
|
|
75982
76172
|
const { col, row } = env.model.getters.getActivePosition();
|
|
@@ -76974,12 +77164,12 @@ const insertDropdown = {
|
|
|
76974
77164
|
const zones = env.model.getters.getSelectedZones();
|
|
76975
77165
|
const sheetId = env.model.getters.getActiveSheetId();
|
|
76976
77166
|
const ranges = zones.map((zone) => env.model.getters.getRangeDataFromZone(sheetId, zone));
|
|
76977
|
-
const
|
|
77167
|
+
const ruleId = env.model.uuidGenerator.smallUuid();
|
|
76978
77168
|
env.model.dispatch("ADD_DATA_VALIDATION_RULE", {
|
|
76979
77169
|
ranges,
|
|
76980
77170
|
sheetId,
|
|
76981
77171
|
rule: {
|
|
76982
|
-
id:
|
|
77172
|
+
id: ruleId,
|
|
76983
77173
|
criterion: {
|
|
76984
77174
|
type: "isValueInList",
|
|
76985
77175
|
values: [],
|
|
@@ -76987,16 +77177,11 @@ const insertDropdown = {
|
|
|
76987
77177
|
},
|
|
76988
77178
|
},
|
|
76989
77179
|
});
|
|
76990
|
-
const rule = env.model.getters.getDataValidationRule(sheetId,
|
|
77180
|
+
const rule = env.model.getters.getDataValidationRule(sheetId, ruleId);
|
|
76991
77181
|
if (!rule) {
|
|
76992
77182
|
return;
|
|
76993
77183
|
}
|
|
76994
|
-
env.openSidePanel("DataValidationEditor", {
|
|
76995
|
-
rule: localizeDataValidationRule(rule, env.model.getters.getLocale()),
|
|
76996
|
-
onExit: () => {
|
|
76997
|
-
env.replaceSidePanel("DataValidation", "DataValidationEditor");
|
|
76998
|
-
},
|
|
76999
|
-
});
|
|
77184
|
+
env.openSidePanel("DataValidationEditor", { ruleId });
|
|
77000
77185
|
},
|
|
77001
77186
|
isEnabled: (env) => !env.isSmall,
|
|
77002
77187
|
icon: "o-spreadsheet-Icon.INSERT_DROPDOWN",
|
|
@@ -80271,7 +80456,6 @@ class GridOverlay extends Component {
|
|
|
80271
80456
|
onGridMoved: Function,
|
|
80272
80457
|
gridOverlayDimensions: String,
|
|
80273
80458
|
slots: { type: Object, optional: true },
|
|
80274
|
-
getGridSize: Function,
|
|
80275
80459
|
};
|
|
80276
80460
|
static components = {
|
|
80277
80461
|
FiguresContainer,
|
|
@@ -80290,14 +80474,7 @@ class GridOverlay extends Component {
|
|
|
80290
80474
|
setup() {
|
|
80291
80475
|
useCellHovered(this.env, this.gridOverlay);
|
|
80292
80476
|
const resizeObserver = new ResizeObserver(() => {
|
|
80293
|
-
|
|
80294
|
-
const { width, height } = this.props.getGridSize();
|
|
80295
|
-
this.props.onGridResized({
|
|
80296
|
-
x: boundingRect.left,
|
|
80297
|
-
y: boundingRect.top,
|
|
80298
|
-
height: height,
|
|
80299
|
-
width: width,
|
|
80300
|
-
});
|
|
80477
|
+
this.props.onGridResized();
|
|
80301
80478
|
});
|
|
80302
80479
|
onMounted(() => {
|
|
80303
80480
|
resizeObserver.observe(this.gridOverlayEl);
|
|
@@ -85456,210 +85633,36 @@ class IconPicker extends Component {
|
|
|
85456
85633
|
}
|
|
85457
85634
|
}
|
|
85458
85635
|
|
|
85459
|
-
|
|
85460
|
-
|
|
85461
|
-
useHighlights({
|
|
85462
|
-
get highlights() {
|
|
85463
|
-
return hoverState.hovered ? highlightProvider.highlights : [];
|
|
85464
|
-
},
|
|
85465
|
-
});
|
|
85466
|
-
}
|
|
85467
|
-
function useHighlights(highlightProvider) {
|
|
85468
|
-
const stores = useStoreProvider();
|
|
85469
|
-
const store = useLocalStore(HighlightStore);
|
|
85470
|
-
onMounted(() => {
|
|
85471
|
-
store.register(highlightProvider);
|
|
85472
|
-
});
|
|
85473
|
-
let currentHighlights = highlightProvider.highlights;
|
|
85474
|
-
useEffect((highlights) => {
|
|
85475
|
-
if (!deepEquals(highlights, currentHighlights)) {
|
|
85476
|
-
currentHighlights = highlights;
|
|
85477
|
-
stores.trigger("store-updated");
|
|
85478
|
-
}
|
|
85479
|
-
}, () => [highlightProvider.highlights]);
|
|
85480
|
-
}
|
|
85481
|
-
|
|
85482
|
-
class ConditionalFormatPreview extends Component {
|
|
85483
|
-
static template = "o-spreadsheet-ConditionalFormatPreview";
|
|
85484
|
-
icons = ICONS;
|
|
85485
|
-
ref = useRef("cfPreview");
|
|
85486
|
-
setup() {
|
|
85487
|
-
useHighlightsOnHover(this.ref, this);
|
|
85488
|
-
}
|
|
85489
|
-
getPreviewImageStyle() {
|
|
85490
|
-
const rule = this.props.conditionalFormat.rule;
|
|
85491
|
-
if (rule.type === "CellIsRule") {
|
|
85492
|
-
return cssPropertiesToCss(cellStyleToCss(rule.style));
|
|
85493
|
-
}
|
|
85494
|
-
else if (rule.type === "ColorScaleRule") {
|
|
85495
|
-
const minColor = colorNumberToHex(rule.minimum.color);
|
|
85496
|
-
const midColor = rule.midpoint ? colorNumberToHex(rule.midpoint.color) : null;
|
|
85497
|
-
const maxColor = colorNumberToHex(rule.maximum.color);
|
|
85498
|
-
const baseString = "background-image: linear-gradient(to right, ";
|
|
85499
|
-
return midColor
|
|
85500
|
-
? baseString + minColor + ", " + midColor + ", " + maxColor + ")"
|
|
85501
|
-
: baseString + minColor + ", " + maxColor + ")";
|
|
85502
|
-
}
|
|
85503
|
-
else if (rule.type === "DataBarRule") {
|
|
85504
|
-
const barColor = colorNumberToHex(rule.color);
|
|
85505
|
-
const gradient = `background-image: linear-gradient(to right, ${barColor} 50%, white 50%)`;
|
|
85506
|
-
return `${gradient}; color: ${TEXT_BODY};`;
|
|
85507
|
-
}
|
|
85508
|
-
return "";
|
|
85509
|
-
}
|
|
85510
|
-
getDescription() {
|
|
85511
|
-
const cf = this.props.conditionalFormat;
|
|
85512
|
-
switch (cf.rule.type) {
|
|
85513
|
-
case "CellIsRule":
|
|
85514
|
-
return criterionEvaluatorRegistry
|
|
85515
|
-
.get(cf.rule.operator)
|
|
85516
|
-
.getPreview({ ...cf.rule, type: cf.rule.operator }, this.env.model.getters);
|
|
85517
|
-
case "ColorScaleRule":
|
|
85518
|
-
return CfTerms.ColorScale;
|
|
85519
|
-
case "IconSetRule":
|
|
85520
|
-
return CfTerms.IconSet;
|
|
85521
|
-
case "DataBarRule":
|
|
85522
|
-
return CfTerms.DataBar;
|
|
85523
|
-
}
|
|
85524
|
-
}
|
|
85525
|
-
deleteConditionalFormat() {
|
|
85526
|
-
this.env.model.dispatch("REMOVE_CONDITIONAL_FORMAT", {
|
|
85527
|
-
id: this.props.conditionalFormat.id,
|
|
85528
|
-
sheetId: this.env.model.getters.getActiveSheetId(),
|
|
85529
|
-
});
|
|
85530
|
-
}
|
|
85531
|
-
onMouseDown(event) {
|
|
85532
|
-
this.props.onMouseDown(event);
|
|
85533
|
-
}
|
|
85534
|
-
get highlights() {
|
|
85535
|
-
const sheetId = this.env.model.getters.getActiveSheetId();
|
|
85536
|
-
return this.props.conditionalFormat.ranges.map((range) => ({
|
|
85537
|
-
range: this.env.model.getters.getRangeFromSheetXC(sheetId, range),
|
|
85538
|
-
color: HIGHLIGHT_COLOR,
|
|
85539
|
-
fillAlpha: 0.06,
|
|
85540
|
-
}));
|
|
85541
|
-
}
|
|
85542
|
-
}
|
|
85543
|
-
ConditionalFormatPreview.props = {
|
|
85544
|
-
conditionalFormat: Object,
|
|
85545
|
-
onPreviewClick: Function,
|
|
85546
|
-
onMouseDown: Function,
|
|
85547
|
-
class: String,
|
|
85548
|
-
};
|
|
85549
|
-
|
|
85550
|
-
class ConditionalFormatPreviewList extends Component {
|
|
85551
|
-
static template = "o-spreadsheet-ConditionalFormatPreviewList";
|
|
85552
|
-
static props = {
|
|
85553
|
-
conditionalFormats: Array,
|
|
85554
|
-
onPreviewClick: Function,
|
|
85555
|
-
onAddConditionalFormat: Function,
|
|
85556
|
-
};
|
|
85557
|
-
static components = { ConditionalFormatPreview };
|
|
85558
|
-
icons = ICONS;
|
|
85559
|
-
dragAndDrop = useDragAndDropListItems();
|
|
85560
|
-
cfListRef = useRef("cfList");
|
|
85561
|
-
setup() {
|
|
85562
|
-
onWillUpdateProps((nextProps) => {
|
|
85563
|
-
if (!deepEquals(this.props.conditionalFormats, nextProps.conditionalFormats)) {
|
|
85564
|
-
this.dragAndDrop.cancel();
|
|
85565
|
-
}
|
|
85566
|
-
});
|
|
85567
|
-
}
|
|
85568
|
-
getPreviewDivStyle(cf) {
|
|
85569
|
-
return this.dragAndDrop.itemsStyle[cf.id] || "";
|
|
85570
|
-
}
|
|
85571
|
-
onPreviewMouseDown(cf, event) {
|
|
85572
|
-
if (event.button !== 0)
|
|
85573
|
-
return;
|
|
85574
|
-
const previewRects = Array.from(this.cfListRef.el.children).map((previewEl) => getBoundingRectAsPOJO(previewEl));
|
|
85575
|
-
const items = this.props.conditionalFormats.map((cf, index) => ({
|
|
85576
|
-
id: cf.id,
|
|
85577
|
-
size: previewRects[index].height,
|
|
85578
|
-
position: previewRects[index].y,
|
|
85579
|
-
}));
|
|
85580
|
-
this.dragAndDrop.start("vertical", {
|
|
85581
|
-
draggedItemId: cf.id,
|
|
85582
|
-
initialMousePosition: event.clientY,
|
|
85583
|
-
items: items,
|
|
85584
|
-
scrollableContainerEl: this.cfListRef.el,
|
|
85585
|
-
onDragEnd: (cfId, finalIndex) => this.onDragEnd(cfId, finalIndex),
|
|
85586
|
-
});
|
|
85587
|
-
}
|
|
85588
|
-
onDragEnd(cfId, finalIndex) {
|
|
85589
|
-
const originalIndex = this.props.conditionalFormats.findIndex((sheet) => sheet.id === cfId);
|
|
85590
|
-
const delta = originalIndex - finalIndex;
|
|
85591
|
-
if (delta !== 0) {
|
|
85592
|
-
this.env.model.dispatch("CHANGE_CONDITIONAL_FORMAT_PRIORITY", {
|
|
85593
|
-
cfId,
|
|
85594
|
-
delta,
|
|
85595
|
-
sheetId: this.env.model.getters.getActiveSheetId(),
|
|
85596
|
-
});
|
|
85597
|
-
}
|
|
85598
|
-
}
|
|
85599
|
-
}
|
|
85600
|
-
|
|
85601
|
-
class ConditionalFormattingEditor extends Component {
|
|
85602
|
-
static template = "o-spreadsheet-ConditionalFormattingEditor";
|
|
85603
|
-
static props = {
|
|
85604
|
-
editedCf: Object,
|
|
85605
|
-
onCancel: Function,
|
|
85606
|
-
onExit: Function,
|
|
85607
|
-
isNewCf: Boolean,
|
|
85608
|
-
};
|
|
85609
|
-
static components = {
|
|
85610
|
-
SelectionInput,
|
|
85611
|
-
IconPicker,
|
|
85612
|
-
ColorPickerWidget,
|
|
85613
|
-
ConditionalFormatPreviewList,
|
|
85614
|
-
Section,
|
|
85615
|
-
RoundColorPicker,
|
|
85616
|
-
StandaloneComposer,
|
|
85617
|
-
BadgeSelection,
|
|
85618
|
-
ValidationMessages,
|
|
85619
|
-
SelectMenu,
|
|
85620
|
-
};
|
|
85636
|
+
class ConditionalFormattingEditorStore extends SpreadsheetStore {
|
|
85637
|
+
mutators = ["updateConditionalFormat", "closeMenus"];
|
|
85621
85638
|
icons = ICONS;
|
|
85622
85639
|
iconSets = ICON_SETS;
|
|
85623
|
-
getTextDecoration = getTextDecoration;
|
|
85624
|
-
colorNumberToHex = colorNumberToHex;
|
|
85625
85640
|
state;
|
|
85626
|
-
|
|
85641
|
+
cfId;
|
|
85642
|
+
constructor(get, cf, isNewCf) {
|
|
85643
|
+
super(get);
|
|
85644
|
+
this.cfId = cf.id;
|
|
85627
85645
|
this.state = useState({
|
|
85628
85646
|
errors: [],
|
|
85629
|
-
currentCFType:
|
|
85630
|
-
ranges:
|
|
85647
|
+
currentCFType: cf.rule.type,
|
|
85648
|
+
ranges: cf.ranges,
|
|
85631
85649
|
rules: this.getDefaultRules(),
|
|
85632
|
-
hasEditedCf:
|
|
85650
|
+
hasEditedCf: isNewCf,
|
|
85633
85651
|
});
|
|
85634
|
-
switch (
|
|
85652
|
+
switch (cf.rule.type) {
|
|
85635
85653
|
case "CellIsRule":
|
|
85636
|
-
this.state.rules.cellIs =
|
|
85654
|
+
this.state.rules.cellIs = cf.rule;
|
|
85637
85655
|
break;
|
|
85638
85656
|
case "ColorScaleRule":
|
|
85639
|
-
this.state.rules.colorScale =
|
|
85657
|
+
this.state.rules.colorScale = cf.rule;
|
|
85640
85658
|
break;
|
|
85641
85659
|
case "IconSetRule":
|
|
85642
|
-
this.state.rules.iconSet =
|
|
85660
|
+
this.state.rules.iconSet = cf.rule;
|
|
85643
85661
|
break;
|
|
85644
85662
|
case "DataBarRule":
|
|
85645
|
-
this.state.rules.dataBar =
|
|
85663
|
+
this.state.rules.dataBar = cf.rule;
|
|
85646
85664
|
break;
|
|
85647
85665
|
}
|
|
85648
|
-
useExternalListener(window, "click", this.closeMenus);
|
|
85649
|
-
}
|
|
85650
|
-
get isRangeValid() {
|
|
85651
|
-
return this.state.errors.includes("EmptyRange" /* CommandResult.EmptyRange */);
|
|
85652
|
-
}
|
|
85653
|
-
get errorMessages() {
|
|
85654
|
-
return this.state.errors.map((error) => CfTerms.Errors[error] || CfTerms.Errors.Unexpected);
|
|
85655
|
-
}
|
|
85656
|
-
get cfTypesValues() {
|
|
85657
|
-
return [
|
|
85658
|
-
{ value: "CellIsRule", label: _t("Single color") },
|
|
85659
|
-
{ value: "ColorScaleRule", label: _t("Color scale") },
|
|
85660
|
-
{ value: "IconSetRule", label: _t("Icon set") },
|
|
85661
|
-
{ value: "DataBarRule", label: _t("Data bar") },
|
|
85662
|
-
];
|
|
85663
85666
|
}
|
|
85664
85667
|
updateConditionalFormat(newCf) {
|
|
85665
85668
|
const ranges = newCf.ranges || this.state.ranges;
|
|
@@ -85668,17 +85671,17 @@ class ConditionalFormattingEditor extends Component {
|
|
|
85668
85671
|
if (!newCf.suppressErrors) {
|
|
85669
85672
|
this.state.errors = ["InvalidRange" /* CommandResult.InvalidRange */];
|
|
85670
85673
|
}
|
|
85671
|
-
return
|
|
85674
|
+
return;
|
|
85672
85675
|
}
|
|
85673
|
-
const sheetId = this.
|
|
85674
|
-
const locale = this.
|
|
85676
|
+
const sheetId = this.model.getters.getActiveSheetId();
|
|
85677
|
+
const locale = this.model.getters.getLocale();
|
|
85675
85678
|
const rule = newCf.rule || this.getEditedRule(this.state.currentCFType);
|
|
85676
|
-
const result = this.
|
|
85679
|
+
const result = this.model.dispatch("ADD_CONDITIONAL_FORMAT", {
|
|
85677
85680
|
cf: {
|
|
85681
|
+
id: this.cfId,
|
|
85678
85682
|
rule: canonicalizeCFRule(rule, locale),
|
|
85679
|
-
id: this.props.editedCf.id,
|
|
85680
85683
|
},
|
|
85681
|
-
ranges: ranges.map((xc) => this.
|
|
85684
|
+
ranges: ranges.map((xc) => this.model.getters.getRangeDataFromXc(sheetId, xc)),
|
|
85682
85685
|
sheetId,
|
|
85683
85686
|
});
|
|
85684
85687
|
if (result.isSuccessful) {
|
|
@@ -85688,71 +85691,18 @@ class ConditionalFormattingEditor extends Component {
|
|
|
85688
85691
|
if (!newCf.suppressErrors) {
|
|
85689
85692
|
this.state.errors = reasons;
|
|
85690
85693
|
}
|
|
85691
|
-
return reasons;
|
|
85692
85694
|
}
|
|
85693
|
-
|
|
85694
|
-
|
|
85695
|
-
case "CellIsRule":
|
|
85696
|
-
return this.state.rules.cellIs;
|
|
85697
|
-
case "ColorScaleRule":
|
|
85698
|
-
return this.state.rules.colorScale;
|
|
85699
|
-
case "IconSetRule":
|
|
85700
|
-
return this.state.rules.iconSet;
|
|
85701
|
-
case "DataBarRule":
|
|
85702
|
-
return this.state.rules.dataBar;
|
|
85703
|
-
}
|
|
85695
|
+
get isRangeValid() {
|
|
85696
|
+
return this.state.errors.includes("EmptyRange" /* CommandResult.EmptyRange */);
|
|
85704
85697
|
}
|
|
85705
|
-
|
|
85706
|
-
|
|
85707
|
-
if (result.length === 0) {
|
|
85708
|
-
this.props.onExit();
|
|
85709
|
-
}
|
|
85698
|
+
get errorMessages() {
|
|
85699
|
+
return this.state.errors.map((error) => CfTerms.Errors[error] || CfTerms.Errors.Unexpected);
|
|
85710
85700
|
}
|
|
85711
|
-
|
|
85712
|
-
|
|
85713
|
-
this.props.onCancel();
|
|
85714
|
-
}
|
|
85715
|
-
else {
|
|
85716
|
-
this.props.onExit();
|
|
85717
|
-
}
|
|
85701
|
+
onRangeUpdate(ranges) {
|
|
85702
|
+
this.state.ranges = ranges;
|
|
85718
85703
|
}
|
|
85719
|
-
|
|
85720
|
-
|
|
85721
|
-
cellIs: {
|
|
85722
|
-
type: "CellIsRule",
|
|
85723
|
-
operator: "isNotEmpty",
|
|
85724
|
-
values: [],
|
|
85725
|
-
style: { fillColor: "#b6d7a8" },
|
|
85726
|
-
},
|
|
85727
|
-
colorScale: {
|
|
85728
|
-
type: "ColorScaleRule",
|
|
85729
|
-
minimum: { type: "value", color: hexaToInt("EFF7FF") },
|
|
85730
|
-
midpoint: undefined,
|
|
85731
|
-
maximum: { type: "value", color: 0x6aa84f },
|
|
85732
|
-
},
|
|
85733
|
-
iconSet: {
|
|
85734
|
-
type: "IconSetRule",
|
|
85735
|
-
icons: {
|
|
85736
|
-
upper: "arrowGood",
|
|
85737
|
-
middle: "arrowNeutral",
|
|
85738
|
-
lower: "arrowBad",
|
|
85739
|
-
},
|
|
85740
|
-
upperInflectionPoint: {
|
|
85741
|
-
type: "percentage",
|
|
85742
|
-
value: "66",
|
|
85743
|
-
operator: "gt",
|
|
85744
|
-
},
|
|
85745
|
-
lowerInflectionPoint: {
|
|
85746
|
-
type: "percentage",
|
|
85747
|
-
value: "33",
|
|
85748
|
-
operator: "gt",
|
|
85749
|
-
},
|
|
85750
|
-
},
|
|
85751
|
-
dataBar: {
|
|
85752
|
-
type: "DataBarRule",
|
|
85753
|
-
color: 0xd9ead3,
|
|
85754
|
-
},
|
|
85755
|
-
};
|
|
85704
|
+
onRangeConfirmed() {
|
|
85705
|
+
this.updateConditionalFormat({ ranges: this.state.ranges });
|
|
85756
85706
|
}
|
|
85757
85707
|
changeRuleType(ruleType) {
|
|
85758
85708
|
if (this.state.currentCFType === ruleType) {
|
|
@@ -85762,34 +85712,45 @@ class ConditionalFormattingEditor extends Component {
|
|
|
85762
85712
|
this.state.currentCFType = ruleType;
|
|
85763
85713
|
this.updateConditionalFormat({ rule: this.getEditedRule(ruleType), suppressErrors: true });
|
|
85764
85714
|
}
|
|
85765
|
-
|
|
85766
|
-
|
|
85767
|
-
|
|
85768
|
-
|
|
85769
|
-
|
|
85770
|
-
|
|
85771
|
-
|
|
85772
|
-
|
|
85773
|
-
|
|
85774
|
-
|
|
85775
|
-
const isSelected = this.state.openedMenu === menu;
|
|
85776
|
-
this.closeMenus();
|
|
85777
|
-
if (!isSelected) {
|
|
85778
|
-
this.state.openedMenu = menu;
|
|
85715
|
+
getEditedRule(ruleType) {
|
|
85716
|
+
switch (ruleType) {
|
|
85717
|
+
case "CellIsRule":
|
|
85718
|
+
return this.state.rules.cellIs;
|
|
85719
|
+
case "ColorScaleRule":
|
|
85720
|
+
return this.state.rules.colorScale;
|
|
85721
|
+
case "IconSetRule":
|
|
85722
|
+
return this.state.rules.iconSet;
|
|
85723
|
+
case "DataBarRule":
|
|
85724
|
+
return this.state.rules.dataBar;
|
|
85779
85725
|
}
|
|
85780
85726
|
}
|
|
85781
|
-
closeMenus() {
|
|
85782
|
-
this.state.openedMenu = undefined;
|
|
85783
|
-
}
|
|
85784
85727
|
/*****************************************************************************
|
|
85785
85728
|
* Cell Is Rule
|
|
85786
85729
|
****************************************************************************/
|
|
85787
|
-
get
|
|
85788
|
-
return (
|
|
85789
|
-
|
|
85730
|
+
get cfCriterionMenuItems() {
|
|
85731
|
+
return getCriterionMenuItems((type) => this.editOperator(type), availableConditionalFormatOperators);
|
|
85732
|
+
}
|
|
85733
|
+
get selectedCriterionName() {
|
|
85734
|
+
return criterionEvaluatorRegistry.get(this.state.rules.cellIs.operator).name;
|
|
85735
|
+
}
|
|
85736
|
+
get criterionComponent() {
|
|
85737
|
+
return criterionComponentRegistry.get(this.state.rules.cellIs.operator).component;
|
|
85790
85738
|
}
|
|
85791
|
-
get
|
|
85792
|
-
return
|
|
85739
|
+
get genericCriterion() {
|
|
85740
|
+
return {
|
|
85741
|
+
...this.state.rules.cellIs,
|
|
85742
|
+
type: this.state.rules.cellIs.operator,
|
|
85743
|
+
};
|
|
85744
|
+
}
|
|
85745
|
+
onRuleValuesChanged(criterion) {
|
|
85746
|
+
const newRule = {
|
|
85747
|
+
...criterion,
|
|
85748
|
+
operator: criterion.type,
|
|
85749
|
+
type: "CellIsRule",
|
|
85750
|
+
style: this.state.rules.cellIs.style,
|
|
85751
|
+
};
|
|
85752
|
+
this.state.rules.cellIs = newRule;
|
|
85753
|
+
this.updateConditionalFormat({ rule: newRule });
|
|
85793
85754
|
}
|
|
85794
85755
|
toggleStyle(tool) {
|
|
85795
85756
|
const style = this.state.rules.cellIs.style;
|
|
@@ -85797,18 +85758,6 @@ class ConditionalFormattingEditor extends Component {
|
|
|
85797
85758
|
this.updateConditionalFormat({ rule: this.state.rules.cellIs });
|
|
85798
85759
|
this.closeMenus();
|
|
85799
85760
|
}
|
|
85800
|
-
onKeydown(event) {
|
|
85801
|
-
if (event.key === "F4") {
|
|
85802
|
-
const target = event.target;
|
|
85803
|
-
const update = cycleFixedReference({ start: target.selectionStart ?? 0, end: target.selectionEnd ?? 0 }, target.value, this.env.model.getters.getLocale());
|
|
85804
|
-
if (!update) {
|
|
85805
|
-
return;
|
|
85806
|
-
}
|
|
85807
|
-
target.value = update.content;
|
|
85808
|
-
target.setSelectionRange(update.selection.start, update.selection.end);
|
|
85809
|
-
target.dispatchEvent(new Event("input"));
|
|
85810
|
-
}
|
|
85811
|
-
}
|
|
85812
85761
|
setColor(target, color) {
|
|
85813
85762
|
this.state.rules.cellIs.style[target] = color;
|
|
85814
85763
|
this.updateConditionalFormat({ rule: this.state.rules.cellIs });
|
|
@@ -85822,62 +85771,10 @@ class ConditionalFormattingEditor extends Component {
|
|
|
85822
85771
|
this.updateConditionalFormat({ rule: this.state.rules.cellIs, suppressErrors: true });
|
|
85823
85772
|
this.closeMenus();
|
|
85824
85773
|
}
|
|
85825
|
-
get cfCriterionMenuItems() {
|
|
85826
|
-
return getCriterionMenuItems((type) => this.editOperator(type), availableConditionalFormatOperators);
|
|
85827
|
-
}
|
|
85828
|
-
get selectedCriterionName() {
|
|
85829
|
-
return criterionEvaluatorRegistry.get(this.state.rules.cellIs.operator).name;
|
|
85830
|
-
}
|
|
85831
|
-
get criterionComponent() {
|
|
85832
|
-
return criterionComponentRegistry.get(this.state.rules.cellIs.operator).component;
|
|
85833
|
-
}
|
|
85834
|
-
get genericCriterion() {
|
|
85835
|
-
return {
|
|
85836
|
-
type: this.state.rules.cellIs.operator,
|
|
85837
|
-
values: this.state.rules.cellIs.values,
|
|
85838
|
-
dateValue: this.state.rules.cellIs.dateValue,
|
|
85839
|
-
};
|
|
85840
|
-
}
|
|
85841
|
-
onRuleValuesChanged(rule) {
|
|
85842
|
-
this.state.rules.cellIs.values = rule.values;
|
|
85843
|
-
this.state.rules.cellIs.dateValue = rule.dateValue;
|
|
85844
|
-
this.updateConditionalFormat({
|
|
85845
|
-
rule: { ...this.state.rules.cellIs, values: rule.values, dateValue: rule.dateValue },
|
|
85846
|
-
});
|
|
85847
|
-
}
|
|
85848
85774
|
/*****************************************************************************
|
|
85849
85775
|
* Color Scale Rule
|
|
85850
85776
|
****************************************************************************/
|
|
85851
|
-
|
|
85852
|
-
switch (threshold) {
|
|
85853
|
-
case "minimum":
|
|
85854
|
-
return (this.state.errors.includes("MinInvalidFormula" /* CommandResult.MinInvalidFormula */) ||
|
|
85855
|
-
this.state.errors.includes("MinBiggerThanMid" /* CommandResult.MinBiggerThanMid */) ||
|
|
85856
|
-
this.state.errors.includes("MinBiggerThanMax" /* CommandResult.MinBiggerThanMax */) ||
|
|
85857
|
-
this.state.errors.includes("MinNaN" /* CommandResult.MinNaN */));
|
|
85858
|
-
case "midpoint":
|
|
85859
|
-
return (this.state.errors.includes("MidInvalidFormula" /* CommandResult.MidInvalidFormula */) ||
|
|
85860
|
-
this.state.errors.includes("MidNaN" /* CommandResult.MidNaN */) ||
|
|
85861
|
-
this.state.errors.includes("MidBiggerThanMax" /* CommandResult.MidBiggerThanMax */));
|
|
85862
|
-
case "maximum":
|
|
85863
|
-
return (this.state.errors.includes("MaxInvalidFormula" /* CommandResult.MaxInvalidFormula */) ||
|
|
85864
|
-
this.state.errors.includes("MaxNaN" /* CommandResult.MaxNaN */));
|
|
85865
|
-
default:
|
|
85866
|
-
return false;
|
|
85867
|
-
}
|
|
85868
|
-
}
|
|
85869
|
-
setColorScaleColor(target, color) {
|
|
85870
|
-
if (!isColorValid(color)) {
|
|
85871
|
-
return;
|
|
85872
|
-
}
|
|
85873
|
-
const point = this.state.rules.colorScale[target];
|
|
85874
|
-
if (point) {
|
|
85875
|
-
point.color = colorToNumber(color);
|
|
85876
|
-
}
|
|
85877
|
-
this.updateConditionalFormat({ rule: this.state.rules.colorScale });
|
|
85878
|
-
this.closeMenus();
|
|
85879
|
-
}
|
|
85880
|
-
getColorScalePreviewStyle() {
|
|
85777
|
+
get previewGradient() {
|
|
85881
85778
|
const rule = this.state.rules.colorScale;
|
|
85882
85779
|
const minColor = colorNumberToHex(rule.minimum.color);
|
|
85883
85780
|
const midColor = colorNumberToHex(rule.midpoint?.color || DEFAULT_COLOR_SCALE_MIDPOINT_COLOR);
|
|
@@ -85891,13 +85788,7 @@ class ConditionalFormattingEditor extends Component {
|
|
|
85891
85788
|
color: "#000",
|
|
85892
85789
|
});
|
|
85893
85790
|
}
|
|
85894
|
-
|
|
85895
|
-
return threshold
|
|
85896
|
-
? colorNumberToHex(threshold.color)
|
|
85897
|
-
: colorNumberToHex(DEFAULT_COLOR_SCALE_MIDPOINT_COLOR);
|
|
85898
|
-
}
|
|
85899
|
-
onMidpointChange(ev) {
|
|
85900
|
-
const type = ev.target.value;
|
|
85791
|
+
onMidpointChange(type) {
|
|
85901
85792
|
const rule = this.state.rules.colorScale;
|
|
85902
85793
|
if (type === "none") {
|
|
85903
85794
|
rule.midpoint = undefined;
|
|
@@ -85920,23 +85811,20 @@ class ConditionalFormattingEditor extends Component {
|
|
|
85920
85811
|
this.state.rules.colorScale[threshold].value = value;
|
|
85921
85812
|
this.updateConditionalFormat({ rule: this.state.rules.colorScale });
|
|
85922
85813
|
}
|
|
85814
|
+
setColorScaleColor(target, color) {
|
|
85815
|
+
if (!isColorValid(color)) {
|
|
85816
|
+
return;
|
|
85817
|
+
}
|
|
85818
|
+
const point = this.state.rules.colorScale[target];
|
|
85819
|
+
if (point) {
|
|
85820
|
+
point.color = colorToNumber(color);
|
|
85821
|
+
}
|
|
85822
|
+
this.updateConditionalFormat({ rule: this.state.rules.colorScale });
|
|
85823
|
+
this.closeMenus();
|
|
85824
|
+
}
|
|
85923
85825
|
/*****************************************************************************
|
|
85924
85826
|
* Icon Set
|
|
85925
85827
|
****************************************************************************/
|
|
85926
|
-
isInflectionPointInvalid(inflectionPoint) {
|
|
85927
|
-
switch (inflectionPoint) {
|
|
85928
|
-
case "lowerInflectionPoint":
|
|
85929
|
-
return (this.state.errors.includes("ValueLowerInflectionNaN" /* CommandResult.ValueLowerInflectionNaN */) ||
|
|
85930
|
-
this.state.errors.includes("ValueLowerInvalidFormula" /* CommandResult.ValueLowerInvalidFormula */) ||
|
|
85931
|
-
this.state.errors.includes("LowerBiggerThanUpper" /* CommandResult.LowerBiggerThanUpper */));
|
|
85932
|
-
case "upperInflectionPoint":
|
|
85933
|
-
return (this.state.errors.includes("ValueUpperInflectionNaN" /* CommandResult.ValueUpperInflectionNaN */) ||
|
|
85934
|
-
this.state.errors.includes("ValueUpperInvalidFormula" /* CommandResult.ValueUpperInvalidFormula */) ||
|
|
85935
|
-
this.state.errors.includes("LowerBiggerThanUpper" /* CommandResult.LowerBiggerThanUpper */));
|
|
85936
|
-
default:
|
|
85937
|
-
return true;
|
|
85938
|
-
}
|
|
85939
|
-
}
|
|
85940
85828
|
reverseIcons() {
|
|
85941
85829
|
const icons = this.state.rules.iconSet.icons;
|
|
85942
85830
|
const upper = icons.upper;
|
|
@@ -85963,12 +85851,176 @@ class ConditionalFormattingEditor extends Component {
|
|
|
85963
85851
|
this.state.rules.iconSet[inflectionPoint].value = value;
|
|
85964
85852
|
this.updateConditionalFormat({ rule: this.state.rules.iconSet });
|
|
85965
85853
|
}
|
|
85966
|
-
setInflectionType(inflectionPoint, type
|
|
85854
|
+
setInflectionType(inflectionPoint, type) {
|
|
85967
85855
|
this.state.rules.iconSet[inflectionPoint].type = type;
|
|
85968
85856
|
this.updateConditionalFormat({ rule: this.state.rules.iconSet, suppressErrors: true });
|
|
85969
85857
|
}
|
|
85858
|
+
/*****************************************************************************
|
|
85859
|
+
* DataBar
|
|
85860
|
+
****************************************************************************/
|
|
85861
|
+
get rangeValues() {
|
|
85862
|
+
return [this.state.rules.dataBar.rangeValues || ""];
|
|
85863
|
+
}
|
|
85864
|
+
updateDataBarColor(color) {
|
|
85865
|
+
if (!isColorValid(color)) {
|
|
85866
|
+
return;
|
|
85867
|
+
}
|
|
85868
|
+
this.state.rules.dataBar.color = Number.parseInt(color.slice(1), 16);
|
|
85869
|
+
this.updateConditionalFormat({ rule: this.state.rules.dataBar });
|
|
85870
|
+
}
|
|
85871
|
+
onDataBarRangeUpdate(ranges) {
|
|
85872
|
+
this.state.rules.dataBar.rangeValues = ranges[0];
|
|
85873
|
+
}
|
|
85874
|
+
onDataBarRangeChange() {
|
|
85875
|
+
this.updateConditionalFormat({ rule: this.state.rules.dataBar });
|
|
85876
|
+
}
|
|
85877
|
+
/*****************************************************************************
|
|
85878
|
+
* Common
|
|
85879
|
+
****************************************************************************/
|
|
85880
|
+
toggleMenu(menu) {
|
|
85881
|
+
const isSelected = this.state.openedMenu === menu;
|
|
85882
|
+
this.closeMenus();
|
|
85883
|
+
if (!isSelected) {
|
|
85884
|
+
this.state.openedMenu = menu;
|
|
85885
|
+
}
|
|
85886
|
+
}
|
|
85887
|
+
closeMenus() {
|
|
85888
|
+
this.state.openedMenu = undefined;
|
|
85889
|
+
}
|
|
85890
|
+
getDefaultRules() {
|
|
85891
|
+
return {
|
|
85892
|
+
cellIs: {
|
|
85893
|
+
type: "CellIsRule",
|
|
85894
|
+
operator: "isNotEmpty",
|
|
85895
|
+
values: [],
|
|
85896
|
+
style: { fillColor: "#b6d7a8" },
|
|
85897
|
+
},
|
|
85898
|
+
colorScale: {
|
|
85899
|
+
type: "ColorScaleRule",
|
|
85900
|
+
minimum: { type: "value", color: hexaToInt("EFF7FF") },
|
|
85901
|
+
midpoint: undefined,
|
|
85902
|
+
maximum: { type: "value", color: 0x6aa84f },
|
|
85903
|
+
},
|
|
85904
|
+
iconSet: {
|
|
85905
|
+
type: "IconSetRule",
|
|
85906
|
+
icons: {
|
|
85907
|
+
upper: "arrowGood",
|
|
85908
|
+
middle: "arrowNeutral",
|
|
85909
|
+
lower: "arrowBad",
|
|
85910
|
+
},
|
|
85911
|
+
upperInflectionPoint: {
|
|
85912
|
+
type: "percentage",
|
|
85913
|
+
value: "66",
|
|
85914
|
+
operator: "gt",
|
|
85915
|
+
},
|
|
85916
|
+
lowerInflectionPoint: {
|
|
85917
|
+
type: "percentage",
|
|
85918
|
+
value: "33",
|
|
85919
|
+
operator: "gt",
|
|
85920
|
+
},
|
|
85921
|
+
},
|
|
85922
|
+
dataBar: {
|
|
85923
|
+
type: "DataBarRule",
|
|
85924
|
+
color: 0xd9ead3,
|
|
85925
|
+
},
|
|
85926
|
+
};
|
|
85927
|
+
}
|
|
85928
|
+
}
|
|
85929
|
+
|
|
85930
|
+
class ConditionalFormattingEditor extends Component {
|
|
85931
|
+
static template = "o-spreadsheet-ConditionalFormattingEditor";
|
|
85932
|
+
static components = {
|
|
85933
|
+
SelectionInput,
|
|
85934
|
+
IconPicker,
|
|
85935
|
+
ColorPickerWidget,
|
|
85936
|
+
Section,
|
|
85937
|
+
RoundColorPicker,
|
|
85938
|
+
StandaloneComposer,
|
|
85939
|
+
BadgeSelection,
|
|
85940
|
+
ValidationMessages,
|
|
85941
|
+
SelectMenu,
|
|
85942
|
+
};
|
|
85943
|
+
static props = { cf: Object, isNewCf: Boolean, onCloseSidePanel: Function };
|
|
85944
|
+
getTextDecoration = getTextDecoration;
|
|
85945
|
+
colorNumberToHex = colorNumberToHex;
|
|
85946
|
+
activeSheetId;
|
|
85947
|
+
store;
|
|
85948
|
+
setup() {
|
|
85949
|
+
this.activeSheetId = this.env.model.getters.getActiveSheetId();
|
|
85950
|
+
this.store = useLocalStore(ConditionalFormattingEditorStore, deepCopy(this.props.cf), this.props.isNewCf);
|
|
85951
|
+
useEffect((sheetId, isCfRemoved) => {
|
|
85952
|
+
if (this.activeSheetId !== sheetId || isCfRemoved) {
|
|
85953
|
+
this.env.replaceSidePanel("ConditionalFormatting", `ConditionalFormattingEditor_${this.props.cf.id}`);
|
|
85954
|
+
}
|
|
85955
|
+
}, () => [this.env.model.getters.getActiveSheetId(), this.isEditedCfRemoved]);
|
|
85956
|
+
useExternalListener(window, "click", () => this.store.closeMenus());
|
|
85957
|
+
}
|
|
85958
|
+
get isEditedCfRemoved() {
|
|
85959
|
+
return !Boolean(this.env.model.getters
|
|
85960
|
+
.getConditionalFormats(this.activeSheetId)
|
|
85961
|
+
.find((cf) => cf.id === this.props.cf.id));
|
|
85962
|
+
}
|
|
85963
|
+
get cfTypesValues() {
|
|
85964
|
+
return [
|
|
85965
|
+
{ value: "CellIsRule", label: _t("Single color") },
|
|
85966
|
+
{ value: "ColorScaleRule", label: _t("Color scale") },
|
|
85967
|
+
{ value: "IconSetRule", label: _t("Icon set") },
|
|
85968
|
+
{ value: "DataBarRule", label: _t("Data bar") },
|
|
85969
|
+
];
|
|
85970
|
+
}
|
|
85971
|
+
onSave() {
|
|
85972
|
+
this.store.updateConditionalFormat({});
|
|
85973
|
+
const isSuccessful = this.store.state.errors.length === 0;
|
|
85974
|
+
if (isSuccessful) {
|
|
85975
|
+
this.env.replaceSidePanel("ConditionalFormatting", `ConditionalFormattingEditor_${this.props.cf.id}`);
|
|
85976
|
+
}
|
|
85977
|
+
}
|
|
85978
|
+
onCancel() {
|
|
85979
|
+
if (this.store.state.hasEditedCf) {
|
|
85980
|
+
if (this.props.isNewCf) {
|
|
85981
|
+
this.env.model.dispatch("REMOVE_CONDITIONAL_FORMAT", {
|
|
85982
|
+
sheetId: this.activeSheetId,
|
|
85983
|
+
id: this.props.cf.id,
|
|
85984
|
+
});
|
|
85985
|
+
}
|
|
85986
|
+
else {
|
|
85987
|
+
this.env.model.dispatch("ADD_CONDITIONAL_FORMAT", {
|
|
85988
|
+
cf: this.props.cf,
|
|
85989
|
+
ranges: this.props.cf.ranges.map((range) => this.env.model.getters.getRangeDataFromXc(this.activeSheetId, range)),
|
|
85990
|
+
sheetId: this.activeSheetId,
|
|
85991
|
+
});
|
|
85992
|
+
}
|
|
85993
|
+
}
|
|
85994
|
+
this.env.replaceSidePanel("ConditionalFormatting", `ConditionalFormattingEditor_${this.props.cf.id}`);
|
|
85995
|
+
}
|
|
85996
|
+
/*****************************************************************************
|
|
85997
|
+
* Color Scale Rule
|
|
85998
|
+
****************************************************************************/
|
|
85999
|
+
getThresholdColor(threshold) {
|
|
86000
|
+
return threshold
|
|
86001
|
+
? colorNumberToHex(threshold.color)
|
|
86002
|
+
: colorNumberToHex(DEFAULT_COLOR_SCALE_MIDPOINT_COLOR);
|
|
86003
|
+
}
|
|
86004
|
+
isValueInvalid(threshold) {
|
|
86005
|
+
const errors = this.store.state.errors;
|
|
86006
|
+
switch (threshold) {
|
|
86007
|
+
case "minimum":
|
|
86008
|
+
return (errors.includes("MinInvalidFormula" /* CommandResult.MinInvalidFormula */) ||
|
|
86009
|
+
errors.includes("MinBiggerThanMid" /* CommandResult.MinBiggerThanMid */) ||
|
|
86010
|
+
errors.includes("MinBiggerThanMax" /* CommandResult.MinBiggerThanMax */) ||
|
|
86011
|
+
errors.includes("MinNaN" /* CommandResult.MinNaN */));
|
|
86012
|
+
case "midpoint":
|
|
86013
|
+
return (errors.includes("MidInvalidFormula" /* CommandResult.MidInvalidFormula */) ||
|
|
86014
|
+
errors.includes("MidNaN" /* CommandResult.MidNaN */) ||
|
|
86015
|
+
errors.includes("MidBiggerThanMax" /* CommandResult.MidBiggerThanMax */));
|
|
86016
|
+
case "maximum":
|
|
86017
|
+
return (errors.includes("MaxInvalidFormula" /* CommandResult.MaxInvalidFormula */) || errors.includes("MaxNaN" /* CommandResult.MaxNaN */));
|
|
86018
|
+
default:
|
|
86019
|
+
return false;
|
|
86020
|
+
}
|
|
86021
|
+
}
|
|
85970
86022
|
getColorScaleComposerProps(thresholdType) {
|
|
85971
|
-
const threshold = this.state.rules.colorScale[thresholdType];
|
|
86023
|
+
const threshold = this.store.state.rules.colorScale[thresholdType];
|
|
85972
86024
|
if (!threshold) {
|
|
85973
86025
|
throw new Error("Threshold not found");
|
|
85974
86026
|
}
|
|
@@ -85976,103 +86028,153 @@ class ConditionalFormattingEditor extends Component {
|
|
|
85976
86028
|
return {
|
|
85977
86029
|
onConfirm: (str) => {
|
|
85978
86030
|
threshold.value = str;
|
|
85979
|
-
this.updateConditionalFormat({ rule: this.state.rules.colorScale });
|
|
86031
|
+
this.store.updateConditionalFormat({ rule: this.store.state.rules.colorScale });
|
|
85980
86032
|
},
|
|
85981
86033
|
composerContent: threshold.value || "",
|
|
85982
86034
|
placeholder: _t("Formula"),
|
|
85983
86035
|
defaultStatic: true,
|
|
85984
86036
|
invalid: isInvalid,
|
|
85985
86037
|
class: "o-sidePanel-composer",
|
|
85986
|
-
defaultRangeSheetId: this.
|
|
86038
|
+
defaultRangeSheetId: this.activeSheetId,
|
|
85987
86039
|
};
|
|
85988
86040
|
}
|
|
86041
|
+
/*****************************************************************************
|
|
86042
|
+
* Icon Set
|
|
86043
|
+
****************************************************************************/
|
|
86044
|
+
isInflectionPointInvalid(inflectionPoint) {
|
|
86045
|
+
const errors = this.store.state.errors;
|
|
86046
|
+
switch (inflectionPoint) {
|
|
86047
|
+
case "lowerInflectionPoint":
|
|
86048
|
+
return (errors.includes("ValueLowerInflectionNaN" /* CommandResult.ValueLowerInflectionNaN */) ||
|
|
86049
|
+
errors.includes("ValueLowerInvalidFormula" /* CommandResult.ValueLowerInvalidFormula */) ||
|
|
86050
|
+
errors.includes("LowerBiggerThanUpper" /* CommandResult.LowerBiggerThanUpper */));
|
|
86051
|
+
case "upperInflectionPoint":
|
|
86052
|
+
return (errors.includes("ValueUpperInflectionNaN" /* CommandResult.ValueUpperInflectionNaN */) ||
|
|
86053
|
+
errors.includes("ValueUpperInvalidFormula" /* CommandResult.ValueUpperInvalidFormula */) ||
|
|
86054
|
+
errors.includes("LowerBiggerThanUpper" /* CommandResult.LowerBiggerThanUpper */));
|
|
86055
|
+
default:
|
|
86056
|
+
return true;
|
|
86057
|
+
}
|
|
86058
|
+
}
|
|
85989
86059
|
getColorIconSetComposerProps(inflectionPoint) {
|
|
85990
|
-
const inflection = this.state.rules.iconSet[inflectionPoint];
|
|
86060
|
+
const inflection = this.store.state.rules.iconSet[inflectionPoint];
|
|
85991
86061
|
const isInvalid = this.isInflectionPointInvalid(inflectionPoint);
|
|
85992
86062
|
return {
|
|
85993
86063
|
onConfirm: (str) => {
|
|
85994
86064
|
inflection.value = str;
|
|
85995
|
-
this.updateConditionalFormat({ rule: this.state.rules.iconSet });
|
|
86065
|
+
this.store.updateConditionalFormat({ rule: this.store.state.rules.iconSet });
|
|
85996
86066
|
},
|
|
85997
86067
|
composerContent: inflection.value || "",
|
|
85998
86068
|
placeholder: _t("Formula"),
|
|
85999
86069
|
defaultStatic: true,
|
|
86000
86070
|
invalid: isInvalid,
|
|
86001
86071
|
class: "o-sidePanel-composer",
|
|
86002
|
-
defaultRangeSheetId: this.
|
|
86072
|
+
defaultRangeSheetId: this.activeSheetId,
|
|
86003
86073
|
};
|
|
86004
86074
|
}
|
|
86005
|
-
|
|
86006
|
-
|
|
86007
|
-
|
|
86008
|
-
|
|
86009
|
-
|
|
86075
|
+
}
|
|
86076
|
+
|
|
86077
|
+
function useHighlightsOnHover(ref, highlightProvider) {
|
|
86078
|
+
const hoverState = useHoveredElement(ref);
|
|
86079
|
+
useHighlights({
|
|
86080
|
+
get highlights() {
|
|
86081
|
+
return hoverState.hovered ? highlightProvider.highlights : [];
|
|
86082
|
+
},
|
|
86083
|
+
});
|
|
86084
|
+
}
|
|
86085
|
+
function useHighlights(highlightProvider) {
|
|
86086
|
+
const stores = useStoreProvider();
|
|
86087
|
+
const store = useLocalStore(HighlightStore);
|
|
86088
|
+
onMounted(() => {
|
|
86089
|
+
store.register(highlightProvider);
|
|
86090
|
+
});
|
|
86091
|
+
let currentHighlights = highlightProvider.highlights;
|
|
86092
|
+
useEffect((highlights) => {
|
|
86093
|
+
if (!deepEquals(highlights, currentHighlights)) {
|
|
86094
|
+
currentHighlights = highlights;
|
|
86095
|
+
stores.trigger("store-updated");
|
|
86096
|
+
}
|
|
86097
|
+
}, () => [highlightProvider.highlights]);
|
|
86098
|
+
}
|
|
86099
|
+
|
|
86100
|
+
class ConditionalFormatPreview extends Component {
|
|
86101
|
+
static template = "o-spreadsheet-ConditionalFormatPreview";
|
|
86102
|
+
static props = {
|
|
86103
|
+
conditionalFormat: Object,
|
|
86104
|
+
onMouseDown: Function,
|
|
86105
|
+
class: String,
|
|
86106
|
+
};
|
|
86107
|
+
icons = ICONS;
|
|
86108
|
+
ref = useRef("cfPreview");
|
|
86109
|
+
setup() {
|
|
86110
|
+
useHighlightsOnHover(this.ref, this);
|
|
86010
86111
|
}
|
|
86011
|
-
|
|
86012
|
-
|
|
86013
|
-
|
|
86112
|
+
get previewImageStyle() {
|
|
86113
|
+
const rule = this.props.conditionalFormat.rule;
|
|
86114
|
+
if (rule.type === "CellIsRule") {
|
|
86115
|
+
return cssPropertiesToCss(cellStyleToCss(rule.style));
|
|
86014
86116
|
}
|
|
86015
|
-
|
|
86016
|
-
|
|
86117
|
+
else if (rule.type === "ColorScaleRule") {
|
|
86118
|
+
const minColor = colorNumberToHex(rule.minimum.color);
|
|
86119
|
+
const midColor = rule.midpoint ? colorNumberToHex(rule.midpoint.color) : null;
|
|
86120
|
+
const maxColor = colorNumberToHex(rule.maximum.color);
|
|
86121
|
+
const baseString = "background-image: linear-gradient(to right, ";
|
|
86122
|
+
return midColor
|
|
86123
|
+
? baseString + minColor + ", " + midColor + ", " + maxColor + ")"
|
|
86124
|
+
: baseString + minColor + ", " + maxColor + ")";
|
|
86125
|
+
}
|
|
86126
|
+
else if (rule.type === "DataBarRule") {
|
|
86127
|
+
const barColor = colorNumberToHex(rule.color);
|
|
86128
|
+
const gradient = `background-image: linear-gradient(to right, ${barColor} 50%, white 50%)`;
|
|
86129
|
+
return `${gradient}; color: ${TEXT_BODY};`;
|
|
86130
|
+
}
|
|
86131
|
+
return "";
|
|
86017
86132
|
}
|
|
86018
|
-
|
|
86019
|
-
this.
|
|
86133
|
+
get description() {
|
|
86134
|
+
const cf = this.props.conditionalFormat;
|
|
86135
|
+
switch (cf.rule.type) {
|
|
86136
|
+
case "CellIsRule":
|
|
86137
|
+
return criterionEvaluatorRegistry
|
|
86138
|
+
.get(cf.rule.operator)
|
|
86139
|
+
.getPreview({ ...cf.rule, type: cf.rule.operator }, this.env.model.getters);
|
|
86140
|
+
case "ColorScaleRule":
|
|
86141
|
+
return CfTerms.ColorScale;
|
|
86142
|
+
case "IconSetRule":
|
|
86143
|
+
return CfTerms.IconSet;
|
|
86144
|
+
case "DataBarRule":
|
|
86145
|
+
return CfTerms.DataBar;
|
|
86146
|
+
}
|
|
86020
86147
|
}
|
|
86021
|
-
|
|
86022
|
-
|
|
86148
|
+
get highlights() {
|
|
86149
|
+
const sheetId = this.env.model.getters.getActiveSheetId();
|
|
86150
|
+
return this.props.conditionalFormat.ranges.map((range) => ({
|
|
86151
|
+
range: this.env.model.getters.getRangeFromSheetXC(sheetId, range),
|
|
86152
|
+
color: HIGHLIGHT_COLOR,
|
|
86153
|
+
fillAlpha: 0.06,
|
|
86154
|
+
}));
|
|
86155
|
+
}
|
|
86156
|
+
editConditionalFormat() {
|
|
86157
|
+
this.env.replaceSidePanel("ConditionalFormattingEditor", "ConditionalFormatting", {
|
|
86158
|
+
cf: this.props.conditionalFormat,
|
|
86159
|
+
isNewCf: false,
|
|
86160
|
+
});
|
|
86161
|
+
}
|
|
86162
|
+
deleteConditionalFormat() {
|
|
86163
|
+
this.env.model.dispatch("REMOVE_CONDITIONAL_FORMAT", {
|
|
86164
|
+
id: this.props.conditionalFormat.id,
|
|
86165
|
+
sheetId: this.env.model.getters.getActiveSheetId(),
|
|
86166
|
+
});
|
|
86023
86167
|
}
|
|
86024
86168
|
}
|
|
86025
86169
|
|
|
86026
|
-
class
|
|
86027
|
-
static template = "o-spreadsheet-
|
|
86170
|
+
class ConditionalFormatPreviewList extends Component {
|
|
86171
|
+
static template = "o-spreadsheet-ConditionalFormatPreviewList";
|
|
86028
86172
|
static props = {
|
|
86029
|
-
selection: { type: Object, optional: true },
|
|
86030
86173
|
onCloseSidePanel: Function,
|
|
86031
86174
|
};
|
|
86032
|
-
static components = {
|
|
86033
|
-
|
|
86034
|
-
|
|
86035
|
-
Section,
|
|
86036
|
-
};
|
|
86037
|
-
activeSheetId;
|
|
86038
|
-
originalEditedCf = undefined;
|
|
86039
|
-
state = useState({
|
|
86040
|
-
mode: "list",
|
|
86041
|
-
});
|
|
86042
|
-
setup() {
|
|
86043
|
-
this.activeSheetId = this.env.model.getters.getActiveSheetId();
|
|
86044
|
-
const sheetId = this.env.model.getters.getActiveSheetId();
|
|
86045
|
-
const rules = this.env.model.getters.getRulesSelection(sheetId, this.props.selection || []);
|
|
86046
|
-
if (rules.length === 1) {
|
|
86047
|
-
const cf = this.conditionalFormats.find((c) => c.id === rules[0]);
|
|
86048
|
-
if (cf) {
|
|
86049
|
-
this.editConditionalFormat(cf);
|
|
86050
|
-
}
|
|
86051
|
-
}
|
|
86052
|
-
onWillUpdateProps((nextProps) => {
|
|
86053
|
-
const newActiveSheetId = this.env.model.getters.getActiveSheetId();
|
|
86054
|
-
if (newActiveSheetId !== this.activeSheetId) {
|
|
86055
|
-
this.activeSheetId = newActiveSheetId;
|
|
86056
|
-
this.switchToList();
|
|
86057
|
-
}
|
|
86058
|
-
else if (nextProps.selection !== this.props.selection) {
|
|
86059
|
-
const sheetId = this.env.model.getters.getActiveSheetId();
|
|
86060
|
-
const rules = this.env.model.getters.getRulesSelection(sheetId, nextProps.selection || []);
|
|
86061
|
-
if (rules.length === 1) {
|
|
86062
|
-
const cf = this.conditionalFormats.find((c) => c.id === rules[0]);
|
|
86063
|
-
if (cf) {
|
|
86064
|
-
this.editConditionalFormat(cf);
|
|
86065
|
-
}
|
|
86066
|
-
}
|
|
86067
|
-
else {
|
|
86068
|
-
this.switchToList();
|
|
86069
|
-
}
|
|
86070
|
-
}
|
|
86071
|
-
else if (!this.editedCF) {
|
|
86072
|
-
this.switchToList();
|
|
86073
|
-
}
|
|
86074
|
-
});
|
|
86075
|
-
}
|
|
86175
|
+
static components = { ConditionalFormatPreview };
|
|
86176
|
+
dragAndDrop = useDragAndDropListItems();
|
|
86177
|
+
cfListRef = useRef("cfList");
|
|
86076
86178
|
get conditionalFormats() {
|
|
86077
86179
|
const cfs = this.env.model.getters.getConditionalFormats(this.env.model.getters.getActiveSheetId());
|
|
86078
86180
|
return cfs.map((cf) => ({
|
|
@@ -86080,66 +86182,129 @@ class ConditionalFormattingPanel extends Component {
|
|
|
86080
86182
|
rule: localizeCFRule(cf.rule, this.env.model.getters.getLocale()),
|
|
86081
86183
|
}));
|
|
86082
86184
|
}
|
|
86083
|
-
|
|
86084
|
-
this.
|
|
86085
|
-
this.state.editedCfId = undefined;
|
|
86086
|
-
this.originalEditedCf = undefined;
|
|
86185
|
+
getPreviewDivStyle(cf) {
|
|
86186
|
+
return this.dragAndDrop.itemsStyle[cf.id] || "";
|
|
86087
86187
|
}
|
|
86088
|
-
|
|
86089
|
-
|
|
86188
|
+
onPreviewMouseDown(cf, event) {
|
|
86189
|
+
if (event.button !== 0)
|
|
86190
|
+
return;
|
|
86191
|
+
const previewRects = Array.from(this.cfListRef.el.children).map((previewEl) => getBoundingRectAsPOJO(previewEl));
|
|
86192
|
+
const items = this.conditionalFormats.map((cf, index) => ({
|
|
86193
|
+
id: cf.id,
|
|
86194
|
+
size: previewRects[index].height,
|
|
86195
|
+
position: previewRects[index].y,
|
|
86196
|
+
}));
|
|
86197
|
+
this.dragAndDrop.start("vertical", {
|
|
86198
|
+
draggedItemId: cf.id,
|
|
86199
|
+
initialMousePosition: event.clientY,
|
|
86200
|
+
items: items,
|
|
86201
|
+
scrollableContainerEl: this.cfListRef.el,
|
|
86202
|
+
onDragEnd: (cfId, finalIndex) => this.onDragEnd(cfId, finalIndex),
|
|
86203
|
+
});
|
|
86204
|
+
}
|
|
86205
|
+
onAddConditionalFormat() {
|
|
86206
|
+
const sheetId = this.env.model.getters.getActiveSheetId();
|
|
86207
|
+
const zones = this.env.model.getters.getSelectedZones();
|
|
86208
|
+
const cf = {
|
|
86209
|
+
id: this.env.model.uuidGenerator.smallUuid(),
|
|
86210
|
+
rule: {
|
|
86211
|
+
type: "CellIsRule",
|
|
86212
|
+
operator: "isNotEmpty",
|
|
86213
|
+
style: { fillColor: "#b6d7a8" },
|
|
86214
|
+
values: [],
|
|
86215
|
+
},
|
|
86216
|
+
};
|
|
86090
86217
|
this.env.model.dispatch("ADD_CONDITIONAL_FORMAT", {
|
|
86091
|
-
|
|
86092
|
-
ranges: this.env.model.getters
|
|
86093
|
-
|
|
86094
|
-
|
|
86218
|
+
cf,
|
|
86219
|
+
ranges: zones.map((zone) => this.env.model.getters.getRangeDataFromZone(sheetId, zone)),
|
|
86220
|
+
sheetId,
|
|
86221
|
+
});
|
|
86222
|
+
return this.env.replaceSidePanel("ConditionalFormattingEditor", "ConditionalFormatting", {
|
|
86095
86223
|
cf: {
|
|
86096
|
-
|
|
86097
|
-
|
|
86098
|
-
type: "CellIsRule",
|
|
86099
|
-
operator: "isNotEmpty",
|
|
86100
|
-
style: { fillColor: "#b6d7a8" },
|
|
86101
|
-
values: [],
|
|
86102
|
-
},
|
|
86224
|
+
...cf,
|
|
86225
|
+
ranges: zones.map((zone) => zoneToXc(this.env.model.getters.getUnboundedZone(sheetId, zone))),
|
|
86103
86226
|
},
|
|
86227
|
+
isNewCf: true,
|
|
86104
86228
|
});
|
|
86105
|
-
this.state.editedCfId = cfId;
|
|
86106
|
-
this.state.mode = "edit";
|
|
86107
|
-
this.originalEditedCf = undefined;
|
|
86108
|
-
}
|
|
86109
|
-
editConditionalFormat(cf) {
|
|
86110
|
-
this.state.mode = "edit";
|
|
86111
|
-
this.state.editedCfId = cf.id;
|
|
86112
|
-
this.originalEditedCf = cf;
|
|
86113
86229
|
}
|
|
86114
|
-
|
|
86115
|
-
|
|
86116
|
-
|
|
86117
|
-
|
|
86118
|
-
|
|
86119
|
-
|
|
86120
|
-
|
|
86121
|
-
|
|
86122
|
-
else if (this.state.editedCfId) {
|
|
86123
|
-
this.env.model.dispatch("REMOVE_CONDITIONAL_FORMAT", {
|
|
86124
|
-
sheetId: this.activeSheetId,
|
|
86125
|
-
id: this.state.editedCfId,
|
|
86230
|
+
onDragEnd(cfId, finalIndex) {
|
|
86231
|
+
const originalIndex = this.conditionalFormats.findIndex((sheet) => sheet.id === cfId);
|
|
86232
|
+
const delta = originalIndex - finalIndex;
|
|
86233
|
+
if (delta !== 0) {
|
|
86234
|
+
this.env.model.dispatch("CHANGE_CONDITIONAL_FORMAT_PRIORITY", {
|
|
86235
|
+
cfId,
|
|
86236
|
+
delta,
|
|
86237
|
+
sheetId: this.env.model.getters.getActiveSheetId(),
|
|
86126
86238
|
});
|
|
86127
86239
|
}
|
|
86128
|
-
this.switchToList();
|
|
86129
86240
|
}
|
|
86130
|
-
|
|
86131
|
-
|
|
86241
|
+
}
|
|
86242
|
+
|
|
86243
|
+
class DataValidationPreview extends Component {
|
|
86244
|
+
static template = "o-spreadsheet-DataValidationPreview";
|
|
86245
|
+
static props = {
|
|
86246
|
+
rule: Object,
|
|
86247
|
+
};
|
|
86248
|
+
ref = useRef("dvPreview");
|
|
86249
|
+
setup() {
|
|
86250
|
+
useHighlightsOnHover(this.ref, this);
|
|
86251
|
+
}
|
|
86252
|
+
onPreviewClick() {
|
|
86253
|
+
this.env.replaceSidePanel("DataValidationEditor", "DataValidation", {
|
|
86254
|
+
ruleId: this.props.rule.id,
|
|
86255
|
+
});
|
|
86256
|
+
}
|
|
86257
|
+
deleteDataValidation() {
|
|
86258
|
+
const sheetId = this.env.model.getters.getActiveSheetId();
|
|
86259
|
+
this.env.model.dispatch("REMOVE_DATA_VALIDATION_RULE", { sheetId, id: this.props.rule.id });
|
|
86260
|
+
}
|
|
86261
|
+
get highlights() {
|
|
86262
|
+
return this.props.rule.ranges.map((range) => ({
|
|
86263
|
+
range,
|
|
86264
|
+
color: HIGHLIGHT_COLOR,
|
|
86265
|
+
fillAlpha: 0.06,
|
|
86266
|
+
}));
|
|
86267
|
+
}
|
|
86268
|
+
get rangesString() {
|
|
86269
|
+
const sheetId = this.env.model.getters.getActiveSheetId();
|
|
86270
|
+
return this.props.rule.ranges
|
|
86271
|
+
.map((range) => this.env.model.getters.getRangeString(range, sheetId))
|
|
86272
|
+
.join(", ");
|
|
86273
|
+
}
|
|
86274
|
+
get descriptionString() {
|
|
86275
|
+
return criterionEvaluatorRegistry
|
|
86276
|
+
.get(this.props.rule.criterion.type)
|
|
86277
|
+
.getPreview(this.props.rule.criterion, this.env.model.getters);
|
|
86278
|
+
}
|
|
86279
|
+
}
|
|
86280
|
+
|
|
86281
|
+
class DataValidationPanel extends Component {
|
|
86282
|
+
static template = "o-spreadsheet-DataValidationPanel";
|
|
86283
|
+
static props = {
|
|
86284
|
+
onCloseSidePanel: Function,
|
|
86285
|
+
};
|
|
86286
|
+
static components = { DataValidationPreview };
|
|
86287
|
+
addDataValidationRule() {
|
|
86288
|
+
this.env.replaceSidePanel("DataValidationEditor", "DataValidation", {
|
|
86289
|
+
ruleId: this.env.model.uuidGenerator.smallUuid(),
|
|
86290
|
+
});
|
|
86291
|
+
}
|
|
86292
|
+
localizeDVRule(rule) {
|
|
86293
|
+
if (!rule)
|
|
86294
|
+
return rule;
|
|
86295
|
+
const locale = this.env.model.getters.getLocale();
|
|
86296
|
+
return localizeDataValidationRule(rule, locale);
|
|
86297
|
+
}
|
|
86298
|
+
get validationRules() {
|
|
86299
|
+
const sheetId = this.env.model.getters.getActiveSheetId();
|
|
86300
|
+
return this.env.model.getters.getDataValidationRules(sheetId);
|
|
86132
86301
|
}
|
|
86133
86302
|
}
|
|
86134
86303
|
|
|
86135
86304
|
class DataValidationEditor extends Component {
|
|
86136
86305
|
static template = "o-spreadsheet-DataValidationEditor";
|
|
86137
86306
|
static components = { SelectionInput, SelectMenu, Section, ValidationMessages };
|
|
86138
|
-
static props = {
|
|
86139
|
-
rule: { type: Object, optional: true },
|
|
86140
|
-
onExit: Function,
|
|
86141
|
-
onCloseSidePanel: { type: Function, optional: true },
|
|
86142
|
-
};
|
|
86307
|
+
static props = { ruleId: String, onCloseSidePanel: Function };
|
|
86143
86308
|
state = useState({
|
|
86144
86309
|
rule: this.defaultDataValidationRule,
|
|
86145
86310
|
errors: [],
|
|
@@ -86148,12 +86313,13 @@ class DataValidationEditor extends Component {
|
|
|
86148
86313
|
editingSheetId;
|
|
86149
86314
|
setup() {
|
|
86150
86315
|
this.editingSheetId = this.env.model.getters.getActiveSheetId();
|
|
86151
|
-
|
|
86316
|
+
const rule = this.env.model.getters.getDataValidationRule(this.editingSheetId, this.props.ruleId);
|
|
86317
|
+
if (rule) {
|
|
86318
|
+
const locale = this.env.model.getters.getLocale();
|
|
86152
86319
|
this.state.rule = {
|
|
86153
|
-
...
|
|
86154
|
-
ranges:
|
|
86320
|
+
...localizeDataValidationRule(rule, locale),
|
|
86321
|
+
ranges: rule.ranges.map((range) => this.env.model.getters.getRangeString(range, this.editingSheetId)),
|
|
86155
86322
|
};
|
|
86156
|
-
this.state.rule.criterion.type = this.props.rule.criterion.type;
|
|
86157
86323
|
}
|
|
86158
86324
|
}
|
|
86159
86325
|
onCriterionTypeChanged(type) {
|
|
@@ -86170,16 +86336,16 @@ class DataValidationEditor extends Component {
|
|
|
86170
86336
|
const isBlocking = ev.target.value;
|
|
86171
86337
|
this.state.rule.isBlocking = isBlocking === "true";
|
|
86172
86338
|
}
|
|
86339
|
+
onCancel() {
|
|
86340
|
+
this.env.replaceSidePanel("DataValidation", `DataValidationEditor_${this.props.ruleId}`);
|
|
86341
|
+
}
|
|
86173
86342
|
onSave() {
|
|
86174
|
-
|
|
86175
|
-
|
|
86176
|
-
|
|
86177
|
-
|
|
86178
|
-
}
|
|
86179
|
-
else {
|
|
86180
|
-
this.props.onExit();
|
|
86181
|
-
}
|
|
86343
|
+
const result = this.env.model.dispatch("ADD_DATA_VALIDATION_RULE", this.dispatchPayload);
|
|
86344
|
+
if (!result.isSuccessful) {
|
|
86345
|
+
this.state.errors = result.reasons;
|
|
86346
|
+
return;
|
|
86182
86347
|
}
|
|
86348
|
+
this.env.replaceSidePanel("DataValidation", `DataValidationEditor_${this.props.ruleId}`);
|
|
86183
86349
|
}
|
|
86184
86350
|
get dispatchPayload() {
|
|
86185
86351
|
const rule = { ...this.state.rule, ranges: undefined };
|
|
@@ -86211,7 +86377,7 @@ class DataValidationEditor extends Component {
|
|
|
86211
86377
|
.getSelectedZones()
|
|
86212
86378
|
.map((zone) => zoneToXc(this.env.model.getters.getUnboundedZone(sheetId, zone)));
|
|
86213
86379
|
return {
|
|
86214
|
-
id: this.
|
|
86380
|
+
id: this.props.ruleId,
|
|
86215
86381
|
criterion: { type: "containsText", values: [""] },
|
|
86216
86382
|
ranges,
|
|
86217
86383
|
};
|
|
@@ -86224,75 +86390,6 @@ class DataValidationEditor extends Component {
|
|
|
86224
86390
|
}
|
|
86225
86391
|
}
|
|
86226
86392
|
|
|
86227
|
-
class DataValidationPreview extends Component {
|
|
86228
|
-
static template = "o-spreadsheet-DataValidationPreview";
|
|
86229
|
-
static props = {
|
|
86230
|
-
onClick: Function,
|
|
86231
|
-
rule: Object,
|
|
86232
|
-
};
|
|
86233
|
-
ref = useRef("dvPreview");
|
|
86234
|
-
setup() {
|
|
86235
|
-
useHighlightsOnHover(this.ref, this);
|
|
86236
|
-
}
|
|
86237
|
-
deleteDataValidation() {
|
|
86238
|
-
const sheetId = this.env.model.getters.getActiveSheetId();
|
|
86239
|
-
this.env.model.dispatch("REMOVE_DATA_VALIDATION_RULE", { sheetId, id: this.props.rule.id });
|
|
86240
|
-
}
|
|
86241
|
-
get highlights() {
|
|
86242
|
-
return this.props.rule.ranges.map((range) => ({
|
|
86243
|
-
range,
|
|
86244
|
-
color: HIGHLIGHT_COLOR,
|
|
86245
|
-
fillAlpha: 0.06,
|
|
86246
|
-
}));
|
|
86247
|
-
}
|
|
86248
|
-
get rangesString() {
|
|
86249
|
-
const sheetId = this.env.model.getters.getActiveSheetId();
|
|
86250
|
-
return this.props.rule.ranges
|
|
86251
|
-
.map((range) => this.env.model.getters.getRangeString(range, sheetId))
|
|
86252
|
-
.join(", ");
|
|
86253
|
-
}
|
|
86254
|
-
get descriptionString() {
|
|
86255
|
-
return criterionEvaluatorRegistry
|
|
86256
|
-
.get(this.props.rule.criterion.type)
|
|
86257
|
-
.getPreview(this.props.rule.criterion, this.env.model.getters);
|
|
86258
|
-
}
|
|
86259
|
-
}
|
|
86260
|
-
|
|
86261
|
-
class DataValidationPanel extends Component {
|
|
86262
|
-
static template = "o-spreadsheet-DataValidationPanel";
|
|
86263
|
-
static props = {
|
|
86264
|
-
onCloseSidePanel: Function,
|
|
86265
|
-
};
|
|
86266
|
-
static components = { DataValidationPreview, DataValidationEditor };
|
|
86267
|
-
state = useState({ mode: "list", activeRule: undefined });
|
|
86268
|
-
onPreviewClick(id) {
|
|
86269
|
-
const sheetId = this.env.model.getters.getActiveSheetId();
|
|
86270
|
-
const rule = this.env.model.getters.getDataValidationRule(sheetId, id);
|
|
86271
|
-
if (rule) {
|
|
86272
|
-
this.state.mode = "edit";
|
|
86273
|
-
this.state.activeRule = rule;
|
|
86274
|
-
}
|
|
86275
|
-
}
|
|
86276
|
-
addDataValidationRule() {
|
|
86277
|
-
this.state.mode = "edit";
|
|
86278
|
-
this.state.activeRule = undefined;
|
|
86279
|
-
}
|
|
86280
|
-
onExitEditMode() {
|
|
86281
|
-
this.state.mode = "list";
|
|
86282
|
-
this.state.activeRule = undefined;
|
|
86283
|
-
}
|
|
86284
|
-
localizeDVRule(rule) {
|
|
86285
|
-
if (!rule)
|
|
86286
|
-
return rule;
|
|
86287
|
-
const locale = this.env.model.getters.getLocale();
|
|
86288
|
-
return localizeDataValidationRule(rule, locale);
|
|
86289
|
-
}
|
|
86290
|
-
get validationRules() {
|
|
86291
|
-
const sheetId = this.env.model.getters.getActiveSheetId();
|
|
86292
|
-
return this.env.model.getters.getDataValidationRules(sheetId);
|
|
86293
|
-
}
|
|
86294
|
-
}
|
|
86295
|
-
|
|
86296
86393
|
const FIND_AND_REPLACE_HIGHLIGHT_COLOR = "#8B008B";
|
|
86297
86394
|
var Direction;
|
|
86298
86395
|
(function (Direction) {
|
|
@@ -89170,7 +89267,18 @@ class TableStyleEditorPanel extends Component {
|
|
|
89170
89267
|
const sidePanelRegistry = new Registry$1();
|
|
89171
89268
|
sidePanelRegistry.add("ConditionalFormatting", {
|
|
89172
89269
|
title: _t("Conditional formatting"),
|
|
89173
|
-
Body:
|
|
89270
|
+
Body: ConditionalFormatPreviewList,
|
|
89271
|
+
});
|
|
89272
|
+
sidePanelRegistry.add("ConditionalFormattingEditor", {
|
|
89273
|
+
title: _t("Conditional formatting"),
|
|
89274
|
+
Body: ConditionalFormattingEditor,
|
|
89275
|
+
computeState: (getters, props) => {
|
|
89276
|
+
return {
|
|
89277
|
+
isOpen: true,
|
|
89278
|
+
props,
|
|
89279
|
+
key: `ConditionalFormattingEditor_${props.cf.id}`,
|
|
89280
|
+
};
|
|
89281
|
+
},
|
|
89174
89282
|
});
|
|
89175
89283
|
sidePanelRegistry.add("ChartPanel", {
|
|
89176
89284
|
title: _t("Chart"),
|
|
@@ -89207,6 +89315,13 @@ sidePanelRegistry.add("DataValidation", {
|
|
|
89207
89315
|
sidePanelRegistry.add("DataValidationEditor", {
|
|
89208
89316
|
title: _t("Data validation"),
|
|
89209
89317
|
Body: DataValidationEditor,
|
|
89318
|
+
computeState: (getters, props) => {
|
|
89319
|
+
return {
|
|
89320
|
+
isOpen: true,
|
|
89321
|
+
props,
|
|
89322
|
+
key: `DataValidationEditor_${props.ruleId}`,
|
|
89323
|
+
};
|
|
89324
|
+
},
|
|
89210
89325
|
});
|
|
89211
89326
|
sidePanelRegistry.add("MoreFormats", {
|
|
89212
89327
|
title: _t("More formats"),
|
|
@@ -89893,7 +90008,8 @@ class Grid extends Component {
|
|
|
89893
90008
|
});
|
|
89894
90009
|
return !(rect.width === 0 || rect.height === 0);
|
|
89895
90010
|
}
|
|
89896
|
-
onGridResized(
|
|
90011
|
+
onGridResized() {
|
|
90012
|
+
const { height, width } = this.props.getGridSize();
|
|
89897
90013
|
this.env.model.dispatch("RESIZE_SHEETVIEW", {
|
|
89898
90014
|
width: width - HEADER_WIDTH,
|
|
89899
90015
|
height: height - HEADER_HEIGHT,
|
|
@@ -93441,10 +93557,8 @@ class SpreadsheetDashboard extends Component {
|
|
|
93441
93557
|
});
|
|
93442
93558
|
}
|
|
93443
93559
|
get gridContainer() {
|
|
93444
|
-
const
|
|
93445
|
-
|
|
93446
|
-
const { end } = this.env.model.getters.getColDimensions(sheetId, right);
|
|
93447
|
-
return cssPropertiesToCss({ "max-width": `${end}px` });
|
|
93560
|
+
const maxWidth = this.getMaxSheetWidth();
|
|
93561
|
+
return cssPropertiesToCss({ "max-width": `${maxWidth}px` });
|
|
93448
93562
|
}
|
|
93449
93563
|
get gridOverlayDimensions() {
|
|
93450
93564
|
return cssPropertiesToCss({
|
|
@@ -93476,10 +93590,12 @@ class SpreadsheetDashboard extends Component {
|
|
|
93476
93590
|
onClosePopover() {
|
|
93477
93591
|
this.cellPopovers.close();
|
|
93478
93592
|
}
|
|
93479
|
-
onGridResized(
|
|
93593
|
+
onGridResized() {
|
|
93594
|
+
const { height, width } = this.props.getGridSize();
|
|
93595
|
+
const maxWidth = this.getMaxSheetWidth();
|
|
93480
93596
|
this.env.model.dispatch("RESIZE_SHEETVIEW", {
|
|
93481
|
-
width: width,
|
|
93482
|
-
height
|
|
93597
|
+
width: Math.min(maxWidth, width),
|
|
93598
|
+
height,
|
|
93483
93599
|
gridOffsetX: 0,
|
|
93484
93600
|
gridOffsetY: 0,
|
|
93485
93601
|
});
|
|
@@ -93497,6 +93613,11 @@ class SpreadsheetDashboard extends Component {
|
|
|
93497
93613
|
...this.env.model.getters.getSheetViewDimensionWithHeaders(),
|
|
93498
93614
|
};
|
|
93499
93615
|
}
|
|
93616
|
+
getMaxSheetWidth() {
|
|
93617
|
+
const sheetId = this.env.model.getters.getActiveSheetId();
|
|
93618
|
+
const { right } = this.env.model.getters.getSheetZone(sheetId);
|
|
93619
|
+
return this.env.model.getters.getColDimensions(sheetId, right).end;
|
|
93620
|
+
}
|
|
93500
93621
|
}
|
|
93501
93622
|
|
|
93502
93623
|
class AbstractHeaderGroup extends Component {
|
|
@@ -97898,6 +98019,6 @@ const chartHelpers = { ...CHART_HELPERS, ...CHART_RUNTIME_HELPERS };
|
|
|
97898
98019
|
export { AbstractCellClipboardHandler, AbstractChart, AbstractFigureClipboardHandler, CellErrorType, ClientDisconnectedError, CommandResult, CorePlugin, CoreViewPlugin, DEFAULT_LOCALE, DEFAULT_LOCALES, DispatchResult, EvaluationError, LocalTransportService, Model, PivotRuntimeDefinition, Registry$1 as Registry, Revision, SPREADSHEET_DIMENSIONS, Spreadsheet, SpreadsheetPivotTable, UIPlugin, __info__, addFunction, addRenderingLayer, astToFormula, categories, chartHelpers, compile, compileTokens, components, constants, convertAstNodes, coreTypes, createAutocompleteArgumentsProvider, findCellInNewZone, functionCache, getCaretDownSvg, getCaretUpSvg, helpers, hooks, invalidateCFEvaluationCommands, invalidateChartEvaluationCommands, invalidateDependenciesCommands, invalidateEvaluationCommands, iterateAstNodes, links, load, parse$1 as parse, parseTokens, readonlyAllowedCommands, registries, setDefaultSheetViewSize, setTranslationMethod, stores, tokenColors, tokenize };
|
|
97899
98020
|
|
|
97900
98021
|
|
|
97901
|
-
__info__.version = "19.2.0-alpha.
|
|
97902
|
-
__info__.date = "
|
|
97903
|
-
__info__.hash = "
|
|
98022
|
+
__info__.version = "19.2.0-alpha.2";
|
|
98023
|
+
__info__.date = "2026-01-07T16:21:35.251Z";
|
|
98024
|
+
__info__.hash = "ac2fa3e";
|